homebridge-openwrt-control 0.0.2 → 0.0.3-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/package.json +1 -1
- package/src/accesspoint.js +19 -15
- package/src/functions.js +0 -11
- package/src/openwrt.js +89 -55
package/README.md
CHANGED
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
## About The Plugin
|
|
29
29
|
|
|
30
30
|
* Access Points:
|
|
31
|
-
* Control `ON/OFF`
|
|
32
|
-
*
|
|
33
|
-
* Siri can be used to switch ON/OFF SSIDs
|
|
31
|
+
* Control `ON/OFF` all exposed SSID.
|
|
32
|
+
* Contact sensor monitor for all `SSIDs`.
|
|
33
|
+
* Siri can be used to switch ON/OFF SSIDs.
|
|
34
34
|
* Home automations and shortcuts can be used for all functions.
|
|
35
35
|
|
|
36
36
|
## Configuration
|
|
@@ -52,11 +52,16 @@
|
|
|
52
52
|
| `auth.enable` | If enabled, authorizatins credentials will be used for login. |
|
|
53
53
|
| `auth.user` | Here set the authorization `Username`. |
|
|
54
54
|
| `auth.passwd` | Here set the authorization `Password`. |
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
55
|
+
| `apDevice{}` | Access Point. |
|
|
56
|
+
| `apDevice.enable` | Here enable access point `SSIDs` control. |
|
|
57
|
+
| `apDevice.name` | Here set Your own access point name or leave empty. |
|
|
58
|
+
| `apDevice.namePrefix` | Here enable accessory name as a prefix for access point name. |
|
|
59
|
+
| `apDevice.sensor` | Here enable access point `SSIDs` sensors. |
|
|
60
|
+
| `swDevice{}` | Switch object. |
|
|
61
|
+
| `swDevice.enable` | Here enable switch `Ports` control. |
|
|
62
|
+
| `swDevice.name` | Here set Your own name or for switch leave empty. |
|
|
63
|
+
| `swDevice.namePrefix` | Here enable accessory name as a prefix for switch name. |
|
|
64
|
+
| `swDevice.sensor` | Here enable switch `Ports` sensors. |
|
|
60
65
|
| `refreshInterval` | Here set the data refresh time in seconds. |
|
|
61
66
|
| `log.deviceInfo` | If enabled, log device info will be displayed by every connections device to the network. |
|
|
62
67
|
| `log.success` | If enabled, success log will be displayed in console. |
|
package/package.json
CHANGED
package/src/accesspoint.js
CHANGED
|
@@ -40,10 +40,11 @@ class AccessPoint extends EventEmitter {
|
|
|
40
40
|
// update state
|
|
41
41
|
const ssids = openWrtInfo.ssids;
|
|
42
42
|
for (let i = 0; i < ssids.length; i++) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
43
|
+
const radio = ssids[i].device;
|
|
44
|
+
const frequency = ssids[i].frequency;
|
|
45
|
+
const ssid = ssids[i].name;
|
|
46
|
+
const state = ssids[i].disabled;
|
|
47
|
+
const serviceName = this.namePrefix ? `${this.name} ${ssid}` : ssid;
|
|
47
48
|
this.services?.[i]
|
|
48
49
|
?.setCharacteristic(Characteristic.ConfiguredName, serviceName)
|
|
49
50
|
.updateCharacteristic(Characteristic.On, state);
|
|
@@ -53,8 +54,10 @@ class AccessPoint extends EventEmitter {
|
|
|
53
54
|
.updateCharacteristic(Characteristic.ContactSensorState, state);
|
|
54
55
|
|
|
55
56
|
if (this.logInfo) {
|
|
56
|
-
this.emit('info', `
|
|
57
|
-
this.emit('info', `
|
|
57
|
+
this.emit('info', `Radio: ${radio}`);
|
|
58
|
+
this.emit('info', `Frequency: ${frequency}`);
|
|
59
|
+
this.emit('info', `Name: ${ssid}`);
|
|
60
|
+
this.emit('info', `State: ${state}`);
|
|
58
61
|
this.emit('info', `Mode: ${ssid.mode}`);
|
|
59
62
|
}
|
|
60
63
|
}
|
|
@@ -175,33 +178,34 @@ class AccessPoint extends EventEmitter {
|
|
|
175
178
|
this.services = [];
|
|
176
179
|
this.sensorServices = [];
|
|
177
180
|
for (const ssid of this.openWrtInfo.ssids) {
|
|
181
|
+
const radio = ssid.device; //radio name
|
|
182
|
+
const frequency = ssid.frequency; //frequency
|
|
178
183
|
const name = ssid.name;
|
|
179
184
|
if (this.logDebug) this.emit('debug', `prepare ssid: ${name} service`);
|
|
180
185
|
|
|
181
|
-
const serviceName = this.namePrefix ? `${accessoryName} ${name}` : name
|
|
182
|
-
const service = accessory.addService(Service.Switch, serviceName, `service${name}`);
|
|
186
|
+
const serviceName = this.namePrefix ? `${accessoryName} ${name} ${frequency}` : `${name} ${frequency}`;
|
|
187
|
+
const service = accessory.addService(Service.Switch, serviceName, `service${name}${radio}`);
|
|
183
188
|
service.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
184
189
|
service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
185
190
|
service.getCharacteristic(Characteristic.On)
|
|
186
191
|
.onGet(async () => {
|
|
187
|
-
const state = ssid.
|
|
188
|
-
if (this.logInfo) this.emit('message', `SSID: ${name}, state: ${state ? 'Enabled' : 'Disabled'}`);
|
|
192
|
+
const state = ssid.disabled;
|
|
193
|
+
if (this.logInfo) this.emit('message', `SSID: ${name}, radio: ${radio}, state: ${state ? 'Enabled' : 'Disabled'}`);
|
|
189
194
|
return state;
|
|
190
195
|
})
|
|
191
196
|
.onSet(async (state) => {
|
|
192
197
|
try {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (this.logInfo) this.emit('message', `SSID: ${name}, set State: ${state ? 'Enabled' : 'Disabled'}`);
|
|
198
|
+
await this.openWrt.send('apDevice', radio, name, state);
|
|
199
|
+
if (this.logInfo) this.emit('message', `SSID: ${name}, radio: ${radio}, set State: ${state ? 'Enabled' : 'Disabled'}`);
|
|
196
200
|
} catch (error) {
|
|
197
|
-
this.emit('warn', `SSID: ${name}, set state error: ${error}`);
|
|
201
|
+
this.emit('warn', `SSID: ${name}, radio: ${radio}, set state error: ${error}`);
|
|
198
202
|
}
|
|
199
203
|
});
|
|
200
204
|
this.services.push(service);
|
|
201
205
|
|
|
202
206
|
if (this.sensorsEnabled) {
|
|
203
207
|
if (this.logDebug) this.emit('debug', `prepare ssid: ${name} sensor service`);
|
|
204
|
-
const sensorService = accessory.addService(Service.ContactSensor, serviceName, `sensorService${name}`);
|
|
208
|
+
const sensorService = accessory.addService(Service.ContactSensor, serviceName, `sensorService${name}${radio}`);
|
|
205
209
|
sensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
206
210
|
sensorService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
207
211
|
sensorService.getCharacteristic(Characteristic.ContactSensorState)
|
package/src/functions.js
CHANGED
|
@@ -42,16 +42,5 @@ class Functions {
|
|
|
42
42
|
throw wrappedError;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
async findIfaceBySsid(status, targetSsid) {
|
|
47
|
-
for (const radio of Object.values(status.radios)) {
|
|
48
|
-
for (const iface of Object.values(radio.interfaces)) {
|
|
49
|
-
if (iface.ssid === targetSsid) {
|
|
50
|
-
return iface;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
45
|
}
|
|
57
46
|
export default Functions
|
package/src/openwrt.js
CHANGED
|
@@ -92,54 +92,84 @@ class OpenWrt extends EventEmitter {
|
|
|
92
92
|
|
|
93
93
|
async connect() {
|
|
94
94
|
try {
|
|
95
|
-
const openWrtInfo = { state: false, info: '', systemInfo: {}, networkInfo: {}, wirelessStatus: {}, wirelessRadios: [],
|
|
95
|
+
const openWrtInfo = { state: false, info: '', systemInfo: {}, networkInfo: {}, wirelessStatus: {}, wirelessRadios: [], wirelessSsids: [] };
|
|
96
|
+
|
|
97
|
+
/* ======================
|
|
98
|
+
* System information
|
|
99
|
+
* ====================== */
|
|
96
100
|
const systemInfo = await this.ubusCall('system', 'board');
|
|
97
101
|
if (this.logDebug) this.emit('debug', `System info data: ${JSON.stringify(systemInfo, null, 2)}`);
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//const wirelessStatus = await this.ubusCall('network.wireless', 'status');
|
|
103
|
+
/* ======================
|
|
104
|
+
* Wireless configuration (UCI)
|
|
105
|
+
* ====================== */
|
|
104
106
|
const wirelessStatus = await this.ubusCall('uci', 'get', { config: 'wireless' });
|
|
105
107
|
if (this.logDebug) this.emit('debug', `Wireless status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
/* ======================
|
|
110
|
+
* Build radio frequency map
|
|
111
|
+
* ====================== */
|
|
112
|
+
const radioFreqMap = Object.entries(wirelessStatus.values || {})
|
|
113
|
+
.filter(([, data]) => data['.type'] === 'wifi-device')
|
|
114
|
+
.reduce((map, [key, data]) => {
|
|
115
|
+
let freq = null;
|
|
116
|
+
if (data.channel) {
|
|
117
|
+
const ch = parseInt(data.channel, 10);
|
|
118
|
+
freq = ch <= 14 ? '2.4GHz' : '5GHz';
|
|
119
|
+
}
|
|
120
|
+
map[data['.name']] = freq;
|
|
121
|
+
return map;
|
|
122
|
+
}, {});
|
|
123
|
+
|
|
124
|
+
/* ======================
|
|
125
|
+
* SSID list with state + radioName + frequency
|
|
126
|
+
* ====================== */
|
|
127
|
+
const ifaceEntries = Object.entries(wirelessStatus.values || {}).filter(([, data]) => data['.type'] === 'wifi-iface');
|
|
128
|
+
const ssids = await Promise.all(
|
|
129
|
+
ifaceEntries.map(async ([key, data]) => {
|
|
130
|
+
const ifaceName = data['.name'] || key;
|
|
131
|
+
const radioName = data.device || null;
|
|
132
|
+
const frequency = radioFreqMap[radioName] || null;
|
|
133
|
+
|
|
134
|
+
let state = false;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const ifaceConfig = await this.ubusCall('uci', 'get', { config: 'wireless', section: ifaceName });
|
|
138
|
+
const values = ifaceConfig?.values || {};
|
|
139
|
+
state = values.disabled === '1';
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (this.logError) this.emit('error', `UCI get failed for ${ifaceName}: ${error.message}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
ifname: ifaceName,
|
|
146
|
+
device: radioName,
|
|
147
|
+
frequency, // 2.4GHz / 5GHz
|
|
148
|
+
ssid: data.ssid || null,
|
|
149
|
+
mode: data.mode || null,
|
|
150
|
+
hidden: data.hidden === '1' || data.hidden === true,
|
|
151
|
+
disabled: state
|
|
152
|
+
};
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
|
|
129
156
|
if (ssids.length === 0) {
|
|
130
157
|
openWrtInfo.info = 'SSIDs not found';
|
|
131
158
|
return openWrtInfo;
|
|
132
159
|
}
|
|
133
160
|
|
|
161
|
+
/* ======================
|
|
162
|
+
* Final object
|
|
163
|
+
* ====================== */
|
|
134
164
|
openWrtInfo.state = true;
|
|
135
|
-
openWrtInfo.info =
|
|
165
|
+
openWrtInfo.info = 'Connect Success';
|
|
136
166
|
openWrtInfo.systemInfo = systemInfo;
|
|
137
|
-
//openWrtInfo.networkInfo = networkInfo;
|
|
138
167
|
openWrtInfo.wirelessStatus = wirelessStatus;
|
|
139
|
-
|
|
140
|
-
openWrtInfo.ssids = ssids;
|
|
168
|
+
openWrtInfo.wirelessSsids = ssids;
|
|
141
169
|
|
|
142
|
-
|
|
170
|
+
/* ======================
|
|
171
|
+
* Emit & return
|
|
172
|
+
* ====================== */
|
|
143
173
|
this.emit('openWrtInfo', openWrtInfo);
|
|
144
174
|
|
|
145
175
|
return openWrtInfo;
|
|
@@ -148,39 +178,43 @@ class OpenWrt extends EventEmitter {
|
|
|
148
178
|
}
|
|
149
179
|
}
|
|
150
180
|
|
|
151
|
-
async send(type,
|
|
181
|
+
async send(type, radio, ssid, state) {
|
|
152
182
|
switch (type) {
|
|
153
|
-
case '
|
|
183
|
+
case 'apDevice':
|
|
154
184
|
await this.handleWithLock(async () => {
|
|
155
|
-
if (this.logDebug) this.emit('debug', `${state ? 'Enabling' : 'Disabling'} SSID ${
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
185
|
+
if (this.logDebug) this.emit('debug', `${state ? 'Enabling' : 'Disabling'} SSID ${ssid} on ${radio}`);
|
|
186
|
+
|
|
187
|
+
// Pobranie konfiguracji wireless z UCI
|
|
188
|
+
const wirelessConfig = await this.ubusCall('uci', 'get', { config: 'wireless' });
|
|
189
|
+
|
|
190
|
+
// Wybór wszystkich pasujących iface po ssid + radio
|
|
191
|
+
const ifaceEntries = Object.entries(wirelessConfig.values || {}).filter(([, data]) => data['.type'] === 'wifi-iface' && data.ssid === ssid && data.device === radio);
|
|
192
|
+
if (ifaceEntries.length === 0) {
|
|
193
|
+
throw new Error(`SSID ${ssid} not found on radio ${radio}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const [, ifaceData] of ifaceEntries) {
|
|
197
|
+
const section = ifaceData['.name'];
|
|
198
|
+
|
|
199
|
+
if (!section) {
|
|
200
|
+
if (this.logWarn) this.emit('warn', `Skipping SSID ${ssid} on ${radio} (missing section)`);
|
|
201
|
+
continue;
|
|
171
202
|
}
|
|
172
|
-
);
|
|
173
203
|
|
|
204
|
+
await this.ubusCall('uci', 'set', { config: 'wireless', section, values: { disabled: state ? '0' : '1' } });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Commit + reload
|
|
174
208
|
await this.ubusCall('uci', 'commit', { config: 'wireless' });
|
|
175
209
|
await this.ubusCall('network.wireless', 'reload', {});
|
|
176
|
-
|
|
177
|
-
if (this.logDebug) this.emit('debug', `Send SSID ${ssidName} ${state ? 'enabled' : 'disabled'}`);
|
|
210
|
+
if (this.logDebug) this.emit('debug', `Send SSID ${ssid} on ${radio} ${state ? 'enabled' : 'disabled'}`);
|
|
178
211
|
});
|
|
179
212
|
break;
|
|
180
|
-
case '
|
|
213
|
+
case 'swDevice':
|
|
181
214
|
break;
|
|
182
215
|
}
|
|
183
216
|
}
|
|
217
|
+
|
|
184
218
|
}
|
|
185
219
|
|
|
186
220
|
export default OpenWrt;
|