homebridge-lib 6.3.6 → 6.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -6
- package/cli/hap.js +2 -80
- package/cli/json.js +2 -154
- package/cli/sysinfo.js +2 -69
- package/cli/upnp.js +2 -138
- package/index.js +41 -99
- package/lib/AccessoryDelegate.js +1 -1
- package/lib/AdaptiveLighting.js +3 -3
- package/lib/Delegate.js +2 -1
- package/lib/EveHomeKitTypes.js +2 -2
- package/lib/MyHomeKitTypes.js +2 -2
- package/lib/Platform.js +7 -5
- package/lib/ServiceDelegate/AccessoryInformation.js +1 -3
- package/lib/ServiceDelegate/Battery.js +1 -3
- package/lib/ServiceDelegate/Dummy.js +1 -3
- package/lib/ServiceDelegate/History.js +6 -11
- package/lib/ServiceDelegate/ServiceLabel.js +1 -3
- package/lib/UiServer.js +1 -2
- package/package.json +4 -6
- package/lib/Colour.js +0 -323
- package/lib/CommandLineParser.js +0 -311
- package/lib/CommandLineTool.js +0 -328
- package/lib/HttpClient.js +0 -478
- package/lib/JsonFormatter.js +0 -200
- package/lib/OptionParser.js +0 -886
- package/lib/SystemInfo.js +0 -522
- package/lib/UpnpClient.js +0 -217
package/lib/UpnpClient.js
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
// homebridge-lib/lib/UpnpClient.js
|
|
2
|
-
//
|
|
3
|
-
// Library for Homebridge plugins.
|
|
4
|
-
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
|
5
|
-
|
|
6
|
-
'use strict'
|
|
7
|
-
|
|
8
|
-
const homebridgeLib = require('../index')
|
|
9
|
-
|
|
10
|
-
const dgram = require('dgram')
|
|
11
|
-
const events = require('events')
|
|
12
|
-
|
|
13
|
-
const listener = {}
|
|
14
|
-
|
|
15
|
-
// Convert raw UPnP message to message object.
|
|
16
|
-
function convert (rawMessage) {
|
|
17
|
-
const message = {}
|
|
18
|
-
const lines = rawMessage.toString().trim().split('\r\n')
|
|
19
|
-
if (lines && lines[0]) {
|
|
20
|
-
message.status = lines[0]
|
|
21
|
-
for (const line of lines) {
|
|
22
|
-
const fields = line.split(': ')
|
|
23
|
-
if (fields.length === 2) {
|
|
24
|
-
message[fields[0].toLowerCase()] = fields[1]
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return message
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Universal Plug and Play client.
|
|
32
|
-
* @extends EventEmitter
|
|
33
|
-
*/
|
|
34
|
-
class UpnpClient extends events.EventEmitter {
|
|
35
|
-
/** Create a new instance of a Universal Plug and Play client.
|
|
36
|
-
* @param {object} params - Paramters.
|
|
37
|
-
* @param {function} [params.filter=() => { return true }] - Function to
|
|
38
|
-
* filter which UPnP messages should result in a
|
|
39
|
-
* {@link UpnpClient#event:deviceAlive deviceAlive} or a
|
|
40
|
-
* {@link UpnpClient#event:deviceFound deviceFound} event.
|
|
41
|
-
* @param {integer} [params.timemout=5] - Timeout (in seconds) for
|
|
42
|
-
* {@link UpnpClient@search search()} to listen for responses.
|
|
43
|
-
* @param {string} [params.class='upnp:rootdevice'] - Filter on class which UPnP
|
|
44
|
-
* messages should result in a
|
|
45
|
-
* {@link UpnpClient#event:deviceAlive deviceAlive} or a
|
|
46
|
-
* {@link UpnpClient#event:deviceFound deviceFound} event.
|
|
47
|
-
*/
|
|
48
|
-
constructor (params = {}) {
|
|
49
|
-
super()
|
|
50
|
-
this._options = {
|
|
51
|
-
filter: () => { return true },
|
|
52
|
-
hostname: '239.255.255.250',
|
|
53
|
-
port: 1900,
|
|
54
|
-
timeout: 5,
|
|
55
|
-
class: 'upnp:rootdevice'
|
|
56
|
-
}
|
|
57
|
-
const optionParser = new homebridgeLib.OptionParser(this._options)
|
|
58
|
-
optionParser.functionKey('filter')
|
|
59
|
-
// optionParser.hostKey() // TODO: need global listener per hostname.
|
|
60
|
-
optionParser.stringKey('class', true)
|
|
61
|
-
optionParser.intKey('timeout', 1, 60)
|
|
62
|
-
optionParser.parse(params)
|
|
63
|
-
this.requestId = 0
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
_onError (error) {
|
|
67
|
-
listener.socket = null
|
|
68
|
-
/** Emitted in case of error.
|
|
69
|
-
* @event UpnpClient#error
|
|
70
|
-
* @param {Error} error - The error.
|
|
71
|
-
*/
|
|
72
|
-
this.emit('error', error)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
_onListening () {
|
|
76
|
-
if (listener.init === this) {
|
|
77
|
-
listener.host = listener.socket.address().address +
|
|
78
|
-
':' + listener.socket.address().port
|
|
79
|
-
listener.socket.addMembership(this._options.hostname)
|
|
80
|
-
delete listener.init
|
|
81
|
-
}
|
|
82
|
-
/** Emitted when listening to UPnP alive broadcasts.
|
|
83
|
-
* @event UpnpClient#listening
|
|
84
|
-
* @param {string} host - IP address and port listening on.
|
|
85
|
-
*/
|
|
86
|
-
this.emit('listening', listener.host)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
_onMessage (buffer, rinfo) {
|
|
90
|
-
const rawMessage = buffer.toString().trim()
|
|
91
|
-
const message = convert(rawMessage)
|
|
92
|
-
if (
|
|
93
|
-
message.status !== 'NOTIFY * HTTP/1.1' ||
|
|
94
|
-
message.nts !== 'ssdp:alive'
|
|
95
|
-
) {
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
if (
|
|
99
|
-
this._options.class !== 'ssdp:all' &&
|
|
100
|
-
message.nt !== this._options.class
|
|
101
|
-
) {
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
if (this._options.filter(message)) {
|
|
105
|
-
/** Emitted for each alive message received, that passes the filers.
|
|
106
|
-
* @event UpnpClient#deviceAlive
|
|
107
|
-
* @param {string} address - IP address of the device.
|
|
108
|
-
* @param {object} message - The parsed message.
|
|
109
|
-
* @param {string} rawMessage - The raw message.
|
|
110
|
-
*/
|
|
111
|
-
this.emit('deviceAlive', rinfo.address, message, rawMessage)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Listen for UPnP alive broadcast messages.
|
|
116
|
-
*/
|
|
117
|
-
async listen () {
|
|
118
|
-
if (listener.socket == null) {
|
|
119
|
-
listener.socket = dgram.createSocket({ type: 'udp4', reuseAddr: true })
|
|
120
|
-
listener.init = this
|
|
121
|
-
listener.socket.bind(this._options.port)
|
|
122
|
-
} else if (listener.init != null) {
|
|
123
|
-
const [host] = await events.once(listener.init, 'listening')
|
|
124
|
-
this.emit('listening', host)
|
|
125
|
-
} else {
|
|
126
|
-
this.emit('listening', listener.host)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
listener.socket
|
|
130
|
-
.on('error', this._onError.bind(this))
|
|
131
|
-
.on('listening', this._onListening.bind(this))
|
|
132
|
-
.on('message', this._onMessage.bind(this))
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** Stop listening for UPnP alive broadcast messages.
|
|
136
|
-
*/
|
|
137
|
-
async stopListen () {
|
|
138
|
-
if (listener.socket != null) {
|
|
139
|
-
listener.socket
|
|
140
|
-
.removeListener('error', this._onError.bind(this))
|
|
141
|
-
.removeListener('listening', this._onListening.bind(this))
|
|
142
|
-
.removeListener('message', this._onMessage.bind(this))
|
|
143
|
-
this.emit('stopListening', listener.host)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Issue a UPnP search message and listen for responses.
|
|
148
|
-
*/
|
|
149
|
-
search () {
|
|
150
|
-
const socket = dgram.createSocket({ type: 'udp4' })
|
|
151
|
-
const request = Buffer.from([
|
|
152
|
-
'M-SEARCH * HTTP/1.1',
|
|
153
|
-
`HOST: ${this._options.hostname}:${this._options.port}`,
|
|
154
|
-
'MAN: "ssdp:discover"',
|
|
155
|
-
`MX: ${this._options.timeout}`,
|
|
156
|
-
`ST: ${this._options.class}`,
|
|
157
|
-
''
|
|
158
|
-
].join('\r\n'))
|
|
159
|
-
let host
|
|
160
|
-
|
|
161
|
-
socket
|
|
162
|
-
.on('error', (error) => {
|
|
163
|
-
this.emit('error', error)
|
|
164
|
-
})
|
|
165
|
-
.on('listening', () => {
|
|
166
|
-
host = socket.address().address + ':' + socket.address().port
|
|
167
|
-
/** Emitted when searching for UPnP devices.
|
|
168
|
-
* @event UpnpClient#searching
|
|
169
|
-
* @param {string} host - IP address and port listening on.
|
|
170
|
-
*/
|
|
171
|
-
this.emit('searching', host)
|
|
172
|
-
})
|
|
173
|
-
.on('message', (buffer, rinfo) => {
|
|
174
|
-
const rawMessage = buffer.toString().trim()
|
|
175
|
-
const message = convert(rawMessage)
|
|
176
|
-
if (message.status !== 'HTTP/1.1 200 OK') {
|
|
177
|
-
return
|
|
178
|
-
}
|
|
179
|
-
if (
|
|
180
|
-
this._options.class !== 'ssdp:all' &&
|
|
181
|
-
message.st !== this._options.class
|
|
182
|
-
) {
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
if (this._options.filter(message)) {
|
|
186
|
-
/** Emitted for each response received, that passes the filers.
|
|
187
|
-
* @event UpnpClient#deviceFound
|
|
188
|
-
* @param {string} address - IP address of the device.
|
|
189
|
-
* @param {object} message - The parsed message.
|
|
190
|
-
* @param {string} rawMessage - The raw message.
|
|
191
|
-
*/
|
|
192
|
-
this.emit('deviceFound', rinfo.address, message, rawMessage)
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
const requestInfo = {
|
|
197
|
-
host: this._options.hostname + ':' + this._options.port,
|
|
198
|
-
id: ++this.requestId,
|
|
199
|
-
method: 'M-SEARCH',
|
|
200
|
-
resource: '*'
|
|
201
|
-
}
|
|
202
|
-
this.emit('request', requestInfo)
|
|
203
|
-
socket.send(
|
|
204
|
-
request, 0, request.length, this._options.port, this._options.hostname
|
|
205
|
-
)
|
|
206
|
-
setTimeout(() => {
|
|
207
|
-
socket.close()
|
|
208
|
-
/** Emitted when searching has finished.
|
|
209
|
-
* @event UpnpClient#searchDone
|
|
210
|
-
* @param {string} host - IP address and port listening on.
|
|
211
|
-
*/
|
|
212
|
-
this.emit('searchDone', host)
|
|
213
|
-
}, this._options.timeout * 1000)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
module.exports = UpnpClient
|