@camstack/addon-provider-bosch 0.1.45 → 0.1.46
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 +6 -0
- package/dist/{account-integration-offline-DAEciV_Q.mjs → account-integration-offline-BGH1q3tj.mjs} +1 -1
- package/dist/{account-integration-offline-BiXbzZsg.js → account-integration-offline-BfsMnlJo.js} +1 -1
- package/dist/{bosch-instances-DdklwoKS.js → bosch-instances-BD-Valeo.js} +148 -14
- package/dist/{bosch-instances-DXID-jMD.mjs → bosch-instances-DkUo6BfT.mjs} +148 -14
- package/dist/home-connect/addon.js +2 -2
- package/dist/home-connect/addon.mjs +2 -2
- package/dist/homecom/addon.js +231 -23
- package/dist/homecom/addon.mjs +231 -23
- package/dist/shc/addon.js +1 -1
- package/dist/shc/addon.mjs +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.46] - 2026-11-09
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- A HomeCom heat pump whose gateway reports only its system modes is now offered for adoption.
|
|
15
|
+
|
|
10
16
|
## [0.1.45] - 2026-11-09
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/{account-integration-offline-DAEciV_Q.mjs → account-integration-offline-BGH1q3tj.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { X as errMsg } from "./bosch-instances-
|
|
1
|
+
import { X as errMsg } from "./bosch-instances-DkUo6BfT.mjs";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
3
|
//#region src/shared/sign-in-redirects.ts
|
|
4
4
|
/** The addresses each vendor sends the browser to after a sign-in; no browser can open them. Kept free of imports so the form schema can name them. */
|
package/dist/{account-integration-offline-BiXbzZsg.js → account-integration-offline-BfsMnlJo.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_bosch_instances = require("./bosch-instances-
|
|
1
|
+
const require_bosch_instances = require("./bosch-instances-BD-Valeo.js");
|
|
2
2
|
let node_crypto = require("node:crypto");
|
|
3
3
|
//#region src/shared/sign-in-redirects.ts
|
|
4
4
|
/** The addresses each vendor sends the browser to after a sign-in; no browser can open them. Kept free of imports so the form schema can name them. */
|
|
@@ -44553,21 +44553,44 @@ var AutomationCapStepBaseSchema = object({
|
|
|
44553
44553
|
*/
|
|
44554
44554
|
argUnits: record(string(), string().min(1)).optional()
|
|
44555
44555
|
});
|
|
44556
|
-
|
|
44556
|
+
/**
|
|
44557
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
44558
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
44559
|
+
* This is the LEAF shape: no condition of its own.
|
|
44560
|
+
*/
|
|
44561
|
+
var LeafActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepBaseSchema.refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44557
44562
|
message: "a cap step needs a target",
|
|
44558
44563
|
path: ["target"]
|
|
44559
44564
|
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
44560
44565
|
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44561
44566
|
path: ["actionId"]
|
|
44562
|
-
});
|
|
44567
|
+
})]);
|
|
44563
44568
|
/**
|
|
44564
|
-
* The
|
|
44565
|
-
*
|
|
44569
|
+
* The optional guard of a step (#472): `if` is the ONE `when` tree (D980), read
|
|
44570
|
+
* on the state mirror when the step is reached; `else` runs when it is false.
|
|
44571
|
+
* An `else` holds leaf steps only — the nesting is bounded at ONE level, so a
|
|
44572
|
+
* branch can never grow a tree the trace has to draw.
|
|
44566
44573
|
*/
|
|
44567
|
-
var
|
|
44574
|
+
var stepGuardShape = {
|
|
44575
|
+
if: AutomationWhenSchema.optional(),
|
|
44576
|
+
else: array(LeafActionStepSchema).max(10).optional()
|
|
44577
|
+
};
|
|
44578
|
+
var elseNeedsIf = {
|
|
44579
|
+
message: "an else list needs the condition it is the other side of (if)",
|
|
44580
|
+
path: ["else"]
|
|
44581
|
+
};
|
|
44582
|
+
var GuardedWaitStepSchema = AutomationWaitStepSchema.extend(stepGuardShape).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44583
|
+
var GuardedCapStepSchema = AutomationCapStepBaseSchema.extend(stepGuardShape).refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44584
|
+
message: "a cap step needs a target",
|
|
44585
|
+
path: ["target"]
|
|
44586
|
+
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
44587
|
+
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44588
|
+
path: ["actionId"]
|
|
44589
|
+
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44590
|
+
var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema, GuardedCapStepSchema]);
|
|
44568
44591
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
44569
|
-
|
|
44570
|
-
|
|
44592
|
+
GuardedWaitStepSchema,
|
|
44593
|
+
GuardedCapStepSchema,
|
|
44571
44594
|
object({
|
|
44572
44595
|
kind: literal("action"),
|
|
44573
44596
|
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
@@ -44947,7 +44970,7 @@ var StepDeviceTraceSchema = object({
|
|
|
44947
44970
|
outcome: _enum(["ok", "error"]),
|
|
44948
44971
|
message: string().optional()
|
|
44949
44972
|
});
|
|
44950
|
-
var
|
|
44973
|
+
var StepTraceBaseSchema = object({
|
|
44951
44974
|
/** `actions.<i>` */
|
|
44952
44975
|
path: string().min(1),
|
|
44953
44976
|
kind: string().min(1),
|
|
@@ -44966,15 +44989,35 @@ var AutomationStepTraceSchema = object({
|
|
|
44966
44989
|
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
44967
44990
|
*/
|
|
44968
44991
|
args: record(string(), TraceValueSchema).optional(),
|
|
44969
|
-
/**
|
|
44992
|
+
/**
|
|
44993
|
+
* The device values a templated arg printed as `—`, one `ref: reason` each
|
|
44994
|
+
* (D1021): why a sentence says "—" where a number was expected. Bounded.
|
|
44995
|
+
*/
|
|
44996
|
+
templates: array(string()).max(20).optional(),
|
|
44997
|
+
/**
|
|
44998
|
+
* What the step's own `if` read when the step was reached (#472): `true` the
|
|
44999
|
+
* step ran, `false` its `else` ran (or nothing did), `unknown` a state leaf
|
|
45000
|
+
* could not be read, so NEITHER side ran. Absent on an unguarded step.
|
|
45001
|
+
*/
|
|
45002
|
+
condition: _enum([
|
|
45003
|
+
"true",
|
|
45004
|
+
"false",
|
|
45005
|
+
"unknown"
|
|
45006
|
+
]).optional(),
|
|
45007
|
+
/** Why the condition read what it read, in the evaluator's own words. */
|
|
45008
|
+
conditionReason: string().optional(),
|
|
45009
|
+
/** `empty`: the target resolved to no device, so nothing was done — never silently `ok`. `skipped`: its `if` was false (or unknown) and nothing ran in its place. */
|
|
44970
45010
|
outcome: _enum([
|
|
44971
45011
|
"ok",
|
|
44972
45012
|
"error",
|
|
44973
45013
|
"not-run",
|
|
44974
|
-
"empty"
|
|
45014
|
+
"empty",
|
|
45015
|
+
"skipped"
|
|
44975
45016
|
]),
|
|
44976
45017
|
message: string().optional()
|
|
44977
45018
|
});
|
|
45019
|
+
/** A step trace; the `else` steps that ran are one level of the same shape (the nesting is bounded at one). */
|
|
45020
|
+
var AutomationStepTraceSchema = StepTraceBaseSchema.extend({ else: array(StepTraceBaseSchema).optional() });
|
|
44978
45021
|
var AutomationRunTraceSchema = object({
|
|
44979
45022
|
runId: string().min(1),
|
|
44980
45023
|
startedAt: number$1(),
|
|
@@ -45150,6 +45193,7 @@ _enum([
|
|
|
45150
45193
|
"invalid-variables",
|
|
45151
45194
|
"invalid-steps",
|
|
45152
45195
|
"not-linked",
|
|
45196
|
+
"template-scope",
|
|
45153
45197
|
"busy"
|
|
45154
45198
|
]);
|
|
45155
45199
|
object({
|
|
@@ -52732,6 +52776,43 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
|
|
|
52732
52776
|
auth: "admin"
|
|
52733
52777
|
});
|
|
52734
52778
|
var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
52779
|
+
var DeviceTemplateSchema = string().min(1).max(4096);
|
|
52780
|
+
var DeviceTemplateRefusalSchema = object({
|
|
52781
|
+
reason: _enum([
|
|
52782
|
+
"too-long",
|
|
52783
|
+
"syntax",
|
|
52784
|
+
"forbidden-syntax",
|
|
52785
|
+
"forbidden-member",
|
|
52786
|
+
"unknown-filter",
|
|
52787
|
+
"ambiguous-field",
|
|
52788
|
+
"busy",
|
|
52789
|
+
"budget-exceeded",
|
|
52790
|
+
"timeout",
|
|
52791
|
+
"output-too-large",
|
|
52792
|
+
"render-failed",
|
|
52793
|
+
"out-of-scope",
|
|
52794
|
+
"unavailable"
|
|
52795
|
+
]),
|
|
52796
|
+
message: string()
|
|
52797
|
+
});
|
|
52798
|
+
var DeviceTemplateTraceEntrySchema = object({
|
|
52799
|
+
ref: string(),
|
|
52800
|
+
reason: _enum([
|
|
52801
|
+
"unknown-alias",
|
|
52802
|
+
"unknown-field",
|
|
52803
|
+
"no-field",
|
|
52804
|
+
"unavailable"
|
|
52805
|
+
])
|
|
52806
|
+
});
|
|
52807
|
+
var DeviceTemplateRenderSchema = discriminatedUnion("ok", [object({
|
|
52808
|
+
ok: literal(true),
|
|
52809
|
+
text: string(),
|
|
52810
|
+
trace: array(DeviceTemplateTraceEntrySchema)
|
|
52811
|
+
}), object({
|
|
52812
|
+
ok: literal(false),
|
|
52813
|
+
refusal: DeviceTemplateRefusalSchema,
|
|
52814
|
+
trace: array(DeviceTemplateTraceEntrySchema)
|
|
52815
|
+
})]);
|
|
52735
52816
|
/**
|
|
52736
52817
|
* A SITE ZONE — a named part of the property that several locations belong to.
|
|
52737
52818
|
*
|
|
@@ -53563,6 +53644,22 @@ method(object({
|
|
|
53563
53644
|
kind: "mutation",
|
|
53564
53645
|
auth: "admin"
|
|
53565
53646
|
}), method(object({
|
|
53647
|
+
templates: array(DeviceTemplateSchema).max(64),
|
|
53648
|
+
allowedDeviceIds: array(number$1().int()).optional()
|
|
53649
|
+
}), object({ results: array(DeviceTemplateRenderSchema) }), { auth: "admin" }), method(object({
|
|
53650
|
+
templates: array(string()).max(64),
|
|
53651
|
+
allowedDeviceIds: array(number$1().int()).optional()
|
|
53652
|
+
}), object({ results: array(discriminatedUnion("ok", [object({
|
|
53653
|
+
ok: literal(true),
|
|
53654
|
+
aliases: array(object({
|
|
53655
|
+
alias: string(),
|
|
53656
|
+
deviceId: number$1().int().nullable(),
|
|
53657
|
+
allowed: boolean()
|
|
53658
|
+
}))
|
|
53659
|
+
}), object({
|
|
53660
|
+
ok: literal(false),
|
|
53661
|
+
refusal: DeviceTemplateRefusalSchema
|
|
53662
|
+
})])) }), { auth: "admin" }), method(object({
|
|
53566
53663
|
deviceId: number$1(),
|
|
53567
53664
|
location: string().nullable()
|
|
53568
53665
|
}), _void(), {
|
|
@@ -57531,7 +57628,13 @@ var HeroTimedRunSlotSchema = object({
|
|
|
57531
57628
|
activeUntil: number$1().nullable(),
|
|
57532
57629
|
quickRun: TimedRunQuickRunSchema,
|
|
57533
57630
|
run: HeroActionSchema,
|
|
57534
|
-
cancel: HeroActionSchema
|
|
57631
|
+
cancel: HeroActionSchema,
|
|
57632
|
+
/**
|
|
57633
|
+
* Whole units left (the limit's unit, ceil). `null` while nothing is armed.
|
|
57634
|
+
* A number, not words: the row formats it, and a text here would not fit the
|
|
57635
|
+
* hero-layout declaration.
|
|
57636
|
+
*/
|
|
57637
|
+
remaining: number$1().nullable()
|
|
57535
57638
|
});
|
|
57536
57639
|
var HeroPowerSupplySlotSchema = object({
|
|
57537
57640
|
/** The plug's state; `null` = unknown, never off. */
|
|
@@ -57650,18 +57753,31 @@ var HeroModelSchema = object({
|
|
|
57650
57753
|
* 4. a trailing fact (power, else battery, else consumables).
|
|
57651
57754
|
*
|
|
57652
57755
|
* Selection rules, all taken from the model:
|
|
57653
|
-
* -
|
|
57756
|
+
* - the primary control is the device's own power; else the FIRST control of
|
|
57654
57757
|
* the FIRST row that has any (a later row's control is never promoted). When
|
|
57655
57758
|
* the model lists that part in `gated`, the row has no inline control: the
|
|
57656
57759
|
* projection never falls through to another control the caller could use.
|
|
57760
|
+
* - a timed run, when the model carries the slot, sits beside that primary:
|
|
57761
|
+
* Run while idle, Stop while armed. Gating the slot hides the control.
|
|
57657
57762
|
* - the status line is the head (what plays, else the state) plus at most
|
|
57658
|
-
* {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded
|
|
57763
|
+
* {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded, then the
|
|
57764
|
+
* time left on an armed timed run;
|
|
57659
57765
|
* - the trailing fact is the first of power, battery, consumables.
|
|
57660
57766
|
*
|
|
57661
57767
|
* `projectDeviceRow` is pure and computes nothing: it selects from a
|
|
57662
57768
|
* {@link HeroModel}. A row that needed a value the hero lacks would mean the
|
|
57663
57769
|
* model is missing it, so the fix goes in the model.
|
|
57664
57770
|
*/
|
|
57771
|
+
/**
|
|
57772
|
+
* The row's timed-run control. `id` is `timed-run.run` while idle and
|
|
57773
|
+
* `timed-run.cancel` while armed; `duration` and `unit` are the one-tap run
|
|
57774
|
+
* a Run press starts.
|
|
57775
|
+
*/
|
|
57776
|
+
var HeroRowTimedRunSchema = object({
|
|
57777
|
+
id: _enum(["timed-run.run", "timed-run.cancel"]),
|
|
57778
|
+
duration: number$1(),
|
|
57779
|
+
unit: _enum(["min", "s"])
|
|
57780
|
+
});
|
|
57665
57781
|
var HeroRowModelSchema = object({
|
|
57666
57782
|
deviceId: number$1().int(),
|
|
57667
57783
|
name: string(),
|
|
@@ -57674,6 +57790,11 @@ var HeroRowModelSchema = object({
|
|
|
57674
57790
|
statusLine: array(HeroTextSchema),
|
|
57675
57791
|
/** `null` = the type has no inline control, or the caller may not act on it. */
|
|
57676
57792
|
primary: HeroControlSchema.nullable(),
|
|
57793
|
+
/**
|
|
57794
|
+
* Beside `primary` when the device offers a timed run: Run while idle, Stop
|
|
57795
|
+
* while armed. `null` when there is no slot, or the caller may not act.
|
|
57796
|
+
*/
|
|
57797
|
+
timedRun: HeroRowTimedRunSchema.nullable(),
|
|
57677
57798
|
trailing: HeroFactSchema.nullable()
|
|
57678
57799
|
});
|
|
57679
57800
|
/**
|
|
@@ -89025,6 +89146,13 @@ Object.freeze({
|
|
|
89025
89146
|
access: "create",
|
|
89026
89147
|
role: "admin"
|
|
89027
89148
|
},
|
|
89149
|
+
"deviceManager.checkDeviceTemplates": {
|
|
89150
|
+
capName: "device-manager",
|
|
89151
|
+
capScope: "system",
|
|
89152
|
+
addonId: null,
|
|
89153
|
+
access: "view",
|
|
89154
|
+
role: "admin"
|
|
89155
|
+
},
|
|
89028
89156
|
"deviceManager.cleanupDeletedDevicesData": {
|
|
89029
89157
|
capName: "device-manager",
|
|
89030
89158
|
capScope: "system",
|
|
@@ -89424,6 +89552,13 @@ Object.freeze({
|
|
|
89424
89552
|
access: "create",
|
|
89425
89553
|
role: "admin"
|
|
89426
89554
|
},
|
|
89555
|
+
"deviceManager.renderDeviceTemplates": {
|
|
89556
|
+
capName: "device-manager",
|
|
89557
|
+
capScope: "system",
|
|
89558
|
+
addonId: null,
|
|
89559
|
+
access: "view",
|
|
89560
|
+
role: "admin"
|
|
89561
|
+
},
|
|
89427
89562
|
"deviceManager.reportDiscoveryObservations": {
|
|
89428
89563
|
capName: "device-manager",
|
|
89429
89564
|
capScope: "system",
|
|
@@ -97340,7 +97475,6 @@ function offered(received) {
|
|
|
97340
97475
|
};
|
|
97341
97476
|
}
|
|
97342
97477
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
97343
|
-
string().min(1).max(4096);
|
|
97344
97478
|
Object.freeze({
|
|
97345
97479
|
"accessories.getStatus": [{
|
|
97346
97480
|
name: "deviceId",
|
|
@@ -44553,21 +44553,44 @@ var AutomationCapStepBaseSchema = object({
|
|
|
44553
44553
|
*/
|
|
44554
44554
|
argUnits: record(string(), string().min(1)).optional()
|
|
44555
44555
|
});
|
|
44556
|
-
|
|
44556
|
+
/**
|
|
44557
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
44558
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
44559
|
+
* This is the LEAF shape: no condition of its own.
|
|
44560
|
+
*/
|
|
44561
|
+
var LeafActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepBaseSchema.refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44557
44562
|
message: "a cap step needs a target",
|
|
44558
44563
|
path: ["target"]
|
|
44559
44564
|
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
44560
44565
|
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44561
44566
|
path: ["actionId"]
|
|
44562
|
-
});
|
|
44567
|
+
})]);
|
|
44563
44568
|
/**
|
|
44564
|
-
* The
|
|
44565
|
-
*
|
|
44569
|
+
* The optional guard of a step (#472): `if` is the ONE `when` tree (D980), read
|
|
44570
|
+
* on the state mirror when the step is reached; `else` runs when it is false.
|
|
44571
|
+
* An `else` holds leaf steps only — the nesting is bounded at ONE level, so a
|
|
44572
|
+
* branch can never grow a tree the trace has to draw.
|
|
44566
44573
|
*/
|
|
44567
|
-
var
|
|
44574
|
+
var stepGuardShape = {
|
|
44575
|
+
if: AutomationWhenSchema.optional(),
|
|
44576
|
+
else: array(LeafActionStepSchema).max(10).optional()
|
|
44577
|
+
};
|
|
44578
|
+
var elseNeedsIf = {
|
|
44579
|
+
message: "an else list needs the condition it is the other side of (if)",
|
|
44580
|
+
path: ["else"]
|
|
44581
|
+
};
|
|
44582
|
+
var GuardedWaitStepSchema = AutomationWaitStepSchema.extend(stepGuardShape).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44583
|
+
var GuardedCapStepSchema = AutomationCapStepBaseSchema.extend(stepGuardShape).refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44584
|
+
message: "a cap step needs a target",
|
|
44585
|
+
path: ["target"]
|
|
44586
|
+
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
44587
|
+
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44588
|
+
path: ["actionId"]
|
|
44589
|
+
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44590
|
+
var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema, GuardedCapStepSchema]);
|
|
44568
44591
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
44569
|
-
|
|
44570
|
-
|
|
44592
|
+
GuardedWaitStepSchema,
|
|
44593
|
+
GuardedCapStepSchema,
|
|
44571
44594
|
object({
|
|
44572
44595
|
kind: literal("action"),
|
|
44573
44596
|
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
@@ -44947,7 +44970,7 @@ var StepDeviceTraceSchema = object({
|
|
|
44947
44970
|
outcome: _enum(["ok", "error"]),
|
|
44948
44971
|
message: string().optional()
|
|
44949
44972
|
});
|
|
44950
|
-
var
|
|
44973
|
+
var StepTraceBaseSchema = object({
|
|
44951
44974
|
/** `actions.<i>` */
|
|
44952
44975
|
path: string().min(1),
|
|
44953
44976
|
kind: string().min(1),
|
|
@@ -44966,15 +44989,35 @@ var AutomationStepTraceSchema = object({
|
|
|
44966
44989
|
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
44967
44990
|
*/
|
|
44968
44991
|
args: record(string(), TraceValueSchema).optional(),
|
|
44969
|
-
/**
|
|
44992
|
+
/**
|
|
44993
|
+
* The device values a templated arg printed as `—`, one `ref: reason` each
|
|
44994
|
+
* (D1021): why a sentence says "—" where a number was expected. Bounded.
|
|
44995
|
+
*/
|
|
44996
|
+
templates: array(string()).max(20).optional(),
|
|
44997
|
+
/**
|
|
44998
|
+
* What the step's own `if` read when the step was reached (#472): `true` the
|
|
44999
|
+
* step ran, `false` its `else` ran (or nothing did), `unknown` a state leaf
|
|
45000
|
+
* could not be read, so NEITHER side ran. Absent on an unguarded step.
|
|
45001
|
+
*/
|
|
45002
|
+
condition: _enum([
|
|
45003
|
+
"true",
|
|
45004
|
+
"false",
|
|
45005
|
+
"unknown"
|
|
45006
|
+
]).optional(),
|
|
45007
|
+
/** Why the condition read what it read, in the evaluator's own words. */
|
|
45008
|
+
conditionReason: string().optional(),
|
|
45009
|
+
/** `empty`: the target resolved to no device, so nothing was done — never silently `ok`. `skipped`: its `if` was false (or unknown) and nothing ran in its place. */
|
|
44970
45010
|
outcome: _enum([
|
|
44971
45011
|
"ok",
|
|
44972
45012
|
"error",
|
|
44973
45013
|
"not-run",
|
|
44974
|
-
"empty"
|
|
45014
|
+
"empty",
|
|
45015
|
+
"skipped"
|
|
44975
45016
|
]),
|
|
44976
45017
|
message: string().optional()
|
|
44977
45018
|
});
|
|
45019
|
+
/** A step trace; the `else` steps that ran are one level of the same shape (the nesting is bounded at one). */
|
|
45020
|
+
var AutomationStepTraceSchema = StepTraceBaseSchema.extend({ else: array(StepTraceBaseSchema).optional() });
|
|
44978
45021
|
var AutomationRunTraceSchema = object({
|
|
44979
45022
|
runId: string().min(1),
|
|
44980
45023
|
startedAt: number$1(),
|
|
@@ -45150,6 +45193,7 @@ _enum([
|
|
|
45150
45193
|
"invalid-variables",
|
|
45151
45194
|
"invalid-steps",
|
|
45152
45195
|
"not-linked",
|
|
45196
|
+
"template-scope",
|
|
45153
45197
|
"busy"
|
|
45154
45198
|
]);
|
|
45155
45199
|
object({
|
|
@@ -52732,6 +52776,43 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
|
|
|
52732
52776
|
auth: "admin"
|
|
52733
52777
|
});
|
|
52734
52778
|
var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
52779
|
+
var DeviceTemplateSchema = string().min(1).max(4096);
|
|
52780
|
+
var DeviceTemplateRefusalSchema = object({
|
|
52781
|
+
reason: _enum([
|
|
52782
|
+
"too-long",
|
|
52783
|
+
"syntax",
|
|
52784
|
+
"forbidden-syntax",
|
|
52785
|
+
"forbidden-member",
|
|
52786
|
+
"unknown-filter",
|
|
52787
|
+
"ambiguous-field",
|
|
52788
|
+
"busy",
|
|
52789
|
+
"budget-exceeded",
|
|
52790
|
+
"timeout",
|
|
52791
|
+
"output-too-large",
|
|
52792
|
+
"render-failed",
|
|
52793
|
+
"out-of-scope",
|
|
52794
|
+
"unavailable"
|
|
52795
|
+
]),
|
|
52796
|
+
message: string()
|
|
52797
|
+
});
|
|
52798
|
+
var DeviceTemplateTraceEntrySchema = object({
|
|
52799
|
+
ref: string(),
|
|
52800
|
+
reason: _enum([
|
|
52801
|
+
"unknown-alias",
|
|
52802
|
+
"unknown-field",
|
|
52803
|
+
"no-field",
|
|
52804
|
+
"unavailable"
|
|
52805
|
+
])
|
|
52806
|
+
});
|
|
52807
|
+
var DeviceTemplateRenderSchema = discriminatedUnion("ok", [object({
|
|
52808
|
+
ok: literal(true),
|
|
52809
|
+
text: string(),
|
|
52810
|
+
trace: array(DeviceTemplateTraceEntrySchema)
|
|
52811
|
+
}), object({
|
|
52812
|
+
ok: literal(false),
|
|
52813
|
+
refusal: DeviceTemplateRefusalSchema,
|
|
52814
|
+
trace: array(DeviceTemplateTraceEntrySchema)
|
|
52815
|
+
})]);
|
|
52735
52816
|
/**
|
|
52736
52817
|
* A SITE ZONE — a named part of the property that several locations belong to.
|
|
52737
52818
|
*
|
|
@@ -53563,6 +53644,22 @@ method(object({
|
|
|
53563
53644
|
kind: "mutation",
|
|
53564
53645
|
auth: "admin"
|
|
53565
53646
|
}), method(object({
|
|
53647
|
+
templates: array(DeviceTemplateSchema).max(64),
|
|
53648
|
+
allowedDeviceIds: array(number$1().int()).optional()
|
|
53649
|
+
}), object({ results: array(DeviceTemplateRenderSchema) }), { auth: "admin" }), method(object({
|
|
53650
|
+
templates: array(string()).max(64),
|
|
53651
|
+
allowedDeviceIds: array(number$1().int()).optional()
|
|
53652
|
+
}), object({ results: array(discriminatedUnion("ok", [object({
|
|
53653
|
+
ok: literal(true),
|
|
53654
|
+
aliases: array(object({
|
|
53655
|
+
alias: string(),
|
|
53656
|
+
deviceId: number$1().int().nullable(),
|
|
53657
|
+
allowed: boolean()
|
|
53658
|
+
}))
|
|
53659
|
+
}), object({
|
|
53660
|
+
ok: literal(false),
|
|
53661
|
+
refusal: DeviceTemplateRefusalSchema
|
|
53662
|
+
})])) }), { auth: "admin" }), method(object({
|
|
53566
53663
|
deviceId: number$1(),
|
|
53567
53664
|
location: string().nullable()
|
|
53568
53665
|
}), _void(), {
|
|
@@ -57531,7 +57628,13 @@ var HeroTimedRunSlotSchema = object({
|
|
|
57531
57628
|
activeUntil: number$1().nullable(),
|
|
57532
57629
|
quickRun: TimedRunQuickRunSchema,
|
|
57533
57630
|
run: HeroActionSchema,
|
|
57534
|
-
cancel: HeroActionSchema
|
|
57631
|
+
cancel: HeroActionSchema,
|
|
57632
|
+
/**
|
|
57633
|
+
* Whole units left (the limit's unit, ceil). `null` while nothing is armed.
|
|
57634
|
+
* A number, not words: the row formats it, and a text here would not fit the
|
|
57635
|
+
* hero-layout declaration.
|
|
57636
|
+
*/
|
|
57637
|
+
remaining: number$1().nullable()
|
|
57535
57638
|
});
|
|
57536
57639
|
var HeroPowerSupplySlotSchema = object({
|
|
57537
57640
|
/** The plug's state; `null` = unknown, never off. */
|
|
@@ -57650,18 +57753,31 @@ var HeroModelSchema = object({
|
|
|
57650
57753
|
* 4. a trailing fact (power, else battery, else consumables).
|
|
57651
57754
|
*
|
|
57652
57755
|
* Selection rules, all taken from the model:
|
|
57653
|
-
* -
|
|
57756
|
+
* - the primary control is the device's own power; else the FIRST control of
|
|
57654
57757
|
* the FIRST row that has any (a later row's control is never promoted). When
|
|
57655
57758
|
* the model lists that part in `gated`, the row has no inline control: the
|
|
57656
57759
|
* projection never falls through to another control the caller could use.
|
|
57760
|
+
* - a timed run, when the model carries the slot, sits beside that primary:
|
|
57761
|
+
* Run while idle, Stop while armed. Gating the slot hides the control.
|
|
57657
57762
|
* - the status line is the head (what plays, else the state) plus at most
|
|
57658
|
-
* {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded
|
|
57763
|
+
* {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded, then the
|
|
57764
|
+
* time left on an armed timed run;
|
|
57659
57765
|
* - the trailing fact is the first of power, battery, consumables.
|
|
57660
57766
|
*
|
|
57661
57767
|
* `projectDeviceRow` is pure and computes nothing: it selects from a
|
|
57662
57768
|
* {@link HeroModel}. A row that needed a value the hero lacks would mean the
|
|
57663
57769
|
* model is missing it, so the fix goes in the model.
|
|
57664
57770
|
*/
|
|
57771
|
+
/**
|
|
57772
|
+
* The row's timed-run control. `id` is `timed-run.run` while idle and
|
|
57773
|
+
* `timed-run.cancel` while armed; `duration` and `unit` are the one-tap run
|
|
57774
|
+
* a Run press starts.
|
|
57775
|
+
*/
|
|
57776
|
+
var HeroRowTimedRunSchema = object({
|
|
57777
|
+
id: _enum(["timed-run.run", "timed-run.cancel"]),
|
|
57778
|
+
duration: number$1(),
|
|
57779
|
+
unit: _enum(["min", "s"])
|
|
57780
|
+
});
|
|
57665
57781
|
var HeroRowModelSchema = object({
|
|
57666
57782
|
deviceId: number$1().int(),
|
|
57667
57783
|
name: string(),
|
|
@@ -57674,6 +57790,11 @@ var HeroRowModelSchema = object({
|
|
|
57674
57790
|
statusLine: array(HeroTextSchema),
|
|
57675
57791
|
/** `null` = the type has no inline control, or the caller may not act on it. */
|
|
57676
57792
|
primary: HeroControlSchema.nullable(),
|
|
57793
|
+
/**
|
|
57794
|
+
* Beside `primary` when the device offers a timed run: Run while idle, Stop
|
|
57795
|
+
* while armed. `null` when there is no slot, or the caller may not act.
|
|
57796
|
+
*/
|
|
57797
|
+
timedRun: HeroRowTimedRunSchema.nullable(),
|
|
57677
57798
|
trailing: HeroFactSchema.nullable()
|
|
57678
57799
|
});
|
|
57679
57800
|
/**
|
|
@@ -89025,6 +89146,13 @@ Object.freeze({
|
|
|
89025
89146
|
access: "create",
|
|
89026
89147
|
role: "admin"
|
|
89027
89148
|
},
|
|
89149
|
+
"deviceManager.checkDeviceTemplates": {
|
|
89150
|
+
capName: "device-manager",
|
|
89151
|
+
capScope: "system",
|
|
89152
|
+
addonId: null,
|
|
89153
|
+
access: "view",
|
|
89154
|
+
role: "admin"
|
|
89155
|
+
},
|
|
89028
89156
|
"deviceManager.cleanupDeletedDevicesData": {
|
|
89029
89157
|
capName: "device-manager",
|
|
89030
89158
|
capScope: "system",
|
|
@@ -89424,6 +89552,13 @@ Object.freeze({
|
|
|
89424
89552
|
access: "create",
|
|
89425
89553
|
role: "admin"
|
|
89426
89554
|
},
|
|
89555
|
+
"deviceManager.renderDeviceTemplates": {
|
|
89556
|
+
capName: "device-manager",
|
|
89557
|
+
capScope: "system",
|
|
89558
|
+
addonId: null,
|
|
89559
|
+
access: "view",
|
|
89560
|
+
role: "admin"
|
|
89561
|
+
},
|
|
89427
89562
|
"deviceManager.reportDiscoveryObservations": {
|
|
89428
89563
|
capName: "device-manager",
|
|
89429
89564
|
capScope: "system",
|
|
@@ -97340,7 +97475,6 @@ function offered(received) {
|
|
|
97340
97475
|
};
|
|
97341
97476
|
}
|
|
97342
97477
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
97343
|
-
string().min(1).max(4096);
|
|
97344
97478
|
Object.freeze({
|
|
97345
97479
|
"accessories.getStatus": [{
|
|
97346
97480
|
name: "deviceId",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_bosch_instances = require("../bosch-instances-
|
|
3
|
-
const require_account_integration_offline = require("../account-integration-offline-
|
|
2
|
+
const require_bosch_instances = require("../bosch-instances-BD-Valeo.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-BfsMnlJo.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 RecordIntegrationInstances, B as array, C as powerMeterCapability, E as runningStateCapability, F as entryBody, G as string, I as isSecretMarker, J as DeviceRole, K as union, L as isSecretMarkerLike, M as customAction, N as defineCustomActions, P as defineEntryKind, R as rowsDepsOver, S as numericSensorCapability, T as runningFromDishwasherOperation, U as object, V as literal, W as record, X as errMsg, Y as DeviceType, Z as EventCategory, a as BaseDevice, b as ingestReading, c as RUNNING_STATE_COLD, f as consumablesCapability, g as eventEmitterCapability, h as enumSensorCapability, i as testBoschInstance, j as createEvent, m as dishwasherCapability, n as boschRowsPorts, o as BaseDeviceProvider, p as deviceAdoptionCapability, q as unknown, r as keepBodyKeys, s as DishwasherOperationStateSchema, t as boschRowsMigration, u as binaryCapability, w as quantityFromUnit, x as integrationInstancesCapability, z as _enum } from "../bosch-instances-
|
|
2
|
-
import { a as loginFailureForAttempt, c as HOME_CONNECT_REDIRECT_URI, i as createSignInAttemptStore, n as cascadeRemoveDevicesForIntegration, o as parseOAuthCallback, r as buildSignInGuideFields, t as setIntegrationDevicesOnline } from "../account-integration-offline-
|
|
1
|
+
import { A as RecordIntegrationInstances, B as array, C as powerMeterCapability, E as runningStateCapability, F as entryBody, G as string, I as isSecretMarker, J as DeviceRole, K as union, L as isSecretMarkerLike, M as customAction, N as defineCustomActions, P as defineEntryKind, R as rowsDepsOver, S as numericSensorCapability, T as runningFromDishwasherOperation, U as object, V as literal, W as record, X as errMsg, Y as DeviceType, Z as EventCategory, a as BaseDevice, b as ingestReading, c as RUNNING_STATE_COLD, f as consumablesCapability, g as eventEmitterCapability, h as enumSensorCapability, i as testBoschInstance, j as createEvent, m as dishwasherCapability, n as boschRowsPorts, o as BaseDeviceProvider, p as deviceAdoptionCapability, q as unknown, r as keepBodyKeys, s as DishwasherOperationStateSchema, t as boschRowsMigration, u as binaryCapability, w as quantityFromUnit, x as integrationInstancesCapability, z as _enum } from "../bosch-instances-DkUo6BfT.mjs";
|
|
2
|
+
import { a as loginFailureForAttempt, c as HOME_CONNECT_REDIRECT_URI, i as createSignInAttemptStore, n as cascadeRemoveDevicesForIntegration, o as parseOAuthCallback, r as buildSignInGuideFields, t as setIntegrationDevicesOnline } from "../account-integration-offline-BGH1q3tj.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { connect } from "node:tls";
|
|
5
5
|
import { inflateRawSync } from "node:zlib";
|
package/dist/homecom/addon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_bosch_instances = require("../bosch-instances-
|
|
3
|
-
const require_account_integration_offline = require("../account-integration-offline-
|
|
2
|
+
const require_bosch_instances = require("../bosch-instances-BD-Valeo.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-BfsMnlJo.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
//#region src/homecom/config.ts
|
|
6
6
|
var HOMECOM_BRANDS = ["bosch", "buderus"];
|
|
@@ -449,7 +449,7 @@ async function readHydronic(input) {
|
|
|
449
449
|
const heating = await input.get("/heatingCircuits");
|
|
450
450
|
const dhwBody = await input.get("/dhwCircuits");
|
|
451
451
|
const circuits = [];
|
|
452
|
-
for (const
|
|
452
|
+
for (const ref of circuitRefs(heating, "hc")) circuits.push(await readCircuit(input.get, ref.id, ref.name));
|
|
453
453
|
const dhwIds = referenceKeys(dhwBody, "dhw");
|
|
454
454
|
const first = dhwIds[0];
|
|
455
455
|
const dhw = first === void 0 ? null : await readDhw(input.get, first);
|
|
@@ -509,7 +509,7 @@ async function readRac(input) {
|
|
|
509
509
|
setpointResource: "/airConditioning/temperatureSetpoint"
|
|
510
510
|
}], null);
|
|
511
511
|
}
|
|
512
|
-
async function readCircuit(get, id) {
|
|
512
|
+
async function readCircuit(get, id, givenName) {
|
|
513
513
|
const base = `/heatingCircuits/${id}`;
|
|
514
514
|
const loaded = await loadEnvelopes(get, [
|
|
515
515
|
`${base}/operationMode`,
|
|
@@ -532,7 +532,7 @@ async function readCircuit(get, id) {
|
|
|
532
532
|
const humidity = numberOf(loaded.get(`${base}/actualHumidity`) ?? null);
|
|
533
533
|
return {
|
|
534
534
|
id,
|
|
535
|
-
name: id,
|
|
535
|
+
name: heatingCircuitName(id, givenName),
|
|
536
536
|
operationMode: stringOf(operation) ?? "off",
|
|
537
537
|
setpoint: cooling ? numberOf(coolingSet) ?? numberOf(current) ?? numberOf(manual) : numberOf(current) ?? numberOf(manual),
|
|
538
538
|
currentRoomTemp: numberOf(loaded.get(`${base}/roomtemperature`) ?? null),
|
|
@@ -634,18 +634,37 @@ function pointtMode(mode) {
|
|
|
634
634
|
default: return mode;
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
|
-
|
|
637
|
+
/** `hc1` → "Heating circuit 1", unless the gateway already named the circuit. */
|
|
638
|
+
function heatingCircuitName(id, given) {
|
|
639
|
+
if (given !== null && given.length > 0 && given !== id) return given;
|
|
640
|
+
const n = id.replace(/^hc/, "");
|
|
641
|
+
return /^\d+$/.test(n) ? `Heating circuit ${n}` : "Heating circuit";
|
|
642
|
+
}
|
|
643
|
+
function circuitRefs(body, prefix) {
|
|
638
644
|
if (!isRecord$2(body) || !Array.isArray(body["references"])) return [];
|
|
639
645
|
const pattern = prefix === "hc" ? /^hc\d+$/ : /^dhw\d+$/;
|
|
640
646
|
const out = [];
|
|
641
647
|
for (const ref of body["references"]) {
|
|
642
648
|
if (!isRecord$2(ref) || typeof ref["id"] !== "string") continue;
|
|
643
649
|
const tail = ref["id"].split("/").filter((part) => part.length > 0).at(-1);
|
|
644
|
-
if (tail === void 0 || !pattern.test(tail) || out.
|
|
645
|
-
out.push(
|
|
650
|
+
if (tail === void 0 || !pattern.test(tail) || out.some((item) => item.id === tail)) continue;
|
|
651
|
+
out.push({
|
|
652
|
+
id: tail,
|
|
653
|
+
name: refName(ref, tail)
|
|
654
|
+
});
|
|
646
655
|
}
|
|
647
656
|
return out;
|
|
648
657
|
}
|
|
658
|
+
function refName(ref, id) {
|
|
659
|
+
const raw = ref["name"];
|
|
660
|
+
if (typeof raw !== "string") return null;
|
|
661
|
+
const trimmed = raw.trim();
|
|
662
|
+
if (trimmed.length === 0 || trimmed === id) return null;
|
|
663
|
+
return trimmed;
|
|
664
|
+
}
|
|
665
|
+
function referenceKeys(body, prefix) {
|
|
666
|
+
return circuitRefs(body, prefix).map((ref) => ref.id);
|
|
667
|
+
}
|
|
649
668
|
function indexStandard(body) {
|
|
650
669
|
const map = /* @__PURE__ */ new Map();
|
|
651
670
|
if (!isRecord$2(body) || !Array.isArray(body["references"])) return map;
|
|
@@ -793,19 +812,186 @@ function safeGet(get, onFailure) {
|
|
|
793
812
|
}
|
|
794
813
|
};
|
|
795
814
|
}
|
|
815
|
+
var SYSTEM_MODE_KEYS = [
|
|
816
|
+
"silent",
|
|
817
|
+
"away",
|
|
818
|
+
"additionalHeater",
|
|
819
|
+
"holiday"
|
|
820
|
+
];
|
|
821
|
+
var HEAT_PUMP_ONLY_READINGS = [
|
|
822
|
+
"thermalW",
|
|
823
|
+
"electricalW",
|
|
824
|
+
"compressorPct"
|
|
825
|
+
];
|
|
796
826
|
/**
|
|
797
|
-
* True when
|
|
798
|
-
*
|
|
799
|
-
*
|
|
827
|
+
* True when the gateway should be offered as a system device (`hp:0`).
|
|
828
|
+
*
|
|
829
|
+
* A system mode counts when the gateway answered it: silent, away, the
|
|
830
|
+
* additional heater, or holiday. An empty string is an answer (holiday often
|
|
831
|
+
* comes back empty); a path that did not answer is not. HomeCom returns no
|
|
832
|
+
* value for silent mode and the auxiliary heater on some heat-pump firmware,
|
|
833
|
+
* so away and holiday alone are enough. A heat-pump-only reading counts too
|
|
834
|
+
* (thermal power, electrical power, compressor modulation). Supply, return,
|
|
835
|
+
* outdoor, pressure, starts and the energy meters do not: a gas boiler's heat
|
|
836
|
+
* source answers flow, pressure and starts, and that is not a heat pump.
|
|
800
837
|
*/
|
|
801
|
-
function
|
|
838
|
+
function hasSystemDevice(reading) {
|
|
839
|
+
if (SYSTEM_MODE_KEYS.some((key) => reading[key] !== null)) return true;
|
|
840
|
+
return HEAT_PUMP_ONLY_READINGS.some((key) => reading[key] !== null);
|
|
841
|
+
}
|
|
842
|
+
/** Which heat-pump paths this gateway answered. One log per gateway corrects the path table. */
|
|
843
|
+
function heatPumpPathCensus(reading) {
|
|
844
|
+
const rows = [
|
|
845
|
+
...systemPathRows(reading),
|
|
846
|
+
...readingPathRows(reading),
|
|
847
|
+
...reading.circuits.flatMap(circuitPathRows)
|
|
848
|
+
];
|
|
849
|
+
const answered = [];
|
|
850
|
+
const unanswered = [];
|
|
851
|
+
for (const row of rows) if (row.present) answered.push(row.path);
|
|
852
|
+
else unanswered.push(row.path);
|
|
853
|
+
return {
|
|
854
|
+
answered,
|
|
855
|
+
unanswered
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function systemPathRows(reading) {
|
|
859
|
+
const s = HEAT_PUMP_PATHS.system;
|
|
860
|
+
return [
|
|
861
|
+
{
|
|
862
|
+
path: s.silent,
|
|
863
|
+
present: reading.silent !== null
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
path: s.away,
|
|
867
|
+
present: reading.away !== null
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
path: s.additionalHeater,
|
|
871
|
+
present: reading.additionalHeater !== null
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
path: s.holiday,
|
|
875
|
+
present: reading.holiday !== null
|
|
876
|
+
}
|
|
877
|
+
];
|
|
878
|
+
}
|
|
879
|
+
function readingPathRows(reading) {
|
|
880
|
+
const r = HEAT_PUMP_PATHS.readings;
|
|
881
|
+
return [
|
|
882
|
+
{
|
|
883
|
+
path: r.thermalW,
|
|
884
|
+
present: reading.thermalW !== null
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
path: r.electricalW,
|
|
888
|
+
present: reading.electricalW !== null
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
path: r.compressorPct,
|
|
892
|
+
present: reading.compressorPct !== null
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
path: r.supply,
|
|
896
|
+
present: reading.supply !== null
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
path: r.return,
|
|
900
|
+
present: reading.return !== null
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
path: r.outdoor,
|
|
904
|
+
present: reading.outdoor !== null
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
path: r.thermalKwh,
|
|
908
|
+
present: reading.thermalKwh !== null
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
path: r.electricalKwh,
|
|
912
|
+
present: reading.electricalKwh !== null
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
path: r.pressureBar,
|
|
916
|
+
present: reading.pressureBar !== null
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
path: r.starts,
|
|
920
|
+
present: reading.starts !== null
|
|
921
|
+
}
|
|
922
|
+
];
|
|
923
|
+
}
|
|
924
|
+
function circuitPathRows(circuit) {
|
|
925
|
+
if (circuit.kind === "dhw") return dhwPathRows(circuit);
|
|
926
|
+
return hcPathRows(circuit);
|
|
927
|
+
}
|
|
928
|
+
function hcPathRows(circuit) {
|
|
929
|
+
const base = `/heatingCircuits/${circuit.id}`;
|
|
930
|
+
const p = HEAT_PUMP_PATHS.circuit;
|
|
802
931
|
return [
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
932
|
+
{
|
|
933
|
+
path: `${base}/${p.operationMode}`,
|
|
934
|
+
present: circuit.operationMode !== null
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
path: `${base}/${p.heatCoolMode}`,
|
|
938
|
+
present: circuit.heatCoolMode !== null
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
path: `${base}/${p.summerWinter}`,
|
|
942
|
+
present: circuit.summerWinter !== null
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
path: `${base}/${p.comfort}`,
|
|
946
|
+
present: circuit.comfort !== null
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
path: `${base}/${p.eco}`,
|
|
950
|
+
present: circuit.eco !== null
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
path: `${base}/${p.maxFlow}`,
|
|
954
|
+
present: circuit.maxFlow !== null
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
path: `${base}/${p.current}`,
|
|
958
|
+
present: circuit.currentTemp !== null
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
path: `${base}/${p.target}`,
|
|
962
|
+
present: circuit.targetTemp !== null
|
|
963
|
+
}
|
|
964
|
+
];
|
|
965
|
+
}
|
|
966
|
+
function dhwPathRows(circuit) {
|
|
967
|
+
const base = `/dhwCircuits/${circuit.id}`;
|
|
968
|
+
const p = HEAT_PUMP_PATHS.dhw;
|
|
969
|
+
return [
|
|
970
|
+
{
|
|
971
|
+
path: `${base}/${p.operationMode}`,
|
|
972
|
+
present: circuit.operationMode !== null
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
path: `${base}/${p.level}`,
|
|
976
|
+
present: circuit.level !== null
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
path: `${base}/${p.chargeDuration}`,
|
|
980
|
+
present: circuit.chargeDuration !== null
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
path: `${base}/${p.charge}`,
|
|
984
|
+
present: circuit.charge !== null
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
path: `${base}/${p.current}`,
|
|
988
|
+
present: circuit.currentTemp !== null
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
path: `${base}/${p.target}`,
|
|
992
|
+
present: circuit.targetTemp !== null
|
|
993
|
+
}
|
|
994
|
+
];
|
|
809
995
|
}
|
|
810
996
|
async function readHc(get, id) {
|
|
811
997
|
const base = `/heatingCircuits/${id}`;
|
|
@@ -910,7 +1096,6 @@ function heatPumpStatusFrom(reading, now) {
|
|
|
910
1096
|
}
|
|
911
1097
|
function circuitOf(c) {
|
|
912
1098
|
const kind = kindOf(c);
|
|
913
|
-
const mode = textOf$1(c.operationMode);
|
|
914
1099
|
return {
|
|
915
1100
|
id: c.id,
|
|
916
1101
|
kind,
|
|
@@ -918,7 +1103,7 @@ function circuitOf(c) {
|
|
|
918
1103
|
icon: kind === "dhw" ? "droplets" : kind === "cooling" ? "snowflake" : "flame",
|
|
919
1104
|
currentTemp: c.currentTemp,
|
|
920
1105
|
targetTemp: c.targetTemp,
|
|
921
|
-
mode,
|
|
1106
|
+
mode: modeValue(c.operationMode),
|
|
922
1107
|
active: null,
|
|
923
1108
|
operationMode: optionOf(c.operationMode),
|
|
924
1109
|
heatCoolMode: optionOf(c.heatCoolMode),
|
|
@@ -938,15 +1123,24 @@ function kindOf(c) {
|
|
|
938
1123
|
function labelOf(c) {
|
|
939
1124
|
const n = c.id.replace(/^\D+/, "");
|
|
940
1125
|
if (c.kind === "dhw") return n === "1" || n === "" ? "Hot water" : `Hot water ${n}`;
|
|
941
|
-
return n === "" ? "
|
|
1126
|
+
return n === "" ? "Heating circuit" : `Heating circuit ${n}`;
|
|
942
1127
|
}
|
|
943
1128
|
function textOf$1(s) {
|
|
944
1129
|
return s !== null && typeof s.value === "string" && s.value.length > 0 ? s.value : null;
|
|
945
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* A mode the gateway answered with an empty value is off (or "none" when the
|
|
1133
|
+
* gateway did not list off). A mode it did not answer at all stays null.
|
|
1134
|
+
*/
|
|
1135
|
+
function modeValue(s) {
|
|
1136
|
+
if (s === null || typeof s.value !== "string") return null;
|
|
1137
|
+
if (s.value.length > 0) return s.value;
|
|
1138
|
+
return s.allowed.includes("off") ? "off" : "none";
|
|
1139
|
+
}
|
|
946
1140
|
function optionOf(s) {
|
|
947
1141
|
if (s === null) return null;
|
|
948
1142
|
return {
|
|
949
|
-
value:
|
|
1143
|
+
value: modeValue(s),
|
|
950
1144
|
allowed: [...s.allowed]
|
|
951
1145
|
};
|
|
952
1146
|
}
|
|
@@ -1113,6 +1307,7 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1113
1307
|
let last = null;
|
|
1114
1308
|
let refreshTail = Promise.resolve();
|
|
1115
1309
|
const dropped = /* @__PURE__ */ new Set();
|
|
1310
|
+
let heatPumpPathsLogged = false;
|
|
1116
1311
|
const base = homeComApiBase(config.region);
|
|
1117
1312
|
function authHeaders() {
|
|
1118
1313
|
return {
|
|
@@ -1267,11 +1462,24 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1267
1462
|
dhw: dhwId === void 0 ? [] : [dhwId]
|
|
1268
1463
|
}
|
|
1269
1464
|
});
|
|
1270
|
-
|
|
1465
|
+
logHeatPumpPaths(heatPump);
|
|
1466
|
+
return hasSystemDevice(heatPump) ? {
|
|
1271
1467
|
...snapshot,
|
|
1272
1468
|
heatPump
|
|
1273
1469
|
} : snapshot;
|
|
1274
1470
|
}
|
|
1471
|
+
function logHeatPumpPaths(reading) {
|
|
1472
|
+
if (heatPumpPathsLogged) return;
|
|
1473
|
+
heatPumpPathsLogged = true;
|
|
1474
|
+
const census = heatPumpPathCensus(reading);
|
|
1475
|
+
logger?.info("HomeCom heat-pump paths", {
|
|
1476
|
+
tags: { gatewayId },
|
|
1477
|
+
meta: {
|
|
1478
|
+
answered: census.answered,
|
|
1479
|
+
unanswered: census.unanswered
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1275
1483
|
function logHeatPumpFailure(path, error) {
|
|
1276
1484
|
const key = `heat-pump-read:${path}`;
|
|
1277
1485
|
if (dropped.has(key)) return;
|
package/dist/homecom/addon.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as RecordIntegrationInstances, B as array, F as entryBody, G as string, H as number, M as customAction, N as defineCustomActions, P as defineEntryKind, R as rowsDepsOver, U as object, V as literal, X as errMsg, Y as DeviceType, Z as EventCategory, _ as heatPumpControlCapability, a as BaseDevice, d as climateControlCapability, i as testBoschInstance, k as waterHeaterCapability, n as boschRowsPorts, o as BaseDeviceProvider, p as deviceAdoptionCapability, r as keepBodyKeys, t as boschRowsMigration, v as heatPumpStatusCapability, x as integrationInstancesCapability, z as _enum } from "../bosch-instances-
|
|
2
|
-
import { a as loginFailureForAttempt, i as createSignInAttemptStore, n as cascadeRemoveDevicesForIntegration, o as parseOAuthCallback, r as buildSignInGuideFields, s as HOMECOM_REDIRECT_URI, t as setIntegrationDevicesOnline } from "../account-integration-offline-
|
|
1
|
+
import { A as RecordIntegrationInstances, B as array, F as entryBody, G as string, H as number, M as customAction, N as defineCustomActions, P as defineEntryKind, R as rowsDepsOver, U as object, V as literal, X as errMsg, Y as DeviceType, Z as EventCategory, _ as heatPumpControlCapability, a as BaseDevice, d as climateControlCapability, i as testBoschInstance, k as waterHeaterCapability, n as boschRowsPorts, o as BaseDeviceProvider, p as deviceAdoptionCapability, r as keepBodyKeys, t as boschRowsMigration, v as heatPumpStatusCapability, x as integrationInstancesCapability, z as _enum } from "../bosch-instances-DkUo6BfT.mjs";
|
|
2
|
+
import { a as loginFailureForAttempt, i as createSignInAttemptStore, n as cascadeRemoveDevicesForIntegration, o as parseOAuthCallback, r as buildSignInGuideFields, s as HOMECOM_REDIRECT_URI, t as setIntegrationDevicesOnline } from "../account-integration-offline-BGH1q3tj.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
//#region src/homecom/config.ts
|
|
5
5
|
var HOMECOM_BRANDS = ["bosch", "buderus"];
|
|
@@ -448,7 +448,7 @@ async function readHydronic(input) {
|
|
|
448
448
|
const heating = await input.get("/heatingCircuits");
|
|
449
449
|
const dhwBody = await input.get("/dhwCircuits");
|
|
450
450
|
const circuits = [];
|
|
451
|
-
for (const
|
|
451
|
+
for (const ref of circuitRefs(heating, "hc")) circuits.push(await readCircuit(input.get, ref.id, ref.name));
|
|
452
452
|
const dhwIds = referenceKeys(dhwBody, "dhw");
|
|
453
453
|
const first = dhwIds[0];
|
|
454
454
|
const dhw = first === void 0 ? null : await readDhw(input.get, first);
|
|
@@ -508,7 +508,7 @@ async function readRac(input) {
|
|
|
508
508
|
setpointResource: "/airConditioning/temperatureSetpoint"
|
|
509
509
|
}], null);
|
|
510
510
|
}
|
|
511
|
-
async function readCircuit(get, id) {
|
|
511
|
+
async function readCircuit(get, id, givenName) {
|
|
512
512
|
const base = `/heatingCircuits/${id}`;
|
|
513
513
|
const loaded = await loadEnvelopes(get, [
|
|
514
514
|
`${base}/operationMode`,
|
|
@@ -531,7 +531,7 @@ async function readCircuit(get, id) {
|
|
|
531
531
|
const humidity = numberOf(loaded.get(`${base}/actualHumidity`) ?? null);
|
|
532
532
|
return {
|
|
533
533
|
id,
|
|
534
|
-
name: id,
|
|
534
|
+
name: heatingCircuitName(id, givenName),
|
|
535
535
|
operationMode: stringOf(operation) ?? "off",
|
|
536
536
|
setpoint: cooling ? numberOf(coolingSet) ?? numberOf(current) ?? numberOf(manual) : numberOf(current) ?? numberOf(manual),
|
|
537
537
|
currentRoomTemp: numberOf(loaded.get(`${base}/roomtemperature`) ?? null),
|
|
@@ -633,18 +633,37 @@ function pointtMode(mode) {
|
|
|
633
633
|
default: return mode;
|
|
634
634
|
}
|
|
635
635
|
}
|
|
636
|
-
|
|
636
|
+
/** `hc1` → "Heating circuit 1", unless the gateway already named the circuit. */
|
|
637
|
+
function heatingCircuitName(id, given) {
|
|
638
|
+
if (given !== null && given.length > 0 && given !== id) return given;
|
|
639
|
+
const n = id.replace(/^hc/, "");
|
|
640
|
+
return /^\d+$/.test(n) ? `Heating circuit ${n}` : "Heating circuit";
|
|
641
|
+
}
|
|
642
|
+
function circuitRefs(body, prefix) {
|
|
637
643
|
if (!isRecord$2(body) || !Array.isArray(body["references"])) return [];
|
|
638
644
|
const pattern = prefix === "hc" ? /^hc\d+$/ : /^dhw\d+$/;
|
|
639
645
|
const out = [];
|
|
640
646
|
for (const ref of body["references"]) {
|
|
641
647
|
if (!isRecord$2(ref) || typeof ref["id"] !== "string") continue;
|
|
642
648
|
const tail = ref["id"].split("/").filter((part) => part.length > 0).at(-1);
|
|
643
|
-
if (tail === void 0 || !pattern.test(tail) || out.
|
|
644
|
-
out.push(
|
|
649
|
+
if (tail === void 0 || !pattern.test(tail) || out.some((item) => item.id === tail)) continue;
|
|
650
|
+
out.push({
|
|
651
|
+
id: tail,
|
|
652
|
+
name: refName(ref, tail)
|
|
653
|
+
});
|
|
645
654
|
}
|
|
646
655
|
return out;
|
|
647
656
|
}
|
|
657
|
+
function refName(ref, id) {
|
|
658
|
+
const raw = ref["name"];
|
|
659
|
+
if (typeof raw !== "string") return null;
|
|
660
|
+
const trimmed = raw.trim();
|
|
661
|
+
if (trimmed.length === 0 || trimmed === id) return null;
|
|
662
|
+
return trimmed;
|
|
663
|
+
}
|
|
664
|
+
function referenceKeys(body, prefix) {
|
|
665
|
+
return circuitRefs(body, prefix).map((ref) => ref.id);
|
|
666
|
+
}
|
|
648
667
|
function indexStandard(body) {
|
|
649
668
|
const map = /* @__PURE__ */ new Map();
|
|
650
669
|
if (!isRecord$2(body) || !Array.isArray(body["references"])) return map;
|
|
@@ -792,19 +811,186 @@ function safeGet(get, onFailure) {
|
|
|
792
811
|
}
|
|
793
812
|
};
|
|
794
813
|
}
|
|
814
|
+
var SYSTEM_MODE_KEYS = [
|
|
815
|
+
"silent",
|
|
816
|
+
"away",
|
|
817
|
+
"additionalHeater",
|
|
818
|
+
"holiday"
|
|
819
|
+
];
|
|
820
|
+
var HEAT_PUMP_ONLY_READINGS = [
|
|
821
|
+
"thermalW",
|
|
822
|
+
"electricalW",
|
|
823
|
+
"compressorPct"
|
|
824
|
+
];
|
|
795
825
|
/**
|
|
796
|
-
* True when
|
|
797
|
-
*
|
|
798
|
-
*
|
|
826
|
+
* True when the gateway should be offered as a system device (`hp:0`).
|
|
827
|
+
*
|
|
828
|
+
* A system mode counts when the gateway answered it: silent, away, the
|
|
829
|
+
* additional heater, or holiday. An empty string is an answer (holiday often
|
|
830
|
+
* comes back empty); a path that did not answer is not. HomeCom returns no
|
|
831
|
+
* value for silent mode and the auxiliary heater on some heat-pump firmware,
|
|
832
|
+
* so away and holiday alone are enough. A heat-pump-only reading counts too
|
|
833
|
+
* (thermal power, electrical power, compressor modulation). Supply, return,
|
|
834
|
+
* outdoor, pressure, starts and the energy meters do not: a gas boiler's heat
|
|
835
|
+
* source answers flow, pressure and starts, and that is not a heat pump.
|
|
799
836
|
*/
|
|
800
|
-
function
|
|
837
|
+
function hasSystemDevice(reading) {
|
|
838
|
+
if (SYSTEM_MODE_KEYS.some((key) => reading[key] !== null)) return true;
|
|
839
|
+
return HEAT_PUMP_ONLY_READINGS.some((key) => reading[key] !== null);
|
|
840
|
+
}
|
|
841
|
+
/** Which heat-pump paths this gateway answered. One log per gateway corrects the path table. */
|
|
842
|
+
function heatPumpPathCensus(reading) {
|
|
843
|
+
const rows = [
|
|
844
|
+
...systemPathRows(reading),
|
|
845
|
+
...readingPathRows(reading),
|
|
846
|
+
...reading.circuits.flatMap(circuitPathRows)
|
|
847
|
+
];
|
|
848
|
+
const answered = [];
|
|
849
|
+
const unanswered = [];
|
|
850
|
+
for (const row of rows) if (row.present) answered.push(row.path);
|
|
851
|
+
else unanswered.push(row.path);
|
|
852
|
+
return {
|
|
853
|
+
answered,
|
|
854
|
+
unanswered
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function systemPathRows(reading) {
|
|
858
|
+
const s = HEAT_PUMP_PATHS.system;
|
|
859
|
+
return [
|
|
860
|
+
{
|
|
861
|
+
path: s.silent,
|
|
862
|
+
present: reading.silent !== null
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
path: s.away,
|
|
866
|
+
present: reading.away !== null
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
path: s.additionalHeater,
|
|
870
|
+
present: reading.additionalHeater !== null
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
path: s.holiday,
|
|
874
|
+
present: reading.holiday !== null
|
|
875
|
+
}
|
|
876
|
+
];
|
|
877
|
+
}
|
|
878
|
+
function readingPathRows(reading) {
|
|
879
|
+
const r = HEAT_PUMP_PATHS.readings;
|
|
880
|
+
return [
|
|
881
|
+
{
|
|
882
|
+
path: r.thermalW,
|
|
883
|
+
present: reading.thermalW !== null
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
path: r.electricalW,
|
|
887
|
+
present: reading.electricalW !== null
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
path: r.compressorPct,
|
|
891
|
+
present: reading.compressorPct !== null
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
path: r.supply,
|
|
895
|
+
present: reading.supply !== null
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
path: r.return,
|
|
899
|
+
present: reading.return !== null
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
path: r.outdoor,
|
|
903
|
+
present: reading.outdoor !== null
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
path: r.thermalKwh,
|
|
907
|
+
present: reading.thermalKwh !== null
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
path: r.electricalKwh,
|
|
911
|
+
present: reading.electricalKwh !== null
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
path: r.pressureBar,
|
|
915
|
+
present: reading.pressureBar !== null
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
path: r.starts,
|
|
919
|
+
present: reading.starts !== null
|
|
920
|
+
}
|
|
921
|
+
];
|
|
922
|
+
}
|
|
923
|
+
function circuitPathRows(circuit) {
|
|
924
|
+
if (circuit.kind === "dhw") return dhwPathRows(circuit);
|
|
925
|
+
return hcPathRows(circuit);
|
|
926
|
+
}
|
|
927
|
+
function hcPathRows(circuit) {
|
|
928
|
+
const base = `/heatingCircuits/${circuit.id}`;
|
|
929
|
+
const p = HEAT_PUMP_PATHS.circuit;
|
|
801
930
|
return [
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
931
|
+
{
|
|
932
|
+
path: `${base}/${p.operationMode}`,
|
|
933
|
+
present: circuit.operationMode !== null
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
path: `${base}/${p.heatCoolMode}`,
|
|
937
|
+
present: circuit.heatCoolMode !== null
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
path: `${base}/${p.summerWinter}`,
|
|
941
|
+
present: circuit.summerWinter !== null
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
path: `${base}/${p.comfort}`,
|
|
945
|
+
present: circuit.comfort !== null
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
path: `${base}/${p.eco}`,
|
|
949
|
+
present: circuit.eco !== null
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
path: `${base}/${p.maxFlow}`,
|
|
953
|
+
present: circuit.maxFlow !== null
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
path: `${base}/${p.current}`,
|
|
957
|
+
present: circuit.currentTemp !== null
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
path: `${base}/${p.target}`,
|
|
961
|
+
present: circuit.targetTemp !== null
|
|
962
|
+
}
|
|
963
|
+
];
|
|
964
|
+
}
|
|
965
|
+
function dhwPathRows(circuit) {
|
|
966
|
+
const base = `/dhwCircuits/${circuit.id}`;
|
|
967
|
+
const p = HEAT_PUMP_PATHS.dhw;
|
|
968
|
+
return [
|
|
969
|
+
{
|
|
970
|
+
path: `${base}/${p.operationMode}`,
|
|
971
|
+
present: circuit.operationMode !== null
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
path: `${base}/${p.level}`,
|
|
975
|
+
present: circuit.level !== null
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
path: `${base}/${p.chargeDuration}`,
|
|
979
|
+
present: circuit.chargeDuration !== null
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
path: `${base}/${p.charge}`,
|
|
983
|
+
present: circuit.charge !== null
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
path: `${base}/${p.current}`,
|
|
987
|
+
present: circuit.currentTemp !== null
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
path: `${base}/${p.target}`,
|
|
991
|
+
present: circuit.targetTemp !== null
|
|
992
|
+
}
|
|
993
|
+
];
|
|
808
994
|
}
|
|
809
995
|
async function readHc(get, id) {
|
|
810
996
|
const base = `/heatingCircuits/${id}`;
|
|
@@ -909,7 +1095,6 @@ function heatPumpStatusFrom(reading, now) {
|
|
|
909
1095
|
}
|
|
910
1096
|
function circuitOf(c) {
|
|
911
1097
|
const kind = kindOf(c);
|
|
912
|
-
const mode = textOf$1(c.operationMode);
|
|
913
1098
|
return {
|
|
914
1099
|
id: c.id,
|
|
915
1100
|
kind,
|
|
@@ -917,7 +1102,7 @@ function circuitOf(c) {
|
|
|
917
1102
|
icon: kind === "dhw" ? "droplets" : kind === "cooling" ? "snowflake" : "flame",
|
|
918
1103
|
currentTemp: c.currentTemp,
|
|
919
1104
|
targetTemp: c.targetTemp,
|
|
920
|
-
mode,
|
|
1105
|
+
mode: modeValue(c.operationMode),
|
|
921
1106
|
active: null,
|
|
922
1107
|
operationMode: optionOf(c.operationMode),
|
|
923
1108
|
heatCoolMode: optionOf(c.heatCoolMode),
|
|
@@ -937,15 +1122,24 @@ function kindOf(c) {
|
|
|
937
1122
|
function labelOf(c) {
|
|
938
1123
|
const n = c.id.replace(/^\D+/, "");
|
|
939
1124
|
if (c.kind === "dhw") return n === "1" || n === "" ? "Hot water" : `Hot water ${n}`;
|
|
940
|
-
return n === "" ? "
|
|
1125
|
+
return n === "" ? "Heating circuit" : `Heating circuit ${n}`;
|
|
941
1126
|
}
|
|
942
1127
|
function textOf$1(s) {
|
|
943
1128
|
return s !== null && typeof s.value === "string" && s.value.length > 0 ? s.value : null;
|
|
944
1129
|
}
|
|
1130
|
+
/**
|
|
1131
|
+
* A mode the gateway answered with an empty value is off (or "none" when the
|
|
1132
|
+
* gateway did not list off). A mode it did not answer at all stays null.
|
|
1133
|
+
*/
|
|
1134
|
+
function modeValue(s) {
|
|
1135
|
+
if (s === null || typeof s.value !== "string") return null;
|
|
1136
|
+
if (s.value.length > 0) return s.value;
|
|
1137
|
+
return s.allowed.includes("off") ? "off" : "none";
|
|
1138
|
+
}
|
|
945
1139
|
function optionOf(s) {
|
|
946
1140
|
if (s === null) return null;
|
|
947
1141
|
return {
|
|
948
|
-
value:
|
|
1142
|
+
value: modeValue(s),
|
|
949
1143
|
allowed: [...s.allowed]
|
|
950
1144
|
};
|
|
951
1145
|
}
|
|
@@ -1112,6 +1306,7 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1112
1306
|
let last = null;
|
|
1113
1307
|
let refreshTail = Promise.resolve();
|
|
1114
1308
|
const dropped = /* @__PURE__ */ new Set();
|
|
1309
|
+
let heatPumpPathsLogged = false;
|
|
1115
1310
|
const base = homeComApiBase(config.region);
|
|
1116
1311
|
function authHeaders() {
|
|
1117
1312
|
return {
|
|
@@ -1266,11 +1461,24 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1266
1461
|
dhw: dhwId === void 0 ? [] : [dhwId]
|
|
1267
1462
|
}
|
|
1268
1463
|
});
|
|
1269
|
-
|
|
1464
|
+
logHeatPumpPaths(heatPump);
|
|
1465
|
+
return hasSystemDevice(heatPump) ? {
|
|
1270
1466
|
...snapshot,
|
|
1271
1467
|
heatPump
|
|
1272
1468
|
} : snapshot;
|
|
1273
1469
|
}
|
|
1470
|
+
function logHeatPumpPaths(reading) {
|
|
1471
|
+
if (heatPumpPathsLogged) return;
|
|
1472
|
+
heatPumpPathsLogged = true;
|
|
1473
|
+
const census = heatPumpPathCensus(reading);
|
|
1474
|
+
logger?.info("HomeCom heat-pump paths", {
|
|
1475
|
+
tags: { gatewayId },
|
|
1476
|
+
meta: {
|
|
1477
|
+
answered: census.answered,
|
|
1478
|
+
unanswered: census.unanswered
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1274
1482
|
function logHeatPumpFailure(path, error) {
|
|
1275
1483
|
const key = `heat-pump-read:${path}`;
|
|
1276
1484
|
if (dropped.has(key)) return;
|
package/dist/shc/addon.js
CHANGED
|
@@ -2,7 +2,7 @@ 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_bosch_instances = require("../bosch-instances-
|
|
5
|
+
const require_bosch_instances = require("../bosch-instances-BD-Valeo.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");
|
package/dist/shc/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as RecordIntegrationInstances, B as array, D as switchCapability, F as entryBody, G as string, H as number, M as customAction, N as defineCustomActions, O as temperatureSensorCapability, P as defineEntryKind, R as rowsDepsOver, U as object, V as literal, X as errMsg, Y as DeviceType, Z as EventCategory, a as BaseDevice, d as climateControlCapability, i as testBoschInstance, l as batteryCapability, n as boschRowsPorts, o as BaseDeviceProvider, p as deviceAdoptionCapability, r as keepBodyKeys, t as boschRowsMigration, x as integrationInstancesCapability, y as heatingValveCapability, z as _enum } from "../bosch-instances-
|
|
1
|
+
import { A as RecordIntegrationInstances, B as array, D as switchCapability, F as entryBody, G as string, H as number, M as customAction, N as defineCustomActions, O as temperatureSensorCapability, P as defineEntryKind, R as rowsDepsOver, U as object, V as literal, X as errMsg, Y as DeviceType, Z as EventCategory, a as BaseDevice, d as climateControlCapability, i as testBoschInstance, l as batteryCapability, n as boschRowsPorts, o as BaseDeviceProvider, p as deviceAdoptionCapability, r as keepBodyKeys, t as boschRowsMigration, x as integrationInstancesCapability, y as heatingValveCapability, z as _enum } from "../bosch-instances-DkUo6BfT.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { request } from "node:https";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-bosch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"description": "Bosch smart-home device-provider addons for CamStack — SHC, Home Connect, and HomeCom cloud integrations (scaffold)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
}
|
|
139
139
|
],
|
|
140
140
|
"requires": {
|
|
141
|
-
"server": ">=1.2.
|
|
141
|
+
"server": ">=1.2.822"
|
|
142
142
|
}
|
|
143
143
|
},
|
|
144
144
|
"files": [
|