homebridge-openwrt-control 0.0.1-beta.2 → 0.0.1-beta.3
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/CHANGELOG.md +1 -425
- package/README.md +78 -1
- package/index.js +5 -5
- package/package.json +1 -1
- package/src/apdevice.js +0 -247
- package/src/constants.js +0 -6
- package/src/functions.js +0 -81
- package/src/impulsegenerator.js +0 -41
- package/src/openwrt.js +0 -173
- package/src/swdevice.js +0 -247
package/CHANGELOG.md
CHANGED
|
@@ -7,430 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## Warning
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
- For plugin >= v1.6.0 use Homebridge UI >= v5.13.0
|
|
12
|
-
- After update to v1.0.0 and above all automations and scenes will not work and the accessory must be added to the HomeKit again
|
|
13
|
-
|
|
14
|
-
## [1.6.0] - (03.01.2026)
|
|
15
|
-
|
|
16
|
-
## Changes
|
|
17
|
-
|
|
18
|
-
- added support for Homebridge UI >= v5.13.0
|
|
19
|
-
- config schema updated
|
|
20
|
-
- readme updated
|
|
21
|
-
|
|
22
|
-
## [1.5.0] - (25.05.2025)
|
|
23
|
-
|
|
24
|
-
## Changes
|
|
25
|
-
|
|
26
|
-
- refactor mr and ms
|
|
27
|
-
- stability improvements
|
|
28
|
-
- cleanup
|
|
29
|
-
|
|
30
|
-
## [1.4.0] - (13.03.2025)
|
|
31
|
-
|
|
32
|
-
## Changes
|
|
33
|
-
|
|
34
|
-
- added possibility to disable indyvidual accessory
|
|
35
|
-
- bump dependencies
|
|
36
|
-
- config schema updated
|
|
37
|
-
- redme updated
|
|
38
|
-
- cleanup
|
|
39
|
-
|
|
40
|
-
## [1.3.2] - (10.02.2025)
|
|
41
|
-
|
|
42
|
-
## Changes
|
|
43
|
-
|
|
44
|
-
- add missing refresh interval in plugin config
|
|
45
|
-
|
|
46
|
-
## [1.3.1] - (07.02.2025)
|
|
47
|
-
|
|
48
|
-
## Changes
|
|
49
|
-
|
|
50
|
-
- stability and improvements
|
|
51
|
-
|
|
52
|
-
## [1.3.0] - (31.01.2025)
|
|
53
|
-
|
|
54
|
-
## Changes
|
|
55
|
-
|
|
56
|
-
- added possibility to disable/enable log success, info, warn, error
|
|
57
|
-
- refactor cnnect code
|
|
58
|
-
- bump dependencies
|
|
59
|
-
- config schema updated
|
|
60
|
-
- redme updated
|
|
61
|
-
- cleanup
|
|
62
|
-
|
|
63
|
-
## [1.2.0] - (01.12.2024)
|
|
64
|
-
|
|
65
|
-
## Changes
|
|
66
|
-
|
|
67
|
-
- move from commonJS to esm module
|
|
68
|
-
- moved constants.json to constants.js
|
|
69
|
-
- cleanup
|
|
70
|
-
|
|
71
|
-
## [1.1.20] - (14.08.2024)
|
|
72
|
-
|
|
73
|
-
## Changes
|
|
74
|
-
|
|
75
|
-
- remove sensitive data from debug log
|
|
76
|
-
- hide api key by typing and display in Config UI
|
|
77
|
-
|
|
78
|
-
## [1.0.0] - (04.11.2023)
|
|
79
|
-
|
|
80
|
-
## Changes
|
|
81
|
-
|
|
82
|
-
- publish Dashboard, Access Points, Switches as a separate dynamic accessory in HomeKit
|
|
83
|
-
- code refactor
|
|
84
|
-
- bump dependencies
|
|
85
|
-
- config.schema updated
|
|
86
|
-
- cleanup
|
|
87
|
-
|
|
88
|
-
## [0.18.0] - (31.10.2023)
|
|
89
|
-
|
|
90
|
-
## Changes
|
|
91
|
-
|
|
92
|
-
- added POE ports control paralle with port
|
|
93
|
-
- config.schema updated
|
|
94
|
-
- cleanup
|
|
95
|
-
|
|
96
|
-
## [0.15.0] - (08.02.2023)
|
|
97
|
-
|
|
98
|
-
## Changes
|
|
99
|
-
|
|
100
|
-
- added support for multi switch
|
|
101
|
-
- config.schema updated
|
|
102
|
-
- cleanup
|
|
103
|
-
|
|
104
|
-
## [0.14.0] - (07.02.2023)
|
|
105
|
-
|
|
106
|
-
## Changes
|
|
107
|
-
|
|
108
|
-
- added possibility expose Contact Sensor in HomeKit app for Ports and SSIDs to use with automations
|
|
109
|
-
- config.schema updated
|
|
110
|
-
- bump dependencies
|
|
111
|
-
- cleanup
|
|
112
|
-
|
|
113
|
-
## [0.13.10] - (31.12.2022)
|
|
114
|
-
|
|
115
|
-
## Changes
|
|
116
|
-
|
|
117
|
-
- bump dependencies
|
|
118
|
-
|
|
119
|
-
## [0.13.9] - (06.12.2022)
|
|
120
|
-
|
|
121
|
-
## Changes
|
|
122
|
-
|
|
123
|
-
- update dependencies
|
|
124
|
-
|
|
125
|
-
## [0.13.8] - (04.11.2022)
|
|
126
|
-
|
|
127
|
-
## Changes
|
|
128
|
-
|
|
129
|
-
- fix TypeError: Cannot read properties of undefined (reading 'updateCharacteristic')
|
|
130
|
-
|
|
131
|
-
## [0.13.7] - (02.11.2022)
|
|
132
|
-
|
|
133
|
-
## Changes
|
|
134
|
-
|
|
135
|
-
- fix status update
|
|
136
|
-
|
|
137
|
-
## [0.13.5] - (10.09.2022)
|
|
138
|
-
|
|
139
|
-
## Changes
|
|
140
|
-
|
|
141
|
-
- reconfigured update and reconnect function
|
|
142
|
-
- config schema updated
|
|
143
|
-
- increased reconnect time to 15s
|
|
144
|
-
|
|
145
|
-
## [0.13.4] - (30.08.2022)
|
|
146
|
-
|
|
147
|
-
## Changes
|
|
148
|
-
|
|
149
|
-
- fix reading 0 of undefined
|
|
150
|
-
|
|
151
|
-
## [0.13.0] - (29.08.2022)
|
|
152
|
-
|
|
153
|
-
## Changes
|
|
154
|
-
|
|
155
|
-
- added suport to controll more as 1 switch
|
|
156
|
-
- cleanup
|
|
157
|
-
|
|
158
|
-
## [0.12.2] - (29.08.2022)
|
|
159
|
-
|
|
160
|
-
## Changes
|
|
161
|
-
|
|
162
|
-
- fix swPortId reference error
|
|
163
|
-
|
|
164
|
-
## [0.12.0] - (20.08.2022)
|
|
165
|
-
|
|
166
|
-
## Changes
|
|
167
|
-
|
|
168
|
-
- added possiblity hide switch ports by port name
|
|
169
|
-
- code cleanup
|
|
170
|
-
- config schema update
|
|
171
|
-
|
|
172
|
-
## [0.11.1] - (19.08.2022)
|
|
173
|
-
|
|
174
|
-
## Changes
|
|
175
|
-
|
|
176
|
-
- fix ssid hide by name
|
|
177
|
-
|
|
178
|
-
## [0.11.0] - (19.08.2022)
|
|
179
|
-
|
|
180
|
-
## Changes
|
|
181
|
-
|
|
182
|
-
- refactor logs
|
|
183
|
-
- added possibility enable debug mode in plugin settings
|
|
184
|
-
- added possibility disable log device info on plugin start
|
|
185
|
-
- added possiblity hide uplinks ports of switches
|
|
186
|
-
- update config schema
|
|
187
|
-
- rebuild data refresh and network reconnect if error
|
|
188
|
-
|
|
189
|
-
## [0.10.23] - (23.07.2022)
|
|
190
|
-
|
|
191
|
-
## Changes
|
|
192
|
-
|
|
193
|
-
- refactor information service
|
|
194
|
-
|
|
195
|
-
## [0.10.21] - (25.04.2022)
|
|
196
|
-
|
|
197
|
-
## Changes
|
|
198
|
-
|
|
199
|
-
- update dependencies
|
|
200
|
-
|
|
201
|
-
## [0.10.20] - (24.04.2022)
|
|
202
|
-
|
|
203
|
-
## Changes
|
|
204
|
-
|
|
205
|
-
- update dependencies
|
|
206
|
-
|
|
207
|
-
## [0.10.18] - (18.01.2022)
|
|
208
|
-
|
|
209
|
-
## Changes
|
|
210
|
-
|
|
211
|
-
- update dependencies
|
|
212
|
-
|
|
213
|
-
## [0.10.17] - (17.01.2022)
|
|
214
|
-
|
|
215
|
-
## Changes
|
|
216
|
-
|
|
217
|
-
- update dependencies
|
|
218
|
-
|
|
219
|
-
## [0.10.16] - (29.12.2021)
|
|
220
|
-
|
|
221
|
-
- prepare directory and files synchronously
|
|
222
|
-
|
|
223
|
-
## [0.10.15] - (28.12.2021)
|
|
224
|
-
|
|
225
|
-
- update node minimum requirements
|
|
226
|
-
|
|
227
|
-
## [0.10.4] - (26.09.2021)
|
|
228
|
-
|
|
229
|
-
## Changes
|
|
230
|
-
|
|
231
|
-
- code cleanup and refactor
|
|
232
|
-
|
|
233
|
-
## [0.10.3] - (26.09.2021)
|
|
234
|
-
|
|
235
|
-
## Changes
|
|
236
|
-
|
|
237
|
-
- config.schema update
|
|
238
|
-
- switch ports names display improvements
|
|
239
|
-
|
|
240
|
-
## [0.10.2] - (26.09.2021)
|
|
241
|
-
|
|
242
|
-
## Changes
|
|
243
|
-
|
|
244
|
-
- config.schema update
|
|
245
|
-
- readme.md update
|
|
246
|
-
- other small fixes
|
|
247
|
-
|
|
248
|
-
## [0.10.1] - (25.09.2021)
|
|
249
|
-
|
|
250
|
-
## Changes
|
|
251
|
-
|
|
252
|
-
- added possibility to disable/enable control for indyvidual switch
|
|
253
|
-
|
|
254
|
-
## [0.10.0] - (25.09.2021)
|
|
255
|
-
|
|
256
|
-
## Changes
|
|
257
|
-
|
|
258
|
-
### WARNING!! - after this update needs to be configured plugin again
|
|
259
|
-
|
|
260
|
-
- added switch ports control ON/OFF
|
|
261
|
-
|
|
262
|
-
## [0.9.1] - (25.09.2021)
|
|
263
|
-
|
|
264
|
-
## Changes
|
|
265
|
-
|
|
266
|
-
- added possibility set policy type for configured clients
|
|
267
|
-
|
|
268
|
-
## [0.9.0] - (25.09.2021)
|
|
269
|
-
|
|
270
|
-
## Changes
|
|
271
|
-
|
|
272
|
-
### WARNING!! - after this update needs to be configured Clients again
|
|
273
|
-
|
|
274
|
-
- config.schema update
|
|
275
|
-
- removed possibility to display clients policy by Meraki Description, please use only MAC Address
|
|
276
|
-
- added mode ON/OFF for configured clients
|
|
277
|
-
- code cleanup
|
|
278
|
-
|
|
279
|
-
## [0.8.21] - (20.09.2021)
|
|
280
|
-
|
|
281
|
-
## Changes
|
|
282
|
-
|
|
283
|
-
- config.schema update
|
|
284
|
-
- prevent use of empty SSID to be hidden
|
|
285
|
-
|
|
286
|
-
## [0.8.20] - (19.09.2021)
|
|
287
|
-
|
|
288
|
-
## Changes
|
|
289
|
-
|
|
290
|
-
- code cleanup
|
|
291
|
-
- fix timeout
|
|
292
|
-
|
|
293
|
-
## [0.8.17] - (08.09.2021)
|
|
294
|
-
|
|
295
|
-
## Changes
|
|
296
|
-
|
|
297
|
-
- bump dependencies
|
|
298
|
-
- fixed socket hangup in some sceneri
|
|
299
|
-
- revert host properties in config
|
|
300
|
-
- stability improvements
|
|
301
|
-
|
|
302
|
-
## [0.8.16] - (05.09.2021)
|
|
303
|
-
|
|
304
|
-
## Changes
|
|
305
|
-
|
|
306
|
-
- bump dependencies
|
|
307
|
-
|
|
308
|
-
## [0.8.15] - (04.09.2021)
|
|
309
|
-
|
|
310
|
-
## Changes
|
|
311
|
-
|
|
312
|
-
- bump dependencies
|
|
313
|
-
|
|
314
|
-
## [0.8.9] - (30.08.2021)
|
|
315
|
-
|
|
316
|
-
## Changes
|
|
317
|
-
|
|
318
|
-
- fixed Client that is not on the network tand exist in plugin config hrows 404 error.
|
|
319
|
-
|
|
320
|
-
## [0.8.8] - (29.08.2021)
|
|
321
|
-
|
|
322
|
-
## Changes
|
|
323
|
-
|
|
324
|
-
- added ON/OFF function for filtered SSIDs
|
|
325
|
-
|
|
326
|
-
## [0.8.7] - (29.08.2021)
|
|
327
|
-
|
|
328
|
-
## Changes
|
|
329
|
-
|
|
330
|
-
- fixed anomaly of switch policy behaviour
|
|
331
|
-
|
|
332
|
-
## [0.8.6] - (29.08.2021)
|
|
333
|
-
|
|
334
|
-
## Changes
|
|
335
|
-
|
|
336
|
-
- removed host properties from config, no nedded anymore
|
|
337
|
-
- code cleanup and some improvements
|
|
338
|
-
- update readme
|
|
339
|
-
|
|
340
|
-
## [0.8.5] - (29.08.2021)
|
|
341
|
-
|
|
342
|
-
## Changes
|
|
343
|
-
|
|
344
|
-
- added possibility to chose between Name or Mac Adress for clients to be exposed with its poplicy state.
|
|
345
|
-
- added possibility to set custom Name to be exposed for client policy.
|
|
346
|
-
- code cleanup
|
|
347
|
-
|
|
348
|
-
## [0.8.2] - (28.08.2021)
|
|
349
|
-
|
|
350
|
-
## Changes
|
|
351
|
-
|
|
352
|
-
- added possibility to expose clients and change its policy.
|
|
353
|
-
|
|
354
|
-
## [0.8.2] - (27.08.2021)
|
|
355
|
-
|
|
356
|
-
## Changes
|
|
357
|
-
|
|
358
|
-
- added possibility to hidden SSIDSs by custom configured name.
|
|
359
|
-
|
|
360
|
-
## [0.8.0] - (26.08.2021)
|
|
361
|
-
|
|
362
|
-
## Changes
|
|
363
|
-
|
|
364
|
-
- added possibility to hidde all unconfigured SSIDs
|
|
365
|
-
|
|
366
|
-
## [0.6.0] - (23.02.2021)
|
|
367
|
-
|
|
368
|
-
## Changes
|
|
369
|
-
|
|
370
|
-
- code rebuild, use Characteristic.onSet, Characteristic.onGet
|
|
371
|
-
- require Homebridge 1.3.x or above
|
|
372
|
-
|
|
373
|
-
## [0.5.9] - (15.02.2021)
|
|
374
|
-
|
|
375
|
-
## Changes
|
|
376
|
-
|
|
377
|
-
- added possibility disable log info, options available in config
|
|
378
|
-
|
|
379
|
-
## [0.5.0] - (04.02.2021)
|
|
380
|
-
|
|
381
|
-
## Changs
|
|
382
|
-
|
|
383
|
-
- code rebuild
|
|
384
|
-
- automatically detect all possible SSIDs
|
|
385
|
-
|
|
386
|
-
## [0.4.42] - (01.01.2021)
|
|
387
|
-
|
|
388
|
-
## Changs
|
|
389
|
-
|
|
390
|
-
- bump dependiencies
|
|
391
|
-
|
|
392
|
-
## [0.4.0] - (09.09.2020)
|
|
393
|
-
|
|
394
|
-
## Changs
|
|
395
|
-
|
|
396
|
-
- added await/async function
|
|
397
|
-
|
|
398
|
-
## [0.3.1] - (07.09.2020)
|
|
399
|
-
|
|
400
|
-
## Changs
|
|
401
|
-
|
|
402
|
-
- added device info log
|
|
403
|
-
- fixed wlan state update
|
|
404
|
-
|
|
405
|
-
## [0.2.0] - (06.09.2020)
|
|
406
|
-
|
|
407
|
-
## Changs
|
|
408
|
-
|
|
409
|
-
- completly reconfigured layout of config schema
|
|
410
|
-
|
|
411
|
-
## [0.1.11] - (25.08.2020)
|
|
412
|
-
|
|
413
|
-
### Changes
|
|
414
|
-
|
|
415
|
-
- performance improvements
|
|
416
|
-
- other small fixes
|
|
417
|
-
|
|
418
|
-
## [0.1.8] - (04.08.2020)
|
|
419
|
-
|
|
420
|
-
- performance changes
|
|
421
|
-
|
|
422
|
-
## [0.1.4] - (03.08.2020)
|
|
423
|
-
|
|
424
|
-
- preparing for future update
|
|
425
|
-
|
|
426
|
-
## [0.1.3] - (03.08.2020)
|
|
427
|
-
|
|
428
|
-
- code cleanup
|
|
429
|
-
|
|
430
|
-
## [0.1.0] - (03.08.2020)
|
|
431
|
-
|
|
432
|
-
- working version
|
|
433
|
-
|
|
434
|
-
## [0.0.1] - (02.08.2020)
|
|
10
|
+
## [0.0.1] - (16.1.2025)
|
|
435
11
|
|
|
436
12
|
- initial release (WLAN control)
|
package/README.md
CHANGED
|
@@ -1,2 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://github.com/grzegorz914/homebridge-openwrt-control"><img src="https://raw.githubusercontent.com/grzegorz914/homebridge-openwrt-control/main/graphics/openwrt.png" width="640"></a>
|
|
3
|
+
</p>
|
|
2
4
|
|
|
5
|
+
<span align="center">
|
|
6
|
+
|
|
7
|
+
# Homebridge OpenWrt Control
|
|
8
|
+
|
|
9
|
+
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
10
|
+
[](https://www.npmjs.com/package/homebridge-openwrt-control)
|
|
11
|
+
[](https://www.npmjs.com/package/homebridge-openwrt-control)
|
|
12
|
+
[](https://www.npmjs.com/package/homebridge-openwrt-control)
|
|
13
|
+
[](https://github.com/grzegorz914/homebridge-openwrt-control/pulls)
|
|
14
|
+
[](https://github.com/grzegorz914/homebridge-openwrt-control/issues)
|
|
15
|
+
|
|
16
|
+
Homebridge plugin for OpenWrt flashed devices.
|
|
17
|
+
|
|
18
|
+
</span>
|
|
19
|
+
|
|
20
|
+
## Package Requirements
|
|
21
|
+
|
|
22
|
+
| Package | Installation | Role | Required |
|
|
23
|
+
| --- | --- | --- | --- |
|
|
24
|
+
| [Homebridge](https://github.com/homebridge/homebridge) | [Homebridge Wiki](https://github.com/homebridge/homebridge/wiki) | HomeKit Bridge | Required |
|
|
25
|
+
| [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x) | [Homebridge UI Wiki](https://github.com/homebridge/homebridge-config-ui-x/wiki) | Homebridge User Interface | Recommended |
|
|
26
|
+
| [OpenWrt Control](https://www.npmjs.com/package/homebridge-openwrt-control) | [Plug-In Wiki](https://github.com/grzegorz914/homebridge-openwrt-control/wiki) | Homebridge Plug-In | Required |
|
|
27
|
+
|
|
28
|
+
## About The Plugin
|
|
29
|
+
|
|
30
|
+
* Access Points:
|
|
31
|
+
* Control `ON/OFF` every SSID.
|
|
32
|
+
* Monitor with Sensors for every `SSIDs`.
|
|
33
|
+
* Siri can be used to switch ON/OFF SSIDs, Policy, Ports.
|
|
34
|
+
* Home automations and shortcuts can be used for all functions.
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
* Run this plugin as a [Child Bridge](https://github.com/homebridge/homebridge/wiki/Child-Bridges) (Highly Recommended), this prevent crash Homebridge if plugin crashes.
|
|
39
|
+
* Install and use [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x/wiki) to configure this plugin.
|
|
40
|
+
* The `sample-config.json` can be edited and used as an alternative.
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<a href="https://github.com/grzegorz914/homebridge-openwrt-control"><img src="https://raw.githubusercontent.com/grzegorz914/homebridge-openwrt-control/main/graphics/ustawienia.png" width="840"></a>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
| Key | Description |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `name` | Here set the accessory `Name` to be displayed in `Homebridge/HomeKit`. |
|
|
49
|
+
| `host` | Here set the `Hsostname or Address IP` of Sat Receiver.|
|
|
50
|
+
| `displayType` | Accessory type to be displayed in Home app: `0 - Disable`, `1 - Enable`. |
|
|
51
|
+
| `auth{}` | Authorization object. |
|
|
52
|
+
| `auth.enable` | If enabled, authorizatins credentials will be used for login. |
|
|
53
|
+
| `auth.user` | Here set the authorization `Username`. |
|
|
54
|
+
| `auth.passwd` | Here set the authorization `Password`. |
|
|
55
|
+
| `accessPoint{}` | Volume object. |
|
|
56
|
+
| `accessPoint.enable` | Here enable access point `SSIDs` control. |
|
|
57
|
+
| `accessPoint.name` | Here set Your own volume/mute control name or leave empty. |
|
|
58
|
+
| `accessPoint.namePrefix` | Here enable accessory name as a prefix for volume control name. |
|
|
59
|
+
| `accessPoint.sensor` | Here enable access point `SSIDs` sensors. |
|
|
60
|
+
| `refreshInterval` | Here set the data refresh time in seconds. |
|
|
61
|
+
| `log.deviceInfo` | If enabled, log device info will be displayed by every connections device to the network. |
|
|
62
|
+
| `log.success` | If enabled, success log will be displayed in console. |
|
|
63
|
+
| `log.info` | If enabled, info log will be displayed in console. |
|
|
64
|
+
| `log.warn` | If enabled, warn log will be displayed in console. |
|
|
65
|
+
| `log.error` | If enabled, error log will be displayed in console. |
|
|
66
|
+
| `log.debug` | If enabled, debug log will be displayed in console. |
|
|
67
|
+
| `restFul{}` | RESTFul object. |
|
|
68
|
+
| `restFul.enable` | If enabled, RESTful server will start automatically and respond to any path request. |
|
|
69
|
+
| `restFul.port` | Here set the listening `Port` for RESTful server. |
|
|
70
|
+
| `mqtt{}` | MQTT object. |
|
|
71
|
+
| `mqtt.enable` | If enabled, MQTT Broker will start automatically and publish all awailable data. |
|
|
72
|
+
| `mqtt.host` | Here set the `IP Address` or `Hostname` for MQTT Broker. |
|
|
73
|
+
| `mqtt.port` | Here set the `Port` for MQTT Broker, default 1883. |
|
|
74
|
+
| `mqtt.clientId` | Here optional set the `Client Id` of MQTT Broker. |
|
|
75
|
+
| `mqtt.prefix` | Here set the `Prefix` for `Topic` or leave empty. |
|
|
76
|
+
| `mqtt.auth{}` | MQTT authorization object. |
|
|
77
|
+
| `mqtt.auth.enable` | Here enable authorization for MQTT Broker. |
|
|
78
|
+
| `mqtt.auth.user` | Here set the MQTT Broker user. |
|
|
79
|
+
| `mqtt.auth.passwd` | Here set the MQTT Broker password. |
|
package/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
2
|
import { mkdirSync, existsSync, writeFileSync } from 'fs';
|
|
3
|
-
import OpenWrt from './src/openwrt.js';
|
|
4
|
-
import AccessPoint from './src/apdevice.js';
|
|
5
|
-
import Switch from './src/swdevice.js';
|
|
6
|
-
import ImpulseGenerator from './src/impulsegenerator.js';
|
|
7
|
-
import { PluginName, PlatformName } from './src/constants.js';
|
|
3
|
+
import OpenWrt from './graphics/src/openwrt.js';
|
|
4
|
+
import AccessPoint from './graphics/src/apdevice.js';
|
|
5
|
+
import Switch from './graphics/src/swdevice.js';
|
|
6
|
+
import ImpulseGenerator from './graphics/src/impulsegenerator.js';
|
|
7
|
+
import { PluginName, PlatformName } from './graphics/src/constants.js';
|
|
8
8
|
|
|
9
9
|
class OpenWrtPlatform {
|
|
10
10
|
constructor(log, config, api) {
|
package/package.json
CHANGED
package/src/apdevice.js
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import EventEmitter from 'events';
|
|
2
|
-
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
3
|
-
|
|
4
|
-
class AccessPointDevice extends EventEmitter {
|
|
5
|
-
constructor(api, config, openWrt, openWrtInfo) {
|
|
6
|
-
super();
|
|
7
|
-
|
|
8
|
-
Accessory = api.platformAccessory;
|
|
9
|
-
Characteristic = api.hap.Characteristic;
|
|
10
|
-
Service = api.hap.Service;
|
|
11
|
-
Categories = api.hap.Categories;
|
|
12
|
-
AccessoryUUID = api.hap.uuid;
|
|
13
|
-
|
|
14
|
-
//config
|
|
15
|
-
this.name = config.name;
|
|
16
|
-
this.accessPoint = config.accessPoint;
|
|
17
|
-
this.logInfo = config.log?.info || false;
|
|
18
|
-
this.logDebug = config.log?.debug || false;
|
|
19
|
-
|
|
20
|
-
//external integration
|
|
21
|
-
this.restFul = config.restFul || {};
|
|
22
|
-
this.restFulConnected = false;
|
|
23
|
-
this.mqtt = config.mqtt || {};
|
|
24
|
-
this.mqttConnected = false;
|
|
25
|
-
|
|
26
|
-
//openwrt
|
|
27
|
-
this.openWrt = openWrt;
|
|
28
|
-
this.openWrtInfo = openWrtInfo;
|
|
29
|
-
this.ssids = openWrtInfo.ssids;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
async externalIntegrations() {
|
|
33
|
-
//RESTFul server
|
|
34
|
-
const restFulEnabled = this.restFul.enable || false;
|
|
35
|
-
if (restFulEnabled) {
|
|
36
|
-
try {
|
|
37
|
-
this.restFul1 = new RestFul({
|
|
38
|
-
port: this.restFul.port || 3000,
|
|
39
|
-
logWarn: this.logWarn,
|
|
40
|
-
logDebug: this.logDebug
|
|
41
|
-
})
|
|
42
|
-
.on('connected', (message) => {
|
|
43
|
-
this.emit('success', message);
|
|
44
|
-
this.restFulConnected = true;
|
|
45
|
-
})
|
|
46
|
-
.on('set', async (key, value) => {
|
|
47
|
-
try {
|
|
48
|
-
await this.setOverExternalIntegration('RESTFul', key, value);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
this.emit('warn', `RESTFul set error: ${error}`);
|
|
51
|
-
};
|
|
52
|
-
})
|
|
53
|
-
.on('debug', (debug) => this.emit('debug', debug))
|
|
54
|
-
.on('warn', (warn) => this.emit('warn', warn))
|
|
55
|
-
.on('error', (error) => this.emit('error', error));
|
|
56
|
-
} catch (error) {
|
|
57
|
-
this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
//mqtt client
|
|
62
|
-
const mqttEnabled = this.mqtt.enable || false;
|
|
63
|
-
if (mqttEnabled) {
|
|
64
|
-
try {
|
|
65
|
-
this.mqtt1 = new Mqtt({
|
|
66
|
-
host: this.mqtt.host,
|
|
67
|
-
port: this.mqtt.port || 1883,
|
|
68
|
-
clientId: this.mqtt.clientId ? `${this.savedInfo.manufacturer}_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `${this.savedInfo.manufacturer}_${Math.random().toString(16).slice(3)}`,
|
|
69
|
-
prefix: this.mqtt.prefix ? `${this.savedInfo.manufacturer}/${this.mqtt.prefix}/${this.name}` : `${this.savedInfo.manufacturer}/${this.name}`,
|
|
70
|
-
user: this.mqtt.auth?.user,
|
|
71
|
-
passwd: this.mqtt.auth?.passwd,
|
|
72
|
-
logWarn: this.logWarn,
|
|
73
|
-
logDebug: this.logDebug
|
|
74
|
-
})
|
|
75
|
-
.on('connected', (message) => {
|
|
76
|
-
this.emit('success', message);
|
|
77
|
-
this.mqttConnected = true;
|
|
78
|
-
})
|
|
79
|
-
.on('subscribed', (message) => {
|
|
80
|
-
this.emit('success', message);
|
|
81
|
-
})
|
|
82
|
-
.on('set', async (key, value) => {
|
|
83
|
-
try {
|
|
84
|
-
await this.setOverExternalIntegration('MQTT', key, value);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
this.emit('warn', `MQTT set error: ${error}`);
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
.on('debug', (debug) => this.emit('debug', debug))
|
|
90
|
-
.on('warn', (warn) => this.emit('warn', warn))
|
|
91
|
-
.on('error', (error) => this.emit('error', error));
|
|
92
|
-
} catch (error) {
|
|
93
|
-
this.emit('warn', `MQTT integration start error: ${error}`);
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async setOverExternalIntegration(integration, key, value) {
|
|
101
|
-
try {
|
|
102
|
-
let set = false
|
|
103
|
-
switch (key) {
|
|
104
|
-
case 'Power':
|
|
105
|
-
const powerState = value ? 'ON' : 'OFF';
|
|
106
|
-
set = await this.openWrt.send('Power', powerState);
|
|
107
|
-
break;
|
|
108
|
-
default:
|
|
109
|
-
this.emit('warn', `${integration}, received key: ${key}, value: ${value}`);
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
return set;
|
|
113
|
-
} catch (error) {
|
|
114
|
-
throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
//prepare accessory
|
|
119
|
-
async prepareAccessory() {
|
|
120
|
-
try {
|
|
121
|
-
//prepare accessory
|
|
122
|
-
if (this.logDebug) this.emit('debug', `prepare accessory`);
|
|
123
|
-
const accessoryName = this.name;
|
|
124
|
-
const accessoryUUID = AccessoryUUID.generate(this.deviceUuid);
|
|
125
|
-
const accessoryCategory = Categories.AIRPORT;
|
|
126
|
-
const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
|
|
127
|
-
|
|
128
|
-
//prepare information service
|
|
129
|
-
if (this.logDebug) this.emit('debug', `prepare information service`);
|
|
130
|
-
accessory.getService(Service.AccessoryInformation)
|
|
131
|
-
.setCharacteristic(Characteristic.Manufacturer, 'OpenWrt')
|
|
132
|
-
.setCharacteristic(Characteristic.Model, accessoryName)
|
|
133
|
-
.setCharacteristic(Characteristic.SerialNumber, this.networkId)
|
|
134
|
-
.setCharacteristic(Characteristic.FirmwareRevision, this.organizationId)
|
|
135
|
-
.setCharacteristic(Characteristic.ConfiguredName, accessoryName);
|
|
136
|
-
|
|
137
|
-
if (this.logDebug) this.emit('debug', `prepare service`);
|
|
138
|
-
|
|
139
|
-
//device
|
|
140
|
-
this.services = [];
|
|
141
|
-
for (const ssid of this.ssids) {
|
|
142
|
-
const ssidName = ssid.name;
|
|
143
|
-
if (this.logDebug) this.emit('debug', `prepare ssid: ${ssidName} service`);
|
|
144
|
-
|
|
145
|
-
const serviceName = this.accessPoint.namePrefix ? `${this.name} ${ssidName}` : ssidName;
|
|
146
|
-
const service = accessory.addService(Service.Switch, serviceName, `service${ssidName}`);
|
|
147
|
-
service.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
148
|
-
service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
149
|
-
service.getCharacteristic(Characteristic.On)
|
|
150
|
-
.onGet(async () => {
|
|
151
|
-
const state = ssid.state;
|
|
152
|
-
if (this.logInfo) this.emit('message', `SSID: ${ssidName}, state: ${state ? 'Enabled' : 'Disabled'}`);
|
|
153
|
-
return state;
|
|
154
|
-
})
|
|
155
|
-
.onSet(async (state) => {
|
|
156
|
-
try {
|
|
157
|
-
state = state ? true : false;
|
|
158
|
-
await this.openWrt.send('ssid', ssidName, state);
|
|
159
|
-
if (this.logInfo) this.emit('message', `SSID: ${ssidName}, set State: ${state ? 'Enabled' : 'Disabled'}`);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
this.emit('warn', `SSID: ${ssidName}, set state error: ${error}`);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
this.services.push(service);
|
|
165
|
-
|
|
166
|
-
if (this.accessPoint.sensor) {
|
|
167
|
-
if (this.logDebug) this.emit('debug', `prepare ssid: ${ssidName} sensor service`);
|
|
168
|
-
|
|
169
|
-
this.sensorServices = [];
|
|
170
|
-
const sensorService = accessory.addService(Service.ContactSensor, ssidName, `sensorService${ssidName}`);
|
|
171
|
-
sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
172
|
-
sensorService.setCharacteristic(Characteristic.ConfiguredName, ssidName);
|
|
173
|
-
sensorService.getCharacteristic(Characteristic.ContactSensorState)
|
|
174
|
-
.onGet(async () => {
|
|
175
|
-
const state = ssid.state;
|
|
176
|
-
return state;
|
|
177
|
-
});
|
|
178
|
-
this.sensorServices.push(sensorService);
|
|
179
|
-
};
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
return accessory;
|
|
183
|
-
} catch (error) {
|
|
184
|
-
throw new Error(error);
|
|
185
|
-
};
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
//start
|
|
189
|
-
async start() {
|
|
190
|
-
try {
|
|
191
|
-
//start external integrations
|
|
192
|
-
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
193
|
-
|
|
194
|
-
this.emit('devInfo', `-------- Access Point ${this.name} --------`);
|
|
195
|
-
this.emit('devInfo', `Name: ${this.openWrtInfo.systemInfo.hostname}`);
|
|
196
|
-
this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model}`);
|
|
197
|
-
this.emit('devInfo', `System: ${this.openWrtInfo.systemInfo.system}`);
|
|
198
|
-
this.emit('devInfo', `Release: ${this.openWrtInfo.systemInfo.release?.description}`);
|
|
199
|
-
this.emit('devInfo', `----------------------------------`);
|
|
200
|
-
|
|
201
|
-
//denon client
|
|
202
|
-
this.openWrt.on('systemInfo', (info) => {
|
|
203
|
-
this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, info.release?.version);
|
|
204
|
-
})
|
|
205
|
-
.on('wirelessStatus', async (status) => {
|
|
206
|
-
})
|
|
207
|
-
.on('wirelessRadios', async (radios) => {
|
|
208
|
-
})
|
|
209
|
-
.on('ssids', async (ssids) => {
|
|
210
|
-
// sensors
|
|
211
|
-
for (let i = 0; i < ssids.length; i++) {
|
|
212
|
-
const ssid = ssids[i];
|
|
213
|
-
|
|
214
|
-
const name = ssid[i].name;
|
|
215
|
-
const state = ssid[i].state;
|
|
216
|
-
const serviceName = this.accessPoint.namePrefix ? `${this.name} ${name}` : name;
|
|
217
|
-
this.services?.[i]
|
|
218
|
-
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
219
|
-
.updateCharacteristic(Characteristic.On, state);
|
|
220
|
-
|
|
221
|
-
this.sensorServices?.[i]
|
|
222
|
-
?.setCharacteristic(Characteristic.ConfiguredName, name)
|
|
223
|
-
.updateCharacteristic(Characteristic.ContactSensorState, state ? 0 : 1);
|
|
224
|
-
|
|
225
|
-
if (this.logInfo) {
|
|
226
|
-
this.emit('info', `SSID name: ${ssid.name}`);
|
|
227
|
-
this.emit('info', `Name: ${ssid.state}`);
|
|
228
|
-
this.emit('info', `Mode: ${ssid.mode}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
})
|
|
232
|
-
.on('restFul', (path, data) => {
|
|
233
|
-
if (this.restFulConnected) this.restFul1.update(path, data);
|
|
234
|
-
})
|
|
235
|
-
.on('mqtt', (topic, message) => {
|
|
236
|
-
if (this.mqttConnected) this.mqtt1.emit('publish', topic, message);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
//prepare accessory
|
|
240
|
-
const accessory = await this.prepareAccessory();
|
|
241
|
-
return accessory;
|
|
242
|
-
} catch (error) {
|
|
243
|
-
throw new Error(`Start error: ${error}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
export default AccessPointDevice;
|
package/src/constants.js
DELETED
package/src/functions.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { promises as fsPromises } from 'fs';
|
|
2
|
-
import { DiacriticsMap } from './constants.js';
|
|
3
|
-
|
|
4
|
-
class Functions {
|
|
5
|
-
constructor() {
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async saveData(path, data, stringify = true) {
|
|
9
|
-
try {
|
|
10
|
-
data = stringify ? JSON.stringify(data, null, 2) : data;
|
|
11
|
-
await fsPromises.writeFile(path, data);
|
|
12
|
-
return true;
|
|
13
|
-
} catch (error) {
|
|
14
|
-
throw new Error(`Save data error: ${error}`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async readData(path, parseJson = false) {
|
|
19
|
-
try {
|
|
20
|
-
const data = await fsPromises.readFile(path, 'utf8');
|
|
21
|
-
|
|
22
|
-
if (parseJson) {
|
|
23
|
-
if (!data.trim()) {
|
|
24
|
-
// Empty file when expecting JSON
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
return JSON.parse(data);
|
|
29
|
-
} catch (jsonError) {
|
|
30
|
-
throw new Error(`JSON parse error in file "${path}": ${jsonError.message}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// For non-JSON, just return file content (can be empty string)
|
|
35
|
-
return data;
|
|
36
|
-
} catch (error) {
|
|
37
|
-
if (error.code === 'ENOENT') {
|
|
38
|
-
// File does not exist
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
// Preserve original error details
|
|
42
|
-
const wrappedError = new Error(`Read data error for "${path}": ${error.message}`);
|
|
43
|
-
wrappedError.original = error;
|
|
44
|
-
throw wrappedError;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async sanitizeString(str) {
|
|
49
|
-
if (!str) return '';
|
|
50
|
-
|
|
51
|
-
// Replace diacritics using map
|
|
52
|
-
str = str.replace(/[^\u0000-\u007E]/g, ch => DiacriticsMap[ch] || ch);
|
|
53
|
-
|
|
54
|
-
// Replace separators between words with space
|
|
55
|
-
str = str.replace(/(\w)[.:;+\-\/]+(\w)/g, '$1 $2');
|
|
56
|
-
|
|
57
|
-
// Replace remaining standalone separators with space
|
|
58
|
-
str = str.replace(/[.:;+\-\/]/g, ' ');
|
|
59
|
-
|
|
60
|
-
// Remove remaining invalid characters (keep letters, digits, space, apostrophe)
|
|
61
|
-
str = str.replace(/[^A-Za-z0-9 ']/g, ' ');
|
|
62
|
-
|
|
63
|
-
// Collapse multiple spaces
|
|
64
|
-
str = str.replace(/\s+/g, ' ');
|
|
65
|
-
|
|
66
|
-
// Trim
|
|
67
|
-
return str.trim();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async findIfaceBySsid(status, targetSsid) {
|
|
71
|
-
for (const radio of Object.values(status.radios)) {
|
|
72
|
-
for (const iface of Object.values(radio.interfaces)) {
|
|
73
|
-
if (iface.ssid === targetSsid) {
|
|
74
|
-
return iface;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
export default Functions
|
package/src/impulsegenerator.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import EventEmitter from 'events';
|
|
2
|
-
|
|
3
|
-
class ImpulseGenerator extends EventEmitter {
|
|
4
|
-
constructor() {
|
|
5
|
-
super();
|
|
6
|
-
this.timersState = false;
|
|
7
|
-
this.timers = [];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async state(state, timers = []) {
|
|
11
|
-
// Stop current timers before new start
|
|
12
|
-
if (this.timersState && state) {
|
|
13
|
-
await this.state(false);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (state) {
|
|
17
|
-
if (!Array.isArray(timers)) throw new Error('Timers must be an array');
|
|
18
|
-
|
|
19
|
-
for (const { name, sampling } of timers) {
|
|
20
|
-
if (!name || !sampling) continue;
|
|
21
|
-
|
|
22
|
-
this.emit(name);
|
|
23
|
-
|
|
24
|
-
const interval = setInterval(() => {
|
|
25
|
-
this.emit(name);
|
|
26
|
-
}, sampling);
|
|
27
|
-
|
|
28
|
-
this.timers.push(interval);
|
|
29
|
-
}
|
|
30
|
-
} else {
|
|
31
|
-
this.timers.forEach(clearInterval);
|
|
32
|
-
this.timers = [];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this.timersState = state;
|
|
36
|
-
this.emit('state', state);
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export default ImpulseGenerator;
|
package/src/openwrt.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import EventEmitter from "events";
|
|
2
|
-
import axios from "axios";
|
|
3
|
-
import Functions from './functions.js';
|
|
4
|
-
import ImpulseGenerator from "./impulsegenerator.js";
|
|
5
|
-
|
|
6
|
-
class OpenWrt extends EventEmitter {
|
|
7
|
-
constructor(config) {
|
|
8
|
-
super();
|
|
9
|
-
|
|
10
|
-
this.name = config.name;
|
|
11
|
-
this.host = config.host;
|
|
12
|
-
this.user = config.user;
|
|
13
|
-
this.passwd = config.passwd;
|
|
14
|
-
this.logDebug = config.logDebug;
|
|
15
|
-
|
|
16
|
-
//external integration
|
|
17
|
-
this.restFulEnabled = config.restFul?.enable || false;
|
|
18
|
-
this.mqttEnabled = config.mqtt?.enable || false;
|
|
19
|
-
|
|
20
|
-
this.firstRun = true;
|
|
21
|
-
this.lock = false;
|
|
22
|
-
|
|
23
|
-
this.sessionId = null;
|
|
24
|
-
this.sessionExpiresAt = 0;
|
|
25
|
-
|
|
26
|
-
this.functions = new Functions();
|
|
27
|
-
this.axiosInstance = axios.create({
|
|
28
|
-
baseURL: `${config.host}/ubus`,
|
|
29
|
-
timeout: 5000,
|
|
30
|
-
headers: {
|
|
31
|
-
"Content-Type": "application/json"
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
this.impulseGenerator = new ImpulseGenerator()
|
|
36
|
-
.on("connect", () => this.handleWithLock(async () => {
|
|
37
|
-
await this.connect();
|
|
38
|
-
}))
|
|
39
|
-
.on("state", (state) => {
|
|
40
|
-
this.emit(state ? "success" : "warn", `Impulse generator ${state ? "started" : "stopped"}`);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async handleWithLock(fn) {
|
|
45
|
-
if (this.lock) return;
|
|
46
|
-
this.lock = true;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
await fn();
|
|
50
|
-
} catch (error) {
|
|
51
|
-
this.emit("error", `Impulse generator error: ${error.message}`
|
|
52
|
-
);
|
|
53
|
-
} finally {
|
|
54
|
-
this.lock = false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async login() {
|
|
59
|
-
const now = Date.now();
|
|
60
|
-
if (this.sessionId && now < this.sessionExpiresAt) {
|
|
61
|
-
return this.sessionId;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const response = await this.axiosInstance.post("", {
|
|
65
|
-
jsonrpc: "2.0",
|
|
66
|
-
id: 1,
|
|
67
|
-
method: "call",
|
|
68
|
-
params: ["00000000000000000000000000000000", "session", "login", { username: this.user, password: this.passwd }]
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const result = response.data?.result?.[1];
|
|
72
|
-
if (!result?.ubus_rpc_session) {
|
|
73
|
-
throw new Error("ubus login failed");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.sessionId = result.ubus_rpc_session;
|
|
77
|
-
this.sessionExpiresAt = now + 240_000;
|
|
78
|
-
|
|
79
|
-
if (this.logDebug) this.emit("debug", `Ubus login OK`);
|
|
80
|
-
return this.sessionId;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async ubusCall(service, method, params = {}) {
|
|
84
|
-
const session = await this.login();
|
|
85
|
-
|
|
86
|
-
const response = await this.axiosInstance.post("", {
|
|
87
|
-
jsonrpc: "2.0",
|
|
88
|
-
id: 2,
|
|
89
|
-
method: "call",
|
|
90
|
-
params: [session, service, method, params]
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
if (response.data?.error) {
|
|
94
|
-
throw new Error(response.data.error.message || "ubus call error");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return response.data.result[1];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async connect() {
|
|
101
|
-
const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
|
|
102
|
-
const systemInfo = await this.ubusCall("system", "board");
|
|
103
|
-
const wirelessStatus = await this.ubusCall("network.wireless", "status");
|
|
104
|
-
if (this.logDebug) this.emit("debug", `Status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
|
|
105
|
-
|
|
106
|
-
const wirelessRadios = Object.values(status.radios).map(radio => ({
|
|
107
|
-
name: radio.name,
|
|
108
|
-
state: radio.up === true,
|
|
109
|
-
interfaces: Object.values(radio.interfaces).map(i => ({
|
|
110
|
-
name: i.ssid,
|
|
111
|
-
state: i.up === true,
|
|
112
|
-
mode: i.mode
|
|
113
|
-
}))
|
|
114
|
-
}));
|
|
115
|
-
|
|
116
|
-
const ssids = openWrtInfo.wirelessRadios.flatMap(radio => radio.interfaces);
|
|
117
|
-
this.emit("systemInfo", systemInfo);
|
|
118
|
-
this.emit("wirelessStatus", wirelessStatus);
|
|
119
|
-
this.emit("wirelessRadios", wirelessRadios);
|
|
120
|
-
this.emit("ssids", ssids);
|
|
121
|
-
|
|
122
|
-
if (this.firstRun) {
|
|
123
|
-
this.emit("success", `Connect success`);
|
|
124
|
-
this.firstRun = false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
openWrtInfo.state = true;
|
|
128
|
-
openWrtInfo.systemInfo = systemInfo;
|
|
129
|
-
openWrtInfo.wirelessStatus = wirelessStatus;
|
|
130
|
-
openWrtInfo.wirelessRadios = wirelessRadios;
|
|
131
|
-
openWrtInfo.ssids = ssids;
|
|
132
|
-
|
|
133
|
-
if (this.logDebug) this.emit("debug", `OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
|
|
134
|
-
return openWrtInfo;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async send(type, ssidName, state) {
|
|
138
|
-
switch (type) {
|
|
139
|
-
case 'ssid':
|
|
140
|
-
await this.handleWithLock(async () => {
|
|
141
|
-
if (this.logDebug) this.emit("debug", `${state ? "Enabling" : "Disabling"} SSID ${ssidName}`);
|
|
142
|
-
|
|
143
|
-
const status = await this.ubusCall("network.wireless", "status");
|
|
144
|
-
const iface = await this.functions.findIfaceBySsid(status, ssidName);
|
|
145
|
-
if (!iface) throw new Error(`SSID ${ssidName} not found`);
|
|
146
|
-
|
|
147
|
-
const section = iface.section;
|
|
148
|
-
if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
|
|
149
|
-
|
|
150
|
-
await this.ubusCall("uci", "set",
|
|
151
|
-
{
|
|
152
|
-
config: "wireless",
|
|
153
|
-
section: section,
|
|
154
|
-
values: {
|
|
155
|
-
disabled: state ? "0" : "1"
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
await this.ubusCall("uci", "commit", { config: "wireless" });
|
|
161
|
-
await this.ubusCall("network.wireless", "reload", {});
|
|
162
|
-
|
|
163
|
-
this.emit("success", `SSID ${ssidName} ${state ? "enabled" : "disabled"}`);
|
|
164
|
-
});
|
|
165
|
-
break;
|
|
166
|
-
case 'switch':
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export default OpenWrt;
|
|
173
|
-
|
package/src/swdevice.js
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import EventEmitter from 'events';
|
|
2
|
-
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
3
|
-
|
|
4
|
-
class SwitchDevice extends EventEmitter {
|
|
5
|
-
constructor(api, config, openWrt, openWrtInfo) {
|
|
6
|
-
super();
|
|
7
|
-
|
|
8
|
-
Accessory = api.platformAccessory;
|
|
9
|
-
Characteristic = api.hap.Characteristic;
|
|
10
|
-
Service = api.hap.Service;
|
|
11
|
-
Categories = api.hap.Categories;
|
|
12
|
-
AccessoryUUID = api.hap.uuid;
|
|
13
|
-
|
|
14
|
-
//config
|
|
15
|
-
this.name = config.name;
|
|
16
|
-
this.switch = config.switch;
|
|
17
|
-
this.logInfo = config.log?.info || false;
|
|
18
|
-
this.logDebug = config.log?.debug || false;
|
|
19
|
-
|
|
20
|
-
//external integration
|
|
21
|
-
this.restFul = config.restFul || {};
|
|
22
|
-
this.restFulConnected = false;
|
|
23
|
-
this.mqtt = config.mqtt || {};
|
|
24
|
-
this.mqttConnected = false;
|
|
25
|
-
|
|
26
|
-
//openwrt
|
|
27
|
-
this.openWrt = openWrt;
|
|
28
|
-
this.openWrtInfo = openWrtInfo;
|
|
29
|
-
this.ssids = openWrtInfo.ssids;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
async externalIntegrations() {
|
|
33
|
-
//RESTFul server
|
|
34
|
-
const restFulEnabled = this.restFul.enable || false;
|
|
35
|
-
if (restFulEnabled) {
|
|
36
|
-
try {
|
|
37
|
-
this.restFul1 = new RestFul({
|
|
38
|
-
port: this.restFul.port || 3000,
|
|
39
|
-
logWarn: this.logWarn,
|
|
40
|
-
logDebug: this.logDebug
|
|
41
|
-
})
|
|
42
|
-
.on('connected', (message) => {
|
|
43
|
-
this.emit('success', message);
|
|
44
|
-
this.restFulConnected = true;
|
|
45
|
-
})
|
|
46
|
-
.on('set', async (key, value) => {
|
|
47
|
-
try {
|
|
48
|
-
await this.setOverExternalIntegration('RESTFul', key, value);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
this.emit('warn', `RESTFul set error: ${error}`);
|
|
51
|
-
};
|
|
52
|
-
})
|
|
53
|
-
.on('debug', (debug) => this.emit('debug', debug))
|
|
54
|
-
.on('warn', (warn) => this.emit('warn', warn))
|
|
55
|
-
.on('error', (error) => this.emit('error', error));
|
|
56
|
-
} catch (error) {
|
|
57
|
-
this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
//mqtt client
|
|
62
|
-
const mqttEnabled = this.mqtt.enable || false;
|
|
63
|
-
if (mqttEnabled) {
|
|
64
|
-
try {
|
|
65
|
-
this.mqtt1 = new Mqtt({
|
|
66
|
-
host: this.mqtt.host,
|
|
67
|
-
port: this.mqtt.port || 1883,
|
|
68
|
-
clientId: this.mqtt.clientId ? `${this.savedInfo.manufacturer}_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `${this.savedInfo.manufacturer}_${Math.random().toString(16).slice(3)}`,
|
|
69
|
-
prefix: this.mqtt.prefix ? `${this.savedInfo.manufacturer}/${this.mqtt.prefix}/${this.name}` : `${this.savedInfo.manufacturer}/${this.name}`,
|
|
70
|
-
user: this.mqtt.auth?.user,
|
|
71
|
-
passwd: this.mqtt.auth?.passwd,
|
|
72
|
-
logWarn: this.logWarn,
|
|
73
|
-
logDebug: this.logDebug
|
|
74
|
-
})
|
|
75
|
-
.on('connected', (message) => {
|
|
76
|
-
this.emit('success', message);
|
|
77
|
-
this.mqttConnected = true;
|
|
78
|
-
})
|
|
79
|
-
.on('subscribed', (message) => {
|
|
80
|
-
this.emit('success', message);
|
|
81
|
-
})
|
|
82
|
-
.on('set', async (key, value) => {
|
|
83
|
-
try {
|
|
84
|
-
await this.setOverExternalIntegration('MQTT', key, value);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
this.emit('warn', `MQTT set error: ${error}`);
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
.on('debug', (debug) => this.emit('debug', debug))
|
|
90
|
-
.on('warn', (warn) => this.emit('warn', warn))
|
|
91
|
-
.on('error', (error) => this.emit('error', error));
|
|
92
|
-
} catch (error) {
|
|
93
|
-
this.emit('warn', `MQTT integration start error: ${error}`);
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async setOverExternalIntegration(integration, key, value) {
|
|
101
|
-
try {
|
|
102
|
-
let set = false
|
|
103
|
-
switch (key) {
|
|
104
|
-
case 'Power':
|
|
105
|
-
const powerState = value ? 'ON' : 'OFF';
|
|
106
|
-
set = await this.openWrt.send('Power', powerState);
|
|
107
|
-
break;
|
|
108
|
-
default:
|
|
109
|
-
this.emit('warn', `${integration}, received key: ${key}, value: ${value}`);
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
return set;
|
|
113
|
-
} catch (error) {
|
|
114
|
-
throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
//prepare accessory
|
|
119
|
-
async prepareAccessory() {
|
|
120
|
-
try {
|
|
121
|
-
//prepare accessory
|
|
122
|
-
if (this.logDebug) this.emit('debug', `prepare accessory`);
|
|
123
|
-
const accessoryName = this.name;
|
|
124
|
-
const accessoryUUID = AccessoryUUID.generate(this.deviceUuid);
|
|
125
|
-
const accessoryCategory = Categories.AIRPORT;
|
|
126
|
-
const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
|
|
127
|
-
|
|
128
|
-
//prepare information service
|
|
129
|
-
if (this.logDebug) this.emit('debug', `prepare information service`);
|
|
130
|
-
accessory.getService(Service.AccessoryInformation)
|
|
131
|
-
.setCharacteristic(Characteristic.Manufacturer, 'OpenWrt')
|
|
132
|
-
.setCharacteristic(Characteristic.Model, accessoryName)
|
|
133
|
-
.setCharacteristic(Characteristic.SerialNumber, this.networkId)
|
|
134
|
-
.setCharacteristic(Characteristic.FirmwareRevision, this.organizationId)
|
|
135
|
-
.setCharacteristic(Characteristic.ConfiguredName, accessoryName);
|
|
136
|
-
|
|
137
|
-
if (this.logDebug) this.emit('debug', `prepare service`);
|
|
138
|
-
|
|
139
|
-
//device
|
|
140
|
-
this.services = [];
|
|
141
|
-
for (const ssid of this.ssids) {
|
|
142
|
-
const ssidName = ssid.name;
|
|
143
|
-
if (this.logDebug) this.emit('debug', `prepare ssid: ${ssidName} service`);
|
|
144
|
-
|
|
145
|
-
const serviceName = this.accessPoint.namePrefix ? `${this.name} ${ssidName}` : ssidName;
|
|
146
|
-
const service = accessory.addService(Service.Switch, serviceName, `service${ssidName}`);
|
|
147
|
-
service.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
148
|
-
service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
149
|
-
service.getCharacteristic(Characteristic.On)
|
|
150
|
-
.onGet(async () => {
|
|
151
|
-
const state = ssid.state;
|
|
152
|
-
if (this.logInfo) this.emit('message', `SSID: ${ssidName}, state: ${state ? 'Enabled' : 'Disabled'}`);
|
|
153
|
-
return state;
|
|
154
|
-
})
|
|
155
|
-
.onSet(async (state) => {
|
|
156
|
-
try {
|
|
157
|
-
state = state ? true : false;
|
|
158
|
-
await this.openWrt.send('ssid', ssidName, state);
|
|
159
|
-
if (this.logInfo) this.emit('message', `SSID: ${ssidName}, set State: ${state ? 'Enabled' : 'Disabled'}`);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
this.emit('warn', `SSID: ${ssidName}, set state error: ${error}`);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
this.services.push(service);
|
|
165
|
-
|
|
166
|
-
if (this.accessPoint.sensor) {
|
|
167
|
-
if (this.logDebug) this.emit('debug', `prepare ssid: ${ssidName} sensor service`);
|
|
168
|
-
|
|
169
|
-
this.sensorServices = [];
|
|
170
|
-
const sensorService = accessory.addService(Service.ContactSensor, ssidName, `sensorService${ssidName}`);
|
|
171
|
-
sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
172
|
-
sensorService.setCharacteristic(Characteristic.ConfiguredName, ssidName);
|
|
173
|
-
sensorService.getCharacteristic(Characteristic.ContactSensorState)
|
|
174
|
-
.onGet(async () => {
|
|
175
|
-
const state = ssid.state;
|
|
176
|
-
return state;
|
|
177
|
-
});
|
|
178
|
-
this.sensorServices.push(sensorService);
|
|
179
|
-
};
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
return accessory;
|
|
183
|
-
} catch (error) {
|
|
184
|
-
throw new Error(error);
|
|
185
|
-
};
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
//start
|
|
189
|
-
async start() {
|
|
190
|
-
try {
|
|
191
|
-
//start external integrations
|
|
192
|
-
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
193
|
-
|
|
194
|
-
this.emit('devInfo', `-------- Switch ${this.name} --------`);
|
|
195
|
-
this.emit('devInfo', `Name: ${this.openWrtInfo.systemInfo.hostname}`);
|
|
196
|
-
this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model}`);
|
|
197
|
-
this.emit('devInfo', `System: ${this.openWrtInfo.systemInfo.system}`);
|
|
198
|
-
this.emit('devInfo', `Release: ${this.openWrtInfo.systemInfo.release?.description}`);
|
|
199
|
-
this.emit('devInfo', `----------------------------------`);
|
|
200
|
-
|
|
201
|
-
//denon client
|
|
202
|
-
this.openWrt.on('systemInfo', (info) => {
|
|
203
|
-
this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, info.release?.version);
|
|
204
|
-
})
|
|
205
|
-
.on('wirelessStatus', async (status) => {
|
|
206
|
-
})
|
|
207
|
-
.on('wirelessRadios', async (radios) => {
|
|
208
|
-
})
|
|
209
|
-
.on('ssids', async (ssids) => {
|
|
210
|
-
// sensors
|
|
211
|
-
for (let i = 0; i < ssids.length; i++) {
|
|
212
|
-
const ssid = ssids[i];
|
|
213
|
-
|
|
214
|
-
const name = ssid[i].name;
|
|
215
|
-
const state = ssid[i].state;
|
|
216
|
-
const serviceName = this.accessPoint.namePrefix ? `${this.name} ${name}` : name;
|
|
217
|
-
this.services?.[i]
|
|
218
|
-
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
219
|
-
.updateCharacteristic(Characteristic.On, state);
|
|
220
|
-
|
|
221
|
-
this.sensorServices?.[i]
|
|
222
|
-
?.setCharacteristic(Characteristic.ConfiguredName, name)
|
|
223
|
-
.updateCharacteristic(Characteristic.ContactSensorState, state ? 0 : 1);
|
|
224
|
-
|
|
225
|
-
if (this.logInfo) {
|
|
226
|
-
this.emit('info', `SSID name: ${ssid.name}`);
|
|
227
|
-
this.emit('info', `Name: ${ssid.state}`);
|
|
228
|
-
this.emit('info', `Mode: ${ssid.mode}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
})
|
|
232
|
-
.on('restFul', (path, data) => {
|
|
233
|
-
if (this.restFulConnected) this.restFul1.update(path, data);
|
|
234
|
-
})
|
|
235
|
-
.on('mqtt', (topic, message) => {
|
|
236
|
-
if (this.mqttConnected) this.mqtt1.emit('publish', topic, message);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
//prepare accessory
|
|
240
|
-
const accessory = await this.prepareAccessory();
|
|
241
|
-
return accessory;
|
|
242
|
-
} catch (error) {
|
|
243
|
-
throw new Error(`Start error: ${error}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
export default SwitchDevice;
|