daikin-local-control 0.1.1
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 +98 -0
- package/LICENSE +674 -0
- package/README.md +317 -0
- package/UNSUPPORTED.md +43 -0
- package/dist/authentication.d.ts +24 -0
- package/dist/authentication.d.ts.map +1 -0
- package/dist/authentication.js +30 -0
- package/dist/authentication.js.map +1 -0
- package/dist/clients/classic.d.ts +53 -0
- package/dist/clients/classic.d.ts.map +1 -0
- package/dist/clients/classic.js +142 -0
- package/dist/clients/classic.js.map +1 -0
- package/dist/clients/client.d.ts +21 -0
- package/dist/clients/client.d.ts.map +1 -0
- package/dist/clients/client.js +2 -0
- package/dist/clients/client.js.map +1 -0
- package/dist/clients/dsiot.d.ts +28 -0
- package/dist/clients/dsiot.d.ts.map +1 -0
- package/dist/clients/dsiot.js +66 -0
- package/dist/clients/dsiot.js.map +1 -0
- package/dist/codecs/classic/capabilities.d.ts +19 -0
- package/dist/codecs/classic/capabilities.d.ts.map +1 -0
- package/dist/codecs/classic/capabilities.js +208 -0
- package/dist/codecs/classic/capabilities.js.map +1 -0
- package/dist/codecs/classic/extendedControl.d.ts +20 -0
- package/dist/codecs/classic/extendedControl.d.ts.map +1 -0
- package/dist/codecs/classic/extendedControl.js +88 -0
- package/dist/codecs/classic/extendedControl.js.map +1 -0
- package/dist/codecs/classic/keyValue.d.ts +10 -0
- package/dist/codecs/classic/keyValue.d.ts.map +1 -0
- package/dist/codecs/classic/keyValue.js +36 -0
- package/dist/codecs/classic/keyValue.js.map +1 -0
- package/dist/codecs/classic/state.d.ts +18 -0
- package/dist/codecs/classic/state.d.ts.map +1 -0
- package/dist/codecs/classic/state.js +235 -0
- package/dist/codecs/classic/state.js.map +1 -0
- package/dist/codecs/credentials.d.ts +8 -0
- package/dist/codecs/credentials.d.ts.map +1 -0
- package/dist/codecs/credentials.js +14 -0
- package/dist/codecs/credentials.js.map +1 -0
- package/dist/codecs/device.d.ts +6 -0
- package/dist/codecs/device.d.ts.map +1 -0
- package/dist/codecs/device.js +35 -0
- package/dist/codecs/device.js.map +1 -0
- package/dist/codecs/dsiot/capabilities.d.ts +5 -0
- package/dist/codecs/dsiot/capabilities.d.ts.map +1 -0
- package/dist/codecs/dsiot/capabilities.js +139 -0
- package/dist/codecs/dsiot/capabilities.js.map +1 -0
- package/dist/codecs/dsiot/multireq.d.ts +37 -0
- package/dist/codecs/dsiot/multireq.d.ts.map +1 -0
- package/dist/codecs/dsiot/multireq.js +122 -0
- package/dist/codecs/dsiot/multireq.js.map +1 -0
- package/dist/codecs/dsiot/state.d.ts +92 -0
- package/dist/codecs/dsiot/state.d.ts.map +1 -0
- package/dist/codecs/dsiot/state.js +461 -0
- package/dist/codecs/dsiot/state.js.map +1 -0
- package/dist/discovery.d.ts +17 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +67 -0
- package/dist/discovery.js.map +1 -0
- package/dist/error.d.ts +15 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +20 -0
- package/dist/error.js.map +1 -0
- package/dist/factory.d.ts +26 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +31 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/models/capabilities.d.ts +42 -0
- package/dist/models/capabilities.d.ts.map +1 -0
- package/dist/models/capabilities.js +2 -0
- package/dist/models/capabilities.js.map +1 -0
- package/dist/models/device.d.ts +24 -0
- package/dist/models/device.d.ts.map +1 -0
- package/dist/models/device.js +2 -0
- package/dist/models/device.js.map +1 -0
- package/dist/models/extendedControl.d.ts +31 -0
- package/dist/models/extendedControl.d.ts.map +1 -0
- package/dist/models/extendedControl.js +7 -0
- package/dist/models/extendedControl.js.map +1 -0
- package/dist/models/state.d.ts +56 -0
- package/dist/models/state.d.ts.map +1 -0
- package/dist/models/state.js +49 -0
- package/dist/models/state.js.map +1 -0
- package/dist/transport.d.ts +75 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +118 -0
- package/dist/transport.js.map +1 -0
- package/package.json +62 -0
- package/src/authentication.ts +53 -0
- package/src/clients/classic.ts +251 -0
- package/src/clients/client.ts +29 -0
- package/src/clients/dsiot.ts +116 -0
- package/src/codecs/classic/capabilities.ts +272 -0
- package/src/codecs/classic/extendedControl.ts +132 -0
- package/src/codecs/classic/keyValue.ts +39 -0
- package/src/codecs/classic/state.ts +302 -0
- package/src/codecs/credentials.ts +19 -0
- package/src/codecs/device.ts +41 -0
- package/src/codecs/dsiot/capabilities.ts +209 -0
- package/src/codecs/dsiot/multireq.ts +162 -0
- package/src/codecs/dsiot/state.ts +644 -0
- package/src/discovery.ts +85 -0
- package/src/error.ts +20 -0
- package/src/factory.ts +66 -0
- package/src/index.ts +21 -0
- package/src/models/capabilities.ts +52 -0
- package/src/models/device.ts +25 -0
- package/src/models/extendedControl.ts +41 -0
- package/src/models/state.ts +110 -0
- package/src/transport.ts +191 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import { FIXED_LOUVER_POSITIONS, HORIZONTAL_POSITIONS, VERTICAL_POSITIONS, } from "../../models/state.js";
|
|
2
|
+
/** Full dotted paths into the `dgc_status` trees. `e_1002` lives on the indoor
|
|
3
|
+
* edge and `e_1003` on the outdoor edge. */
|
|
4
|
+
export const DSIOT_PATHS = {
|
|
5
|
+
power: "dgc_status.e_1002.e_A002.p_01",
|
|
6
|
+
/** Sent with every control. */
|
|
7
|
+
powerTransition: "dgc_status.e_1002.e_3003.p_2D",
|
|
8
|
+
mode: "dgc_status.e_1002.e_3001.p_01",
|
|
9
|
+
indoorTemperature: "dgc_status.e_1002.e_A00B.p_01",
|
|
10
|
+
indoorHumidity: "dgc_status.e_1002.e_A00B.p_02",
|
|
11
|
+
outdoorTemperature: "dgc_status.e_1003.e_A00D.p_01",
|
|
12
|
+
model: "dgc_status.e_1002.e_A001.p_0D",
|
|
13
|
+
modelType: "dgc_status.e_1002.e_A001.p_07",
|
|
14
|
+
energyMonitoring: "dgc_status.e_1002.e_A011.p_01",
|
|
15
|
+
};
|
|
16
|
+
export const DSIOT_MODE_NODE = "dgc_status.e_1002.e_3001";
|
|
17
|
+
export const DSIOT_MODE_PATHS = {
|
|
18
|
+
auto: {
|
|
19
|
+
setpoint: { path: `${DSIOT_MODE_NODE}.p_1D`, signed: true, writable: true },
|
|
20
|
+
humidity: {
|
|
21
|
+
value: `${DSIOT_MODE_NODE}.p_2E`,
|
|
22
|
+
flag: `${DSIOT_MODE_NODE}.p_2F`,
|
|
23
|
+
},
|
|
24
|
+
fanSpeed: `${DSIOT_MODE_NODE}.p_26`,
|
|
25
|
+
vertical: `${DSIOT_MODE_NODE}.p_20`,
|
|
26
|
+
horizontal: `${DSIOT_MODE_NODE}.p_21`,
|
|
27
|
+
},
|
|
28
|
+
dehumidify: {
|
|
29
|
+
setpoint: {
|
|
30
|
+
path: `${DSIOT_MODE_NODE}.p_1E`,
|
|
31
|
+
signed: true,
|
|
32
|
+
writable: false,
|
|
33
|
+
},
|
|
34
|
+
humidity: {
|
|
35
|
+
value: `${DSIOT_MODE_NODE}.p_30`,
|
|
36
|
+
flag: `${DSIOT_MODE_NODE}.p_31`,
|
|
37
|
+
},
|
|
38
|
+
fanSpeed: `${DSIOT_MODE_NODE}.p_27`,
|
|
39
|
+
vertical: `${DSIOT_MODE_NODE}.p_22`,
|
|
40
|
+
horizontal: `${DSIOT_MODE_NODE}.p_23`,
|
|
41
|
+
},
|
|
42
|
+
cool: {
|
|
43
|
+
setpoint: {
|
|
44
|
+
path: `${DSIOT_MODE_NODE}.p_02`,
|
|
45
|
+
signed: false,
|
|
46
|
+
writable: true,
|
|
47
|
+
},
|
|
48
|
+
humidity: {
|
|
49
|
+
value: `${DSIOT_MODE_NODE}.p_0B`,
|
|
50
|
+
flag: `${DSIOT_MODE_NODE}.p_0C`,
|
|
51
|
+
},
|
|
52
|
+
fanSpeed: `${DSIOT_MODE_NODE}.p_09`,
|
|
53
|
+
vertical: `${DSIOT_MODE_NODE}.p_05`,
|
|
54
|
+
horizontal: `${DSIOT_MODE_NODE}.p_06`,
|
|
55
|
+
},
|
|
56
|
+
heat: {
|
|
57
|
+
setpoint: {
|
|
58
|
+
path: `${DSIOT_MODE_NODE}.p_03`,
|
|
59
|
+
signed: false,
|
|
60
|
+
writable: true,
|
|
61
|
+
},
|
|
62
|
+
humidity: {
|
|
63
|
+
value: `${DSIOT_MODE_NODE}.p_2C`,
|
|
64
|
+
flag: `${DSIOT_MODE_NODE}.p_2D`,
|
|
65
|
+
},
|
|
66
|
+
fanSpeed: `${DSIOT_MODE_NODE}.p_0A`,
|
|
67
|
+
vertical: `${DSIOT_MODE_NODE}.p_07`,
|
|
68
|
+
horizontal: `${DSIOT_MODE_NODE}.p_08`,
|
|
69
|
+
},
|
|
70
|
+
fan: {
|
|
71
|
+
fanSpeed: `${DSIOT_MODE_NODE}.p_28`,
|
|
72
|
+
vertical: `${DSIOT_MODE_NODE}.p_24`,
|
|
73
|
+
horizontal: `${DSIOT_MODE_NODE}.p_25`,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
// --- value encodings -------------------------------------------------------
|
|
77
|
+
// Leaf values are hex strings. Multi-byte values are little-endian, and
|
|
78
|
+
// temperatures count half degrees.
|
|
79
|
+
export function encodeDsiotBoolean(on) {
|
|
80
|
+
return on ? "01" : "00";
|
|
81
|
+
}
|
|
82
|
+
export function decodeDsiotBoolean(pv) {
|
|
83
|
+
return pv !== undefined && Number.parseInt(pv, 16) > 0;
|
|
84
|
+
}
|
|
85
|
+
export function decodeDsiotLittleEndian(pv) {
|
|
86
|
+
const bytes = pv.match(/../g) ?? [];
|
|
87
|
+
return Number.parseInt(bytes.reverse().join(""), 16);
|
|
88
|
+
}
|
|
89
|
+
export function encodeDsiotSetpoint(setpointInC) {
|
|
90
|
+
return (Math.trunc(setpointInC * 2) & 0xff).toString(16);
|
|
91
|
+
}
|
|
92
|
+
export function decodeDsiotSetpoint(pv, signed) {
|
|
93
|
+
if (!pv) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const halfDegrees = signed ? signedByte(pv) : decodeDsiotLittleEndian(pv);
|
|
97
|
+
return Number.isFinite(halfDegrees) ? halfDegrees / 2 : null;
|
|
98
|
+
}
|
|
99
|
+
/** Indoor temperature: a signed byte of whole °C. */
|
|
100
|
+
export function decodeDsiotIndoorTemperature(pv) {
|
|
101
|
+
return pv ? nullIfNaN(signedByte(pv)) : null;
|
|
102
|
+
}
|
|
103
|
+
/** Indoor humidity: an unsigned byte of %. */
|
|
104
|
+
export function decodeDsiotIndoorHumidity(pv) {
|
|
105
|
+
return pv ? nullIfNaN(Number.parseInt(pv, 16)) : null;
|
|
106
|
+
}
|
|
107
|
+
/** Outdoor temperature: a signed little-endian 16-bit count of half degrees. */
|
|
108
|
+
export function decodeDsiotOutdoorTemperature(pv) {
|
|
109
|
+
if (!pv) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const unsigned = decodeDsiotLittleEndian(pv);
|
|
113
|
+
// The shift turns NaN into 0, so a value that is not hex is caught first.
|
|
114
|
+
return Number.isFinite(unsigned) ? ((unsigned << 16) >> 16) / 2 : null;
|
|
115
|
+
}
|
|
116
|
+
function signedByte(pv) {
|
|
117
|
+
const byte = Number.parseInt(pv.slice(0, 2), 16);
|
|
118
|
+
return byte > 0x7f ? byte - 0x100 : byte;
|
|
119
|
+
}
|
|
120
|
+
function nullIfNaN(value) {
|
|
121
|
+
return Number.isFinite(value) ? value : null;
|
|
122
|
+
}
|
|
123
|
+
const OPERATING_MODE_CODES = {
|
|
124
|
+
fan: "0000",
|
|
125
|
+
heat: "0100",
|
|
126
|
+
cool: "0200",
|
|
127
|
+
auto: "0300",
|
|
128
|
+
dehumidify: "0500",
|
|
129
|
+
};
|
|
130
|
+
const OPERATING_MODES_BY_CODE = {
|
|
131
|
+
"0000": "fan",
|
|
132
|
+
"0100": "heat",
|
|
133
|
+
"0200": "cool",
|
|
134
|
+
"0300": "auto",
|
|
135
|
+
"0500": "dehumidify",
|
|
136
|
+
"0900": "cool",
|
|
137
|
+
"0A00": "heat",
|
|
138
|
+
};
|
|
139
|
+
export function encodeDsiotMode(mode) {
|
|
140
|
+
return OPERATING_MODE_CODES[mode];
|
|
141
|
+
}
|
|
142
|
+
export function decodeDsiotMode(pv) {
|
|
143
|
+
return OPERATING_MODES_BY_CODE[(pv ?? "").toUpperCase()];
|
|
144
|
+
}
|
|
145
|
+
const FAN_SPEED_CODES = {
|
|
146
|
+
auto: "0A00",
|
|
147
|
+
silent: "0B00",
|
|
148
|
+
level1: "0300",
|
|
149
|
+
level2: "0400",
|
|
150
|
+
level3: "0500",
|
|
151
|
+
level4: "0600",
|
|
152
|
+
level5: "0700",
|
|
153
|
+
};
|
|
154
|
+
const FAN_SPEEDS_BY_CODE = Object.fromEntries(Object.entries(FAN_SPEED_CODES).map(([speed, code]) => [
|
|
155
|
+
code,
|
|
156
|
+
speed,
|
|
157
|
+
]));
|
|
158
|
+
export function encodeDsiotFanSpeed(speed) {
|
|
159
|
+
return FAN_SPEED_CODES[speed];
|
|
160
|
+
}
|
|
161
|
+
export function decodeDsiotFanSpeed(pv) {
|
|
162
|
+
return FAN_SPEEDS_BY_CODE[(pv ?? "").toUpperCase()];
|
|
163
|
+
}
|
|
164
|
+
/** The values a louver leaf holds besides a fixed position, which is 1-14.
|
|
165
|
+
* The leaf is three bytes, little-endian, and bit n of its `md.mx` is set
|
|
166
|
+
* when the unit takes value n. */
|
|
167
|
+
export const DSIOT_LOUVER_VALUES = {
|
|
168
|
+
stop: 0,
|
|
169
|
+
swing: 15,
|
|
170
|
+
auto: 16,
|
|
171
|
+
naturalWind: 19,
|
|
172
|
+
floorWarming: 25,
|
|
173
|
+
};
|
|
174
|
+
const FIXED_LOUVER_VALUES = Array.from({ length: 14 }, (_, index) => index + 1);
|
|
175
|
+
export const DSIOT_LOUVER_STOP = "000000";
|
|
176
|
+
export const DSIOT_LOUVER_SWING = "0F0000";
|
|
177
|
+
export function isDsiotLouverSwinging(pv) {
|
|
178
|
+
return (pv ?? "").toUpperCase() === DSIOT_LOUVER_SWING;
|
|
179
|
+
}
|
|
180
|
+
/** The value each `VerticalPosition` is held as. A fixed position has none: it
|
|
181
|
+
* is held as the value at its rank among the fixed values the unit takes. */
|
|
182
|
+
export const DSIOT_VERTICAL_POSITION_VALUES = {
|
|
183
|
+
stop: DSIOT_LOUVER_VALUES.stop,
|
|
184
|
+
swing: DSIOT_LOUVER_VALUES.swing,
|
|
185
|
+
naturalWind: DSIOT_LOUVER_VALUES.naturalWind,
|
|
186
|
+
auto: DSIOT_LOUVER_VALUES.auto,
|
|
187
|
+
floorWarming: DSIOT_LOUVER_VALUES.floorWarming,
|
|
188
|
+
};
|
|
189
|
+
const FIXED_VERTICAL_POSITIONS = FIXED_LOUVER_POSITIONS;
|
|
190
|
+
/** The value each `HorizontalPosition` is held as. `stop` and a fixed position
|
|
191
|
+
* have none: see UNSUPPORTED.md. */
|
|
192
|
+
export const DSIOT_HORIZONTAL_POSITION_VALUES = {
|
|
193
|
+
swing: DSIOT_LOUVER_VALUES.swing,
|
|
194
|
+
auto: DSIOT_LOUVER_VALUES.auto,
|
|
195
|
+
};
|
|
196
|
+
function positionOf(positions, held, value) {
|
|
197
|
+
return positions.find((position) => held[position] === value) ?? null;
|
|
198
|
+
}
|
|
199
|
+
function isFixedLouverValue(value) {
|
|
200
|
+
return value >= 1 && value <= 14;
|
|
201
|
+
}
|
|
202
|
+
function fixedLouverValues(values, path) {
|
|
203
|
+
const mask = decodeDsiotLittleEndian(values[`${path}.md.mx`] ?? "");
|
|
204
|
+
return FIXED_LOUVER_VALUES.filter((value) => mask & (1 << value));
|
|
205
|
+
}
|
|
206
|
+
function encodeLouver(value) {
|
|
207
|
+
return `${value.toString(16).toUpperCase().padStart(2, "0")}0000`;
|
|
208
|
+
}
|
|
209
|
+
// A fixed vertical position is named by its rank among the values the unit
|
|
210
|
+
// takes: a unit taking 2, 4 and 6 reports them as `position1`, `position2` and
|
|
211
|
+
// `position3`. A value the unit does not list reads as `stop`.
|
|
212
|
+
function parseVerticalPosition(values, path) {
|
|
213
|
+
const pv = values[path];
|
|
214
|
+
if (pv === undefined) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const value = decodeDsiotLittleEndian(pv);
|
|
218
|
+
if (!isFixedLouverValue(value)) {
|
|
219
|
+
return positionOf(VERTICAL_POSITIONS, DSIOT_VERTICAL_POSITION_VALUES, value);
|
|
220
|
+
}
|
|
221
|
+
const rank = fixedLouverValues(values, path).indexOf(value);
|
|
222
|
+
return rank === -1 ? "stop" : (FIXED_VERTICAL_POSITIONS[rank] ?? null);
|
|
223
|
+
}
|
|
224
|
+
// `undefined` for a position the unit does not take in this mode.
|
|
225
|
+
function encodeVerticalPosition(position, values, path) {
|
|
226
|
+
const rank = FIXED_VERTICAL_POSITIONS.indexOf(position);
|
|
227
|
+
const value = rank === -1
|
|
228
|
+
? DSIOT_VERTICAL_POSITION_VALUES[position]
|
|
229
|
+
: fixedLouverValues(values, path)[rank];
|
|
230
|
+
return value === undefined ? undefined : encodeLouver(value);
|
|
231
|
+
}
|
|
232
|
+
// A fixed horizontal position reads as `stop`: see UNSUPPORTED.md.
|
|
233
|
+
function parseHorizontalPosition(pv) {
|
|
234
|
+
if (pv === undefined) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const value = decodeDsiotLittleEndian(pv);
|
|
238
|
+
return value === DSIOT_LOUVER_VALUES.stop || isFixedLouverValue(value)
|
|
239
|
+
? "stop"
|
|
240
|
+
: positionOf(HORIZONTAL_POSITIONS, DSIOT_HORIZONTAL_POSITION_VALUES, value);
|
|
241
|
+
}
|
|
242
|
+
// `undefined` for a fixed position, which has no encoding: see UNSUPPORTED.md.
|
|
243
|
+
function encodeHorizontalPosition(position, current) {
|
|
244
|
+
if (position === "stop") {
|
|
245
|
+
// Stopping the axis keeps a fixed position it already holds.
|
|
246
|
+
return current !== undefined &&
|
|
247
|
+
isFixedLouverValue(decodeDsiotLittleEndian(current))
|
|
248
|
+
? current
|
|
249
|
+
: DSIOT_LOUVER_STOP;
|
|
250
|
+
}
|
|
251
|
+
const value = DSIOT_HORIZONTAL_POSITION_VALUES[position];
|
|
252
|
+
return value === undefined ? undefined : encodeLouver(value);
|
|
253
|
+
}
|
|
254
|
+
const HUMIDITY_OFF = "00";
|
|
255
|
+
const HUMIDITY_ON = "01";
|
|
256
|
+
// Also reads as off, and a control that switches humidity off leaves it.
|
|
257
|
+
const HUMIDITY_OFF_KEPT = "05";
|
|
258
|
+
const HUMIDITY_SETTING_FLAGS = {
|
|
259
|
+
auto: "02",
|
|
260
|
+
autoHigh: "03",
|
|
261
|
+
autoLow: "04",
|
|
262
|
+
continuous: "06",
|
|
263
|
+
};
|
|
264
|
+
const HUMIDITY_SETTINGS_BY_FLAG = Object.fromEntries(Object.entries(HUMIDITY_SETTING_FLAGS).map(([setting, flag]) => [
|
|
265
|
+
flag,
|
|
266
|
+
setting,
|
|
267
|
+
]));
|
|
268
|
+
// A step (`md.st`) is one byte, in decimal: the low nibble counts, and the
|
|
269
|
+
// high nibble, as a signed nibble, is the power of ten that scales the count.
|
|
270
|
+
// A value is used as it is when its leaf has no step, or a step of zero.
|
|
271
|
+
function decodeStep(st) {
|
|
272
|
+
const byte = Number(st ?? Number.NaN);
|
|
273
|
+
if (!Number.isInteger(byte) || byte < 0 || byte > 255) {
|
|
274
|
+
return 1;
|
|
275
|
+
}
|
|
276
|
+
const exponent = byte >> 4;
|
|
277
|
+
return (byte & 0x0f) * 10 ** (exponent < 8 ? exponent : exponent - 16) || 1;
|
|
278
|
+
}
|
|
279
|
+
function parseTargetHumidity(values, humidity) {
|
|
280
|
+
const flag = humidity && values[humidity.flag];
|
|
281
|
+
if (flag === HUMIDITY_OFF || flag === HUMIDITY_OFF_KEPT) {
|
|
282
|
+
return "off";
|
|
283
|
+
}
|
|
284
|
+
if (humidity === undefined || flag !== HUMIDITY_ON) {
|
|
285
|
+
return HUMIDITY_SETTINGS_BY_FLAG[flag ?? ""] ?? null;
|
|
286
|
+
}
|
|
287
|
+
const count = Number.parseInt(values[humidity.value] ?? "", 16);
|
|
288
|
+
return Number.isFinite(count)
|
|
289
|
+
? {
|
|
290
|
+
percent: Math.trunc(count * decodeStep(values[`${humidity.value}.md.st`])),
|
|
291
|
+
}
|
|
292
|
+
: null;
|
|
293
|
+
}
|
|
294
|
+
function applyTargetHumidity(values, { value, flag }, targetHumidity) {
|
|
295
|
+
if (targetHumidity === "off") {
|
|
296
|
+
return [
|
|
297
|
+
{
|
|
298
|
+
path: flag,
|
|
299
|
+
value: values[flag] === HUMIDITY_OFF_KEPT ? HUMIDITY_OFF_KEPT : HUMIDITY_OFF,
|
|
300
|
+
},
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
if (typeof targetHumidity === "string") {
|
|
304
|
+
return [{ path: flag, value: HUMIDITY_SETTING_FLAGS[targetHumidity] }];
|
|
305
|
+
}
|
|
306
|
+
const count = Math.trunc(targetHumidity.percent / decodeStep(values[`${value}.md.st`]));
|
|
307
|
+
return [
|
|
308
|
+
{ path: flag, value: HUMIDITY_ON },
|
|
309
|
+
{ path: value, value: count.toString(16).toUpperCase().padStart(2, "0") },
|
|
310
|
+
];
|
|
311
|
+
}
|
|
312
|
+
// Whether the mode's humidity setting is on once the control is applied.
|
|
313
|
+
function isHumidityOn(values, humidity, targetHumidity) {
|
|
314
|
+
const flag = humidity && values[humidity.flag];
|
|
315
|
+
if (flag === undefined) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
return targetHumidity == null
|
|
319
|
+
? flag !== HUMIDITY_OFF && flag !== HUMIDITY_OFF_KEPT
|
|
320
|
+
: targetHumidity !== "off";
|
|
321
|
+
}
|
|
322
|
+
const POWER_TRANSITIONS = {
|
|
323
|
+
turningOn: "00",
|
|
324
|
+
turningOff: "01",
|
|
325
|
+
unchanged: "02",
|
|
326
|
+
};
|
|
327
|
+
/** Turns the flattened indoor `dgc_status` tree, read with `md`, into a
|
|
328
|
+
* `DaikinState`. Every value is per mode: the setpoint, humidity, fan speed and
|
|
329
|
+
* louvers each have a leaf for each mode, and the state reads the current
|
|
330
|
+
* mode's. */
|
|
331
|
+
export function parseDsiotState(values) {
|
|
332
|
+
const mode = decodeDsiotMode(values[DSIOT_PATHS.mode]) ?? "auto";
|
|
333
|
+
const paths = DSIOT_MODE_PATHS[mode];
|
|
334
|
+
return {
|
|
335
|
+
power: decodeDsiotBoolean(values[DSIOT_PATHS.power]),
|
|
336
|
+
mode,
|
|
337
|
+
targetTemperatureInC: paths.setpoint
|
|
338
|
+
? decodeDsiotSetpoint(values[paths.setpoint.path], paths.setpoint.signed)
|
|
339
|
+
: null,
|
|
340
|
+
targetHumidity: parseTargetHumidity(values, paths.humidity),
|
|
341
|
+
fanSpeed: decodeDsiotFanSpeed(values[paths.fanSpeed]) ?? "auto",
|
|
342
|
+
swingMode: decodeSwing(values[paths.vertical], values[paths.horizontal]),
|
|
343
|
+
verticalPosition: parseVerticalPosition(values, paths.vertical),
|
|
344
|
+
horizontalPosition: parseHorizontalPosition(values[paths.horizontal]),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/** Turns `values`, the flattened indoor `dgc_status` tree read with `md`, and
|
|
348
|
+
* a control into the leaves `buildDsiotMultireqUpdate` writes: power, its
|
|
349
|
+
* transition and the mode every time, then whichever of the target mode's
|
|
350
|
+
* leaves the control changes. It drops a `targetTemperatureInC` in a mode without
|
|
351
|
+
* a writable setpoint, a `targetHumidity` in a mode the unit has no humidity
|
|
352
|
+
* leaves for, and a louver position the unit does not take. It always writes
|
|
353
|
+
* the `auto` fan speed in dehumidify, and in cool while a humidity setting is
|
|
354
|
+
* on. A unit in a mode outside `OperatingMode` keeps it unless the control
|
|
355
|
+
* sets one, and gets power, its transition and that mode alone. */
|
|
356
|
+
export function applyDsiotControl(values, control) {
|
|
357
|
+
const currentPower = decodeDsiotBoolean(values[DSIOT_PATHS.power]);
|
|
358
|
+
const power = control.power ?? currentPower;
|
|
359
|
+
const heldMode = control.mode === undefined
|
|
360
|
+
? (values[DSIOT_PATHS.mode] ?? encodeDsiotMode("auto"))
|
|
361
|
+
: encodeDsiotMode(control.mode);
|
|
362
|
+
const mode = decodeDsiotMode(heldMode);
|
|
363
|
+
const updates = [
|
|
364
|
+
{ path: DSIOT_PATHS.power, value: encodeDsiotBoolean(power) },
|
|
365
|
+
{
|
|
366
|
+
path: DSIOT_PATHS.powerTransition,
|
|
367
|
+
value: power === currentPower
|
|
368
|
+
? POWER_TRANSITIONS.unchanged
|
|
369
|
+
: power
|
|
370
|
+
? POWER_TRANSITIONS.turningOn
|
|
371
|
+
: POWER_TRANSITIONS.turningOff,
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
path: DSIOT_PATHS.mode,
|
|
375
|
+
value: mode === undefined ? heldMode : encodeDsiotMode(mode),
|
|
376
|
+
},
|
|
377
|
+
];
|
|
378
|
+
// Which leaves a mode outside the model reads and writes is not known.
|
|
379
|
+
if (mode === undefined) {
|
|
380
|
+
return updates;
|
|
381
|
+
}
|
|
382
|
+
const paths = DSIOT_MODE_PATHS[mode];
|
|
383
|
+
if (control.targetTemperatureInC != null && paths.setpoint?.writable) {
|
|
384
|
+
updates.push({
|
|
385
|
+
path: paths.setpoint.path,
|
|
386
|
+
value: encodeDsiotSetpoint(control.targetTemperatureInC),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (control.targetHumidity != null &&
|
|
390
|
+
paths.humidity &&
|
|
391
|
+
paths.humidity.flag in values) {
|
|
392
|
+
updates.push(...applyTargetHumidity(values, paths.humidity, control.targetHumidity));
|
|
393
|
+
}
|
|
394
|
+
// Dehumidify runs the fan on auto only, and so does cool while a humidity
|
|
395
|
+
// setting is on.
|
|
396
|
+
if (mode === "dehumidify" ||
|
|
397
|
+
(mode === "cool" &&
|
|
398
|
+
isHumidityOn(values, paths.humidity, control.targetHumidity))) {
|
|
399
|
+
updates.push({ path: paths.fanSpeed, value: encodeDsiotFanSpeed("auto") });
|
|
400
|
+
}
|
|
401
|
+
else if (control.fanSpeed !== undefined) {
|
|
402
|
+
updates.push({
|
|
403
|
+
path: paths.fanSpeed,
|
|
404
|
+
value: encodeDsiotFanSpeed(control.fanSpeed),
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
let vertical;
|
|
408
|
+
let horizontal;
|
|
409
|
+
if (control.swingMode !== undefined) {
|
|
410
|
+
const swingsVertically = control.swingMode === "vertical" || control.swingMode === "both";
|
|
411
|
+
const swingsHorizontally = control.swingMode === "horizontal" || control.swingMode === "both";
|
|
412
|
+
vertical = louver(swingsVertically, values[paths.vertical]);
|
|
413
|
+
horizontal = louver(swingsHorizontally, values[paths.horizontal]);
|
|
414
|
+
}
|
|
415
|
+
// A position overrides the swing mode sent with it.
|
|
416
|
+
if (control.verticalPosition != null) {
|
|
417
|
+
vertical =
|
|
418
|
+
encodeVerticalPosition(control.verticalPosition, values, paths.vertical) ?? vertical;
|
|
419
|
+
}
|
|
420
|
+
if (control.horizontalPosition != null) {
|
|
421
|
+
horizontal =
|
|
422
|
+
encodeHorizontalPosition(control.horizontalPosition, values[paths.horizontal]) ?? horizontal;
|
|
423
|
+
}
|
|
424
|
+
if (vertical !== undefined) {
|
|
425
|
+
updates.push({ path: paths.vertical, value: vertical });
|
|
426
|
+
}
|
|
427
|
+
if (horizontal !== undefined) {
|
|
428
|
+
updates.push({ path: paths.horizontal, value: horizontal });
|
|
429
|
+
}
|
|
430
|
+
return updates;
|
|
431
|
+
}
|
|
432
|
+
export function parseDsiotSensors(values) {
|
|
433
|
+
return {
|
|
434
|
+
indoorTemperatureInC: decodeDsiotIndoorTemperature(values[DSIOT_PATHS.indoorTemperature]),
|
|
435
|
+
indoorHumidityInPercent: decodeDsiotIndoorHumidity(values[DSIOT_PATHS.indoorHumidity]),
|
|
436
|
+
outdoorTemperatureInC: decodeDsiotOutdoorTemperature(values[DSIOT_PATHS.outdoorTemperature]),
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function decodeSwing(vertical, horizontal) {
|
|
440
|
+
const swingsVertically = isDsiotLouverSwinging(vertical);
|
|
441
|
+
const swingsHorizontally = isDsiotLouverSwinging(horizontal);
|
|
442
|
+
if (swingsVertically && swingsHorizontally) {
|
|
443
|
+
return "both";
|
|
444
|
+
}
|
|
445
|
+
return swingsVertically
|
|
446
|
+
? "vertical"
|
|
447
|
+
: swingsHorizontally
|
|
448
|
+
? "horizontal"
|
|
449
|
+
: "off";
|
|
450
|
+
}
|
|
451
|
+
// Stopping a swinging axis stops the louver; an axis already at a fixed
|
|
452
|
+
// position keeps it.
|
|
453
|
+
function louver(swing, current) {
|
|
454
|
+
if (swing) {
|
|
455
|
+
return DSIOT_LOUVER_SWING;
|
|
456
|
+
}
|
|
457
|
+
return current === undefined || isDsiotLouverSwinging(current)
|
|
458
|
+
? DSIOT_LOUVER_STOP
|
|
459
|
+
: current;
|
|
460
|
+
}
|
|
461
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/codecs/dsiot/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DaikinDevice } from "./models/device.js";
|
|
2
|
+
export interface DiscoverDevicesOptions {
|
|
3
|
+
/** How long to listen for replies, in milliseconds. Defaults to 3000. */
|
|
4
|
+
timeoutInMs?: number;
|
|
5
|
+
}
|
|
6
|
+
/** Finds the adapters on the LAN: broadcasts `DAIKIN_UDP/common/basic_info` to
|
|
7
|
+
* UDP port 30050 and collects the `basic_info` replies, which carry the same
|
|
8
|
+
* `key=value` payload as the HTTP `/common/basic_info`. Classic and DSIOT
|
|
9
|
+
* adapters both answer, and `parseDevice` picks the protocol from the reply.
|
|
10
|
+
* Anything on the LAN can send to the port it listens on, so it ignores a
|
|
11
|
+
* datagram whose `ret` is not `OK`.
|
|
12
|
+
*
|
|
13
|
+
* It resolves once `timeoutInMs` has passed, never earlier, with one device
|
|
14
|
+
* per replying address. It binds UDP port 30000, and rejects with the socket's
|
|
15
|
+
* error when the socket fails. */
|
|
16
|
+
export declare function discoverDevices(options?: DiscoverDevicesOptions): Promise<DaikinDevice[]>;
|
|
17
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAcvD,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;kCASkC;AAClC,wBAAsB,eAAe,CACnC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,YAAY,EAAE,CAAC,CA+CzB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createSocket } from "node:dgram";
|
|
2
|
+
import { isClassicOk, parseClassicKeyValue, } from "./codecs/classic/keyValue.js";
|
|
3
|
+
import { parseDevice } from "./codecs/device.js";
|
|
4
|
+
// The UDP discovery probe: broadcast this to :30050, listen for `basic_info`
|
|
5
|
+
// replies.
|
|
6
|
+
const DISCOVERY_PROBE = "DAIKIN_UDP/common/basic_info";
|
|
7
|
+
const DISCOVERY_SEND_PORT = 30000;
|
|
8
|
+
const DISCOVERY_LISTEN_PORT = 30050;
|
|
9
|
+
const DEFAULT_DISCOVERY_TIMEOUT_IN_MS = 3_000;
|
|
10
|
+
const BROADCAST_ADDRESS = "255.255.255.255";
|
|
11
|
+
// UDP drops packets, so the probe goes out 10 times, 200 ms apart.
|
|
12
|
+
const PROBE_COUNT = 10;
|
|
13
|
+
const PROBE_INTERVAL_IN_MS = 200;
|
|
14
|
+
/** Finds the adapters on the LAN: broadcasts `DAIKIN_UDP/common/basic_info` to
|
|
15
|
+
* UDP port 30050 and collects the `basic_info` replies, which carry the same
|
|
16
|
+
* `key=value` payload as the HTTP `/common/basic_info`. Classic and DSIOT
|
|
17
|
+
* adapters both answer, and `parseDevice` picks the protocol from the reply.
|
|
18
|
+
* Anything on the LAN can send to the port it listens on, so it ignores a
|
|
19
|
+
* datagram whose `ret` is not `OK`.
|
|
20
|
+
*
|
|
21
|
+
* It resolves once `timeoutInMs` has passed, never earlier, with one device
|
|
22
|
+
* per replying address. It binds UDP port 30000, and rejects with the socket's
|
|
23
|
+
* error when the socket fails. */
|
|
24
|
+
export async function discoverDevices(options = {}) {
|
|
25
|
+
const socket = createSocket({ type: "udp4", reuseAddr: true });
|
|
26
|
+
const devices = new Map();
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
let probesSent = 0;
|
|
29
|
+
let probeTimer;
|
|
30
|
+
const listenTimer = setTimeout(() => {
|
|
31
|
+
clearInterval(probeTimer);
|
|
32
|
+
socket.close();
|
|
33
|
+
resolve([...devices.values()]);
|
|
34
|
+
}, options.timeoutInMs ?? DEFAULT_DISCOVERY_TIMEOUT_IN_MS);
|
|
35
|
+
socket.on("error", (error) => {
|
|
36
|
+
clearInterval(probeTimer);
|
|
37
|
+
// A socket throws when it is closed twice, so the timer must not fire.
|
|
38
|
+
clearTimeout(listenTimer);
|
|
39
|
+
socket.close();
|
|
40
|
+
reject(error);
|
|
41
|
+
});
|
|
42
|
+
socket.on("message", (message, remote) => {
|
|
43
|
+
if (devices.has(remote.address)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const fields = parseClassicKeyValue(message.toString("utf8"));
|
|
47
|
+
if (!isClassicOk(fields)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
devices.set(remote.address, parseDevice(remote.address, fields));
|
|
51
|
+
});
|
|
52
|
+
socket.bind(DISCOVERY_SEND_PORT, () => {
|
|
53
|
+
socket.setBroadcast(true);
|
|
54
|
+
const probe = Buffer.from(DISCOVERY_PROBE, "utf8");
|
|
55
|
+
const sendProbe = () => {
|
|
56
|
+
socket.send(probe, DISCOVERY_LISTEN_PORT, BROADCAST_ADDRESS);
|
|
57
|
+
probesSent += 1;
|
|
58
|
+
if (probesSent >= PROBE_COUNT) {
|
|
59
|
+
clearInterval(probeTimer);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
sendProbe();
|
|
63
|
+
probeTimer = setInterval(sendProbe, PROBE_INTERVAL_IN_MS);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACL,WAAW,EACX,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGjD,6EAA6E;AAC7E,WAAW;AACX,MAAM,eAAe,GAAG,8BAA8B,CAAC;AACvD,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAClC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAEpC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAC9C,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAC5C,mEAAmE;AACnE,MAAM,WAAW,GAAG,EAAE,CAAC;AACvB,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAOjC;;;;;;;;;kCASkC;AAClC,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAO,GAA2B,EAAE;IAEpC,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEhD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,UAAsC,CAAC;QAE3C,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1B,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjC,CAAC,EAAE,OAAO,CAAC,WAAW,IAAI,+BAA+B,CAAC,CAAC;QAE3D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1B,uEAAuE;YACvE,YAAY,CAAC,WAAW,CAAC,CAAC;YAC1B,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACvC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChC,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAC9D,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE;YACpC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YACnD,MAAM,SAAS,GAAG,GAAG,EAAE;gBACrB,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;gBAC7D,UAAU,IAAI,CAAC,CAAC;gBAChB,IAAI,UAAU,IAAI,WAAW,EAAE,CAAC;oBAC9B,aAAa,CAAC,UAAU,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC,CAAC;YACF,SAAS,EAAE,CAAC;YACZ,UAAU,GAAG,WAAW,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class DaikinError extends Error {
|
|
2
|
+
/** The adapter's own HTTP status, or the status this package gives a
|
|
3
|
+
* failure that has none: the README's "Errors and retries" lists them. */
|
|
4
|
+
readonly httpStatusCode: number;
|
|
5
|
+
constructor(message: string,
|
|
6
|
+
/** The adapter's own HTTP status, or the status this package gives a
|
|
7
|
+
* failure that has none: the README's "Errors and retries" lists them. */
|
|
8
|
+
httpStatusCode: number);
|
|
9
|
+
}
|
|
10
|
+
/** The adapter refused or dropped the connection before answering. The only
|
|
11
|
+
* failure `withRetry` repeats: see DESIGN.md#retries. */
|
|
12
|
+
export declare class DaikinConnectionError extends DaikinError {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAGlC;8EAC0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,MAAM;IAJjC,YACE,OAAO,EAAE,MAAM;IACf;8EAC0E;IACjE,cAAc,EAAE,MAAM,EAIhC;CACF;AAED;yDACyD;AACzD,qBAAa,qBAAsB,SAAQ,WAAW;IACpD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF"}
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class DaikinError extends Error {
|
|
2
|
+
httpStatusCode;
|
|
3
|
+
constructor(message,
|
|
4
|
+
/** The adapter's own HTTP status, or the status this package gives a
|
|
5
|
+
* failure that has none: the README's "Errors and retries" lists them. */
|
|
6
|
+
httpStatusCode) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.httpStatusCode = httpStatusCode;
|
|
9
|
+
this.name = "DaikinError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** The adapter refused or dropped the connection before answering. The only
|
|
13
|
+
* failure `withRetry` repeats: see DESIGN.md#retries. */
|
|
14
|
+
export class DaikinConnectionError extends DaikinError {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message, 503);
|
|
17
|
+
this.name = "DaikinConnectionError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAY,SAAQ,KAAK;IAKzB,cAAc;IAJzB,YACE,OAAe;IACf;8EAC0E;IACjE,cAAsB;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;8BAFN,cAAc;QAGvB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED;yDACyD;AACzD,MAAM,OAAO,qBAAsB,SAAQ,WAAW;IACpD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DaikinClient } from "./clients/client.js";
|
|
2
|
+
import type { DaikinDevice } from "./models/device.js";
|
|
3
|
+
import { type DaikinRetryOptions, type DaikinTlsOptions } from "./transport.js";
|
|
4
|
+
export interface CreateDaikinClientOptions {
|
|
5
|
+
/** The terminal uuid, sent as `X-Daikin-uuid`. Generate one and reuse it:
|
|
6
|
+
* an adapter that requires registration remembers it. */
|
|
7
|
+
terminalUuid: string;
|
|
8
|
+
/** The child lock code, for a device whose `requiresLocalPassword` is true. */
|
|
9
|
+
localPassword?: string;
|
|
10
|
+
/** How long, in milliseconds, a request waits for the adapter. Defaults to
|
|
11
|
+
* 10000: see `DaikinTransportOptions.timeoutInMs`. */
|
|
12
|
+
timeoutInMs?: number;
|
|
13
|
+
/** The waits between retries of a refused or dropped connection. Defaults to
|
|
14
|
+
* waits of 0, 50, 100, 200 and 400 milliseconds: see
|
|
15
|
+
* `DaikinRetryOptions.delaysInMs`. */
|
|
16
|
+
retry?: DaikinRetryOptions;
|
|
17
|
+
/** HTTPS adapters only. Without a CA the adapter's certificate isn't verified. */
|
|
18
|
+
tls?: DaikinTlsOptions;
|
|
19
|
+
}
|
|
20
|
+
/** Builds the client for a device: a `DsiotDaikinClient` when
|
|
21
|
+
* `device.protocol` is `dsiot`, a `ClassicDaikinClient` otherwise. The client
|
|
22
|
+
* sends over HTTPS when `device.usesHttps` is set, and sends the local
|
|
23
|
+
* password when `options.localPassword` is given. Call `close` on the client
|
|
24
|
+
* when done with the unit. */
|
|
25
|
+
export declare function createDaikinClient(device: DaikinDevice, options: CreateDaikinClientOptions): DaikinClient;
|
|
26
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAEtB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,yBAAyB;IACxC;6DACyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;0DACsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;0CAEsC;IACtC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,kFAAkF;IAClF,GAAG,CAAC,EAAE,gBAAgB,CAAC;CACxB;AAED;;;;8BAI8B;AAC9B,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,yBAAyB,GACjC,YAAY,CAad"}
|