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

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