├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── example ├── aligenie │ ├── example_aligenie_aircondition.ts │ ├── example_aligenie_fan.ts │ ├── example_aligenie_light.ts │ ├── example_aligenie_multi_outlet.ts │ ├── example_aligenie_outlet.ts │ └── example_aligenie_sensor.ts ├── dueros │ ├── example_dueros_aircondition.ts │ ├── example_dueros_fan.ts │ ├── example_dueros_light.ts │ ├── example_dueros_multi_outlet.ts │ ├── example_dueros_outlet.ts │ └── example_dueros_sensor.ts ├── example.ts ├── example_hello.ts ├── example_pro.ts ├── example_realtime.ts ├── example_server.ts ├── miot │ ├── example_miot_aircondition.ts │ ├── example_miot_fan.ts │ ├── example_miot_light.ts │ ├── example_miot_multi_outlet.ts │ ├── example_miot_outlet.ts │ └── example_miot_sensor.ts ├── package-lock.json ├── package.json └── readme.md ├── lib ├── blinker-cli.ts ├── blinker-pro.ts ├── blinker.ts ├── debug.ts ├── fun.ts ├── message.ts ├── server.config.ts ├── voice-assistant.ts ├── widget.ts └── wireless.ts ├── package-lock.json ├── package.json ├── readme.md └── tsconfig.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: auto-deploy 2 | on: 3 | push: 4 | branches: [typescript] 5 | 6 | jobs: 7 | archive-file: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: get version 12 | id: version 13 | uses: notiz-dev/github-action-json-property@release 14 | with: 15 | path: 'package.json' 16 | prop_path: 'version' 17 | 18 | - name: get current time 19 | uses: 1466587594/get-current-time@v2 20 | id: current-time 21 | with: 22 | format: YYYY.MM.DD 23 | utcOffset: "+08:00" 24 | 25 | - name: action-zip 26 | uses: montudor/action-zip@v0.1.1 27 | with: 28 | args: zip -qq -r blinker-js-typescript-${{steps.version.outputs.prop}}.zip . -x .github/* -x .git/* 29 | 30 | - name: create json 31 | id: jsonfile 32 | uses: jsdaniell/create-json@1.1.2 33 | with: 34 | name: "nodejs.json" 35 | json: '{"img": "assets/sdk/nodejs.png", "text": "NodeJs (TypeScript)", "update": "${{ steps.current-time.outputs.formattedTime}}", "version": "${{steps.version.outputs.prop}}", "github": "https://github.com/blinker-iot/blinker-js", "document": "https://diandeng.tech/doc/javascript-support", "download": "sdk/blinker-js-typescript-${{steps.version.outputs.prop}}.zip" }' 36 | 37 | - name: upload zip 38 | uses: garygrossgarten/github-action-scp@release 39 | with: 40 | local: blinker-js-typescript-${{steps.version.outputs.prop}}.zip 41 | remote: ${{ secrets.REMOTE_PATH }}/blinker-js-typescript-${{steps.version.outputs.prop}}.zip 42 | host: ${{ secrets.SERVER_IP }} 43 | username: ubuntu 44 | password: ${{ secrets.SERVER_PASSWD }} 45 | recursive: true 46 | 47 | - name: upload json 48 | uses: garygrossgarten/github-action-scp@release 49 | with: 50 | local: nodejs.json 51 | remote: ${{ secrets.REMOTE_PATH }}/nodejs.json 52 | host: ${{ secrets.SERVER_IP }} 53 | username: ubuntu 54 | password: ${{ secrets.SERVER_PASSWD }} 55 | recursive: true 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | config.json 3 | config.ts 4 | .*.json 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 i3water 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /example/aligenie/example_aligenie_aircondition.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/aligenie/example_aligenie_aircondition.ts -------------------------------------------------------------------------------- /example/aligenie/example_aligenie_fan.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/aligenie/example_aligenie_fan.ts -------------------------------------------------------------------------------- /example/aligenie/example_aligenie_light.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { VA_TYPE, ALI_LIGHT_MODE, AliGenie } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let aliGenie = device.addVoiceAssistant(new AliGenie(VA_TYPE.LIGHT)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 适用于灯和插座 10 | aliGenie.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.set.pState) { 13 | case 'on': 14 | message.power('on').update(); 15 | break; 16 | case 'off': 17 | message.power('off').update(); 18 | break; 19 | default: 20 | break; 21 | } 22 | }) 23 | // 模式改变 适用于灯和插座 24 | aliGenie.modeChange.subscribe(message => { 25 | // console.log(message.data); 26 | switch (message.data.set.mode) { 27 | case ALI_LIGHT_MODE.READING: 28 | break; 29 | 30 | case ALI_LIGHT_MODE.MOVIE: 31 | 32 | break; 33 | case ALI_LIGHT_MODE.SLEEP: 34 | 35 | break; 36 | case ALI_LIGHT_MODE.HOLIDAY: 37 | 38 | break; 39 | case ALI_LIGHT_MODE.MUSIC: 40 | 41 | break; 42 | case ALI_LIGHT_MODE.COMMON: 43 | 44 | break; 45 | default: 46 | break; 47 | } 48 | message.mode(message.data.set.mode).update(); 49 | }) 50 | 51 | // 颜色改变 适用于灯 52 | // 支持的颜色:Red红色\Yellow黄色\Blue蓝色\Green绿色\White白色\Black黑色\Cyan青色\Purple紫色\Orange橙色 53 | aliGenie.colorChange.subscribe(message => { 54 | console.log(message.data.set.col); 55 | message.color(message.data.set.col).update(); 56 | }) 57 | 58 | // 色温改变 适用于灯 59 | aliGenie.colorTempChange.subscribe(message => { 60 | console.log(message.data); 61 | message.colorTemp(100).update(); 62 | }) 63 | 64 | // 亮度改变 适用于灯 65 | let brightness = 50; 66 | aliGenie.brightnessChange.subscribe(message => { 67 | // console.log(message.data.set); 68 | if (typeof message.data.set.bright != 'undefined') { 69 | brightness = Number(message.data.set.bright) 70 | } else if (typeof message.data.set.upBright != 'undefined') { 71 | brightness = brightness + Number(message.data.set.upBright) 72 | } else if (typeof message.data.set.downBright != 'undefined') { 73 | brightness = brightness - Number(message.data.set.downBright) 74 | } 75 | message.brightness(brightness).update(); 76 | }) 77 | 78 | aliGenie.stateQuery.subscribe(message => { 79 | console.log(message.data.get); 80 | message.power('on').mode(ALI_LIGHT_MODE.HOLIDAY).color('red').brightness(66).update(); 81 | }) 82 | 83 | device.dataRead.subscribe(message => { 84 | console.log('otherData:', message); 85 | }) 86 | 87 | device.builtinSwitch.change.subscribe(message => { 88 | console.log('builtinSwitch:', message); 89 | device.builtinSwitch.setState(turnSwitch()).update(); 90 | }) 91 | 92 | }) 93 | 94 | 95 | /* 96 | 以下为测试用函数 97 | */ 98 | let switchState = false 99 | function turnSwitch() { 100 | switchState = !switchState 101 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 102 | return switchState ? 'on' : 'off' 103 | } 104 | -------------------------------------------------------------------------------- /example/aligenie/example_aligenie_multi_outlet.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { AliGenie, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let aliGenie = device.addVoiceAssistant(new AliGenie(VA_TYPE.MULTI_OUTLET)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 10 | aliGenie.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | if (typeof message.data.set.num != 'undefined') { 13 | message.num(message.data.set.num) 14 | } 15 | switch (message.data.set.pState) { 16 | case 'on': 17 | message.power('on').update(); 18 | break; 19 | case 'off': 20 | message.power('off').update(); 21 | break; 22 | default: 23 | break; 24 | } 25 | }) 26 | 27 | device.dataRead.subscribe(message => { 28 | console.log('otherData:', message); 29 | }) 30 | 31 | device.builtinSwitch.change.subscribe(message => { 32 | console.log('builtinSwitch:', message); 33 | device.builtinSwitch.setState(turnSwitch()).update(); 34 | }) 35 | 36 | }) 37 | 38 | 39 | /* 40 | 以下为测试用函数 41 | */ 42 | 43 | let switchState = false 44 | function turnSwitch() { 45 | switchState = !switchState 46 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 47 | return switchState ? 'on' : 'off' 48 | } 49 | -------------------------------------------------------------------------------- /example/aligenie/example_aligenie_outlet.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { AliGenie, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let aliGenie = device.addVoiceAssistant(new AliGenie(VA_TYPE.OUTLET)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 10 | aliGenie.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.set.pState) { 13 | case 'on': 14 | message.power('on').update(); 15 | break; 16 | case 'off': 17 | message.power('off').update(); 18 | break; 19 | default: 20 | break; 21 | } 22 | }) 23 | 24 | aliGenie.stateQuery.subscribe(message => { 25 | message.power('on').update(); 26 | }) 27 | 28 | device.dataRead.subscribe(message => { 29 | console.log('otherData:', message); 30 | }) 31 | 32 | device.builtinSwitch.change.subscribe(message => { 33 | console.log('builtinSwitch:', message); 34 | device.builtinSwitch.setState(turnSwitch()).update(); 35 | }) 36 | 37 | }) 38 | 39 | 40 | /* 41 | 以下为测试用函数 42 | */ 43 | 44 | 45 | let switchState = false 46 | function turnSwitch() { 47 | switchState = !switchState 48 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 49 | return switchState ? 'on' : 'off' 50 | } 51 | -------------------------------------------------------------------------------- /example/aligenie/example_aligenie_sensor.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { AliGenie, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let aliGenie = device.addVoiceAssistant(new AliGenie(VA_TYPE.SENSOR)); 7 | 8 | device.ready().then(() => { 9 | // 查询传感器状态 10 | aliGenie.stateQuery.subscribe(message => { 11 | // console.log(message.data); 12 | message.humi(10).temp(10).pm25(10).update(); 13 | }) 14 | 15 | device.dataRead.subscribe(message => { 16 | console.log('otherData:', message); 17 | }) 18 | 19 | device.builtinSwitch.change.subscribe(message => { 20 | console.log('builtinSwitch:', message); 21 | device.builtinSwitch.setState(turnSwitch()).update(); 22 | }) 23 | 24 | }) 25 | 26 | 27 | /* 28 | 以下为测试用函数 29 | */ 30 | 31 | let switchState = false 32 | function turnSwitch() { 33 | switchState = !switchState 34 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 35 | return switchState ? 'on' : 'off' 36 | } 37 | -------------------------------------------------------------------------------- /example/dueros/example_dueros_aircondition.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/dueros/example_dueros_aircondition.ts -------------------------------------------------------------------------------- /example/dueros/example_dueros_fan.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/dueros/example_dueros_fan.ts -------------------------------------------------------------------------------- /example/dueros/example_dueros_light.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { DuerOS, VA_TYPE, DUER_LIGHT_MODE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let duerOS = device.addVoiceAssistant(new DuerOS(VA_TYPE.LIGHT)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 适用于灯和插座 10 | duerOS.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.set.pState) { 13 | case 'on': 14 | message.power('on').update(); 15 | break; 16 | case 'off': 17 | message.power('off').update(); 18 | break; 19 | default: 20 | break; 21 | } 22 | }) 23 | // 模式改变 适用于灯和插座 24 | duerOS.modeChange.subscribe(message => { 25 | // console.log(message.data); 26 | switch (message.data.set.mode) { 27 | case DUER_LIGHT_MODE.READING: 28 | break; 29 | case DUER_LIGHT_MODE.SLEEP: 30 | 31 | break; 32 | case DUER_LIGHT_MODE.ALARM: 33 | 34 | break; 35 | case DUER_LIGHT_MODE.NIGHT_LIGHT: 36 | 37 | break; 38 | case DUER_LIGHT_MODE.ROMANTIC: 39 | 40 | break; 41 | case DUER_LIGHT_MODE.SUNDOWN: 42 | 43 | break; 44 | case DUER_LIGHT_MODE.SUNRISE: 45 | 46 | break; 47 | case DUER_LIGHT_MODE.RELAX: 48 | 49 | break; 50 | case DUER_LIGHT_MODE.LIGHTING: 51 | 52 | break; 53 | case DUER_LIGHT_MODE.SUN: 54 | 55 | break; 56 | case DUER_LIGHT_MODE.STAR: 57 | 58 | break; 59 | case DUER_LIGHT_MODE.MOON: 60 | 61 | break; 62 | case DUER_LIGHT_MODE.ENERGY_SAVING: 63 | 64 | break; 65 | case DUER_LIGHT_MODE.JUDI: 66 | 67 | break; 68 | default: 69 | break; 70 | } 71 | message.mode(message.data.set.mode).update(); 72 | }) 73 | 74 | // 颜色改变 适用于灯 75 | duerOS.colorChange.subscribe(message => { 76 | console.log('RGB:',int2rgb(Number(message.data.set.col))); 77 | message.color(message.data.set.col).update(); 78 | }) 79 | 80 | // 色温改变 适用于灯 81 | // duerOS.colorTempChange.subscribe(message => { 82 | // console.log(message); 83 | // message.colorTemp(100).update(); 84 | // }) 85 | 86 | // 亮度改变 适用于灯 87 | let brightness = 50; 88 | duerOS.brightnessChange.subscribe(message => { 89 | // console.log(message.data.set); 90 | if (typeof message.data.set.bright != 'undefined') { 91 | brightness = Number(message.data.set.bright) 92 | } else if (typeof message.data.set.upBright != 'undefined') { 93 | brightness = brightness + Number(message.data.set.upBright) 94 | } else if (typeof message.data.set.downBright != 'undefined') { 95 | brightness = brightness - Number(message.data.set.downBright) 96 | } 97 | message.brightness(brightness).update(); 98 | }) 99 | 100 | device.dataRead.subscribe(message => { 101 | console.log('otherData:', message); 102 | }) 103 | 104 | device.builtinSwitch.change.subscribe(message => { 105 | console.log('builtinSwitch:', message); 106 | device.builtinSwitch.setState(turnSwitch()).update(); 107 | }) 108 | 109 | }) 110 | 111 | 112 | /* 113 | 以下为测试用函数 114 | */ 115 | 116 | function rgb2int(r: number, g: number, b: number) { 117 | return ((0xFF << 24) | (r << 16) | (g << 8) | b) 118 | } 119 | 120 | function int2rgb(value: number) { 121 | let r = (value & 0xff0000) >> 16; 122 | let g = (value & 0xff00) >> 8; 123 | let b = (value & 0xff); 124 | return [r, g, b] 125 | } 126 | 127 | let switchState = false 128 | function turnSwitch() { 129 | switchState = !switchState 130 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 131 | return switchState ? 'on' : 'off' 132 | } 133 | -------------------------------------------------------------------------------- /example/dueros/example_dueros_multi_outlet.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { DuerOS, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let duerOS = device.addVoiceAssistant(new DuerOS(VA_TYPE.MULTI_OUTLET)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 10 | duerOS.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | if (typeof message.data.set.num != 'undefined') { 13 | message.num(message.data.set.num) 14 | } 15 | switch (message.data.set.pState) { 16 | case 'on': 17 | message.power('on').update(); 18 | break; 19 | case 'off': 20 | message.power('off').update(); 21 | break; 22 | default: 23 | break; 24 | } 25 | }) 26 | 27 | device.dataRead.subscribe(message => { 28 | console.log('otherData:', message); 29 | }) 30 | 31 | device.builtinSwitch.change.subscribe(message => { 32 | console.log('builtinSwitch:', message); 33 | device.builtinSwitch.setState(turnSwitch()).update(); 34 | }) 35 | 36 | }) 37 | 38 | 39 | /* 40 | 以下为测试用函数 41 | */ 42 | 43 | function rgb2int(r: number, g: number, b: number) { 44 | return ((0xFF << 24) | (r << 16) | (g << 8) | b) 45 | } 46 | 47 | function int2rgb(value: number) { 48 | let r = (value & 0xff0000) >> 16; 49 | let g = (value & 0xff00) >> 8; 50 | let b = (value & 0xff); 51 | return [r, g, b] 52 | } 53 | 54 | let switchState = false 55 | function turnSwitch() { 56 | switchState = !switchState 57 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 58 | return switchState ? 'on' : 'off' 59 | } 60 | -------------------------------------------------------------------------------- /example/dueros/example_dueros_outlet.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { DuerOS, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let duerOS = device.addVoiceAssistant(new DuerOS(VA_TYPE.OUTLET)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 10 | duerOS.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.set.pState) { 13 | case 'on': 14 | message.power('on').update(); 15 | break; 16 | case 'off': 17 | message.power('off').update(); 18 | break; 19 | default: 20 | break; 21 | } 22 | }) 23 | 24 | device.dataRead.subscribe(message => { 25 | console.log('otherData:', message); 26 | }) 27 | 28 | device.builtinSwitch.change.subscribe(message => { 29 | console.log('builtinSwitch:', message); 30 | device.builtinSwitch.setState(turnSwitch()).update(); 31 | }) 32 | 33 | }) 34 | 35 | 36 | /* 37 | 以下为测试用函数 38 | */ 39 | 40 | function rgb2int(r: number, g: number, b: number) { 41 | return ((0xFF << 24) | (r << 16) | (g << 8) | b) 42 | } 43 | 44 | function int2rgb(value: number) { 45 | let r = (value & 0xff0000) >> 16; 46 | let g = (value & 0xff00) >> 8; 47 | let b = (value & 0xff); 48 | return [r, g, b] 49 | } 50 | 51 | let switchState = false 52 | function turnSwitch() { 53 | switchState = !switchState 54 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 55 | return switchState ? 'on' : 'off' 56 | } 57 | -------------------------------------------------------------------------------- /example/dueros/example_dueros_sensor.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { DuerOS, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let duerOS = device.addVoiceAssistant(new DuerOS(VA_TYPE.SENSOR)); 7 | 8 | device.ready().then(() => { 9 | // 查询传感器状态 10 | duerOS.stateQuery.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.get) { 13 | case 'aqi': 14 | message.aqi(10).update(); 15 | break; 16 | case 'pm25': 17 | message.pm25(10).update(); 18 | break; 19 | case 'pm10': 20 | message.pm10(10).update(); 21 | break; 22 | case 'co2': 23 | message.co2(10).update(); 24 | break; 25 | case 'humi': 26 | message.humi(10).update(); 27 | break; 28 | case 'temp': 29 | message.temp(10).update(); 30 | break; 31 | default: 32 | break; 33 | } 34 | }) 35 | 36 | device.dataRead.subscribe(message => { 37 | console.log('otherData:', message); 38 | }) 39 | 40 | device.builtinSwitch.change.subscribe(message => { 41 | console.log('builtinSwitch:', message); 42 | device.builtinSwitch.setState(turnSwitch()).update(); 43 | }) 44 | 45 | }) 46 | 47 | 48 | /* 49 | 以下为测试用函数 50 | */ 51 | 52 | let switchState = false 53 | function turnSwitch() { 54 | switchState = !switchState 55 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 56 | return switchState ? 'on' : 'off' 57 | } 58 | -------------------------------------------------------------------------------- /example/example.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../lib/blinker'; 2 | import { ButtonWidget, TextWidget, RangeWidget, NumberWidget, RGBWidget, JoystickWidget, ChartWidget, ImageWidget } from '../lib/widget'; 3 | 4 | let device = new BlinkerDevice(/*您申请到的Secret Key*/, // 设备authkey 5 | { 6 | protocol: 'mqtts', // 默认mqtts加密通信,可选配置mqtt\mqtts 7 | webSocket: true, // 默认开启websocket,会占用81端口,使用false可关闭 8 | }); 9 | 10 | // 注册组件 11 | let button1: ButtonWidget = device.addWidget(new ButtonWidget('btn-crf')); 12 | let button2: ButtonWidget = device.addWidget(new ButtonWidget('btn-b9g')); 13 | let text1: TextWidget = device.addWidget(new TextWidget('tex-pnd')); 14 | let range1: RangeWidget = device.addWidget(new RangeWidget('ran-i89')); 15 | let number1: NumberWidget = device.addWidget(new NumberWidget('num-lnw')); 16 | let colorPicker1: RGBWidget = device.addWidget(new RGBWidget('col-a9t')); 17 | let joystick1: JoystickWidget = device.addWidget(new JoystickWidget('joy-d32')); 18 | let chart1: ChartWidget = device.addWidget(new JoystickWidget('cha-t12')); 19 | let image1: ImageWidget = device.addWidget(new ImageWidget('img-abc')); 20 | 21 | 22 | device.ready().then(() => { 23 | 24 | device.dataRead.subscribe(message => { 25 | console.log('otherData:', message); 26 | }) 27 | 28 | device.heartbeat.subscribe(message => { 29 | console.log('heartbeat:', message); 30 | // device.builtinSwitch.setState(getSwitchState()).update(); 31 | range1.value(randomNumber()).color(randomColor()).update(); 32 | number1.value(randomNumber()).unit('米').text('长度').color(randomColor()).update(); 33 | button2.color(randomColor()).update(); 34 | button1.color(randomColor()).update(); 35 | colorPicker1.color(randomColor()).brightness(randomNumber(0, 255)).update() 36 | device.vibrate(); 37 | }) 38 | 39 | button1.listen().subscribe(message => { 40 | console.log('button1:', message.data); 41 | device.push('blinker push'); 42 | let state = turnSwitch() 43 | button1.turn(state).update(); 44 | text1.text('button1 actions').text1(message.data).update(); 45 | if (state == 'on') 46 | image1.show(1).update() 47 | else 48 | image1.show(0).update() 49 | }) 50 | 51 | button2.listen().subscribe(message => { 52 | console.log('button2:', message); 53 | // device.sms('短信功能测试'); 54 | text1.text('button2的动作').text1(message.data).update(); 55 | }) 56 | 57 | range1.listen().subscribe(message => { 58 | console.log('range:', message.data); 59 | }) 60 | 61 | colorPicker1.listen().subscribe(message => { 62 | console.log('color:', message.data); 63 | console.log('red:', message.data[0]); 64 | console.log('green:', message.data[1]); 65 | console.log('blue:', message.data[2]); 66 | console.log('brightness:', message.data[3]); 67 | }) 68 | 69 | joystick1.listen().subscribe(message => { 70 | console.log('joystick:', message.data); 71 | console.log('x:', message.data[0]); 72 | console.log('y:', message.data[1]); 73 | }) 74 | 75 | 76 | chart1.listen().subscribe(message => { 77 | console.log('chart:', message.data); 78 | }) 79 | 80 | // 云存储时序数据 仅限blinker broker 81 | setInterval(() => { 82 | device.saveTsData({ 83 | humi: randomNumber(), 84 | temp: randomNumber(), 85 | pm25: randomNumber(), 86 | pm10: randomNumber() 87 | }); 88 | }, 5000) 89 | 90 | }) 91 | 92 | 93 | 94 | 95 | /* 96 | 以下为测试用函数 97 | */ 98 | // 随机数 99 | function randomNumber(min = -10000, max = 10000) { 100 | let random = Math.random() 101 | return Math.floor((min + (max - min) * random)) 102 | } 103 | 104 | // 随机颜色 105 | function randomColor() { 106 | var r = Math.floor(Math.random() * 256); 107 | var g = Math.floor(Math.random() * 256); 108 | var b = Math.floor(Math.random() * 256); 109 | var color = '#' + r.toString(16) + g.toString(16) + b.toString(16); 110 | return color; 111 | } 112 | 113 | // 开关切换 114 | let switchState = false 115 | function turnSwitch() { 116 | switchState = !switchState 117 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 118 | return switchState ? 'on' : 'off' 119 | } 120 | -------------------------------------------------------------------------------- /example/example_hello.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../lib/blinker'; 2 | import { ButtonWidget, NumberWidget } from '../lib/widget'; 3 | 4 | let device = new BlinkerDevice(/*您申请到的Secret Key*/); 5 | 6 | // 注册组件 7 | let button1: ButtonWidget = device.addWidget(new ButtonWidget('btn-123')); 8 | let button2: ButtonWidget = device.addWidget(new ButtonWidget('btn-abc')); 9 | let number1: NumberWidget = device.addWidget(new NumberWidget('num-abc')); 10 | 11 | let num = 0; 12 | 13 | device.ready().then(() => { 14 | device.dataRead.subscribe(message => { 15 | console.log('otherData:', message); 16 | }) 17 | 18 | button1.listen().subscribe(message => { 19 | console.log('button1:', message.data); 20 | num++; 21 | number1.value(num).update(); 22 | }) 23 | 24 | button2.listen().subscribe(message => { 25 | console.log('button2:', message.data); 26 | // 其他控制代码 27 | }) 28 | 29 | device.sms('功能测试'); 30 | 31 | }) -------------------------------------------------------------------------------- /example/example_pro.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/example_pro.ts -------------------------------------------------------------------------------- /example/example_realtime.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../lib/blinker'; 2 | 3 | let device = new BlinkerDevice(/*您申请到的Secret Key*/); 4 | 5 | device.ready().then(() => { 6 | 7 | device.dataRead.subscribe(message => { 8 | console.log('otherData:', message); 9 | }) 10 | 11 | device.realtimeRequest.subscribe(keys => { 12 | console.log('realtimeRequest', keys); 13 | keys.forEach(key => { 14 | switch (key) { 15 | case 'humi': 16 | device.sendRtData('humi', randomNumber) 17 | break; 18 | case 'temp': 19 | device.sendRtData('temp', randomNumber) 20 | break; 21 | default: 22 | break; 23 | } 24 | }); 25 | }) 26 | }) 27 | 28 | 29 | 30 | 31 | /* 32 | 以下为测试用函数 33 | */ 34 | // 随机数 35 | function randomNumber(min = 0, max = 100) { 36 | let random = Math.random() 37 | return Math.floor((min + (max - min) * random)) 38 | } -------------------------------------------------------------------------------- /example/example_server.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../lib/blinker'; 2 | import { NumberWidget } from '../lib/widget'; 3 | import * as os from 'os'; 4 | import fs from 'fs'; 5 | import * as osUtils from 'os-utils'; 6 | import { diskinfo } from '@dropb/diskinfo'; 7 | 8 | let state: any = {} 9 | let platform = os.platform(); 10 | 11 | let device = new BlinkerDevice('',{ 12 | webSocket:false 13 | }); 14 | 15 | let number1: NumberWidget = device.addWidget(new NumberWidget('cpu')); 16 | let number2: NumberWidget = device.addWidget(new NumberWidget('mem')); 17 | 18 | 19 | device.ready().then(() => { 20 | 21 | device.heartbeat.subscribe(message => { 22 | console.log('heartbeat:', message); 23 | number1.value(state.cpuUsage * 100).update(); 24 | number2.value(state.memUsage * 100).update(); 25 | device.builtinSwitch.setState('on').update(); 26 | }) 27 | 28 | setInterval(async () => { 29 | await getInfo() 30 | console.log('state', state); 31 | device.saveTsData({ 32 | cpu: state.cpuUsage, 33 | mem: state.memUsage 34 | }); 35 | }, 5000) 36 | 37 | 38 | async function getInfo() { 39 | if (platform == 'linux') { 40 | let meminfo = fs.readFileSync('/proc/meminfo', 'utf8'); 41 | let itemArray = meminfo.split('\n'); 42 | let memAvailable = parseInt(itemArray[2].replace(/[^0-9]/g, "")); 43 | let memTotle = parseInt(itemArray[0].replace(/[^0-9]/g, "")); 44 | let memUsage = 1 - (memAvailable / memTotle); 45 | addData('memAvailable', memAvailable); 46 | addData('memTotle', memTotle); 47 | addData('memUsage', memUsage); 48 | } else { 49 | addData('memAvailable', osUtils.freemem()); 50 | addData('memTotle', osUtils.totalmem()); 51 | addData('memUsage', osUtils.freememPercentage()); 52 | } 53 | addData('cpuUsage', await getCpuUsage()) 54 | addData('cpuFree', await getCpuFree()) 55 | addData('cpuCount', osUtils.cpuCount()) 56 | addData('average', [osUtils.loadavg(1), osUtils.loadavg(5), osUtils.loadavg(15)]) 57 | await getDiskinfo() 58 | } 59 | }) 60 | 61 | async function getDiskinfo() { 62 | let diskData = await diskinfo('./'); 63 | addData('diskTotle', diskData.size) 64 | addData('diskUsed', diskData.used) 65 | addData('diskAvailable', diskData.avail) 66 | } 67 | 68 | function getCpuUsage() { 69 | return new Promise((reslove, reject) => { 70 | osUtils.cpuUsage(value => { 71 | reslove(value) 72 | }) 73 | }) 74 | } 75 | 76 | function getCpuFree() { 77 | return new Promise((reslove, reject) => { 78 | osUtils.cpuFree(value => { 79 | reslove(value) 80 | }) 81 | }) 82 | } 83 | 84 | function addData(key, value) { 85 | state[key] = value 86 | } -------------------------------------------------------------------------------- /example/miot/example_miot_aircondition.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/miot/example_miot_aircondition.ts -------------------------------------------------------------------------------- /example/miot/example_miot_fan.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/example/miot/example_miot_fan.ts -------------------------------------------------------------------------------- /example/miot/example_miot_light.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { Miot, VA_TYPE, MI_LIGHT_MODE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let miot = device.addVoiceAssistant(new Miot(VA_TYPE.LIGHT)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 适用于灯和插座 10 | miot.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.set.pState) { 13 | case "true": 14 | message.power("on").update(); 15 | break; 16 | case "false": 17 | message.power("off").update(); 18 | break; 19 | default: 20 | break; 21 | } 22 | }) 23 | // 模式改变 适用于灯和插座 24 | miot.modeChange.subscribe(message => { 25 | // console.log(message.data); 26 | switch (message.data.set.mode) { 27 | case MI_LIGHT_MODE.DAY: 28 | 29 | break; 30 | case MI_LIGHT_MODE.NIGHT: 31 | 32 | break; 33 | case MI_LIGHT_MODE.COLOR: 34 | 35 | break; 36 | case MI_LIGHT_MODE.WARMTH: 37 | 38 | break; 39 | case MI_LIGHT_MODE.TV: 40 | 41 | break; 42 | case MI_LIGHT_MODE.READING: 43 | 44 | break; 45 | case MI_LIGHT_MODE.COMPUTER: 46 | 47 | break; 48 | default: 49 | break; 50 | } 51 | message.mode(message.data.set.mode).update(); 52 | }) 53 | 54 | // 颜色改变 适用于灯 55 | miot.colorChange.subscribe(message => { 56 | console.log('RGB:', int2rgb(Number(message.data.set.col))); 57 | message.color(message.data.set.col).update(); 58 | }) 59 | 60 | // 色温改变 适用于灯 61 | miot.colorTempChange.subscribe(message => { 62 | console.log(message); 63 | message.colorTemp(255).update(); 64 | }) 65 | 66 | // 亮度改变 适用于灯 67 | let brightness = 50; 68 | miot.brightnessChange.subscribe(message => { 69 | // console.log(message.data.set); 70 | if (typeof message.data.set.bright != 'undefined') { 71 | brightness = Number(message.data.set.bright) 72 | } else if (typeof message.data.set.upBright != 'undefined') { 73 | brightness = brightness + Number(message.data.set.upBright) 74 | } else if (typeof message.data.set.downBright != 'undefined') { 75 | brightness = brightness - Number(message.data.set.downBright) 76 | } 77 | message.brightness(brightness).update(); 78 | }) 79 | 80 | miot.stateQuery.subscribe(message => { 81 | // console.log(message.data); 82 | message.power('on').update() 83 | }) 84 | 85 | device.dataRead.subscribe(message => { 86 | console.log('otherData:', message); 87 | }) 88 | 89 | device.builtinSwitch.change.subscribe(message => { 90 | console.log('builtinSwitch:', message); 91 | device.builtinSwitch.setState(turnSwitch()).update(); 92 | }) 93 | 94 | }) 95 | 96 | 97 | /* 98 | 以下为测试用函数 99 | */ 100 | 101 | function rgb2int(r: number, g: number, b: number) { 102 | return ((0xFF << 24) | (r << 16) | (g << 8) | b) 103 | } 104 | 105 | function int2rgb(value: number) { 106 | let r = (value & 0xff0000) >> 16; 107 | let g = (value & 0xff00) >> 8; 108 | let b = (value & 0xff); 109 | return [r, g, b] 110 | } 111 | 112 | let switchState = false 113 | function turnSwitch() { 114 | switchState = !switchState 115 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 116 | return switchState ? 'on' : 'off' 117 | } 118 | -------------------------------------------------------------------------------- /example/miot/example_miot_multi_outlet.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { Miot, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let miot = device.addVoiceAssistant(new Miot(VA_TYPE.MULTI_OUTLET)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 10 | miot.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | if (typeof message.data.set.num != 'undefined') { 13 | message.num(message.data.set.num) 14 | } 15 | switch (message.data.set.pState) { 16 | case 'true': 17 | message.power('on').update(); 18 | break; 19 | case 'false': 20 | message.power('off').update(); 21 | break; 22 | default: 23 | break; 24 | } 25 | }) 26 | 27 | device.dataRead.subscribe(message => { 28 | console.log('otherData:', message); 29 | }) 30 | 31 | device.builtinSwitch.change.subscribe(message => { 32 | console.log('builtinSwitch:', message); 33 | device.builtinSwitch.setState(turnSwitch()).update(); 34 | }) 35 | 36 | }) 37 | 38 | 39 | /* 40 | 以下为测试用函数 41 | */ 42 | 43 | function rgb2int(r: number, g: number, b: number) { 44 | return ((0xFF << 24) | (r << 16) | (g << 8) | b) 45 | } 46 | 47 | function int2rgb(value: number) { 48 | let r = (value & 0xff0000) >> 16; 49 | let g = (value & 0xff00) >> 8; 50 | let b = (value & 0xff); 51 | return [r, g, b] 52 | } 53 | 54 | let switchState = false 55 | function turnSwitch() { 56 | switchState = !switchState 57 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 58 | return switchState ? 'on' : 'off' 59 | } 60 | -------------------------------------------------------------------------------- /example/miot/example_miot_outlet.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { Miot, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let miot = device.addVoiceAssistant(new Miot(VA_TYPE.OUTLET)); 7 | 8 | device.ready().then(() => { 9 | // 电源状态改变 10 | miot.powerChange.subscribe(message => { 11 | // console.log(message.data); 12 | switch (message.data.set.pState) { 13 | case 'true': 14 | message.power('on').update(); 15 | break; 16 | case 'false': 17 | message.power('off').update(); 18 | break; 19 | default: 20 | break; 21 | } 22 | }) 23 | 24 | device.dataRead.subscribe(message => { 25 | console.log('otherData:', message); 26 | }) 27 | 28 | device.builtinSwitch.change.subscribe(message => { 29 | console.log('builtinSwitch:', message); 30 | device.builtinSwitch.setState(turnSwitch()).update(); 31 | }) 32 | 33 | }) 34 | 35 | 36 | /* 37 | 以下为测试用函数 38 | */ 39 | 40 | function rgb2int(r: number, g: number, b: number) { 41 | return ((0xFF << 24) | (r << 16) | (g << 8) | b) 42 | } 43 | 44 | function int2rgb(value: number) { 45 | let r = (value & 0xff0000) >> 16; 46 | let g = (value & 0xff00) >> 8; 47 | let b = (value & 0xff); 48 | return [r, g, b] 49 | } 50 | 51 | let switchState = false 52 | function turnSwitch() { 53 | switchState = !switchState 54 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 55 | return switchState ? 'on' : 'off' 56 | } 57 | -------------------------------------------------------------------------------- /example/miot/example_miot_sensor.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from '../../lib/blinker'; 2 | import { Miot, VA_TYPE } from '../../lib/voice-assistant'; 3 | 4 | let device = new BlinkerDevice(''); 5 | 6 | let miot = device.addVoiceAssistant(new Miot(VA_TYPE.SENSOR)); 7 | 8 | device.ready().then(() => { 9 | // 查询传感器状态 10 | miot.stateQuery.subscribe(message => { 11 | // console.log(message.data); 12 | message.aqi(10).pm25(10).co2(10).humi(10).temp(10).update(); 13 | }) 14 | 15 | device.dataRead.subscribe(message => { 16 | console.log('otherData:', message); 17 | }) 18 | 19 | device.builtinSwitch.change.subscribe(message => { 20 | console.log('builtinSwitch:', message); 21 | device.builtinSwitch.setState(turnSwitch()).update(); 22 | }) 23 | 24 | }) 25 | 26 | 27 | /* 28 | 以下为测试用函数 29 | */ 30 | 31 | let switchState = false 32 | function turnSwitch() { 33 | switchState = !switchState 34 | device.log("切换设备状态为" + (switchState ? 'on' : 'off')) 35 | return switchState ? 'on' : 'off' 36 | } 37 | -------------------------------------------------------------------------------- /example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@dropb/diskinfo": { 8 | "version": "2.0.0", 9 | "resolved": "https://registry.npm.taobao.org/@dropb/diskinfo/download/@dropb/diskinfo-2.0.0.tgz", 10 | "integrity": "sha1-uV3vxwQptbkH5ErAl8eeBhFTkGs=" 11 | }, 12 | "os-utils": { 13 | "version": "0.0.14", 14 | "resolved": "https://registry.npm.taobao.org/os-utils/download/os-utils-0.0.14.tgz", 15 | "integrity": "sha1-KeURaXsZgrjGJ3Ihdf45eX72QVY=" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dropb/diskinfo": "^2.0.0", 13 | "os-utils": "0.0.14" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## example_server.ts 4 | 这是一个用于服务器状态采集和监控的程序。 5 | 6 | Layouter2界面配置: 7 | ``` 8 | {¨config¨{¨headerColor¨¨transparent¨¨headerStyle¨¨light¨¨background¨{¨img¨´´}}¨dashboard¨|{¨type¨¨cha¨¨bg¨É¨sty¨¨line¨¨clr¨¨#EA0909¨¨sty1¨ßC¨clr1¨¨#076EEF¨¨sty2¨ßC¨clr2¨¨#389BEE¨¨cols¨Ñ¨rows¨Ì¨key¨¨cha-t12¨´x´É´y´¤B¨speech¨|÷¨key1¨¨mem¨¨t0¨¨CPU¨¨t1¨¨RAM¨¨lstyle¨É¨key0¨¨cpu¨¨key2¨´´}{ß8¨tex¨ßS¨everything is ok¨ßU¨文本2¨¨size¨¤EßA˨ico¨¨fad fa-siren¨ßLÍßMÊßN¨tex-ivl¨´x´É´y´¤AßP|÷ßWÊßD¨#595959¨}{ß8ß6ßA˨list¨|¦¨url¨‡¨https://diandeng.tech/assets/devices/nuc.png¨´´þþþ—÷ß6ÉßLÍßMÌßN¨img-pui¨´x´Ë´y´ÍßP|÷ßWË}{ß8¨num¨ßS¨CPU利用率¨ße¨fal fa-question¨ßDßH¨min¨É¨max¨¢1c¨uni¨´%´ßAÉßLÍßMÍßNßY´x´É´y´ÉßP|÷ßWÍ}{ß8ßmßS¨内存利用率¨ßeßoßDßKßpÉßqº0ßr´%´ßAÉßLÍßMÍßNßR´x´Í´y´ÉßP|÷ßWÍ}{ß8¨btn¨ße¨fal fa-sync¨¨mode¨ÉßS¨reboot¨ßUßcßAÌßLÊßMÊßN¨reset¨´x´Ð´y´¤AßP|÷ßWÍ}÷¨actions¨|¦¨cmd¨¦¨switch¨‡¨text¨‡´on´¨打开?name¨¨off¨¨关闭?name¨—÷¨triggers¨|÷} 9 | ``` -------------------------------------------------------------------------------- /lib/blinker-cli.ts: -------------------------------------------------------------------------------- 1 | import getMAC from 'getmac' 2 | import * as qrcode from 'qrcode-terminal' 3 | import axios from 'axios'; 4 | import mqtt from 'mqtt'; 5 | 6 | const API_Register = ""; 7 | const API_RegisterCheck = "" 8 | 9 | 10 | const { program } = require('commander'); 11 | 12 | program 13 | .option('--no-sauce', 'Remove sauce') 14 | .option('--cheese ', 'cheese flavour', 'mozzarella') 15 | .option('--no-cheese', 'plain with no cheese') 16 | 17 | 18 | program 19 | .command('start ', 'start named service') 20 | .command('stop [service]', 'stop named service, or all if no name supplied') 21 | .parse(process.argv); 22 | 23 | 24 | program.on('--help', () => { 25 | console.log(''); 26 | console.log('Example call:'); 27 | console.log(' $ custom-help --help'); 28 | }); 29 | 30 | function createConfig() { 31 | 32 | } 33 | 34 | function readConfig() { 35 | 36 | } 37 | 38 | function register() { 39 | axios.get(API_Register).then(resp => { 40 | if (resp.data.message == 1000) { 41 | showQrcode(resp.data.detail); 42 | let times = 0; 43 | let timer = setInterval(() => { 44 | times++; 45 | if (times > 10) { 46 | clearInterval(timer); 47 | console.log("设备注册超时"); 48 | return 49 | } 50 | if (checkRegister()) { 51 | clearInterval(timer); 52 | console.log("设备注册成功"); 53 | } 54 | }, 6000) 55 | } 56 | }) 57 | } 58 | 59 | function checkRegister() { 60 | return axios.get(API_RegisterCheck).then(resp => { 61 | if (resp.data.message == 1000) { 62 | return true 63 | } else 64 | return false 65 | }) 66 | } 67 | 68 | function getMacAddr() { 69 | return getMAC().replace(/:/g, '') 70 | } 71 | 72 | function showQrcode(str: string) { 73 | qrcode.setErrorLevel('Q'); 74 | qrcode.generate(str); 75 | } 76 | 77 | 78 | console.log(getMacAddr()); 79 | showQrcode(getMacAddr()) -------------------------------------------------------------------------------- /lib/blinker-pro.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from "./blinker"; 2 | 3 | 4 | export class BlinkerProDevice extends BlinkerDevice { 5 | 6 | constructor(type, key) { 7 | super(type, key) 8 | } 9 | } -------------------------------------------------------------------------------- /lib/blinker.ts: -------------------------------------------------------------------------------- 1 | import * as mqtt from 'mqtt'; 2 | import axios from 'axios'; 3 | import { BehaviorSubject, Subject } from 'rxjs'; 4 | import { Widget } from './widget'; 5 | import bonjour from 'bonjour'; 6 | import * as WebSocket from 'ws'; 7 | import * as schedule from 'node-schedule'; 8 | import * as pauseable from 'pauseable'; 9 | import { tip, warn, error, timerLog, mqttLog } from './debug' 10 | import getMAC from 'getmac' 11 | 12 | import { VoiceAssistant } from './voice-assistant'; 13 | 14 | export interface Message { 15 | fromDevice?: string, 16 | data: any 17 | } 18 | 19 | export interface authOption { 20 | "authKey"?: string, 21 | "version"?: string, 22 | "protocol"?: string, 23 | "webSocket"?: boolean, 24 | "sourceCheck"?: boolean 25 | } 26 | 27 | export class BlinkerDevice { 28 | 29 | options: authOption = { 30 | version: '1.0', 31 | protocol: 'mqtts', 32 | webSocket: true, 33 | sourceCheck: true, 34 | }; 35 | 36 | mqttClient: mqtt.MqttClient; 37 | 38 | wsServer; 39 | ws; 40 | 41 | config: { 42 | broker: string, 43 | deviceName: string, 44 | host: string, 45 | iotId: string, 46 | iotToken: string, 47 | port: string, 48 | productKey: string, 49 | uuid: string, 50 | authKey?: string 51 | }; 52 | 53 | subTopic; 54 | pubTopic; 55 | 56 | deviceName; 57 | 58 | targetDevice; 59 | 60 | dataRead = new Subject() 61 | 62 | heartbeat = new Subject() 63 | 64 | realtimeRequest = new Subject() 65 | 66 | // builtinSwitch = new BuiltinSwitch(); 67 | 68 | configReady = new BehaviorSubject(false) 69 | 70 | widgetKeyList = [] 71 | widgetDict = {} 72 | 73 | sharedUserList = [] 74 | 75 | private tempData; 76 | private tempDataPath; 77 | 78 | constructor(authkey = '', options?: authOption) { 79 | if (authkey == '') { 80 | authkey = loadJsonFile('.auth.json').authkey 81 | console.log(authkey); 82 | } 83 | for (const key in options) { 84 | this.options[key] = options[key] 85 | } 86 | this.options['authKey'] = authkey 87 | this.init(authkey) 88 | } 89 | 90 | init(authkey) { 91 | axios.get(API.AUTH, { params: this.options }).then(async resp => { 92 | console.log(resp.data); 93 | if (resp.data.message != 1000) { 94 | error(resp.data); 95 | return 96 | } 97 | this.config = resp.data.detail 98 | this.config['authKey'] = authkey 99 | if (this.config.broker == 'blinker') { 100 | mqttLog('broker:blinker') 101 | this.initBroker_Blinker() 102 | } 103 | await this.connectBroker() 104 | // this.addWidget(this.builtinSwitch) 105 | this.getShareInfo() 106 | this.initLocalService() 107 | // 加载暂存数据 108 | this.tempDataPath = `.${this.config.deviceName}.json` 109 | this.tempData = loadJsonFile(this.tempDataPath) 110 | this.loadTimingTask() 111 | this.configReady.next(true) 112 | }) 113 | 114 | } 115 | 116 | ready(): Promise { 117 | return new Promise((resolve, reject) => { 118 | let subscription = this.configReady.subscribe(state => { 119 | if (state) { 120 | subscription.unsubscribe 121 | resolve(true) 122 | } 123 | }) 124 | }) 125 | } 126 | 127 | // 本地服务:MDNS\WS SERVER 128 | initLocalService() { 129 | if (!this.options.webSocket) return 130 | // 开启mdns服务 131 | bonjour().publish({ 132 | name: this.config.deviceName, 133 | type: 'blinker', 134 | host: this.config.deviceName + '.local', 135 | port: 81, 136 | txt: { mac: getMAC().replace(/:/g, '').toUpperCase() } 137 | }) 138 | this.wsServer = new WebSocket.Server({ port: 81 }); 139 | this.wsServer.on('connection', ws => { 140 | tip('local connection'); 141 | ws.send(`{"state":"connected"}`) 142 | this.ws = ws 143 | this.ws.on('message', (message) => { 144 | let data; 145 | let fromDevice; 146 | try { 147 | data = JSON.parse(message) 148 | this.processData(data, fromDevice) 149 | } catch (error) { 150 | console.log(error); 151 | console.log(message); 152 | } 153 | }); 154 | }) 155 | this.wsServer.on('close', () => { 156 | console.log('ws client disconnect'); 157 | }) 158 | } 159 | 160 | getShareInfo() { 161 | axios.get(API.SHARE + `?deviceName=${this.config.deviceName}&key=${this.config.authKey}`).then(resp => { 162 | if (resp.data.message != 1000) { 163 | error(resp.data); 164 | return 165 | } 166 | this.sharedUserList = resp.data.detail.users 167 | }) 168 | } 169 | 170 | initBroker_Blinker() { 171 | this.subTopic = `/device/${this.config.deviceName}/r`; 172 | this.pubTopic = `/device/${this.config.deviceName}/s`; 173 | this.targetDevice = this.config.uuid; 174 | } 175 | 176 | connectBroker() { 177 | return new Promise((resolve, reject) => { 178 | this.mqttClient = mqtt.connect(this.config.host + ':' + this.config.port, { 179 | clientId: this.config.deviceName, 180 | username: this.config.iotId, 181 | password: this.config.iotToken 182 | }); 183 | 184 | this.mqttClient.on('connect', () => { 185 | mqttLog('blinker connected'); 186 | this.mqttClient.subscribe(this.subTopic); 187 | this.startHeartbeat2cloud(); 188 | resolve(true) 189 | }) 190 | 191 | this.mqttClient.on('message', (topic, message) => { 192 | if (topic == this.subTopic) { 193 | let data; 194 | let fromDevice; 195 | 196 | try { 197 | let messageString = u8aToString(message) 198 | // console.log(topic); 199 | console.log(messageString); 200 | 201 | let messageObject = JSON.parse(messageString) 202 | fromDevice = messageObject.fromDevice 203 | data = messageObject.data 204 | this.targetDevice = fromDevice 205 | } catch (error) { 206 | console.log(error); 207 | } 208 | // 权限检查 209 | if (this.options.sourceCheck) 210 | if (this.sharedUserList.indexOf(fromDevice) < 0 && fromDevice != this.config.uuid) return 211 | this.processData(data, fromDevice) 212 | } 213 | }) 214 | 215 | this.mqttClient.on('close', (err) => { 216 | mqttLog('blinker close'); 217 | this.stopHeartbeat2cloud() 218 | }) 219 | 220 | this.mqttClient.on('error', (err) => { 221 | mqttLog(err); 222 | }) 223 | }) 224 | } 225 | 226 | // 云端心跳 227 | timer_heartbeat2cloud; 228 | startHeartbeat2cloud() { 229 | axios.get(API.HEARTBEAT + `?deviceName=${this.config.deviceName}&key=${this.config.authKey}&heartbeat=600`) 230 | this.timer_heartbeat2cloud = setInterval(() => { 231 | axios.get(API.HEARTBEAT + `?deviceName=${this.config.deviceName}&key=${this.config.authKey}&heartbeat=600`) 232 | }, 599000) 233 | } 234 | 235 | stopHeartbeat2cloud() { 236 | clearInterval(this.timer_heartbeat2cloud) 237 | } 238 | 239 | processData(data, fromDevice = this.targetDevice) { 240 | if (typeof data == 'string' || typeof data == 'number') { 241 | this.dataRead.next({ 242 | fromDevice: fromDevice, 243 | data: data 244 | }) 245 | return 246 | } 247 | if (typeof data['get'] != 'undefined') { 248 | if (data['get'] == 'state') { 249 | this.heartbeat.next(data); 250 | this.sendMessage(`{"state":"online"}`) 251 | } else if (data['get'] == 'timing') { 252 | // tip('反馈定时任务') 253 | // console.log(this.getTimingData()); 254 | this.sendMessage(this.getTimingData()) 255 | } else if (data['get'] == 'countdown') { 256 | // tip('反馈倒计时任务') 257 | this.sendMessage(this.getCountdownData()) 258 | } 259 | } else if (typeof data['set'] != 'undefined') { 260 | if (typeof data['set']['timing'] != 'undefined') { 261 | if (typeof data['set']['timing'][0]["dlt"] != 'undefined') { 262 | this.delTimingData(data['set']['timing'][0]["dlt"]) 263 | } else { 264 | this.setTimingData(data['set']['timing']); 265 | } 266 | this.sendMessage(this.getTimingData()) 267 | } else if (typeof data['set']['countdown'] != 'undefined') { 268 | // tip('设定倒计时任务') 269 | this.setCountdownData(data['set']['countdown']); 270 | this.sendMessage(this.getCountdownData()) 271 | } 272 | } else if (typeof data['rt'] != 'undefined') { 273 | this.realtimeRequest.next(data['rt']); 274 | } else { 275 | // tip(JSON.stringify(data)); 276 | let otherData = {} 277 | for (const key in data) { 278 | // 处理组件数据 279 | if (this.widgetKeyList.indexOf(key) > -1) { 280 | let widget: Widget = this.widgetDict[key] 281 | widget.change.next({ 282 | fromDevice: fromDevice, 283 | data: data[key], 284 | }) 285 | } else { 286 | let temp = {}; 287 | temp[key] = data[key] 288 | otherData = Object.assign(otherData, temp) 289 | } 290 | } 291 | if (JSON.stringify(otherData) != '{}') 292 | this.dataRead.next({ 293 | fromDevice: fromDevice, 294 | data: otherData 295 | }) 296 | } 297 | } 298 | 299 | sendTimers = {}; 300 | 301 | messageDataCache = {} 302 | 303 | sendMessage(message: string | Object, toDevice = this.targetDevice) { 304 | // console.log(message); 305 | let sendMessage: string; 306 | if (typeof message == 'object') sendMessage = JSON.stringify(message) 307 | else sendMessage = message 308 | if (isJson(sendMessage)) { 309 | if (typeof this.messageDataCache[toDevice] == 'undefined') this.messageDataCache[toDevice] = ''; 310 | let ob = this.messageDataCache[toDevice] == '' ? {} : JSON.parse(this.messageDataCache[toDevice]); 311 | let ob2 = JSON.parse(sendMessage) 312 | this.messageDataCache[toDevice] = JSON.stringify(Object.assign(ob, ob2)) 313 | if (typeof this.sendTimers[toDevice] != 'undefined') clearTimeout(this.sendTimers[toDevice]); 314 | //检查设备是否是本地设备,是否已连接 315 | // let deviceInLocal = false; 316 | // if (this.islocalDevice(device)) { 317 | // if (this.lanDeviceList[toDevice].state == 'connected') 318 | // deviceInLocal = true 319 | // } 320 | this.sendTimers[toDevice] = setTimeout(() => { 321 | this.mqttClient.publish(this.pubTopic, formatMess2Device(this.config.deviceName, toDevice, this.messageDataCache[toDevice])) 322 | this.messageDataCache[toDevice] = ''; 323 | delete this.sendTimers[toDevice]; 324 | }, 100) 325 | } else { 326 | console.log('not json'); 327 | if (!isNumber(sendMessage)) sendMessage = `"${sendMessage}"` 328 | this.mqttClient.publish(this.pubTopic, formatMess2Device(this.config.deviceName, toDevice, sendMessage)) 329 | } 330 | } 331 | 332 | // toDevice 333 | sendMessage2Device(message, toDevice = this.targetDevice) { 334 | this.sendMessage(message, toDevice) 335 | } 336 | // toGrounp 337 | sendMessage2Grounp(message, toGrounp) { 338 | 339 | } 340 | // toStorage 341 | storageCache = []; 342 | tsDataTimer; 343 | saveTsData(data: any) { 344 | if (this.config.broker != 'blinker') { 345 | warn('saveTsData:仅可用于blinker broker'); 346 | return 347 | } 348 | // console.log(JSON.stringify(this.storageCache)); 349 | clearTimeout(this.tsDataTimer); 350 | let currentData = Object.assign({ date: Math.floor((new Date).getTime() / 1000) }, data) 351 | if (this.storageCache.length == 0 || currentData.date - this.storageCache[this.storageCache.length - 1].date >= 5) { 352 | this.storageCache.push(currentData) 353 | } 354 | if (this.storageCache[this.storageCache.length - 1].date - this.storageCache[0].date >= 60 || this.storageCache.length >= 12) { 355 | this.sendTsData() 356 | } else 357 | this.tsDataTimer = setTimeout(() => { 358 | this.sendTsData() 359 | }, 60000); 360 | } 361 | 362 | private sendTsData() { 363 | let data = JSON.stringify(this.storageCache) 364 | if (data.length > 10240) { 365 | error('saveTsData:单次上传数据长度超过10Kb,请减少数据内容,或降低数据上传频率'); 366 | return 367 | } 368 | tip('sendTsData') 369 | this.mqttClient.publish(this.pubTopic, formatMess2StorageTs(this.config.deviceName, 'ts', data)) 370 | this.storageCache = [] 371 | } 372 | objectDataTimer 373 | saveObjectData(data: any) { 374 | if (this.config.broker != 'blinker') { 375 | warn('saveObjectData:仅可用于blinker broker') 376 | return 377 | } 378 | let dataCache; 379 | if (typeof data == 'string') { 380 | if (!isJson(data)) { 381 | error(`saveObjectData:数据不是对象`) 382 | return 383 | } else { 384 | dataCache = JSON.parse(data) 385 | } 386 | } else { 387 | dataCache = data 388 | } 389 | clearTimeout(this.objectDataTimer); 390 | this.objectDataTimer = setTimeout(() => { 391 | tip('saveObjectData') 392 | this.mqttClient.publish(this.pubTopic, formatMess2StorageOt(this.config.deviceName, 'ot', JSON.stringify(dataCache))) 393 | }, 5000); 394 | } 395 | textDataTimer 396 | saveTextData(data: string) { 397 | if (this.config.broker != 'blinker') { 398 | warn('saveTextData:仅可用于blinker broker'); 399 | return 400 | } 401 | if (data.length > 1024) { 402 | error('saveTextData:数据长度超过1024字节'); 403 | return 404 | } 405 | clearTimeout(this.textDataTimer); 406 | this.textDataTimer = setTimeout(() => { 407 | tip('saveTextData') 408 | this.mqttClient.publish(this.pubTopic, formatMess2StorageTt(this.config.deviceName, 'tt', data)) 409 | }, 5000); 410 | } 411 | 412 | addWidget(widget: Widget | any): Widget | any { 413 | widget.device = this; 414 | this.widgetKeyList.push(widget.key); 415 | this.widgetDict[widget.key] = widget; 416 | return widget 417 | } 418 | 419 | addVoiceAssistant(voiceAssistant: VoiceAssistant) { 420 | this.configReady.subscribe(state => { 421 | if (state) { 422 | let params = Object.assign({ token: this.config.iotToken }, voiceAssistant.vaType) 423 | axios.post(API.VOICE_ASSISTANT, params).then(resp => { 424 | // console.log(resp); 425 | voiceAssistant.device = this; 426 | voiceAssistant.listen(); 427 | }) 428 | } 429 | }) 430 | return voiceAssistant 431 | } 432 | 433 | vibrate(time = 500) { 434 | this.sendMessage(`{"vibrate":${time}}`) 435 | } 436 | 437 | sendSmsTimeout = true; 438 | sms(text: string) { 439 | if (!this.sendSmsTimeout) { 440 | warn('sendSms:too frequent requests') 441 | return 442 | } 443 | this.sendSmsTimeout = false; 444 | setTimeout(() => { 445 | this.sendSmsTimeout = true 446 | }, 60000); 447 | axios.post(API.SMS, { 448 | 'deviceName': this.config.deviceName, 449 | 'key': this.config.authKey, 450 | 'msg': text 451 | }).then(resp => { 452 | if (resp.data.message != 1000) 453 | error(resp.data); 454 | }) 455 | } 456 | 457 | wechat(title: string, state: string, text: string) { 458 | axios.post(API.WECHAT, { 459 | 'deviceName': this.config.deviceName, 460 | 'key': this.config.authKey, 461 | 'title': title, 462 | 'state': state, 463 | 'msg': text 464 | }).then(resp => { 465 | if (resp.data.message != 1000) 466 | error(resp.data); 467 | }) 468 | } 469 | 470 | push(text: string) { 471 | axios.post(API.PUSH, { 472 | 'deviceName': this.config.deviceName, 473 | 'key': this.config.authKey, 474 | 'msg': text 475 | }).then(resp => { 476 | if (resp.data.message != 1000) 477 | error(resp.data); 478 | }) 479 | } 480 | 481 | notice(message) { 482 | this.sendMessage(`{"notice":"${message}"}`) 483 | } 484 | 485 | // 定时功能 486 | timingTasks = []; 487 | private setTimingData(data) { 488 | timerLog('set timing task') 489 | if (typeof this.tempData['timing'] == 'undefined') this.tempData['timing'] = [] 490 | this.tempData['timing'][data[0].task] = data[0] 491 | this.addTimingTask(data[0]) 492 | } 493 | 494 | private getTimingData() { 495 | if (typeof this.tempData['timing'] == 'undefined') 496 | return { timing: [] } 497 | else 498 | return { timing: this.tempData['timing'] } 499 | } 500 | 501 | private delTimingData(taskId) { 502 | this.delTimingTask(taskId) 503 | arrayRemove(this.tempData['timing'], taskId) 504 | for (let index = taskId; index < this.tempData['timing'].length; index++) 505 | this.tempData['timing'][index].task = index 506 | } 507 | 508 | private addTimingTask(taskData) { 509 | // console.log(taskData); 510 | if (taskData.ena == 0) { 511 | this.disableTimingTask(taskData.task) 512 | return 513 | } 514 | let hour = Math.floor(taskData.tim / 60); 515 | let minute = taskData.tim % 60 516 | let dayOfWeek = [] 517 | for (let index = 0; index < taskData.day.length; index++) { 518 | if (taskData.day[index] == '1') 519 | dayOfWeek.push(index) 520 | } 521 | let config = { 522 | minute: minute, 523 | hour: hour 524 | } 525 | if (dayOfWeek.length == 1) { 526 | config['dayOfWeek'] = dayOfWeek[0] 527 | } else if (dayOfWeek.length > 1) { 528 | config['dayOfWeek'] = dayOfWeek 529 | } 530 | // console.log(config); 531 | this.timingTasks[taskData.task] = schedule.scheduleJob(config, () => { 532 | this.processData(taskData.act[0]) 533 | this.sendMessage(this.getTimingData()) 534 | if (taskData.day == '0000000') 535 | this.disableTimingTask(taskData.task) 536 | timerLog('timer task done') 537 | }) 538 | saveJsonFile(this.tempDataPath, this.tempData) 539 | } 540 | 541 | private delTimingTask(taskId) { 542 | this.disableTimingTask(taskId); 543 | arrayRemove(this.timingTasks, taskId) 544 | saveJsonFile(this.tempDataPath, this.tempData) 545 | } 546 | 547 | private disableTimingTask(taskId) { 548 | this.tempData['timing'][taskId].ena = 0; 549 | this.timingTasks[taskId].cancel(); 550 | saveJsonFile(this.tempDataPath, this.tempData) 551 | } 552 | 553 | // 重启后,加载定时配置 554 | private loadTimingTask() { 555 | if (typeof this.tempData['timing'] == 'undefined') return 556 | timerLog("load timing tasks") 557 | for (let index = 0; index < this.tempData['timing'].length; index++) { 558 | const task = this.tempData['timing'][index]; 559 | if (task.ena == 1) 560 | this.addTimingTask(task) 561 | } 562 | } 563 | 564 | // 倒计时功能 565 | countdownTimer; 566 | countdownTimer2; 567 | 568 | setCountdownData(data) { 569 | if (data == 'dlt') { 570 | timerLog('countdown stop') 571 | this.tempData['countdown'] = false 572 | this.clearCountdownTimer() 573 | return 574 | } else if (JSON.stringify(data).indexOf(`{"run":1}`) > -1 || JSON.stringify(data).indexOf(`{"run":0}`) > -1) { 575 | this.tempData['countdown']['run'] = data.run 576 | if (this.tempData['countdown']['run'] == 0) { 577 | timerLog('countdown pause') 578 | this.countdownTimer.pause() 579 | this.countdownTimer2.pause() 580 | } else if (this.tempData['countdown']['run'] == 1) { 581 | timerLog('countdown resume') 582 | this.countdownTimer.resume() 583 | this.countdownTimer2.resume() 584 | } 585 | return 586 | } 587 | timerLog('countdown start') 588 | this.tempData['countdown'] = data; 589 | this.tempData['countdown']['rtim'] = 0 590 | this.clearCountdownTimer(); 591 | this.countdownTimer = pauseable.setTimeout(() => { 592 | this.clearCountdownTimer(); 593 | timerLog('countdown done') 594 | this.processData(this.tempData['countdown'].act[0]); 595 | // 关闭倒计时 596 | this.tempData['countdown'] = false 597 | this.sendMessage(this.getCountdownData()) 598 | }, data.ttim * 60 * 1000); 599 | this.countdownTimer2 = pauseable.setInterval(() => { 600 | this.tempData['countdown']['rtim']++; 601 | if (this.tempData['countdown']['rtim'] == this.tempData['countdown']['ttim']) clearInterval(this.countdownTimer2) 602 | }, 60 * 1000) 603 | } 604 | 605 | clearCountdownTimer() { 606 | if (typeof this.countdownTimer != 'undefined') 607 | this.countdownTimer.clear() 608 | if (typeof this.countdownTimer != 'undefined') 609 | this.countdownTimer2.clear() 610 | } 611 | 612 | getCountdownData() { 613 | if (typeof this.tempData['countdown'] == 'undefined') 614 | return { countdown: false } 615 | else 616 | return { countdown: this.tempData['countdown'] } 617 | } 618 | 619 | log(logString) { 620 | return axios.post(API.LOG, { 621 | token: this.config.iotToken, 622 | data: [[(new Date()).getTime().toString().substr(0, 10), logString]] 623 | }).then((resp: any) => { 624 | if (resp.data.message == 1000) 625 | tip('log2Cloud') 626 | return resp.data 627 | }) 628 | } 629 | 630 | setPosition(lng, lat) { 631 | return axios.post(API.POSITION, { 632 | token: this.config.iotToken, 633 | data: [[(new Date()).getTime().toString().substr(0, 10), [lng, lat]]] 634 | }).then((resp: any) => { 635 | if (resp.data.message == 1000) 636 | tip('position2Cloud') 637 | return resp.data 638 | }) 639 | } 640 | 641 | // 实时数据传输功能 642 | realtimeTasks = {} 643 | sendRtData(key: string, func: Function, time = 1000) { 644 | if (typeof this.realtimeTasks[key] != 'undefined') this.realtimeTasks[key].cancel() 645 | this.realtimeTasks[key] = schedule.scheduleJob( 646 | { 647 | end: Date.now() + 10000, 648 | rule: `*/${time / 1000} * * * * *` 649 | }, () => { 650 | let message = `{"${key}":{"val":${func()},"date":${Math.round(new Date().getTime() / 1000)}}}` 651 | this.sendMessage(message) 652 | }) 653 | } 654 | } 655 | 656 | // 内置开关 657 | // 1.0.5后不再提供,直接替换为ButtonWidget 658 | // export class BuiltinSwitch { 659 | // key = 'switch'; 660 | // state = ''; 661 | // change = new Subject(); 662 | 663 | // setState(state) { 664 | // this.state = state 665 | // return this 666 | // } 667 | 668 | // update() { 669 | // let message = {} 670 | // message[this.key] = this.state 671 | // this.device.sendMessage(message) 672 | // } 673 | // device: BlinkerDevice; 674 | // } 675 | 676 | function formatMess2Device(deviceId, toDevice, data) { 677 | // 兼容阿里broker保留deviceType和fromDevice 678 | return `{"deviceType":"OwnApp","data":${data},"fromDevice":"${deviceId}","toDevice":"${toDevice}"}` 679 | } 680 | 681 | function formatMess2Grounp(deviceId, toGrounp, data) { 682 | return `{"data":${data},"fromDevice":"${deviceId}","toGrounp":"${toGrounp}"}` 683 | } 684 | 685 | function formatMess2StorageTs(deviceId, storageType, data) { 686 | return `{"data":${data},"fromDevice":"${deviceId}","toStorage":"${storageType}"}` 687 | } 688 | 689 | function formatMess2StorageTt(deviceId, storageType, data) { 690 | return `{"data":"${data}","fromDevice":"${deviceId}","toStorage":"${storageType}"}` 691 | } 692 | 693 | function formatMess2StorageOt(deviceId, storageType, data) { 694 | return `{"data":${data},"fromDevice":"${deviceId}","toStorage":"${storageType}"}` 695 | } 696 | 697 | function isJson(str: string) { 698 | if (isNumber(str)) { 699 | return false; 700 | } 701 | try { 702 | JSON.parse(str); 703 | return true; 704 | } catch (e) { 705 | return false; 706 | } 707 | } 708 | 709 | function isNumber(val: string) { 710 | var regPos = /^\d+(\.\d+)?$/; //非负浮点数 711 | var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数 712 | if (regPos.test(val) || regNeg.test(val)) { 713 | return true; 714 | } else { 715 | // console.log("不是数字"); 716 | return false; 717 | } 718 | } 719 | 720 | 721 | import * as fs from 'fs'; 722 | import { API, SERVER } from './server.config'; 723 | import { clearInterval } from 'timers'; 724 | import { u8aToString } from './fun'; 725 | 726 | function loadJsonFile(path) { 727 | if (fs.existsSync(path)) 728 | return JSON.parse(fs.readFileSync(path, 'utf8')); 729 | else 730 | return {} 731 | } 732 | 733 | function saveJsonFile(path, data) { 734 | fs.writeFileSync(path, JSON.stringify(data)); 735 | } 736 | 737 | function arrayRemove(array, index) { 738 | if (index <= (array.length - 1)) { 739 | for (var i = index; i < array.length; i++) { 740 | array[i] = array[i + 1]; 741 | } 742 | } 743 | else { 744 | // throw new Error('超出最大索引!'); 745 | } 746 | array.length = array.length - 1; 747 | return array; 748 | } -------------------------------------------------------------------------------- /lib/debug.ts: -------------------------------------------------------------------------------- 1 | // 辅助调试 2 | export function log(msg, { title = 'TITLE', color = 'white' } = {}) { 3 | // console.log(msg); 4 | 5 | if (typeof msg == 'object') msg = JSON.stringify(msg) 6 | const COLOR_CODE = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'].indexOf(color) 7 | if (COLOR_CODE >= 0) { 8 | const TITLE_STR = title ? `\x1b[4${COLOR_CODE};30m ${title} \x1b[0m ` : '' 9 | console.log(`${TITLE_STR}\x1b[3${COLOR_CODE}m${msg}\x1b[;0m`) 10 | } 11 | else { 12 | console.log(title ? `${title} ${msg}` : msg) 13 | } 14 | } 15 | 16 | export function tip(msg) { 17 | log(msg, { title: 'log', color: 'white' }) 18 | } 19 | 20 | export function warn(msg) { 21 | log(msg, { title: 'warn', color: 'yellow' }) 22 | } 23 | 24 | export function error(msg) { 25 | log(msg, { title: 'error', color: 'red' }) 26 | } 27 | 28 | 29 | export function timerLog(msg) { 30 | log(msg, { title: 'timer', color: 'blue' }) 31 | } 32 | 33 | export function mqttLog(msg) { 34 | log(msg, { title: 'mqtt', color: 'blue' }) 35 | } 36 | 37 | export function vaLog(msg, title) { 38 | log(msg, { title: title, color: 'blue' }) 39 | } 40 | -------------------------------------------------------------------------------- /lib/fun.ts: -------------------------------------------------------------------------------- 1 | export function u8aToString(fileData) { 2 | var dataString = ""; 3 | for (var i = 0; i < fileData.length; i++) { 4 | dataString += String.fromCharCode(fileData[i]); 5 | } 6 | return dataString 7 | } 8 | -------------------------------------------------------------------------------- /lib/message.ts: -------------------------------------------------------------------------------- 1 | import { BlinkerDevice } from "./blinker"; 2 | import { vaLog } from "./debug"; 3 | import { VoiceAssistant } from "./voice-assistant"; 4 | // import Base64 from 'crypto-js/enc-base64'; 5 | // import UTF8 from 'crypto-js/enc-base64'; 6 | 7 | export class Message { 8 | device: BlinkerDevice; 9 | 10 | constructor(device) { 11 | this.device = device 12 | } 13 | 14 | send() { } 15 | 16 | update() { } 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/server.config.ts: -------------------------------------------------------------------------------- 1 | export const SERVER = { 2 | HOST: 'https://iot.diandeng.tech' 3 | } 4 | 5 | export const API = { 6 | AUTH: SERVER.HOST + '/api/v1/user/device/diy/auth', 7 | HEARTBEAT: SERVER.HOST + '/api/v1/user/device/heartbeat', 8 | SMS: SERVER.HOST + '/api/v1/user/device/sms', 9 | WECHAT: SERVER.HOST + '/api/v1/user/device/wxMsg/', 10 | PUSH: SERVER.HOST + '/api/v1/user/device/push', 11 | SHARE: SERVER.HOST + '/api/v1/user/device/share/device', 12 | LOG: SERVER.HOST + '/api/v1/user/device/cloud_storage/logs', 13 | POSITION:SERVER.HOST + '/api/v1/user/device/cloud_storage/coordinate', 14 | VOICE_ASSISTANT: SERVER.HOST + '/api/v1/user/device/voice_assistant' 15 | } 16 | -------------------------------------------------------------------------------- /lib/voice-assistant.ts: -------------------------------------------------------------------------------- 1 | export enum VA_TYPE { 2 | LIGHT = 'light', 3 | OUTLET = 'outlet', 4 | MULTI_OUTLET = 'multi_outlet', 5 | SENSOR = 'sensor', 6 | FAN = 'fan', 7 | AIRCONDITION = 'aircondition' 8 | } 9 | 10 | export enum MI_LIGHT_MODE { 11 | DAY, 12 | NIGHT, 13 | COLOR, 14 | WARMTH, 15 | TV, 16 | READING, 17 | COMPUTER, 18 | } 19 | 20 | export enum ALI_LIGHT_MODE { 21 | READING = 'reading', 22 | MOVIE = 'movie', 23 | SLEEP = 'sleep', 24 | LIVE = 'live', 25 | HOLIDAY = 'holiday', 26 | MUSIC = 'music', 27 | COMMON = 'common', 28 | NIGHT = 'night' 29 | } 30 | 31 | export enum DUER_LIGHT_MODE { 32 | READING = 'READING', 33 | SLEEP = 'SLEEP', 34 | ALARM = 'ALARM', 35 | // DAYTIME = 'DAYTIME', 36 | NIGHT_LIGHT = 'NIGHT_LIGHT', 37 | ROMANTIC = 'ROMANTIC', 38 | SUNDOWN = 'SUNDOWN', 39 | SUNRISE = 'SUNRISE', 40 | RELAX = 'RELAX', 41 | LIGHTING = 'LIGHTING', 42 | SUN = 'SUN', 43 | STAR = 'STAR', 44 | ENERGY_SAVING = 'ENERGY_SAVING', 45 | MOON = 'MOON', 46 | JUDI = 'JUDI', 47 | // HEAT = 'HEAT', 48 | // COOL = 'COOL', 49 | // AUTO = 'AUTO' 50 | } 51 | 52 | import { Subject } from "rxjs"; 53 | import { BlinkerDevice } from "./blinker"; 54 | import { u8aToString } from "./fun" 55 | import { Message } from "./message" 56 | import { vaLog } from "./debug" 57 | 58 | export class VoiceAssistant { 59 | 60 | get subTopic() { 61 | if (this.device.config.broker == 'blinker') 62 | return `/device/${this.device.config.deviceName}/r` 63 | return `/sys/${this.device.config.productKey}/${this.device.config.deviceName}/rrpc/request/+` 64 | } 65 | 66 | get pubTopic() { 67 | if (this.device.config.broker == 'blinker') 68 | return `/device/${this.device.config.deviceName}/s` 69 | return `/sys/${this.device.config.productKey}/${this.device.config.deviceName}/rrpc/response/` 70 | } 71 | 72 | vaType; 73 | vaName; 74 | 75 | device: BlinkerDevice; 76 | 77 | change = new Subject(); 78 | 79 | targetDevice; 80 | 81 | powerChange = new Subject(); 82 | modeChange = new Subject(); 83 | colorChange = new Subject(); 84 | colorTempChange = new Subject(); 85 | brightnessChange = new Subject(); 86 | stateQuery = new Subject(); 87 | 88 | constructor(key) { 89 | this.vaType = key 90 | } 91 | 92 | listen() { 93 | this.device.mqttClient.on('message', (topic, message) => { 94 | if (topic.indexOf(this.subTopic.substr(0, this.subTopic.length - 1)) > -1) { 95 | let data; 96 | let fromDevice; 97 | let messageId; 98 | try { 99 | let messageString = u8aToString(message) 100 | let messageObject = JSON.parse(messageString) 101 | fromDevice = messageObject.data.from 102 | data = messageObject.data 103 | this.targetDevice = fromDevice 104 | messageId = topic.split('/')[6] 105 | vaLog(data, `${this.vaName}>device`) 106 | } catch (error) { 107 | console.log(error); 108 | } 109 | if (fromDevice == this.vaName) 110 | this.processData(messageId, data) 111 | } 112 | }) 113 | } 114 | 115 | unlisten() { 116 | // this.changeSubscription.unsubscribe(); 117 | } 118 | 119 | 120 | processData(messageId, data) { 121 | // console.log(data); 122 | if (typeof data.set != 'undefined') { 123 | if (typeof data.set.pState != 'undefined') { 124 | this.powerChange.next(new powerMessage(this.device, this, messageId, data)) 125 | } else if (typeof data.set.col != 'undefined') { 126 | this.colorChange.next(new colorMessage(this.device, this, messageId, data)) 127 | } else if (typeof data.set.colTemp != 'undefined') { 128 | this.colorTempChange.next(new colorTempMessage(this.device, this, messageId, data)) 129 | } else if (typeof data.set.mode != 'undefined') { 130 | this.modeChange.next(new modeMessage(this.device, this, messageId, data)) 131 | } else if (typeof data.set.bright != 'undefined' || typeof data.set.upBright != 'undefined' || typeof data.set.downBright != 'undefined') { 132 | this.brightnessChange.next(new brightnessMessage(this.device, this, messageId, data)) 133 | } 134 | } else if (typeof data.get != 'undefined') { 135 | this.stateQuery.next(new dataMessage(this.device, this, messageId, data)) 136 | } 137 | } 138 | } 139 | 140 | export class Miot extends VoiceAssistant { 141 | 142 | constructor(key) { 143 | super(key) 144 | this.vaType = { miType: key } 145 | this.vaName = 'MIOT' 146 | } 147 | 148 | mode(mode: MI_LIGHT_MODE) { 149 | return this 150 | } 151 | } 152 | 153 | export class AliGenie extends VoiceAssistant { 154 | 155 | constructor(key) { 156 | super(key) 157 | this.vaType = { aliType: key } 158 | this.vaName = 'AliGenie' 159 | } 160 | } 161 | 162 | export class DuerOS extends VoiceAssistant { 163 | 164 | constructor(key) { 165 | super(key) 166 | let newkey; 167 | switch (key) { 168 | case VA_TYPE.LIGHT: 169 | newkey = 'LIGHT' 170 | break; 171 | case VA_TYPE.OUTLET: 172 | newkey = 'SOCKET' 173 | break; 174 | case VA_TYPE.MULTI_OUTLET: 175 | newkey = 'MULTI_SOCKET' 176 | break; 177 | case VA_TYPE.SENSOR: 178 | newkey = 'AIR_MONITOR' 179 | break; 180 | } 181 | this.vaType = { duerType: newkey } 182 | this.vaName = 'DuerOS' 183 | } 184 | 185 | } 186 | 187 | 188 | export class VaMessage extends Message { 189 | id: number; 190 | device: BlinkerDevice; 191 | voiceAssistant: VoiceAssistant; 192 | 193 | get data(): any { 194 | return this.request 195 | } 196 | 197 | request = {}; 198 | response = {}; 199 | 200 | constructor(device, voiceAssistant, id, request) { 201 | super(device) 202 | this.device = device 203 | this.id = id 204 | this.request = request 205 | this.voiceAssistant = voiceAssistant 206 | } 207 | 208 | update() { 209 | let responseStr = JSON.stringify(Object.assign(this.response, { messageId: this.data.messageId })) 210 | let data = `{ "fromDevice": "${this.device.config.deviceName}", "toDevice": "ServerReceiver", "data": ${responseStr}, "deviceType": "vAssistant"}` 211 | let base64Data = Buffer.from(data).toString('base64') 212 | this.device.mqttClient.publish(this.voiceAssistant.pubTopic + this.id, base64Data) 213 | vaLog(responseStr, `device>${this.voiceAssistant.vaName}`) 214 | } 215 | } 216 | 217 | class powerMessage extends VaMessage { 218 | power(state: string) { 219 | let data = { pState: state } 220 | this.response = Object.assign(this.response, data) 221 | return this 222 | } 223 | 224 | num(num: number) { 225 | let data = { num: num } 226 | this.response = Object.assign(this.response, data) 227 | return this 228 | } 229 | } 230 | 231 | class modeMessage extends VaMessage { 232 | mode(state: string | number) { 233 | let data = { mode: state } 234 | this.response = Object.assign(this.response, data) 235 | return this 236 | } 237 | } 238 | 239 | class colorMessage extends VaMessage { 240 | color(color: string | number[]) { 241 | let data = { clr: color } 242 | this.response = Object.assign(this.response, data) 243 | return this 244 | } 245 | } 246 | 247 | class colorTempMessage extends VaMessage { 248 | colorTemp(val: number) { 249 | let data = { colTemp: val } 250 | this.response = Object.assign(this.response, data) 251 | return this 252 | } 253 | } 254 | 255 | class brightnessMessage extends VaMessage { 256 | brightness(val: number | string) { 257 | if (typeof val == 'number') val = val.toString() 258 | let data = { bright: val } 259 | this.response = Object.assign(this.response, data) 260 | return this 261 | } 262 | } 263 | 264 | class dataMessage extends VaMessage { 265 | temp(val: number | string) { 266 | if (typeof val == 'number') val = val.toString() 267 | let data = { temp: val } 268 | this.response = Object.assign(this.response, data) 269 | return this 270 | } 271 | 272 | humi(val: number | string) { 273 | if (typeof val == 'number') val = val.toString() 274 | let data = { humi: val } 275 | this.response = Object.assign(this.response, data) 276 | return this 277 | } 278 | 279 | aqi(val: number | string) { 280 | if (typeof val == 'number') val = val.toString() 281 | let data = { aqi: val } 282 | this.response = Object.assign(this.response, data) 283 | return this 284 | } 285 | 286 | pm25(val: number | string) { 287 | if (typeof val == 'number') val = val.toString() 288 | let data = { pm25: val } 289 | this.response = Object.assign(this.response, data) 290 | return this 291 | } 292 | 293 | pm10(val: number | string) { 294 | if (typeof val == 'number') val = val.toString() 295 | let data = { pm10: val } 296 | this.response = Object.assign(this.response, data) 297 | return this 298 | } 299 | 300 | co2(val: number | string) { 301 | if (typeof val == 'number') val = val.toString() 302 | let data = { co2: val } 303 | this.response = Object.assign(this.response, data) 304 | return this 305 | } 306 | 307 | brightness(val: number | string) { 308 | if (typeof val == 'number') val = val.toString() 309 | let data = { bright: val } 310 | this.response = Object.assign(this.response, data) 311 | return this 312 | } 313 | 314 | color(color: string | number[]) { 315 | let data = { clr: color } 316 | this.response = Object.assign(this.response, data) 317 | return this 318 | } 319 | 320 | colorTemp(val: number | string) { 321 | if (typeof val == 'number') val = val.toString() 322 | let data = { colorTemp: val } 323 | this.response = Object.assign(this.response, data) 324 | return this 325 | } 326 | 327 | mode(state: string | number) { 328 | let data = { mode: state } 329 | this.response = Object.assign(this.response, data) 330 | return this 331 | } 332 | 333 | power(state: string) { 334 | if (this.voiceAssistant.vaName == 'MIOT') { 335 | if (state == 'on') state = 'true' 336 | else state = 'false' 337 | } 338 | let data = { pState: state } 339 | this.response = Object.assign(this.response, data) 340 | return this 341 | } 342 | } -------------------------------------------------------------------------------- /lib/widget.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from "rxjs"; 2 | import { BlinkerDevice, Message } from "./blinker"; 3 | 4 | export class Widget { 5 | device: BlinkerDevice; 6 | key: string; 7 | state = {}; 8 | 9 | change = new Subject(); 10 | private change2 = new Subject(); 11 | changeSubscription; 12 | 13 | constructor(key) { 14 | this.key = key 15 | } 16 | 17 | listen() { 18 | this.changeSubscription = this.change.subscribe(message => { 19 | this.device.targetDevice = message.fromDevice 20 | this.change2.next(message) 21 | }) 22 | return this.change2 23 | } 24 | 25 | unlisten() { 26 | this.changeSubscription.unsubscribe(); 27 | } 28 | 29 | update(value = '') { 30 | let message = {} 31 | message[this.key] = this.state 32 | this.device.sendMessage(message) 33 | } 34 | } 35 | 36 | export class ButtonWidget extends Widget { 37 | constructor(key) { 38 | super(key) 39 | } 40 | 41 | turn(swi) { 42 | this.state['swi'] = swi 43 | return this 44 | } 45 | 46 | text(text) { 47 | this.state['tex'] = text 48 | return this 49 | } 50 | 51 | icon(icon) { 52 | this.state['ico'] = icon 53 | return this 54 | } 55 | 56 | color(color) { 57 | this.state['clr'] = color 58 | return this 59 | } 60 | } 61 | 62 | export class TextWidget extends Widget { 63 | 64 | constructor(key) { 65 | super(key) 66 | } 67 | 68 | text(text:string) { 69 | this.state['tex'] = text 70 | return this 71 | } 72 | 73 | text1(text:string) { 74 | this.state['tex1'] = text 75 | return this 76 | } 77 | 78 | icon(icon) { 79 | this.state['ico'] = icon 80 | return this 81 | } 82 | 83 | color(color) { 84 | this.state['clr'] = color 85 | return this 86 | } 87 | } 88 | 89 | export class NumberWidget extends Widget { 90 | 91 | constructor(key) { 92 | super(key) 93 | } 94 | 95 | text(text) { 96 | this.state['tex'] = text 97 | return this 98 | } 99 | 100 | value(value) { 101 | this.state['val'] = value 102 | return this 103 | } 104 | 105 | unit(unit) { 106 | this.state['uni'] = unit 107 | return this 108 | } 109 | 110 | icon(icon) { 111 | this.state['ico'] = icon 112 | return this 113 | } 114 | 115 | color(color) { 116 | this.state['clr'] = color 117 | return this 118 | } 119 | 120 | max(max) { 121 | this.state['max'] = max 122 | return this 123 | } 124 | } 125 | 126 | export class RangeWidget extends Widget { 127 | 128 | constructor(key) { 129 | super(key) 130 | } 131 | 132 | text(text) { 133 | this.state['tex'] = text 134 | return this 135 | } 136 | 137 | value(value) { 138 | this.state['val'] = value 139 | return this 140 | } 141 | 142 | unit(unit) { 143 | this.state['uni'] = unit 144 | return this 145 | } 146 | 147 | icon(icon) { 148 | this.state['ico'] = icon 149 | return this 150 | } 151 | 152 | color(color) { 153 | this.state['clr'] = color 154 | return this 155 | } 156 | 157 | max(max) { 158 | this.state['max'] = max 159 | return this 160 | } 161 | } 162 | 163 | export class RGBWidget extends Widget { 164 | 165 | constructor(key) { 166 | super(key) 167 | } 168 | 169 | text(text) { 170 | this.state['tex'] = text 171 | return this 172 | } 173 | 174 | color(color) { 175 | if (typeof color == 'string' && color.indexOf('#') == 0) 176 | this.state = this.toRgb(color) 177 | else if (color.length == 3 || color.length == 4) 178 | this.state = color 179 | return this 180 | } 181 | 182 | brightness(brightness) { 183 | this.state[3] = brightness 184 | return this 185 | } 186 | 187 | private toRgb(colorHex) { 188 | let colorStr = colorHex.toLowerCase() 189 | var colorArray = [] 190 | for (let i = 1; i < 7; i += 2) { 191 | colorArray.push(parseInt('0x' + colorStr.slice(i, i + 2))) 192 | } 193 | return colorArray 194 | } 195 | } 196 | 197 | export class JoystickWidget extends Widget { 198 | 199 | constructor(key) { 200 | super(key) 201 | } 202 | } 203 | 204 | export class ImageWidget extends Widget { 205 | 206 | constructor(key) { 207 | super(key) 208 | } 209 | 210 | show(img: number) { 211 | this.state['img'] = img 212 | return this 213 | } 214 | } 215 | 216 | export class VideoWidget extends Widget { 217 | 218 | constructor(key) { 219 | super(key) 220 | } 221 | 222 | url(addr: string) { 223 | this.state['url'] = addr 224 | return this 225 | } 226 | 227 | autoplay(swi: boolean) { 228 | this.state['auto'] = swi 229 | return this 230 | } 231 | } 232 | 233 | export class ChartWidget extends Widget { 234 | 235 | constructor(key) { 236 | super(key) 237 | } 238 | 239 | } -------------------------------------------------------------------------------- /lib/wireless.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blinker-iot/blinker-js/e0c626b620efd151f829d498a6936389fbe310c0/lib/wireless.ts -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blinkerjs", 3 | "version": "1.0.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "blinkerjs", 9 | "version": "1.0.4", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@types/bonjour": "^3.5.6", 13 | "@types/node": "^13.9.5", 14 | "@types/node-schedule": "^1.3.2", 15 | "axios": "^0.21.1", 16 | "bonjour": "^3.5.0", 17 | "commander": "^5.1.0", 18 | "getmac": "^5.5.0", 19 | "mqtt": "^4.2.6", 20 | "node-schedule": "^2.0.0", 21 | "pauseable": "^0.3.2", 22 | "qrcode-terminal": "^0.12.0", 23 | "rxjs": "^6.5.5", 24 | "wireless-tools": "^0.19.0", 25 | "ws": "^7.4.6" 26 | } 27 | }, 28 | "node_modules/@types/bonjour": { 29 | "version": "3.5.10", 30 | "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", 31 | "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", 32 | "dependencies": { 33 | "@types/node": "*" 34 | } 35 | }, 36 | "node_modules/@types/node": { 37 | "version": "13.13.52", 38 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", 39 | "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" 40 | }, 41 | "node_modules/@types/node-schedule": { 42 | "version": "1.3.2", 43 | "resolved": "https://registry.npmjs.org/@types/node-schedule/-/node-schedule-1.3.2.tgz", 44 | "integrity": "sha512-Y0CqdAr+lCpArT8CJJjJq4U2v8Bb5e7ru2nV/NhDdaptCMCRdOL3Y7tAhen39HluQMaIKWvPbDuiFBUQpg7Srw==", 45 | "dependencies": { 46 | "@types/node": "*" 47 | } 48 | }, 49 | "node_modules/array-flatten": { 50 | "version": "2.1.2", 51 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", 52 | "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" 53 | }, 54 | "node_modules/axios": { 55 | "version": "0.21.4", 56 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", 57 | "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", 58 | "dependencies": { 59 | "follow-redirects": "^1.14.0" 60 | } 61 | }, 62 | "node_modules/balanced-match": { 63 | "version": "1.0.2", 64 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 65 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 66 | }, 67 | "node_modules/base64-js": { 68 | "version": "1.5.1", 69 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 70 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 71 | "funding": [ 72 | { 73 | "type": "github", 74 | "url": "https://github.com/sponsors/feross" 75 | }, 76 | { 77 | "type": "patreon", 78 | "url": "https://www.patreon.com/feross" 79 | }, 80 | { 81 | "type": "consulting", 82 | "url": "https://feross.org/support" 83 | } 84 | ] 85 | }, 86 | "node_modules/bl": { 87 | "version": "4.1.0", 88 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 89 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 90 | "dependencies": { 91 | "buffer": "^5.5.0", 92 | "inherits": "^2.0.4", 93 | "readable-stream": "^3.4.0" 94 | } 95 | }, 96 | "node_modules/bonjour": { 97 | "version": "3.5.0", 98 | "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", 99 | "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", 100 | "dependencies": { 101 | "array-flatten": "^2.1.0", 102 | "deep-equal": "^1.0.1", 103 | "dns-equal": "^1.0.0", 104 | "dns-txt": "^2.0.2", 105 | "multicast-dns": "^6.0.1", 106 | "multicast-dns-service-types": "^1.1.0" 107 | } 108 | }, 109 | "node_modules/brace-expansion": { 110 | "version": "1.1.11", 111 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 112 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 113 | "dependencies": { 114 | "balanced-match": "^1.0.0", 115 | "concat-map": "0.0.1" 116 | } 117 | }, 118 | "node_modules/buffer": { 119 | "version": "5.7.1", 120 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 121 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 122 | "funding": [ 123 | { 124 | "type": "github", 125 | "url": "https://github.com/sponsors/feross" 126 | }, 127 | { 128 | "type": "patreon", 129 | "url": "https://www.patreon.com/feross" 130 | }, 131 | { 132 | "type": "consulting", 133 | "url": "https://feross.org/support" 134 | } 135 | ], 136 | "dependencies": { 137 | "base64-js": "^1.3.1", 138 | "ieee754": "^1.1.13" 139 | } 140 | }, 141 | "node_modules/buffer-from": { 142 | "version": "1.1.2", 143 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 144 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 145 | }, 146 | "node_modules/buffer-indexof": { 147 | "version": "1.1.1", 148 | "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", 149 | "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" 150 | }, 151 | "node_modules/call-bind": { 152 | "version": "1.0.2", 153 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 154 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 155 | "dependencies": { 156 | "function-bind": "^1.1.1", 157 | "get-intrinsic": "^1.0.2" 158 | }, 159 | "funding": { 160 | "url": "https://github.com/sponsors/ljharb" 161 | } 162 | }, 163 | "node_modules/commander": { 164 | "version": "5.1.0", 165 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", 166 | "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", 167 | "engines": { 168 | "node": ">= 6" 169 | } 170 | }, 171 | "node_modules/commist": { 172 | "version": "1.1.0", 173 | "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", 174 | "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", 175 | "dependencies": { 176 | "leven": "^2.1.0", 177 | "minimist": "^1.1.0" 178 | } 179 | }, 180 | "node_modules/concat-map": { 181 | "version": "0.0.1", 182 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 183 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 184 | }, 185 | "node_modules/concat-stream": { 186 | "version": "2.0.0", 187 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", 188 | "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", 189 | "engines": [ 190 | "node >= 6.0" 191 | ], 192 | "dependencies": { 193 | "buffer-from": "^1.0.0", 194 | "inherits": "^2.0.3", 195 | "readable-stream": "^3.0.2", 196 | "typedarray": "^0.0.6" 197 | } 198 | }, 199 | "node_modules/cron-parser": { 200 | "version": "3.5.0", 201 | "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", 202 | "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", 203 | "dependencies": { 204 | "is-nan": "^1.3.2", 205 | "luxon": "^1.26.0" 206 | }, 207 | "engines": { 208 | "node": ">=0.8" 209 | } 210 | }, 211 | "node_modules/debug": { 212 | "version": "4.3.3", 213 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 214 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 215 | "dependencies": { 216 | "ms": "2.1.2" 217 | }, 218 | "engines": { 219 | "node": ">=6.0" 220 | }, 221 | "peerDependenciesMeta": { 222 | "supports-color": { 223 | "optional": true 224 | } 225 | } 226 | }, 227 | "node_modules/deep-equal": { 228 | "version": "1.1.1", 229 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", 230 | "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", 231 | "dependencies": { 232 | "is-arguments": "^1.0.4", 233 | "is-date-object": "^1.0.1", 234 | "is-regex": "^1.0.4", 235 | "object-is": "^1.0.1", 236 | "object-keys": "^1.1.1", 237 | "regexp.prototype.flags": "^1.2.0" 238 | }, 239 | "funding": { 240 | "url": "https://github.com/sponsors/ljharb" 241 | } 242 | }, 243 | "node_modules/define-properties": { 244 | "version": "1.1.3", 245 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 246 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 247 | "dependencies": { 248 | "object-keys": "^1.0.12" 249 | }, 250 | "engines": { 251 | "node": ">= 0.4" 252 | } 253 | }, 254 | "node_modules/dns-equal": { 255 | "version": "1.0.0", 256 | "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", 257 | "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" 258 | }, 259 | "node_modules/dns-packet": { 260 | "version": "1.3.4", 261 | "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", 262 | "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", 263 | "dependencies": { 264 | "ip": "^1.1.0", 265 | "safe-buffer": "^5.0.1" 266 | } 267 | }, 268 | "node_modules/dns-txt": { 269 | "version": "2.0.2", 270 | "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", 271 | "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", 272 | "dependencies": { 273 | "buffer-indexof": "^1.0.0" 274 | } 275 | }, 276 | "node_modules/duplexify": { 277 | "version": "4.1.2", 278 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", 279 | "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", 280 | "dependencies": { 281 | "end-of-stream": "^1.4.1", 282 | "inherits": "^2.0.3", 283 | "readable-stream": "^3.1.1", 284 | "stream-shift": "^1.0.0" 285 | } 286 | }, 287 | "node_modules/end-of-stream": { 288 | "version": "1.4.4", 289 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 290 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 291 | "dependencies": { 292 | "once": "^1.4.0" 293 | } 294 | }, 295 | "node_modules/follow-redirects": { 296 | "version": "1.14.8", 297 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", 298 | "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", 299 | "funding": [ 300 | { 301 | "type": "individual", 302 | "url": "https://github.com/sponsors/RubenVerborgh" 303 | } 304 | ], 305 | "engines": { 306 | "node": ">=4.0" 307 | }, 308 | "peerDependenciesMeta": { 309 | "debug": { 310 | "optional": true 311 | } 312 | } 313 | }, 314 | "node_modules/fs.realpath": { 315 | "version": "1.0.0", 316 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 317 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 318 | }, 319 | "node_modules/function-bind": { 320 | "version": "1.1.1", 321 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 322 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 323 | }, 324 | "node_modules/get-intrinsic": { 325 | "version": "1.1.1", 326 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 327 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 328 | "dependencies": { 329 | "function-bind": "^1.1.1", 330 | "has": "^1.0.3", 331 | "has-symbols": "^1.0.1" 332 | }, 333 | "funding": { 334 | "url": "https://github.com/sponsors/ljharb" 335 | } 336 | }, 337 | "node_modules/getmac": { 338 | "version": "5.20.0", 339 | "resolved": "https://registry.npmjs.org/getmac/-/getmac-5.20.0.tgz", 340 | "integrity": "sha512-O9T855fb+Hx9dsTJHNv72ZUuA6Y18+BO/0ypPXf6s/tunzXqhc3kbQkNAl+9HVKVlwkWmglHS4LMoJ9YbymKYQ==", 341 | "dependencies": { 342 | "@types/node": "^16.4.7" 343 | }, 344 | "engines": { 345 | "node": ">=10" 346 | }, 347 | "funding": { 348 | "url": "https://bevry.me/fund" 349 | } 350 | }, 351 | "node_modules/getmac/node_modules/@types/node": { 352 | "version": "16.11.22", 353 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz", 354 | "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==" 355 | }, 356 | "node_modules/glob": { 357 | "version": "7.2.0", 358 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 359 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 360 | "dependencies": { 361 | "fs.realpath": "^1.0.0", 362 | "inflight": "^1.0.4", 363 | "inherits": "2", 364 | "minimatch": "^3.0.4", 365 | "once": "^1.3.0", 366 | "path-is-absolute": "^1.0.0" 367 | }, 368 | "engines": { 369 | "node": "*" 370 | }, 371 | "funding": { 372 | "url": "https://github.com/sponsors/isaacs" 373 | } 374 | }, 375 | "node_modules/has": { 376 | "version": "1.0.3", 377 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 378 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 379 | "dependencies": { 380 | "function-bind": "^1.1.1" 381 | }, 382 | "engines": { 383 | "node": ">= 0.4.0" 384 | } 385 | }, 386 | "node_modules/has-symbols": { 387 | "version": "1.0.2", 388 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", 389 | "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", 390 | "engines": { 391 | "node": ">= 0.4" 392 | }, 393 | "funding": { 394 | "url": "https://github.com/sponsors/ljharb" 395 | } 396 | }, 397 | "node_modules/has-tostringtag": { 398 | "version": "1.0.0", 399 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 400 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 401 | "dependencies": { 402 | "has-symbols": "^1.0.2" 403 | }, 404 | "engines": { 405 | "node": ">= 0.4" 406 | }, 407 | "funding": { 408 | "url": "https://github.com/sponsors/ljharb" 409 | } 410 | }, 411 | "node_modules/help-me": { 412 | "version": "3.0.0", 413 | "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", 414 | "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", 415 | "dependencies": { 416 | "glob": "^7.1.6", 417 | "readable-stream": "^3.6.0" 418 | } 419 | }, 420 | "node_modules/ieee754": { 421 | "version": "1.2.1", 422 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 423 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 424 | "funding": [ 425 | { 426 | "type": "github", 427 | "url": "https://github.com/sponsors/feross" 428 | }, 429 | { 430 | "type": "patreon", 431 | "url": "https://www.patreon.com/feross" 432 | }, 433 | { 434 | "type": "consulting", 435 | "url": "https://feross.org/support" 436 | } 437 | ] 438 | }, 439 | "node_modules/inflight": { 440 | "version": "1.0.6", 441 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 442 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 443 | "dependencies": { 444 | "once": "^1.3.0", 445 | "wrappy": "1" 446 | } 447 | }, 448 | "node_modules/inherits": { 449 | "version": "2.0.4", 450 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 451 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 452 | }, 453 | "node_modules/ip": { 454 | "version": "1.1.5", 455 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 456 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 457 | }, 458 | "node_modules/is-arguments": { 459 | "version": "1.1.1", 460 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 461 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 462 | "dependencies": { 463 | "call-bind": "^1.0.2", 464 | "has-tostringtag": "^1.0.0" 465 | }, 466 | "engines": { 467 | "node": ">= 0.4" 468 | }, 469 | "funding": { 470 | "url": "https://github.com/sponsors/ljharb" 471 | } 472 | }, 473 | "node_modules/is-date-object": { 474 | "version": "1.0.5", 475 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 476 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 477 | "dependencies": { 478 | "has-tostringtag": "^1.0.0" 479 | }, 480 | "engines": { 481 | "node": ">= 0.4" 482 | }, 483 | "funding": { 484 | "url": "https://github.com/sponsors/ljharb" 485 | } 486 | }, 487 | "node_modules/is-nan": { 488 | "version": "1.3.2", 489 | "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", 490 | "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", 491 | "dependencies": { 492 | "call-bind": "^1.0.0", 493 | "define-properties": "^1.1.3" 494 | }, 495 | "engines": { 496 | "node": ">= 0.4" 497 | }, 498 | "funding": { 499 | "url": "https://github.com/sponsors/ljharb" 500 | } 501 | }, 502 | "node_modules/is-regex": { 503 | "version": "1.1.4", 504 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 505 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 506 | "dependencies": { 507 | "call-bind": "^1.0.2", 508 | "has-tostringtag": "^1.0.0" 509 | }, 510 | "engines": { 511 | "node": ">= 0.4" 512 | }, 513 | "funding": { 514 | "url": "https://github.com/sponsors/ljharb" 515 | } 516 | }, 517 | "node_modules/js-sdsl": { 518 | "version": "2.1.4", 519 | "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-2.1.4.tgz", 520 | "integrity": "sha512-/Ew+CJWHNddr7sjwgxaVeIORIH4AMVC9dy0hPf540ZGMVgS9d3ajwuVdyhDt6/QUvT8ATjR3yuYBKsS79F+H4A==" 521 | }, 522 | "node_modules/leven": { 523 | "version": "2.1.0", 524 | "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", 525 | "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", 526 | "engines": { 527 | "node": ">=0.10.0" 528 | } 529 | }, 530 | "node_modules/long-timeout": { 531 | "version": "0.1.1", 532 | "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", 533 | "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ=" 534 | }, 535 | "node_modules/lru-cache": { 536 | "version": "6.0.0", 537 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 538 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 539 | "dependencies": { 540 | "yallist": "^4.0.0" 541 | }, 542 | "engines": { 543 | "node": ">=10" 544 | } 545 | }, 546 | "node_modules/luxon": { 547 | "version": "1.28.1", 548 | "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", 549 | "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", 550 | "engines": { 551 | "node": "*" 552 | } 553 | }, 554 | "node_modules/minimatch": { 555 | "version": "3.1.2", 556 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 557 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 558 | "dependencies": { 559 | "brace-expansion": "^1.1.7" 560 | }, 561 | "engines": { 562 | "node": "*" 563 | } 564 | }, 565 | "node_modules/minimist": { 566 | "version": "1.2.6", 567 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 568 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" 569 | }, 570 | "node_modules/mqtt": { 571 | "version": "4.3.4", 572 | "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.4.tgz", 573 | "integrity": "sha512-yAVDfVHz3Cjn6K68z54mf7fTni/AWsPhiEsRwZSvet2wO47R6NFUn2psWxYIph2JxWtL3ZKa/da8pjJKSaXPdQ==", 574 | "dependencies": { 575 | "commist": "^1.0.0", 576 | "concat-stream": "^2.0.0", 577 | "debug": "^4.1.1", 578 | "duplexify": "^4.1.1", 579 | "help-me": "^3.0.0", 580 | "inherits": "^2.0.3", 581 | "lru-cache": "^6.0.0", 582 | "minimist": "^1.2.5", 583 | "mqtt-packet": "^6.8.0", 584 | "number-allocator": "^1.0.9", 585 | "pump": "^3.0.0", 586 | "readable-stream": "^3.6.0", 587 | "reinterval": "^1.1.0", 588 | "rfdc": "^1.3.0", 589 | "split2": "^3.1.0", 590 | "ws": "^7.5.5", 591 | "xtend": "^4.0.2" 592 | }, 593 | "bin": { 594 | "mqtt": "bin/mqtt.js", 595 | "mqtt_pub": "bin/pub.js", 596 | "mqtt_sub": "bin/sub.js" 597 | }, 598 | "engines": { 599 | "node": ">=10.0.0" 600 | } 601 | }, 602 | "node_modules/mqtt-packet": { 603 | "version": "6.10.0", 604 | "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", 605 | "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", 606 | "dependencies": { 607 | "bl": "^4.0.2", 608 | "debug": "^4.1.1", 609 | "process-nextick-args": "^2.0.1" 610 | } 611 | }, 612 | "node_modules/ms": { 613 | "version": "2.1.2", 614 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 615 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 616 | }, 617 | "node_modules/multicast-dns": { 618 | "version": "6.2.3", 619 | "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", 620 | "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", 621 | "dependencies": { 622 | "dns-packet": "^1.3.1", 623 | "thunky": "^1.0.2" 624 | }, 625 | "bin": { 626 | "multicast-dns": "cli.js" 627 | } 628 | }, 629 | "node_modules/multicast-dns-service-types": { 630 | "version": "1.1.0", 631 | "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", 632 | "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" 633 | }, 634 | "node_modules/node-schedule": { 635 | "version": "2.1.0", 636 | "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", 637 | "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", 638 | "dependencies": { 639 | "cron-parser": "^3.5.0", 640 | "long-timeout": "0.1.1", 641 | "sorted-array-functions": "^1.3.0" 642 | }, 643 | "engines": { 644 | "node": ">=6" 645 | } 646 | }, 647 | "node_modules/number-allocator": { 648 | "version": "1.0.9", 649 | "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.9.tgz", 650 | "integrity": "sha512-sIIF0dZKMs3roPUD7rLreH8H3x47QKV9dHZ+PeSnH24gL0CxKxz/823woGZC0hLBSb2Ar/rOOeHiNbnPBum/Mw==", 651 | "dependencies": { 652 | "debug": "^4.3.1", 653 | "js-sdsl": "^2.1.2" 654 | } 655 | }, 656 | "node_modules/object-is": { 657 | "version": "1.1.5", 658 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", 659 | "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", 660 | "dependencies": { 661 | "call-bind": "^1.0.2", 662 | "define-properties": "^1.1.3" 663 | }, 664 | "engines": { 665 | "node": ">= 0.4" 666 | }, 667 | "funding": { 668 | "url": "https://github.com/sponsors/ljharb" 669 | } 670 | }, 671 | "node_modules/object-keys": { 672 | "version": "1.1.1", 673 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 674 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 675 | "engines": { 676 | "node": ">= 0.4" 677 | } 678 | }, 679 | "node_modules/once": { 680 | "version": "1.4.0", 681 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 682 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 683 | "dependencies": { 684 | "wrappy": "1" 685 | } 686 | }, 687 | "node_modules/path-is-absolute": { 688 | "version": "1.0.1", 689 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 690 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 691 | "engines": { 692 | "node": ">=0.10.0" 693 | } 694 | }, 695 | "node_modules/pauseable": { 696 | "version": "0.3.2", 697 | "resolved": "https://registry.npmjs.org/pauseable/-/pauseable-0.3.2.tgz", 698 | "integrity": "sha512-QduJkcfPqTUtJiOQoUWZkmmisBA6LoAeIgGeC3b7wfICtGdo89hC174eS91pKFOegIsrS5Tla2Mh9NEvObLd0Q==", 699 | "engines": { 700 | "node": ">=8" 701 | } 702 | }, 703 | "node_modules/process-nextick-args": { 704 | "version": "2.0.1", 705 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 706 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 707 | }, 708 | "node_modules/pump": { 709 | "version": "3.0.0", 710 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 711 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 712 | "dependencies": { 713 | "end-of-stream": "^1.1.0", 714 | "once": "^1.3.1" 715 | } 716 | }, 717 | "node_modules/qrcode-terminal": { 718 | "version": "0.12.0", 719 | "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", 720 | "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", 721 | "bin": { 722 | "qrcode-terminal": "bin/qrcode-terminal.js" 723 | } 724 | }, 725 | "node_modules/readable-stream": { 726 | "version": "3.6.0", 727 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 728 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 729 | "dependencies": { 730 | "inherits": "^2.0.3", 731 | "string_decoder": "^1.1.1", 732 | "util-deprecate": "^1.0.1" 733 | }, 734 | "engines": { 735 | "node": ">= 6" 736 | } 737 | }, 738 | "node_modules/regexp.prototype.flags": { 739 | "version": "1.4.1", 740 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", 741 | "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", 742 | "dependencies": { 743 | "call-bind": "^1.0.2", 744 | "define-properties": "^1.1.3" 745 | }, 746 | "engines": { 747 | "node": ">= 0.4" 748 | }, 749 | "funding": { 750 | "url": "https://github.com/sponsors/ljharb" 751 | } 752 | }, 753 | "node_modules/reinterval": { 754 | "version": "1.1.0", 755 | "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", 756 | "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" 757 | }, 758 | "node_modules/rfdc": { 759 | "version": "1.3.0", 760 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", 761 | "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" 762 | }, 763 | "node_modules/rxjs": { 764 | "version": "6.6.7", 765 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", 766 | "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", 767 | "dependencies": { 768 | "tslib": "^1.9.0" 769 | }, 770 | "engines": { 771 | "npm": ">=2.0.0" 772 | } 773 | }, 774 | "node_modules/safe-buffer": { 775 | "version": "5.2.1", 776 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 777 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 778 | "funding": [ 779 | { 780 | "type": "github", 781 | "url": "https://github.com/sponsors/feross" 782 | }, 783 | { 784 | "type": "patreon", 785 | "url": "https://www.patreon.com/feross" 786 | }, 787 | { 788 | "type": "consulting", 789 | "url": "https://feross.org/support" 790 | } 791 | ] 792 | }, 793 | "node_modules/sorted-array-functions": { 794 | "version": "1.3.0", 795 | "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", 796 | "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" 797 | }, 798 | "node_modules/split2": { 799 | "version": "3.2.2", 800 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", 801 | "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", 802 | "dependencies": { 803 | "readable-stream": "^3.0.0" 804 | } 805 | }, 806 | "node_modules/stream-shift": { 807 | "version": "1.0.1", 808 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", 809 | "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" 810 | }, 811 | "node_modules/string_decoder": { 812 | "version": "1.3.0", 813 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 814 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 815 | "dependencies": { 816 | "safe-buffer": "~5.2.0" 817 | } 818 | }, 819 | "node_modules/thunky": { 820 | "version": "1.1.0", 821 | "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", 822 | "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" 823 | }, 824 | "node_modules/tslib": { 825 | "version": "1.14.1", 826 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 827 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 828 | }, 829 | "node_modules/typedarray": { 830 | "version": "0.0.6", 831 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 832 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 833 | }, 834 | "node_modules/util-deprecate": { 835 | "version": "1.0.2", 836 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 837 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 838 | }, 839 | "node_modules/wireless-tools": { 840 | "version": "0.19.0", 841 | "resolved": "https://registry.npmjs.org/wireless-tools/-/wireless-tools-0.19.0.tgz", 842 | "integrity": "sha1-Z/tzzTcfLZujue8lNACjH4x4SxE=" 843 | }, 844 | "node_modules/wrappy": { 845 | "version": "1.0.2", 846 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 847 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 848 | }, 849 | "node_modules/ws": { 850 | "version": "7.5.6", 851 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", 852 | "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", 853 | "engines": { 854 | "node": ">=8.3.0" 855 | }, 856 | "peerDependencies": { 857 | "bufferutil": "^4.0.1", 858 | "utf-8-validate": "^5.0.2" 859 | }, 860 | "peerDependenciesMeta": { 861 | "bufferutil": { 862 | "optional": true 863 | }, 864 | "utf-8-validate": { 865 | "optional": true 866 | } 867 | } 868 | }, 869 | "node_modules/xtend": { 870 | "version": "4.0.2", 871 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 872 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 873 | "engines": { 874 | "node": ">=0.4" 875 | } 876 | }, 877 | "node_modules/yallist": { 878 | "version": "4.0.0", 879 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 880 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 881 | } 882 | }, 883 | "dependencies": { 884 | "@types/bonjour": { 885 | "version": "3.5.10", 886 | "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", 887 | "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", 888 | "requires": { 889 | "@types/node": "*" 890 | } 891 | }, 892 | "@types/node": { 893 | "version": "13.13.52", 894 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", 895 | "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" 896 | }, 897 | "@types/node-schedule": { 898 | "version": "1.3.2", 899 | "resolved": "https://registry.npmjs.org/@types/node-schedule/-/node-schedule-1.3.2.tgz", 900 | "integrity": "sha512-Y0CqdAr+lCpArT8CJJjJq4U2v8Bb5e7ru2nV/NhDdaptCMCRdOL3Y7tAhen39HluQMaIKWvPbDuiFBUQpg7Srw==", 901 | "requires": { 902 | "@types/node": "*" 903 | } 904 | }, 905 | "array-flatten": { 906 | "version": "2.1.2", 907 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", 908 | "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" 909 | }, 910 | "axios": { 911 | "version": "0.21.4", 912 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", 913 | "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", 914 | "requires": { 915 | "follow-redirects": "^1.14.0" 916 | } 917 | }, 918 | "balanced-match": { 919 | "version": "1.0.2", 920 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 921 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 922 | }, 923 | "base64-js": { 924 | "version": "1.5.1", 925 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 926 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 927 | }, 928 | "bl": { 929 | "version": "4.1.0", 930 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 931 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 932 | "requires": { 933 | "buffer": "^5.5.0", 934 | "inherits": "^2.0.4", 935 | "readable-stream": "^3.4.0" 936 | } 937 | }, 938 | "bonjour": { 939 | "version": "3.5.0", 940 | "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", 941 | "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", 942 | "requires": { 943 | "array-flatten": "^2.1.0", 944 | "deep-equal": "^1.0.1", 945 | "dns-equal": "^1.0.0", 946 | "dns-txt": "^2.0.2", 947 | "multicast-dns": "^6.0.1", 948 | "multicast-dns-service-types": "^1.1.0" 949 | } 950 | }, 951 | "brace-expansion": { 952 | "version": "1.1.11", 953 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 954 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 955 | "requires": { 956 | "balanced-match": "^1.0.0", 957 | "concat-map": "0.0.1" 958 | } 959 | }, 960 | "buffer": { 961 | "version": "5.7.1", 962 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 963 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 964 | "requires": { 965 | "base64-js": "^1.3.1", 966 | "ieee754": "^1.1.13" 967 | } 968 | }, 969 | "buffer-from": { 970 | "version": "1.1.2", 971 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 972 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 973 | }, 974 | "buffer-indexof": { 975 | "version": "1.1.1", 976 | "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", 977 | "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" 978 | }, 979 | "call-bind": { 980 | "version": "1.0.2", 981 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 982 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 983 | "requires": { 984 | "function-bind": "^1.1.1", 985 | "get-intrinsic": "^1.0.2" 986 | } 987 | }, 988 | "commander": { 989 | "version": "5.1.0", 990 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", 991 | "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" 992 | }, 993 | "commist": { 994 | "version": "1.1.0", 995 | "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", 996 | "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", 997 | "requires": { 998 | "leven": "^2.1.0", 999 | "minimist": "^1.1.0" 1000 | } 1001 | }, 1002 | "concat-map": { 1003 | "version": "0.0.1", 1004 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1005 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 1006 | }, 1007 | "concat-stream": { 1008 | "version": "2.0.0", 1009 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", 1010 | "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", 1011 | "requires": { 1012 | "buffer-from": "^1.0.0", 1013 | "inherits": "^2.0.3", 1014 | "readable-stream": "^3.0.2", 1015 | "typedarray": "^0.0.6" 1016 | } 1017 | }, 1018 | "cron-parser": { 1019 | "version": "3.5.0", 1020 | "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", 1021 | "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", 1022 | "requires": { 1023 | "is-nan": "^1.3.2", 1024 | "luxon": "^1.26.0" 1025 | } 1026 | }, 1027 | "debug": { 1028 | "version": "4.3.3", 1029 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 1030 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 1031 | "requires": { 1032 | "ms": "2.1.2" 1033 | } 1034 | }, 1035 | "deep-equal": { 1036 | "version": "1.1.1", 1037 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", 1038 | "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", 1039 | "requires": { 1040 | "is-arguments": "^1.0.4", 1041 | "is-date-object": "^1.0.1", 1042 | "is-regex": "^1.0.4", 1043 | "object-is": "^1.0.1", 1044 | "object-keys": "^1.1.1", 1045 | "regexp.prototype.flags": "^1.2.0" 1046 | } 1047 | }, 1048 | "define-properties": { 1049 | "version": "1.1.3", 1050 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 1051 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 1052 | "requires": { 1053 | "object-keys": "^1.0.12" 1054 | } 1055 | }, 1056 | "dns-equal": { 1057 | "version": "1.0.0", 1058 | "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", 1059 | "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" 1060 | }, 1061 | "dns-packet": { 1062 | "version": "1.3.4", 1063 | "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", 1064 | "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", 1065 | "requires": { 1066 | "ip": "^1.1.0", 1067 | "safe-buffer": "^5.0.1" 1068 | } 1069 | }, 1070 | "dns-txt": { 1071 | "version": "2.0.2", 1072 | "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", 1073 | "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", 1074 | "requires": { 1075 | "buffer-indexof": "^1.0.0" 1076 | } 1077 | }, 1078 | "duplexify": { 1079 | "version": "4.1.2", 1080 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", 1081 | "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", 1082 | "requires": { 1083 | "end-of-stream": "^1.4.1", 1084 | "inherits": "^2.0.3", 1085 | "readable-stream": "^3.1.1", 1086 | "stream-shift": "^1.0.0" 1087 | } 1088 | }, 1089 | "end-of-stream": { 1090 | "version": "1.4.4", 1091 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 1092 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 1093 | "requires": { 1094 | "once": "^1.4.0" 1095 | } 1096 | }, 1097 | "follow-redirects": { 1098 | "version": "1.14.8", 1099 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", 1100 | "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" 1101 | }, 1102 | "fs.realpath": { 1103 | "version": "1.0.0", 1104 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1105 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 1106 | }, 1107 | "function-bind": { 1108 | "version": "1.1.1", 1109 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1110 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 1111 | }, 1112 | "get-intrinsic": { 1113 | "version": "1.1.1", 1114 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 1115 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 1116 | "requires": { 1117 | "function-bind": "^1.1.1", 1118 | "has": "^1.0.3", 1119 | "has-symbols": "^1.0.1" 1120 | } 1121 | }, 1122 | "getmac": { 1123 | "version": "5.20.0", 1124 | "resolved": "https://registry.npmjs.org/getmac/-/getmac-5.20.0.tgz", 1125 | "integrity": "sha512-O9T855fb+Hx9dsTJHNv72ZUuA6Y18+BO/0ypPXf6s/tunzXqhc3kbQkNAl+9HVKVlwkWmglHS4LMoJ9YbymKYQ==", 1126 | "requires": { 1127 | "@types/node": "^16.4.7" 1128 | }, 1129 | "dependencies": { 1130 | "@types/node": { 1131 | "version": "16.11.22", 1132 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz", 1133 | "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==" 1134 | } 1135 | } 1136 | }, 1137 | "glob": { 1138 | "version": "7.2.0", 1139 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 1140 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 1141 | "requires": { 1142 | "fs.realpath": "^1.0.0", 1143 | "inflight": "^1.0.4", 1144 | "inherits": "2", 1145 | "minimatch": "^3.0.4", 1146 | "once": "^1.3.0", 1147 | "path-is-absolute": "^1.0.0" 1148 | } 1149 | }, 1150 | "has": { 1151 | "version": "1.0.3", 1152 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1153 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1154 | "requires": { 1155 | "function-bind": "^1.1.1" 1156 | } 1157 | }, 1158 | "has-symbols": { 1159 | "version": "1.0.2", 1160 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", 1161 | "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" 1162 | }, 1163 | "has-tostringtag": { 1164 | "version": "1.0.0", 1165 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 1166 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 1167 | "requires": { 1168 | "has-symbols": "^1.0.2" 1169 | } 1170 | }, 1171 | "help-me": { 1172 | "version": "3.0.0", 1173 | "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", 1174 | "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", 1175 | "requires": { 1176 | "glob": "^7.1.6", 1177 | "readable-stream": "^3.6.0" 1178 | } 1179 | }, 1180 | "ieee754": { 1181 | "version": "1.2.1", 1182 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1183 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1184 | }, 1185 | "inflight": { 1186 | "version": "1.0.6", 1187 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1188 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1189 | "requires": { 1190 | "once": "^1.3.0", 1191 | "wrappy": "1" 1192 | } 1193 | }, 1194 | "inherits": { 1195 | "version": "2.0.4", 1196 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1197 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1198 | }, 1199 | "ip": { 1200 | "version": "1.1.5", 1201 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 1202 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 1203 | }, 1204 | "is-arguments": { 1205 | "version": "1.1.1", 1206 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 1207 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 1208 | "requires": { 1209 | "call-bind": "^1.0.2", 1210 | "has-tostringtag": "^1.0.0" 1211 | } 1212 | }, 1213 | "is-date-object": { 1214 | "version": "1.0.5", 1215 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 1216 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 1217 | "requires": { 1218 | "has-tostringtag": "^1.0.0" 1219 | } 1220 | }, 1221 | "is-nan": { 1222 | "version": "1.3.2", 1223 | "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", 1224 | "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", 1225 | "requires": { 1226 | "call-bind": "^1.0.0", 1227 | "define-properties": "^1.1.3" 1228 | } 1229 | }, 1230 | "is-regex": { 1231 | "version": "1.1.4", 1232 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 1233 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 1234 | "requires": { 1235 | "call-bind": "^1.0.2", 1236 | "has-tostringtag": "^1.0.0" 1237 | } 1238 | }, 1239 | "js-sdsl": { 1240 | "version": "2.1.4", 1241 | "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-2.1.4.tgz", 1242 | "integrity": "sha512-/Ew+CJWHNddr7sjwgxaVeIORIH4AMVC9dy0hPf540ZGMVgS9d3ajwuVdyhDt6/QUvT8ATjR3yuYBKsS79F+H4A==" 1243 | }, 1244 | "leven": { 1245 | "version": "2.1.0", 1246 | "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", 1247 | "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" 1248 | }, 1249 | "long-timeout": { 1250 | "version": "0.1.1", 1251 | "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", 1252 | "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ=" 1253 | }, 1254 | "lru-cache": { 1255 | "version": "6.0.0", 1256 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1257 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1258 | "requires": { 1259 | "yallist": "^4.0.0" 1260 | } 1261 | }, 1262 | "luxon": { 1263 | "version": "1.28.1", 1264 | "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", 1265 | "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==" 1266 | }, 1267 | "minimatch": { 1268 | "version": "3.1.2", 1269 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1270 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1271 | "requires": { 1272 | "brace-expansion": "^1.1.7" 1273 | } 1274 | }, 1275 | "minimist": { 1276 | "version": "1.2.6", 1277 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 1278 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" 1279 | }, 1280 | "mqtt": { 1281 | "version": "4.3.4", 1282 | "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.4.tgz", 1283 | "integrity": "sha512-yAVDfVHz3Cjn6K68z54mf7fTni/AWsPhiEsRwZSvet2wO47R6NFUn2psWxYIph2JxWtL3ZKa/da8pjJKSaXPdQ==", 1284 | "requires": { 1285 | "commist": "^1.0.0", 1286 | "concat-stream": "^2.0.0", 1287 | "debug": "^4.1.1", 1288 | "duplexify": "^4.1.1", 1289 | "help-me": "^3.0.0", 1290 | "inherits": "^2.0.3", 1291 | "lru-cache": "^6.0.0", 1292 | "minimist": "^1.2.5", 1293 | "mqtt-packet": "^6.8.0", 1294 | "number-allocator": "^1.0.9", 1295 | "pump": "^3.0.0", 1296 | "readable-stream": "^3.6.0", 1297 | "reinterval": "^1.1.0", 1298 | "rfdc": "^1.3.0", 1299 | "split2": "^3.1.0", 1300 | "ws": "^7.5.5", 1301 | "xtend": "^4.0.2" 1302 | } 1303 | }, 1304 | "mqtt-packet": { 1305 | "version": "6.10.0", 1306 | "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", 1307 | "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", 1308 | "requires": { 1309 | "bl": "^4.0.2", 1310 | "debug": "^4.1.1", 1311 | "process-nextick-args": "^2.0.1" 1312 | } 1313 | }, 1314 | "ms": { 1315 | "version": "2.1.2", 1316 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1317 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1318 | }, 1319 | "multicast-dns": { 1320 | "version": "6.2.3", 1321 | "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", 1322 | "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", 1323 | "requires": { 1324 | "dns-packet": "^1.3.1", 1325 | "thunky": "^1.0.2" 1326 | } 1327 | }, 1328 | "multicast-dns-service-types": { 1329 | "version": "1.1.0", 1330 | "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", 1331 | "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" 1332 | }, 1333 | "node-schedule": { 1334 | "version": "2.1.0", 1335 | "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", 1336 | "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", 1337 | "requires": { 1338 | "cron-parser": "^3.5.0", 1339 | "long-timeout": "0.1.1", 1340 | "sorted-array-functions": "^1.3.0" 1341 | } 1342 | }, 1343 | "number-allocator": { 1344 | "version": "1.0.9", 1345 | "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.9.tgz", 1346 | "integrity": "sha512-sIIF0dZKMs3roPUD7rLreH8H3x47QKV9dHZ+PeSnH24gL0CxKxz/823woGZC0hLBSb2Ar/rOOeHiNbnPBum/Mw==", 1347 | "requires": { 1348 | "debug": "^4.3.1", 1349 | "js-sdsl": "^2.1.2" 1350 | } 1351 | }, 1352 | "object-is": { 1353 | "version": "1.1.5", 1354 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", 1355 | "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", 1356 | "requires": { 1357 | "call-bind": "^1.0.2", 1358 | "define-properties": "^1.1.3" 1359 | } 1360 | }, 1361 | "object-keys": { 1362 | "version": "1.1.1", 1363 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1364 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 1365 | }, 1366 | "once": { 1367 | "version": "1.4.0", 1368 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1369 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1370 | "requires": { 1371 | "wrappy": "1" 1372 | } 1373 | }, 1374 | "path-is-absolute": { 1375 | "version": "1.0.1", 1376 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1377 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1378 | }, 1379 | "pauseable": { 1380 | "version": "0.3.2", 1381 | "resolved": "https://registry.npmjs.org/pauseable/-/pauseable-0.3.2.tgz", 1382 | "integrity": "sha512-QduJkcfPqTUtJiOQoUWZkmmisBA6LoAeIgGeC3b7wfICtGdo89hC174eS91pKFOegIsrS5Tla2Mh9NEvObLd0Q==" 1383 | }, 1384 | "process-nextick-args": { 1385 | "version": "2.0.1", 1386 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1387 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1388 | }, 1389 | "pump": { 1390 | "version": "3.0.0", 1391 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1392 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1393 | "requires": { 1394 | "end-of-stream": "^1.1.0", 1395 | "once": "^1.3.1" 1396 | } 1397 | }, 1398 | "qrcode-terminal": { 1399 | "version": "0.12.0", 1400 | "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", 1401 | "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==" 1402 | }, 1403 | "readable-stream": { 1404 | "version": "3.6.0", 1405 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1406 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1407 | "requires": { 1408 | "inherits": "^2.0.3", 1409 | "string_decoder": "^1.1.1", 1410 | "util-deprecate": "^1.0.1" 1411 | } 1412 | }, 1413 | "regexp.prototype.flags": { 1414 | "version": "1.4.1", 1415 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", 1416 | "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", 1417 | "requires": { 1418 | "call-bind": "^1.0.2", 1419 | "define-properties": "^1.1.3" 1420 | } 1421 | }, 1422 | "reinterval": { 1423 | "version": "1.1.0", 1424 | "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", 1425 | "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" 1426 | }, 1427 | "rfdc": { 1428 | "version": "1.3.0", 1429 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", 1430 | "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" 1431 | }, 1432 | "rxjs": { 1433 | "version": "6.6.7", 1434 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", 1435 | "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", 1436 | "requires": { 1437 | "tslib": "^1.9.0" 1438 | } 1439 | }, 1440 | "safe-buffer": { 1441 | "version": "5.2.1", 1442 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1443 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1444 | }, 1445 | "sorted-array-functions": { 1446 | "version": "1.3.0", 1447 | "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", 1448 | "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" 1449 | }, 1450 | "split2": { 1451 | "version": "3.2.2", 1452 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", 1453 | "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", 1454 | "requires": { 1455 | "readable-stream": "^3.0.0" 1456 | } 1457 | }, 1458 | "stream-shift": { 1459 | "version": "1.0.1", 1460 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", 1461 | "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" 1462 | }, 1463 | "string_decoder": { 1464 | "version": "1.3.0", 1465 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1466 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1467 | "requires": { 1468 | "safe-buffer": "~5.2.0" 1469 | } 1470 | }, 1471 | "thunky": { 1472 | "version": "1.1.0", 1473 | "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", 1474 | "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" 1475 | }, 1476 | "tslib": { 1477 | "version": "1.14.1", 1478 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 1479 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 1480 | }, 1481 | "typedarray": { 1482 | "version": "0.0.6", 1483 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1484 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 1485 | }, 1486 | "util-deprecate": { 1487 | "version": "1.0.2", 1488 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1489 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1490 | }, 1491 | "wireless-tools": { 1492 | "version": "0.19.0", 1493 | "resolved": "https://registry.npmjs.org/wireless-tools/-/wireless-tools-0.19.0.tgz", 1494 | "integrity": "sha1-Z/tzzTcfLZujue8lNACjH4x4SxE=" 1495 | }, 1496 | "wrappy": { 1497 | "version": "1.0.2", 1498 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1499 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1500 | }, 1501 | "ws": { 1502 | "version": "7.5.6", 1503 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", 1504 | "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", 1505 | "requires": {} 1506 | }, 1507 | "xtend": { 1508 | "version": "4.0.2", 1509 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1510 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 1511 | }, 1512 | "yallist": { 1513 | "version": "4.0.0", 1514 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1515 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1516 | } 1517 | } 1518 | } 1519 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blinkerjs", 3 | "version": "1.0.5", 4 | "description": "blinker nodejs sdk", 5 | "main": "blinker.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/blinker-iot/blinker-js.git" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "blinker", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@types/bonjour": "^3.5.6", 17 | "@types/node": "^13.9.5", 18 | "@types/node-schedule": "^1.3.2", 19 | "axios": "^0.21.1", 20 | "bonjour": "^3.5.0", 21 | "commander": "^5.1.0", 22 | "getmac": "^5.5.0", 23 | "mqtt": "^4.2.6", 24 | "node-schedule": "^2.0.0", 25 | "pauseable": "^0.3.2", 26 | "qrcode-terminal": "^0.12.0", 27 | "rxjs": "^6.5.5", 28 | "wireless-tools": "^0.19.0", 29 | "ws": "^7.4.6" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # blinker JavaScript/TypeScript SDK 2 | 3 | 本SDK可用于 **Linux/Windows/MacOS** 设备MQTT接入 4 | **亦适用于树莓派等带操作系统的嵌入式设备** 5 | 6 | 使用WiFi接入,当设备和手机在同一个局域网中,为局域网通信 7 | 其余情况,使用MQTT远程通信 8 | 9 | ## 准备工作 10 | 开始使用前您需要做好如下准备: 11 | 12 | **下载并安装blinker APP** 13 | **Android下载:** 14 | [点击下载](https://github.com/blinker-iot/app-release/releases) 或 在android应用商店搜索“blinker”下载安装 15 | **IOS下载:** 16 | [点击下载](https://itunes.apple.com/cn/app/id1357907814) 或 在app store中搜索“blinker”下载 17 | 18 | 19 | ## 环境/依赖安装 20 | 运行blinker程序需要最新nodejs LTS版本及Ts-Node支持 21 | ``` 22 | npm i -g ts-node 23 | git clone https://github.com/blinker-iot/blinker-js.git 24 | cd blinker-js 25 | npm i 26 | ``` 27 | 28 | ## 在app中添加设备,获取Secret Key 29 | 1. 进入App,点击右上角的“+”号,然后选择 **添加设备** 30 | 2. 点击选择**Arduino > WiFi接入** 31 | 3. 复制申请到的**Secret Key** 32 | 33 | ## DIY界面 34 | 1. 在设备列表页,点击设备图标,进入设备控制面板 35 | 2. 首次进入设备控制面板,会弹出向导页 36 | 3. 在向导页点击 **载入示例**,即可载入示例组件 37 | 38 | ## 编译并上传示例程序 39 | 40 | **.\example\example_hello.ts** 为入门示例, 替换示例中的以下语句,修改参数为app中申请到的设备的Secret Key 41 | ``` 42 | let device = new BlinkerDevice('xxxxxxxxxxxx'); 43 | ``` 44 | 运行示例程序: 45 | ``` 46 | ts-node .\example\example_hello.ts 47 | ``` 48 | 49 | > blinker在局域网通信时会使用到81端口,如遇到权限报错,请使用sudo等方式提权运行 50 | 51 | ## 恭喜!一切就绪 52 | 53 | 在APP中点击刚才您添加的设备,即可进入控制界面,点点按钮就可以控制设备了 54 | 另一个按钮也点下试试,放心,您的手机不会爆炸~ 55 | 56 | ## 进一步使用blinker 57 | 58 | #### 想了解各接入方式的区别? 59 | 看看[添加设备](?file=002-开发入门/001-添加设备 "添加设备") 60 | 61 | #### 想深入理解以上例程? 62 | 63 | 看看[Nodejs开发入门](https://diandeng.tech/doc/getting-start-nodejs "Nodejs开发入门") 和 [JavaScript/TypeScript 支持库函数参考](https://diandeng.tech/doc/javascript-support)) 64 | #### 更多示例程序? 65 | 66 | 看看[Github](https://github.com/blinker-iot/blinker-js/tree/typescript/example) 67 | 68 | #### 想制作与众不同的物联网设备? 69 | 看看[自定义界面](https://diandeng.tech/doc/layouter-2) 70 | 71 | #### 树莓派GPIO控制? 72 | 看看[pigpio](https://github.com/fivdi/pigpio) 73 | 74 | ## 完整示例程序 75 | 76 | 77 | ```javascript 78 | import { BlinkerDevice } from '../lib/blinker'; 79 | import { ButtonWidget, NumberWidget } from '../lib/widget'; 80 | 81 | let device = new BlinkerDevice(/*您申请到的Secret Key*/); 82 | 83 | // 注册组件 84 | let button1: ButtonWidget = device.addWidget(new ButtonWidget('btn-123')); 85 | let button2: ButtonWidget = device.addWidget(new ButtonWidget('btn-abc')); 86 | let number1: NumberWidget = device.addWidget(new NumberWidget('num-abc')); 87 | 88 | let num = 0; 89 | 90 | device.ready().then(() => { 91 | 92 | device.dataRead.subscribe(message => { 93 | console.log('otherData:', message); 94 | }) 95 | 96 | button1.listen().subscribe(message => { 97 | console.log('button1:', message.data); 98 | num++; 99 | number1.value(num).update(); 100 | }) 101 | 102 | button2.listen().subscribe(message => { 103 | console.log('button2:', message.data); 104 | // 其他控制代码 105 | }) 106 | 107 | }) 108 | ``` 109 | 110 | ## 为什么设备显示不在线? 111 | 112 | 0. blinker App如何判断设备是否在线? 113 | 114 | blinker App在 **App打开时、进入设备页面时、在设备页面中每隔一定时间** 会向设备发送心跳请求,内容为**{"get":"state"}**。 115 | 设备收到请求后,会返回 **{"state":"online"}**,app接收到这个返回,即会显示设备在线。 116 | 117 | 1. 程序没有成功上传到开发板 118 | 119 | 解决办法:重新上传,上传后打开串口监视器,确认程序正确运行 120 | 121 | 2. 程序中没有设置正确的ssid和密码,导致没有连接上网络 122 | 123 | 解决办法:设置后再重新上传程序,上传后打开串口监视器,确认程序正确运行 124 | 125 | 3. 程序错误,导致程序运行不正确 126 | 127 | 解决办法:先使用并理解blinker例程,再自由发挥 128 | 129 | 4. 开发板供电不足 130 | 131 | 解决办法:换电源 或 换USB口 132 | 133 | ## 为什么无法切换到局域网通信? 134 | 135 | 1. 路由器开启了AP隔离功能或禁止了UDP通信,从而阻止了局域网中设备的发现和通信 136 | 137 | 解决办法:关闭路由器AP隔离功能 或 允许UDP通信;如果找不到相关设置,通常可重置路由器解决 138 | 139 | 2. mdns没有及时发现设备 140 | 141 | 解决办法:在首页下拉刷新,可以重新搜索局域网中的设备 142 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | // "outDir": "./", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | // "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": false, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | } 66 | } 67 | --------------------------------------------------------------------------------