node-red-contrib-ccu 4.5.3 → 4.6.0
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 +19 -0
- package/README.md +9 -0
- package/nodes/ccu-homeassistant.html +10 -0
- package/nodes/ccu-homeassistant.js +2 -0
- package/nodes/lib/hadiscovery.js +32 -8
- package/nodes/locales/de/ccu-homeassistant.html +11 -0
- package/nodes/locales/en-US/ccu-homeassistant.html +11 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ Notable changes to node-red-contrib-ccu. Format follows
|
|
|
5
5
|
user-visible symptom and the cause, not the commit list (the release notes
|
|
6
6
|
append commits automatically).
|
|
7
7
|
|
|
8
|
+
## 4.6.0 (2026-10-22)
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **A device is created in Home Assistant in the room of its channels.** The
|
|
13
|
+
discovery config carries the room as the device's `suggested_area` (task
|
|
14
|
+
18). It did so before only when every channel of the device lay in one and
|
|
15
|
+
the same room: a device with channels in two rooms got no area, and on
|
|
16
|
+
openccu-lite, whose rooms can be nested and whose room list of a channel
|
|
17
|
+
names the parents too (`Küche`, `Erdgeschoss`), no device got one at all.
|
|
18
|
+
Now the area is the room shared by all channels of the device that lie in a
|
|
19
|
+
room, otherwise the room of the lowest-numbered channel that lies in one; a
|
|
20
|
+
channel in several rooms counts with its first (on openccu-lite the most
|
|
21
|
+
specific), and a room of the device itself comes first. The rooms are the
|
|
22
|
+
ReGaHSS's on a CCU and the system's metadata's on openccu-lite. New option
|
|
23
|
+
_Suggest the channel's room as the device's area_ on the node, on by
|
|
24
|
+
default. Home Assistant applies the suggestion only when it first creates
|
|
25
|
+
the device; a room changed later stays as assigned in Home Assistant.
|
|
26
|
+
|
|
8
27
|
## 4.5.3 (2026-10-05)
|
|
9
28
|
|
|
10
29
|
### Fixed
|
package/README.md
CHANGED
|
@@ -81,6 +81,15 @@ publishes its current value (retained) right after the discovery configs and eve
|
|
|
81
81
|
is available without ever having been unreachable. Any message on the node's input republishes both; the example
|
|
82
82
|
wires Home Assistant's birth message (`homeassistant/status`) to it, so a Home Assistant restart gets them again.
|
|
83
83
|
|
|
84
|
+
The device is created in the room of its channels: the discovery config carries it as the device's `suggested_area`
|
|
85
|
+
(Home Assistant's areas are per device, the CCU's rooms per channel). The rule: the room shared by all channels of the
|
|
86
|
+
device that lie in a room, otherwise the room of the lowest-numbered channel that lies in one; a channel in several
|
|
87
|
+
rooms counts with its first - on openccu-lite, where rooms can be nested, with the most specific one - and a room of
|
|
88
|
+
the device itself comes first. The rooms come from the ReGaHSS on a CCU and from the system's metadata on openccu-lite.
|
|
89
|
+
Home Assistant applies the suggestion only when it first creates the device; a room changed later on the CCU stays as
|
|
90
|
+
assigned in Home Assistant (change it there, or delete the device in Home Assistant and republish the discovery). The
|
|
91
|
+
node's option _Suggest the channel's room as the device's area_ (on by default) switches it off.
|
|
92
|
+
|
|
84
93
|
## Dynamic configuration
|
|
85
94
|
|
|
86
95
|
Most nodes can be reconfigured per message: put an object in `msg.config` and its
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
mqttNode: {value: '', required: true},
|
|
8
8
|
prefix: {value: 'homeassistant'},
|
|
9
9
|
generic: {value: true},
|
|
10
|
+
area: {value: true},
|
|
10
11
|
devices: {value: {}},
|
|
11
12
|
},
|
|
12
13
|
inputs: 1,
|
|
@@ -267,6 +268,15 @@
|
|
|
267
268
|
</label>
|
|
268
269
|
</div>
|
|
269
270
|
</div>
|
|
271
|
+
<div class="form-row">
|
|
272
|
+
<label for="node-input-area"></label>
|
|
273
|
+
<div style="width: 70%; display: inline-block; vertical-align: text-top;">
|
|
274
|
+
<label class="ccu-checkbox">
|
|
275
|
+
<input type="checkbox" id="node-input-area">
|
|
276
|
+
<span>Suggest the channel's room as the device's area</span>
|
|
277
|
+
</label>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
270
280
|
|
|
271
281
|
<div class="form-row">
|
|
272
282
|
<b>Devices published to Home Assistant</b>
|
|
@@ -37,6 +37,7 @@ module.exports = function (RED) {
|
|
|
37
37
|
this.mqttNodeId = config.mqttNode;
|
|
38
38
|
this.prefix = String(config.prefix || 'homeassistant').replace(/\/+$/, '');
|
|
39
39
|
this.generic = config.generic !== false;
|
|
40
|
+
this.area = config.area !== false;
|
|
40
41
|
this.devices = config.devices || {};
|
|
41
42
|
this.selected = Object.keys(this.devices).filter(
|
|
42
43
|
(a) => this.devices[a] && this.devices[a].enabled === true,
|
|
@@ -214,6 +215,7 @@ module.exports = function (RED) {
|
|
|
214
215
|
origin: {name: pkg.name, sw: pkg.version, url: pkg.homepage},
|
|
215
216
|
jsonPayloads: mqtt.payloadOutput !== 'plain',
|
|
216
217
|
generic: this.generic,
|
|
218
|
+
area: this.area,
|
|
217
219
|
devices: ccu.metadata.devices,
|
|
218
220
|
selected: this.selected,
|
|
219
221
|
description: (iface, address) => {
|
package/nodes/lib/hadiscovery.js
CHANGED
|
@@ -51,7 +51,9 @@ const BINARY_ONLY_CLASSES = new Set([
|
|
|
51
51
|
* @property {string[]} selected device addresses to publish
|
|
52
52
|
* @property {(iface: string, address: string) => object | undefined} description VALUES description of a channel
|
|
53
53
|
* @property {(address: string) => string | undefined} channelName
|
|
54
|
-
* @property {(address: string) => string[] | undefined} [rooms]
|
|
54
|
+
* @property {(address: string) => string[] | undefined} [rooms] the rooms of a channel (or device), the
|
|
55
|
+
* most specific first where rooms are nested (openccu-lite)
|
|
56
|
+
* @property {boolean} [area] the room becomes the device's `suggested_area` (default true, task 18)
|
|
55
57
|
* @property {(iface: string, address: string, datapoint: string) => string} statusTopicFor
|
|
56
58
|
* @property {(iface: string, address: string, datapoint: string) => string} setTopicFor
|
|
57
59
|
*/
|
|
@@ -184,6 +186,32 @@ function resolveVirtualReceivers(channels) {
|
|
|
184
186
|
* @property {string} topic the status topic the discovery config names in `avty`
|
|
185
187
|
*/
|
|
186
188
|
|
|
189
|
+
/**
|
|
190
|
+
* The room Home Assistant is to suggest as the device's area (task 18). Home
|
|
191
|
+
* Assistant's area is per device, the CCU's rooms are per channel, so: a room
|
|
192
|
+
* every channel that lies in a room shares - the first one in the order of the
|
|
193
|
+
* lowest-numbered such channel - or, when they share none, the first room of
|
|
194
|
+
* the lowest-numbered channel that has one. A room of the device itself (an
|
|
195
|
+
* openccu-lite device may be in a room as a whole) comes first. Home Assistant
|
|
196
|
+
* applies `suggested_area` only when it creates the device; a room changed
|
|
197
|
+
* later stays as assigned in Home Assistant.
|
|
198
|
+
* @param {(address: string) => string[] | undefined} roomsOf
|
|
199
|
+
* @param {string} deviceAddress
|
|
200
|
+
* @param {Array<{ADDRESS: string}>} channels sorted by channel index
|
|
201
|
+
* @returns {string | undefined}
|
|
202
|
+
*/
|
|
203
|
+
function suggestedArea(roomsOf, deviceAddress, channels) {
|
|
204
|
+
const lists = [deviceAddress, ...channels.map((ch) => ch.ADDRESS)]
|
|
205
|
+
.map((address) => roomsOf(address))
|
|
206
|
+
.filter((rooms) => Array.isArray(rooms) && rooms.length > 0);
|
|
207
|
+
if (lists.length === 0) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const [first, ...rest] = lists;
|
|
212
|
+
return first.find((room) => rest.every((rooms) => rooms.includes(room))) || first[0];
|
|
213
|
+
}
|
|
214
|
+
|
|
187
215
|
/**
|
|
188
216
|
* Builds the discovery block of one device.
|
|
189
217
|
* @param {DiscoveryContext} ctx
|
|
@@ -224,12 +252,7 @@ function deviceBlock(ctx, iface, device) {
|
|
|
224
252
|
components[key] = e;
|
|
225
253
|
};
|
|
226
254
|
|
|
227
|
-
const
|
|
228
|
-
for (const ch of channels) {
|
|
229
|
-
for (const r of roomsOf(ch.ADDRESS) || []) {
|
|
230
|
-
rooms.add(r);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
255
|
+
const area = ctx.area === false ? undefined : suggestedArea(roomsOf, device.ADDRESS, channels);
|
|
233
256
|
|
|
234
257
|
const maintenance = channels.find((ch) => ch.index === 0);
|
|
235
258
|
|
|
@@ -783,7 +806,7 @@ function deviceBlock(ctx, iface, device) {
|
|
|
783
806
|
mf: 'eQ-3',
|
|
784
807
|
mdl: device.TYPE,
|
|
785
808
|
sw: device.FIRMWARE ? String(device.FIRMWARE) : undefined,
|
|
786
|
-
sa:
|
|
809
|
+
sa: area,
|
|
787
810
|
}),
|
|
788
811
|
components,
|
|
789
812
|
availability,
|
|
@@ -860,6 +883,7 @@ module.exports = {
|
|
|
860
883
|
discoveryTopic,
|
|
861
884
|
ifaceOf,
|
|
862
885
|
resolveVirtualReceivers,
|
|
886
|
+
suggestedArea,
|
|
863
887
|
deviceBlock,
|
|
864
888
|
devicePayload,
|
|
865
889
|
removalMessage,
|
|
@@ -28,4 +28,15 @@
|
|
|
28
28
|
bis auf diesem Topic ein Wert ankommt. Deshalb veröffentlicht der Node direkt nach den
|
|
29
29
|
Discovery-Konfigurationen den aktuellen UNREACH-Wert jedes Geräts (retained), fragt ihn dazu bei der
|
|
30
30
|
CCU ab und sendet jede Änderung. Ein Gerät, das nie unerreichbar war, ist so trotzdem verfügbar.</p>
|
|
31
|
+
<h3>Raum und Bereich</h3>
|
|
32
|
+
<p>Home Assistant ordnet Geräte Bereichen zu, die CCU Kanäle Räumen. Der Node schlägt Home Assistant den
|
|
33
|
+
Raum der Kanäle als Bereich des Geräts vor (<code>suggested_area</code>): den Raum, den alle Kanäle des
|
|
34
|
+
Geräts teilen, die in einem Raum liegen, sonst den Raum des Kanals mit der niedrigsten Nummer, der in
|
|
35
|
+
einem liegt. Liegt ein Kanal in mehreren Räumen, zählt der erste; auf openccu-lite, wo Räume verschachtelt
|
|
36
|
+
sein können, der speziellste (das Zimmer vor dem Stockwerk), und ein Raum des Geräts selbst geht vor. Die
|
|
37
|
+
Räume kommen auf einer CCU aus der ReGaHSS, auf openccu-lite aus den Metadaten des Systems.</p>
|
|
38
|
+
<p>Home Assistant übernimmt den Vorschlag nur, wenn es das Gerät neu anlegt. Ein später auf der CCU
|
|
39
|
+
geänderter Raum bleibt in Home Assistant, wie er dort zugeordnet ist: dort ändern, oder das Gerät in Home
|
|
40
|
+
Assistant löschen und die Discovery erneut veröffentlichen. Die Option <i>Raum als Bereich vorschlagen</i>
|
|
41
|
+
schaltet den Vorschlag ab.</p>
|
|
31
42
|
</script>
|
|
@@ -28,4 +28,15 @@
|
|
|
28
28
|
a value arrives on that topic. So right after the discovery configs the node publishes each
|
|
29
29
|
device's current UNREACH (retained), asking the CCU for it, and sends every change. A device that
|
|
30
30
|
was never unreachable is available all the same.</p>
|
|
31
|
+
<h3>Room and area</h3>
|
|
32
|
+
<p>Home Assistant assigns devices to areas, the CCU assigns channels to rooms. The node suggests the
|
|
33
|
+
channels' room as the device's area (<code>suggested_area</code>): the room shared by all channels of
|
|
34
|
+
the device that lie in a room, otherwise the room of the lowest-numbered channel that lies in one. A
|
|
35
|
+
channel in several rooms counts with its first; on openccu-lite, where rooms can be nested, with the most
|
|
36
|
+
specific one (the room before the floor), and a room of the device itself comes first. The rooms come
|
|
37
|
+
from the ReGaHSS on a CCU and from the system's metadata on openccu-lite.</p>
|
|
38
|
+
<p>Home Assistant applies the suggestion only when it creates the device. A room changed later on the
|
|
39
|
+
CCU stays as assigned in Home Assistant: change it there, or delete the device in Home Assistant and
|
|
40
|
+
republish the discovery. The option <i>Suggest the channel's room as the device's area</i> switches the
|
|
41
|
+
suggestion off.</p>
|
|
31
42
|
</script>
|