homebridge-melcloud-control 4.11.0 → 4.11.2
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/CHANGELOG.md +8 -1
- package/README.md +2 -0
- package/config.schema.json +12 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/melcloudata.js +1 -1
- package/src/restful.js +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -25,7 +25,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
25
|
- For plugin < v4.6.0 use Homebridge UI <= v5.5.0
|
|
26
26
|
- For plugin >= v4.6.0 use Homebridge UI >= v5.13.0
|
|
27
27
|
|
|
28
|
-
## [4.11.
|
|
28
|
+
## [4.11.2] - (23.09.2026)
|
|
29
|
+
|
|
30
|
+
### Changes
|
|
31
|
+
|
|
32
|
+
- added: optional RESTful access `Token`. When set, every request (GET and POST) must send the header `Authorization: Bearer <token>`, otherwise the server responds `401`. Leaving it empty keeps the previous behaviour, so existing configs keep working
|
|
33
|
+
- readme update
|
|
34
|
+
|
|
35
|
+
## [4.11.1] - (23.09.2026)
|
|
29
36
|
|
|
30
37
|
### Changes
|
|
31
38
|
|
package/README.md
CHANGED
|
@@ -358,6 +358,7 @@
|
|
|
358
358
|
| `log.debug` | This enable debug log will be displayed in console. |
|
|
359
359
|
| `restFul{}` | RSTful object. |
|
|
360
360
|
| `restFul.enable` | This enable RESTful server will start automatically and respond to any path request. |
|
|
361
|
+
| `restFul.token` | Here optional set the access token. When set, every request must send the header `Authorization: Bearer <token>`, otherwise the server responds `401`. |
|
|
361
362
|
| `mqtt{}` | MQTT object. |
|
|
362
363
|
| `mqtt.enable` | This enable MQTT Broker will start automatically and publish all awailable PV data. |
|
|
363
364
|
| `mqtt.host` | Here set the `IP Address` or `Hostname` for MQTT Broker. |
|
|
@@ -376,6 +377,7 @@
|
|
|
376
377
|
* MELCLoud Home, start at `30000`, correct port is displayed in HB log during start.
|
|
377
378
|
* POST data as a JSON Object `{OperationMode: 8}`.
|
|
378
379
|
* Header content type must be `application/json`.
|
|
380
|
+
* If `Token` is set, every request (GET and POST) must send the header `Authorization: Bearer <token>`.
|
|
379
381
|
* Path `status` response all available paths.
|
|
380
382
|
|
|
381
383
|
| Method | URL | Path | Response | Type |
|
package/config.schema.json
CHANGED
|
@@ -2362,6 +2362,16 @@
|
|
|
2362
2362
|
"type": "boolean",
|
|
2363
2363
|
"default": false,
|
|
2364
2364
|
"description": "This enable RESTful server."
|
|
2365
|
+
},
|
|
2366
|
+
"token": {
|
|
2367
|
+
"title": "Token",
|
|
2368
|
+
"type": "string",
|
|
2369
|
+
"placeholder": "optional",
|
|
2370
|
+
"description": "Optional access token. When set, every request must send the header 'Authorization: Bearer <token>'. Leave empty to keep the server open on the local network.",
|
|
2371
|
+
"format": "password",
|
|
2372
|
+
"condition": {
|
|
2373
|
+
"functionBody": "return model.accounts[arrayIndices[0]].restFul.enable === true;"
|
|
2374
|
+
}
|
|
2365
2375
|
}
|
|
2366
2376
|
}
|
|
2367
2377
|
},
|
|
@@ -3074,7 +3084,8 @@
|
|
|
3074
3084
|
"key": "accounts[].restFul",
|
|
3075
3085
|
"title": "RESTFul",
|
|
3076
3086
|
"items": [
|
|
3077
|
-
"accounts[].restFul.enable"
|
|
3087
|
+
"accounts[].restFul.enable",
|
|
3088
|
+
"accounts[].restFul.token"
|
|
3078
3089
|
]
|
|
3079
3090
|
},
|
|
3080
3091
|
{
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.11.
|
|
4
|
+
"version": "4.11.2",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
package/src/melcloudata.js
CHANGED
|
@@ -344,7 +344,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
//send payload
|
|
347
|
-
if (
|
|
347
|
+
if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
348
348
|
await this.client(path, { method: method, data: payload });
|
|
349
349
|
return true;
|
|
350
350
|
default:
|
package/src/restful.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import express, { json } from 'express';
|
|
2
2
|
import EventEmitter from 'events';
|
|
3
|
+
import { createHash, timingSafeEqual } from 'crypto';
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MESSAGE = 'This data is not available at this time.';
|
|
5
6
|
|
|
@@ -7,6 +8,7 @@ class RestFul extends EventEmitter {
|
|
|
7
8
|
constructor(config) {
|
|
8
9
|
super();
|
|
9
10
|
this.port = config.port;
|
|
11
|
+
this.token = typeof config.token === 'string' ? config.token.trim() : '';
|
|
10
12
|
this.logWarn = config.logWarn;
|
|
11
13
|
this.logDebug = config.logDebug;
|
|
12
14
|
|
|
@@ -24,6 +26,19 @@ class RestFul extends EventEmitter {
|
|
|
24
26
|
app.set('json spaces', 2);
|
|
25
27
|
app.use(json());
|
|
26
28
|
|
|
29
|
+
// Optional token auth, when a token is configured every route requires "Authorization: Bearer <token>"
|
|
30
|
+
if (this.token) {
|
|
31
|
+
app.use((req, res, next) => {
|
|
32
|
+
const header = req.headers.authorization ?? '';
|
|
33
|
+
const provided = header.startsWith('Bearer ') ? header.slice(7) : '';
|
|
34
|
+
if (!this.tokenValid(provided)) {
|
|
35
|
+
if (this.logWarn) this.emit('warn', `RESTFul Unauthorized request from: ${req.ip}, ${req.method} ${req.path}`);
|
|
36
|
+
return res.status(401).json({ error: 'RESTFul Unauthorized' });
|
|
37
|
+
}
|
|
38
|
+
next();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
// Register GET routes for all keys
|
|
28
43
|
for (const key of Object.keys(this.restFulData)) {
|
|
29
44
|
app.get(`/${key}`, (req, res) => {
|
|
@@ -72,6 +87,13 @@ class RestFul extends EventEmitter {
|
|
|
72
87
|
}
|
|
73
88
|
}
|
|
74
89
|
|
|
90
|
+
tokenValid(provided) {
|
|
91
|
+
// Compare fixed-length hashes so the check takes the same time regardless of token length or content
|
|
92
|
+
const a = createHash('sha256').update(provided).digest();
|
|
93
|
+
const b = createHash('sha256').update(this.token).digest();
|
|
94
|
+
return timingSafeEqual(a, b);
|
|
95
|
+
}
|
|
96
|
+
|
|
75
97
|
update(path, data) {
|
|
76
98
|
if (this.restFulData.hasOwnProperty(path)) {
|
|
77
99
|
this.restFulData[path] = data;
|