homebridge-openwrt-control 0.0.2-beta.5 → 0.0.2-beta.50
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/config.schema.json +2 -2
- package/index.js +50 -50
- package/package.json +1 -1
- package/src/{apdevice.js → accesspoint.js} +62 -56
- package/src/openwrt.js +80 -69
- package/src/{swdevice.js → switch.js} +59 -54
package/config.schema.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"singular": true,
|
|
5
5
|
"fixArrays": true,
|
|
6
6
|
"strictValidation": true,
|
|
7
|
-
"headerDisplay": "This plugin works with OpenWrt
|
|
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": "
|
|
16
|
+
"title": "Device",
|
|
17
17
|
"properties": {
|
|
18
18
|
"name": {
|
|
19
19
|
"title": "Name",
|
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/
|
|
5
|
-
import Switch from './src/
|
|
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');
|
|
@@ -27,9 +26,9 @@ class OpenWrtPlatform {
|
|
|
27
26
|
|
|
28
27
|
api.on('didFinishLaunching', async () => {
|
|
29
28
|
for (const deviceConfig of config.devices) {
|
|
30
|
-
const { name, host,
|
|
29
|
+
const { name, host, displayType } = deviceConfig;
|
|
31
30
|
if (!name || !host || !displayType) {
|
|
32
|
-
log.warn(`Device: ${host || 'host missing'}, ${name || 'name missing'}, ${!displayType ? ',
|
|
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
|
}
|
|
35
34
|
|
|
@@ -61,43 +60,46 @@ class OpenWrtPlatform {
|
|
|
61
60
|
log.info(`Device: ${host} ${name}, Config: ${JSON.stringify(safeConfig, null, 2)}`);
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
if (deviceConfig.accessPoint?.enable) this.devices.push('accessPoint');
|
|
66
|
-
if (deviceConfig.switch?.enable) this.devices.push('switch');
|
|
63
|
+
try {
|
|
67
64
|
|
|
68
|
-
|
|
65
|
+
// create impulse generator for every device
|
|
66
|
+
const impulseGenerator = new ImpulseGenerator()
|
|
67
|
+
.on('start', async () => {
|
|
68
|
+
try {
|
|
69
|
+
|
|
70
|
+
let openWrt = new OpenWrt(deviceConfig)
|
|
71
|
+
.on('success', msg => logLevel.success && log.success(`Device: ${host}, ${msg}`))
|
|
72
|
+
.on('info', msg => log.info(`Device: ${host} ${name}, ${msg}`))
|
|
73
|
+
.on('debug', msg => log.info(`Device: ${host} ${name}, debug: ${msg}`))
|
|
74
|
+
.on('warn', msg => log.warn(`Device: ${host} ${name}, ${msg}`))
|
|
75
|
+
.on('error', msg => log.error(`Device: ${host} ${name}, ${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
|
+
}
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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}`))
|
|
83
|
+
// start openwrt impulse generator
|
|
84
|
+
const refreshInterval = (deviceConfig.refreshInterval ?? 5) * 1000;
|
|
85
|
+
await openWrt.impulseGenerator.state(true, [{ name: 'connect', sampling: refreshInterval }], false);
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
87
|
+
const configuredDevices = [];
|
|
88
|
+
if (deviceConfig.accessPoint?.enable) configuredDevices.push('accessPoint');
|
|
89
|
+
if (deviceConfig.switch?.enable) configuredDevices.push('switch');
|
|
90
|
+
if (configuredDevices.length === 0) return;
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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;
|
|
95
|
-
default:
|
|
96
|
-
if (logLevel.warn) log.warn(`Device: ${host} ${name}, unknown device: ${device}`);
|
|
97
|
-
return;
|
|
92
|
+
for (const device of configuredDevices) {
|
|
93
|
+
// create device clases
|
|
94
|
+
const DeviceClasses = { accessPoint: AccessPoint, switch: Switch };
|
|
95
|
+
const DeviceClass = DeviceClasses[device];
|
|
96
|
+
|
|
97
|
+
if (!DeviceClass) {
|
|
98
|
+
if (logLevel.warn) log.warn(`Device: ${host} ${name}, class not found for: ${device}`);
|
|
99
|
+
continue;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
type
|
|
102
|
+
const type = new DeviceClass(api, deviceConfig, openWrt, openWrtInfo)
|
|
101
103
|
.on('devInfo', msg => logLevel.devInfo && log.info(msg))
|
|
102
104
|
.on('success', msg => logLevel.success && log.success(`Device: ${host} ${name}, ${msg}`))
|
|
103
105
|
.on('info', msg => log.info(`Device: ${host} ${name}, ${msg}`))
|
|
@@ -110,23 +112,21 @@ class OpenWrtPlatform {
|
|
|
110
112
|
api.publishExternalAccessories(PluginName, [accessory]);
|
|
111
113
|
if (logLevel.success) log.success(`Device: ${host} ${name}, Published as external accessory.`);
|
|
112
114
|
}
|
|
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
115
|
}
|
|
119
|
-
})
|
|
120
|
-
.on('state', (state) => {
|
|
121
|
-
if (logLevel.debug) log.info(`Device: ${host} ${name}, Start impulse generator ${state ? 'started' : 'stopped'}.`);
|
|
122
|
-
});
|
|
123
116
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
// stop accessory impulse generator
|
|
118
|
+
await impulseGenerator.state(false);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (logLevel.error) log.error(`Device: ${host} ${name}, Start impulse generator error: ${error.message ?? error}, trying again.`);
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
.on('state', (state) => {
|
|
124
|
+
if (logLevel.debug) log.info(`Device: ${host} ${name}, Start impulse generator ${state ? 'started' : 'stopped'}.`);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// start accessory impulse generator
|
|
128
|
+
await impulseGenerator.state(true, [{ name: 'start', sampling: 120000 }]);
|
|
127
129
|
|
|
128
|
-
// start openwrt impulse generator
|
|
129
|
-
await openWrt.impulseGenerator.state(true, [{ name: 'connect', sampling: refreshInterval }], false);
|
|
130
130
|
} catch (error) {
|
|
131
131
|
if (logLevel.error) log.error(`Device: ${host} ${name}, Did finish launching error: ${error.message ?? error}`);
|
|
132
132
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class AccessPoint extends EventEmitter {
|
|
5
5
|
constructor(api, config, openWrt, openWrtInfo) {
|
|
6
6
|
super();
|
|
7
7
|
|
|
@@ -12,8 +12,10 @@ class AccessPointDevice extends EventEmitter {
|
|
|
12
12
|
AccessoryUUID = api.hap.uuid;
|
|
13
13
|
|
|
14
14
|
//config
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
15
|
+
this.host = config.host;
|
|
16
|
+
this.name = config.accessPoint?.name || openWrtInfo.systemInfo.hostname;
|
|
17
|
+
this.namePrefix = config.accessPoint?.namePrefix || false;
|
|
18
|
+
this.sensorsEnabled = config.accessPoint?.sensor || false
|
|
17
19
|
this.logInfo = config.log?.info || false;
|
|
18
20
|
this.logDebug = config.log?.debug || false;
|
|
19
21
|
|
|
@@ -27,6 +29,48 @@ class AccessPointDevice extends EventEmitter {
|
|
|
27
29
|
this.openWrt = openWrt;
|
|
28
30
|
this.openWrtInfo = openWrtInfo;
|
|
29
31
|
this.ssids = openWrtInfo.ssids;
|
|
32
|
+
|
|
33
|
+
//openwrt client
|
|
34
|
+
this.openWrt.on('systemInfo', (info) => {
|
|
35
|
+
this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, info.release?.version);
|
|
36
|
+
})
|
|
37
|
+
.on('networkInfo', async (info) => {
|
|
38
|
+
})
|
|
39
|
+
.on('wirelessStatus', async (status) => {
|
|
40
|
+
})
|
|
41
|
+
.on('wirelessRadios', async (radios) => {
|
|
42
|
+
})
|
|
43
|
+
.on('ssids', async (ssids) => {
|
|
44
|
+
this.ssids = ssids;
|
|
45
|
+
|
|
46
|
+
// sensors
|
|
47
|
+
for (let i = 0; i < ssids.length; i++) {
|
|
48
|
+
const ssid = ssids[i];
|
|
49
|
+
const name = ssid.name;
|
|
50
|
+
const state = ssid.state;
|
|
51
|
+
const serviceName = this.namePrefix ? `${this.name} ${name}` : name;
|
|
52
|
+
this.services?.[i]
|
|
53
|
+
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
54
|
+
.updateCharacteristic(Characteristic.On, state);
|
|
55
|
+
|
|
56
|
+
this.sensorServices?.[i]
|
|
57
|
+
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
58
|
+
.updateCharacteristic(Characteristic.ContactSensorState, !state);
|
|
59
|
+
|
|
60
|
+
if (this.logInfo) {
|
|
61
|
+
this.emit('info', `Name: ${ssid.name}`);
|
|
62
|
+
this.emit('info', `State: ${ssid.state}`);
|
|
63
|
+
this.emit('info', `Mode: ${ssid.mode}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.on('restFul', (path, data) => {
|
|
68
|
+
if (this.restFulConnected) this.restFul1.update(path, data);
|
|
69
|
+
})
|
|
70
|
+
.on('mqtt', (topic, message) => {
|
|
71
|
+
if (this.mqttConnected) this.mqtt1.emit('publish', topic, message);
|
|
72
|
+
});
|
|
73
|
+
|
|
30
74
|
};
|
|
31
75
|
|
|
32
76
|
async externalIntegrations() {
|
|
@@ -65,8 +109,8 @@ class AccessPointDevice extends EventEmitter {
|
|
|
65
109
|
this.mqtt1 = new Mqtt({
|
|
66
110
|
host: this.mqtt.host,
|
|
67
111
|
port: this.mqtt.port || 1883,
|
|
68
|
-
clientId: this.mqtt.clientId ? `${this.
|
|
69
|
-
prefix: this.mqtt.prefix ? `${this.
|
|
112
|
+
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)}`,
|
|
113
|
+
prefix: this.mqtt.prefix ? `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.mqtt.prefix}/${this.name}` : `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.name}`,
|
|
70
114
|
user: this.mqtt.auth?.user,
|
|
71
115
|
passwd: this.mqtt.auth?.passwd,
|
|
72
116
|
logWarn: this.logWarn,
|
|
@@ -121,28 +165,28 @@ class AccessPointDevice extends EventEmitter {
|
|
|
121
165
|
//prepare accessory
|
|
122
166
|
if (this.logDebug) this.emit('debug', `prepare accessory`);
|
|
123
167
|
const accessoryName = this.name;
|
|
124
|
-
const accessoryUUID = AccessoryUUID.generate(this.openWrtInfo.systemInfo.
|
|
168
|
+
const accessoryUUID = AccessoryUUID.generate(this.host + this.openWrtInfo.systemInfo.system);
|
|
125
169
|
const accessoryCategory = Categories.AIRPORT;
|
|
126
170
|
const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
|
|
127
171
|
|
|
128
172
|
//prepare information service
|
|
129
173
|
if (this.logDebug) this.emit('debug', `prepare information service`);
|
|
130
|
-
accessory.getService(Service.AccessoryInformation)
|
|
131
|
-
.setCharacteristic(Characteristic.Manufacturer, 'OpenWrt')
|
|
174
|
+
this.informationService = accessory.getService(Service.AccessoryInformation)
|
|
175
|
+
.setCharacteristic(Characteristic.Manufacturer, this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt')
|
|
132
176
|
.setCharacteristic(Characteristic.Model, this.openWrtInfo.systemInfo.model)
|
|
133
177
|
.setCharacteristic(Characteristic.SerialNumber, this.openWrtInfo.systemInfo.system)
|
|
134
|
-
.setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.
|
|
135
|
-
.setCharacteristic(Characteristic.ConfiguredName, accessoryName);
|
|
178
|
+
.setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.version);
|
|
136
179
|
|
|
137
180
|
if (this.logDebug) this.emit('debug', `prepare service`);
|
|
138
181
|
|
|
139
|
-
//
|
|
182
|
+
//services
|
|
140
183
|
this.services = [];
|
|
184
|
+
this.sensorServices = [];
|
|
141
185
|
for (const ssid of this.ssids) {
|
|
142
186
|
const name = ssid.name;
|
|
143
187
|
if (this.logDebug) this.emit('debug', `prepare ssid: ${name} service`);
|
|
144
188
|
|
|
145
|
-
const serviceName = this.
|
|
189
|
+
const serviceName = this.namePrefix ? `${accessoryName} ${name}` : name;
|
|
146
190
|
const service = accessory.addService(Service.Switch, serviceName, `service${name}`);
|
|
147
191
|
service.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
148
192
|
service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
@@ -163,10 +207,8 @@ class AccessPointDevice extends EventEmitter {
|
|
|
163
207
|
});
|
|
164
208
|
this.services.push(service);
|
|
165
209
|
|
|
166
|
-
if (this.
|
|
210
|
+
if (this.sensorsEnabled) {
|
|
167
211
|
if (this.logDebug) this.emit('debug', `prepare ssid: ${name} sensor service`);
|
|
168
|
-
|
|
169
|
-
this.sensorServices = [];
|
|
170
212
|
const sensorService = accessory.addService(Service.ContactSensor, serviceName, `sensorService${name}`);
|
|
171
213
|
sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
172
214
|
sensorService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
@@ -192,50 +234,14 @@ class AccessPointDevice extends EventEmitter {
|
|
|
192
234
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
193
235
|
|
|
194
236
|
this.emit('devInfo', `-------- Access Point ${this.name} --------`);
|
|
195
|
-
this.emit('devInfo', `
|
|
196
|
-
this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model}`);
|
|
237
|
+
this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model || this.openWrtInfo.systemInfo.board_name}`);
|
|
197
238
|
this.emit('devInfo', `System: ${this.openWrtInfo.systemInfo.system}`);
|
|
239
|
+
this.emit('devInfo', `Kernel: ${this.openWrtInfo.systemInfo.kernel}`);
|
|
198
240
|
this.emit('devInfo', `Firmware: ${this.openWrtInfo.systemInfo.release?.description}`);
|
|
241
|
+
this.emit('devInfo', `Target: ${this.openWrtInfo.systemInfo.release?.target}`);
|
|
242
|
+
this.emit('devInfo', `SSIDs: ${this.ssids.length}`);
|
|
199
243
|
this.emit('devInfo', `----------------------------------`);
|
|
200
244
|
|
|
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
|
-
});
|
|
238
|
-
|
|
239
245
|
//prepare accessory
|
|
240
246
|
const accessory = await this.prepareAccessory();
|
|
241
247
|
return accessory;
|
|
@@ -244,4 +250,4 @@ class AccessPointDevice extends EventEmitter {
|
|
|
244
250
|
}
|
|
245
251
|
}
|
|
246
252
|
};
|
|
247
|
-
export default
|
|
253
|
+
export default AccessPoint;
|
package/src/openwrt.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import EventEmitter from
|
|
2
|
-
import axios from
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import axios from 'axios';
|
|
3
3
|
import Functions from './functions.js';
|
|
4
|
-
import ImpulseGenerator from
|
|
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.
|
|
11
|
-
this.
|
|
12
|
-
this.user = config.user;
|
|
13
|
-
this.passwd = config.passwd;
|
|
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
|
|
|
@@ -26,7 +24,7 @@ class OpenWrt extends EventEmitter {
|
|
|
26
24
|
|
|
27
25
|
this.functions = new Functions();
|
|
28
26
|
this.axiosInstance = axios.create({
|
|
29
|
-
baseURL:
|
|
27
|
+
baseURL: `http://${config.host}/ubus`,
|
|
30
28
|
timeout: 5000,
|
|
31
29
|
headers: {
|
|
32
30
|
"Content-Type": "application/json"
|
|
@@ -34,11 +32,11 @@ class OpenWrt extends EventEmitter {
|
|
|
34
32
|
});
|
|
35
33
|
|
|
36
34
|
this.impulseGenerator = new ImpulseGenerator()
|
|
37
|
-
.on(
|
|
35
|
+
.on('connect', () => this.handleWithLock(async () => {
|
|
38
36
|
await this.connect();
|
|
39
37
|
}))
|
|
40
|
-
.on(
|
|
41
|
-
this.emit(state ?
|
|
38
|
+
.on('state', (state) => {
|
|
39
|
+
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
42
40
|
});
|
|
43
41
|
}
|
|
44
42
|
|
|
@@ -49,8 +47,7 @@ class OpenWrt extends EventEmitter {
|
|
|
49
47
|
try {
|
|
50
48
|
await fn();
|
|
51
49
|
} catch (error) {
|
|
52
|
-
this.emit(
|
|
53
|
-
);
|
|
50
|
+
this.emit('error', `Impulse generator error: ${error.message}`);
|
|
54
51
|
} finally {
|
|
55
52
|
this.lock = false;
|
|
56
53
|
}
|
|
@@ -62,83 +59,98 @@ class OpenWrt extends EventEmitter {
|
|
|
62
59
|
return this.sessionId;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
const response = await this.axiosInstance.post(
|
|
66
|
-
jsonrpc:
|
|
62
|
+
const response = await this.axiosInstance.post('', {
|
|
63
|
+
jsonrpc: '2.0',
|
|
67
64
|
id: 1,
|
|
68
|
-
method:
|
|
69
|
-
params: [
|
|
65
|
+
method: 'call',
|
|
66
|
+
params: ['00000000000000000000000000000000', 'session', 'login', { username: this.user, password: this.passwd }]
|
|
70
67
|
});
|
|
71
68
|
|
|
72
69
|
const result = response.data?.result?.[1];
|
|
73
|
-
if (!result?.ubus_rpc_session)
|
|
74
|
-
throw new Error("ubus login failed");
|
|
75
|
-
}
|
|
70
|
+
if (!result?.ubus_rpc_session) throw new Error('Ubus login failed');
|
|
76
71
|
|
|
77
72
|
this.sessionId = result.ubus_rpc_session;
|
|
78
73
|
this.sessionExpiresAt = now + 240_000;
|
|
79
74
|
|
|
80
|
-
if (this.logDebug) this.emit(
|
|
75
|
+
if (this.logDebug) this.emit('debug', `Ubus login OK`);
|
|
81
76
|
return this.sessionId;
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
async ubusCall(service, method, params = {}) {
|
|
85
80
|
const session = await this.login();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
jsonrpc: "2.0",
|
|
81
|
+
const response = await this.axiosInstance.post('', {
|
|
82
|
+
jsonrpc: '2.0',
|
|
89
83
|
id: 2,
|
|
90
|
-
method:
|
|
84
|
+
method: 'call',
|
|
91
85
|
params: [session, service, method, params]
|
|
92
86
|
});
|
|
93
87
|
|
|
94
|
-
if (response.data?.error)
|
|
95
|
-
throw new Error(response.data.error.message || "ubus call error");
|
|
96
|
-
}
|
|
88
|
+
if (response.data?.error) throw new Error(response.data.error.message || 'Ubus call error');
|
|
97
89
|
|
|
98
90
|
return response.data.result[1];
|
|
99
91
|
}
|
|
100
92
|
|
|
101
93
|
async connect() {
|
|
102
|
-
|
|
103
|
-
const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
|
|
104
|
-
const systemInfo = await this.ubusCall("system", "board");
|
|
105
|
-
if (this.logDebug) this.emit("debug", `System info data: ${JSON.stringify(systemInfo, null, 2)}`);
|
|
106
|
-
|
|
107
|
-
const wirelessStatus = await this.ubusCall("network.wireless", "status");
|
|
108
|
-
if (this.logDebug) this.emit("debug", `Wireless status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
|
|
109
|
-
|
|
110
|
-
const wirelessRadios = Object.values(status.radios).map(radio => ({
|
|
111
|
-
name: radio.name,
|
|
112
|
-
state: radio.up === true,
|
|
113
|
-
interfaces: Object.values(radio.interfaces).map(i => ({
|
|
114
|
-
name: i.ssid,
|
|
115
|
-
state: i.up === true,
|
|
116
|
-
mode: i.mode
|
|
117
|
-
}))
|
|
118
|
-
}));
|
|
119
|
-
|
|
120
|
-
const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
|
|
121
|
-
this.emit("systemInfo", systemInfo);
|
|
122
|
-
this.emit("wirelessStatus", wirelessStatus);
|
|
123
|
-
this.emit("wirelessRadios", wirelessRadios);
|
|
124
|
-
this.emit("ssids", ssids);
|
|
94
|
+
const openWrtInfo = { state: false, systemInfo: {}, networkInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
|
|
125
95
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
96
|
+
try {
|
|
97
|
+
const systemInfo = await this.ubusCall('system', 'board');
|
|
98
|
+
if (this.logDebug) this.emit('debug', `System info data: ${JSON.stringify(systemInfo, null, 2)}`);
|
|
99
|
+
|
|
100
|
+
//const networkInfo = await this.ubusCall('network.device', 'status', '{ "name": "eth0" }');
|
|
101
|
+
//const networkInfo = await this.ubusCall('file', 'read', { path: '/sys/class/net/eth0/address' });
|
|
102
|
+
//if (this.logDebug) this.emit('debug', `Network info data: ${networkInfo}`);
|
|
103
|
+
|
|
104
|
+
//const wirelessStatus = await this.ubusCall('network.wireless', 'status');
|
|
105
|
+
const wirelessStatus = await this.ubusCall('uci', 'get', { config: 'wireless' });
|
|
106
|
+
if (this.logDebug) this.emit('debug', `Wireless status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
|
|
107
|
+
|
|
108
|
+
//const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
|
|
109
|
+
//name: radio.name,
|
|
110
|
+
//state: radio.up === true,
|
|
111
|
+
//interfaces: Object.values(radio.interfaces).map(i => ({
|
|
112
|
+
//name: i.ssid,
|
|
113
|
+
//state: i.up === true,
|
|
114
|
+
//mode: i.mode
|
|
115
|
+
//}))
|
|
116
|
+
//}));
|
|
117
|
+
|
|
118
|
+
//const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
|
|
119
|
+
const ssids = Object.entries(wirelessStatus.values || {}).flatMap(([key, data]) => {
|
|
120
|
+
if (!key.startsWith('wifinet')) return [];
|
|
121
|
+
return [{
|
|
122
|
+
ifname: data['.name'] || key,
|
|
123
|
+
name: data.ssid || null,
|
|
124
|
+
device: data.device || null,
|
|
125
|
+
mode: data.mode || null,
|
|
126
|
+
hidden: data.hidden === '1' || data.hidden === true,
|
|
127
|
+
state: true
|
|
128
|
+
}];
|
|
129
|
+
});
|
|
130
130
|
|
|
131
131
|
openWrtInfo.state = true;
|
|
132
132
|
openWrtInfo.systemInfo = systemInfo;
|
|
133
|
+
//openWrtInfo.networkInfo = networkInfo;
|
|
133
134
|
openWrtInfo.wirelessStatus = wirelessStatus;
|
|
134
|
-
openWrtInfo.wirelessRadios = wirelessRadios;
|
|
135
|
+
//openWrtInfo.wirelessRadios = wirelessRadios;
|
|
135
136
|
openWrtInfo.ssids = ssids;
|
|
136
137
|
|
|
137
|
-
if (this.
|
|
138
|
+
if (this.firstRun) {
|
|
139
|
+
this.emit('success', `Connect success`);
|
|
140
|
+
this.firstRun = false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// emit data
|
|
144
|
+
this.emit('systemInfo', systemInfo);
|
|
145
|
+
//this.emit('networkInfo', networkInfo);
|
|
146
|
+
this.emit('wirelessStatus', wirelessStatus);
|
|
147
|
+
//this.emit('wirelessRadios', wirelessRadios);
|
|
148
|
+
this.emit('ssids', ssids);
|
|
149
|
+
|
|
138
150
|
return openWrtInfo;
|
|
139
151
|
} catch (error) {
|
|
140
|
-
if (this.logError) this.emit(
|
|
141
|
-
return
|
|
152
|
+
if (this.logError) this.emit('error', `Connect error: ${error.message}`);
|
|
153
|
+
return openWrtInfo;
|
|
142
154
|
}
|
|
143
155
|
}
|
|
144
156
|
|
|
@@ -146,29 +158,29 @@ class OpenWrt extends EventEmitter {
|
|
|
146
158
|
switch (type) {
|
|
147
159
|
case 'ssid':
|
|
148
160
|
await this.handleWithLock(async () => {
|
|
149
|
-
if (this.logDebug) this.emit(
|
|
161
|
+
if (this.logDebug) this.emit('debug', `${state ? 'Enabling' : 'Disabling'} SSID ${ssidName}`);
|
|
150
162
|
|
|
151
|
-
const status = await this.ubusCall(
|
|
163
|
+
const status = await this.ubusCall('network.wireless', 'status');
|
|
152
164
|
const iface = await this.functions.findIfaceBySsid(status, ssidName);
|
|
153
165
|
if (!iface) throw new Error(`SSID ${ssidName} not found`);
|
|
154
166
|
|
|
155
167
|
const section = iface.section;
|
|
156
168
|
if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
|
|
157
169
|
|
|
158
|
-
await this.ubusCall(
|
|
170
|
+
await this.ubusCall('uci', 'set',
|
|
159
171
|
{
|
|
160
|
-
config:
|
|
172
|
+
config: 'wireless',
|
|
161
173
|
section: section,
|
|
162
174
|
values: {
|
|
163
|
-
disabled: state ?
|
|
175
|
+
disabled: state ? '0' : '1'
|
|
164
176
|
}
|
|
165
177
|
}
|
|
166
178
|
);
|
|
167
179
|
|
|
168
|
-
await this.ubusCall(
|
|
169
|
-
await this.ubusCall(
|
|
180
|
+
await this.ubusCall('uci', 'commit', { config: 'wireless' });
|
|
181
|
+
await this.ubusCall('network.wireless', 'reload', {});
|
|
170
182
|
|
|
171
|
-
if (this.logDebug) this.emit(
|
|
183
|
+
if (this.logDebug) this.emit('debug', `Send SSID ${ssidName} ${state ? 'enabled' : 'disabled'}`);
|
|
172
184
|
});
|
|
173
185
|
break;
|
|
174
186
|
case 'switch':
|
|
@@ -177,5 +189,4 @@ class OpenWrt extends EventEmitter {
|
|
|
177
189
|
}
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
export default OpenWrt;
|
|
181
|
-
|
|
192
|
+
export default OpenWrt;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class Switch extends EventEmitter {
|
|
5
5
|
constructor(api, config, openWrt, openWrtInfo) {
|
|
6
6
|
super();
|
|
7
7
|
|
|
@@ -12,8 +12,10 @@ class SwitchDevice extends EventEmitter {
|
|
|
12
12
|
AccessoryUUID = api.hap.uuid;
|
|
13
13
|
|
|
14
14
|
//config
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
15
|
+
this.host = config.host;
|
|
16
|
+
this.name = config.accessPoint.name || openWrtInfo.systemInfo.hostname;
|
|
17
|
+
this.namePrefix = config.accessPoint?.namePrefix || false;
|
|
18
|
+
this.sensorsEnabled = config.accessPoint?.sensor || false
|
|
17
19
|
this.logInfo = config.log?.info || false;
|
|
18
20
|
this.logDebug = config.log?.debug || false;
|
|
19
21
|
|
|
@@ -27,6 +29,43 @@ class SwitchDevice extends EventEmitter {
|
|
|
27
29
|
this.openWrt = openWrt;
|
|
28
30
|
this.openWrtInfo = openWrtInfo;
|
|
29
31
|
this.ssids = openWrtInfo.ssids;
|
|
32
|
+
|
|
33
|
+
//openwrt client
|
|
34
|
+
this.openWrt.on('systemInfo', (info) => {
|
|
35
|
+
this.informationService?.updateCharacteristic(Characteristic.FirmwareRevision, info.release?.version);
|
|
36
|
+
})
|
|
37
|
+
.on('networkInfo', async (info) => {
|
|
38
|
+
})
|
|
39
|
+
.on('ports', async (ports) => {
|
|
40
|
+
this.ports = ports;
|
|
41
|
+
|
|
42
|
+
// sensors
|
|
43
|
+
for (let i = 0; i < ports.length; i++) {
|
|
44
|
+
const port = ports[i];
|
|
45
|
+
const name = port.name;
|
|
46
|
+
const state = port.state;
|
|
47
|
+
const serviceName = this.namePrefix ? `${this.name} ${name}` : name;
|
|
48
|
+
this.services?.[i]
|
|
49
|
+
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
50
|
+
.updateCharacteristic(Characteristic.On, state);
|
|
51
|
+
|
|
52
|
+
this.sensorServices?.[i]
|
|
53
|
+
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
54
|
+
.updateCharacteristic(Characteristic.ContactSensorState, !state);
|
|
55
|
+
|
|
56
|
+
if (this.logInfo) {
|
|
57
|
+
this.emit('info', `Name: ${ports.name}`);
|
|
58
|
+
this.emit('info', `State: ${ports.state}`);
|
|
59
|
+
this.emit('info', `Mode: ${ports.mode}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.on('restFul', (path, data) => {
|
|
64
|
+
if (this.restFulConnected) this.restFul1.update(path, data);
|
|
65
|
+
})
|
|
66
|
+
.on('mqtt', (topic, message) => {
|
|
67
|
+
if (this.mqttConnected) this.mqtt1.emit('publish', topic, message);
|
|
68
|
+
});
|
|
30
69
|
};
|
|
31
70
|
|
|
32
71
|
async externalIntegrations() {
|
|
@@ -65,8 +104,8 @@ class SwitchDevice extends EventEmitter {
|
|
|
65
104
|
this.mqtt1 = new Mqtt({
|
|
66
105
|
host: this.mqtt.host,
|
|
67
106
|
port: this.mqtt.port || 1883,
|
|
68
|
-
clientId: this.mqtt.clientId ? `${this.
|
|
69
|
-
prefix: this.mqtt.prefix ? `${this.
|
|
107
|
+
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)}`,
|
|
108
|
+
prefix: this.mqtt.prefix ? `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.mqtt.prefix}/${this.name}` : `${this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt'}/${this.name}`,
|
|
70
109
|
user: this.mqtt.auth?.user,
|
|
71
110
|
passwd: this.mqtt.auth?.passwd,
|
|
72
111
|
logWarn: this.logWarn,
|
|
@@ -121,29 +160,30 @@ class SwitchDevice extends EventEmitter {
|
|
|
121
160
|
//prepare accessory
|
|
122
161
|
if (this.logDebug) this.emit('debug', `prepare accessory`);
|
|
123
162
|
const accessoryName = this.name;
|
|
124
|
-
const accessoryUUID = AccessoryUUID.generate(this.openWrtInfo.systemInfo.
|
|
125
|
-
const accessoryCategory = Categories.
|
|
163
|
+
const accessoryUUID = AccessoryUUID.generate(this.host + this.openWrtInfo.systemInfo.system);
|
|
164
|
+
const accessoryCategory = Categories.AIRPORT;
|
|
126
165
|
const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
|
|
127
166
|
|
|
128
167
|
//prepare information service
|
|
129
168
|
if (this.logDebug) this.emit('debug', `prepare information service`);
|
|
130
169
|
accessory.getService(Service.AccessoryInformation)
|
|
131
|
-
.setCharacteristic(Characteristic.Manufacturer, 'OpenWrt')
|
|
170
|
+
.setCharacteristic(Characteristic.Manufacturer, this.openWrtInfo.systemInfo.release.distribution || 'OpenWrt')
|
|
132
171
|
.setCharacteristic(Characteristic.Model, this.openWrtInfo.systemInfo.model)
|
|
133
172
|
.setCharacteristic(Characteristic.SerialNumber, this.openWrtInfo.systemInfo.system)
|
|
134
|
-
.setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.
|
|
173
|
+
.setCharacteristic(Characteristic.FirmwareRevision, this.openWrtInfo.systemInfo.release?.version)
|
|
135
174
|
.setCharacteristic(Characteristic.ConfiguredName, accessoryName);
|
|
136
175
|
|
|
137
176
|
if (this.logDebug) this.emit('debug', `prepare service`);
|
|
138
177
|
|
|
139
|
-
//
|
|
178
|
+
//services
|
|
140
179
|
this.services = [];
|
|
180
|
+
this.sensorServices = [];
|
|
141
181
|
for (const port of this.ports) {
|
|
142
182
|
const name = port.name;
|
|
143
183
|
if (this.logDebug) this.emit('debug', `prepare port: ${name} service`);
|
|
144
184
|
|
|
145
|
-
const serviceName = this.
|
|
146
|
-
const service = accessory.addService(Service.Switch, serviceName, `service${
|
|
185
|
+
const serviceName = this.namePrefix ? `${accessoryName} ${name}` : name;
|
|
186
|
+
const service = accessory.addService(Service.Switch, serviceName, `service${name}`);
|
|
147
187
|
service.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
148
188
|
service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
149
189
|
service.getCharacteristic(Characteristic.On)
|
|
@@ -163,10 +203,9 @@ class SwitchDevice extends EventEmitter {
|
|
|
163
203
|
});
|
|
164
204
|
this.services.push(service);
|
|
165
205
|
|
|
166
|
-
if (this.
|
|
206
|
+
if (this.sensorsEnabled) {
|
|
167
207
|
if (this.logDebug) this.emit('debug', `prepare port: ${name} sensor service`);
|
|
168
208
|
|
|
169
|
-
this.sensorServices = [];
|
|
170
209
|
const sensorService = accessory.addService(Service.ContactSensor, serviceName, `sensorService${name}`);
|
|
171
210
|
sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
172
211
|
sensorService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
@@ -191,49 +230,15 @@ class SwitchDevice extends EventEmitter {
|
|
|
191
230
|
//start external integrations
|
|
192
231
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
193
232
|
|
|
194
|
-
this.emit('devInfo', `--------
|
|
195
|
-
this.emit('devInfo', `
|
|
196
|
-
this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model}`);
|
|
233
|
+
this.emit('devInfo', `-------- Access Point ${this.name} --------`);
|
|
234
|
+
this.emit('devInfo', `Model: ${this.openWrtInfo.systemInfo.model || this.openWrtInfo.systemInfo.board_name}`);
|
|
197
235
|
this.emit('devInfo', `System: ${this.openWrtInfo.systemInfo.system}`);
|
|
236
|
+
this.emit('devInfo', `Kernel: ${this.openWrtInfo.systemInfo.kernel}`);
|
|
198
237
|
this.emit('devInfo', `Firmware: ${this.openWrtInfo.systemInfo.release?.description}`);
|
|
238
|
+
this.emit('devInfo', `Target: ${this.openWrtInfo.systemInfo.release?.target}`);
|
|
239
|
+
this.emit('devInfo', `Ports: ${this.ports.length}`);
|
|
199
240
|
this.emit('devInfo', `----------------------------------`);
|
|
200
241
|
|
|
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
|
-
});
|
|
236
|
-
|
|
237
242
|
//prepare accessory
|
|
238
243
|
const accessory = await this.prepareAccessory();
|
|
239
244
|
return accessory;
|
|
@@ -242,4 +247,4 @@ class SwitchDevice extends EventEmitter {
|
|
|
242
247
|
}
|
|
243
248
|
}
|
|
244
249
|
};
|
|
245
|
-
export default
|
|
250
|
+
export default Switch;
|