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.
Files changed (115) hide show
  1. package/DESIGN.md +98 -0
  2. package/LICENSE +674 -0
  3. package/README.md +317 -0
  4. package/UNSUPPORTED.md +43 -0
  5. package/dist/authentication.d.ts +24 -0
  6. package/dist/authentication.d.ts.map +1 -0
  7. package/dist/authentication.js +30 -0
  8. package/dist/authentication.js.map +1 -0
  9. package/dist/clients/classic.d.ts +53 -0
  10. package/dist/clients/classic.d.ts.map +1 -0
  11. package/dist/clients/classic.js +142 -0
  12. package/dist/clients/classic.js.map +1 -0
  13. package/dist/clients/client.d.ts +21 -0
  14. package/dist/clients/client.d.ts.map +1 -0
  15. package/dist/clients/client.js +2 -0
  16. package/dist/clients/client.js.map +1 -0
  17. package/dist/clients/dsiot.d.ts +28 -0
  18. package/dist/clients/dsiot.d.ts.map +1 -0
  19. package/dist/clients/dsiot.js +66 -0
  20. package/dist/clients/dsiot.js.map +1 -0
  21. package/dist/codecs/classic/capabilities.d.ts +19 -0
  22. package/dist/codecs/classic/capabilities.d.ts.map +1 -0
  23. package/dist/codecs/classic/capabilities.js +208 -0
  24. package/dist/codecs/classic/capabilities.js.map +1 -0
  25. package/dist/codecs/classic/extendedControl.d.ts +20 -0
  26. package/dist/codecs/classic/extendedControl.d.ts.map +1 -0
  27. package/dist/codecs/classic/extendedControl.js +88 -0
  28. package/dist/codecs/classic/extendedControl.js.map +1 -0
  29. package/dist/codecs/classic/keyValue.d.ts +10 -0
  30. package/dist/codecs/classic/keyValue.d.ts.map +1 -0
  31. package/dist/codecs/classic/keyValue.js +36 -0
  32. package/dist/codecs/classic/keyValue.js.map +1 -0
  33. package/dist/codecs/classic/state.d.ts +18 -0
  34. package/dist/codecs/classic/state.d.ts.map +1 -0
  35. package/dist/codecs/classic/state.js +235 -0
  36. package/dist/codecs/classic/state.js.map +1 -0
  37. package/dist/codecs/credentials.d.ts +8 -0
  38. package/dist/codecs/credentials.d.ts.map +1 -0
  39. package/dist/codecs/credentials.js +14 -0
  40. package/dist/codecs/credentials.js.map +1 -0
  41. package/dist/codecs/device.d.ts +6 -0
  42. package/dist/codecs/device.d.ts.map +1 -0
  43. package/dist/codecs/device.js +35 -0
  44. package/dist/codecs/device.js.map +1 -0
  45. package/dist/codecs/dsiot/capabilities.d.ts +5 -0
  46. package/dist/codecs/dsiot/capabilities.d.ts.map +1 -0
  47. package/dist/codecs/dsiot/capabilities.js +139 -0
  48. package/dist/codecs/dsiot/capabilities.js.map +1 -0
  49. package/dist/codecs/dsiot/multireq.d.ts +37 -0
  50. package/dist/codecs/dsiot/multireq.d.ts.map +1 -0
  51. package/dist/codecs/dsiot/multireq.js +122 -0
  52. package/dist/codecs/dsiot/multireq.js.map +1 -0
  53. package/dist/codecs/dsiot/state.d.ts +92 -0
  54. package/dist/codecs/dsiot/state.d.ts.map +1 -0
  55. package/dist/codecs/dsiot/state.js +461 -0
  56. package/dist/codecs/dsiot/state.js.map +1 -0
  57. package/dist/discovery.d.ts +17 -0
  58. package/dist/discovery.d.ts.map +1 -0
  59. package/dist/discovery.js +67 -0
  60. package/dist/discovery.js.map +1 -0
  61. package/dist/error.d.ts +15 -0
  62. package/dist/error.d.ts.map +1 -0
  63. package/dist/error.js +20 -0
  64. package/dist/error.js.map +1 -0
  65. package/dist/factory.d.ts +26 -0
  66. package/dist/factory.d.ts.map +1 -0
  67. package/dist/factory.js +31 -0
  68. package/dist/factory.js.map +1 -0
  69. package/dist/index.d.ts +22 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +21 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/models/capabilities.d.ts +42 -0
  74. package/dist/models/capabilities.d.ts.map +1 -0
  75. package/dist/models/capabilities.js +2 -0
  76. package/dist/models/capabilities.js.map +1 -0
  77. package/dist/models/device.d.ts +24 -0
  78. package/dist/models/device.d.ts.map +1 -0
  79. package/dist/models/device.js +2 -0
  80. package/dist/models/device.js.map +1 -0
  81. package/dist/models/extendedControl.d.ts +31 -0
  82. package/dist/models/extendedControl.d.ts.map +1 -0
  83. package/dist/models/extendedControl.js +7 -0
  84. package/dist/models/extendedControl.js.map +1 -0
  85. package/dist/models/state.d.ts +56 -0
  86. package/dist/models/state.d.ts.map +1 -0
  87. package/dist/models/state.js +49 -0
  88. package/dist/models/state.js.map +1 -0
  89. package/dist/transport.d.ts +75 -0
  90. package/dist/transport.d.ts.map +1 -0
  91. package/dist/transport.js +118 -0
  92. package/dist/transport.js.map +1 -0
  93. package/package.json +62 -0
  94. package/src/authentication.ts +53 -0
  95. package/src/clients/classic.ts +251 -0
  96. package/src/clients/client.ts +29 -0
  97. package/src/clients/dsiot.ts +116 -0
  98. package/src/codecs/classic/capabilities.ts +272 -0
  99. package/src/codecs/classic/extendedControl.ts +132 -0
  100. package/src/codecs/classic/keyValue.ts +39 -0
  101. package/src/codecs/classic/state.ts +302 -0
  102. package/src/codecs/credentials.ts +19 -0
  103. package/src/codecs/device.ts +41 -0
  104. package/src/codecs/dsiot/capabilities.ts +209 -0
  105. package/src/codecs/dsiot/multireq.ts +162 -0
  106. package/src/codecs/dsiot/state.ts +644 -0
  107. package/src/discovery.ts +85 -0
  108. package/src/error.ts +20 -0
  109. package/src/factory.ts +66 -0
  110. package/src/index.ts +21 -0
  111. package/src/models/capabilities.ts +52 -0
  112. package/src/models/device.ts +25 -0
  113. package/src/models/extendedControl.ts +41 -0
  114. package/src/models/state.ts +110 -0
  115. package/src/transport.ts +191 -0
package/README.md ADDED
@@ -0,0 +1,317 @@
1
+ # daikin-local-control
2
+
3
+ A TypeScript client for controlling Daikin air-conditioner Wi-Fi adapters **over
4
+ the local network**, with no cloud dependency. It implements the same
5
+ local-control protocol as the **DAIKIN Mobile Controller** app, so it works with
6
+ the units and Wi-Fi adapters that app supports — split / multi-split and SkyAir
7
+ units fitted with adapters such as the BRP072A42, BRP072C42 and BRP084 — in the
8
+ [markets where that app is used](#coverage--limitations).
9
+
10
+ Daikin Wi-Fi adapters come in two generations, and the package has a client for
11
+ each:
12
+
13
+ - **`ClassicDaikinClient`** — classic adapters (BRP072A42, BRP072C42, BRP069).
14
+ Full core control plus typed [extended controls](#extended-controls).
15
+ - **`DsiotDaikinClient`** — newer DSIOT adapters (BRP084). Power, mode,
16
+ setpoint, humidity, fan, swing and louver positions;
17
+ [UNSUPPORTED.md](UNSUPPORTED.md) lists what is not mapped yet.
18
+
19
+ You normally don't pick one yourself: `createDaikinClient` chooses the right
20
+ client for each discovered device.
21
+
22
+ ## Usage
23
+
24
+ ```ts
25
+ import { discoverDevices, createDaikinClient } from "daikin-local-control";
26
+
27
+ // 1. Find adapters on the LAN (UDP broadcast DAIKIN_UDP/common/basic_info :30050).
28
+ const [device] = await discoverDevices();
29
+ if (device === undefined) {
30
+ throw new Error("No adapter answered");
31
+ }
32
+
33
+ // 2. Build the right client: createDaikinClient picks the classic or the DSIOT
34
+ // client from the device. Pass localPassword only when the adapter has a
35
+ // child lock code (see Credentials below).
36
+ const client = createDaikinClient(device, {
37
+ terminalUuid: savedUuid, // generated once and stored (see Credentials below)
38
+ localPassword: device.requiresLocalPassword ? "<child lock code>" : undefined,
39
+ });
40
+
41
+ // 3. Capabilities are data — branch on them, don't special-case device types.
42
+ const capabilities = await client.getCapabilities();
43
+ if (capabilities.swingModes.includes("both")) {
44
+ /* this unit swings on both axes */
45
+ }
46
+
47
+ // 4. Read and control.
48
+ const state = await client.getState();
49
+ await client.setState({ power: true, mode: "cool", targetTemperatureInC: 24 });
50
+
51
+ const { indoorTemperatureInC } = await client.getSensors();
52
+
53
+ // 5. Close the connection to the adapter when you're done with the unit.
54
+ await client.close();
55
+ ```
56
+
57
+ ### Discovery
58
+
59
+ `discoverDevices()` resolves with one `DaikinDevice` for each adapter that
60
+ answered, and with an empty array when none did.
61
+
62
+ | It | So |
63
+ | --- | --- |
64
+ | Resolves after `timeoutInMs`, never earlier | Pass `discoverDevices({ timeoutInMs: 1000 })` to wait less than the default 3000 |
65
+ | Broadcasts to `255.255.255.255` | It does not find an adapter on another subnet or VLAN |
66
+ | Binds UDP port 30000 | It rejects with the socket's error when the socket fails |
67
+
68
+ Write the device by hand when you already know its address, or when discovery
69
+ cannot reach it:
70
+
71
+ ```ts
72
+ import { createDaikinClient, type DaikinDevice } from "daikin-local-control";
73
+
74
+ const device: DaikinDevice = { host: "192.168.1.50", protocol: "classic" };
75
+ const client = createDaikinClient(device, { terminalUuid: savedUuid });
76
+ ```
77
+
78
+ Set `protocol` to `dsiot` for a BRP084 and to `classic` for every other
79
+ adapter. A device without flags is an adapter that requires neither
80
+ registration nor a child lock code. If yours requires either, set
81
+ `requiresRegistration: true` / `requiresLocalPassword: true` and set up
82
+ [credentials](#credentials) to match; add `usesHttps: true` if it also speaks
83
+ HTTPS. The flags are independent, and discovery reads them all from the adapter
84
+ for you.
85
+
86
+ ### Credentials
87
+
88
+ An adapter can need up to three things. Discovery tells you which ones apply
89
+ (`device.requiresRegistration`, `device.requiresLocalPassword`).
90
+
91
+ **Terminal uuid (always).** It identifies your app to the adapter. Any random
92
+ UUID works. Generate it **once**, store it, and reuse it on every run: an
93
+ adapter that requires registration remembers it, so a new uuid means registering
94
+ again.
95
+
96
+ ```ts
97
+ import { randomUUID } from "node:crypto";
98
+
99
+ const savedUuid = loadFromConfiguration() ?? saveToConfiguration(randomUUID());
100
+ ```
101
+
102
+ **KEY (when `device.requiresRegistration` is `true`).** These
103
+ adapters only answer terminals they have registered. Each adapter's KEY is
104
+ printed on its label, next to the SSID (also in the operation manual). Register
105
+ your uuid with it once. Until you do, every call fails with HTTP 401/403.
106
+ Each uuid is registered separately, so an adapter already set up in the DAIKIN
107
+ Mobile Controller app still needs registering here.
108
+
109
+ ```ts
110
+ import { ClassicDaikinClient } from "daikin-local-control";
111
+
112
+ if (device.requiresRegistration && client instanceof ClassicDaikinClient) {
113
+ await client.registerTerminal("<KEY from the label>");
114
+
115
+ // To unregister later: this uuid (default), every other uuid, or all of them.
116
+ // await client.unregisterTerminal("others");
117
+ }
118
+ ```
119
+
120
+ Registration applies to classic adapters only; DSIOT adapters don't use it.
121
+
122
+ Some of these adapters also switch to HTTPS (`device.usesHttps`); see
123
+ [Local HTTPS](#local-https).
124
+
125
+ **Child lock code (when `device.requiresLocalPassword` is `true`).** The protocol
126
+ calls it the local password (`lpw`). It is the 4-digit code set under Child lock
127
+ in the DAIKIN Mobile Controller app. It is **not** printed on the label, and it
128
+ is not your Wi-Fi or Out-of-Home password. Pass it as `localPassword` to
129
+ `createDaikinClient`. If it's forgotten, the adapter has to be reset.
130
+
131
+ ### State and capabilities
132
+
133
+ `getState()` resolves with a `DaikinState`:
134
+
135
+ | Field | Holds |
136
+ | --- | --- |
137
+ | `power` | `true` or `false` |
138
+ | `mode` | `auto`, `dehumidify`, `cool`, `heat` or `fan` |
139
+ | `targetTemperatureInC` | A number, or `null` in a mode without a setpoint |
140
+ | `targetHumidity` | `off`, `autoLow`, `auto`, `autoHigh` or `continuous`, or `null` when the unit has no humidity setting. A Japanese unit set to a percentage reads as `{ percent: 50 }`; `getCapabilities()` never lists a percentage, so `setState()` sends one unchecked: see [UNSUPPORTED.md](UNSUPPORTED.md) |
141
+ | `fanSpeed` | `auto`, `silent`, or `level1` to `level5` |
142
+ | `swingMode` | `off`, `vertical`, `horizontal` or `both`: `vertical` moves the air up and down, `horizontal` left and right |
143
+ | `verticalPosition` | `stop`, `position1` to `position5`, `swing`, `naturalWind`, `auto` or `floorWarming`, or `null` where the unit reports none |
144
+ | `horizontalPosition` | `stop`, `position1` to `position5`, `swing` or `auto`, or `null` where the unit reports none |
145
+
146
+ `getCapabilities()` resolves with a `DaikinCapabilities`, which lists the
147
+ values this unit takes under the name of the state field each one limits:
148
+
149
+ | Field | Holds |
150
+ | --- | --- |
151
+ | `modes` | The modes the unit offers |
152
+ | `fanSpeeds`, `swingModes`, `verticalPositions`, `horizontalPositions` | The values the unit takes, empty when it has no such control |
153
+ | `targetTemperatureInC` | For each mode, `{ min, max, step }` in °C, or `null` where the mode has no setpoint |
154
+ | `targetHumidity` | For each mode, the humidity settings it takes, empty where it takes none |
155
+ | `specialOutlet`, `energyMonitoring`, `demandControl`, `scheduleTimer` | `true` when the unit has the feature |
156
+ | `model` | The adapter's own code for the unit's model |
157
+
158
+ `getSensors()` resolves with `indoorTemperatureInC`, `outdoorTemperatureInC`
159
+ and `indoorHumidityInPercent`, each `null` when the unit does not report it.
160
+
161
+ ### Changing state
162
+
163
+ Pass `setState()` only the fields to change: a field left out keeps the value
164
+ the unit holds for the target mode.
165
+
166
+ Check a value against `getCapabilities()` before sending it: `setState()`
167
+ resolves without an error when it drops a field, and sends any other value as
168
+ given, whether or not the unit lists it.
169
+
170
+ | Field | `setState()` drops it |
171
+ | --- | --- |
172
+ | `targetTemperatureInC` | In `fan` and `dehumidify` |
173
+ | `targetHumidity` | In `fan` |
174
+ | `fanSpeed` | In `dehumidify`, and on a DSIOT adapter in `cool` while a humidity setting is on: the fan runs on `auto` |
175
+ | `verticalPosition`, `horizontalPosition` | On a classic adapter, when the unit has no 3D airflow. On a DSIOT adapter, a fixed vertical position the unit lacks, and every fixed horizontal position |
176
+
177
+ ### Extended controls
178
+
179
+ Beyond core climate, `ClassicDaikinClient` exposes the adapter's other `set_*`
180
+ endpoints as typed methods, plus a raw `command()` hatch for anything without a
181
+ wrapper. Narrow to the concrete class to reach them:
182
+
183
+ ```ts
184
+ import { ClassicDaikinClient } from "daikin-local-control";
185
+
186
+ if (client instanceof ClassicDaikinClient) {
187
+ await client.setLed(false);
188
+ await client.setHolidayMode(true);
189
+ await client.setDemandControl({
190
+ enabled: true,
191
+ maxPowerInPercent: 70,
192
+ mode: "manual",
193
+ });
194
+ await client.setSpecialMode("powerful", true);
195
+ await client.setOutOfHomeAccess(false); // home network only
196
+ await client.setEnergyPrice(0.3); // per kWh
197
+ await client.setMetadata({
198
+ name: "Living Room",
199
+ group: { name: "Upstairs", enabled: true },
200
+ });
201
+ await client.reboot();
202
+
203
+ // Any other endpoint, by its path: the reply's key=value fields come back.
204
+ const basicInfo = await client.command("/common/basic_info");
205
+ }
206
+ ```
207
+
208
+ [UNSUPPORTED.md](UNSUPPORTED.md) lists the endpoints without a typed method and
209
+ what is not known about each.
210
+
211
+ `DsiotDaikinClient` has `reboot()` and the raw `multireqGet` / `multireqUpdate`
212
+ hatches for any `dgc_status` leaf.
213
+
214
+ ### Errors and retries
215
+
216
+ A client method that fails rejects with a `DaikinError`; branch on
217
+ `httpStatusCode`:
218
+
219
+ | `httpStatusCode` | Meaning |
220
+ | --- | --- |
221
+ | 401 | The adapter rejected the credentials: `ret=AUTH NG` or `LPW NG`, or DSIOT `rsc` 401 or 4001 |
222
+ | 502 | The adapter answered with an error: a `ret` or `rsc` other than OK, or a reply that does not parse |
223
+ | 503 | The adapter refused or dropped the connection (a `DaikinConnectionError`), or DSIOT answered `rsc` 503 |
224
+ | 504 | The request timed out, or the TLS handshake failed |
225
+ | Any other | The adapter's own HTTP status, such as 403 |
226
+
227
+ Two failures are not a `DaikinError`: `discoverDevices()` rejects with the
228
+ socket's error, and `multireqUpdate()` throws an `Error` for a path that names
229
+ a resource without a leaf, such as `dgc_status`.
230
+
231
+ Only a `DaikinConnectionError` is retried, up to five times by default.
232
+ [DESIGN.md](DESIGN.md#retries) says why.
233
+
234
+ | To | Pass |
235
+ | --- | --- |
236
+ | Change how often and how long a client retries | `retry: { delaysInMs: [0, 500] }` to `createDaikinClient`: one wait, in milliseconds, before each retry |
237
+ | Stop a client retrying | `retry: { delaysInMs: [] }` to `createDaikinClient` |
238
+ | Send one request that is not safe to repeat | `{ retry: false }` as the last argument of `command()`, `multireqGet()` or `multireqUpdate()` |
239
+
240
+ ## Coverage & limitations
241
+
242
+ It supports the same markets and Wi-Fi adapters as the DAIKIN Mobile Controller
243
+ app, so it is **not valid for every Daikin region or app**.
244
+
245
+ | Region | Markets using the DAIKIN Mobile Controller app |
246
+ | -------------- | -------------------------------------------------------------- |
247
+ | Southeast Asia | Thailand, Singapore, the Philippines |
248
+ | East Asia | Hong Kong, Taiwan |
249
+ | Oceania | Australia, New Zealand |
250
+
251
+ The app is published by Daikin Thailand and may be available in other countries
252
+ too; if your local Daikin site points you to it, this package should apply. Units
253
+ sold for other apps likely use a different protocol, e.g. Onecta / Daikin Online
254
+ Controller (Europe), GO DAIKIN (Malaysia) or Daikin Comfort Control / Daikin One
255
+ (North America).
256
+
257
+ This package is still young, so expect some rough edges. If something doesn't
258
+ work with your unit, please
259
+ [open an issue](https://github.com/cheziyi/daikin-local-control/issues).
260
+ [UNSUPPORTED.md](UNSUPPORTED.md) lists what isn't supported yet.
261
+
262
+ ## Local HTTPS
263
+
264
+ Only some classic adapters use HTTPS: those reporting `adp_kind=3` with
265
+ `en_secure=1`. Discovery sets `device.usesHttps` for them. Every other adapter,
266
+ DSIOT included, talks plain HTTP on the LAN, even when it requires registration.
267
+
268
+ Over HTTPS the adapter presents a certificate issued by Daikin's own CA, which
269
+ this package can't ship. So by default **the certificate is not verified**: the
270
+ connection is encrypted, but another device on your LAN could impersonate the
271
+ adapter. If you have the CA certificate, pass it to turn verification on:
272
+
273
+ ```ts
274
+ import { readFileSync } from "node:fs";
275
+
276
+ const client = createDaikinClient(device, {
277
+ terminalUuid: savedUuid,
278
+ tls: { ca: readFileSync("daikin-ca.pem") },
279
+ });
280
+ ```
281
+
282
+ With a CA, the certificate chain is verified but the hostname isn't, because the
283
+ adapter's certificate doesn't name its LAN address.
284
+
285
+ ## Development
286
+
287
+ Uses [pnpm](https://pnpm.io) and Node 22.19 or later (see `.nvmrc`; `fnm use` / `nvm use` picks it up).
288
+
289
+ ```bash
290
+ pnpm build # tsc -> dist
291
+ pnpm test # typecheck + vitest
292
+ pnpm check # biome lint + format + import sorting
293
+ pnpm fix # check, applying safe fixes
294
+ ```
295
+
296
+ `pnpm install` sets up a [lefthook](https://lefthook.dev) pre-commit hook that runs `pnpm fix` on staged files.
297
+
298
+ ## Legal disclaimer
299
+
300
+ An independent, community project for local interoperability and home automation.
301
+ It is not affiliated with, authorized, or endorsed by Daikin Industries, Ltd. All
302
+ product names and trademarks are the property of their respective owners; their use
303
+ here is for identification and compatibility only.
304
+
305
+ **Local-only.** The package talks only to the adapter over the LAN — it never contacts
306
+ any manufacturer cloud, remote server, or online API — and acts as an ordinary
307
+ client of the adapter's own endpoints.
308
+
309
+ **No proprietary material.** It ships no manufacturer keys, tokens, certificates, or
310
+ binaries. The protocol was documented independently, for interoperability.
311
+
312
+ Provided "as is", without warranty of any kind (see [LICENSE](LICENSE)); use it at
313
+ your own risk.
314
+
315
+ ## License
316
+
317
+ [GPL-3.0-or-later](LICENSE). Copyright © 2026 the contributors.
package/UNSUPPORTED.md ADDED
@@ -0,0 +1,43 @@
1
+ # Unsupported
2
+
3
+ Everything below is left out because its exact request or reply is not known.
4
+ To add one, pin down the request and reply first, then delete its row.
5
+
6
+ ## Left out
7
+
8
+ | Feature | What is unknown | Reach it with |
9
+ | --- | --- | --- |
10
+ | DSIOT special air outlet | Which leaf reports it. `getCapabilities` reports `specialOutlet: false`. | `DsiotDaikinClient.multireqGet()` |
11
+ | Classic `/common/set_location` | The parameter name. | `ClassicDaikinClient.command()` |
12
+ | DSIOT `/config/*` endpoints: time zone, date and time, network settings, Wi-Fi scan, firmware update | The request bodies. | Not reachable |
13
+ | DSIOT fixed horizontal louver positions | Which position each value 1-14 stands for. `getState` reports `stop` for any of them, and a control that sets `stop` keeps the position. | `DsiotDaikinClient.multireqUpdate()` |
14
+ | DSIOT dehumidify setpoint write | Whether `p_1E` accepts a write; it is treated as read-only, so `setState` drops a `targetTemperatureInC` in dehumidify. | `DsiotDaikinClient.multireqUpdate()` |
15
+ | Classic `shum_asean`, the humidity setting of a `subtype=qa` unit | What each value stands for. `setState` sends such a unit no humidity setting, and `getCapabilities` lists none. | `ClassicDaikinClient.command()` |
16
+ | Classic vertical louver value `f_dir_ud=C` | What it stands for. `getState` reports `null`. | `ClassicDaikinClient.command()` |
17
+ | Classic schedule demand mode (`mode=1`) | The full format of the weekly table it needs (`moc`..`suc`, `<day><n>_en`/`_t`/`_p`). | `ClassicDaikinClient.command()` |
18
+ | Classic special mode `spmode_kind=4` (heat plus) | Which units take it. | `ClassicDaikinClient.command()` |
19
+ | Timers, weekly programs, schedule timers (`/aircon/get_timer`, `set_timer`, `get_program`, `set_program`, `get_scdltimer_info`, `get_scdltimer_body`, `set_scdltimer_info`, `set_scdltimer_body`) | Their parameters. | `ClassicDaikinClient.command()` |
20
+ | Classic setting reads: `/aircon/get_demand_control`, `/aircon/get_price`, `/aircon/get_target`, `/common/get_holiday` | The reply fields. | `ClassicDaikinClient.command()` |
21
+ | Classic notifications, clock and account: `/common/get_notify`, `set_notify`, `set_push_notice`, `set_timezone`, `notify_date_time`, `set_account` | Their parameters. | `ClassicDaikinClient.command()` |
22
+ | Classic network: `/common/get_network_setting`, `set_network_setting`, `start_wifi_scan`, `get_wifi_scan_result`, `start_wifi_connection`, `permit_wifi_connection`, `set_lpw` | Their parameters. | `ClassicDaikinClient.command()` |
23
+ | Classic firmware update: a POST to `/dkac/system/fwupdate` | The format of the upload. | Not reachable |
24
+
25
+ ## Not modelled
26
+
27
+ These values are known, but the package's types don't represent them yet:
28
+
29
+ | Value | What `getState` reports instead |
30
+ | --- | --- |
31
+ | Humidify mode (classic `HUM`, DSIOT `0800`) and laundry mode (classic `LND`) | `mode: "auto"`. `setState` keeps the mode unless the control sets one, and on a DSIOT adapter writes power alone while it is kept. |
32
+ | DSIOT vertical louver values other than stop, the first five fixed positions, swing, auto, natural wind and floor warming | `verticalPosition: null` |
33
+ | DSIOT laundry mode (`dgc_status.e_1002.e_3003.p_25` = `01`) | The mode in `dgc_status.e_1002.e_3001.p_01` |
34
+ | Classic `f_dir=6` (heat with the special air outlet) | `swingMode: "off"` |
35
+ | Japanese units (`reg=jp`), which follow separate rules for limits, humidity and fan, and take a humidity percentage | The rules for other regions |
36
+
37
+ These adapter codes pass through a model unchanged, because what each one
38
+ stands for is not known:
39
+
40
+ | Model field | Holds |
41
+ | --- | --- |
42
+ | `DeviceMetadata.icon` | The `icon` that classic `/common/set_icon` takes |
43
+ | `DaikinCapabilities.model` | The classic `model` field, or DSIOT `dgc_status.e_1002.e_A001.p_0D`, such as `0ABB` |
@@ -0,0 +1,24 @@
1
+ /** `DaikinTransport.send` calls both methods on every request and adds what
2
+ * they return. */
3
+ export interface DaikinAuthentication {
4
+ headers(): Record<string, string> | Promise<Record<string, string>>;
5
+ /** Replaces a request parameter of the same name. */
6
+ query(): Record<string, string> | Promise<Record<string, string>>;
7
+ }
8
+ /** Classic adapter that requires the local password: sent as
9
+ * `?lpw=<value>`. */
10
+ export declare class ClassicLocalPasswordAuthentication implements DaikinAuthentication {
11
+ private readonly localPassword;
12
+ constructor(localPassword: string);
13
+ headers(): Record<string, string>;
14
+ query(): Record<string, string>;
15
+ }
16
+ /** DSIOT adapter that requires the local password: sent as
17
+ * `Authorization: Basic <lpw>`. */
18
+ export declare class DsiotLocalPasswordAuthentication implements DaikinAuthentication {
19
+ private readonly localPassword;
20
+ constructor(localPassword: string);
21
+ headers(): Record<string, string>;
22
+ query(): Record<string, string>;
23
+ }
24
+ //# sourceMappingURL=authentication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../src/authentication.ts"],"names":[],"mappings":"AAgBA;kBACkB;AAClB,MAAM,WAAW,oBAAoB;IACnC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE,qDAAqD;IACrD,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnE;AAED;qBACqB;AACrB,qBAAa,kCACX,YAAW,oBAAoB;IAEnB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA1C,YAA6B,aAAa,EAAE,MAAM,EAAI;IAEtD,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEhC;IAED,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE9B;CACF;AAED;mCACmC;AACnC,qBAAa,gCAAiC,YAAW,oBAAoB;IAC/D,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA1C,YAA6B,aAAa,EAAE,MAAM,EAAI;IAEtD,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEhC;IAED,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE9B;CACF"}
@@ -0,0 +1,30 @@
1
+ import { encodeClassicLocalPassword, encodeDsiotLocalPassword, } from "./codecs/credentials.js";
2
+ /** Classic adapter that requires the local password: sent as
3
+ * `?lpw=<value>`. */
4
+ export class ClassicLocalPasswordAuthentication {
5
+ localPassword;
6
+ constructor(localPassword) {
7
+ this.localPassword = localPassword;
8
+ }
9
+ headers() {
10
+ return {};
11
+ }
12
+ query() {
13
+ return encodeClassicLocalPassword(this.localPassword);
14
+ }
15
+ }
16
+ /** DSIOT adapter that requires the local password: sent as
17
+ * `Authorization: Basic <lpw>`. */
18
+ export class DsiotLocalPasswordAuthentication {
19
+ localPassword;
20
+ constructor(localPassword) {
21
+ this.localPassword = localPassword;
22
+ }
23
+ headers() {
24
+ return encodeDsiotLocalPassword(this.localPassword);
25
+ }
26
+ query() {
27
+ return {};
28
+ }
29
+ }
30
+ //# sourceMappingURL=authentication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication.js","sourceRoot":"","sources":["../src/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AAqBjC;qBACqB;AACrB,MAAM,OAAO,kCAAkC;IAGhB,aAAa;IAA1C,YAA6B,aAAqB;6BAArB,aAAa;IAAW,CAAC;IAEtD,OAAO;QACL,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK;QACH,OAAO,0BAA0B,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACxD,CAAC;CACF;AAED;mCACmC;AACnC,MAAM,OAAO,gCAAgC;IACd,aAAa;IAA1C,YAA6B,aAAqB;6BAArB,aAAa;IAAW,CAAC;IAEtD,OAAO;QACL,OAAO,wBAAwB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACtD,CAAC;IAED,KAAK;QACH,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
@@ -0,0 +1,53 @@
1
+ import type { DaikinCapabilities } from "../models/capabilities.js";
2
+ import type { DemandControl, DeviceMetadata, SpecialMode, TerminalScope } from "../models/extendedControl.js";
3
+ import type { DaikinControl, DaikinSensors, DaikinState } from "../models/state.js";
4
+ import { type DaikinRequestOptions, type DaikinRetryOptions, type DaikinTransport } from "../transport.js";
5
+ import type { DaikinClient } from "./client.js";
6
+ /** Classic CGI adapter (BRP069-style). Reads and writes are HTTP GET with query
7
+ * parameters; replies are `key=value,key=value`. */
8
+ export declare class ClassicDaikinClient implements DaikinClient {
9
+ private readonly transport;
10
+ private readonly retryOptions?;
11
+ private capabilityFields;
12
+ /** `close` closes `transport`, so give each client a transport of its own. */
13
+ constructor(transport: DaikinTransport, retryOptions?: DaikinRetryOptions | undefined);
14
+ close(): Promise<void>;
15
+ getCapabilities(): Promise<DaikinCapabilities>;
16
+ getState(): Promise<DaikinState>;
17
+ setState(control: DaikinControl): Promise<void>;
18
+ getSensors(): Promise<DaikinSensors>;
19
+ /** Adapter status LED on/off (`set_led`). */
20
+ setLed(on: boolean): Promise<void>;
21
+ /** Holiday / away mode (`set_holiday`). */
22
+ setHolidayMode(enabled: boolean): Promise<void>;
23
+ /** Demand control — cap the unit's power draw (`set_demand_control`). */
24
+ setDemandControl(demand: DemandControl): Promise<void>;
25
+ /** Powerful, econo or streamer (`set_special_mode`). */
26
+ setSpecialMode(mode: SpecialMode, enabled: boolean): Promise<void>;
27
+ /** Whether the unit can be operated from outside the home network
28
+ * (`set_remote_method`). */
29
+ setOutOfHomeAccess(enabled: boolean): Promise<void>;
30
+ /** Electricity price per kWh for the consumption estimates (`set_price`),
31
+ * to two decimal places. */
32
+ setEnergyPrice(price: number): Promise<void>;
33
+ /** Energy target for the "target" feature (`set_target`). */
34
+ setEnergyTarget(target: number): Promise<void>;
35
+ /** Name / icon / group metadata (`set_name` etc.). */
36
+ setMetadata(metadata: DeviceMetadata): Promise<void>;
37
+ /** Register this client's uuid with an adapter whose
38
+ * `requiresRegistration` is true, using the KEY printed on the adapter's
39
+ * label (`register_terminal`). Once per uuid; the adapter remembers it. */
40
+ registerTerminal(key: string): Promise<void>;
41
+ /** Unregister terminals (`unregister_terminal`): this uuid, every other
42
+ * uuid, or all of them. */
43
+ unregisterTerminal(scope?: TerminalScope): Promise<void>;
44
+ /** Reboot the adapter (`reboot`). */
45
+ reboot(): Promise<void>;
46
+ /** Calls any classic CGI endpoint with query parameters and returns the reply's
47
+ * `key=value` fields. It reaches the controls without a typed method:
48
+ * timers, schedules, programs, network, credentials and firmware. It throws
49
+ * a `DaikinError` when the reply's `ret` is not `OK`. */
50
+ command(path: string, parameters?: Record<string, string>, { retry }?: DaikinRequestOptions): Promise<Record<string, string>>;
51
+ private readCapabilityFields;
52
+ }
53
+ //# sourceMappingURL=classic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classic.d.ts","sourceRoot":"","sources":["../../src/clients/classic.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACd,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAErB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA0BhD;oDACoD;AACpD,qBAAa,mBAAoB,YAAW,YAAY;IAOpD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IALhC,OAAO,CAAC,gBAAgB,CAA8C;IAEtE,8EAA8E;IAC9E,YACmB,SAAS,EAAE,eAAe,EAC1B,YAAY,CAAC,EAAE,kBAAkB,YAAA,EAChD;IAEJ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;IAEK,eAAe,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAEnD;IAEK,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC,CAErC;IAEK,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpD;IAEK,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC,CAIzC;IAKD,6CAA6C;IACvC,MAAM,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvC;IAED,2CAA2C;IACrC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpD;IAED,yEAAyE;IACnE,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3D;IAED,wDAAwD;IAClD,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvE;IAED;gCAC4B;IACtB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxD;IAED;gCAC4B;IACtB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjD;IAED,6DAA6D;IACvD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnD;IAED,sDAAsD;IAChD,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBzD;IAED;;+EAE2E;IACrE,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjD;IAED;+BAC2B;IACrB,kBAAkB,CAAC,KAAK,GAAE,aAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKrE;IAED,qCAAqC;IAC/B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAE5B;IAED;;;6DAGyD;IACnD,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,EAAE,KAAY,EAAE,GAAE,oBAAyB,GAC1C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAYjC;IAED,OAAO,CAAC,oBAAoB;CAY7B"}
@@ -0,0 +1,142 @@
1
+ import { mergeClassicCapabilityFields, parseClassicCapabilities, } from "../codecs/classic/capabilities.js";
2
+ import { applyClassicOutOfHomeAccess, encodeClassicDemandControl, encodeClassicEnergyPrice, encodeClassicEnergyTarget, encodeClassicGroup, encodeClassicHolidayMode, encodeClassicIcon, encodeClassicLed, encodeClassicName, encodeClassicSpecialMode, encodeClassicTerminalKey, encodeClassicTerminalScope, } from "../codecs/classic/extendedControl.js";
3
+ import { assertClassicOk, parseClassicKeyValue, } from "../codecs/classic/keyValue.js";
4
+ import { applyClassicControl, parseClassicSensors, parseClassicState, } from "../codecs/classic/state.js";
5
+ import { withRetry, } from "../transport.js";
6
+ // Classic CGI endpoints (BRP069-style). Reads and writes are both HTTP GET with
7
+ // query parameters; the reply is `key=value,key=value`.
8
+ const CLASSIC_ROUTES = {
9
+ basicInfo: "/common/basic_info",
10
+ getModelInfo: "/aircon/get_model_info",
11
+ getControlInfo: "/aircon/get_control_info",
12
+ setControlInfo: "/aircon/set_control_info",
13
+ getSensorInfo: "/aircon/get_sensor_info",
14
+ setSpecialMode: "/aircon/set_special_mode",
15
+ setDemandControl: "/aircon/set_demand_control",
16
+ setPrice: "/aircon/set_price",
17
+ setTarget: "/aircon/set_target",
18
+ setLed: "/common/set_led",
19
+ setHoliday: "/common/set_holiday",
20
+ setName: "/common/set_name",
21
+ setIcon: "/common/set_icon",
22
+ setGroup: "/common/set_group",
23
+ getRemoteMethod: "/common/get_remote_method",
24
+ setRemoteMethod: "/common/set_remote_method",
25
+ reboot: "/common/reboot",
26
+ registerTerminal: "/common/register_terminal",
27
+ unregisterTerminal: "/common/unregister_terminal",
28
+ };
29
+ /** Classic CGI adapter (BRP069-style). Reads and writes are HTTP GET with query
30
+ * parameters; replies are `key=value,key=value`. */
31
+ export class ClassicDaikinClient {
32
+ transport;
33
+ retryOptions;
34
+ // A unit's capabilities don't change, and `setState` needs them, so they are
35
+ // read once.
36
+ capabilityFields;
37
+ /** `close` closes `transport`, so give each client a transport of its own. */
38
+ constructor(transport, retryOptions) {
39
+ this.transport = transport;
40
+ this.retryOptions = retryOptions;
41
+ }
42
+ close() {
43
+ return this.transport.close();
44
+ }
45
+ async getCapabilities() {
46
+ return parseClassicCapabilities(await this.readCapabilityFields());
47
+ }
48
+ async getState() {
49
+ return parseClassicState(await this.command(CLASSIC_ROUTES.getControlInfo));
50
+ }
51
+ async setState(control) {
52
+ const capabilityFields = await this.readCapabilityFields();
53
+ const current = await this.command(CLASSIC_ROUTES.getControlInfo);
54
+ await this.command(CLASSIC_ROUTES.setControlInfo, applyClassicControl(current, control, capabilityFields));
55
+ }
56
+ async getSensors() {
57
+ return parseClassicSensors(await this.command(CLASSIC_ROUTES.getSensorInfo));
58
+ }
59
+ // --- extended controls ---------------------------------------------------
60
+ // Each maps to its own endpoint (not control_info).
61
+ /** Adapter status LED on/off (`set_led`). */
62
+ async setLed(on) {
63
+ await this.command(CLASSIC_ROUTES.setLed, encodeClassicLed(on));
64
+ }
65
+ /** Holiday / away mode (`set_holiday`). */
66
+ async setHolidayMode(enabled) {
67
+ await this.command(CLASSIC_ROUTES.setHoliday, encodeClassicHolidayMode(enabled));
68
+ }
69
+ /** Demand control — cap the unit's power draw (`set_demand_control`). */
70
+ async setDemandControl(demand) {
71
+ await this.command(CLASSIC_ROUTES.setDemandControl, encodeClassicDemandControl(demand));
72
+ }
73
+ /** Powerful, econo or streamer (`set_special_mode`). */
74
+ async setSpecialMode(mode, enabled) {
75
+ await this.command(CLASSIC_ROUTES.setSpecialMode, encodeClassicSpecialMode(mode, enabled));
76
+ }
77
+ /** Whether the unit can be operated from outside the home network
78
+ * (`set_remote_method`). */
79
+ async setOutOfHomeAccess(enabled) {
80
+ const current = await this.command(CLASSIC_ROUTES.getRemoteMethod);
81
+ await this.command(CLASSIC_ROUTES.setRemoteMethod, applyClassicOutOfHomeAccess(current, enabled));
82
+ }
83
+ /** Electricity price per kWh for the consumption estimates (`set_price`),
84
+ * to two decimal places. */
85
+ async setEnergyPrice(price) {
86
+ await this.command(CLASSIC_ROUTES.setPrice, encodeClassicEnergyPrice(price));
87
+ }
88
+ /** Energy target for the "target" feature (`set_target`). */
89
+ async setEnergyTarget(target) {
90
+ await this.command(CLASSIC_ROUTES.setTarget, encodeClassicEnergyTarget(target));
91
+ }
92
+ /** Name / icon / group metadata (`set_name` etc.). */
93
+ async setMetadata(metadata) {
94
+ if (metadata.name !== undefined) {
95
+ await this.command(CLASSIC_ROUTES.setName, encodeClassicName(metadata.name));
96
+ }
97
+ if (metadata.icon !== undefined) {
98
+ await this.command(CLASSIC_ROUTES.setIcon, encodeClassicIcon(metadata.icon));
99
+ }
100
+ if (metadata.group !== undefined) {
101
+ await this.command(CLASSIC_ROUTES.setGroup, encodeClassicGroup(metadata.group));
102
+ }
103
+ }
104
+ /** Register this client's uuid with an adapter whose
105
+ * `requiresRegistration` is true, using the KEY printed on the adapter's
106
+ * label (`register_terminal`). Once per uuid; the adapter remembers it. */
107
+ async registerTerminal(key) {
108
+ await this.command(CLASSIC_ROUTES.registerTerminal, encodeClassicTerminalKey(key));
109
+ }
110
+ /** Unregister terminals (`unregister_terminal`): this uuid, every other
111
+ * uuid, or all of them. */
112
+ async unregisterTerminal(scope = "self") {
113
+ await this.command(CLASSIC_ROUTES.unregisterTerminal, encodeClassicTerminalScope(scope));
114
+ }
115
+ /** Reboot the adapter (`reboot`). */
116
+ async reboot() {
117
+ await this.command(CLASSIC_ROUTES.reboot, undefined, { retry: false });
118
+ }
119
+ /** Calls any classic CGI endpoint with query parameters and returns the reply's
120
+ * `key=value` fields. It reaches the controls without a typed method:
121
+ * timers, schedules, programs, network, credentials and firmware. It throws
122
+ * a `DaikinError` when the reply's `ret` is not `OK`. */
123
+ async command(path, parameters, { retry = true } = {}) {
124
+ const send = () => this.transport.send({
125
+ method: "GET",
126
+ path,
127
+ ...(parameters ? { query: parameters } : {}),
128
+ });
129
+ const fields = parseClassicKeyValue(await (retry ? withRetry(send, this.retryOptions) : send()));
130
+ assertClassicOk(fields);
131
+ return fields;
132
+ }
133
+ readCapabilityFields() {
134
+ this.capabilityFields ??= (async () => mergeClassicCapabilityFields(await this.command(CLASSIC_ROUTES.basicInfo), await this.command(CLASSIC_ROUTES.getModelInfo)))();
135
+ // A failed read is not cached, so the next call tries again.
136
+ this.capabilityFields.catch(() => {
137
+ this.capabilityFields = undefined;
138
+ });
139
+ return this.capabilityFields;
140
+ }
141
+ }
142
+ //# sourceMappingURL=classic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classic.js","sourceRoot":"","sources":["../../src/clients/classic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { DaikinCapabilities } from "../models/capabilities.js";
2
+ import type { DaikinControl, DaikinSensors, DaikinState } from "../models/state.js";
3
+ /** The controls every adapter offers, whatever its wire protocol or
4
+ * authentication. `createDaikinClient` picks the implementation, so a caller
5
+ * needs the concrete class only for what one protocol adds, such as
6
+ * `ClassicDaikinClient.registerTerminal`. Every method rejects with a
7
+ * `DaikinError` when its request fails. */
8
+ export interface DaikinClient {
9
+ /** The unit's supported features. */
10
+ getCapabilities(): Promise<DaikinCapabilities>;
11
+ /** Current controllable state. */
12
+ getState(): Promise<DaikinState>;
13
+ /** Change part of the state. The client reads the current state, overlays the
14
+ * control, and writes the whole thing back. */
15
+ setState(control: DaikinControl): Promise<void>;
16
+ /** Read-only temperature / humidity readings. */
17
+ getSensors(): Promise<DaikinSensors>;
18
+ /** Close the connection to the adapter. */
19
+ close(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=client.d.ts.map