@camstack/addon-provider-bosch 0.1.32 → 0.1.33

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.
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-CStkg32S.js");
3
- const require_account_integration_offline = require("../account-integration-offline-B9C4OAHn.js");
2
+ const require_dist = require("../dist-BHYWG3lC.js");
3
+ const require_account_integration_offline = require("../account-integration-offline-BueUSEv3.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_tls = require("node:tls");
6
6
  let node_zlib = require("node:zlib");
@@ -1,5 +1,5 @@
1
- import { A as customAction, B as record, D as DeviceType, E as DeviceRole, F as array, H as union, I as boolean, L as literal, M as isSecretMarker, N as isSecretMarkerLike, O as errMsg, P as _enum, S as runningStateCapability, U as unknown, V as string, W as EventCategory, _ as ingestReading, b as quantityFromUnit, c as binaryCapability, d as consumablesCapability, f as deviceAdoptionCapability, h as eventEmitterCapability, i as RUNNING_STATE_COLD, j as defineCustomActions, k as createEvent, m as enumSensorCapability, n as BaseDeviceProvider, o as VendorLoginProxy, p as dishwasherCapability, r as DishwasherOperationStateSchema, t as BaseDevice, u as connectionTestCapability, v as numericSensorCapability, x as runningFromDishwasherOperation, y as powerMeterCapability, z as object } from "../dist-n4Mk3Uu1.mjs";
2
- import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-CSmJOed1.mjs";
1
+ import { A as customAction, B as record, D as DeviceType, E as DeviceRole, F as array, H as union, I as boolean, L as literal, M as isSecretMarker, N as isSecretMarkerLike, O as errMsg, P as _enum, S as runningStateCapability, U as unknown, V as string, W as EventCategory, _ as ingestReading, b as quantityFromUnit, c as binaryCapability, d as consumablesCapability, f as deviceAdoptionCapability, h as eventEmitterCapability, i as RUNNING_STATE_COLD, j as defineCustomActions, k as createEvent, m as enumSensorCapability, n as BaseDeviceProvider, o as VendorLoginProxy, p as dishwasherCapability, r as DishwasherOperationStateSchema, t as BaseDevice, u as connectionTestCapability, v as numericSensorCapability, x as runningFromDishwasherOperation, y as powerMeterCapability, z as object } from "../dist-hH1ghdQ4.mjs";
2
+ import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-Bpa-ZlLg.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { connect } from "node:tls";
5
5
  import { inflateRawSync } from "node:zlib";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-CStkg32S.js");
3
- const require_account_integration_offline = require("../account-integration-offline-B9C4OAHn.js");
2
+ const require_dist = require("../dist-BHYWG3lC.js");
3
+ const require_account_integration_offline = require("../account-integration-offline-BueUSEv3.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  //#region src/homecom/config.ts
6
6
  var HOMECOM_BRANDS = ["bosch", "buderus"];
@@ -1,5 +1,5 @@
1
- import { A as customAction, D as DeviceType, F as array, I as boolean, L as literal, M as isSecretMarker, N as isSecretMarkerLike, O as errMsg, P as _enum, R as number, T as waterHeaterCapability, V as string, W as EventCategory, f as deviceAdoptionCapability, j as defineCustomActions, l as climateControlCapability, n as BaseDeviceProvider, o as VendorLoginProxy, t as BaseDevice, u as connectionTestCapability, z as object } from "../dist-n4Mk3Uu1.mjs";
2
- import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-CSmJOed1.mjs";
1
+ import { A as customAction, D as DeviceType, F as array, I as boolean, L as literal, M as isSecretMarker, N as isSecretMarkerLike, O as errMsg, P as _enum, R as number, T as waterHeaterCapability, V as string, W as EventCategory, f as deviceAdoptionCapability, j as defineCustomActions, l as climateControlCapability, n as BaseDeviceProvider, o as VendorLoginProxy, t as BaseDevice, u as connectionTestCapability, z as object } from "../dist-hH1ghdQ4.mjs";
2
+ import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-Bpa-ZlLg.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  //#region src/homecom/config.ts
5
5
  var HOMECOM_BRANDS = ["bosch", "buderus"];
package/dist/shc/addon.js CHANGED
@@ -2,14 +2,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  //#region \0rolldown/runtime.js
3
3
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
4
4
  //#endregion
5
- const require_dist = require("../dist-CStkg32S.js");
5
+ const require_dist = require("../dist-BHYWG3lC.js");
6
6
  let node_crypto = require("node:crypto");
7
7
  let node_https = require("node:https");
8
8
  let node_child_process = require("node:child_process");
9
9
  let node_fs = require("node:fs");
10
10
  let node_os = require("node:os");
11
11
  let node_path = require("node:path");
12
- let node_dgram = require("node:dgram");
13
12
  let node_net = require("node:net");
14
13
  //#region src/shc/custom-actions.ts
15
14
  var boschShcActions = require_dist.defineCustomActions({
@@ -332,78 +331,18 @@ async function registerShcClient(deps) {
332
331
  }
333
332
  //#endregion
334
333
  //#region src/shc/shc-controller-discovery.ts
335
- /** `prefilledConfig` flag: the scan opens the integration form instead of adopting a device. */
336
- var SHC_SETUP_INTEGRATION_KEY = "setupIntegration";
337
- /** Top bit of the question class: ask for a unicast reply (RFC 6762). */
338
- var MDNS_QU_CLASS = 32769;
339
- var SHC_MDNS_SERVICE = "_http._tcp.local";
334
+ /** The controller announces itself as an `_http._tcp` service named `Bosch SHC`. */
335
+ var SHC_MDNS_SERVICE = "_http._tcp";
340
336
  var SHC_CONTROLLER_NAME_PREFIX = "Bosch SHC";
341
- var SHC_DISCOVERY_TIMEOUT_MS = 1500;
342
- var MDNS_PORT = 5353;
343
- var MDNS_ADDRESS = "224.0.0.251";
344
- var PTR = 12;
345
- var A = 1;
346
- var SRV = 33;
337
+ /** How the scan engine finds a controller: one mDNS browse, filtered on the instance name. */
338
+ var SHC_DISCOVERY_MODES = [{
339
+ kind: "mdns",
340
+ serviceType: SHC_MDNS_SERVICE,
341
+ instancePattern: `^${SHC_CONTROLLER_NAME_PREFIX}`
342
+ }];
347
343
  function isShcControllerName(name) {
348
344
  return (name.split("._")[0] ?? name).toLowerCase().startsWith(SHC_CONTROLLER_NAME_PREFIX.toLowerCase());
349
345
  }
350
- function readName(buf, start) {
351
- const labels = [];
352
- let pos = start;
353
- let end = start;
354
- let jumped = false;
355
- for (let hops = 0; hops < 16; hops++) {
356
- if (pos < 0 || pos >= buf.length) return null;
357
- const len = buf[pos] ?? 0;
358
- if (len === 0) {
359
- if (!jumped) end = pos + 1;
360
- return {
361
- name: labels.join("."),
362
- end
363
- };
364
- }
365
- if ((len & 192) === 192) {
366
- if (pos + 1 >= buf.length) return null;
367
- if (!jumped) end = pos + 2;
368
- jumped = true;
369
- pos = (len & 63) << 8 | (buf[pos + 1] ?? 0);
370
- continue;
371
- }
372
- if ((len & 192) !== 0 || pos + 1 + len > buf.length) return null;
373
- labels.push(buf.subarray(pos + 1, pos + 1 + len).toString("utf8"));
374
- pos += 1 + len;
375
- if (!jumped) end = pos;
376
- }
377
- return null;
378
- }
379
- function readRecords(buf, offset, count) {
380
- const records = [];
381
- let pos = offset;
382
- for (let i = 0; i < count; i++) {
383
- const name = readName(buf, pos);
384
- if (name === null || name.end + 10 > buf.length) return null;
385
- const type = buf.readUInt16BE(name.end);
386
- const dataLength = buf.readUInt16BE(name.end + 8);
387
- const dataOffset = name.end + 10;
388
- if (dataOffset + dataLength > buf.length) return null;
389
- records.push({
390
- name: name.name,
391
- type,
392
- message: buf,
393
- dataOffset,
394
- dataLength
395
- });
396
- pos = dataOffset + dataLength;
397
- }
398
- return {
399
- records,
400
- next: pos
401
- };
402
- }
403
- function ipv4(buf, offset, length) {
404
- if (length !== 4 || offset + 4 > buf.length) return null;
405
- return `${buf[offset] ?? 0}.${buf[offset + 1] ?? 0}.${buf[offset + 2] ?? 0}.${buf[offset + 3] ?? 0}`;
406
- }
407
346
  /** Identity used to recognise a controller already stored on an integration. */
408
347
  function shcControllerId(input) {
409
348
  const service = input.serviceHost?.trim().toLowerCase() ?? "";
@@ -412,40 +351,24 @@ function shcControllerId(input) {
412
351
  if (serial.length > 0) return serial;
413
352
  return input.host;
414
353
  }
415
- function hostsFromRecords(records) {
416
- const instances = [];
417
- const srv = /* @__PURE__ */ new Map();
418
- const addresses = /* @__PURE__ */ new Map();
419
- for (const rec of records) {
420
- const key = rec.name.toLowerCase();
421
- if (rec.type === PTR) {
422
- const target = readName(rec.message, rec.dataOffset);
423
- if (target !== null && isShcControllerName(target.name)) instances.push(target.name);
424
- } else if (rec.type === SRV && rec.dataLength >= 6) {
425
- const target = readName(rec.message, rec.dataOffset + 6);
426
- if (target !== null) srv.set(key, target.name);
427
- } else if (rec.type === A) {
428
- const ip = ipv4(rec.message, rec.dataOffset, rec.dataLength);
429
- if (ip !== null) addresses.set(key, ip);
430
- }
431
- }
354
+ /** Controllers among the engine's mDNS hits (a `Bosch SHC…` instance), one per host. */
355
+ function shcHostsFromHits(hits) {
432
356
  const out = [];
433
357
  const seen = /* @__PURE__ */ new Set();
434
- for (const instance of instances) {
435
- const viaSrv = srv.get(instance.toLowerCase());
436
- const host = (viaSrv !== void 0 ? addresses.get(viaSrv.toLowerCase()) : void 0) ?? addresses.get(instance.toLowerCase());
437
- if (host === void 0 || seen.has(host)) continue;
438
- seen.add(host);
439
- const label = instance.split("._")[0] ?? instance;
440
- const id = shcControllerId({
441
- name: label,
442
- host,
443
- ...viaSrv !== void 0 ? { serviceHost: viaSrv } : {}
444
- });
358
+ for (const hit of hits) {
359
+ const mdns = hit.mdns;
360
+ if (mdns === void 0 || !isShcControllerName(mdns.instance)) continue;
361
+ if (hit.host.length === 0 || seen.has(hit.host)) continue;
362
+ seen.add(hit.host);
363
+ const name = mdns.instance.split("._")[0] ?? mdns.instance;
445
364
  out.push({
446
- host,
447
- name: label,
448
- id
365
+ host: hit.host,
366
+ name,
367
+ id: shcControllerId({
368
+ name,
369
+ host: hit.host,
370
+ serviceHost: mdns.host
371
+ })
449
372
  });
450
373
  }
451
374
  return out;
@@ -465,11 +388,10 @@ function proposeShcControllers(found, existingHosts) {
465
388
  function shcDiscoveryCandidates(proposals) {
466
389
  return proposals.map((found) => ({
467
390
  stableId: `bosch-shc:${found.id}`,
391
+ type: require_dist.DeviceType.Hub,
468
392
  suggestedName: found.name,
469
- prefilledConfig: {
470
- host: found.host,
471
- [SHC_SETUP_INTEGRATION_KEY]: true
472
- }
393
+ prefilledConfig: { host: found.host },
394
+ setupIntegration: true
473
395
  }));
474
396
  }
475
397
  function shcDiscoverySummary(foundCount, proposedCount) {
@@ -477,101 +399,6 @@ function shcDiscoverySummary(foundCount, proposedCount) {
477
399
  if (proposedCount === 0) return "No controller found on the network.";
478
400
  return `Found ${proposedCount} ${proposedCount === 1 ? "controller" : "controllers"}.`;
479
401
  }
480
- /** Hosts advertised as a Bosch Smart Home Controller on one mDNS message. */
481
- function shcHostsFromDnsMessage(buf) {
482
- if (buf.length < 12) return [];
483
- const questions = buf.readUInt16BE(4);
484
- const answers = buf.readUInt16BE(6);
485
- const authorities = buf.readUInt16BE(8);
486
- const extra = buf.readUInt16BE(10);
487
- let pos = 12;
488
- for (let i = 0; i < questions; i++) {
489
- const name = readName(buf, pos);
490
- if (name === null) return [];
491
- pos = name.end + 4;
492
- if (pos > buf.length) return [];
493
- }
494
- const answerRecords = readRecords(buf, pos, answers);
495
- if (answerRecords === null) return [];
496
- const authorityRecords = readRecords(buf, answerRecords.next, authorities);
497
- if (authorityRecords === null) return [];
498
- const extraRecords = readRecords(buf, authorityRecords.next, extra);
499
- if (extraRecords === null) return [];
500
- return hostsFromRecords([
501
- ...answerRecords.records,
502
- ...authorityRecords.records,
503
- ...extraRecords.records
504
- ]);
505
- }
506
- function mdnsPtrQuery(service) {
507
- const encoded = service.split(".").filter((part) => part.length > 0).map((label) => Buffer.concat([Buffer.from([label.length]), Buffer.from(label)]));
508
- const header = Buffer.alloc(12);
509
- header.writeUInt16BE(1, 4);
510
- const tail = Buffer.alloc(4);
511
- tail.writeUInt16BE(PTR, 0);
512
- tail.writeUInt16BE(MDNS_QU_CLASS, 2);
513
- return Buffer.concat([
514
- header,
515
- ...encoded,
516
- Buffer.from([0]),
517
- tail
518
- ]);
519
- }
520
- function closeQuietly(socket) {
521
- try {
522
- socket.close();
523
- } catch {}
524
- }
525
- function exchangeMdns(query, timeoutMs) {
526
- return new Promise((resolve) => {
527
- let socket;
528
- try {
529
- socket = (0, node_dgram.createSocket)({
530
- type: "udp4",
531
- reuseAddr: true
532
- });
533
- } catch {
534
- resolve([]);
535
- return;
536
- }
537
- const packets = [];
538
- let settled = false;
539
- const finish = () => {
540
- if (settled) return;
541
- settled = true;
542
- closeQuietly(socket);
543
- resolve(packets);
544
- };
545
- socket.on("error", () => finish());
546
- socket.on("message", (msg) => {
547
- packets.push(Buffer.from(msg));
548
- });
549
- socket.bind(0, () => {
550
- try {
551
- socket.send(query, MDNS_PORT, MDNS_ADDRESS);
552
- } catch {
553
- finish();
554
- }
555
- });
556
- setTimeout(finish, timeoutMs);
557
- });
558
- }
559
- async function browseShcControllers(exchange = exchangeMdns) {
560
- let packets;
561
- try {
562
- packets = await exchange(mdnsPtrQuery(SHC_MDNS_SERVICE), SHC_DISCOVERY_TIMEOUT_MS);
563
- } catch {
564
- return [];
565
- }
566
- const hosts = [];
567
- const seen = /* @__PURE__ */ new Set();
568
- for (const packet of packets) for (const found of shcHostsFromDnsMessage(packet)) {
569
- if (seen.has(found.host)) continue;
570
- seen.add(found.host);
571
- hosts.push(found);
572
- }
573
- return hosts;
574
- }
575
402
  /** Manual host wins. Otherwise the address discovery filled in. */
576
403
  function resolveShcPairingHost(input) {
577
404
  const manual = input.hostManual?.trim() ?? "";
@@ -23864,14 +23691,15 @@ var BoschShcProviderAddon = class extends require_dist.BaseDeviceProvider {
23864
23691
  customActions: boschShcActions,
23865
23692
  actionHandlers: {
23866
23693
  discoverController: async () => {
23867
- const { proposals, summary } = await this.discoverShcControllers();
23694
+ const { candidates } = await this.ctx.api.deviceManager.discoverProvider.mutate({ addonId: this.ctx.id });
23695
+ const hosts = candidates.map((candidate) => ({
23696
+ host: String(candidate.prefilledConfig["host"] ?? ""),
23697
+ name: candidate.suggestedName,
23698
+ id: candidate.stableId.replace(/^bosch-shc:/, "")
23699
+ }));
23868
23700
  return {
23869
- hosts: proposals.map((found) => ({
23870
- host: found.host,
23871
- name: found.name,
23872
- id: found.id
23873
- })),
23874
- summary
23701
+ hosts,
23702
+ summary: shcDiscoverySummary(hosts.length, hosts.length)
23875
23703
  };
23876
23704
  },
23877
23705
  registerClient: async (input) => {
@@ -23907,24 +23735,21 @@ var BoschShcProviderAddon = class extends require_dist.BaseDeviceProvider {
23907
23735
  async supportsManualCreation() {
23908
23736
  return false;
23909
23737
  }
23910
- /**
23911
- * LAN mDNS probe. This addon is hub-only, so the socket is the hub's.
23912
- * The operator's hub is on br0 (its own LAN address), the same path the
23913
- * hub already uses to announce itself — multicast UDP 5353 reaches the
23914
- * LAN. The probe asks for a unicast reply and does not bind 5353, which
23915
- * the hub's announcer holds. Agents do not run this addon.
23916
- */
23738
+ /** A controller is found by the scan engine's mDNS browse; this addon only classifies the hits. */
23917
23739
  async supportsDiscovery() {
23918
23740
  return true;
23919
23741
  }
23920
- async discoverDevices() {
23921
- const { proposals } = await this.discoverShcControllers();
23922
- return shcDiscoveryCandidates(proposals).map((candidate) => ({
23923
- stableId: candidate.stableId,
23924
- type: require_dist.DeviceType.Hub,
23925
- suggestedName: candidate.suggestedName,
23926
- prefilledConfig: { ...candidate.prefilledConfig }
23927
- }));
23742
+ async getDiscoveryModes() {
23743
+ return SHC_DISCOVERY_MODES;
23744
+ }
23745
+ async classifyDiscoveryHits(input) {
23746
+ const found = shcHostsFromHits(input.hits);
23747
+ const proposals = proposeShcControllers(found, await this.knownControllerHosts());
23748
+ if (found.length > proposals.length) this.ctx.logger.info("SHC discovery skipped controllers already added", { meta: {
23749
+ found: found.length,
23750
+ proposed: proposals.length
23751
+ } });
23752
+ return shcDiscoveryCandidates(proposals);
23928
23753
  }
23929
23754
  async adoptDiscoveredDevice(_input) {
23930
23755
  throw new Error("Bosch SHC controllers are paired from the integration form");
@@ -23942,24 +23767,6 @@ var BoschShcProviderAddon = class extends require_dist.BaseDeviceProvider {
23942
23767
  if (typeof shcDeviceId !== "string" || shcDeviceId.length === 0) throw new Error("bosch-shc generateStableId: shcDeviceId required");
23943
23768
  return `bosch-shc:${integrationId}:${shcDeviceId}`;
23944
23769
  }
23945
- async discoverShcControllers() {
23946
- let found = [];
23947
- try {
23948
- found = await browseShcControllers();
23949
- } catch (err) {
23950
- this.ctx.logger.warn("SHC controller discovery failed", { meta: { error: require_dist.errMsg(err) } });
23951
- }
23952
- const known = await this.knownControllerHosts();
23953
- const proposals = proposeShcControllers(found, known);
23954
- if (found.length > proposals.length) this.ctx.logger.info("SHC discovery skipped controllers already added", { meta: {
23955
- found: found.length,
23956
- proposed: proposals.length
23957
- } });
23958
- return {
23959
- proposals,
23960
- summary: shcDiscoverySummary(found.length, proposals.length)
23961
- };
23962
- }
23963
23770
  async knownControllerHosts() {
23964
23771
  try {
23965
23772
  const all = await this.ctx.api.integrations.list.query();