homebridge-openwrt-control 0.0.2-beta.1 → 0.0.2-beta.12

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.
@@ -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",
@@ -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[0]].accessPoint.enable === true;"
114
+ "functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
115
115
  }
116
116
  },
117
117
  "namePrefix": {
@@ -119,13 +119,13 @@
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[0]].accessPoint.enable === true;"
122
+ "functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
123
123
  }
124
124
  },
125
125
  "sensor": {
126
- "title": "Enable",
126
+ "title": "Sensor",
127
127
  "type": "boolean",
128
- "description": "Here Enable/Disable sensor for every ssid.",
128
+ "description": "Here Enable/Disable sensor for every SSID.",
129
129
  "condition": {
130
130
  "functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
131
131
  }
@@ -375,7 +375,7 @@
375
375
  "devices[].accessPoint.enable",
376
376
  "devices[].accessPoint.name",
377
377
  "devices[].accessPoint.namePrefix",
378
- "devices[].accessPoint.sensors"
378
+ "devices[].accessPoint.sensor"
379
379
  ]
380
380
  },
381
381
  {
package/index.js CHANGED
@@ -26,34 +26,34 @@ class OpenWrtPlatform {
26
26
  }
27
27
 
28
28
  api.on('didFinishLaunching', async () => {
29
- for (const config of config.devices) {
30
- const { name, host, port, displayType } = config;
31
- if (!name || !host || !port || !displayType) {
32
- log.warn(`Device: ${host || 'host missing'}, ${name || 'name missing'}, ${port || 'port missing'}${!displayType ? ', disply type disabled' : ''} in config, will not be published in the Home app`);
29
+ for (const deviceConfig of config.devices) {
30
+ const { name, host, displayType } = deviceConfig;
31
+ 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`);
33
33
  continue;
34
34
  }
35
35
 
36
36
  //log config
37
37
  const logLevel = {
38
- devInfo: config.log?.deviceInfo,
39
- success: config.log?.success,
40
- info: config.log?.info,
41
- warn: config.log?.warn,
42
- error: config.log?.error,
43
- debug: config.log?.debug
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
44
44
  };
45
45
 
46
46
  if (logLevel.debug) {
47
47
  log.info(`Device: ${host} ${name}, did finish launching.`);
48
48
  const safeConfig = {
49
- ...config,
49
+ ...deviceConfig,
50
50
  auth: {
51
- ...config.auth,
51
+ ...deviceConfig.auth,
52
52
  passwd: 'removed',
53
53
  },
54
54
  mqtt: {
55
55
  auth: {
56
- ...config.mqtt?.auth,
56
+ ...deviceConfig.mqtt?.auth,
57
57
  passwd: 'removed',
58
58
  }
59
59
  },
@@ -61,13 +61,13 @@ class OpenWrtPlatform {
61
61
  log.info(`Device: ${host} ${name}, Config: ${JSON.stringify(safeConfig, null, 2)}`);
62
62
  }
63
63
 
64
- const refreshInterval = (config.refreshInterval ?? 5) * 1000;
65
- if (config.accessPoint?.enable) this.devices.push('accessPoint');
66
- if (config.switch?.enable) this.devices.push('switch');
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
67
 
68
68
  if (this.devices.length === 0) return;
69
69
 
70
- const openWrt = new OpenWrt(config)
70
+ const openWrt = new OpenWrt(deviceConfig)
71
71
  .on('success', msg => logLevel.success && log.success(`Device: ${host}, ${msg}`))
72
72
  .on('info', msg => log.info(`Device: ${host}, ${msg}`))
73
73
  .on('debug', msg => log.info(`Device: ${host}, debug: ${msg}`))
@@ -90,10 +90,10 @@ class OpenWrtPlatform {
90
90
  // create device instance
91
91
  let type;
92
92
  switch (device) {
93
- case 'accessPoint': type = new AccessPoint(api, config, openWrt, openWrtInfo); break;
94
- case 'switch': type = new Switch(api, config, openWrt, openWrtInfo); break;
93
+ case 'accessPoint': type = new AccessPoint(api, deviceConfig, openWrt, openWrtInfo); break;
94
+ case 'switch': type = new Switch(api, deviceConfig, openWrt, openWrtInfo); break;
95
95
  default:
96
- if (logLevel.warn) log.warn(`Device: ${host} ${name}, unknown zone: ${zoneControl}`);
96
+ if (logLevel.warn) log.warn(`Device: ${host} ${name}, unknown device: ${device}`);
97
97
  return;
98
98
  }
99
99
 
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.1",
4
+ "version": "0.0.2-beta.12",
5
5
  "description": "Homebridge plugin to control OpenWrt flashed devices.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/functions.js CHANGED
@@ -1,6 +1,4 @@
1
1
  import { promises as fsPromises } from 'fs';
2
- import { DiacriticsMap } from './constants.js';
3
-
4
2
  class Functions {
5
3
  constructor() {
6
4
  }
@@ -45,28 +43,6 @@ class Functions {
45
43
  }
46
44
  }
47
45
 
48
- async sanitizeString(str) {
49
- if (!str) return '';
50
-
51
- // Replace diacritics using map
52
- str = str.replace(/[^\u0000-\u007E]/g, ch => DiacriticsMap[ch] || ch);
53
-
54
- // Replace separators between words with space
55
- str = str.replace(/(\w)[.:;+\-\/]+(\w)/g, '$1 $2');
56
-
57
- // Replace remaining standalone separators with space
58
- str = str.replace(/[.:;+\-\/]/g, ' ');
59
-
60
- // Remove remaining invalid characters (keep letters, digits, space, apostrophe)
61
- str = str.replace(/[^A-Za-z0-9 ']/g, ' ');
62
-
63
- // Collapse multiple spaces
64
- str = str.replace(/\s+/g, ' ');
65
-
66
- // Trim
67
- return str.trim();
68
- }
69
-
70
46
  async findIfaceBySsid(status, targetSsid) {
71
47
  for (const radio of Object.values(status.radios)) {
72
48
  for (const iface of Object.values(radio.interfaces)) {
package/src/openwrt.js CHANGED
@@ -1,16 +1,14 @@
1
- import EventEmitter from "events";
2
- import axios from "axios";
1
+ import EventEmitter from 'events';
2
+ import axios from 'axios';
3
3
  import Functions from './functions.js';
4
- import ImpulseGenerator from "./impulsegenerator.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.name = config.name;
11
- this.host = config.host;
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,19 +24,20 @@ class OpenWrt extends EventEmitter {
26
24
 
27
25
  this.functions = new Functions();
28
26
  this.axiosInstance = axios.create({
29
- baseURL: `${config.host}/ubus`,
27
+ baseURL: `http://${config.host}/ubus`,
30
28
  timeout: 5000,
31
29
  headers: {
32
- "Content-Type": "application/json"
30
+ "Content-Type": "application/json",
31
+ "Accept": "application/json"
33
32
  }
34
33
  });
35
34
 
36
35
  this.impulseGenerator = new ImpulseGenerator()
37
- .on("connect", () => this.handleWithLock(async () => {
36
+ .on('connect', () => this.handleWithLock(async () => {
38
37
  await this.connect();
39
38
  }))
40
- .on("state", (state) => {
41
- this.emit(state ? "success" : "warn", `Impulse generator ${state ? "started" : "stopped"}`);
39
+ .on('state', (state) => {
40
+ this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
42
41
  });
43
42
  }
44
43
 
@@ -49,7 +48,7 @@ class OpenWrt extends EventEmitter {
49
48
  try {
50
49
  await fn();
51
50
  } catch (error) {
52
- this.emit("error", `Impulse generator error: ${error.message}`
51
+ this.emit('error', `Impulse generator error: ${error.message}`
53
52
  );
54
53
  } finally {
55
54
  this.lock = false;
@@ -62,52 +61,53 @@ class OpenWrt extends EventEmitter {
62
61
  return this.sessionId;
63
62
  }
64
63
 
65
- const response = await this.axiosInstance.post("", {
66
- jsonrpc: "2.0",
64
+ const response = await this.axiosInstance.post('', {
65
+ jsonrpc: '2.0',
67
66
  id: 1,
68
- method: "call",
69
- params: ["00000000000000000000000000000000", "session", "login", { username: this.user, password: this.passwd }]
67
+ method: 'call',
68
+ params: ['00000000000000000000000000000000', 'session', 'login', { username: this.user, password: this.passwd }]
70
69
  });
71
70
 
72
71
  const result = response.data?.result?.[1];
73
72
  if (!result?.ubus_rpc_session) {
74
- throw new Error("ubus login failed");
73
+ throw new Error('ubus login failed');
75
74
  }
76
75
 
77
76
  this.sessionId = result.ubus_rpc_session;
78
77
  this.sessionExpiresAt = now + 240_000;
79
78
 
80
- if (this.logDebug) this.emit("debug", `Ubus login OK`);
79
+ if (this.logDebug) this.emit('debug', `Ubus login OK`);
81
80
  return this.sessionId;
82
81
  }
83
82
 
84
83
  async ubusCall(service, method, params = {}) {
85
84
  const session = await this.login();
86
85
 
87
- const response = await this.axiosInstance.post("", {
88
- jsonrpc: "2.0",
86
+ const response = await this.axiosInstance.post('', {
87
+ jsonrpc: '2.0',
89
88
  id: 2,
90
- method: "call",
89
+ method: 'call',
91
90
  params: [session, service, method, params]
92
91
  });
93
92
 
94
93
  if (response.data?.error) {
95
- throw new Error(response.data.error.message || "ubus call error");
94
+ throw new Error(response.data.error.message || 'ubus call error');
96
95
  }
97
96
 
98
97
  return response.data.result[1];
99
98
  }
100
99
 
101
100
  async connect() {
101
+ const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
102
+
102
103
  try {
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)}`);
104
+ const systemInfo = await this.ubusCall('system', 'board');
105
+ if (this.logDebug) this.emit('debug', `System info data: ${JSON.stringify(systemInfo, null, 2)}`);
106
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)}`);
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
109
 
110
- const wirelessRadios = Object.values(status.radios).map(radio => ({
110
+ const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
111
111
  name: radio.name,
112
112
  state: radio.up === true,
113
113
  interfaces: Object.values(radio.interfaces).map(i => ({
@@ -118,13 +118,13 @@ class OpenWrt extends EventEmitter {
118
118
  }));
119
119
 
120
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);
121
+ this.emit('systemInfo', systemInfo);
122
+ this.emit('wirelessStatus', wirelessStatus);
123
+ this.emit('wirelessRadios', wirelessRadios);
124
+ this.emit('ssids', ssids);
125
125
 
126
126
  if (this.firstRun) {
127
- this.emit("success", `Connect success`);
127
+ this.emit('success', `Connect success`);
128
128
  this.firstRun = false;
129
129
  }
130
130
 
@@ -134,11 +134,11 @@ class OpenWrt extends EventEmitter {
134
134
  openWrtInfo.wirelessRadios = wirelessRadios;
135
135
  openWrtInfo.ssids = ssids;
136
136
 
137
- if (this.logDebug) this.emit("debug", `OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
137
+ if (this.logDebug) this.emit('debug', `OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
138
138
  return openWrtInfo;
139
139
  } catch (error) {
140
- if (this.logError) this.emit("error", `Connect error: ${error.message}`);
141
- return null;
140
+ if (this.logError) this.emit('error', `Connect error: ${error.message}`);
141
+ return openWrtInfo;
142
142
  }
143
143
  }
144
144
 
@@ -146,29 +146,29 @@ class OpenWrt extends EventEmitter {
146
146
  switch (type) {
147
147
  case 'ssid':
148
148
  await this.handleWithLock(async () => {
149
- if (this.logDebug) this.emit("debug", `${state ? "Enabling" : "Disabling"} SSID ${ssidName}`);
149
+ if (this.logDebug) this.emit('debug', `${state ? 'Enabling' : 'Disabling'} SSID ${ssidName}`);
150
150
 
151
- const status = await this.ubusCall("network.wireless", "status");
151
+ const status = await this.ubusCall('network.wireless', 'status');
152
152
  const iface = await this.functions.findIfaceBySsid(status, ssidName);
153
153
  if (!iface) throw new Error(`SSID ${ssidName} not found`);
154
154
 
155
155
  const section = iface.section;
156
156
  if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
157
157
 
158
- await this.ubusCall("uci", "set",
158
+ await this.ubusCall('uci', 'set',
159
159
  {
160
- config: "wireless",
160
+ config: 'wireless',
161
161
  section: section,
162
162
  values: {
163
- disabled: state ? "0" : "1"
163
+ disabled: state ? '0' : '1'
164
164
  }
165
165
  }
166
166
  );
167
167
 
168
- await this.ubusCall("uci", "commit", { config: "wireless" });
169
- await this.ubusCall("network.wireless", "reload", {});
168
+ await this.ubusCall('uci', 'commit', { config: 'wireless' });
169
+ await this.ubusCall('network.wireless', 'reload', {});
170
170
 
171
- if (this.logDebug) this.emit("debug", `Send SSID ${ssidName} ${state ? "enabled" : "disabled"}`);
171
+ if (this.logDebug) this.emit('debug', `Send SSID ${ssidName} ${state ? 'enabled' : 'disabled'}`);
172
172
  });
173
173
  break;
174
174
  case 'switch':