homebridge-lgwebos-tv 4.3.13 → 4.3.14
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 +7 -0
- package/README.md +2 -0
- package/config.schema.json +12 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/restful.js +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
15
15
|
- For plugin < v4.1.0 use Homebridge UI <= v5.5.0
|
|
16
16
|
- For plugin >= v4.1.0 use Homebridge UI >= v5.13.0
|
|
17
17
|
|
|
18
|
+
## [4.3.14] - (23.09.2026)
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- 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
|
|
23
|
+
- readme update
|
|
24
|
+
|
|
18
25
|
## [4.3.13] - (13.09.2026)
|
|
19
26
|
|
|
20
27
|
## Changes
|
package/README.md
CHANGED
|
@@ -137,6 +137,7 @@
|
|
|
137
137
|
| `restFul{}` | RESTFul object. |
|
|
138
138
|
| `restFul.enable` | If enabled, RESTful server will start automatically and respond to any path request. |
|
|
139
139
|
| `restFul.port` | Here set the listening `Port` for RESTful server. |
|
|
140
|
+
| `restFul.token` | Here optional set the access token. When set, every request must send the header `Authorization: Bearer <token>`, otherwise the server responds `401`. |
|
|
140
141
|
| `mqtt{}` | MQTT object. |
|
|
141
142
|
| `mqtt.enable` | If enabled, MQTT Broker will start automatically and publish all awailable PV data. |
|
|
142
143
|
| `mqtt.host` | Here set the `IP Address` or `Hostname` for MQTT Broker. |
|
|
@@ -152,6 +153,7 @@
|
|
|
152
153
|
### RESTFul Integration
|
|
153
154
|
|
|
154
155
|
* POST data as a JSON Object `{Power: true}`, content type must be `application/json`
|
|
156
|
+
* If `Token` is set, every request (GET and POST) must send the header `Authorization: Bearer <token>`.
|
|
155
157
|
* Path `status` response all available paths.
|
|
156
158
|
* References:
|
|
157
159
|
* Picture Mode - `cinema`, `eco`, `expert1`, `expert2`, `game`, `normal`, `photo`, `sports`, `technicolor`, `vivid`, `hdrEffect`, `hdrFilmMaker`, `hdrCinema`, `hdrCinemaBright`, `hdrStandard`, `hdrEffect`, `hdrGame`, `hdrVivid`, `hdrTechnicolor`, `hdrExternal`, `dolbyHdrCinema`, `dolbyHdrCinemaBright`, `dolbyHdrDarkAmazon`, `dolbyHdrStandard`, `dolbyHdrGame`, `dolbyHdrVivid`.
|
package/config.schema.json
CHANGED
|
@@ -1728,6 +1728,16 @@
|
|
|
1728
1728
|
"condition": {
|
|
1729
1729
|
"functionBody": "return model.devices[arrayIndices].restFul.enable === true;"
|
|
1730
1730
|
}
|
|
1731
|
+
},
|
|
1732
|
+
"token": {
|
|
1733
|
+
"title": "Token",
|
|
1734
|
+
"type": "string",
|
|
1735
|
+
"placeholder": "optional",
|
|
1736
|
+
"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.",
|
|
1737
|
+
"format": "password",
|
|
1738
|
+
"condition": {
|
|
1739
|
+
"functionBody": "return model.devices[arrayIndices].restFul.enable === true;"
|
|
1740
|
+
}
|
|
1731
1741
|
}
|
|
1732
1742
|
}
|
|
1733
1743
|
},
|
|
@@ -2107,7 +2117,8 @@
|
|
|
2107
2117
|
"title": "RESTFul",
|
|
2108
2118
|
"items": [
|
|
2109
2119
|
"devices[].restFul.enable",
|
|
2110
|
-
"devices[].restFul.port"
|
|
2120
|
+
"devices[].restFul.port",
|
|
2121
|
+
"devices[].restFul.token"
|
|
2111
2122
|
]
|
|
2112
2123
|
},
|
|
2113
2124
|
{
|
package/index.js
CHANGED
package/package.json
CHANGED
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
|
|
|
@@ -35,6 +37,19 @@ class RestFul extends EventEmitter {
|
|
|
35
37
|
app.set('json spaces', 2);
|
|
36
38
|
app.use(json());
|
|
37
39
|
|
|
40
|
+
// Optional token auth, when a token is configured every route requires "Authorization: Bearer <token>"
|
|
41
|
+
if (this.token) {
|
|
42
|
+
app.use((req, res, next) => {
|
|
43
|
+
const header = req.headers.authorization ?? '';
|
|
44
|
+
const provided = header.startsWith('Bearer ') ? header.slice(7) : '';
|
|
45
|
+
if (!this.tokenValid(provided)) {
|
|
46
|
+
if (this.logWarn) this.emit('warn', `RESTFul Unauthorized request from: ${req.ip}, ${req.method} ${req.path}`);
|
|
47
|
+
return res.status(401).json({ error: 'RESTFul Unauthorized' });
|
|
48
|
+
}
|
|
49
|
+
next();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
// Register GET routes for all keys
|
|
39
54
|
for (const key of Object.keys(this.data)) {
|
|
40
55
|
app.get(`/${key}`, (req, res) => {
|
|
@@ -83,6 +98,13 @@ class RestFul extends EventEmitter {
|
|
|
83
98
|
}
|
|
84
99
|
}
|
|
85
100
|
|
|
101
|
+
tokenValid(provided) {
|
|
102
|
+
// Compare fixed-length hashes so the check takes the same time regardless of token length or content
|
|
103
|
+
const a = createHash('sha256').update(provided).digest();
|
|
104
|
+
const b = createHash('sha256').update(this.token).digest();
|
|
105
|
+
return timingSafeEqual(a, b);
|
|
106
|
+
}
|
|
107
|
+
|
|
86
108
|
update(path, data) {
|
|
87
109
|
if (this.data.hasOwnProperty(path)) {
|
|
88
110
|
this.data[path] = data;
|