daikin-airbase 0.1.6 → 0.3.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/dist/index.cjs CHANGED
@@ -30,7 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- API: () => API,
33
+ Client: () => Client,
34
34
  ControlMode: () => ControlMode,
35
35
  DaikinClient: () => DaikinClient,
36
36
  FanSpeed: () => FanSpeed,
@@ -39,15 +39,26 @@ __export(index_exports, {
39
39
  Zones: () => Zones,
40
40
  basic_info: () => basic_info,
41
41
  discover: () => discover,
42
- get_zone_settings: () => get_zone_settings
42
+ getNetwork_setting: () => getNetwork_setting,
43
+ get_control_info: () => get_control_info,
44
+ get_datetime: () => get_datetime,
45
+ get_dealer_info: () => get_dealer_info,
46
+ get_model_info: () => get_model_info,
47
+ get_quick_timer: () => get_quick_timer,
48
+ get_sensor_info: () => get_sensor_info,
49
+ get_wifi_setting: () => get_wifi_setting,
50
+ get_zone_settings: () => get_zone_settings,
51
+ set_control_info: () => set_control_info,
52
+ set_zone_setting: () => set_zone_setting
43
53
  });
44
54
  module.exports = __toCommonJS(index_exports);
45
55
 
46
56
  // src/ParamParser.ts
47
- function parse(str) {
57
+ var int = (v) => Math.round(v);
58
+ var bool = (v) => v ? 1 : 0;
59
+ function deserialize(data) {
48
60
  const result = {};
49
- const parts = str.split(",");
50
- for (const part of parts) {
61
+ for (const part of data) {
51
62
  const [key, value] = part.split("=");
52
63
  if (key === void 0) continue;
53
64
  const maybeNumber = Number(value);
@@ -59,10 +70,20 @@ function parse(str) {
59
70
  }
60
71
  return result;
61
72
  }
73
+ function serialize(parameters) {
74
+ const queries = [];
75
+ for (const key in parameters) {
76
+ const value = parameters[key];
77
+ if (value === void 0) continue;
78
+ const encoded = encodeURIComponent(typeof value === "string" ? value : value !== null ? `${int(value)}` : "");
79
+ queries.push(`${key}=${encoded}`);
80
+ }
81
+ return queries;
82
+ }
62
83
 
63
- // src/api/API.ts
84
+ // src/api/Client.ts
64
85
  var import_node_http = __toESM(require("http"));
65
- var API = class {
86
+ var Client = class {
66
87
  host;
67
88
  constructor(host) {
68
89
  this.host = host;
@@ -70,16 +91,8 @@ var API = class {
70
91
  async request(endpoint, parameters) {
71
92
  const body = await new Promise((resolve, reject) => {
72
93
  const url = new URL(`/skyfi${endpoint}`, `http://${this.host}/`);
73
- const queries = [];
74
- if (parameters) {
75
- for (const key in parameters) {
76
- const value = parameters[key];
77
- if (value === void 0) continue;
78
- const encoded = encodeURIComponent(`${value ?? ""}`);
79
- queries.push(`${key}=${encoded}`);
80
- }
81
- }
82
- const search = queries.length > 0 ? `?${queries.join("&")}` : "";
94
+ const serialized = parameters ? serialize(parameters) : [];
95
+ const search = serialized.length > 0 ? `?${serialized.join("&")}` : "";
83
96
  const req = import_node_http.default.request(
84
97
  {
85
98
  host: url.hostname,
@@ -102,7 +115,7 @@ var API = class {
102
115
  req.on("error", reject);
103
116
  req.end();
104
117
  });
105
- return parse(body);
118
+ return deserialize(body.split(","));
106
119
  }
107
120
  };
108
121
 
@@ -125,7 +138,7 @@ function discover(timeoutMs = 3e3) {
125
138
  });
126
139
  socket.on("message", (msg, rinfo) => {
127
140
  const raw = msg.toString("utf8");
128
- const info = parse(raw);
141
+ const info = deserialize(raw.split(","));
129
142
  const key = `${info.mac || info.id || rinfo.address}`;
130
143
  availableUnits.set(key, {
131
144
  discoveredAddress: rinfo.address,
@@ -152,15 +165,42 @@ function discover(timeoutMs = 3e3) {
152
165
  });
153
166
  }
154
167
 
155
- // src/api/common/basic_info.ts
156
- var basic_info = (http2) => http2.request("/common/basic_info");
168
+ // src/api/aircon/get_control_info.ts
169
+ var get_control_info = (http2) => http2.request("/aircon/get_control_info");
170
+
171
+ // src/api/aircon/get_model_info.ts
172
+ var get_model_info = (http2) => http2.request("/aircon/get_model_info");
173
+
174
+ // src/api/aircon/get_quick_timer.ts
175
+ var get_quick_timer = (http2) => http2.request("/aircon/get_quick_timer");
176
+
177
+ // src/api/aircon/get_sensor_info.ts
178
+ var get_sensor_info = (http2) => http2.request("/aircon/get_sensor_info");
157
179
 
158
180
  // src/api/aircon/get_zone_setting.ts
159
181
  var get_zone_settings = (http2) => http2.request("/aircon/get_zone_setting");
160
182
 
183
+ // src/api/aircon/set_control_info.ts
184
+ var set_control_info = (http2, params) => http2.request("/aircon/set_control_info", params);
185
+
161
186
  // src/api/aircon/set_zone_setting.ts
162
187
  var set_zone_setting = (http2, params) => http2.request("/aircon/set_zone_setting", params);
163
188
 
189
+ // src/api/common/basic_info.ts
190
+ var basic_info = (http2) => http2.request("/common/basic_info");
191
+
192
+ // src/api/common/get_datetime.ts
193
+ var get_datetime = (http2) => http2.request("/common/get_datetime");
194
+
195
+ // src/api/common/get_dealer_info.ts
196
+ var get_dealer_info = (http2) => http2.request("/common/get_dealer_info");
197
+
198
+ // src/api/common/get_network_setting.ts
199
+ var getNetwork_setting = (http2) => http2.request("/common/get_network_setting");
200
+
201
+ // src/api/common/get_wifi_setting.ts
202
+ var get_wifi_setting = (http2) => http2.request("/common/get_wifi_setting");
203
+
164
204
  // src/Zones.ts
165
205
  var Zones = class {
166
206
  zones = [];
@@ -168,69 +208,105 @@ var Zones = class {
168
208
  constructor(client) {
169
209
  this.client = client;
170
210
  }
171
- /** Refreshes the cached zone state */
172
- async refresh() {
211
+ /**
212
+ * Requests all the zones current state from the airbase controller.
213
+ * Results are cached.
214
+ * @returns the current state of all zones.
215
+ */
216
+ async getZones() {
173
217
  const response = await get_zone_settings(this.client.api);
174
- const zoneNames = response.zone_name.split(";");
175
- const zoneState = response.zone_onoff.split(";");
176
- return this.zones = zoneNames.map((name, index) => ({
218
+ const names = response.zone_name.split(";");
219
+ const onoff = response.zone_onoff.split(";");
220
+ const temprartures = response.lztemp_c ? response.lztemp_c.split(";") : null;
221
+ const humidities = response.lztemp_h ? response.lztemp_h.split(";") : null;
222
+ return this.zones = names.map((name, index) => Object.freeze({
223
+ index,
177
224
  name,
178
- state: zoneState[index] === "1"
225
+ isOn: onoff[index] === "1",
226
+ humidity: humidities ? +`${humidities[index]}` : void 0,
227
+ temperature: temprartures ? +`${temprartures[index]}` : void 0
179
228
  }));
180
229
  }
181
230
  /**
182
231
  * Gets the state of all cached zones.
183
232
  */
184
233
  getCachedZones() {
185
- return [...this.zones];
234
+ return this.zones;
186
235
  }
187
236
  /**
188
- * Gets all zones' state
237
+ * Gets the current state of a zone with the matching name.
238
+ * @param name
189
239
  */
190
- async getZones() {
191
- return await this.refresh();
240
+ async getZone(name) {
241
+ return await this.getZones().then(() => this.getCachedZone(name));
242
+ }
243
+ /**
244
+ * Gets the current state of a zone at the given controller index.
245
+ * @param index
246
+ * @throws Error if the index is out of bounds.
247
+ */
248
+ async getZoneAt(index) {
249
+ return await this.getZones().then(() => this.getCachedZoneAt(index));
192
250
  }
193
251
  /**
194
252
  * Gets the cached state of a zone
195
253
  * @param name
196
254
  */
197
255
  getCachedZone(name) {
198
- return this.zones.find((zone) => zone.name === name)?.state;
256
+ return this.zones.find((zone) => zone.name === name);
199
257
  }
200
258
  /**
201
- * Gets the state of a Zone
202
- * @param name
259
+ * Gets the cached state of a zone at the given controller index.
260
+ * @param index
261
+ * @throws Error if the index is out of bounds.
203
262
  */
204
- async getZone(name) {
205
- const zones = await this.refresh();
206
- return zones.find((zone) => zone.name === name)?.state;
263
+ getCachedZoneAt(index) {
264
+ if (index < 0 || index >= this.zones.length || this.zones[index] === void 0)
265
+ throw new Error(`Zone index ${index} is out of bounds`);
266
+ if (this.zones[index].index !== index)
267
+ throw new Error(`Zone at ${index} does not believe it is in the correct spot.`);
268
+ return this.zones[index];
207
269
  }
208
270
  /**
209
- * Sets a Zone's state
210
- * @param name
211
- * @param state
271
+ * Updates a zone
272
+ * @param name the name of the zone to update
273
+ * @param info the new data to replace
212
274
  */
213
- async setZone(name, state) {
214
- await this.setZones([{ name, state }]);
275
+ async updateZone(name, info) {
276
+ const zones = await this.getZones();
277
+ const index = zones.findIndex((zone) => zone.name === name);
278
+ zones[index] = { ...zones[index], ...info };
279
+ await this.setZones(zones);
280
+ return zones[index];
215
281
  }
216
282
  /**
217
- * Sets multiple zones' state
218
- * @param changes
283
+ * Bulk updates all the zones
284
+ * @param zones the new zones. These are sorted by index and pushed into the API in that order.
285
+ * @returns the updated zones
219
286
  */
220
- async setZones(changes) {
221
- await this.refresh();
222
- for (const change of changes) {
223
- const existingZone = this.zones.find((z) => z.name === change.name);
224
- if (existingZone) {
225
- existingZone.state = change.state;
226
- }
287
+ async setZones(zones) {
288
+ let names = [];
289
+ let onoff = [];
290
+ let humidity = [];
291
+ let temperature = [];
292
+ const sortedZones = zones.sort((a, b) => a.index - b.index);
293
+ for (const zone of sortedZones) {
294
+ names.push(zone.name);
295
+ onoff.push(`${bool(zone.isOn)}`);
296
+ if (zone.humidity !== void 0)
297
+ humidity.push(`${int(zone.humidity)}`);
298
+ if (zone.temperature !== void 0)
299
+ temperature.push(`${int(zone.temperature)}`);
227
300
  }
228
301
  const response = await set_zone_setting(this.client.api, {
229
- zone_name: this.zones.map((zone) => zone.name).join(";"),
230
- zone_onoff: this.zones.map((zone) => zone.state ? "1" : "0").join(";")
302
+ zone_name: names.join(";"),
303
+ zone_onoff: onoff.join(";"),
304
+ lztemp_h: humidity.length > 0 ? humidity.join(";") : void 0,
305
+ lztemp_c: temperature.length > 0 ? temperature.join(";") : void 0
231
306
  });
232
307
  if (response.ret !== "OK")
233
308
  throw new Error(`Failed to set zones: ${response.ret}`);
309
+ return this.zones = zones.map((zone) => Object.freeze({ ...zone }));
234
310
  }
235
311
  };
236
312
 
@@ -262,27 +338,26 @@ var RemoteType = /* @__PURE__ */ ((RemoteType3) => {
262
338
  return RemoteType3;
263
339
  })(RemoteType || {});
264
340
 
265
- // src/api/aircon/get_control_info.ts
266
- var get_control_info = (http2) => http2.request("/aircon/get_control_info");
267
-
268
- // src/api/aircon/set_control_info.ts
269
- var set_control_info = (http2, params) => http2.request("/aircon/set_control_info", params);
270
-
271
341
  // src/DaikinClient.ts
272
342
  var DaikinClient = class {
273
343
  options;
274
344
  api;
275
345
  zones;
276
346
  cachedControlInfo;
347
+ cachedSensorInfo;
277
348
  constructor(options) {
278
349
  this.options = options;
279
350
  if ("host" in options) {
280
- this.api = new API(options.host);
351
+ this.api = new Client(options.host);
281
352
  } else {
282
- this.api = new API(options.device.discoveredAddress);
353
+ this.api = new Client(options.device.discoveredAddress);
283
354
  }
284
355
  this.zones = new Zones(this);
285
356
  }
357
+ /**
358
+ * Gets the current state of the aircon.
359
+ * @returns the current state of the aircon.
360
+ */
286
361
  async getControlInfo() {
287
362
  const response = await get_control_info(this.api);
288
363
  const responseValues = Object.entries(response);
@@ -292,7 +367,7 @@ var DaikinClient = class {
292
367
  fanAuto: response.f_auto != 0,
293
368
  fanAirside: response.f_airside != 0,
294
369
  targetTemperature: response.stemp,
295
- sensorTemperatures: responseValues.filter(([k, v]) => k.startsWith("dt")).map(([k, v]) => +v),
370
+ controlTemperature: responseValues.filter(([k, v]) => k.startsWith("dt")).map(([k, v]) => +v),
296
371
  power: response.pow != 0,
297
372
  status: Number(response.operate),
298
373
  swinging: response.f_dir != 0,
@@ -301,21 +376,40 @@ var DaikinClient = class {
301
376
  remoteControlType: Number(response.remo)
302
377
  };
303
378
  }
379
+ /** Gets the cached state of the aircon. */
304
380
  getCachedControlInfo() {
305
381
  return this.cachedControlInfo;
306
382
  }
383
+ /**
384
+ * Retrieves the power state of the unit.
385
+ */
307
386
  async getPowered() {
308
387
  return (await this.getControlInfo()).power;
309
388
  }
389
+ /**
390
+ * Sets the powered state of the unit.
391
+ * @param state true to turn the unit on.
392
+ */
310
393
  async setPowered(state) {
311
394
  return await this.setControlInfo({ power: state });
312
395
  }
396
+ /**
397
+ * Gets the current mode of the unit.
398
+ */
313
399
  async getMode() {
314
400
  return (await this.getControlInfo()).mode;
315
401
  }
402
+ /**
403
+ * Sets the mode of the unit.
404
+ * @param mode The mode to set
405
+ */
316
406
  async setMode(mode) {
317
407
  return await this.setControlInfo({ mode });
318
408
  }
409
+ /**
410
+ * Gets the current fan settings of the unit.
411
+ * @returns the current fan settings.
412
+ */
319
413
  async getFan() {
320
414
  const info = await this.getControlInfo();
321
415
  return {
@@ -324,26 +418,34 @@ var DaikinClient = class {
324
418
  airside: info.fanAirside
325
419
  };
326
420
  }
421
+ /**
422
+ * Sets the fan settings of the unit.
423
+ * @param fan the fan settings to set.
424
+ */
327
425
  async setFan(fan) {
328
426
  return await this.setControlInfo({ fanSpeed: fan.speed, fanAuto: fan.auto, fanAirside: fan.airside ?? false });
329
427
  }
428
+ /**
429
+ * Gets the current target temperature of the unit
430
+ */
330
431
  async getTargetTemperature() {
331
432
  return (await this.getControlInfo()).targetTemperature;
332
433
  }
434
+ /**
435
+ * Sets the target temperature of the unit.
436
+ * @param temperature the temperature to set. This is rounded to the nearest integer.
437
+ */
333
438
  async setTargetTemperature(temperature) {
334
439
  return await this.setControlInfo({ targetTemperature: temperature });
335
440
  }
336
- async getSensorTemperatures() {
337
- return (await this.getControlInfo()).sensorTemperatures;
338
- }
441
+ /** Gets the current status of the unit. */
339
442
  async getStatus() {
340
443
  return (await this.getControlInfo()).status;
341
444
  }
445
+ /** Sets the control state of the unit. */
342
446
  async setControlInfo(info) {
343
447
  const latest = await this.getControlInfo();
344
448
  const update = { ...latest, ...info };
345
- const int = (v) => Math.round(v);
346
- const bool = (v) => v ? 1 : 0;
347
449
  const params = {
348
450
  f_airside: 0,
349
451
  f_auto: bool(update.fanAuto),
@@ -363,6 +465,32 @@ var DaikinClient = class {
363
465
  throw new Error(`Failed to update control info: ${response.ret}`);
364
466
  this.cachedControlInfo = update;
365
467
  }
468
+ /**
469
+ * Get the current reading of the temperature sensors.
470
+ */
471
+ async getSensorInfo() {
472
+ const response = await get_sensor_info(this.api);
473
+ return this.cachedSensorInfo = {
474
+ insideTemperature: response.htemp,
475
+ outsideTemperature: response.otemp === "-" ? void 0 : response.otemp
476
+ };
477
+ }
478
+ /** Gets the current indoor temperature reading. */
479
+ async getInsideTemperature() {
480
+ return (await this.getSensorInfo()).insideTemperature;
481
+ }
482
+ /** Gets the current outdoor temperature reading. */
483
+ async getOutsideTemperature() {
484
+ return (await this.getSensorInfo()).outsideTemperature;
485
+ }
486
+ /** Gets the cached state of the sensor info. */
487
+ getCachedSensorInfo() {
488
+ return this.cachedSensorInfo;
489
+ }
490
+ /**
491
+ * Gets the basic information about the aircon.
492
+ * @returns the basic information about the aircon.
493
+ */
366
494
  async getBasicInfo() {
367
495
  const response = await basic_info(this.api);
368
496
  return {
@@ -380,13 +508,14 @@ var DaikinClient = class {
380
508
  adp_kind: response.adp_kind
381
509
  };
382
510
  }
511
+ /** Broadcasts a request to discover devices on the network. */
383
512
  static async discover(timeoutMs = 3e3) {
384
513
  return discover(timeoutMs);
385
514
  }
386
515
  };
387
516
  // Annotate the CommonJS export names for ESM import in node:
388
517
  0 && (module.exports = {
389
- API,
518
+ Client,
390
519
  ControlMode,
391
520
  DaikinClient,
392
521
  FanSpeed,
@@ -395,5 +524,15 @@ var DaikinClient = class {
395
524
  Zones,
396
525
  basic_info,
397
526
  discover,
398
- get_zone_settings
527
+ getNetwork_setting,
528
+ get_control_info,
529
+ get_datetime,
530
+ get_dealer_info,
531
+ get_model_info,
532
+ get_quick_timer,
533
+ get_sensor_info,
534
+ get_wifi_setting,
535
+ get_zone_settings,
536
+ set_control_info,
537
+ set_zone_setting
399
538
  });