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 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
- - For plugin < v1.6.0 use Homebridge UI <= v5.5.0
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
- # homebridge-openwrt-control
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
+ [![verified-by-homebridge](https://img.shields.io/badge/homebridge-verified-purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
10
+ [![npm](https://shields.io/npm/dt/homebridge-openwrt-control?color=purple)](https://www.npmjs.com/package/homebridge-openwrt-control)
11
+ [![npm](https://shields.io/npm/v/homebridge-openwrt-control?color=purple)](https://www.npmjs.com/package/homebridge-openwrt-control)
12
+ [![npm](https://img.shields.io/npm/v/homebridge-openwrt-control/beta.svg?style=flat-square)](https://www.npmjs.com/package/homebridge-openwrt-control)
13
+ [![GitHub pull requests](https://img.shields.io/github/issues-pr/grzegorz914/homebridge-openwrt-control.svg)](https://github.com/grzegorz914/homebridge-openwrt-control/pulls)
14
+ [![GitHub issues](https://img.shields.io/github/issues/grzegorz914/homebridge-openwrt-control.svg)](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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "OpenWrt Control",
3
3
  "name": "homebridge-openwrt-control",
4
- "version": "0.0.1-beta.2",
4
+ "version": "0.0.1-beta.3",
5
5
  "description": "Homebridge plugin to control OpenWrt flashed devices.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
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
@@ -1,6 +0,0 @@
1
- export const PlatformName = "OpenWrt";
2
- export const PluginName = "homebridge-openwrt-control";
3
-
4
- export const ApiUrls = {
5
-
6
- }
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
@@ -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;