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 CHANGED
@@ -28,9 +28,9 @@
28
28
  ## About The Plugin
29
29
 
30
30
  * Access Points:
31
- * Control `ON/OFF` every SSID.
32
- * Monitor with Sensors for every `SSIDs`.
33
- * Siri can be used to switch ON/OFF SSIDs, Policy, Ports.
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
- | `accessPoint{}` | Volume object. |
56
- | `accessPoint.enable` | Here enable access point `SSIDs` control. |
57
- | `accessPoint.name` | Here set Your own volume/mute control name or leave empty. |
58
- | `accessPoint.namePrefix` | Here enable accessory name as a prefix for volume control name. |
59
- | `accessPoint.sensor` | Here enable access point `SSIDs` sensors. |
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "OpenWrt Control",
3
3
  "name": "homebridge-openwrt-control",
4
- "version": "0.0.2",
4
+ "version": "0.0.3-beta.0",
5
5
  "description": "Homebridge plugin to control OpenWrt flashed devices.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -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 ssid = ssids[i];
44
- const name = ssid.name;
45
- const state = ssid.state;
46
- const serviceName = this.namePrefix ? `${this.name} ${name}` : name;
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', `Name: ${ssid.name}`);
57
- this.emit('info', `State: ${ssid.state}`);
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.state;
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
- state = state ? true : false;
194
- await this.openWrt.send('ssid', name, state);
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: [], ssids: [] }
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
- //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');
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
- //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
- });
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 = `Connect Success`;
165
+ openWrtInfo.info = 'Connect Success';
136
166
  openWrtInfo.systemInfo = systemInfo;
137
- //openWrtInfo.networkInfo = networkInfo;
138
167
  openWrtInfo.wirelessStatus = wirelessStatus;
139
- //openWrtInfo.wirelessRadios = wirelessRadios;
140
- openWrtInfo.ssids = ssids;
168
+ openWrtInfo.wirelessSsids = ssids;
141
169
 
142
- // emit data
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, ssidName, state) {
181
+ async send(type, radio, ssid, state) {
152
182
  switch (type) {
153
- case 'ssid':
183
+ case 'apDevice':
154
184
  await this.handleWithLock(async () => {
155
- if (this.logDebug) this.emit('debug', `${state ? 'Enabling' : 'Disabling'} SSID ${ssidName}`);
156
-
157
- const status = await this.ubusCall('network.wireless', 'status');
158
- const iface = await this.functions.findIfaceBySsid(status, ssidName);
159
- if (!iface) throw new Error(`SSID ${ssidName} not found`);
160
-
161
- const section = iface.section;
162
- if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
163
-
164
- await this.ubusCall('uci', 'set',
165
- {
166
- config: 'wireless',
167
- section: section,
168
- values: {
169
- disabled: state ? '0' : '1'
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 'switch':
213
+ case 'swDevice':
181
214
  break;
182
215
  }
183
216
  }
217
+
184
218
  }
185
219
 
186
220
  export default OpenWrt;