homebridge-denon-tv 6.2.3 → 6.2.5
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 +12 -0
- package/package.json +1 -1
- package/src/hadiscovery.js +25 -0
- package/src/mainzone.js +3 -2
- package/src/mqtt.js +1 -1
- package/src/passthroughinputs.js +3 -2
- package/src/surrounds.js +3 -2
- package/src/zone.js +14 -9
- package/src/zone2.js +3 -2
- package/src/zone3.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,18 @@ 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.5] - (24.09.2026)
|
|
25
|
+
|
|
26
|
+
### Changes
|
|
27
|
+
|
|
28
|
+
- fix: with 85 inputs (the HomeKit limit) the input list from the receiver no longer updated existing inputs, the limit skipped every input instead of only new ones, so names of existing inputs were never refreshed. The limit now applies to new inputs only
|
|
29
|
+
|
|
30
|
+
## [6.2.4] - (24.09.2026)
|
|
31
|
+
|
|
32
|
+
### Changes
|
|
33
|
+
|
|
34
|
+
- fix: with more than one zone configured for the same receiver, every zone overwrote the shared receiver capabilities. The Main Zone lost sound mode and picture mode support (sound mode was no longer read, so Home Assistant did not show the current sound mode) and could report the name of another zone. Each zone now works on its own copy of the receiver capabilities
|
|
35
|
+
|
|
24
36
|
## [6.2.3] - (24.09.2026)
|
|
25
37
|
|
|
26
38
|
### Changes
|
package/package.json
CHANGED
package/src/hadiscovery.js
CHANGED
|
@@ -20,12 +20,14 @@ class HaDiscovery {
|
|
|
20
20
|
this.configTopic = `${mqtt.haPrefix}/media_player/${this.objectId}/config`;
|
|
21
21
|
this.stateTopic = `${mqtt.config.prefix}/HA State`;
|
|
22
22
|
this.commandTopic = `${mqtt.config.prefix}/Set`;
|
|
23
|
+
this.imageTopic = config.image ? `${mqtt.config.prefix}/HA Image` : null;
|
|
23
24
|
|
|
24
25
|
this.sources = [];
|
|
25
26
|
this.soundModes = [];
|
|
26
27
|
this.state = {};
|
|
27
28
|
this.lastConfig = '';
|
|
28
29
|
this.lastState = '';
|
|
30
|
+
this.lastImageKey = undefined;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// Publish (or republish when changed) the retained discovery message
|
|
@@ -45,6 +47,7 @@ class HaDiscovery {
|
|
|
45
47
|
state_topic: this.stateTopic,
|
|
46
48
|
command_topic: this.commandTopic,
|
|
47
49
|
availability_topic: this.mqtt.availabilityTopic,
|
|
50
|
+
...(this.imageTopic ? { image_topic: this.imageTopic } : {}),
|
|
48
51
|
device,
|
|
49
52
|
commands: this.commands,
|
|
50
53
|
sources: this.sources,
|
|
@@ -58,6 +61,28 @@ class HaDiscovery {
|
|
|
58
61
|
return true;
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
// Publish the image of the current source (app icon, channel picon) once per source.
|
|
65
|
+
// key identifies the image, fetchImage returns a Buffer or null, null clears the image
|
|
66
|
+
async updateImage(key, fetchImage) {
|
|
67
|
+
if (!this.imageTopic || key === this.lastImageKey) return false;
|
|
68
|
+
this.lastImageKey = key;
|
|
69
|
+
|
|
70
|
+
let image = null;
|
|
71
|
+
try {
|
|
72
|
+
image = key ? await fetchImage() : null;
|
|
73
|
+
} catch {
|
|
74
|
+
// Network or device error, allow a retry on the next state update
|
|
75
|
+
image = null;
|
|
76
|
+
if (key === this.lastImageKey) this.lastImageKey = undefined;
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The source changed again while fetching, a newer call publishes its own image
|
|
81
|
+
if (key !== this.lastImageKey) return false;
|
|
82
|
+
await this.mqtt.publishRetained(this.imageTopic, image ?? '');
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
61
86
|
// Merge a partial state and publish it retained when something changed
|
|
62
87
|
async updateState(partial) {
|
|
63
88
|
for (const [key, value] of Object.entries(partial)) {
|
package/src/mainzone.js
CHANGED
|
@@ -227,8 +227,6 @@ class MainZone extends EventEmitter {
|
|
|
227
227
|
let updated = false;
|
|
228
228
|
|
|
229
229
|
for (const input of inputs) {
|
|
230
|
-
if (this.inputsServices.length >= 85 && !remove) continue;
|
|
231
|
-
|
|
232
230
|
const inputReference = input.reference;
|
|
233
231
|
const savedName = this.savedInputsNames[inputReference] ?? input.name;
|
|
234
232
|
const sanitizedName = await this.functions.sanitizeString(savedName);
|
|
@@ -259,6 +257,9 @@ class MainZone extends EventEmitter {
|
|
|
259
257
|
updated = true;
|
|
260
258
|
}
|
|
261
259
|
} else {
|
|
260
|
+
// HomeKit allows at most 85 inputs, the limit applies to new inputs only, existing ones are still updated
|
|
261
|
+
if (this.inputsServices.length >= 85) continue;
|
|
262
|
+
|
|
262
263
|
const identifier = this.inputsServices.length + 1;
|
|
263
264
|
inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${inputReference}`);
|
|
264
265
|
inputService.identifier = identifier;
|
package/src/mqtt.js
CHANGED
|
@@ -110,7 +110,7 @@ class Mqtt extends EventEmitter {
|
|
|
110
110
|
if (this.config.logWarn) this.emit('warn', `MQTT Publish error: ${error.message}`);
|
|
111
111
|
return reject(error);
|
|
112
112
|
}
|
|
113
|
-
if (this.config.logDebug) this.emit('debug', `MQTT Publish retained Topic: ${fullTopic}, Payload: ${payload}`);
|
|
113
|
+
if (this.config.logDebug) this.emit('debug', `MQTT Publish retained Topic: ${fullTopic}, Payload: ${typeof payload === 'string' ? payload : `${payload.length} bytes`}`);
|
|
114
114
|
resolve();
|
|
115
115
|
});
|
|
116
116
|
});
|
package/src/passthroughinputs.js
CHANGED
|
@@ -107,8 +107,6 @@ class PassThroughInputs extends EventEmitter {
|
|
|
107
107
|
let updated = false;
|
|
108
108
|
|
|
109
109
|
for (const input of inputs) {
|
|
110
|
-
if (this.inputsServices.length >= 85 && !remove) continue;
|
|
111
|
-
|
|
112
110
|
const inputReference = input.reference;
|
|
113
111
|
const savedName = this.savedInputsNames[inputReference] ?? input.name;
|
|
114
112
|
const sanitizedName = await this.functions.sanitizeString(savedName);
|
|
@@ -139,6 +137,9 @@ class PassThroughInputs extends EventEmitter {
|
|
|
139
137
|
updated = true;
|
|
140
138
|
}
|
|
141
139
|
} else {
|
|
140
|
+
// HomeKit allows at most 85 inputs, the limit applies to new inputs only, existing ones are still updated
|
|
141
|
+
if (this.inputsServices.length >= 85) continue;
|
|
142
|
+
|
|
142
143
|
const identifier = this.inputsServices.length + 1;
|
|
143
144
|
inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${inputReference}`);
|
|
144
145
|
inputService.identifier = identifier;
|
package/src/surrounds.js
CHANGED
|
@@ -118,8 +118,6 @@ class Surrounds extends EventEmitter {
|
|
|
118
118
|
let updated = false;
|
|
119
119
|
|
|
120
120
|
for (const input of inputs) {
|
|
121
|
-
if (this.inputsServices.length >= 85 && !remove) continue;
|
|
122
|
-
|
|
123
121
|
const inputReference = input.reference;
|
|
124
122
|
const savedName = this.savedInputsNames[inputReference] ?? input.name;
|
|
125
123
|
const sanitizedName = await this.functions.sanitizeString(savedName);
|
|
@@ -150,6 +148,9 @@ class Surrounds extends EventEmitter {
|
|
|
150
148
|
updated = true;
|
|
151
149
|
}
|
|
152
150
|
} else {
|
|
151
|
+
// HomeKit allows at most 85 inputs, the limit applies to new inputs only, existing ones are still updated
|
|
152
|
+
if (this.inputsServices.length >= 85) continue;
|
|
153
|
+
|
|
153
154
|
const identifier = this.inputsServices.length + 1;
|
|
154
155
|
inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${inputReference}`);
|
|
155
156
|
inputService.identifier = identifier;
|
package/src/zone.js
CHANGED
|
@@ -261,10 +261,15 @@ class Zone extends EventEmitter {
|
|
|
261
261
|
|
|
262
262
|
async checkInfo(denonInfo) {
|
|
263
263
|
try {
|
|
264
|
+
// All zones of one receiver share the same denonInfo object. Keep the receiver capabilities
|
|
265
|
+
// untouched and work on a copy per zone, otherwise every zone overwrites the flags of the others
|
|
266
|
+
const deviceInfo = denonInfo.deviceInfo ??= { ...denonInfo.info };
|
|
267
|
+
const info = { ...deviceInfo };
|
|
268
|
+
|
|
264
269
|
// Capabilities
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
info.supportPictureMode = this.zoneControl === 0 && deviceInfo.supportPictureMode;
|
|
271
|
+
info.supportSoundMode = (this.zoneControl === 0 || this.zoneControl === 3) && deviceInfo.supportSoundMode;
|
|
272
|
+
info.supportFavorites = this.zoneControl < 3 && deviceInfo.supportFavorites;
|
|
268
273
|
|
|
269
274
|
// Zone capabilities
|
|
270
275
|
const keys = Object.keys(denonInfo);
|
|
@@ -274,14 +279,14 @@ class Zone extends EventEmitter {
|
|
|
274
279
|
const zone = this.zoneControl === 4 ? 0 : this.zoneControl;
|
|
275
280
|
zoneCaps = zones[zone] || {};
|
|
276
281
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
this.info =
|
|
282
|
+
info.supportShortcuts = zoneCaps.ShortcutControl?.Control === 1;
|
|
283
|
+
info.supportQuickSelect = zoneCaps.Operation?.QuickSelect?.Control === 1;
|
|
284
|
+
info.controlZone = ZoneName[this.zoneControl];
|
|
285
|
+
this.info = info;
|
|
281
286
|
|
|
282
287
|
// Success event
|
|
283
288
|
if (this.firstRun) {
|
|
284
|
-
this.emit('deviceInfo',
|
|
289
|
+
this.emit('deviceInfo', info);
|
|
285
290
|
this.firstRun = false;
|
|
286
291
|
}
|
|
287
292
|
|
|
@@ -295,7 +300,7 @@ class Zone extends EventEmitter {
|
|
|
295
300
|
2: inputsNewDevice
|
|
296
301
|
};
|
|
297
302
|
const inputs = this.getInputsFromDevice ? inputsMap[this.generation] : this.inputs;
|
|
298
|
-
const allInputs = await this.prepareInputs(denonInfo, this.generation, this.zoneControl, inputs, zoneCaps, this.getInputsFromDevice, this.getFavoritesFromDevice, this.getQuickSmartSelectFromDevice,
|
|
303
|
+
const allInputs = await this.prepareInputs(denonInfo, this.generation, this.zoneControl, inputs, zoneCaps, this.getInputsFromDevice, this.getFavoritesFromDevice, this.getQuickSmartSelectFromDevice, info.supportFavorites, info.supportShortcuts, info.supportQuickSelect);
|
|
299
304
|
|
|
300
305
|
// Emit inputs
|
|
301
306
|
this.emit('addRemoveOrUpdateInput', allInputs, false);
|
package/src/zone2.js
CHANGED
|
@@ -225,8 +225,6 @@ class Zone2 extends EventEmitter {
|
|
|
225
225
|
let updated = false;
|
|
226
226
|
|
|
227
227
|
for (const input of inputs) {
|
|
228
|
-
if (this.inputsServices.length >= 85 && !remove) continue;
|
|
229
|
-
|
|
230
228
|
const inputReference = input.reference;
|
|
231
229
|
const savedName = this.savedInputsNames[inputReference] ?? input.name;
|
|
232
230
|
const sanitizedName = await this.functions.sanitizeString(savedName);
|
|
@@ -257,6 +255,9 @@ class Zone2 extends EventEmitter {
|
|
|
257
255
|
updated = true;
|
|
258
256
|
}
|
|
259
257
|
} else {
|
|
258
|
+
// HomeKit allows at most 85 inputs, the limit applies to new inputs only, existing ones are still updated
|
|
259
|
+
if (this.inputsServices.length >= 85) continue;
|
|
260
|
+
|
|
260
261
|
const identifier = this.inputsServices.length + 1;
|
|
261
262
|
inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${inputReference}`);
|
|
262
263
|
inputService.identifier = identifier;
|
package/src/zone3.js
CHANGED
|
@@ -225,8 +225,6 @@ class Zone3 extends EventEmitter {
|
|
|
225
225
|
let updated = false;
|
|
226
226
|
|
|
227
227
|
for (const input of inputs) {
|
|
228
|
-
if (this.inputsServices.length >= 85 && !remove) continue;
|
|
229
|
-
|
|
230
228
|
const inputReference = input.reference;
|
|
231
229
|
const savedName = this.savedInputsNames[inputReference] ?? input.name;
|
|
232
230
|
const sanitizedName = await this.functions.sanitizeString(savedName);
|
|
@@ -257,6 +255,9 @@ class Zone3 extends EventEmitter {
|
|
|
257
255
|
updated = true;
|
|
258
256
|
}
|
|
259
257
|
} else {
|
|
258
|
+
// HomeKit allows at most 85 inputs, the limit applies to new inputs only, existing ones are still updated
|
|
259
|
+
if (this.inputsServices.length >= 85) continue;
|
|
260
|
+
|
|
260
261
|
const identifier = this.inputsServices.length + 1;
|
|
261
262
|
inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${inputReference}`);
|
|
262
263
|
inputService.identifier = identifier;
|