daikin-local-control 0.4.0 → 0.5.0
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/DESIGN.md +47 -4
- package/README.md +154 -46
- package/UNSUPPORTED.md +27 -2
- package/dist/clients/airbase.d.ts +64 -0
- package/dist/clients/airbase.d.ts.map +1 -0
- package/dist/clients/airbase.js +175 -0
- package/dist/clients/airbase.js.map +1 -0
- package/dist/clients/client.d.ts +3 -2
- package/dist/clients/client.d.ts.map +1 -1
- package/dist/codecs/airbase/capabilities.d.ts +23 -0
- package/dist/codecs/airbase/capabilities.d.ts.map +1 -0
- package/dist/codecs/airbase/capabilities.js +108 -0
- package/dist/codecs/airbase/capabilities.js.map +1 -0
- package/dist/codecs/airbase/state.d.ts +29 -0
- package/dist/codecs/airbase/state.d.ts.map +1 -0
- package/dist/codecs/airbase/state.js +166 -0
- package/dist/codecs/airbase/state.js.map +1 -0
- package/dist/codecs/airbase/tables.d.ts +7 -0
- package/dist/codecs/airbase/tables.d.ts.map +1 -0
- package/dist/codecs/airbase/tables.js +27 -0
- package/dist/codecs/airbase/tables.js.map +1 -0
- package/dist/codecs/airbase/zone.d.ts +12 -0
- package/dist/codecs/airbase/zone.d.ts.map +1 -0
- package/dist/codecs/airbase/zone.js +50 -0
- package/dist/codecs/airbase/zone.js.map +1 -0
- package/dist/codecs/classic/tables.d.ts +2 -0
- package/dist/codecs/classic/tables.d.ts.map +1 -1
- package/dist/codecs/classic/tables.js +2 -0
- package/dist/codecs/classic/tables.js.map +1 -1
- package/dist/codecs/device.d.ts +7 -1
- package/dist/codecs/device.d.ts.map +1 -1
- package/dist/codecs/device.js +14 -2
- package/dist/codecs/device.js.map +1 -1
- package/dist/codecs/dsiot/tables.d.ts +3 -0
- package/dist/codecs/dsiot/tables.d.ts.map +1 -1
- package/dist/codecs/dsiot/tables.js +3 -0
- package/dist/codecs/dsiot/tables.js.map +1 -1
- package/dist/discovery.d.ts +20 -10
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +96 -30
- package/dist/discovery.js.map +1 -1
- package/dist/factory.d.ts +8 -5
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +13 -3
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/models/device.d.ts +3 -2
- package/dist/models/device.d.ts.map +1 -1
- package/dist/models/device.js +1 -1
- package/dist/models/device.js.map +1 -1
- package/dist/models/zone.d.ts +9 -0
- package/dist/models/zone.d.ts.map +1 -0
- package/dist/models/zone.js +2 -0
- package/dist/models/zone.js.map +1 -0
- package/dist/simulators/airbase.d.ts +82 -0
- package/dist/simulators/airbase.d.ts.map +1 -0
- package/dist/simulators/airbase.js +352 -0
- package/dist/simulators/airbase.js.map +1 -0
- package/dist/simulators/classic.d.ts.map +1 -1
- package/dist/simulators/classic.js +2 -2
- package/dist/simulators/classic.js.map +1 -1
- package/dist/simulators/index.d.ts +1 -0
- package/dist/simulators/index.d.ts.map +1 -1
- package/dist/simulators/index.js +1 -0
- package/dist/simulators/index.js.map +1 -1
- package/dist/simulators/simulator.d.ts +11 -10
- package/dist/simulators/simulator.d.ts.map +1 -1
- package/dist/simulators/simulator.js +2 -2
- package/dist/simulators/simulator.js.map +1 -1
- package/dist/transport.d.ts +3 -2
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +4 -1
- package/dist/transport.js.map +1 -1
- package/package.json +7 -3
- package/src/clients/airbase.ts +274 -0
- package/src/clients/client.ts +3 -2
- package/src/codecs/airbase/capabilities.ts +150 -0
- package/src/codecs/airbase/state.ts +223 -0
- package/src/codecs/airbase/tables.ts +32 -0
- package/src/codecs/airbase/zone.ts +62 -0
- package/src/codecs/classic/tables.ts +3 -0
- package/src/codecs/device.ts +18 -2
- package/src/codecs/dsiot/tables.ts +4 -0
- package/src/discovery.ts +101 -32
- package/src/factory.ts +21 -5
- package/src/index.ts +6 -0
- package/src/models/device.ts +3 -2
- package/src/models/zone.ts +8 -0
- package/src/simulators/airbase.ts +495 -0
- package/src/simulators/classic.ts +2 -1
- package/src/simulators/index.ts +1 -0
- package/src/simulators/simulator.ts +13 -12
- package/src/transport.ts +7 -3
|
@@ -11,7 +11,7 @@ export interface DaikinSimulatorRequest {
|
|
|
11
11
|
headers: IncomingHttpHeaders;
|
|
12
12
|
body: string;
|
|
13
13
|
}
|
|
14
|
-
/** What
|
|
14
|
+
/** What every simulator resolves with. */
|
|
15
15
|
export interface DaikinSimulator {
|
|
16
16
|
/** `<address>:<port>`, as `DaikinDevice.host` takes it: `127.0.0.1`, unless
|
|
17
17
|
* the simulator was started at another `address`, and a port that was free
|
|
@@ -40,9 +40,10 @@ export interface DaikinSimulatorTemperatures {
|
|
|
40
40
|
min: number;
|
|
41
41
|
max: number;
|
|
42
42
|
}
|
|
43
|
-
/** What a simulated unit has, where
|
|
44
|
-
* about a unit is set in the adapter's own terms:
|
|
45
|
-
* simulator, and `leaves` on a DSIOT one.
|
|
43
|
+
/** What a simulated unit has, where the classic and the DSIOT protocol can
|
|
44
|
+
* both say it. Anything else about a unit is set in the adapter's own terms:
|
|
45
|
+
* `modelInfo` on a classic simulator, and `leaves` on a DSIOT one. An Airbase
|
|
46
|
+
* simulator takes an `AirbaseSimulatorUnit` instead. */
|
|
46
47
|
export interface DaikinSimulatorUnit {
|
|
47
48
|
/** A unit that only cools offers dehumidify, cool and fan. Defaults to
|
|
48
49
|
* false: a heat pump, which offers auto and heat as well. */
|
|
@@ -63,10 +64,9 @@ export interface DaikinSimulatorUnit {
|
|
|
63
64
|
* classic one. */
|
|
64
65
|
heatTemperaturesInC?: DaikinSimulatorTemperatures;
|
|
65
66
|
}
|
|
66
|
-
/** The options
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* ways. */
|
|
67
|
+
/** The options every simulator takes. A simulator applies `deviceInfo`,
|
|
68
|
+
* `state` and `sensors` after the fields given in the adapter's own terms, so
|
|
69
|
+
* they replace a field given both ways. */
|
|
70
70
|
export interface StartSimulatorOptions {
|
|
71
71
|
/** The adapter's name, MAC address and firmware version, as
|
|
72
72
|
* `getDeviceInfo` reads them back. A field left out keeps its default.
|
|
@@ -78,7 +78,8 @@ export interface StartSimulatorOptions {
|
|
|
78
78
|
state?: Partial<DaikinState>;
|
|
79
79
|
/** What the unit's sensors read at the start: see
|
|
80
80
|
* `DaikinSimulator.setSensors`. Defaults to 26 °C and 55 % indoors, and
|
|
81
|
-
* 30 °C outdoors.
|
|
81
|
+
* 30 °C outdoors. An Airbase simulator reports no humidity unless it is
|
|
82
|
+
* given one. */
|
|
82
83
|
sensors?: Partial<DaikinSensors>;
|
|
83
84
|
/** How long, in milliseconds, the adapter takes to answer a request. It
|
|
84
85
|
* answers one request at a time, in the order they arrive, so a request
|
|
@@ -96,7 +97,7 @@ export interface StartSimulatorOptions {
|
|
|
96
97
|
answersDiscovery?: boolean;
|
|
97
98
|
}
|
|
98
99
|
/** What `startSimulator` resolves with: the part of a `DaikinSimulator` that
|
|
99
|
-
* is the same for
|
|
100
|
+
* is the same for every protocol. */
|
|
100
101
|
export type SimulatorServer = Pick<DaikinSimulator, "host" | "requests" | "stop">;
|
|
101
102
|
export interface SimulatorReply {
|
|
102
103
|
statusCode: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulator.d.ts","sourceRoot":"","sources":["../../src/simulators/simulator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAU5B,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,
|
|
1
|
+
{"version":3,"file":"simulator.d.ts","sourceRoot":"","sources":["../../src/simulators/simulator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createSocket } from "node:dgram";
|
|
2
2
|
import { once } from "node:events";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
|
-
// What the
|
|
5
|
-
//
|
|
4
|
+
// What the simulators share: the HTTP server, and the reply to a discovery
|
|
5
|
+
// probe, which every adapter sends in the same form.
|
|
6
6
|
const DISCOVERY_PROBE = "DAIKIN_UDP/common/basic_info";
|
|
7
7
|
const DISCOVERY_PORT = 30050;
|
|
8
8
|
const DEFAULT_ADDRESS = "127.0.0.1";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulator.js","sourceRoot":"","sources":["../../src/simulators/simulator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,YAAY,EAA4B,MAAM,WAAW,CAAC;AASnE,2EAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"simulator.js","sourceRoot":"","sources":["../../src/simulators/simulator.ts"],"names":[],"mappings":"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"}
|
package/dist/transport.d.ts
CHANGED
|
@@ -29,8 +29,9 @@ export interface DaikinTransportOptions {
|
|
|
29
29
|
/** HTTPS (a Marvell adapter in secure mode). */
|
|
30
30
|
usesHttps?: boolean;
|
|
31
31
|
/** The terminal uuid, sent as `X-Daikin-uuid` on every call: 32 lowercase
|
|
32
|
-
* hexadecimal digits, a UUID without its dashes.
|
|
33
|
-
|
|
32
|
+
* hexadecimal digits, a UUID without its dashes. Without one the header is
|
|
33
|
+
* left out, as an Airbase adapter takes it. */
|
|
34
|
+
terminalUuid?: string;
|
|
34
35
|
authentication?: DaikinAuthentication;
|
|
35
36
|
/** How long, in milliseconds, to wait for each of the connection, the
|
|
36
37
|
* reply's headers and the reply's body before `send` throws a
|
package/dist/transport.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIhE,MAAM,WAAW,oBAAoB;IACnC;6CACyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC;;oEAEgE;IAChE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAID;;wBAEwB;AACxB,wBAAsB,SAAS,CAAC,OAAO,EACrC,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EACjC,EAAE,UAAuC,EAAE,GAAE,kBAAuB,GACnE,OAAO,CAAC,OAAO,CAAC,CAYlB;AAoBD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC/B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIhE,MAAM,WAAW,oBAAoB;IACnC;6CACyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC;;oEAEgE;IAChE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAID;;wBAEwB;AACxB,wBAAsB,SAAS,CAAC,OAAO,EACrC,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EACjC,EAAE,UAAuC,EAAE,GAAE,kBAAuB,GACnE,OAAO,CAAC,OAAO,CAAC,CAYlB;AAoBD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC/B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;mDAE+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC;;2DAEuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,gBAAgB,CAAC;CACxB;AAED;;wDAEwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B;mFAC+E;IAC/E,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACtB;AAED;;;;;;;gBAOgB;AAChB,qBAAa,eAAe;IAQd,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyB;IAE7D;;0DAEsD;IACtD,YAA6B,OAAO,EAAE,sBAAsB,EAgB3D;IAED;;;;;;;2BAOuB;IACjB,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDlD;IAED,2CAA2C;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;CACF"}
|
package/dist/transport.js
CHANGED
|
@@ -52,7 +52,10 @@ export class DaikinTransport {
|
|
|
52
52
|
constructor(options) {
|
|
53
53
|
this.options = options;
|
|
54
54
|
// Before the Client, so a uuid that throws leaves no connection open.
|
|
55
|
-
this.terminalUuidHeaders =
|
|
55
|
+
this.terminalUuidHeaders =
|
|
56
|
+
options.terminalUuid === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: encodeTerminalUuid(options.terminalUuid);
|
|
56
59
|
const scheme = options.usesHttps ? "https" : "http";
|
|
57
60
|
this.origin = `${scheme}://${options.host.replace(/\/+$/, "")}`;
|
|
58
61
|
const timeoutInMs = options.timeoutInMs ?? DEFAULT_TIMEOUT_IN_MS;
|
package/dist/transport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAehE,MAAM,0BAA0B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAE1D;;wBAEwB;AACxB,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,SAAiC,EACjC,EAAE,UAAU,GAAG,0BAA0B,EAAE,GAAuB,EAAE;IAEpE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,qBAAqB,CAAC,EAAE,CAAC;gBAC9C,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QACD,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,SAAS,EAAE,CAAC;AACrB,CAAC;AAED,SAAS,KAAK,CAAC,SAAiB;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,oBAAoB,GAAG;IAC3B,cAAc,EAAE,kBAAkB;CACnC,CAAC;AAEF,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC,4EAA4E;AAC5E,gDAAgD;AAChD,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC;IACvC,cAAc;IACd,YAAY;IACZ,gBAAgB;CACjB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "daikin-local-control",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Unofficial Node.js and TypeScript client for controlling Daikin air conditioners over the local network (LAN), with no cloud. Supports classic (BRP069, BRP072)
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Unofficial Node.js and TypeScript client for controlling Daikin air conditioners over the local network (LAN), with no cloud. Supports classic (BRP069, BRP072), DSIOT (BRP084) and Airbase (BRP15B61) Wi-Fi adapters.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"daikin",
|
|
7
7
|
"daikin-ac",
|
|
8
8
|
"daikin-api",
|
|
9
9
|
"daikin-mobile-controller",
|
|
10
|
+
"daikin-airbase",
|
|
11
|
+
"onecta",
|
|
10
12
|
"air-conditioner",
|
|
11
13
|
"air-conditioning",
|
|
12
14
|
"aircon",
|
|
@@ -19,6 +21,7 @@
|
|
|
19
21
|
"brp072a42",
|
|
20
22
|
"brp072c42",
|
|
21
23
|
"brp084",
|
|
24
|
+
"brp15b61",
|
|
22
25
|
"dsiot",
|
|
23
26
|
"skyair",
|
|
24
27
|
"home-automation",
|
|
@@ -31,7 +34,8 @@
|
|
|
31
34
|
"taiwan",
|
|
32
35
|
"philippines",
|
|
33
36
|
"australia",
|
|
34
|
-
"new-zealand"
|
|
37
|
+
"new-zealand",
|
|
38
|
+
"europe"
|
|
35
39
|
],
|
|
36
40
|
"license": "MIT",
|
|
37
41
|
"homepage": "https://github.com/cheziyi/daikin-local-control#readme",
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mergeAirbaseCapabilityFields,
|
|
3
|
+
parseAirbaseCapabilities,
|
|
4
|
+
parseAirbaseZoneCount,
|
|
5
|
+
takesAirbaseAirside,
|
|
6
|
+
takesAirbaseFanAuto,
|
|
7
|
+
} from "../codecs/airbase/capabilities.js";
|
|
8
|
+
import {
|
|
9
|
+
applyAirbaseAirside,
|
|
10
|
+
applyAirbaseControl,
|
|
11
|
+
applyAirbaseFanAuto,
|
|
12
|
+
parseAirbaseAirside,
|
|
13
|
+
parseAirbaseFanAuto,
|
|
14
|
+
parseAirbaseSensors,
|
|
15
|
+
parseAirbaseState,
|
|
16
|
+
} from "../codecs/airbase/state.js";
|
|
17
|
+
import { applyAirbaseZone, parseAirbaseZones } from "../codecs/airbase/zone.js";
|
|
18
|
+
import {
|
|
19
|
+
assertClassicOk,
|
|
20
|
+
isClassicOk,
|
|
21
|
+
parseClassicKeyValue,
|
|
22
|
+
} from "../codecs/classic/keyValue.js";
|
|
23
|
+
import { parseDeviceInfo } from "../codecs/device.js";
|
|
24
|
+
import { DaikinError } from "../error.js";
|
|
25
|
+
import type { DaikinCapabilities } from "../models/capabilities.js";
|
|
26
|
+
import type { DaikinDeviceInfo } from "../models/device.js";
|
|
27
|
+
import type {
|
|
28
|
+
DaikinControl,
|
|
29
|
+
DaikinSensors,
|
|
30
|
+
DaikinState,
|
|
31
|
+
DaikinStateAndSensors,
|
|
32
|
+
SpecialMode,
|
|
33
|
+
} from "../models/state.js";
|
|
34
|
+
import type { DaikinZone } from "../models/zone.js";
|
|
35
|
+
import {
|
|
36
|
+
type DaikinRequestOptions,
|
|
37
|
+
type DaikinRetryOptions,
|
|
38
|
+
type DaikinTransport,
|
|
39
|
+
withRetry,
|
|
40
|
+
} from "../transport.js";
|
|
41
|
+
import type { DaikinClient } from "./client.js";
|
|
42
|
+
import { SetStateCache } from "./setStateCache.js";
|
|
43
|
+
|
|
44
|
+
// An Airbase adapter answers the endpoints of a classic adapter under this
|
|
45
|
+
// path, and HTTP 404 at the paths a classic adapter answers.
|
|
46
|
+
const AIRBASE_ROOT = "/skyfi";
|
|
47
|
+
|
|
48
|
+
const AIRBASE_ROUTES = {
|
|
49
|
+
basicInfo: "/common/basic_info",
|
|
50
|
+
getModelInfo: "/aircon/get_model_info",
|
|
51
|
+
getControlInfo: "/aircon/get_control_info",
|
|
52
|
+
setControlInfo: "/aircon/set_control_info",
|
|
53
|
+
getSensorInfo: "/aircon/get_sensor_info",
|
|
54
|
+
getZoneSetting: "/aircon/get_zone_setting",
|
|
55
|
+
setZoneSetting: "/aircon/set_zone_setting",
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
const NOT_FOUND = 404;
|
|
59
|
+
|
|
60
|
+
/** Whether the adapter `transport` reaches is an Airbase adapter: one that
|
|
61
|
+
* answers HTTP 404 at `/common/basic_info`, where a classic adapter answers,
|
|
62
|
+
* and `ret=OK` at `/skyfi/common/basic_info`. It sends the second request
|
|
63
|
+
* only after that 404, and retries a refused or dropped connection as
|
|
64
|
+
* `withRetry` does by default: an adapter drops one while it answers another
|
|
65
|
+
* caller. It resolves with `false` when either request fails any other way.
|
|
66
|
+
* `discoverDevices` calls it, and `src/index.ts` does not export it. */
|
|
67
|
+
export async function isAirbaseAdapter(
|
|
68
|
+
transport: DaikinTransport,
|
|
69
|
+
): Promise<boolean> {
|
|
70
|
+
const get = (path: string) =>
|
|
71
|
+
withRetry(() => transport.send({ method: "GET", path }));
|
|
72
|
+
try {
|
|
73
|
+
await get(AIRBASE_ROUTES.basicInfo);
|
|
74
|
+
return false;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (!(error instanceof DaikinError) || error.httpStatusCode !== NOT_FOUND) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return isClassicOk(
|
|
82
|
+
parseClassicKeyValue(
|
|
83
|
+
await get(`${AIRBASE_ROOT}${AIRBASE_ROUTES.basicInfo}`),
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Airbase adapter (BRP15B61), on a ducted unit. Reads and writes are HTTP GET
|
|
92
|
+
* with query parameters, as on a classic adapter, at paths under `/skyfi`.
|
|
93
|
+
* It takes no terminal uuid and no local password. */
|
|
94
|
+
export class AirbaseDaikinClient implements DaikinClient {
|
|
95
|
+
// A unit's capabilities don't change, so they are read once.
|
|
96
|
+
private capabilityFields: Promise<Record<string, string>> | undefined;
|
|
97
|
+
private readonly setStateCache: SetStateCache;
|
|
98
|
+
|
|
99
|
+
/** `close` closes `transport`, so give each client a transport of its own.
|
|
100
|
+
* `maxStateAgeForSetStateInMs` defaults to 0: see
|
|
101
|
+
* `CreateDaikinClientOptions.maxStateAgeForSetStateInMs`. */
|
|
102
|
+
constructor(
|
|
103
|
+
private readonly transport: DaikinTransport,
|
|
104
|
+
private readonly retryOptions?: DaikinRetryOptions,
|
|
105
|
+
maxStateAgeForSetStateInMs = 0,
|
|
106
|
+
) {
|
|
107
|
+
this.setStateCache = new SetStateCache(maxStateAgeForSetStateInMs);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
close(): Promise<void> {
|
|
111
|
+
return this.transport.close();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async getDeviceInfo(): Promise<DaikinDeviceInfo> {
|
|
115
|
+
return parseDeviceInfo(await this.command(AIRBASE_ROUTES.basicInfo));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async getCapabilities(): Promise<DaikinCapabilities> {
|
|
119
|
+
return parseAirbaseCapabilities(await this.readCapabilityFields());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getState(): Promise<DaikinState> {
|
|
123
|
+
return parseAirbaseState(await this.readControlInfo());
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async setState(control: DaikinControl): Promise<void> {
|
|
127
|
+
const current =
|
|
128
|
+
this.setStateCache.get() ??
|
|
129
|
+
(await this.command(AIRBASE_ROUTES.getControlInfo));
|
|
130
|
+
await this.command(
|
|
131
|
+
AIRBASE_ROUTES.setControlInfo,
|
|
132
|
+
applyAirbaseControl(current, control),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async getSensors(): Promise<DaikinSensors> {
|
|
137
|
+
return parseAirbaseSensors(
|
|
138
|
+
await this.command(AIRBASE_ROUTES.getSensorInfo),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async getStateAndSensors(): Promise<DaikinStateAndSensors> {
|
|
143
|
+
return { state: await this.getState(), sensors: await this.getSensors() };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Rejects with a `DaikinError` whose status is 501: an Airbase adapter has
|
|
147
|
+
* no known request for a special mode. */
|
|
148
|
+
async setSpecialMode(_mode: SpecialMode, _enabled: boolean): Promise<void> {
|
|
149
|
+
throw new DaikinError("An Airbase adapter takes no special mode", 501);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// --- extended controls ---------------------------------------------------
|
|
153
|
+
|
|
154
|
+
/** Whether fan auto is on: the unit then picks the fan speed itself, up to
|
|
155
|
+
* the one that is set. `null` on a unit that takes no fan auto. */
|
|
156
|
+
async getFanAuto(): Promise<boolean | null> {
|
|
157
|
+
return takesAirbaseFanAuto(await this.readCapabilityFields())
|
|
158
|
+
? parseAirbaseFanAuto(await this.readControlInfo())
|
|
159
|
+
: null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Switches fan auto on or off. It switches airside off, and keeps the fan
|
|
163
|
+
* speed. Check `getFanAuto` first: the client sends it to any unit. */
|
|
164
|
+
async setFanAuto(enabled: boolean): Promise<void> {
|
|
165
|
+
await this.command(
|
|
166
|
+
AIRBASE_ROUTES.setControlInfo,
|
|
167
|
+
applyAirbaseFanAuto(
|
|
168
|
+
await this.command(AIRBASE_ROUTES.getControlInfo),
|
|
169
|
+
enabled,
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Whether airside is on. `null` on a unit that takes no airside. */
|
|
175
|
+
async getAirside(): Promise<boolean | null> {
|
|
176
|
+
return takesAirbaseAirside(await this.readCapabilityFields())
|
|
177
|
+
? parseAirbaseAirside(await this.readControlInfo())
|
|
178
|
+
: null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Switches airside on or off. Switching it on switches fan auto off and
|
|
182
|
+
* sets the lowest fan speed. Check `getAirside` first: the client sends it
|
|
183
|
+
* to any unit. */
|
|
184
|
+
async setAirside(enabled: boolean): Promise<void> {
|
|
185
|
+
await this.command(
|
|
186
|
+
AIRBASE_ROUTES.setControlInfo,
|
|
187
|
+
applyAirbaseAirside(
|
|
188
|
+
await this.command(AIRBASE_ROUTES.getControlInfo),
|
|
189
|
+
enabled,
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** The unit's zones, in the unit's order; empty on a unit without zones,
|
|
195
|
+
* which is not asked for any. */
|
|
196
|
+
async getZones(): Promise<DaikinZone[]> {
|
|
197
|
+
const capabilityFields = await this.readCapabilityFields();
|
|
198
|
+
return parseAirbaseZones(
|
|
199
|
+
await this.readZoneSetting(capabilityFields),
|
|
200
|
+
capabilityFields,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Opens or closes the zone at `index` in `getZones`, counted from 0. It
|
|
205
|
+
* rejects with an `Error` for an index the unit has no zone at. */
|
|
206
|
+
async setZone(index: number, enabled: boolean): Promise<void> {
|
|
207
|
+
const capabilityFields = await this.readCapabilityFields();
|
|
208
|
+
const current = await this.readZoneSetting(capabilityFields);
|
|
209
|
+
await this.command(
|
|
210
|
+
AIRBASE_ROUTES.setZoneSetting,
|
|
211
|
+
applyAirbaseZone(current, capabilityFields, index, enabled),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Calls any endpoint by its path below `/skyfi`, such as
|
|
216
|
+
* `/aircon/get_control_info`, with query parameters, and returns the reply's
|
|
217
|
+
* `key=value` fields. It throws a `DaikinError` when the reply's `ret` is
|
|
218
|
+
* not `OK`. */
|
|
219
|
+
async command(
|
|
220
|
+
path: string,
|
|
221
|
+
parameters?: Record<string, string>,
|
|
222
|
+
{ retry = true }: DaikinRequestOptions = {},
|
|
223
|
+
): Promise<Record<string, string>> {
|
|
224
|
+
const send = () =>
|
|
225
|
+
this.transport.send({
|
|
226
|
+
method: "GET",
|
|
227
|
+
path: `${AIRBASE_ROOT}${path}`,
|
|
228
|
+
...(parameters ? { query: parameters } : {}),
|
|
229
|
+
});
|
|
230
|
+
try {
|
|
231
|
+
const fields = parseClassicKeyValue(
|
|
232
|
+
await (retry ? withRetry(send, this.retryOptions) : send()),
|
|
233
|
+
);
|
|
234
|
+
assertClassicOk(fields);
|
|
235
|
+
return fields;
|
|
236
|
+
} finally {
|
|
237
|
+
// Every request that changes the unit takes parameters, and any of them
|
|
238
|
+
// may change what `get_control_info` answers, even one that failed: a
|
|
239
|
+
// request that timed out may still have reached the unit.
|
|
240
|
+
if (parameters) {
|
|
241
|
+
this.setStateCache.clear();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// What a unit without zones answers at `get_zone_setting` is not known, so
|
|
247
|
+
// it is not asked.
|
|
248
|
+
private async readZoneSetting(
|
|
249
|
+
capabilityFields: Record<string, string>,
|
|
250
|
+
): Promise<Record<string, string>> {
|
|
251
|
+
return parseAirbaseZoneCount(capabilityFields) === 0
|
|
252
|
+
? {}
|
|
253
|
+
: this.command(AIRBASE_ROUTES.getZoneSetting);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private async readControlInfo(): Promise<Record<string, string>> {
|
|
257
|
+
const fields = await this.command(AIRBASE_ROUTES.getControlInfo);
|
|
258
|
+
this.setStateCache.set(fields);
|
|
259
|
+
return fields;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private readCapabilityFields(): Promise<Record<string, string>> {
|
|
263
|
+
this.capabilityFields ??= (async () =>
|
|
264
|
+
mergeAirbaseCapabilityFields(
|
|
265
|
+
await this.command(AIRBASE_ROUTES.basicInfo),
|
|
266
|
+
await this.command(AIRBASE_ROUTES.getModelInfo),
|
|
267
|
+
))();
|
|
268
|
+
// A failed read is not cached, so the next call tries again.
|
|
269
|
+
this.capabilityFields.catch(() => {
|
|
270
|
+
this.capabilityFields = undefined;
|
|
271
|
+
});
|
|
272
|
+
return this.capabilityFields;
|
|
273
|
+
}
|
|
274
|
+
}
|
package/src/clients/client.ts
CHANGED
|
@@ -31,7 +31,8 @@ export interface DaikinClient {
|
|
|
31
31
|
|
|
32
32
|
/** Turn powerful, econo or streamer on or off, with a request of its own.
|
|
33
33
|
* `DaikinCapabilities.specialModes` lists the ones the unit takes in each
|
|
34
|
-
* mode, and the client sends any other as given.
|
|
34
|
+
* mode, and the client sends any other as given. An `AirbaseDaikinClient`
|
|
35
|
+
* rejects every one with status 501. */
|
|
35
36
|
setSpecialMode(mode: SpecialMode, enabled: boolean): Promise<void>;
|
|
36
37
|
|
|
37
38
|
/** Read-only temperature / humidity readings. */
|
|
@@ -39,7 +40,7 @@ export interface DaikinClient {
|
|
|
39
40
|
|
|
40
41
|
/** What `getState` and `getSensors` resolve with, together. A DSIOT adapter
|
|
41
42
|
* answers both in one request, where calling the two sends the same request
|
|
42
|
-
* twice. A classic adapter takes one request for each. */
|
|
43
|
+
* twice. A classic or an Airbase adapter takes one request for each. */
|
|
43
44
|
getStateAndSensors(): Promise<DaikinStateAndSensors>;
|
|
44
45
|
|
|
45
46
|
/** Close the connection to the adapter. */
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DaikinCapabilities,
|
|
3
|
+
TemperatureRange,
|
|
4
|
+
} from "../../models/capabilities.js";
|
|
5
|
+
import {
|
|
6
|
+
type FanSpeed,
|
|
7
|
+
OPERATING_MODES,
|
|
8
|
+
type OperatingMode,
|
|
9
|
+
SWING_MODES,
|
|
10
|
+
} from "../../models/state.js";
|
|
11
|
+
|
|
12
|
+
const ALWAYS_OFFERED_MODES: readonly OperatingMode[] = ["cool", "heat", "fan"];
|
|
13
|
+
const THREE_FAN_LEVELS: FanSpeed[] = ["level1", "level3", "level5"];
|
|
14
|
+
const TWO_FAN_LEVELS: FanSpeed[] = ["level1", "level5"];
|
|
15
|
+
|
|
16
|
+
/** Merges the `basic_info` and `get_model_info` replies of an Airbase adapter
|
|
17
|
+
* into the one field map `parseAirbaseCapabilities`, `takesAirbaseFanAuto`,
|
|
18
|
+
* `takesAirbaseAirside` and `parseAirbaseZones` read. */
|
|
19
|
+
export function mergeAirbaseCapabilityFields(
|
|
20
|
+
basicInfo: Record<string, string>,
|
|
21
|
+
modelInfo: Record<string, string>,
|
|
22
|
+
): Record<string, string> {
|
|
23
|
+
return { ...basicInfo, ...modelInfo };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Turns the field map from `mergeAirbaseCapabilityFields` into
|
|
27
|
+
* `DaikinCapabilities`. A unit with zones (`en_zone`) takes no `auto` fan
|
|
28
|
+
* speed and no direction. It lists no humidity setting, louver position or
|
|
29
|
+
* special mode, and reports `energyMonitoring` and `demandControl` as
|
|
30
|
+
* `false`: which field says a unit has either is not known. */
|
|
31
|
+
export function parseAirbaseCapabilities(
|
|
32
|
+
fields: Record<string, string>,
|
|
33
|
+
): DaikinCapabilities {
|
|
34
|
+
const offeredModes = OPERATING_MODES.filter(
|
|
35
|
+
(mode) =>
|
|
36
|
+
ALWAYS_OFFERED_MODES.includes(mode) ||
|
|
37
|
+
(mode === "auto" && fields.en_auto === "1") ||
|
|
38
|
+
(mode === "dehumidify" && fields.en_dry === "1"),
|
|
39
|
+
);
|
|
40
|
+
const offered = (mode: OperatingMode, range: TemperatureRange) =>
|
|
41
|
+
offeredModes.includes(mode) ? range : null;
|
|
42
|
+
const none = { auto: [], dehumidify: [], cool: [], heat: [], fan: [] };
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
modes: offeredModes,
|
|
46
|
+
fanSpeeds: fanSpeeds(fields),
|
|
47
|
+
swingModes:
|
|
48
|
+
!hasZones(fields) && fields.en_fdir === "1" ? [...SWING_MODES] : [],
|
|
49
|
+
verticalPositions: [],
|
|
50
|
+
horizontalPositions: [],
|
|
51
|
+
targetTemperaturesInC: {
|
|
52
|
+
// Auto runs from the lowest setpoint of cool to the highest of heat.
|
|
53
|
+
auto: offered("auto", range(fields.cool_l, 18, fields.heat_h, 30)),
|
|
54
|
+
cool: offered("cool", range(fields.cool_l, 18, fields.cool_h, 32)),
|
|
55
|
+
heat: offered("heat", range(fields.heat_l, 10, fields.heat_h, 30)),
|
|
56
|
+
dehumidify: null,
|
|
57
|
+
fan: null,
|
|
58
|
+
},
|
|
59
|
+
targetHumidities: none,
|
|
60
|
+
specialModes: none,
|
|
61
|
+
specialOutlet: false,
|
|
62
|
+
energyMonitoring: false,
|
|
63
|
+
demandControl: false,
|
|
64
|
+
scheduleTimer: ["1", "2"].includes(fields.en_scdltmr ?? ""),
|
|
65
|
+
model: fields.model,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Whether a unit takes fan auto, read from the field map of
|
|
70
|
+
* `mergeAirbaseCapabilityFields`: one with zones that counts its fan speeds
|
|
71
|
+
* (`frate_steps`), unless `en_frate_auto` is 0. */
|
|
72
|
+
export function takesAirbaseFanAuto(fields: Record<string, string>): boolean {
|
|
73
|
+
return (
|
|
74
|
+
isFanControllable(fields) &&
|
|
75
|
+
hasZones(fields) &&
|
|
76
|
+
fanLevels(fields).length > 0 &&
|
|
77
|
+
"frate_steps" in fields &&
|
|
78
|
+
(fields.en_frate_auto ?? "1") === "1"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Whether a unit takes airside, read from the field map of
|
|
83
|
+
* `mergeAirbaseCapabilityFields`: one with zones that reports
|
|
84
|
+
* `en_airside=1`. */
|
|
85
|
+
export function takesAirbaseAirside(fields: Record<string, string>): boolean {
|
|
86
|
+
return (
|
|
87
|
+
isFanControllable(fields) && hasZones(fields) && fields.en_airside === "1"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** How many zones a unit has, read from the field map of
|
|
92
|
+
* `mergeAirbaseCapabilityFields`: `en_zone`, and none without it. */
|
|
93
|
+
export function parseAirbaseZoneCount(fields: Record<string, string>): number {
|
|
94
|
+
const count = Number.parseInt(fields.en_zone ?? "", 10);
|
|
95
|
+
return count > 0 ? count : 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasZones(fields: Record<string, string>): boolean {
|
|
99
|
+
return "en_zone" in fields && fields.en_zone !== "0";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// A unit that sets each zone's airflow itself (`en_linear_zone=1`) takes no
|
|
103
|
+
// fan speed.
|
|
104
|
+
function isFanControllable(fields: Record<string, string>): boolean {
|
|
105
|
+
return (
|
|
106
|
+
(fields.reg ?? "") !== "" &&
|
|
107
|
+
fields.reg !== "jp" &&
|
|
108
|
+
fields.en_frate === "1" &&
|
|
109
|
+
fields.en_linear_zone !== "1"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Three levels unless `frate_steps` is 2, and none when it is not a number.
|
|
114
|
+
function fanLevels(fields: Record<string, string>): FanSpeed[] {
|
|
115
|
+
if (!("frate_steps" in fields)) {
|
|
116
|
+
return THREE_FAN_LEVELS;
|
|
117
|
+
}
|
|
118
|
+
const steps = Number.parseInt(fields.frate_steps ?? "", 10);
|
|
119
|
+
if (!Number.isFinite(steps)) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
return steps === 2 ? TWO_FAN_LEVELS : THREE_FAN_LEVELS;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function fanSpeeds(fields: Record<string, string>): FanSpeed[] {
|
|
126
|
+
if (!isFanControllable(fields)) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
const takesAuto = !hasZones(fields) && fields.en_frate_auto === "1";
|
|
130
|
+
return [...(takesAuto ? (["auto"] as const) : []), ...fanLevels(fields)];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Setpoint limits are whole °C on the wire; any fraction is dropped.
|
|
134
|
+
function range(
|
|
135
|
+
low: string | undefined,
|
|
136
|
+
fallbackMin: number,
|
|
137
|
+
high: string | undefined,
|
|
138
|
+
fallbackMax: number,
|
|
139
|
+
): TemperatureRange {
|
|
140
|
+
return {
|
|
141
|
+
min: wholeCelsius(low, fallbackMin),
|
|
142
|
+
max: wholeCelsius(high, fallbackMax),
|
|
143
|
+
step: 1,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function wholeCelsius(value: string | undefined, fallback: number): number {
|
|
148
|
+
const parsed = Number.parseFloat(value ?? "");
|
|
149
|
+
return Number.isFinite(parsed) ? Math.trunc(parsed) : fallback;
|
|
150
|
+
}
|