homebridge-openwrt-control 0.0.2-beta.7 → 0.0.2-beta.9
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/package.json +1 -1
- package/src/openwrt.js +72 -77
package/package.json
CHANGED
package/src/openwrt.js
CHANGED
|
@@ -1,114 +1,78 @@
|
|
|
1
|
-
import EventEmitter from
|
|
2
|
-
import
|
|
3
|
-
import Functions from
|
|
4
|
-
import ImpulseGenerator from
|
|
1
|
+
import EventEmitter from "events";
|
|
2
|
+
import { exec } from "child_process";
|
|
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.name = config.name;
|
|
11
10
|
this.host = config.host;
|
|
12
|
-
this.user = config.auth
|
|
13
|
-
this.passwd = config.auth
|
|
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: `http://${config.host}/ubus`,
|
|
30
|
-
timeout: 5000,
|
|
31
|
-
headers: {
|
|
32
|
-
'Content-Type': 'application/json'
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
21
|
|
|
36
22
|
this.impulseGenerator = new ImpulseGenerator()
|
|
37
|
-
.on(
|
|
23
|
+
.on("connect", () => this.handleWithLock(async () => {
|
|
38
24
|
await this.connect();
|
|
39
25
|
}))
|
|
40
|
-
.on(
|
|
41
|
-
this.emit(state ?
|
|
26
|
+
.on("state", (state) => {
|
|
27
|
+
this.emit(state ? "success" : "warn", `Impulse generator ${state ? "started" : "stopped"}`);
|
|
42
28
|
});
|
|
43
29
|
}
|
|
44
30
|
|
|
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(
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
// --- Helper do wywołań SSH ---
|
|
44
|
+
async sshCall(command) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const sshCmd = `ssh ${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
|
|
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() {
|
|
102
|
-
const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
|
|
103
|
-
|
|
67
|
+
const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] };
|
|
104
68
|
try {
|
|
105
|
-
const systemInfo = await this.ubusCall(
|
|
106
|
-
if (this.logDebug) this.emit(
|
|
69
|
+
const systemInfo = await this.ubusCall("system", "board");
|
|
70
|
+
if (this.logDebug) this.emit("debug", `[${this.host}] System info: ${JSON.stringify(systemInfo, null, 2)}`);
|
|
107
71
|
|
|
108
|
-
const wirelessStatus = await this.ubusCall(
|
|
109
|
-
if (this.logDebug) this.emit(
|
|
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)}`);
|
|
110
74
|
|
|
111
|
-
const wirelessRadios = Object.values(
|
|
75
|
+
const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
|
|
112
76
|
name: radio.name,
|
|
113
77
|
state: radio.up === true,
|
|
114
78
|
interfaces: Object.values(radio.interfaces).map(i => ({
|
|
@@ -119,13 +83,14 @@ class OpenWrt extends EventEmitter {
|
|
|
119
83
|
}));
|
|
120
84
|
|
|
121
85
|
const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
|
|
122
|
-
|
|
123
|
-
this.emit(
|
|
124
|
-
this.emit(
|
|
125
|
-
this.emit(
|
|
86
|
+
|
|
87
|
+
this.emit("systemInfo", systemInfo);
|
|
88
|
+
this.emit("wirelessStatus", wirelessStatus);
|
|
89
|
+
this.emit("wirelessRadios", wirelessRadios);
|
|
90
|
+
this.emit("ssids", ssids);
|
|
126
91
|
|
|
127
92
|
if (this.firstRun) {
|
|
128
|
-
this.emit(
|
|
93
|
+
this.emit("success", `Connect success`);
|
|
129
94
|
this.firstRun = false;
|
|
130
95
|
}
|
|
131
96
|
|
|
@@ -135,14 +100,44 @@ class OpenWrt extends EventEmitter {
|
|
|
135
100
|
openWrtInfo.wirelessRadios = wirelessRadios;
|
|
136
101
|
openWrtInfo.ssids = ssids;
|
|
137
102
|
|
|
138
|
-
if (this.logDebug) this.emit(
|
|
103
|
+
if (this.logDebug) this.emit("debug", `[${this.host}] OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
|
|
139
104
|
return openWrtInfo;
|
|
140
105
|
} catch (error) {
|
|
141
|
-
if (this.logError) this.emit(
|
|
106
|
+
if (this.logError) this.emit("error", `[${this.host}] Connect error: ${error.message}`);
|
|
142
107
|
return openWrtInfo;
|
|
143
108
|
}
|
|
144
109
|
}
|
|
145
110
|
|
|
111
|
+
// --- Włączanie / wyłączanie SSID ---
|
|
112
|
+
async send(type, ssidName, state) {
|
|
113
|
+
switch (type) {
|
|
114
|
+
case "ssid":
|
|
115
|
+
await this.handleWithLock(async () => {
|
|
116
|
+
if (this.logDebug) this.emit("debug", `[${this.host}] ${state ? "Enabling" : "Disabling"} SSID ${ssidName}`);
|
|
117
|
+
|
|
118
|
+
const status = await this.ubusCall("network.wireless", "status");
|
|
119
|
+
const iface = await this.functions.findIfaceBySsid(status, ssidName);
|
|
120
|
+
if (!iface) throw new Error(`SSID ${ssidName} not found`);
|
|
121
|
+
|
|
122
|
+
const section = iface.section;
|
|
123
|
+
if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
|
|
124
|
+
|
|
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", {});
|
|
132
|
+
|
|
133
|
+
if (this.logDebug) this.emit("debug", `[${this.host}] SSID ${ssidName} ${state ? "enabled" : "disabled"}`);
|
|
134
|
+
});
|
|
135
|
+
break;
|
|
136
|
+
case "switch":
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
146
141
|
async send(type, ssidName, state) {
|
|
147
142
|
switch (type) {
|
|
148
143
|
case 'ssid':
|