@camstack/addon-provider-homematic 1.2.162 → 1.2.164

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/dist/index.mjs CHANGED
@@ -1,551 +1,2 @@
1
- import { A as object, C as deviceAdminLinkCapability, D as switchCapability, E as motionCapability, HomematicProviderAddon, M as EventCategory, O as DeviceType, S as coverCapability, T as lockControlCapability, _ as homematicConfigSchema, a as decodeClimate, b as buildDeviceAdminUrl, c as toHmClimateMode, d as inferHmDeviceType, f as mapHmChannel, g as homematicAddonConfigSchema, h as buildConnectionFormSchema, i as registerTemperatureCap, j as string, k as createEvent, l as deriveHmEntitySpecs, m as homematicFacades, n as registerHumidityCap, o as decodeCover, p as mapHmDeviceChannels, r as registerPowerMeterCap, s as decodeLock, t as registerAmbientLightCap, u as coldMotionKeepingRise, v as toHomematicOptions, w as eventEmitterCapability, x as climateControlCapability, y as BaseDevice } from "./addon.mjs";
2
- //#region src/homematic-device.ts
3
- /**
4
- * Persisted config every Homematic-backed device carries. `address` is
5
- * the CCU device address (e.g. `0001D3C99AB123`) — the dispatch key the
6
- * device uses to resolve its live `HmDevice` from the facade and to
7
- * filter inbound `valueChanged` events to its own datapoints.
8
- */
9
- var homematicDeviceSchema = object({
10
- address: string().min(1).describe("CCU device address"),
11
- /** The registry broker id this device belongs to (e.g. `hm_001`).
12
- * Absent on devices persisted before the multi-CCU migration; those
13
- * fall back to the primary broker via `homematicFacades.get(null)`. */
14
- brokerId: string().optional()
15
- });
16
- /** Cold-start defaults so reads never miss a slice before the first push. */
17
- var COLD_SWITCH = {
18
- on: false,
19
- lastChangedAt: 0
20
- };
21
- var COLD_COVER = {
22
- state: "closed",
23
- position: null,
24
- tiltPosition: null,
25
- lastChangedAt: 0
26
- };
27
- var COLD_LOCK = {
28
- state: "unlocked",
29
- lastChangedAt: 0
30
- };
31
- var COLD_MOTION = {
32
- detected: false,
33
- lastDetectedAt: null,
34
- autoClearAfterMs: null
35
- };
36
- var COLD_CLIMATE = {
37
- mode: "auto",
38
- availableModes: [...[
39
- "off",
40
- "heat",
41
- "auto"
42
- ]],
43
- fanMode: "",
44
- availableFanModes: [],
45
- preset: "",
46
- availablePresets: ["boost"],
47
- target: null,
48
- targetHigh: null,
49
- targetLow: null,
50
- targetHumidity: null,
51
- currentTemp: null,
52
- currentHumidity: null,
53
- swingVertical: null,
54
- swingHorizontal: null,
55
- lastFetchedAt: 0
56
- };
57
- /**
58
- * The standard HmIP key-channel datapoint vocabulary, used VERBATIM (NO
59
- * normalization) as the `event-emitter` event types. These are the native
60
- * uppercase tokens a `KEY_TRANSCEIVER` / `KEY_TRANSMITTER` / `VIRTUAL_KEY`
61
- * / `KEY` channel emits on the facade `valueChanged` stream (via HA they
62
- * surface lowercased as `press_short` etc.; natively they are uppercase):
63
- * - PRESS_SHORT single short press
64
- * - PRESS_LONG long press (repeats while held on some firmware)
65
- * - PRESS_LONG_START long-press begin
66
- * - PRESS_LONG_RELEASE long-press end
67
- * - PRESS_CONT continuous-press tick while held
68
- * Used both as the seed vocabulary fallback and as the fire-detection set
69
- * (a non-key datapoint such as a maintenance LOW_BAT is ignored).
70
- */
71
- var HM_KEY_PRESS_DATAPOINTS = [
72
- "PRESS_SHORT",
73
- "PRESS_LONG",
74
- "PRESS_LONG_START",
75
- "PRESS_LONG_RELEASE",
76
- "PRESS_CONT"
77
- ];
78
- /** Set form for O(1) fire-detection membership tests. */
79
- var HM_KEY_PRESS_SET = new Set(HM_KEY_PRESS_DATAPOINTS);
80
- /** Cold-start event-emitter slice; vocabulary supplied per-device. */
81
- function coldEventEmitter(eventTypes) {
82
- return {
83
- eventTypes: [...eventTypes],
84
- lastEvent: null,
85
- eventCountSinceStart: 0
86
- };
87
- }
88
- /**
89
- * Collect the union of native `PRESS_*` datapoints a device's key channels
90
- * support, VERBATIM. Reads each key channel's `dataPoints` (when the
91
- * facade enumerated them) and keeps only the recognised press tokens. Falls
92
- * back to the standard HmIP key vocabulary when no datapoints were
93
- * enumerated, so the cap always advertises a non-empty vocabulary.
94
- */
95
- function keyEventVocabulary(keyChannels) {
96
- const found = /* @__PURE__ */ new Set();
97
- for (const channel of keyChannels) for (const dp of channel.dataPoints ?? []) if (HM_KEY_PRESS_SET.has(dp.parameter)) found.add(dp.parameter);
98
- if (found.size === 0) return HM_KEY_PRESS_DATAPOINTS;
99
- return HM_KEY_PRESS_DATAPOINTS.filter((dp) => found.has(dp));
100
- }
101
- /**
102
- * The CamStack cap names that are ACTUATORS — driven by the library's typed
103
- * custom entities (correct channel), NOT by the raw mapped channels. The
104
- * raw-channel registration loop skips these so they are not double-registered
105
- * against the wrong channel.
106
- */
107
- var ACTUATOR_CAP_NAMES = new Set([
108
- "switch",
109
- "cover",
110
- "light",
111
- "lock-control",
112
- "climate-control"
113
- ]);
114
- /**
115
- * Map a `nodehomematic` custom-entity `kind` to its CamStack actuator cap
116
- * name. The union is discriminated by `kind`; the exhaustive switch needs no
117
- * cast. Returns `null` for kinds with no actuator cap wired yet.
118
- */
119
- function actuatorCapForEntityKind(kind) {
120
- switch (kind) {
121
- case "switch": return "switch";
122
- case "cover":
123
- case "blind": return "cover";
124
- case "light": return "light";
125
- case "lock": return "lock-control";
126
- case "climate": return "climate-control";
127
- default: return null;
128
- }
129
- }
130
- /** Parse the numeric channel index suffix (`:N`) from a channel address.
131
- * Returns the raw suffix string (kept as a string for the event payload,
132
- * mirroring the CCU's `device:index` addressing). Null when absent. */
133
- function channelIndexFromAddress(channelAddress) {
134
- const idx = channelAddress.lastIndexOf(":");
135
- if (idx === -1) return null;
136
- const suffix = channelAddress.slice(idx + 1);
137
- return suffix.length > 0 ? suffix : null;
138
- }
139
- /**
140
- * One CamStack device per Homematic device. Maps the device's channels to
141
- * CamStack caps (via `homematic-mapping.ts`) and wires each cap's
142
- * get/set surface to the `nodehomematic` facade's typed helpers
143
- * (`switchTurnOn` / `coverSetPosition` / `climateSetTemperature` /
144
- * `lockLock`) and raw `getValue`/`setValue` for read-only sensors.
145
- *
146
- * Status flows back via the facade `valueChanged` stream: each push is
147
- * decoded by parameter name into the relevant cap's runtime-state slice
148
- * (see `homematic-decode.ts` for the pure decoders).
149
- *
150
- * MAPPED (control + status):
151
- * - switch (HmIP-BSM / BS2)
152
- * - cover (HmIP-BROLL)
153
- * - climate-control (HmIP-eTRV-C / BWTH / STHD / WTH-2 / HEATING)
154
- * - lock-control (HmIP-DLD)
155
- * MAPPED (read-only sensors):
156
- * - temperature-sensor / humidity-sensor (climate channels)
157
- * - motion + ambient-light-sensor (HmIP-SMI)
158
- * - power-meter (HmIP-BSM / ESI energy)
159
- * MAPPED (push-only events):
160
- * - event-emitter (HmIP-WRC* wall switches / HmIP-RC* remotes / key
161
- * channels) — one singleton cap over all key channels;
162
- * native PRESS_* datapoints fired verbatim with the
163
- * button index in `data.channel`.
164
- */
165
- var HomematicDevice = class extends BaseDevice {
166
- features = [];
167
- /** CCU device address — dispatch + event-filter key. */
168
- address;
169
- /**
170
- * Registry broker id this device belongs to. Undefined for devices
171
- * persisted before the multi-CCU migration; those resolve to the primary
172
- * broker via `homematicFacades.get(null)`.
173
- */
174
- brokerId;
175
- /** Unsubscribe for the facade `valueChanged` listener. */
176
- valueChangedUnsub = null;
177
- /** Channel address that owns each registered cap (for decode routing). */
178
- capChannels = /* @__PURE__ */ new Map();
179
- /** Native press-datapoint vocabulary for this device's key channels
180
- * (verbatim, NO normalization). Empty when the device has no key
181
- * channel — gates the `event-emitter` fire path. */
182
- eventTypes = [];
183
- /** Monotonic event sequence — two identical presses still produce
184
- * distinct slice writes / bus emissions. */
185
- #eventSeq = 0;
186
- /** Count of key presses observed since this device instance started. */
187
- #eventCount = 0;
188
- constructor(ctx) {
189
- const persisted = ctx.persistedConfig ?? {};
190
- const address = persisted.address;
191
- if (typeof address !== "string" || address.length === 0) throw new Error(`HomematicDevice: missing address in persisted config (id=${ctx.id})`);
192
- super(ctx, homematicDeviceSchema, { type: ctx.deviceMeta.type });
193
- this.address = address;
194
- this.brokerId = typeof persisted.brokerId === "string" ? persisted.brokerId : void 0;
195
- this.registerAdminLinkCap();
196
- this.registerCapabilities();
197
- this.attachValueChangedListener();
198
- this.updateSourceInfo({
199
- id: this.address,
200
- system: "homematic"
201
- });
202
- }
203
- /** The live facade for this device's CCU, or null when disconnected. */
204
- get facade() {
205
- return homematicFacades.get(this.brokerId ?? null);
206
- }
207
- /** Resolve this device's current `HmDevice` snapshot from the facade. */
208
- resolveHmDevice() {
209
- const facade = this.facade;
210
- if (!facade) return null;
211
- return facade.devices().find((d) => d.address === this.address) ?? null;
212
- }
213
- /**
214
- * device-admin-link — a Homematic device has no web server; the CCU3 /
215
- * RaspberryMatic WebUI is where it is configured. The CCU has no stable
216
- * per-device deep link (the WebUI addresses devices through a session-bound
217
- * frameset), so every device on a CCU gets that CCU's root, labelled for
218
- * what it is. A device persisted before multi-CCU support carries no
219
- * `brokerId` and cannot be attributed to a CCU at all — it answers null.
220
- *
221
- * Registered OUTSIDE `registerCapabilities`, which returns early when the
222
- * facade has not yet seen the device: whether the CCU has a WebUI does not
223
- * depend on that, and an early return there used to be how a device ended up
224
- * with no caps at all.
225
- */
226
- registerAdminLinkCap() {
227
- const brokerId = this.brokerId;
228
- this.ctx.registerNativeCap(deviceAdminLinkCapability, { getAdminLink: async ({ deviceId }) => {
229
- if (deviceId !== this.id) throw new Error(`HomematicDevice: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
230
- if (brokerId === void 0) {
231
- this.ctx.logger.debug("homematic admin link unavailable: no brokerId on device", {
232
- tags: { deviceId: this.id },
233
- meta: { address: this.address }
234
- });
235
- return null;
236
- }
237
- const cfg = await this.ctx.api.broker.getBrokerConfig.query({ id: brokerId });
238
- const host = typeof cfg?.["host"] === "string" ? cfg["host"] : "";
239
- const built = buildDeviceAdminUrl({
240
- host,
241
- port: null,
242
- scheme: cfg?.["tls"] === true ? "https" : "http",
243
- path: "/"
244
- });
245
- if (!built.ok) {
246
- this.ctx.logger.debug("homematic admin link unavailable", {
247
- tags: { deviceId: this.id },
248
- meta: {
249
- reason: built.reason,
250
- brokerId
251
- }
252
- });
253
- return null;
254
- }
255
- return {
256
- url: built.url,
257
- label: "CCU WebUI",
258
- target: "integration",
259
- host
260
- };
261
- } });
262
- }
263
- registerCapabilities() {
264
- const hmDevice = this.resolveHmDevice();
265
- if (!hmDevice) {
266
- this.ctx.logger.warn("HomematicDevice: device not found on facade at construction", { meta: { address: this.address } });
267
- return;
268
- }
269
- this.registerActuatorCapsFromCustomEntities();
270
- const keyChannels = [];
271
- for (const { channel, mapping } of mapHmDeviceChannels(hmDevice)) for (const capName of mapping.capabilities) {
272
- if (capName === "event-emitter") {
273
- keyChannels.push(channel);
274
- continue;
275
- }
276
- if (ACTUATOR_CAP_NAMES.has(capName)) continue;
277
- this.registerCap(capName, channel.address);
278
- }
279
- if (keyChannels.length > 0) this.registerEventEmitterCap(keyChannels);
280
- }
281
- /**
282
- * Register the device's ACTUATOR caps (switch / cover / light / lock /
283
- * climate-control) from the library's typed custom entities. Each
284
- * `HmCustomEntity` carries the EXACT channel the lib bound the entity to —
285
- * the channel the facade control helpers require. The cap channel stored in
286
- * `capChannels` is therefore the custom-entity channel, so `coverOpen`
287
- * (etc.) dispatch to the correct one.
288
- *
289
- * When `customEntities()` is empty (facade not fully built yet at
290
- * construction time), this is a no-op beyond a debug note; the actuator
291
- * caps register on the next reconcile/re-probe once the entities exist.
292
- */
293
- registerActuatorCapsFromCustomEntities() {
294
- const entities = this.facade?.customEntities()?.filter((e) => e.device === this.address) ?? [];
295
- if (entities.length === 0) {
296
- this.ctx.logger.debug("HomematicDevice: no custom entities at registration; actuator caps deferred to next reconcile", { meta: { address: this.address } });
297
- return;
298
- }
299
- for (const entity of entities) {
300
- const capName = actuatorCapForEntityKind(entity.kind);
301
- if (capName === null) continue;
302
- this.registerCap(capName, entity.channel);
303
- }
304
- }
305
- /** Dispatch one cap registration to its concrete wiring. */
306
- registerCap(capName, channelAddress) {
307
- this.capChannels.set(capName, channelAddress);
308
- switch (capName) {
309
- case "switch": return this.registerSwitchCap(channelAddress);
310
- case "cover": return this.registerCoverCap(channelAddress);
311
- case "climate-control": return this.registerClimateCap(channelAddress);
312
- case "lock-control": return this.registerLockCap(channelAddress);
313
- case "motion": return this.registerMotionCap();
314
- case "ambient-light-sensor": return registerAmbientLightCap(this.ctx, this.runtimeState);
315
- case "temperature-sensor": return registerTemperatureCap(this.ctx, this.runtimeState);
316
- case "humidity-sensor": return registerHumidityCap(this.ctx, this.runtimeState);
317
- case "power-meter": return registerPowerMeterCap(this.ctx, this.runtimeState);
318
- default:
319
- this.ctx.logger.debug("HomematicDevice: cap not yet wired", { meta: {
320
- capName,
321
- channelAddress,
322
- address: this.address
323
- } });
324
- return;
325
- }
326
- }
327
- registerSwitchCap(channelAddress) {
328
- this.ctx.registerNativeCap(switchCapability, {
329
- getStatus: async () => this.runtimeState.getCapState("switch") ?? COLD_SWITCH,
330
- setState: async ({ on }) => {
331
- const facade = this.requireFacade();
332
- if (on) await facade.switchTurnOn(this.address, channelAddress);
333
- else await facade.switchTurnOff(this.address, channelAddress);
334
- }
335
- });
336
- this.runtimeState.setCapState("switch", COLD_SWITCH);
337
- }
338
- registerCoverCap(channelAddress) {
339
- this.ctx.registerNativeCap(coverCapability, {
340
- getStatus: async () => this.runtimeState.getCapState("cover") ?? COLD_COVER,
341
- open: async () => {
342
- await this.requireFacade().coverOpen(this.address, channelAddress);
343
- },
344
- close: async () => {
345
- await this.requireFacade().coverClose(this.address, channelAddress);
346
- },
347
- stop: async () => {
348
- await this.requireFacade().coverStop(this.address, channelAddress);
349
- },
350
- setPosition: async ({ position }) => {
351
- await this.requireFacade().coverSetPosition(this.address, channelAddress, position);
352
- },
353
- setTiltPosition: async () => this.unsupported("cover tilt")
354
- });
355
- this.runtimeState.setCapState("cover", COLD_COVER);
356
- }
357
- registerClimateCap(channelAddress) {
358
- this.ctx.registerNativeCap(climateControlCapability, {
359
- getStatus: async () => this.runtimeState.getCapState("climate-control") ?? COLD_CLIMATE,
360
- setMode: async ({ mode }) => {
361
- await this.requireFacade().climateSetMode(this.address, channelAddress, toHmClimateMode(mode));
362
- },
363
- setTarget: async ({ target }) => {
364
- await this.requireFacade().climateSetTemperature(this.address, channelAddress, target);
365
- },
366
- setPreset: async ({ preset }) => {
367
- await this.requireFacade().climateSetBoost(this.address, channelAddress, preset === "boost");
368
- },
369
- setFanMode: async () => this.unsupported("climate fan mode"),
370
- setTargetRange: async () => this.unsupported("climate dual setpoint"),
371
- setTargetHumidity: async () => this.unsupported("climate humidity control"),
372
- setSwingVertical: async () => this.unsupported("climate vertical swing"),
373
- setSwingHorizontal: async () => this.unsupported("climate horizontal swing")
374
- });
375
- this.runtimeState.setCapState("climate-control", COLD_CLIMATE);
376
- }
377
- registerLockCap(channelAddress) {
378
- this.ctx.registerNativeCap(lockControlCapability, {
379
- getStatus: async () => this.runtimeState.getCapState("lock-control") ?? COLD_LOCK,
380
- lock: async () => {
381
- await this.requireFacade().lockLock(this.address, channelAddress);
382
- },
383
- unlock: async () => {
384
- await this.requireFacade().lockUnlock(this.address, channelAddress);
385
- },
386
- open: async () => {
387
- await this.requireFacade().lockOpen(this.address, channelAddress);
388
- }
389
- });
390
- this.runtimeState.setCapState("lock-control", COLD_LOCK);
391
- }
392
- registerMotionCap() {
393
- this.ctx.registerNativeCap(motionCapability, {
394
- getStatus: async () => this.runtimeState.getCapState("motion") ?? COLD_MOTION,
395
- isDetected: async () => (this.runtimeState.getCapState("motion") ?? COLD_MOTION).detected
396
- });
397
- this.runtimeState.setCapState("motion", coldMotionKeepingRise(this.runtimeState.getCapState("motion")));
398
- }
399
- registerEventEmitterCap(keyChannels) {
400
- this.eventTypes = keyEventVocabulary(keyChannels);
401
- this.ctx.registerNativeCap(eventEmitterCapability, { getStatus: async () => this.runtimeState.getCapState("event-emitter") ?? coldEventEmitter(this.eventTypes) });
402
- this.runtimeState.setCapState("event-emitter", coldEventEmitter(this.eventTypes));
403
- }
404
- attachValueChangedListener() {
405
- const facade = this.facade;
406
- if (!facade) return;
407
- this.valueChangedUnsub = facade.on("valueChanged", (event) => {
408
- if (event.device !== this.address) return;
409
- this.handleValueChanged(event);
410
- });
411
- }
412
- /**
413
- * Translate an inbound facade `valueChanged` into the relevant cap
414
- * slice, keyed by parameter name. Only updates caps the device
415
- * actually registered (`capChannels`).
416
- */
417
- handleValueChanged(event) {
418
- const now = event.ts || Date.now();
419
- const { parameter, value } = event;
420
- if (this.capChannels.has("switch") && parameter === "STATE" && typeof value === "boolean") this.runtimeState.setCapState("switch", {
421
- on: value,
422
- lastChangedAt: now
423
- });
424
- if (this.capChannels.has("cover")) {
425
- const next = decodeCover(this.runtimeState.getCapState("cover") ?? COLD_COVER, parameter, value, now);
426
- if (next) this.runtimeState.setCapState("cover", next);
427
- }
428
- if (this.capChannels.has("lock-control")) {
429
- const next = decodeLock(parameter, value, now);
430
- if (next) this.runtimeState.setCapState("lock-control", next);
431
- }
432
- if (this.capChannels.has("climate-control")) {
433
- const next = decodeClimate(this.runtimeState.getCapState("climate-control") ?? COLD_CLIMATE, parameter, value, now);
434
- if (next) this.runtimeState.setCapState("climate-control", next);
435
- }
436
- if (this.capChannels.has("motion") && parameter === "MOTION" && typeof value === "boolean") this.runtimeState.setCapState("motion", {
437
- detected: value,
438
- lastDetectedAt: value ? now : this.runtimeState.getCapState("motion")?.lastDetectedAt ?? null,
439
- autoClearAfterMs: null
440
- });
441
- if (this.eventTypes.length > 0) this.decodeKeyPress(event, now);
442
- this.decodeSensorPush(parameter, value, now);
443
- }
444
- /**
445
- * Translate a key-channel `PRESS_*` datapoint into an `EventFire`: emit
446
- * `EventCategory.EventEmitted` on the bus and write the `event-emitter`
447
- * slice. The `eventType` is the native datapoint name VERBATIM (no
448
- * normalization); the button index (`:N` suffix of the channel address)
449
- * travels in `data.channel`. Non-key datapoints and value resets (the
450
- * key datapoint dropping back to `false`) are ignored.
451
- */
452
- decodeKeyPress(event, now) {
453
- const { parameter, value, channel } = event;
454
- if (!HM_KEY_PRESS_SET.has(parameter)) return;
455
- if (typeof value === "boolean" && value === false) return;
456
- const channelIndex = channelIndexFromAddress(channel);
457
- const fire = {
458
- deviceId: this.id,
459
- eventType: parameter,
460
- data: channelIndex !== null ? { channel: channelIndex } : {},
461
- timestamp: now,
462
- seq: ++this.#eventSeq
463
- };
464
- this.ctx.eventBus.emit(createEvent(EventCategory.EventEmitted, {
465
- type: "device",
466
- id: this.stableId,
467
- deviceId: this.id,
468
- addonId: this.ctx.deviceMeta.addonId
469
- }, fire));
470
- const slice = {
471
- eventTypes: [...this.eventTypes],
472
- lastEvent: fire,
473
- eventCountSinceStart: ++this.#eventCount
474
- };
475
- this.runtimeState.setCapState("event-emitter", slice);
476
- }
477
- /** Decode read-only sensor pushes into their slices. */
478
- decodeSensorPush(parameter, value, now) {
479
- if (typeof value !== "number") return;
480
- if (this.capChannels.has("temperature-sensor") && parameter === "ACTUAL_TEMPERATURE") this.runtimeState.setCapState("temperature-sensor", {
481
- temperature: value,
482
- lastFetchedAt: now
483
- });
484
- if (this.capChannels.has("humidity-sensor") && parameter === "HUMIDITY") this.runtimeState.setCapState("humidity-sensor", {
485
- humidity: value,
486
- lastFetchedAt: now
487
- });
488
- if (this.capChannels.has("ambient-light-sensor") && parameter === "ILLUMINATION") this.runtimeState.setCapState("ambient-light-sensor", {
489
- illuminance: Math.max(0, value),
490
- lastFetchedAt: now
491
- });
492
- this.decodePowerPush(parameter, value, now);
493
- }
494
- /** Decode HmIP energy-meter parameters into the power-meter slice. */
495
- decodePowerPush(parameter, value, now) {
496
- if (!this.capChannels.has("power-meter")) return;
497
- const prev = this.runtimeState.getCapState("power-meter") ?? { lastFetchedAt: 0 };
498
- switch (parameter) {
499
- case "POWER":
500
- this.runtimeState.setCapState("power-meter", {
501
- ...prev,
502
- power: value,
503
- lastFetchedAt: now
504
- });
505
- return;
506
- case "ENERGY_COUNTER":
507
- this.runtimeState.setCapState("power-meter", {
508
- ...prev,
509
- energy: value / 1e3,
510
- lastFetchedAt: now
511
- });
512
- return;
513
- case "VOLTAGE":
514
- this.runtimeState.setCapState("power-meter", {
515
- ...prev,
516
- voltage: value,
517
- lastFetchedAt: now
518
- });
519
- return;
520
- case "CURRENT":
521
- this.runtimeState.setCapState("power-meter", {
522
- ...prev,
523
- current: value / 1e3,
524
- lastFetchedAt: now
525
- });
526
- return;
527
- default: return;
528
- }
529
- }
530
- /** Reject an operation the underlying HmIP hardware does not support. */
531
- unsupported(surface) {
532
- throw new Error(`HomematicDevice: ${surface} not supported (address=${this.address})`);
533
- }
534
- /** Narrow the optional facade to a present one or throw a clear error. */
535
- requireFacade() {
536
- const facade = this.facade;
537
- if (!facade) throw new Error(`HomematicDevice: CCU connection unavailable (address=${this.address})`);
538
- return facade;
539
- }
540
- async removeDevice() {
541
- if (this.valueChangedUnsub) {
542
- try {
543
- this.valueChangedUnsub();
544
- } catch {}
545
- this.valueChangedUnsub = null;
546
- }
547
- }
548
- };
549
- DeviceType.Container, DeviceType.Switch, DeviceType.Cover, DeviceType.Thermostat, DeviceType.Lock, DeviceType.Sensor, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic;
550
- //#endregion
551
- export { HomematicDevice, HomematicProviderAddon, buildConnectionFormSchema, deriveHmEntitySpecs, homematicAddonConfigSchema, homematicConfigSchema, homematicDeviceSchema, inferHmDeviceType, mapHmChannel, mapHmDeviceChannels, toHomematicOptions };
1
+ import { HomematicProviderAddon, a as buildConnectionFormSchema, c as homematicGatewayDeviceSchema, i as mapHmDeviceChannels, l as toHomematicOptions, n as inferHmDeviceType, o as connectionFromSettings, r as mapHmChannel, s as homematicConfigSchema, t as deriveHmEntitySpecs } from "./addon.mjs";
2
+ export { HomematicProviderAddon, buildConnectionFormSchema, connectionFromSettings, deriveHmEntitySpecs, homematicConfigSchema, homematicGatewayDeviceSchema, inferHmDeviceType, mapHmChannel, mapHmDeviceChannels, toHomematicOptions };
package/i18n/en.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "globalSettings.s_hm-broker.title": "Homematic broker",
3
- "globalSettings.s_hm-broker.brokerHelp.label": "Brokers are managed separately",
4
2
  "connection.s_connection.title": "CCU connection",
3
+ "connection.s_connection.__homematic-discovered.label": "CCU",
5
4
  "connection.s_connection.host.label": "Host / IP",
5
+ "connection.s_connection.host.description": "Filled when one CCU is found. Type the address if it is not.",
6
6
  "connection.s_interfaces.title": "Interfaces",
7
7
  "connection.s_interfaces.description": "CCU-Jack and CUxD are not supported.",
8
8
  "connection.s_interfaces.enableHmipRf.label": "Enable Homematic IP",
package/i18n/it.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "globalSettings.s_hm-broker.title": "Broker Homematic",
3
- "globalSettings.s_hm-broker.brokerHelp.label": "I broker si gestiscono separatamente",
4
2
  "connection.s_connection.title": "Connessione alla CCU",
3
+ "connection.s_connection.__homematic-discovered.label": "CCU",
5
4
  "connection.s_connection.host.label": "Host / IP",
5
+ "connection.s_connection.host.description": "Compilato quando viene trovata una CCU. Digita l'indirizzo se non viene trovata.",
6
6
  "connection.s_interfaces.title": "Interfacce",
7
7
  "connection.s_interfaces.description": "CCU-Jack e CUxD non sono supportati.",
8
8
  "connection.s_interfaces.enableHmipRf.label": "Attiva Homematic IP",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.162",
3
+ "version": "1.2.164",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",
@@ -39,12 +39,12 @@
39
39
  "category": "providers",
40
40
  "name": "Homematic",
41
41
  "version": "0.1.0",
42
- "description": "Connect to a Homematic CCU3 / RaspberryMatic hub and adopt its switches, covers, lights, locks, thermostats and sensors",
42
+ "description": "Adopt a Homematic CCU3 / RaspberryMatic (as a Gateway device) and its switches, covers, lights, locks, thermostats and sensors, from an integration over the local network",
43
43
  "entry": "./dist/addon.js",
44
44
  "icon": "assets/icon.svg",
45
45
  "color": "#e2001a",
46
46
  "instanceMode": "multiple",
47
- "brokerKind": "homematic",
47
+ "mode": "account",
48
48
  "execution": {
49
49
  "placement": "hub-only"
50
50
  },
@@ -53,10 +53,10 @@
53
53
  "name": "device-provider"
54
54
  },
55
55
  {
56
- "name": "broker"
56
+ "name": "device-adoption"
57
57
  },
58
58
  {
59
- "name": "device-adoption"
59
+ "name": "connection-test"
60
60
  }
61
61
  ]
62
62
  }