homebridge-openwrt-control 0.0.2-beta.9 → 0.0.2

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,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## Warning
9
9
 
10
+ ## [0.0.2] - (17.01.2026)
11
+
12
+ ## Changes
13
+
14
+ - added RESTFul and MQTT external integration (work in progress)
15
+ - refactor and optimizations
16
+ - cleanup
17
+
10
18
  ## [0.0.1] - (16.01.2026)
11
19
 
12
20
  ## Changes
@@ -4,7 +4,7 @@
4
4
  "singular": true,
5
5
  "fixArrays": true,
6
6
  "strictValidation": true,
7
- "headerDisplay": "This plugin works with OpenWrt Devices based on Dashboard API. Devices are exposed to HomeKit as separate accessories and each needs to be manually paired.",
7
+ "headerDisplay": "This plugin works with OpenWrt flashed devices. Devices are exposed to HomeKit as separate accessories and each needs to be manually paired.",
8
8
  "footerDisplay": "For documentation please see [GitHub repository](https://github.com/grzegorz914/homebridge-openwrt-control).",
9
9
  "schema": {
10
10
  "type": "object",
@@ -13,7 +13,7 @@
13
13
  "type": "array",
14
14
  "items": {
15
15
  "type": "object",
16
- "title": "Network",
16
+ "title": "Device",
17
17
  "properties": {
18
18
  "name": {
19
19
  "title": "Name",
@@ -96,7 +96,7 @@
96
96
  }
97
97
  ]
98
98
  },
99
- "accessPoint": {
99
+ "apDevice": {
100
100
  "title": "Access point",
101
101
  "type": "object",
102
102
  "properties": {
@@ -111,7 +111,7 @@
111
111
  "placeholder": "Name",
112
112
  "description": "Here set the Name to be displayed in Homebridge/HomeKit for this access point.",
113
113
  "condition": {
114
- "functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
114
+ "functionBody": "return model.devices[arrayIndices].apDevice.enable === true;"
115
115
  }
116
116
  },
117
117
  "namePrefix": {
@@ -119,7 +119,7 @@
119
119
  "type": "boolean",
120
120
  "description": "Here enable/disable the accessory name as a prefix for the access point name.",
121
121
  "condition": {
122
- "functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
122
+ "functionBody": "return model.devices[arrayIndices].apDevice.enable === true;"
123
123
  }
124
124
  },
125
125
  "sensor": {
@@ -127,7 +127,43 @@
127
127
  "type": "boolean",
128
128
  "description": "Here Enable/Disable sensor for every SSID.",
129
129
  "condition": {
130
- "functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
130
+ "functionBody": "return model.devices[arrayIndices].apDevice.enable === true;"
131
+ }
132
+ }
133
+ }
134
+ },
135
+ "swDevice": {
136
+ "title": "Access point",
137
+ "type": "object",
138
+ "properties": {
139
+ "enable": {
140
+ "title": "Enable",
141
+ "type": "boolean",
142
+ "description": "Here Enable/Disable this switch control."
143
+ },
144
+ "name": {
145
+ "title": "Name",
146
+ "type": "string",
147
+ "placeholder": "Name",
148
+ "description": "Here set the Name to be displayed in Homebridge/HomeKit for this switch.",
149
+ "condition": {
150
+ "functionBody": "return model.devices[arrayIndices].swDevice.enable === true;"
151
+ }
152
+ },
153
+ "namePrefix": {
154
+ "title": "Prefix",
155
+ "type": "boolean",
156
+ "description": "Here enable/disable the accessory name as a prefix for the port name.",
157
+ "condition": {
158
+ "functionBody": "return model.devices[arrayIndices].swDevice.enable === true;"
159
+ }
160
+ },
161
+ "sensor": {
162
+ "title": "Sensor",
163
+ "type": "boolean",
164
+ "description": "Here Enable/Disable sensor for every port.",
165
+ "condition": {
166
+ "functionBody": "return model.devices[arrayIndices].swDevice.enable === true;"
131
167
  }
132
168
  }
133
169
  }
@@ -369,13 +405,23 @@
369
405
  "title": "{{ value.title }}",
370
406
  "items": [
371
407
  {
372
- "key": "devices[].accessPoint",
408
+ "key": "devices[].apDevice",
373
409
  "title": "Access Point",
374
410
  "items": [
375
- "devices[].accessPoint.enable",
376
- "devices[].accessPoint.name",
377
- "devices[].accessPoint.namePrefix",
378
- "devices[].accessPoint.sensor"
411
+ "devices[].apDevice.enable",
412
+ "devices[].apDevice.name",
413
+ "devices[].apDevice.namePrefix",
414
+ "devices[].apDevice.sensor"
415
+ ]
416
+ },
417
+ {
418
+ "key": "devices[].swDevice",
419
+ "title": "Switch",
420
+ "items": [
421
+ "devices[].swDevice.enable",
422
+ "devices[].swDevice.name",
423
+ "devices[].swDevice.namePrefix",
424
+ "devices[].swDevice.sensor"
379
425
  ]
380
426
  },
381
427
  {
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { join } from 'path';
2
2
  import { mkdirSync, existsSync, writeFileSync } from 'fs';
3
3
  import OpenWrt from './src/openwrt.js';
4
- import AccessPoint from './src/apdevice.js';
5
- import Switch from './src/swdevice.js';
4
+ import AccessPoint from './src/accesspoint.js';
5
+ import Switch from './src/switch.js';
6
6
  import ImpulseGenerator from './src/impulsegenerator.js';
7
7
  import { PluginName, PlatformName } from './src/constants.js';
8
8
 
@@ -14,7 +14,6 @@ class OpenWrtPlatform {
14
14
  return;
15
15
  }
16
16
  this.accessories = [];
17
- this.devices = [];
18
17
 
19
18
  //check if prefs directory exist
20
19
  const prefDir = join(api.user.storagePath(), 'openWrt');
@@ -26,34 +25,43 @@ class OpenWrtPlatform {
26
25
  }
27
26
 
28
27
  api.on('didFinishLaunching', async () => {
29
- for (const deviceConfig of config.devices) {
30
- const { name, host, displayType } = deviceConfig;
28
+ for (const device of config.devices) {
29
+ const { name, host, displayType } = device;
31
30
  if (!name || !host || !displayType) {
32
- log.warn(`Device: ${host || 'host missing'}, ${name || 'name missing'}, ${!displayType ? ', disply type disabled' : ''} in config, will not be published in the Home app`);
31
+ log.warn(`Device: ${host || 'host missing'}, ${name || 'name missing'}, ${!displayType ? ', display type disabled' : ''} in config, will not be published in the Home app`);
33
32
  continue;
34
33
  }
34
+ const apDevice = device.apDevice || {};
35
+ const swDevice = device.swDevice || {};
36
+ const refreshInterval = (device.refreshInterval ?? 5) * 1000;
37
+
38
+ //check enabled devices
39
+ const configuredDevices = [];
40
+ if (apDevice.enable) configuredDevices.push(0);
41
+ if (swDevice.enable) configuredDevices.push(1);
42
+ if (configuredDevices.length === 0) continue;
35
43
 
36
44
  //log config
37
45
  const logLevel = {
38
- devInfo: deviceConfig.log?.deviceInfo,
39
- success: deviceConfig.log?.success,
40
- info: deviceConfig.log?.info,
41
- warn: deviceConfig.log?.warn,
42
- error: deviceConfig.log?.error,
43
- debug: deviceConfig.log?.debug
46
+ devInfo: device.log?.deviceInfo,
47
+ success: device.log?.success,
48
+ info: device.log?.info,
49
+ warn: device.log?.warn,
50
+ error: device.log?.error,
51
+ debug: device.log?.debug
44
52
  };
45
53
 
46
54
  if (logLevel.debug) {
47
55
  log.info(`Device: ${host} ${name}, did finish launching.`);
48
56
  const safeConfig = {
49
- ...deviceConfig,
57
+ ...device,
50
58
  auth: {
51
- ...deviceConfig.auth,
59
+ ...device.auth,
52
60
  passwd: 'removed',
53
61
  },
54
62
  mqtt: {
55
63
  auth: {
56
- ...deviceConfig.mqtt?.auth,
64
+ ...device.mqtt?.auth,
57
65
  passwd: 'removed',
58
66
  }
59
67
  },
@@ -61,75 +69,74 @@ class OpenWrtPlatform {
61
69
  log.info(`Device: ${host} ${name}, Config: ${JSON.stringify(safeConfig, null, 2)}`);
62
70
  }
63
71
 
64
- const refreshInterval = (deviceConfig.refreshInterval ?? 5) * 1000;
65
- if (deviceConfig.accessPoint?.enable) this.devices.push('accessPoint');
66
- if (deviceConfig.switch?.enable) this.devices.push('switch');
67
-
68
- if (this.devices.length === 0) return;
69
-
70
- const openWrt = new OpenWrt(deviceConfig)
71
- .on('success', msg => logLevel.success && log.success(`Device: ${host}, ${msg}`))
72
- .on('info', msg => log.info(`Device: ${host}, ${msg}`))
73
- .on('debug', msg => log.info(`Device: ${host}, debug: ${msg}`))
74
- .on('warn', msg => log.warn(`Device: ${host}, ${msg}`))
75
- .on('error', msg => log.error(`Device: ${host}, ${msg}`))
76
-
77
- const openWrtInfo = await openWrt.connect();
78
- if (!openWrtInfo.state) {
79
- if (logLevel.warn) log.warn(`Device: ${host} ${name}, no data received`);
80
- return;
81
- }
82
-
83
72
  try {
84
- for (const device of this.devices) {
85
- // create impulse generator
86
- const impulseGenerator = new ImpulseGenerator()
87
- .on('start', async () => {
88
- try {
89
-
90
- // create device instance
91
- let type;
92
- switch (device) {
93
- case 'accessPoint': type = new AccessPoint(api, deviceConfig, openWrt, openWrtInfo); break;
94
- case 'switch': type = new Switch(api, deviceConfig, openWrt, openWrtInfo); break;
73
+ // create impulse generator for every device
74
+ const impulseGenerator = new ImpulseGenerator()
75
+ .on('start', async () => {
76
+ try {
77
+
78
+ const openWrt = new OpenWrt(device)
79
+ .on('success', msg => logLevel.success && log.success(`Device: ${host}, ${msg}`))
80
+ .on('info', msg => log.info(`Device: ${host} ${name}, ${msg}`))
81
+ .on('debug', msg => log.info(`Device: ${host} ${name}, debug: ${msg}`))
82
+ .on('warn', msg => log.warn(`Device: ${host} ${name}, ${msg}`))
83
+ .on('error', msg => log.error(`Device: ${host} ${name}, ${msg}`));
84
+
85
+ const openWrtInfo = await openWrt.connect();
86
+ if (!openWrtInfo.state) {
87
+ if (logLevel.warn) log.warn(`Device: ${host} ${name}, ${openWrtInfo.info}`);
88
+ return;
89
+ }
90
+ if (logLevel.success) log.success(`Device: ${host} ${name}, ${openWrtInfo.info}`);
91
+
92
+ // start openwrt impulse generator
93
+ await openWrt.impulseGenerator.state(true, [{ name: 'connect', sampling: refreshInterval }], false);
94
+
95
+ for (const deviceType of configuredDevices) {
96
+ let configuredDevice;
97
+ switch (deviceType) {
98
+ case 0:
99
+ configuredDevice = new AccessPoint(api, device, openWrt, openWrtInfo);
100
+ break;
101
+ case 1:
102
+ configuredDevice = new Switch(api, device, openWrt, openWrtInfo);
103
+ break;
95
104
  default:
96
- if (logLevel.warn) log.warn(`Device: ${host} ${name}, unknown device: ${device}`);
105
+ if (logLevel.warn) log.warn(`Device: ${host} ${name}, class not found for: ${deviceType}`);
97
106
  return;
98
107
  }
99
108
 
100
- type
101
- .on('devInfo', msg => logLevel.devInfo && log.info(msg))
109
+ configuredDevice.on('devInfo', msg => logLevel.devInfo && log.info(msg))
102
110
  .on('success', msg => logLevel.success && log.success(`Device: ${host} ${name}, ${msg}`))
103
111
  .on('info', msg => log.info(`Device: ${host} ${name}, ${msg}`))
104
112
  .on('debug', msg => log.info(`Device: ${host} ${name}, debug: ${msg}`))
105
113
  .on('warn', msg => log.warn(`Device: ${host} ${name}, ${msg}`))
106
114
  .on('error', msg => log.error(`Device: ${host} ${name}, ${msg}`));
107
115
 
108
- const accessory = await type.start();
116
+ const accessory = await configuredDevice.start();
109
117
  if (accessory) {
110
118
  api.publishExternalAccessories(PluginName, [accessory]);
111
119
  if (logLevel.success) log.success(`Device: ${host} ${name}, Published as external accessory.`);
112
120
  }
113
-
114
- // stop master impulse generator
115
- await impulseGenerator.state(false);
116
- } catch (error) {
117
- if (logLevel.error) log.error(`Device: ${host} ${name}, Start impulse generator error: ${error.message ?? error}, trying again.`);
118
121
  }
119
- })
120
- .on('state', (state) => {
121
- if (logLevel.debug) log.info(`Device: ${host} ${name}, Start impulse generator ${state ? 'started' : 'stopped'}.`);
122
- });
123
122
 
124
- // start impulse generator
125
- await impulseGenerator.state(true, [{ name: 'start', sampling: 120000 }]);
126
- }
123
+ // stop accessory impulse generator
124
+ await impulseGenerator.state(false);
125
+ } catch (error) {
126
+ if (logLevel.error) log.error(`Device: ${host} ${name}, Start impulse generator error: ${error.message ?? error}, trying again.`);
127
+ }
128
+ })
129
+ .on('state', (state) => {
130
+ if (logLevel.debug) log.info(`Device: ${host} ${name}, Start impulse generator ${state ? 'started' : 'stopped'}.`);
131
+ });
127
132
 
128
- // start openwrt impulse generator
129
- await openWrt.impulseGenerator.state(true, [{ name: 'connect', sampling: refreshInterval }], false);
133
+ // start accessory impulse generator
134
+ await impulseGenerator.state(true, [{ name: 'start', sampling: 120000 }]);
130
135
  } catch (error) {
131
136
  if (logLevel.error) log.error(`Device: ${host} ${name}, Did finish launching error: ${error.message ?? error}`);
132
137
  }
138
+
139
+ await new Promise(r => setTimeout(r, 500));
133
140
  }
134
141
  });
135
142
  }
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.2-beta.9",
4
+ "version": "0.0.2",
5
5
  "description": "Homebridge plugin to control OpenWrt flashed devices.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -34,7 +34,9 @@
34
34
  "node": "^20 || ^22 || ^24 || ^25"
35
35
  },
36
36
  "dependencies": {
37
- "axios": "^1.13.2"
37
+ "mqtt": "^5.14.1",
38
+ "axios": "^1.13.2",
39
+ "express": "^5.2.1"
38
40
  },
39
41
  "keywords": [
40
42
  "homebridge",
@@ -44,7 +46,6 @@
44
46
  "accesspoint",
45
47
  "router",
46
48
  "switch"
47
-
48
49
  ],
49
50
  "contributors": [],
50
51
  "scripts": {
@@ -1,7 +1,9 @@
1
1
  import EventEmitter from 'events';
2
+ import RestFul from './restful.js';
3
+ import Mqtt from './mqtt.js';
2
4
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
3
5
 
4
- class AccessPointDevice extends EventEmitter {
6
+ class AccessPoint extends EventEmitter {
5
7
  constructor(api, config, openWrt, openWrtInfo) {
6
8
  super();
7
9
 
@@ -12,8 +14,12 @@ class AccessPointDevice extends EventEmitter {
12
14
  AccessoryUUID = api.hap.uuid;
13
15
 
14
16
  //config
15
- this.name = config.accessPoint.name || openWrtInfo.systemInfo.hostname;
16
- this.accessPoint = config.accessPoint;
17
+ this.config = config;
18
+ this.host = config.host;
19
+ this.name = config.apDevice?.name || openWrtInfo.systemInfo.hostname;
20
+ this.namePrefix = config.apDevice?.namePrefix || false;
21
+ this.sensorsEnabled = config.apDevice?.sensor || false
22
+ this.logDeviceInfo = config.log?.deviceInfo || false;
17
23
  this.logInfo = config.log?.info || false;
18
24
  this.logDebug = config.log?.debug || false;
19
25
 
@@ -26,7 +32,37 @@ class AccessPointDevice extends EventEmitter {
26
32
  //openwrt
27
33
  this.openWrt = openWrt;
28
34
  this.openWrtInfo = openWrtInfo;
29
- this.ssids = openWrtInfo.ssids;
35
+
36
+ //openwrt client
37
+ openWrt.on('openWrtInfo', (openWrtInfo) => {
38
+ this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, openWrtInfo.systemInfo.release?.version);
39
+
40
+ // update state
41
+ const ssids = openWrtInfo.ssids;
42
+ for (let i = 0; i < ssids.length; i++) {
43
+ const ssid = ssids[i];
44
+ const name = ssid.name;
45
+ const state = ssid.state;
46
+ const serviceName = this.namePrefix ? `${this.name} ${name}` : name;
47
+ this.services?.[i]
48
+ ?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
49
+ .updateCharacteristic(Characteristic.On, state);
50
+
51
+ this.sensorServices?.[i]
52
+ ?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
53
+ .updateCharacteristic(Characteristic.ContactSensorState, state);
54
+
55
+ if (this.logInfo) {
56
+ this.emit('info', `Name: ${ssid.name}`);
57
+ this.emit('info', `State: ${ssid.state}`);
58
+ this.emit('info', `Mode: ${ssid.mode}`);
59
+ }
60
+ }
61
+
62
+ //restFul and mqtt
63
+ if (this.restFulConnected) this.emit('restFul', 'info', openWrtInfo);
64
+ if (this.mqttConnected) this.emit('mqtt', 'Info', openWrtInfo);
65
+ });
30
66
  };
31
67
 
32
68
  async externalIntegrations() {
@@ -65,8 +101,8 @@ class AccessPointDevice extends EventEmitter {
65
101
  this.mqtt1 = new Mqtt({
66
102
  host: this.mqtt.host,
67
103
  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}`,
104
+ clientId: this.mqtt.clientId ? `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}_${Math.random().toString(16).slice(3)}`,
105
+ prefix: this.mqtt.prefix ? `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.mqtt.prefix}/${this.name}` : `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.name}`,
70
106
  user: this.mqtt.auth?.user,
71
107
  passwd: this.mqtt.auth?.passwd,
72
108
  logWarn: this.logWarn,
@@ -121,28 +157,28 @@ class AccessPointDevice extends EventEmitter {
121
157
  //prepare accessory
122
158
  if (this.logDebug) this.emit('debug', `prepare accessory`);
123
159
  const accessoryName = this.name;
124
- const accessoryUUID = AccessoryUUID.generate(this.openWrtInfo.systemInfo.hostname);
160
+ const accessoryUUID = AccessoryUUID.generate(this.host + this.openWrtInfo.systemInfo.system);
125
161
  const accessoryCategory = Categories.AIRPORT;
126
162
  const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
127
163
 
128
164
  //prepare information service
129
165
  if (this.logDebug) this.emit('debug', `prepare information service`);
130
- accessory.getService(Service.AccessoryInformation)
131
- .setCharacteristic(Characteristic.Manufacturer, 'OpenWrt')
166
+ this.informationService = accessory.getService(Service.AccessoryInformation)
167
+ .setCharacteristic(Characteristic.Manufacturer, this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt')
132
168
  .setCharacteristic(Characteristic.Model, this.openWrtInfo.systemInfo.model)
133
169
  .setCharacteristic(Characteristic.SerialNumber, this.openWrtInfo.systemInfo.system)
134
- .setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.description)
135
- .setCharacteristic(Characteristic.ConfiguredName, accessoryName);
170
+ .setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.version);
136
171
 
137
172
  if (this.logDebug) this.emit('debug', `prepare service`);
138
173
 
139
- //device
174
+ //services
140
175
  this.services = [];
141
- for (const ssid of this.ssids) {
176
+ this.sensorServices = [];
177
+ for (const ssid of this.openWrtInfo.ssids) {
142
178
  const name = ssid.name;
143
179
  if (this.logDebug) this.emit('debug', `prepare ssid: ${name} service`);
144
180
 
145
- const serviceName = this.accessPoint.namePrefix ? `${accessoryName} ${name}` : name;
181
+ const serviceName = this.namePrefix ? `${accessoryName} ${name}` : name;
146
182
  const service = accessory.addService(Service.Switch, serviceName, `service${name}`);
147
183
  service.addOptionalCharacteristic(Characteristic.ConfiguredName);
148
184
  service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
@@ -163,10 +199,8 @@ class AccessPointDevice extends EventEmitter {
163
199
  });
164
200
  this.services.push(service);
165
201
 
166
- if (this.accessPoint.sensor) {
202
+ if (this.sensorsEnabled) {
167
203
  if (this.logDebug) this.emit('debug', `prepare ssid: ${name} sensor service`);
168
-
169
- this.sensorServices = [];
170
204
  const sensorService = accessory.addService(Service.ContactSensor, serviceName, `sensorService${name}`);
171
205
  sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
172
206
  sensorService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
@@ -191,50 +225,16 @@ class AccessPointDevice extends EventEmitter {
191
225
  //start external integrations
192
226
  if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
193
227
 
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', `Firmware: ${this.openWrtInfo.systemInfo.release?.description}`);
199
- this.emit('devInfo', `----------------------------------`);
200
-
201
- //openwrt client
202
- this.openWrt.on('systemInfo', (info) => {
203
- this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, info.release?.description);
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
- });
228
+ if (this.logDeviceInfo) {
229
+ this.emit('devInfo', `-------- Access Point ${this.name} --------`);
230
+ this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model || this.openWrtInfo.systemInfo.board_name}`);
231
+ this.emit('devInfo', `System: ${this.openWrtInfo.systemInfo.system}`);
232
+ this.emit('devInfo', `Kernel: ${this.openWrtInfo.systemInfo.kernel}`);
233
+ this.emit('devInfo', `Firmware: ${this.openWrtInfo.systemInfo.release?.description}`);
234
+ this.emit('devInfo', `Target: ${this.openWrtInfo.systemInfo.release?.target}`);
235
+ this.emit('devInfo', `SSIDs: ${this.openWrtInfo.ssids.length}`);
236
+ this.emit('devInfo', `----------------------------------`);
237
+ }
238
238
 
239
239
  //prepare accessory
240
240
  const accessory = await this.prepareAccessory();
@@ -244,4 +244,4 @@ class AccessPointDevice extends EventEmitter {
244
244
  }
245
245
  }
246
246
  };
247
- export default AccessPointDevice;
247
+ export default AccessPoint;
package/src/mqtt.js ADDED
@@ -0,0 +1,103 @@
1
+ import { connect } from 'mqtt';
2
+ import EventEmitter from 'events';
3
+
4
+ class Mqtt extends EventEmitter {
5
+ constructor(config) {
6
+ super();
7
+
8
+ const url = `mqtt://${config.host}:${config.port}`;
9
+ const subscribeTopic = `${config.prefix}/Set`;
10
+
11
+ const options = {
12
+ clientId: config.clientId,
13
+ username: config.user,
14
+ password: config.passwd,
15
+ protocolVersion: 5,
16
+ clean: false,
17
+ properties: {
18
+ sessionExpiryInterval: 60 * 60, // 1 hour
19
+ userProperties: {
20
+ source: 'node-client'
21
+ }
22
+ }
23
+ };
24
+
25
+ this.mqttClient = connect(url, options);
26
+
27
+ // === CONNECTED ===
28
+ this.mqttClient.on('connect', async (packet) => {
29
+ this.emit('connected', 'MQTT v5 connected.');
30
+
31
+ try {
32
+ const result = await this.mqttClient.subscribeAsync(subscribeTopic, {
33
+ qos: 1,
34
+ properties: {
35
+ userProperties: {
36
+ type: 'subscription'
37
+ }
38
+ }
39
+ });
40
+
41
+ // MQTT v5 subscription results contain reason codes
42
+ if (config.logDebug) this.emit('debug', `Subscribed to ${subscribeTopic}, reason codes: ${JSON.stringify(result)}`);
43
+ this.emit('subscribed', `MQTT Subscribe topic: ${subscribeTopic}`);
44
+
45
+ } catch (error) {
46
+ if (config.logWarn) this.emit('warn', `MQTT Subscribe error: ${error}`);
47
+ }
48
+ });
49
+
50
+ // === MESSAGE ===
51
+ this.mqttClient.on('message', (topic, payload, packet) => {
52
+ try {
53
+ const obj = JSON.parse(payload.toString());
54
+ if (config.logDebug) this.emit('debug', `MQTT Received:\nTopic: ${topic}\nPayload: ${JSON.stringify(obj, null, 2)}\nProperties: ${JSON.stringify(packet.properties, null, 2)}`);
55
+
56
+ const key = Object.keys(obj)[0];
57
+ const value = Object.values(obj)[0];
58
+ this.emit('set', key, value);
59
+
60
+ } catch (error) {
61
+ if (config.logWarn) this.emit('warn', `MQTT Parse error: ${error}`);
62
+ }
63
+ });
64
+
65
+ // === PUBLISH EVENT ===
66
+ this.on('publish', async (topic, message) => {
67
+ try {
68
+ const fullTopic = `${config.prefix}/${topic}`;
69
+ const publishMessage = JSON.stringify(message);
70
+
71
+ await this.mqttClient.publishAsync(fullTopic, publishMessage, {
72
+ qos: 1,
73
+ properties: {
74
+ contentType: 'application/json',
75
+ userProperties: {
76
+ source: 'node',
77
+ action: 'set'
78
+ }
79
+ }
80
+ });
81
+
82
+ if (config.logDebug) this.emit('debug', `MQTT Publish:\nTopic: ${fullTopic}\nPayload: ${publishMessage}`);
83
+ } catch (error) {
84
+ if (config.logWarn) this.emit('warn', `MQTT Publish error: ${error}`);
85
+ }
86
+ });
87
+
88
+ // === ERRORS / STATE ===
89
+ this.mqttClient.on('error', (err) => {
90
+ if (config.logWarn) this.emit('warn', `MQTT Error: ${err.message}`);
91
+ });
92
+
93
+ this.mqttClient.on('reconnect', () => {
94
+ if (config.logDebug) this.emit('debug', 'MQTT Reconnecting...');
95
+ });
96
+
97
+ this.mqttClient.on('close', () => {
98
+ if (config.logDebug) this.emit('debug', 'MQTT Connection closed.');
99
+ });
100
+ }
101
+ }
102
+
103
+ export default Mqtt;
package/src/openwrt.js CHANGED
@@ -1,140 +1,150 @@
1
- import EventEmitter from "events";
2
- import { exec } from "child_process";
3
- import Functions from "./functions.js";
4
- import ImpulseGenerator from "./impulsegenerator.js";
1
+ import EventEmitter from 'events';
2
+ import axios from 'axios';
3
+ import Functions from './functions.js';
4
+ import ImpulseGenerator from './impulsegenerator.js';
5
5
 
6
6
  class OpenWrt extends EventEmitter {
7
7
  constructor(config) {
8
8
  super();
9
9
 
10
- this.host = config.host;
11
- this.user = config.auth?.user || "root";
12
- this.passwd = config.auth?.passwd || "";
13
-
10
+ this.user = config.auth?.user || 'root';
11
+ this.passwd = config.auth?.passwd;
14
12
  this.logError = config.log?.error;
15
13
  this.logDebug = config.log?.debug;
16
14
 
17
- this.firstRun = true;
15
+ //external integration
16
+ this.restFulEnabled = config.restFul?.enable || false;
17
+ this.mqttEnabled = config.mqtt?.enable || false;
18
+
18
19
  this.lock = false;
20
+ this.sessionId = null;
21
+ this.sessionExpiresAt = 0;
19
22
 
20
23
  this.functions = new Functions();
21
24
 
25
+ const baseUrl = `http://${config.host}/ubus`;
26
+ this.axiosInstance = axios.create({
27
+ baseURL: baseUrl,
28
+ timeout: 5000,
29
+ headers: {
30
+ "Content-Type": "application/json"
31
+ }
32
+ });
33
+
22
34
  this.impulseGenerator = new ImpulseGenerator()
23
- .on("connect", () => this.handleWithLock(async () => {
35
+ .on('connect', () => this.handleWithLock(async () => {
24
36
  await this.connect();
25
37
  }))
26
- .on("state", (state) => {
27
- this.emit(state ? "success" : "warn", `Impulse generator ${state ? "started" : "stopped"}`);
38
+ .on('state', (state) => {
39
+ this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
28
40
  });
29
41
  }
30
42
 
31
43
  async handleWithLock(fn) {
32
44
  if (this.lock) return;
33
45
  this.lock = true;
46
+
34
47
  try {
35
48
  await fn();
36
49
  } catch (error) {
37
- this.emit("error", `Impulse generator error: ${error.message}`);
50
+ this.emit('error', `Impulse generator error: ${error.message}`);
38
51
  } finally {
39
52
  this.lock = false;
40
53
  }
41
54
  }
42
55
 
43
- // --- Helper do wywołań SSH ---
44
- async sshCall(command) {
45
- return new Promise((resolve, reject) => {
46
- const sshCmd = `ssh ${this.user}@${this.host} '${command.replace(/'/g, `'\\''`)}'`;
47
- exec(sshCmd, (error, stdout, stderr) => {
48
- if (error) return reject(new Error(stderr || error.message));
49
- try {
50
- const json = JSON.parse(stdout);
51
- resolve(json);
52
- } catch (parseError) {
53
- reject(new Error(`Invalid JSON from SSH: ${parseError.message}`));
54
- }
55
- });
56
+ async login() {
57
+ const now = Date.now();
58
+ if (this.sessionId && now < this.sessionExpiresAt) {
59
+ return this.sessionId;
60
+ }
61
+
62
+ const response = await this.axiosInstance.post('', {
63
+ jsonrpc: '2.0',
64
+ id: 1,
65
+ method: 'call',
66
+ params: ['00000000000000000000000000000000', 'session', 'login', { username: this.user, password: this.passwd }]
56
67
  });
68
+
69
+ const result = response.data?.result?.[1];
70
+ if (!result?.ubus_rpc_session) throw new Error('Ubus login failed');
71
+
72
+ this.sessionId = result.ubus_rpc_session;
73
+ this.sessionExpiresAt = now + 240_000;
74
+
75
+ if (this.logDebug) this.emit('debug', `Ubus login OK`);
76
+ return this.sessionId;
57
77
  }
58
78
 
59
- // --- ubus call przez SSH ---
60
79
  async ubusCall(service, method, params = {}) {
61
- const cmd = `ubus call ${service} ${method} '${JSON.stringify(params)}'`;
62
- return await this.sshCall(cmd);
80
+ const session = await this.login();
81
+ const response = await this.axiosInstance.post('', {
82
+ jsonrpc: '2.0',
83
+ id: 2,
84
+ method: 'call',
85
+ params: [session, service, method, params]
86
+ });
87
+
88
+ if (response.data?.error) throw new Error(response.data.error.message || 'Ubus call error');
89
+
90
+ return response.data.result[1];
63
91
  }
64
92
 
65
- // --- Pobranie info i statusu ---
66
93
  async connect() {
67
- const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] };
68
94
  try {
69
- const systemInfo = await this.ubusCall("system", "board");
70
- if (this.logDebug) this.emit("debug", `[${this.host}] System info: ${JSON.stringify(systemInfo, null, 2)}`);
71
-
72
- const wirelessStatus = await this.ubusCall("network.wireless", "status");
73
- if (this.logDebug) this.emit("debug", `[${this.host}] Wireless status: ${JSON.stringify(wirelessStatus, null, 2)}`);
74
-
75
- const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
76
- name: radio.name,
77
- state: radio.up === true,
78
- interfaces: Object.values(radio.interfaces).map(i => ({
79
- name: i.ssid,
80
- state: i.up === true,
81
- mode: i.mode
82
- }))
83
- }));
84
-
85
- const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
86
-
87
- this.emit("systemInfo", systemInfo);
88
- this.emit("wirelessStatus", wirelessStatus);
89
- this.emit("wirelessRadios", wirelessRadios);
90
- this.emit("ssids", ssids);
91
-
92
- if (this.firstRun) {
93
- this.emit("success", `Connect success`);
94
- this.firstRun = false;
95
+ const openWrtInfo = { state: false, info: '', systemInfo: {}, networkInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
96
+ const systemInfo = await this.ubusCall('system', 'board');
97
+ if (this.logDebug) this.emit('debug', `System info data: ${JSON.stringify(systemInfo, null, 2)}`);
98
+
99
+ //const networkInfo = await this.ubusCall('network.device', 'status', '{ "name": "eth0" }');
100
+ //const networkInfo = await this.ubusCall('file', 'read', { path: '/sys/class/net/eth0/address' });
101
+ //if (this.logDebug) this.emit('debug', `Network info data: ${networkInfo}`);
102
+
103
+ //const wirelessStatus = await this.ubusCall('network.wireless', 'status');
104
+ const wirelessStatus = await this.ubusCall('uci', 'get', { config: 'wireless' });
105
+ if (this.logDebug) this.emit('debug', `Wireless status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
106
+
107
+ //const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
108
+ //name: radio.name,
109
+ //state: radio.up === true,
110
+ //interfaces: Object.values(radio.interfaces).map(i => ({
111
+ //name: i.ssid,
112
+ //state: i.up === true,
113
+ //mode: i.mode
114
+ //}))
115
+ //}));
116
+
117
+ //const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
118
+ const ssids = Object.entries(wirelessStatus.values || {}).flatMap(([key, data]) => {
119
+ if (!key.startsWith('wifinet') && !key.startsWith('default_radio')) return [];
120
+ return [{
121
+ ifname: data['.name'] || key,
122
+ name: data.ssid || null,
123
+ device: data.device || null,
124
+ mode: data.mode || null,
125
+ hidden: data.hidden === '1' || data.hidden === true,
126
+ state: true
127
+ }];
128
+ });
129
+ if (ssids.length === 0) {
130
+ openWrtInfo.info = 'SSIDs not found';
131
+ return openWrtInfo;
95
132
  }
96
133
 
97
134
  openWrtInfo.state = true;
135
+ openWrtInfo.info = `Connect Success`;
98
136
  openWrtInfo.systemInfo = systemInfo;
137
+ //openWrtInfo.networkInfo = networkInfo;
99
138
  openWrtInfo.wirelessStatus = wirelessStatus;
100
- openWrtInfo.wirelessRadios = wirelessRadios;
139
+ //openWrtInfo.wirelessRadios = wirelessRadios;
101
140
  openWrtInfo.ssids = ssids;
102
141
 
103
- if (this.logDebug) this.emit("debug", `[${this.host}] OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
142
+ // emit data
143
+ this.emit('openWrtInfo', openWrtInfo);
144
+
104
145
  return openWrtInfo;
105
146
  } catch (error) {
106
- if (this.logError) this.emit("error", `[${this.host}] Connect error: ${error.message}`);
107
- return openWrtInfo;
108
- }
109
- }
110
-
111
- // --- Włączanie / wyłączanie SSID ---
112
- async send(type, ssidName, state) {
113
- switch (type) {
114
- case "ssid":
115
- await this.handleWithLock(async () => {
116
- if (this.logDebug) this.emit("debug", `[${this.host}] ${state ? "Enabling" : "Disabling"} SSID ${ssidName}`);
117
-
118
- const status = await this.ubusCall("network.wireless", "status");
119
- const iface = await this.functions.findIfaceBySsid(status, ssidName);
120
- if (!iface) throw new Error(`SSID ${ssidName} not found`);
121
-
122
- const section = iface.section;
123
- if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
124
-
125
- await this.ubusCall("uci", "set", {
126
- config: "wireless",
127
- section: section,
128
- values: { disabled: state ? "0" : "1" }
129
- });
130
- await this.ubusCall("uci", "commit", { config: "wireless" });
131
- await this.ubusCall("network.wireless", "reload", {});
132
-
133
- if (this.logDebug) this.emit("debug", `[${this.host}] SSID ${ssidName} ${state ? "enabled" : "disabled"}`);
134
- });
135
- break;
136
- case "switch":
137
- break;
147
+ throw new Error(`Connect error: ${error.message}`);
138
148
  }
139
149
  }
140
150
 
@@ -173,5 +183,4 @@ class OpenWrt extends EventEmitter {
173
183
  }
174
184
  }
175
185
 
176
- export default OpenWrt;
177
-
186
+ export default OpenWrt;
package/src/restful.js ADDED
@@ -0,0 +1,87 @@
1
+ import express, { json } from 'express';
2
+ import EventEmitter from 'events';
3
+
4
+ const DEFAULT_MESSAGE = 'This data is not available at this time.';
5
+
6
+ class RestFul extends EventEmitter {
7
+ constructor(config) {
8
+ super();
9
+ this.port = config.port;
10
+ this.logWarn = config.logWarn;
11
+ this.logDebug = config.logDebug;
12
+
13
+ this.restFulData = {
14
+ info: DEFAULT_MESSAGE,
15
+ state: DEFAULT_MESSAGE
16
+ };
17
+
18
+ this.connect();
19
+ }
20
+
21
+ connect() {
22
+ try {
23
+ const app = express();
24
+ app.set('json spaces', 2);
25
+ app.use(json());
26
+
27
+ // Register GET routes for all keys
28
+ for (const key of Object.keys(this.restFulData)) {
29
+ app.get(`/${key}`, (req, res) => {
30
+ res.json(this.restFulData[key]);
31
+ });
32
+ }
33
+
34
+ // Health check route
35
+ app.get('/status', (req, res) => {
36
+ res.json({
37
+ status: 'online',
38
+ uptime: process.uptime(),
39
+ available_paths: Object.keys(this.restFulData).map(k => `/${k}`)
40
+ });
41
+ });
42
+
43
+ // POST route to update values
44
+ app.post('/', (req, res) => {
45
+ try {
46
+ const obj = req.body;
47
+ if (!obj || typeof obj !== 'object' || Object.keys(obj).length === 0) {
48
+ if (this.logWarn) this.emit('warn', 'RESTFul Invalid JSON payload');
49
+ return res.status(400).json({ error: 'RESTFul Invalid JSON payload' });
50
+ }
51
+
52
+ const key = Object.keys(obj)[0];
53
+ const value = obj[key];
54
+ this.emit('set', key, value);
55
+ this.update(key, value);
56
+
57
+ if (this.logDebug) this.emit('debug', `RESTFul post data: ${JSON.stringify(obj, null, 2)}`);
58
+
59
+ res.json({ success: true, received: obj });
60
+ } catch (error) {
61
+ if (this.logWarn) this.emit('warn', `RESTFul Parse error: ${error}`);
62
+ res.status(500).json({ error: 'RESTFul Internal Server Error' });
63
+ }
64
+ });
65
+
66
+ // Start the server
67
+ app.listen(this.port, () => {
68
+ this.emit('connected', `RESTful started on port: ${this.port}`);
69
+ });
70
+ } catch (error) {
71
+ if (this.logWarn) this.emit('warn', `RESTful Connect error: ${error}`);
72
+ }
73
+ }
74
+
75
+ update(path, data) {
76
+ if (this.restFulData.hasOwnProperty(path)) {
77
+ this.restFulData[path] = data;
78
+ } else {
79
+ if (this.logWarn) this.emit('warn', `Unknown RESTFul update path: ${path}, data: ${JSON.stringify(data)}`);
80
+ return;
81
+ }
82
+
83
+ if (this.logDebug) this.emit('debug', `RESTFul update path: ${path}, data: ${JSON.stringify(data)}`);
84
+ }
85
+ }
86
+
87
+ export default RestFul;
@@ -1,7 +1,9 @@
1
1
  import EventEmitter from 'events';
2
+ import RestFul from './restful.js';
3
+ import Mqtt from './mqtt.js';
2
4
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
3
5
 
4
- class SwitchDevice extends EventEmitter {
6
+ class Switch extends EventEmitter {
5
7
  constructor(api, config, openWrt, openWrtInfo) {
6
8
  super();
7
9
 
@@ -12,8 +14,12 @@ class SwitchDevice extends EventEmitter {
12
14
  AccessoryUUID = api.hap.uuid;
13
15
 
14
16
  //config
15
- this.name = config.switch.name || openWrtInfo.systemInfo.hostname;
16
- this.switch = config.switch;
17
+ this.config = config;
18
+ this.host = config.host;
19
+ this.name = config.apDevice?.name || openWrtInfo.systemInfo.hostname;
20
+ this.namePrefix = config.apDevice?.namePrefix || false;
21
+ this.sensorsEnabled = config.apDevice?.sensor || false
22
+ this.logDeviceInfo = config.log?.deviceInfo || false;
17
23
  this.logInfo = config.log?.info || false;
18
24
  this.logDebug = config.log?.debug || false;
19
25
 
@@ -26,7 +32,37 @@ class SwitchDevice extends EventEmitter {
26
32
  //openwrt
27
33
  this.openWrt = openWrt;
28
34
  this.openWrtInfo = openWrtInfo;
29
- this.ssids = openWrtInfo.ssids;
35
+
36
+ //openwrt client
37
+ openWrt.on('openWrtInfo', (openWrtInfo) => {
38
+ this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, openWrtInfo.systemInfo.release?.version);
39
+
40
+ // update state
41
+ const ports = openWrtInfo.ports;
42
+ for (let i = 0; i < ports.length; i++) {
43
+ const port = ports[i];
44
+ const name = port.name;
45
+ const state = port.state;
46
+ const serviceName = this.namePrefix ? `${this.name} ${name}` : name;
47
+ this.services?.[i]
48
+ ?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
49
+ .updateCharacteristic(Characteristic.On, state);
50
+
51
+ this.sensorServices?.[i]
52
+ ?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
53
+ .updateCharacteristic(Characteristic.ContactSensorState, state);
54
+
55
+ if (this.logInfo) {
56
+ this.emit('info', `Name: ${port.name}`);
57
+ this.emit('info', `State: ${port.state}`);
58
+ this.emit('info', `Mode: ${port.mode}`);
59
+ }
60
+ }
61
+
62
+ //restFul and mqtt
63
+ if (this.restFulConnected) this.emit('restFul', 'info', openWrtInfo);
64
+ if (this.mqttConnected) this.emit('mqtt', 'Info', openWrtInfo);
65
+ });
30
66
  };
31
67
 
32
68
  async externalIntegrations() {
@@ -65,8 +101,8 @@ class SwitchDevice extends EventEmitter {
65
101
  this.mqtt1 = new Mqtt({
66
102
  host: this.mqtt.host,
67
103
  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}`,
104
+ clientId: this.mqtt.clientId ? `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}_${Math.random().toString(16).slice(3)}`,
105
+ prefix: this.mqtt.prefix ? `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.mqtt.prefix}/${this.name}` : `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.name}`,
70
106
  user: this.mqtt.auth?.user,
71
107
  passwd: this.mqtt.auth?.passwd,
72
108
  logWarn: this.logWarn,
@@ -121,29 +157,29 @@ class SwitchDevice extends EventEmitter {
121
157
  //prepare accessory
122
158
  if (this.logDebug) this.emit('debug', `prepare accessory`);
123
159
  const accessoryName = this.name;
124
- const accessoryUUID = AccessoryUUID.generate(this.openWrtInfo.systemInfo.hostname);
125
- const accessoryCategory = Categories.ROUTER;
160
+ const accessoryUUID = AccessoryUUID.generate(this.host + this.openWrtInfo.systemInfo.system);
161
+ const accessoryCategory = Categories.AIRPORT;
126
162
  const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
127
163
 
128
164
  //prepare information service
129
165
  if (this.logDebug) this.emit('debug', `prepare information service`);
130
- accessory.getService(Service.AccessoryInformation)
131
- .setCharacteristic(Characteristic.Manufacturer, 'OpenWrt')
166
+ this.informationService = accessory.getService(Service.AccessoryInformation)
167
+ .setCharacteristic(Characteristic.Manufacturer, this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt')
132
168
  .setCharacteristic(Characteristic.Model, this.openWrtInfo.systemInfo.model)
133
169
  .setCharacteristic(Characteristic.SerialNumber, this.openWrtInfo.systemInfo.system)
134
- .setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.description)
135
- .setCharacteristic(Characteristic.ConfiguredName, accessoryName);
170
+ .setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.version);
136
171
 
137
172
  if (this.logDebug) this.emit('debug', `prepare service`);
138
173
 
139
- //device
174
+ //services
140
175
  this.services = [];
141
- for (const port of this.ports) {
176
+ this.sensorServices = [];
177
+ for (const port of this.openWrtInfo.ports) {
142
178
  const name = port.name;
143
179
  if (this.logDebug) this.emit('debug', `prepare port: ${name} service`);
144
180
 
145
- const serviceName = this.accessPoint.namePrefix ? `${accessoryName} ${ssidName}` : ssidName;
146
- const service = accessory.addService(Service.Switch, serviceName, `service${ssidName}`);
181
+ const serviceName = this.namePrefix ? `${accessoryName} ${name}` : name;
182
+ const service = accessory.addService(Service.Switch, serviceName, `service${name}`);
147
183
  service.addOptionalCharacteristic(Characteristic.ConfiguredName);
148
184
  service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
149
185
  service.getCharacteristic(Characteristic.On)
@@ -163,10 +199,8 @@ class SwitchDevice extends EventEmitter {
163
199
  });
164
200
  this.services.push(service);
165
201
 
166
- if (this.accessPoint.sensor) {
202
+ if (this.sensorsEnabled) {
167
203
  if (this.logDebug) this.emit('debug', `prepare port: ${name} sensor service`);
168
-
169
- this.sensorServices = [];
170
204
  const sensorService = accessory.addService(Service.ContactSensor, serviceName, `sensorService${name}`);
171
205
  sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
172
206
  sensorService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
@@ -191,48 +225,16 @@ class SwitchDevice extends EventEmitter {
191
225
  //start external integrations
192
226
  if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
193
227
 
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', `Firmware: ${this.openWrtInfo.systemInfo.release?.description}`);
199
- this.emit('devInfo', `----------------------------------`);
200
-
201
- //openwrt client
202
- this.openWrt.on('systemInfo', (info) => {
203
- this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, info.release?.description);
204
- })
205
- .on('switchStatus', async (status) => {
206
- })
207
- .on('switchPorts', async (ports) => {
208
- // sensors
209
- for (let i = 0; i < ports.length; i++) {
210
- const port = ports[i];
211
-
212
- const name = port[i].name;
213
- const state = port[i].state;
214
- const serviceName = this.accessPoint.namePrefix ? `${this.name} ${name}` : name;
215
- this.services?.[i]
216
- ?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
217
- .updateCharacteristic(Characteristic.On, state);
218
-
219
- this.sensorServices?.[i]
220
- ?.setCharacteristic(Characteristic.ConfiguredName, name)
221
- .updateCharacteristic(Characteristic.ContactSensorState, state ? 0 : 1);
222
-
223
- if (this.logInfo) {
224
- this.emit('info', `Port name: ${port.name}`);
225
- this.emit('info', `Name: ${port.state}`);
226
- this.emit('info', `Mode: ${port.mode}`);
227
- }
228
- }
229
- })
230
- .on('restFul', (path, data) => {
231
- if (this.restFulConnected) this.restFul1.update(path, data);
232
- })
233
- .on('mqtt', (topic, message) => {
234
- if (this.mqttConnected) this.mqtt1.emit('publish', topic, message);
235
- });
228
+ if (this.logDeviceInfo) {
229
+ this.emit('devInfo', `-------- Access Point ${this.name} --------`);
230
+ this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model || this.openWrtInfo.systemInfo.board_name}`);
231
+ this.emit('devInfo', `System: ${this.openWrtInfo.systemInfo.system}`);
232
+ this.emit('devInfo', `Kernel: ${this.openWrtInfo.systemInfo.kernel}`);
233
+ this.emit('devInfo', `Firmware: ${this.openWrtInfo.systemInfo.release?.description}`);
234
+ this.emit('devInfo', `Target: ${this.openWrtInfo.systemInfo.release?.target}`);
235
+ this.emit('devInfo', `Ports: ${this.openWrtInfo.ports.length}`);
236
+ this.emit('devInfo', `----------------------------------`);
237
+ }
236
238
 
237
239
  //prepare accessory
238
240
  const accessory = await this.prepareAccessory();
@@ -242,4 +244,4 @@ class SwitchDevice extends EventEmitter {
242
244
  }
243
245
  }
244
246
  };
245
- export default SwitchDevice;
247
+ export default Switch;