homebridge-denon-tv 6.2.0 → 6.2.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 +13 -0
- package/README.md +5 -1
- package/config.schema.json +4 -1
- package/index.js +5 -2
- package/package.json +1 -1
- package/src/restful.js +43 -0
package/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
21
|
- For plugin < v6.1.0 use Homebridge UI <= v5.5.0
|
|
22
22
|
- For plugin >= v6.1.0 use Homebridge UI >= v5.13.0
|
|
23
23
|
|
|
24
|
+
## [6.2.2] - (24.09.2026)
|
|
25
|
+
|
|
26
|
+
### Changes
|
|
27
|
+
|
|
28
|
+
- added: RESTFul request rate limit, 600 requests per minute per client and at most 1000 clients per minute, answered with `429` and `Retry-After`, one log warning per client and minute.
|
|
29
|
+
|
|
30
|
+
## [6.2.1] - (23.09.2026)
|
|
31
|
+
|
|
32
|
+
### Changes
|
|
33
|
+
|
|
34
|
+
- fix: MQTT `Enable` was shown in the config UI for the Surrounds and Pass Through Inputs zones, which do not support MQTT
|
|
35
|
+
- fix: RESTFul server and MQTT client are no longer started for the Surrounds and Pass Through Inputs zones when enabled in an old config
|
|
36
|
+
|
|
24
37
|
## [6.2.0] - (23.09.2026)
|
|
25
38
|
|
|
26
39
|
### Changes
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
* Surrounds/Pass Trough Inputs control as a [extra accessory tile](https://github.com/grzegorz914/homebridge-denon-tv?tab=readme-ov-file#extra-accessory-tile).
|
|
43
43
|
* Siri, some times need to create buttons/switches/sensors.
|
|
44
44
|
* Automations, some times need to create buttons/switches/sensors.
|
|
45
|
-
* External integrations, [RESTFul](https://github.com/grzegorz914/homebridge-denon-tv?tab=readme-ov-file#restful-integration), [MQTT](https://github.com/grzegorz914/homebridge-denon-tv?tab=readme-ov-file#mqtt-integration).
|
|
45
|
+
* External integrations, [RESTFul](https://github.com/grzegorz914/homebridge-denon-tv?tab=readme-ov-file#restful-integration), [MQTT](https://github.com/grzegorz914/homebridge-denon-tv?tab=readme-ov-file#mqtt-integration), [HA Discovery](https://github.com/grzegorz914/homeassistant-mqtt-media-player).
|
|
46
46
|
|
|
47
47
|
<p align="center">
|
|
48
48
|
<a href="https://github.com/grzegorz914/homebridge-denon-tv"><img src="https://raw.githubusercontent.com/grzegorz914/homebridge-denon-tv/main/graphics/homekit.png" width="382"></a>
|
|
@@ -168,6 +168,10 @@
|
|
|
168
168
|
|
|
169
169
|
* Only for Main Zone, Zone 2, Zone 3
|
|
170
170
|
* Subscribe data as a JSON Object `{Power: true}`
|
|
171
|
+
* HA Discovery - if enabled, the device is published to Home Assistant as one media player (class `receiver`) with power, volume, mute, source selection and more. Main Zone, Zone 2 and Zone 3 are published as separate media players. Requires the [MQTT Universal Media Player](https://github.com/grzegorz914/homeassistant-mqtt-media-player) integration. Additional retained topics:
|
|
172
|
+
* `homeassistant/media_player/<id>/config` - discovery message.
|
|
173
|
+
* `HA State` - `{"power": true, "volume": 49, "muted": false, "source": "SITV", "sound_mode": "MOVIE"}`.
|
|
174
|
+
* `Availability` - `online`, `offline` (last will).
|
|
171
175
|
|
|
172
176
|
| Method | Topic | Message | Type |
|
|
173
177
|
| --- | --- | --- | --- |
|
package/config.schema.json
CHANGED
|
@@ -7007,7 +7007,10 @@
|
|
|
7007
7007
|
"title": "Enable",
|
|
7008
7008
|
"type": "boolean",
|
|
7009
7009
|
"default": false,
|
|
7010
|
-
"description": "This enable MQTT client."
|
|
7010
|
+
"description": "This enable MQTT client.",
|
|
7011
|
+
"condition": {
|
|
7012
|
+
"functionBody": "return model.devices[arrayIndices].zoneControl <= 2;"
|
|
7013
|
+
}
|
|
7011
7014
|
},
|
|
7012
7015
|
"host": {
|
|
7013
7016
|
"title": "IP/Hostname",
|
package/index.js
CHANGED
|
@@ -182,7 +182,10 @@ class DenonPlatform {
|
|
|
182
182
|
const state = zoneStates.get(zone);
|
|
183
183
|
const manufacturer = denonInfo?.info?.manufacturer || 'denon';
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
// RESTFul and MQTT are used only by Main Zone, Zone 2 and Zone 3
|
|
186
|
+
const externalIntegrations = zone.zoneControl <= 2;
|
|
187
|
+
|
|
188
|
+
if (!state.restFul1 && externalIntegrations && zone.restFul?.enable) {
|
|
186
189
|
try {
|
|
187
190
|
await new Promise((resolve) => {
|
|
188
191
|
const timer = setTimeout(resolve, 5000);
|
|
@@ -214,7 +217,7 @@ class DenonPlatform {
|
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
if (!state.mqtt1 && zone.mqtt?.enable) {
|
|
220
|
+
if (!state.mqtt1 && externalIntegrations && zone.mqtt?.enable) {
|
|
218
221
|
try {
|
|
219
222
|
await new Promise((resolve) => {
|
|
220
223
|
const timer = setTimeout(resolve, 10000);
|
package/package.json
CHANGED
package/src/restful.js
CHANGED
|
@@ -26,6 +26,49 @@ class RestFul extends EventEmitter {
|
|
|
26
26
|
try {
|
|
27
27
|
const app = express();
|
|
28
28
|
app.set('json spaces', 2);
|
|
29
|
+
|
|
30
|
+
// Request rate limit per client, protects Homebridge from request floods (CWE-770)
|
|
31
|
+
const rateLimitWindow = 60 * 1000;
|
|
32
|
+
const rateLimitMax = 600;
|
|
33
|
+
const rateLimitClients = 1000;
|
|
34
|
+
const clients = new Map();
|
|
35
|
+
let clientsLimitWarned = 0;
|
|
36
|
+
app.use((req, res, next) => {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
|
|
39
|
+
// Drop expired windows so the map cannot grow without limit
|
|
40
|
+
if (clients.size > 100) {
|
|
41
|
+
for (const [ip, client] of clients) {
|
|
42
|
+
if (now - client.start >= rateLimitWindow) clients.delete(ip);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let client = clients.get(req.ip);
|
|
47
|
+
if (!client || now - client.start >= rateLimitWindow) {
|
|
48
|
+
// Hard limit of tracked addresses, new addresses wait until older windows expire
|
|
49
|
+
if (!client && clients.size >= rateLimitClients) {
|
|
50
|
+
if (now - clientsLimitWarned >= rateLimitWindow) {
|
|
51
|
+
clientsLimitWarned = now;
|
|
52
|
+
if (this.logWarn) this.emit('warn', `RESTFul too many clients (${rateLimitClients}) in one minute, new clients are rejected until the window ends`);
|
|
53
|
+
}
|
|
54
|
+
res.set('Retry-After', String(Math.ceil(rateLimitWindow / 1000)));
|
|
55
|
+
return res.status(429).json({ error: 'RESTFul Too Many Requests' });
|
|
56
|
+
}
|
|
57
|
+
client = { count: 0, start: now, warned: false };
|
|
58
|
+
clients.set(req.ip, client);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (++client.count > rateLimitMax) {
|
|
62
|
+
// Warn once per client and window, a flood must not flood the log too
|
|
63
|
+
if (!client.warned && this.logWarn) this.emit('warn', `RESTFul rate limit ${rateLimitMax} requests per minute exceeded by: ${req.ip}, further requests are rejected until the window ends`);
|
|
64
|
+
client.warned = true;
|
|
65
|
+
res.set('Retry-After', String(Math.ceil((client.start + rateLimitWindow - now) / 1000)));
|
|
66
|
+
return res.status(429).json({ error: 'RESTFul Too Many Requests' });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
next();
|
|
70
|
+
});
|
|
71
|
+
|
|
29
72
|
app.use(json());
|
|
30
73
|
|
|
31
74
|
// Optional token auth, when a token is configured every route requires "Authorization: Bearer <token>"
|