-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkonkeio.js
125 lines (115 loc) · 4.15 KB
/
konkeio.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const crypto = require('crypto')
const dgram = require("dgram")
const key = Buffer.from('6664736c3b6d6577726a6f706534353666647334666276666e6a77617567666f', 'hex')
const port = 27431
const zeroPad = (data) => {
if (data instanceof Buffer != true) { data = Buffer.from(data) }
const bs = data.length % 16
return data.length % 16 === 0 ? data : Buffer.concat([data, Buffer.alloc(16 - data.length % 16, 0)])
}
// aes256ecb加密,数据补0
const encrypt = message => {
const cipher = crypto.createCipheriv('aes-256-ecb', key, '')
cipher.setAutoPadding(false)
const ciphertext = Buffer.concat([cipher.update(zeroPad(message)), cipher.final()])
return ciphertext
}
// aes256ecb解密
const decrypt = message => {
const cipher = crypto.createDecipheriv('aes-256-ecb', key, '')
cipher.setAutoPadding(false)
const ciphertext = Buffer.concat([cipher.update(message), cipher.final()])
return ciphertext.toString('utf8')
}
// 广播探知局域网的设备信息
const discover = (timeout = 1000, ip = '255.255.255.255') => new Promise((resolve, reject) => {
// console.log('discover', timeout, ip)
const list = []
const socket = dgram.createSocket('udp4')
socket.on('listening', () => socket.setBroadcast(true))
socket.on('message', (message, rinfo) => {
const data = decrypt(message)
const ip = rinfo.address
const [type, mac, password, info] = data.split('%')
const [status] = info.split('#')
list.push({ ip, mac, password, status })
})
socket.send(encrypt('lan_phone%test%test%test%heart'), port, ip)
setTimeout(() => {
resolve(list)
socket.close()
}, timeout)
})
// 变更状态和检查状态
// status取值可以为open,close,check
const action = (ip, mac, password, status) => new Promise((resolve, reject) => {
// console.log('action', ip, mac, password, status)
let timer = 0
const socket = dgram.createSocket('udp4')
const cmd = `lan_phone%${mac}%${password}%${status}%relay`
socket.on('listening', () => socket.setBroadcast(true))
socket.on('message', (message, rinfo) => {
const data = decrypt(message)
const [type, mac, password, status] = data.split('%')
socket.close()
clearTimeout(timer)
if (status == 'open' || status == 'close') { resolve(status) }
else { reject(status) }
})
socket.send(encrypt(cmd), port, ip)
timer = setTimeout(() => {
reject('timeout')
socket.close()
}, 1000)
})
module.exports = RED => {
RED.nodes.registerType("konkeio-discover", class {
constructor(config) {
RED.nodes.createNode(this, config)
this.on('input', async msg => {
const list = await discover(config.timeout)
msg.payload = list
msg.list = JSON.stringify(list, null, 2)
this.send(msg)
})
}
})
RED.nodes.registerType("konkeio-action", class {
constructor(config) {
RED.nodes.createNode(this, config)
this.on('input', async msg => {
try {
// 查询这个ip的相关信息
const list = await discover(200, config.ip)
const { mac, password, status } = list[0] || {}
if(status == null){
throw 'check fail'
}
let result = status
let act = ''
// 如果为自动模式则直接通过输入的流来决定动作
if (config.sw == 'auto') {
act = !!msg.payload ? 'open' : 'close'
}
// 如果配置的是开或关则直接配置动作
else if (config.sw == 'open' || config.sw == 'close') {
act = config.sw
}
// 如果动作不为空则为自动模式或开关模式
// 并且满足动作和查询到的状态不同则才调用
if (act != '' && act != status) {
result = await action(config.ip, mac, password, act)
}
msg.status = result
msg.payload = result === 'open' ? true : false
this.send(msg)
this.status({ shape: 'dot', fill: 'green', text: `操作: ${config.sw} 状态:${result}` })
} catch (err) {
this.status({ shape: 'dot', fill: 'red', text: `操作: ${config.sw} 状态:${err}` })
msg.payload = err
this.send(msg)
}
})
}
})
}