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