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