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

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.
@@ -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.10",
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,37 +1,23 @@
1
1
  import EventEmitter from "events";
2
- import axios from "axios";
3
- import Functions from './functions.js';
2
+ import { exec } from "child_process";
3
+ import Functions from "./functions.js";
4
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
10
  this.host = config.host;
12
- this.user = config.user;
13
- this.passwd = config.passwd;
11
+ this.user = config.auth?.user || "root";
12
+ this.passwd = config.auth?.passwd || "";
13
+
14
14
  this.logError = config.log?.error;
15
15
  this.logDebug = config.log?.debug;
16
16
 
17
- //external integration
18
- this.restFulEnabled = config.restFul?.enable || false;
19
- this.mqttEnabled = config.mqtt?.enable || false;
20
-
21
17
  this.firstRun = true;
22
18
  this.lock = false;
23
19
 
24
- this.sessionId = null;
25
- this.sessionExpiresAt = 0;
26
-
27
20
  this.functions = new Functions();
28
- this.axiosInstance = axios.create({
29
- baseURL: `${config.host}/ubus`,
30
- timeout: 5000,
31
- headers: {
32
- "Content-Type": "application/json"
33
- }
34
- });
35
21
 
36
22
  this.impulseGenerator = new ImpulseGenerator()
37
23
  .on("connect", () => this.handleWithLock(async () => {
@@ -45,69 +31,48 @@ class OpenWrt extends EventEmitter {
45
31
  async handleWithLock(fn) {
46
32
  if (this.lock) return;
47
33
  this.lock = true;
48
-
49
34
  try {
50
35
  await fn();
51
36
  } catch (error) {
52
- this.emit("error", `Impulse generator error: ${error.message}`
53
- );
37
+ this.emit("error", `Impulse generator error: ${error.message}`);
54
38
  } finally {
55
39
  this.lock = false;
56
40
  }
57
41
  }
58
42
 
59
- async login() {
60
- const now = Date.now();
61
- if (this.sessionId && now < this.sessionExpiresAt) {
62
- return this.sessionId;
63
- }
64
-
65
- const response = await this.axiosInstance.post("", {
66
- jsonrpc: "2.0",
67
- id: 1,
68
- method: "call",
69
- params: ["00000000000000000000000000000000", "session", "login", { username: this.user, password: this.passwd }]
43
+ // --- Helper do wywołań SSH z wyłączeniem StrictHostKeyChecking ---
44
+ async sshCall(command) {
45
+ return new Promise((resolve, reject) => {
46
+ const sshCmd = `ssh -o StrictHostKeyChecking=no ${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
+ });
70
56
  });
71
-
72
- const result = response.data?.result?.[1];
73
- if (!result?.ubus_rpc_session) {
74
- throw new Error("ubus login failed");
75
- }
76
-
77
- this.sessionId = result.ubus_rpc_session;
78
- this.sessionExpiresAt = now + 240_000;
79
-
80
- if (this.logDebug) this.emit("debug", `Ubus login OK`);
81
- return this.sessionId;
82
57
  }
83
58
 
59
+ // --- ubus call przez SSH ---
84
60
  async ubusCall(service, method, params = {}) {
85
- const session = await this.login();
86
-
87
- const response = await this.axiosInstance.post("", {
88
- jsonrpc: "2.0",
89
- id: 2,
90
- method: "call",
91
- params: [session, service, method, params]
92
- });
93
-
94
- if (response.data?.error) {
95
- throw new Error(response.data.error.message || "ubus call error");
96
- }
97
-
98
- return response.data.result[1];
61
+ const cmd = `ubus call ${service} ${method} '${JSON.stringify(params)}'`;
62
+ return await this.sshCall(cmd);
99
63
  }
100
64
 
65
+ // --- Pobranie info i statusu ---
101
66
  async connect() {
67
+ const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] };
102
68
  try {
103
- const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
104
69
  const systemInfo = await this.ubusCall("system", "board");
105
- if (this.logDebug) this.emit("debug", `System info data: ${JSON.stringify(systemInfo, null, 2)}`);
70
+ if (this.logDebug) this.emit("debug", `[${this.host}] System info: ${JSON.stringify(systemInfo, null, 2)}`);
106
71
 
107
72
  const wirelessStatus = await this.ubusCall("network.wireless", "status");
108
- if (this.logDebug) this.emit("debug", `Wireless status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
73
+ if (this.logDebug) this.emit("debug", `[${this.host}] Wireless status: ${JSON.stringify(wirelessStatus, null, 2)}`);
109
74
 
110
- const wirelessRadios = Object.values(status.radios).map(radio => ({
75
+ const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
111
76
  name: radio.name,
112
77
  state: radio.up === true,
113
78
  interfaces: Object.values(radio.interfaces).map(i => ({
@@ -118,6 +83,7 @@ class OpenWrt extends EventEmitter {
118
83
  }));
119
84
 
120
85
  const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
86
+
121
87
  this.emit("systemInfo", systemInfo);
122
88
  this.emit("wirelessStatus", wirelessStatus);
123
89
  this.emit("wirelessRadios", wirelessRadios);
@@ -134,19 +100,20 @@ class OpenWrt extends EventEmitter {
134
100
  openWrtInfo.wirelessRadios = wirelessRadios;
135
101
  openWrtInfo.ssids = ssids;
136
102
 
137
- if (this.logDebug) this.emit("debug", `OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
103
+ if (this.logDebug) this.emit("debug", `[${this.host}] OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
138
104
  return openWrtInfo;
139
105
  } catch (error) {
140
- if (this.logError) this.emit("error", `Connect error: ${error.message}`);
141
- return null;
106
+ if (this.logError) this.emit("error", `[${this.host}] Connect error: ${error.message}`);
107
+ return openWrtInfo;
142
108
  }
143
109
  }
144
110
 
111
+ // --- Włączanie / wyłączanie SSID ---
145
112
  async send(type, ssidName, state) {
146
113
  switch (type) {
147
- case 'ssid':
114
+ case "ssid":
148
115
  await this.handleWithLock(async () => {
149
- if (this.logDebug) this.emit("debug", `${state ? "Enabling" : "Disabling"} SSID ${ssidName}`);
116
+ if (this.logDebug) this.emit("debug", `[${this.host}] ${state ? "Enabling" : "Disabling"} SSID ${ssidName}`);
150
117
 
151
118
  const status = await this.ubusCall("network.wireless", "status");
152
119
  const iface = await this.functions.findIfaceBySsid(status, ssidName);
@@ -155,23 +122,18 @@ class OpenWrt extends EventEmitter {
155
122
  const section = iface.section;
156
123
  if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
157
124
 
158
- await this.ubusCall("uci", "set",
159
- {
160
- config: "wireless",
161
- section: section,
162
- values: {
163
- disabled: state ? "0" : "1"
164
- }
165
- }
166
- );
167
-
125
+ await this.ubusCall("uci", "set", {
126
+ config: "wireless",
127
+ section: section,
128
+ values: { disabled: state ? "0" : "1" }
129
+ });
168
130
  await this.ubusCall("uci", "commit", { config: "wireless" });
169
131
  await this.ubusCall("network.wireless", "reload", {});
170
132
 
171
- if (this.logDebug) this.emit("debug", `Send SSID ${ssidName} ${state ? "enabled" : "disabled"}`);
133
+ if (this.logDebug) this.emit("debug", `[${this.host}] SSID ${ssidName} ${state ? "enabled" : "disabled"}`);
172
134
  });
173
135
  break;
174
- case 'switch':
136
+ case "switch":
175
137
  break;
176
138
  }
177
139
  }
@@ -179,3 +141,4 @@ class OpenWrt extends EventEmitter {
179
141
 
180
142
  export default OpenWrt;
181
143
 
144
+