homebridge-openwrt-control 0.0.2-beta.2 → 0.0.2-beta.21
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/config.schema.json +5 -5
- package/index.js +19 -19
- package/package.json +1 -1
- package/src/functions.js +0 -24
- package/src/openwrt.js +44 -45
package/config.schema.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"singular": true,
|
|
5
5
|
"fixArrays": true,
|
|
6
6
|
"strictValidation": true,
|
|
7
|
-
"headerDisplay": "This plugin works with OpenWrt
|
|
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
|
|
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
|
|
122
|
+
"functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
|
|
123
123
|
}
|
|
124
124
|
},
|
|
125
125
|
"sensor": {
|
|
126
|
-
"title": "
|
|
126
|
+
"title": "Sensor",
|
|
127
127
|
"type": "boolean",
|
|
128
|
-
"description": "Here Enable/Disable sensor for every
|
|
128
|
+
"description": "Here Enable/Disable sensor for every SSID.",
|
|
129
129
|
"condition": {
|
|
130
130
|
"functionBody": "return model.devices[arrayIndices].accessPoint.enable === true;"
|
|
131
131
|
}
|
package/index.js
CHANGED
|
@@ -26,34 +26,34 @@ class OpenWrtPlatform {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
api.on('didFinishLaunching', async () => {
|
|
29
|
-
for (const
|
|
30
|
-
const { name, host,
|
|
31
|
-
if (!name || !host || !
|
|
32
|
-
log.warn(`Device: ${host || 'host missing'}, ${name || 'name missing'}, ${
|
|
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:
|
|
39
|
-
success:
|
|
40
|
-
info:
|
|
41
|
-
warn:
|
|
42
|
-
error:
|
|
43
|
-
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
|
-
...
|
|
49
|
+
...deviceConfig,
|
|
50
50
|
auth: {
|
|
51
|
-
...
|
|
51
|
+
...deviceConfig.auth,
|
|
52
52
|
passwd: 'removed',
|
|
53
53
|
},
|
|
54
54
|
mqtt: {
|
|
55
55
|
auth: {
|
|
56
|
-
...
|
|
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 = (
|
|
65
|
-
if (
|
|
66
|
-
if (
|
|
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(
|
|
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,8 +90,8 @@ class OpenWrtPlatform {
|
|
|
90
90
|
// create device instance
|
|
91
91
|
let type;
|
|
92
92
|
switch (device) {
|
|
93
|
-
case 'accessPoint': type = new AccessPoint(api,
|
|
94
|
-
case 'switch': type = new Switch(api,
|
|
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
96
|
if (logLevel.warn) log.warn(`Device: ${host} ${name}, unknown device: ${device}`);
|
|
97
97
|
return;
|
package/package.json
CHANGED
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
|
|
2
|
-
import axios from
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import axios from 'axios';
|
|
3
3
|
import Functions from './functions.js';
|
|
4
|
-
import ImpulseGenerator from
|
|
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.
|
|
11
|
-
this.
|
|
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,7 +24,7 @@ class OpenWrt extends EventEmitter {
|
|
|
26
24
|
|
|
27
25
|
this.functions = new Functions();
|
|
28
26
|
this.axiosInstance = axios.create({
|
|
29
|
-
baseURL:
|
|
27
|
+
baseURL: `http://${config.host}/ubus`,
|
|
30
28
|
timeout: 5000,
|
|
31
29
|
headers: {
|
|
32
30
|
"Content-Type": "application/json"
|
|
@@ -34,11 +32,11 @@ class OpenWrt extends EventEmitter {
|
|
|
34
32
|
});
|
|
35
33
|
|
|
36
34
|
this.impulseGenerator = new ImpulseGenerator()
|
|
37
|
-
.on(
|
|
35
|
+
.on('connect', () => this.handleWithLock(async () => {
|
|
38
36
|
await this.connect();
|
|
39
37
|
}))
|
|
40
|
-
.on(
|
|
41
|
-
this.emit(state ?
|
|
38
|
+
.on('state', (state) => {
|
|
39
|
+
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
42
40
|
});
|
|
43
41
|
}
|
|
44
42
|
|
|
@@ -49,8 +47,7 @@ class OpenWrt extends EventEmitter {
|
|
|
49
47
|
try {
|
|
50
48
|
await fn();
|
|
51
49
|
} catch (error) {
|
|
52
|
-
this.emit(
|
|
53
|
-
);
|
|
50
|
+
this.emit('error', `Impulse generator error: ${error.message}`);
|
|
54
51
|
} finally {
|
|
55
52
|
this.lock = false;
|
|
56
53
|
}
|
|
@@ -62,52 +59,54 @@ class OpenWrt extends EventEmitter {
|
|
|
62
59
|
return this.sessionId;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
this.emit('debug', `Login: ${this.user} ${this.passwd}`)
|
|
63
|
+
const response = await this.axiosInstance.post('', {
|
|
64
|
+
jsonrpc: '2.0',
|
|
67
65
|
id: 1,
|
|
68
|
-
method:
|
|
69
|
-
params: [
|
|
66
|
+
method: 'call',
|
|
67
|
+
params: ['00000000000000000000000000000000', 'session', 'login', { username: this.user, password: this.passwd }]
|
|
70
68
|
});
|
|
71
69
|
|
|
72
70
|
const result = response.data?.result?.[1];
|
|
73
71
|
if (!result?.ubus_rpc_session) {
|
|
74
|
-
throw new Error(
|
|
72
|
+
throw new Error('ubus login failed');
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
this.sessionId = result.ubus_rpc_session;
|
|
78
76
|
this.sessionExpiresAt = now + 240_000;
|
|
79
77
|
|
|
80
|
-
if (this.logDebug) this.emit(
|
|
78
|
+
if (this.logDebug) this.emit('debug', `Ubus login OK`);
|
|
81
79
|
return this.sessionId;
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
async ubusCall(service, method, params = {}) {
|
|
85
83
|
const session = await this.login();
|
|
86
84
|
|
|
87
|
-
const response = await this.axiosInstance.post(
|
|
88
|
-
jsonrpc:
|
|
85
|
+
const response = await this.axiosInstance.post('', {
|
|
86
|
+
jsonrpc: '2.0',
|
|
89
87
|
id: 2,
|
|
90
|
-
method:
|
|
88
|
+
method: 'call',
|
|
91
89
|
params: [session, service, method, params]
|
|
92
90
|
});
|
|
93
91
|
|
|
94
92
|
if (response.data?.error) {
|
|
95
|
-
throw new Error(response.data.error.message ||
|
|
93
|
+
throw new Error(response.data.error.message || 'ubus call error');
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
return response.data.result[1];
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
async connect() {
|
|
100
|
+
const openWrtInfo = { state: false, systemInfo: {}, wirelessStatus: {}, wirelessRadios: [], ssids: [] }
|
|
101
|
+
|
|
102
102
|
try {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
if (this.logDebug) this.emit("debug", `System info data: ${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)}`);
|
|
106
105
|
|
|
107
|
-
const wirelessStatus = await this.ubusCall(
|
|
108
|
-
if (this.logDebug) this.emit(
|
|
106
|
+
const wirelessStatus = await this.ubusCall('network.wireless', 'status');
|
|
107
|
+
if (this.logDebug) this.emit('debug', `Wireless status data: ${JSON.stringify(wirelessStatus, null, 2)}`);
|
|
109
108
|
|
|
110
|
-
const wirelessRadios = Object.values(
|
|
109
|
+
const wirelessRadios = Object.values(wirelessStatus.radios).map(radio => ({
|
|
111
110
|
name: radio.name,
|
|
112
111
|
state: radio.up === true,
|
|
113
112
|
interfaces: Object.values(radio.interfaces).map(i => ({
|
|
@@ -118,13 +117,13 @@ class OpenWrt extends EventEmitter {
|
|
|
118
117
|
}));
|
|
119
118
|
|
|
120
119
|
const ssids = wirelessRadios.flatMap(radio => radio.interfaces);
|
|
121
|
-
this.emit(
|
|
122
|
-
this.emit(
|
|
123
|
-
this.emit(
|
|
124
|
-
this.emit(
|
|
120
|
+
this.emit('systemInfo', systemInfo);
|
|
121
|
+
this.emit('wirelessStatus', wirelessStatus);
|
|
122
|
+
this.emit('wirelessRadios', wirelessRadios);
|
|
123
|
+
this.emit('ssids', ssids);
|
|
125
124
|
|
|
126
125
|
if (this.firstRun) {
|
|
127
|
-
this.emit(
|
|
126
|
+
this.emit('success', `Connect success`);
|
|
128
127
|
this.firstRun = false;
|
|
129
128
|
}
|
|
130
129
|
|
|
@@ -134,11 +133,11 @@ class OpenWrt extends EventEmitter {
|
|
|
134
133
|
openWrtInfo.wirelessRadios = wirelessRadios;
|
|
135
134
|
openWrtInfo.ssids = ssids;
|
|
136
135
|
|
|
137
|
-
if (this.logDebug) this.emit(
|
|
136
|
+
if (this.logDebug) this.emit('debug', `OpenWrt Data: ${JSON.stringify(openWrtInfo, null, 2)}`);
|
|
138
137
|
return openWrtInfo;
|
|
139
138
|
} catch (error) {
|
|
140
|
-
if (this.logError) this.emit(
|
|
141
|
-
return
|
|
139
|
+
if (this.logError) this.emit('error', `Connect error: ${error.message}`);
|
|
140
|
+
return openWrtInfo;
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
143
|
|
|
@@ -146,29 +145,29 @@ class OpenWrt extends EventEmitter {
|
|
|
146
145
|
switch (type) {
|
|
147
146
|
case 'ssid':
|
|
148
147
|
await this.handleWithLock(async () => {
|
|
149
|
-
if (this.logDebug) this.emit(
|
|
148
|
+
if (this.logDebug) this.emit('debug', `${state ? 'Enabling' : 'Disabling'} SSID ${ssidName}`);
|
|
150
149
|
|
|
151
|
-
const status = await this.ubusCall(
|
|
150
|
+
const status = await this.ubusCall('network.wireless', 'status');
|
|
152
151
|
const iface = await this.functions.findIfaceBySsid(status, ssidName);
|
|
153
152
|
if (!iface) throw new Error(`SSID ${ssidName} not found`);
|
|
154
153
|
|
|
155
154
|
const section = iface.section;
|
|
156
155
|
if (!section) throw new Error(`No UCI section for SSID ${ssidName}`);
|
|
157
156
|
|
|
158
|
-
await this.ubusCall(
|
|
157
|
+
await this.ubusCall('uci', 'set',
|
|
159
158
|
{
|
|
160
|
-
config:
|
|
159
|
+
config: 'wireless',
|
|
161
160
|
section: section,
|
|
162
161
|
values: {
|
|
163
|
-
disabled: state ?
|
|
162
|
+
disabled: state ? '0' : '1'
|
|
164
163
|
}
|
|
165
164
|
}
|
|
166
165
|
);
|
|
167
166
|
|
|
168
|
-
await this.ubusCall(
|
|
169
|
-
await this.ubusCall(
|
|
167
|
+
await this.ubusCall('uci', 'commit', { config: 'wireless' });
|
|
168
|
+
await this.ubusCall('network.wireless', 'reload', {});
|
|
170
169
|
|
|
171
|
-
if (this.logDebug) this.emit(
|
|
170
|
+
if (this.logDebug) this.emit('debug', `Send SSID ${ssidName} ${state ? 'enabled' : 'disabled'}`);
|
|
172
171
|
});
|
|
173
172
|
break;
|
|
174
173
|
case 'switch':
|