@camstack/addon-provider-bosch 0.1.47 → 0.1.49
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 +12 -0
- package/dist/{account-integration-offline-Bie4aS5e.mjs → account-integration-offline-BKDol6kz.mjs} +1 -1
- package/dist/{account-integration-offline-C6tzFB-H.js → account-integration-offline-Dcx0sq_v.js} +1 -1
- package/dist/{bosch-instances-DAqkAsRO.mjs → bosch-instances-B2e4DAI1.mjs} +753 -561
- package/dist/{bosch-instances-D0eF-Jjy.js → bosch-instances-BHFcR1TK.js} +753 -561
- package/dist/home-connect/addon.js +2 -2
- package/dist/home-connect/addon.mjs +2 -2
- package/dist/homecom/addon.js +113 -18
- package/dist/homecom/addon.mjs +113 -18
- package/dist/shc/addon.js +1 -1
- package/dist/shc/addon.mjs +1 -1
- package/package.json +2 -2
|
@@ -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-BHFcR1TK.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-Dcx0sq_v.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-B2e4DAI1.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-BKDol6kz.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-BHFcR1TK.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-Dcx0sq_v.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
//#region src/homecom/config.ts
|
|
6
6
|
var HOMECOM_BRANDS = ["bosch", "buderus"];
|
|
@@ -735,6 +735,14 @@ var HEAT_PUMP_PATHS = {
|
|
|
735
735
|
pressureBar: "/heatSources/hs1/waterPressure",
|
|
736
736
|
starts: "/heatSources/hs1/numberOfStarts"
|
|
737
737
|
},
|
|
738
|
+
/**
|
|
739
|
+
* Resources only a heat pump's heat source has. A gateway that answers one of them with
|
|
740
|
+
* anything but 404 (a 403 or a 500 included) has a heat pump it will not let us read.
|
|
741
|
+
*/
|
|
742
|
+
identity: {
|
|
743
|
+
heatPumpType: "/heatSources/hs1/heatPumpType",
|
|
744
|
+
heatPumpPower: "/heatSources/hs1/heatPumpPower"
|
|
745
|
+
},
|
|
738
746
|
circuit: {
|
|
739
747
|
operationMode: "operationMode",
|
|
740
748
|
heatCoolMode: "heatCoolMode",
|
|
@@ -763,6 +771,7 @@ async function readHeatPump(input) {
|
|
|
763
771
|
const circuits = await Promise.all([...input.circuitIds.hc.map((id) => readHc(get, id)), ...input.circuitIds.dhw.map((id) => readDhwCircuit(get, id))]);
|
|
764
772
|
const r = HEAT_PUMP_PATHS.readings;
|
|
765
773
|
const s = HEAT_PUMP_PATHS.system;
|
|
774
|
+
const heatSourceAnswered = await anyAnswers(input.answers, input.onFailure, [HEAT_PUMP_PATHS.identity.heatPumpType, HEAT_PUMP_PATHS.identity.heatPumpPower]);
|
|
766
775
|
const [silent, away, additionalHeater, holiday] = await Promise.all([
|
|
767
776
|
setting(get, s.silent),
|
|
768
777
|
setting(get, s.away),
|
|
@@ -785,6 +794,7 @@ async function readHeatPump(input) {
|
|
|
785
794
|
]);
|
|
786
795
|
return {
|
|
787
796
|
circuits,
|
|
797
|
+
heatSourceAnswered,
|
|
788
798
|
silent,
|
|
789
799
|
away,
|
|
790
800
|
additionalHeater,
|
|
@@ -801,6 +811,17 @@ async function readHeatPump(input) {
|
|
|
801
811
|
starts
|
|
802
812
|
};
|
|
803
813
|
}
|
|
814
|
+
async function anyAnswers(answers, onFailure, paths) {
|
|
815
|
+
if (answers === void 0) return false;
|
|
816
|
+
return (await Promise.all(paths.map(async (path) => {
|
|
817
|
+
try {
|
|
818
|
+
return await answers(path);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
onFailure?.(path, error);
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
}))).some(Boolean);
|
|
824
|
+
}
|
|
804
825
|
/** Any failure of a heat-pump read (HTTP 4xx/5xx, a throw) is "not answered", never an error. */
|
|
805
826
|
function safeGet(get, onFailure) {
|
|
806
827
|
return async (path) => {
|
|
@@ -828,16 +849,28 @@ var HEAT_PUMP_ONLY_READINGS = [
|
|
|
828
849
|
*
|
|
829
850
|
* A system mode counts when the gateway answered it: silent, away, the
|
|
830
851
|
* additional heater, or holiday. An empty string is an answer (holiday often
|
|
831
|
-
* comes back empty); a path that did not answer is not.
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
852
|
+
* comes back empty); a path that did not answer is not. A heat-pump-only
|
|
853
|
+
* reading counts too (thermal power, electrical power, compressor modulation).
|
|
854
|
+
* So does a circuit whose `heatCoolMode` can be cooling, and a heat source that
|
|
855
|
+
* answers a heat-pump-only resource with any status but 404: some gateways
|
|
856
|
+
* refuse (403) or fail (500) the whole system tree and still serve the
|
|
857
|
+
* circuits. Supply, return, outdoor, pressure, starts and the energy meters do
|
|
858
|
+
* not count: a gas boiler's heat source answers flow, pressure and starts, and
|
|
859
|
+
* that is not a heat pump.
|
|
837
860
|
*/
|
|
838
861
|
function hasSystemDevice(reading) {
|
|
839
862
|
if (SYSTEM_MODE_KEYS.some((key) => reading[key] !== null)) return true;
|
|
840
|
-
|
|
863
|
+
if (HEAT_PUMP_ONLY_READINGS.some((key) => reading[key] !== null)) return true;
|
|
864
|
+
if (reading.heatSourceAnswered) return true;
|
|
865
|
+
return reading.circuits.some((c) => c.kind === "hc" && offersCooling(c.heatCoolMode));
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* A boiler circuit can carry a `heatCoolMode` that only ever says heating, so the mode alone
|
|
869
|
+
* is no proof. A circuit that can switch to (or already is in) cooling is a heat pump's.
|
|
870
|
+
*/
|
|
871
|
+
function offersCooling(setting) {
|
|
872
|
+
if (setting === null) return false;
|
|
873
|
+
return setting.value === "cooling" || setting.allowed.includes("cooling");
|
|
841
874
|
}
|
|
842
875
|
/** Which heat-pump paths this gateway answered. One log per gateway corrects the path table. */
|
|
843
876
|
function heatPumpPathCensus(reading) {
|
|
@@ -1376,6 +1409,17 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1376
1409
|
if (!res.ok) throw new Error(`HomeCom API ${path} failed: HTTP ${res.status}`);
|
|
1377
1410
|
return res.body;
|
|
1378
1411
|
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Evidence that the resource exists: a 2xx, or the 403 the gateway gives a resource it
|
|
1414
|
+
* serves but will not show. 404 is "absent". Anything else (429, 5xx) is a transient
|
|
1415
|
+
* failure, so it is NOT evidence either way: it is logged and counts as unknown.
|
|
1416
|
+
*/
|
|
1417
|
+
async function resourceAnswers(path) {
|
|
1418
|
+
const res = await authed(homeComResourceUrl(gatewayId, path), { method: "GET" });
|
|
1419
|
+
if (res.ok || res.status === 403) return true;
|
|
1420
|
+
if (res.status !== 404) logHeatPumpFailure(path, /* @__PURE__ */ new Error(`HTTP ${res.status}`));
|
|
1421
|
+
return false;
|
|
1422
|
+
}
|
|
1379
1423
|
async function putWrite(write) {
|
|
1380
1424
|
const res = await authed(homeComResourceUrl(gatewayId, write.path), {
|
|
1381
1425
|
method: "PUT",
|
|
@@ -1457,6 +1501,7 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1457
1501
|
const heatPump = await readHeatPump({
|
|
1458
1502
|
get: getResource,
|
|
1459
1503
|
onFailure: logHeatPumpFailure,
|
|
1504
|
+
answers: resourceAnswers,
|
|
1460
1505
|
circuitIds: {
|
|
1461
1506
|
hc: snapshot.circuits.map((circuit) => circuit.id),
|
|
1462
1507
|
dhw: dhwId === void 0 ? [] : [dhwId]
|
|
@@ -1851,6 +1896,15 @@ var HomeComConnectionResolver = class {
|
|
|
1851
1896
|
this.#surfaces.clear();
|
|
1852
1897
|
}
|
|
1853
1898
|
};
|
|
1899
|
+
/** Native id of the gateway pick: a Gateway device, sibling of the circuits. */
|
|
1900
|
+
var HOMECOM_GATEWAY_NATIVE_ID = "gateway:0";
|
|
1901
|
+
function gatewayDiscoverable(snapshot) {
|
|
1902
|
+
return {
|
|
1903
|
+
nativeId: HOMECOM_GATEWAY_NATIVE_ID,
|
|
1904
|
+
name: snapshot.gateway.name.length > 0 ? snapshot.gateway.name : "HomeCom gateway",
|
|
1905
|
+
kind: "gateway"
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1854
1908
|
var BOILER_DHW_NAME = "Domestic hot water";
|
|
1855
1909
|
function circuitDiscoverable(circuit) {
|
|
1856
1910
|
return {
|
|
@@ -1868,18 +1922,25 @@ function dhwDiscoverable(name) {
|
|
|
1868
1922
|
};
|
|
1869
1923
|
}
|
|
1870
1924
|
/**
|
|
1871
|
-
* A gateway with a heat pump is one pick. A boiler still offers each circuit
|
|
1925
|
+
* The gateway itself is always the first pick. A gateway with a heat pump is one more pick. A boiler still offers each circuit
|
|
1872
1926
|
* and the hot-water row on its own.
|
|
1873
1927
|
*/
|
|
1874
1928
|
function buildDiscoverables(snapshot) {
|
|
1875
1929
|
const circuits = snapshot.circuits.map(circuitDiscoverable);
|
|
1876
|
-
if (snapshot.heatPump !== void 0)
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1930
|
+
if (snapshot.heatPump !== void 0) {
|
|
1931
|
+
const children = [...circuits, ...snapshot.dhw === null ? [] : [dhwDiscoverable(HOMECOM_HOT_WATER_CHILD_NAME)]];
|
|
1932
|
+
return [gatewayDiscoverable(snapshot), {
|
|
1933
|
+
nativeId: "hp:0",
|
|
1934
|
+
name: "Heat pump",
|
|
1935
|
+
kind: "heat-pump",
|
|
1936
|
+
children
|
|
1937
|
+
}];
|
|
1938
|
+
}
|
|
1939
|
+
return [
|
|
1940
|
+
gatewayDiscoverable(snapshot),
|
|
1941
|
+
...circuits,
|
|
1942
|
+
...snapshot.dhw === null ? [] : [dhwDiscoverable(BOILER_DHW_NAME)]
|
|
1943
|
+
];
|
|
1883
1944
|
}
|
|
1884
1945
|
var homeComConnections = new HomeComConnectionResolver();
|
|
1885
1946
|
//#endregion
|
|
@@ -1962,6 +2023,10 @@ function shapeOf(kind) {
|
|
|
1962
2023
|
type: require_bosch_instances.DeviceType.WaterHeater,
|
|
1963
2024
|
caps: ["water-heater"]
|
|
1964
2025
|
};
|
|
2026
|
+
case "gateway": return {
|
|
2027
|
+
type: require_bosch_instances.DeviceType.Gateway,
|
|
2028
|
+
caps: []
|
|
2029
|
+
};
|
|
1965
2030
|
case "heat-pump": return {
|
|
1966
2031
|
type: require_bosch_instances.DeviceType.HeatPump,
|
|
1967
2032
|
caps: ["heat-pump-status", "heat-pump-control"]
|
|
@@ -2809,6 +2874,35 @@ var HomeComWaterHeaterDevice = class extends require_bosch_instances.BaseDevice
|
|
|
2809
2874
|
}
|
|
2810
2875
|
};
|
|
2811
2876
|
//#endregion
|
|
2877
|
+
//#region src/homecom/devices/homecom-gateway-device.ts
|
|
2878
|
+
var homeComGatewaySchema = require_bosch_instances.object({
|
|
2879
|
+
nativeId: require_bosch_instances.string().min(1),
|
|
2880
|
+
brokerId: require_bosch_instances.string().min(1),
|
|
2881
|
+
system: require_bosch_instances.literal("homecom").optional(),
|
|
2882
|
+
integrationId: require_bosch_instances.string().optional()
|
|
2883
|
+
});
|
|
2884
|
+
/**
|
|
2885
|
+
* The HomeCom gateway, as a {@link DeviceType.Gateway}. Its connection lives on the
|
|
2886
|
+
* INTEGRATION; this device is the gateway's presence in the device list and follows the
|
|
2887
|
+
* integration's online state (its connect and disconnect callbacks flip it).
|
|
2888
|
+
*
|
|
2889
|
+
* It registers no action: the cloud account offers no reboot, no update and no local page,
|
|
2890
|
+
* so there is nothing real to expose. The circuits, the hot water and the heat pump are NOT
|
|
2891
|
+
* children of it; each is adopted as its own top-level device of the same integration.
|
|
2892
|
+
*/
|
|
2893
|
+
var HomeComGatewayDevice = class extends require_bosch_instances.BaseDevice {
|
|
2894
|
+
features = [];
|
|
2895
|
+
constructor(ctx) {
|
|
2896
|
+
const cfg = homeComGatewaySchema.parse(ctx.persistedConfig ?? {});
|
|
2897
|
+
super(ctx, homeComGatewaySchema, { type: require_bosch_instances.DeviceType.Gateway });
|
|
2898
|
+
this.online = homeComConnections.has(cfg.brokerId);
|
|
2899
|
+
this.updateSourceInfo({
|
|
2900
|
+
id: cfg.nativeId,
|
|
2901
|
+
system: "homecom"
|
|
2902
|
+
});
|
|
2903
|
+
}
|
|
2904
|
+
};
|
|
2905
|
+
//#endregion
|
|
2812
2906
|
//#region src/homecom/devices/homecom-heat-pump-device.ts
|
|
2813
2907
|
var homeComHeatPumpSchema = require_bosch_instances.object({
|
|
2814
2908
|
nativeId: require_bosch_instances.string().min(1),
|
|
@@ -2959,7 +3053,8 @@ var BoschHomeComProviderAddon = class extends require_bosch_instances.BaseDevice
|
|
|
2959
3053
|
deviceClasses = {
|
|
2960
3054
|
[require_bosch_instances.DeviceType.Climate]: HomeComClimateDevice,
|
|
2961
3055
|
[require_bosch_instances.DeviceType.WaterHeater]: HomeComWaterHeaterDevice,
|
|
2962
|
-
[require_bosch_instances.DeviceType.HeatPump]: HomeComHeatPumpDevice
|
|
3056
|
+
[require_bosch_instances.DeviceType.HeatPump]: HomeComHeatPumpDevice,
|
|
3057
|
+
[require_bosch_instances.DeviceType.Gateway]: HomeComGatewayDevice
|
|
2963
3058
|
};
|
|
2964
3059
|
clients = null;
|
|
2965
3060
|
instances = null;
|
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-B2e4DAI1.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-BKDol6kz.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
//#region src/homecom/config.ts
|
|
5
5
|
var HOMECOM_BRANDS = ["bosch", "buderus"];
|
|
@@ -734,6 +734,14 @@ var HEAT_PUMP_PATHS = {
|
|
|
734
734
|
pressureBar: "/heatSources/hs1/waterPressure",
|
|
735
735
|
starts: "/heatSources/hs1/numberOfStarts"
|
|
736
736
|
},
|
|
737
|
+
/**
|
|
738
|
+
* Resources only a heat pump's heat source has. A gateway that answers one of them with
|
|
739
|
+
* anything but 404 (a 403 or a 500 included) has a heat pump it will not let us read.
|
|
740
|
+
*/
|
|
741
|
+
identity: {
|
|
742
|
+
heatPumpType: "/heatSources/hs1/heatPumpType",
|
|
743
|
+
heatPumpPower: "/heatSources/hs1/heatPumpPower"
|
|
744
|
+
},
|
|
737
745
|
circuit: {
|
|
738
746
|
operationMode: "operationMode",
|
|
739
747
|
heatCoolMode: "heatCoolMode",
|
|
@@ -762,6 +770,7 @@ async function readHeatPump(input) {
|
|
|
762
770
|
const circuits = await Promise.all([...input.circuitIds.hc.map((id) => readHc(get, id)), ...input.circuitIds.dhw.map((id) => readDhwCircuit(get, id))]);
|
|
763
771
|
const r = HEAT_PUMP_PATHS.readings;
|
|
764
772
|
const s = HEAT_PUMP_PATHS.system;
|
|
773
|
+
const heatSourceAnswered = await anyAnswers(input.answers, input.onFailure, [HEAT_PUMP_PATHS.identity.heatPumpType, HEAT_PUMP_PATHS.identity.heatPumpPower]);
|
|
765
774
|
const [silent, away, additionalHeater, holiday] = await Promise.all([
|
|
766
775
|
setting(get, s.silent),
|
|
767
776
|
setting(get, s.away),
|
|
@@ -784,6 +793,7 @@ async function readHeatPump(input) {
|
|
|
784
793
|
]);
|
|
785
794
|
return {
|
|
786
795
|
circuits,
|
|
796
|
+
heatSourceAnswered,
|
|
787
797
|
silent,
|
|
788
798
|
away,
|
|
789
799
|
additionalHeater,
|
|
@@ -800,6 +810,17 @@ async function readHeatPump(input) {
|
|
|
800
810
|
starts
|
|
801
811
|
};
|
|
802
812
|
}
|
|
813
|
+
async function anyAnswers(answers, onFailure, paths) {
|
|
814
|
+
if (answers === void 0) return false;
|
|
815
|
+
return (await Promise.all(paths.map(async (path) => {
|
|
816
|
+
try {
|
|
817
|
+
return await answers(path);
|
|
818
|
+
} catch (error) {
|
|
819
|
+
onFailure?.(path, error);
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
}))).some(Boolean);
|
|
823
|
+
}
|
|
803
824
|
/** Any failure of a heat-pump read (HTTP 4xx/5xx, a throw) is "not answered", never an error. */
|
|
804
825
|
function safeGet(get, onFailure) {
|
|
805
826
|
return async (path) => {
|
|
@@ -827,16 +848,28 @@ var HEAT_PUMP_ONLY_READINGS = [
|
|
|
827
848
|
*
|
|
828
849
|
* A system mode counts when the gateway answered it: silent, away, the
|
|
829
850
|
* additional heater, or holiday. An empty string is an answer (holiday often
|
|
830
|
-
* comes back empty); a path that did not answer is not.
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
851
|
+
* comes back empty); a path that did not answer is not. A heat-pump-only
|
|
852
|
+
* reading counts too (thermal power, electrical power, compressor modulation).
|
|
853
|
+
* So does a circuit whose `heatCoolMode` can be cooling, and a heat source that
|
|
854
|
+
* answers a heat-pump-only resource with any status but 404: some gateways
|
|
855
|
+
* refuse (403) or fail (500) the whole system tree and still serve the
|
|
856
|
+
* circuits. Supply, return, outdoor, pressure, starts and the energy meters do
|
|
857
|
+
* not count: a gas boiler's heat source answers flow, pressure and starts, and
|
|
858
|
+
* that is not a heat pump.
|
|
836
859
|
*/
|
|
837
860
|
function hasSystemDevice(reading) {
|
|
838
861
|
if (SYSTEM_MODE_KEYS.some((key) => reading[key] !== null)) return true;
|
|
839
|
-
|
|
862
|
+
if (HEAT_PUMP_ONLY_READINGS.some((key) => reading[key] !== null)) return true;
|
|
863
|
+
if (reading.heatSourceAnswered) return true;
|
|
864
|
+
return reading.circuits.some((c) => c.kind === "hc" && offersCooling(c.heatCoolMode));
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* A boiler circuit can carry a `heatCoolMode` that only ever says heating, so the mode alone
|
|
868
|
+
* is no proof. A circuit that can switch to (or already is in) cooling is a heat pump's.
|
|
869
|
+
*/
|
|
870
|
+
function offersCooling(setting) {
|
|
871
|
+
if (setting === null) return false;
|
|
872
|
+
return setting.value === "cooling" || setting.allowed.includes("cooling");
|
|
840
873
|
}
|
|
841
874
|
/** Which heat-pump paths this gateway answered. One log per gateway corrects the path table. */
|
|
842
875
|
function heatPumpPathCensus(reading) {
|
|
@@ -1375,6 +1408,17 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1375
1408
|
if (!res.ok) throw new Error(`HomeCom API ${path} failed: HTTP ${res.status}`);
|
|
1376
1409
|
return res.body;
|
|
1377
1410
|
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Evidence that the resource exists: a 2xx, or the 403 the gateway gives a resource it
|
|
1413
|
+
* serves but will not show. 404 is "absent". Anything else (429, 5xx) is a transient
|
|
1414
|
+
* failure, so it is NOT evidence either way: it is logged and counts as unknown.
|
|
1415
|
+
*/
|
|
1416
|
+
async function resourceAnswers(path) {
|
|
1417
|
+
const res = await authed(homeComResourceUrl(gatewayId, path), { method: "GET" });
|
|
1418
|
+
if (res.ok || res.status === 403) return true;
|
|
1419
|
+
if (res.status !== 404) logHeatPumpFailure(path, /* @__PURE__ */ new Error(`HTTP ${res.status}`));
|
|
1420
|
+
return false;
|
|
1421
|
+
}
|
|
1378
1422
|
async function putWrite(write) {
|
|
1379
1423
|
const res = await authed(homeComResourceUrl(gatewayId, write.path), {
|
|
1380
1424
|
method: "PUT",
|
|
@@ -1456,6 +1500,7 @@ function createHomeComApiClient(config, gatewayId, deps = {}) {
|
|
|
1456
1500
|
const heatPump = await readHeatPump({
|
|
1457
1501
|
get: getResource,
|
|
1458
1502
|
onFailure: logHeatPumpFailure,
|
|
1503
|
+
answers: resourceAnswers,
|
|
1459
1504
|
circuitIds: {
|
|
1460
1505
|
hc: snapshot.circuits.map((circuit) => circuit.id),
|
|
1461
1506
|
dhw: dhwId === void 0 ? [] : [dhwId]
|
|
@@ -1850,6 +1895,15 @@ var HomeComConnectionResolver = class {
|
|
|
1850
1895
|
this.#surfaces.clear();
|
|
1851
1896
|
}
|
|
1852
1897
|
};
|
|
1898
|
+
/** Native id of the gateway pick: a Gateway device, sibling of the circuits. */
|
|
1899
|
+
var HOMECOM_GATEWAY_NATIVE_ID = "gateway:0";
|
|
1900
|
+
function gatewayDiscoverable(snapshot) {
|
|
1901
|
+
return {
|
|
1902
|
+
nativeId: HOMECOM_GATEWAY_NATIVE_ID,
|
|
1903
|
+
name: snapshot.gateway.name.length > 0 ? snapshot.gateway.name : "HomeCom gateway",
|
|
1904
|
+
kind: "gateway"
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1853
1907
|
var BOILER_DHW_NAME = "Domestic hot water";
|
|
1854
1908
|
function circuitDiscoverable(circuit) {
|
|
1855
1909
|
return {
|
|
@@ -1867,18 +1921,25 @@ function dhwDiscoverable(name) {
|
|
|
1867
1921
|
};
|
|
1868
1922
|
}
|
|
1869
1923
|
/**
|
|
1870
|
-
* A gateway with a heat pump is one pick. A boiler still offers each circuit
|
|
1924
|
+
* The gateway itself is always the first pick. A gateway with a heat pump is one more pick. A boiler still offers each circuit
|
|
1871
1925
|
* and the hot-water row on its own.
|
|
1872
1926
|
*/
|
|
1873
1927
|
function buildDiscoverables(snapshot) {
|
|
1874
1928
|
const circuits = snapshot.circuits.map(circuitDiscoverable);
|
|
1875
|
-
if (snapshot.heatPump !== void 0)
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1929
|
+
if (snapshot.heatPump !== void 0) {
|
|
1930
|
+
const children = [...circuits, ...snapshot.dhw === null ? [] : [dhwDiscoverable(HOMECOM_HOT_WATER_CHILD_NAME)]];
|
|
1931
|
+
return [gatewayDiscoverable(snapshot), {
|
|
1932
|
+
nativeId: "hp:0",
|
|
1933
|
+
name: "Heat pump",
|
|
1934
|
+
kind: "heat-pump",
|
|
1935
|
+
children
|
|
1936
|
+
}];
|
|
1937
|
+
}
|
|
1938
|
+
return [
|
|
1939
|
+
gatewayDiscoverable(snapshot),
|
|
1940
|
+
...circuits,
|
|
1941
|
+
...snapshot.dhw === null ? [] : [dhwDiscoverable(BOILER_DHW_NAME)]
|
|
1942
|
+
];
|
|
1882
1943
|
}
|
|
1883
1944
|
var homeComConnections = new HomeComConnectionResolver();
|
|
1884
1945
|
//#endregion
|
|
@@ -1961,6 +2022,10 @@ function shapeOf(kind) {
|
|
|
1961
2022
|
type: DeviceType.WaterHeater,
|
|
1962
2023
|
caps: ["water-heater"]
|
|
1963
2024
|
};
|
|
2025
|
+
case "gateway": return {
|
|
2026
|
+
type: DeviceType.Gateway,
|
|
2027
|
+
caps: []
|
|
2028
|
+
};
|
|
1964
2029
|
case "heat-pump": return {
|
|
1965
2030
|
type: DeviceType.HeatPump,
|
|
1966
2031
|
caps: ["heat-pump-status", "heat-pump-control"]
|
|
@@ -2808,6 +2873,35 @@ var HomeComWaterHeaterDevice = class extends BaseDevice {
|
|
|
2808
2873
|
}
|
|
2809
2874
|
};
|
|
2810
2875
|
//#endregion
|
|
2876
|
+
//#region src/homecom/devices/homecom-gateway-device.ts
|
|
2877
|
+
var homeComGatewaySchema = object({
|
|
2878
|
+
nativeId: string().min(1),
|
|
2879
|
+
brokerId: string().min(1),
|
|
2880
|
+
system: literal("homecom").optional(),
|
|
2881
|
+
integrationId: string().optional()
|
|
2882
|
+
});
|
|
2883
|
+
/**
|
|
2884
|
+
* The HomeCom gateway, as a {@link DeviceType.Gateway}. Its connection lives on the
|
|
2885
|
+
* INTEGRATION; this device is the gateway's presence in the device list and follows the
|
|
2886
|
+
* integration's online state (its connect and disconnect callbacks flip it).
|
|
2887
|
+
*
|
|
2888
|
+
* It registers no action: the cloud account offers no reboot, no update and no local page,
|
|
2889
|
+
* so there is nothing real to expose. The circuits, the hot water and the heat pump are NOT
|
|
2890
|
+
* children of it; each is adopted as its own top-level device of the same integration.
|
|
2891
|
+
*/
|
|
2892
|
+
var HomeComGatewayDevice = class extends BaseDevice {
|
|
2893
|
+
features = [];
|
|
2894
|
+
constructor(ctx) {
|
|
2895
|
+
const cfg = homeComGatewaySchema.parse(ctx.persistedConfig ?? {});
|
|
2896
|
+
super(ctx, homeComGatewaySchema, { type: DeviceType.Gateway });
|
|
2897
|
+
this.online = homeComConnections.has(cfg.brokerId);
|
|
2898
|
+
this.updateSourceInfo({
|
|
2899
|
+
id: cfg.nativeId,
|
|
2900
|
+
system: "homecom"
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2904
|
+
//#endregion
|
|
2811
2905
|
//#region src/homecom/devices/homecom-heat-pump-device.ts
|
|
2812
2906
|
var homeComHeatPumpSchema = object({
|
|
2813
2907
|
nativeId: string().min(1),
|
|
@@ -2958,7 +3052,8 @@ var BoschHomeComProviderAddon = class extends BaseDeviceProvider {
|
|
|
2958
3052
|
deviceClasses = {
|
|
2959
3053
|
[DeviceType.Climate]: HomeComClimateDevice,
|
|
2960
3054
|
[DeviceType.WaterHeater]: HomeComWaterHeaterDevice,
|
|
2961
|
-
[DeviceType.HeatPump]: HomeComHeatPumpDevice
|
|
3055
|
+
[DeviceType.HeatPump]: HomeComHeatPumpDevice,
|
|
3056
|
+
[DeviceType.Gateway]: HomeComGatewayDevice
|
|
2962
3057
|
};
|
|
2963
3058
|
clients = null;
|
|
2964
3059
|
instances = null;
|
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-BHFcR1TK.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-B2e4DAI1.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.49",
|
|
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.834"
|
|
142
142
|
}
|
|
143
143
|
},
|
|
144
144
|
"files": [
|