homebridge-eliot-smart-control 1.0.0 → 1.2.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/CHANGELOG.md CHANGED
@@ -6,6 +6,66 @@ adheres to [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.2.0] — 2026-09-21
10
+
11
+ ### Added
12
+
13
+ - **Eco mode as a setting**, off by default and left alone unless you set it.
14
+ It writes eco and travel speed as a pair — eco on with the slowest travel the
15
+ Eliot app offers, eco off with the fastest — because they are one decision
16
+ rather than two. It is worth having: measured on hardware, the pair takes the
17
+ desk from 22.6 mm/s to 42.4 mm/s. The desk stores both and goes on running
18
+ whatever it was last reset with, so the log says plainly that a manual reset
19
+ is needed and does not pretend the setting took effect.
20
+
21
+ ### Fixed
22
+
23
+ - **`idlePollSeconds` says what the poll must stay clear of.** The desk does
24
+ stream its height while the control box drives itself — the note claiming
25
+ otherwise was wrong and has been withdrawn. What is worth documenting is the
26
+ hazard in the other direction: `SETTINGS` is a command, and one arriving
27
+ mid-move cancels the move, so a poll of a few hundred milliseconds turns
28
+ every move into a ten-millimetre nudge. The plugin's 30 s idle poll was never
29
+ anywhere near that; the tools in `tools/` briefly were.
30
+
31
+ ## [1.1.0] — 2026-09-21
32
+
33
+ ### Added
34
+
35
+ - **The control box's firmware version**, shown in the Home app's accessory
36
+ details instead of nothing. It is published exactly as the desk reports it:
37
+ this one says `10`, which is probably version 1.0, but nothing here has
38
+ established that and a dot would make a guess look like a reading. The desk
39
+ only says so a couple of seconds after connecting, which is later than
40
+ HomeKit reads the information service, so the version is remembered between
41
+ restarts and published from that. A desk seen for the first time shows
42
+ nothing until its second start — the price of not inventing a number.
43
+ - **The desk's own settings are now read back**, not assumed. `CONNECT` (`0xFE`)
44
+ turns out to be the command that fetches the whole settings block — travel
45
+ speed, eco mode, motion mode, collision sensitivity, display units and the
46
+ firmware version. Until now this plugin sent it never and the protocol notes
47
+ called its purpose unknown.
48
+ - **A second line in the `GOTO_HEIGHT` fallback warning**, naming the motion
49
+ mode the desk reports. It is the setting most likely to explain a control box
50
+ that will not drive itself, and the log now says which value is in force
51
+ rather than leaving it to be guessed at.
52
+ - **Frames received are logged**, at debug level, mirroring the `→` that was
53
+ already there. Without them the log showed what was asked and never what came
54
+ back, so a desk that did not answer and an answer that was not understood
55
+ looked exactly alike.
56
+
57
+ ### Changed
58
+
59
+ - **Reconnect attempts now top out at a minute**, not five. The usual reason
60
+ the desk is unreachable is that something else holds the dongle's single
61
+ connection — the Eliot app, most often — and that ends the moment the app is
62
+ closed. The old ceiling meant the desk stayed missing for up to five minutes
63
+ after it was free again, with nothing to show for the wait.
64
+ - Nothing else a user can see beyond the above. The settings are read but not
65
+ yet offered as switches: eco and travel speed only take effect after the desk
66
+ is reset, and a switch that silently needs a reset to mean anything would be
67
+ worse than no switch.
68
+
9
69
  ## [1.0.0] — 2026-09-18
10
70
 
11
71
  First stable release. Everything in it has been verified against an Eliot
@@ -124,7 +184,9 @@ First release.
124
184
  verified against hardware, including the measured travel speed and stopping
125
185
  distance.
126
186
 
127
- [Unreleased]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.0.0...HEAD
187
+ [Unreleased]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.2.0...HEAD
188
+ [1.2.0]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.1.0...v1.2.0
189
+ [1.1.0]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.0.0...v1.1.0
128
190
  [1.0.0]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v0.5.0...v1.0.0
129
191
  [0.5.0]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v0.4.0...v0.5.0
130
192
  [0.4.0]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v0.3.1...v0.4.0
@@ -40,7 +40,7 @@
40
40
  "type": "integer",
41
41
  "default": 30,
42
42
  "minimum": 0,
43
- "description": "How often to ask a standing desk for its height. The desk reports by itself while moving, so this only catches the handset being used. 0 turns it off."
43
+ "description": "Wie oft ein stehender Tisch nach seiner Höhe gefragt wird. Die Controlbox meldet die Höhe nie von selbst — auch nicht während der Fahrt —, sie antwortet nur auf Nachfrage. Das hier ist also die einzige Quelle für die Position, solange sonst niemand fragt. 0 schaltet es ab."
44
44
  },
45
45
  "memorySwitches": {
46
46
  "title": "Memory-Schalter",
@@ -62,6 +62,11 @@
62
62
  "type": "boolean",
63
63
  "default": true,
64
64
  "description": "Die Kindersicherung des Tisches in der Home-App anbieten. Sie ist die einzige Einstellung, die die Controlbox zurückmeldet, also die einzige, die ehrlich angezeigt werden kann."
65
+ },
66
+ "ecoMode": {
67
+ "title": "Eco-Modus",
68
+ "type": "boolean",
69
+ "description": "Eco-Modus des Tisches, zusammen mit der Fahrgeschwindigkeit: **an** = langsam und leise (Stufe 28), **aus** = die schnellste Stufe, die die Eliot-App anbietet (40). Beides gehört zusammen und wird deshalb als Paar geschrieben.\n\n**Wirkt erst nach einem Reset des Tisches.** Die Controlbox speichert die Einstellung sofort, fährt aber weiter mit dem Wert, mit dem sie zuletzt zurückgesetzt wurde — deshalb kann der Tisch eine Einstellung melden, die er sichtbar nicht benutzt. Scharf wird sie erst so: den Tisch ganz nach unten fahren und die Ab-Taste weiter gedrückt halten, bis er von selbst nach unten fährt und sich neu einmisst. Kein Kommando kann das auslösen, auch dieses Plugin nicht.\n\nOhne Häkchen wird die Einstellung des Tisches nicht angefasst. Das ist die Voreinstellung: eine ungefragt geschriebene Einstellung würde beim nächsten Reset losgehen, lange nachdem irgendwer sie mit Homebridge in Verbindung bringt."
65
70
  }
66
71
  }
67
72
  }
@@ -81,6 +86,7 @@
81
86
  "desks[].name",
82
87
  "desks[].mac",
83
88
  "desks[].idlePollSeconds",
89
+ "desks[].ecoMode",
84
90
  "desks[].childLockSwitch",
85
91
  "desks[].memorySwitches",
86
92
  {
package/dist/accessory.js CHANGED
@@ -23,6 +23,8 @@ export class EliotAccessory {
23
23
  #service;
24
24
  /** Position to report instead of the real one, after a successful move. */
25
25
  #snapTo = null;
26
+ /** Last firmware version published, so it is only written when it changes. */
27
+ #firmware = null;
26
28
  /** Memory switches by slot, created lazily once the desk lists its memories. */
27
29
  #memoryServices = new Map();
28
30
  #lockService;
@@ -40,12 +42,29 @@ export class EliotAccessory {
40
42
  const link = transport ?? new DeskLink(config.mac, platform.log);
41
43
  this.#desk = new Desk(link, platform.log, {
42
44
  idlePollMs: (config.idlePollSeconds ?? 30) * 1000,
45
+ eco: config.ecoMode,
43
46
  });
44
47
  accessory
45
48
  .getService(HapService.AccessoryInformation)
46
49
  ?.setCharacteristic(Characteristic.Manufacturer, 'Eliot')
47
50
  .setCharacteristic(Characteristic.Model, 'Smart Dongle')
48
51
  .setCharacteristic(Characteristic.SerialNumber, config.mac);
52
+ // The version the desk gave last time, set before the bridge publishes.
53
+ //
54
+ // HomeKit reads the information service when the accessory database is
55
+ // published and does not come back for it: FirmwareRevision cannot notify,
56
+ // so a value written later is correct in this process and invisible in the
57
+ // Home app. The settings block arrives about two seconds after connecting,
58
+ // which is always too late. Remembering it across restarts is what makes it
59
+ // visible — a desk seen for the first time shows nothing until its second
60
+ // start, which is the price of not inventing a number.
61
+ const remembered = accessory.context.firmware;
62
+ if (typeof remembered === 'number') {
63
+ this.#firmware = remembered;
64
+ accessory
65
+ .getService(HapService.AccessoryInformation)
66
+ ?.setCharacteristic(Characteristic.FirmwareRevision, String(remembered));
67
+ }
49
68
  this.#service =
50
69
  accessory.getService(HapService.WindowCovering) ??
51
70
  accessory.addService(HapService.WindowCovering, config.name);
@@ -82,6 +101,7 @@ export class EliotAccessory {
82
101
  .onSet((value) => this.#setLock(Boolean(value)));
83
102
  }
84
103
  this.#desk.on('change', (state) => {
104
+ this.#syncFirmware(state);
85
105
  this.#syncMemorySwitches(state);
86
106
  this.#publish(state);
87
107
  });
@@ -96,6 +116,32 @@ export class EliotAccessory {
96
116
  async stop() {
97
117
  await this.#desk.close();
98
118
  }
119
+ /**
120
+ * Record the control box's firmware version once it has told us.
121
+ *
122
+ * It arrives with the settings block, seconds after this accessory was
123
+ * published, so setting it here is what keeps the value true rather than
124
+ * what makes it visible — see the constructor, which publishes the
125
+ * remembered one early enough for HomeKit to read it.
126
+ *
127
+ * The number is published as the desk reports it. A `10` is probably
128
+ * version 1.0, but nothing here has established that, and inventing a dot
129
+ * would turn a guess into something that looks like a reading.
130
+ */
131
+ #syncFirmware(state) {
132
+ const { firmware } = state.settings;
133
+ if (firmware === null || firmware === this.#firmware) {
134
+ return;
135
+ }
136
+ this.#firmware = firmware;
137
+ const { Characteristic, Service: HapService } = this.#platform.api.hap;
138
+ this.#accessory
139
+ .getService(HapService.AccessoryInformation)
140
+ ?.setCharacteristic(Characteristic.FirmwareRevision, String(firmware));
141
+ // Persisted so the next start can publish it in time to be read.
142
+ this.#accessory.context.firmware = firmware;
143
+ this.#platform.api.updatePlatformAccessories([this.#accessory]);
144
+ }
99
145
  /**
100
146
  * Refuse to answer rather than answer with something stale.
101
147
  *
@@ -1 +1 @@
1
- {"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -14,9 +14,13 @@ export interface DeskConfig {
14
14
  /**
15
15
  * How often to ask an idle desk for its height, in seconds. 0 disables it.
16
16
  *
17
- * The control box reports height by itself while moving, so this only
18
- * catches what happens in between: somebody using the handset, or the desk
19
- * being moved while Homebridge was not connected.
17
+ * The control box streams its height while it drives itself, so this is only
18
+ * for what happens in between: somebody using the handset, or the desk being
19
+ * moved while Homebridge was not connected.
20
+ *
21
+ * Leave it leisurely. `SETTINGS` is a command, and one that arrives while the
22
+ * box is driving to a position cancels the move — at a few hundred
23
+ * milliseconds it turns every move into a ten-millimetre nudge.
20
24
  */
21
25
  idlePollSeconds?: number;
22
26
  /**
@@ -40,6 +44,28 @@ export interface DeskConfig {
40
44
  * the only one that can be shown honestly rather than remembered.
41
45
  */
42
46
  childLockSwitch?: boolean;
47
+ /**
48
+ * Eco mode, and with it the travel speed. Left alone unless set.
49
+ *
50
+ * The control box stores these two together in its settings block, and this
51
+ * plugin writes them as a pair: eco on goes with the slowest travel the app
52
+ * offers, eco off with the fastest. They are not separate knobs here because
53
+ * they are not separate decisions — eco mode is the slow, quiet setting.
54
+ *
55
+ * Neither takes effect when written. The box stores them and goes on running
56
+ * whatever it was reset with, which is why the settings block can report a
57
+ * value the desk is visibly not using. They come into force only when the
58
+ * desk is reset by hand: run it to the bottom and hold the down key until it
59
+ * re-homes. No command on this port can do that, and the plugin does not
60
+ * pretend otherwise — it writes the pair, says so in the log, and leaves the
61
+ * reset to whoever is standing there.
62
+ *
63
+ * Undefined means the desk's own setting is left untouched, which is the
64
+ * default: a plugin that quietly rewrote a stored setting would be arming a
65
+ * change that fires at the next reset, long after anyone connected it to
66
+ * Homebridge.
67
+ */
68
+ ecoMode?: boolean;
43
69
  }
44
70
  export interface EliotPlatformConfig extends PlatformConfig {
45
71
  /** Homebridge uses this as the log prefix for everything this plugin says. */
package/dist/config.js CHANGED
@@ -19,6 +19,9 @@ export function validateDeskConfig(desk, index) {
19
19
  (!Number.isFinite(desk.idlePollSeconds) || desk.idlePollSeconds < 0)) {
20
20
  problems.push(`${where}.idlePollSeconds must be zero or a positive number`);
21
21
  }
22
+ if (desk.ecoMode !== undefined && typeof desk.ecoMode !== 'boolean') {
23
+ problems.push(`${where}.ecoMode must be true or false`);
24
+ }
22
25
  return problems;
23
26
  }
24
27
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAmDA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAyB,EAAE,KAAa;IACzE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,SAAS,KAAK,GAAG,CAAC;IAEhC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,mBAAmB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACxE,QAAQ,CAAC,IAAI,CACX,GAAG,KAAK,8CAA8C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAClF,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,sCAAsC,CAAC,CAAC;IAChE,CAAC;IACD,IACE,IAAI,CAAC,eAAe,KAAK,SAAS;QAClC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,EACpE,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,oDAAoD,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA6EA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAyB,EAAE,KAAa;IACzE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,SAAS,KAAK,GAAG,CAAC;IAEhC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,mBAAmB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACxE,QAAQ,CAAC,IAAI,CACX,GAAG,KAAK,8CAA8C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAClF,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,sCAAsC,CAAC,CAAC;IAChE,CAAC;IACD,IACE,IAAI,CAAC,eAAe,KAAK,SAAS;QAClC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,EACpE,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,oDAAoD,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACpE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,gCAAgC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -29,6 +29,29 @@ export type MoveOutcome = MoveResult
29
29
  | 'disconnected'
30
30
  /** Never started: no state, or the target was out of range. */
31
31
  | 'refused';
32
+ /**
33
+ * The control box's own configuration, as it reports it.
34
+ *
35
+ * Every field is `null` until the settings block arrives, and the block only
36
+ * arrives in answer to `CONNECT`. The app keeps a copy of these on the phone
37
+ * and writes it to the desk when it connects, so they can change without this
38
+ * plugin doing anything — which is the reason for reading them back rather
39
+ * than remembering what was last sent.
40
+ */
41
+ export interface DeskSettings {
42
+ /** Firmware version as the box reports it, unscaled. */
43
+ firmware: number | null;
44
+ /** Travel speed. The app offers 28, 31, 35, 38 and 40. */
45
+ velocity: number | null;
46
+ /** Eco mode. Takes effect only after the desk is reset — see README. */
47
+ lowPower: boolean | null;
48
+ /** How the desk responds to a memory button; the mapping is unconfirmed. */
49
+ motionMode: number | null;
50
+ /** Anti-collision sensitivity: `1` high, `2` medium, `3` low. */
51
+ sensitivity: number | null;
52
+ /** What the desk's own display shows. */
53
+ units: 'cm' | 'inch' | null;
54
+ }
32
55
  export interface DeskState {
33
56
  connected: boolean;
34
57
  /**
@@ -54,12 +77,12 @@ export interface DeskState {
54
77
  /**
55
78
  * Whether the desk's own child lock is on, or `null` before it has said.
56
79
  *
57
- * The one setting the control box will read back on this port. Everything
58
- * else the app can change — eco mode, travel speed, collision sensitivity —
59
- * is write-only here, which is presumably why the app keeps its own copy of
60
- * them on the phone.
80
+ * Unlike the fields in {@link DeskState.settings} this one answers its own
81
+ * command, so it is known even before the settings block arrives.
61
82
  */
62
83
  locked: boolean | null;
84
+ /** What the control box reports about its own configuration. */
85
+ settings: DeskSettings;
63
86
  /**
64
87
  * The four memory heights in millimetres, `null` where unset.
65
88
  *
@@ -73,10 +96,14 @@ export interface DeskOptions {
73
96
  /**
74
97
  * How often to ask for the height while idle.
75
98
  *
76
- * The control box reports height unprompted while it moves, so this is only
99
+ * The control box streams its height while it drives itself, so this is only
77
100
  * for what happens in between: someone using the handset, or a desk that was
78
- * moved while we were disconnected. Cheap, and the alternative is a Home app
79
- * showing a height from an hour ago.
101
+ * moved while we were disconnected.
102
+ *
103
+ * It must stay well clear of a move. `SETTINGS` is a command, and one that
104
+ * lands while the box is driving to a position abandons the move — polling at
105
+ * a few hundred milliseconds reduces every move to about ten millimetres.
106
+ * Thirty seconds is idle-only by a wide margin.
80
107
  */
81
108
  idlePollMs: number;
82
109
  /**
@@ -115,6 +142,14 @@ export interface DeskOptions {
115
142
  nativeStartMs: number;
116
143
  /** How far off a memory height still counts as being at that preset. */
117
144
  memoryToleranceMm: number;
145
+ /**
146
+ * Eco mode to store on the desk, with the travel speed that goes with it.
147
+ *
148
+ * `undefined` leaves the desk's own setting alone, which is the default and
149
+ * the only honest one: writing it stores a change that takes effect at the
150
+ * next reset, which may be weeks away and nowhere near this decision.
151
+ */
152
+ eco?: boolean;
118
153
  }
119
154
  export declare const DEFAULT_DESK_OPTIONS: DeskOptions;
120
155
  export interface Desk {
@@ -10,6 +10,26 @@
10
10
  import { EventEmitter } from 'node:events';
11
11
  import { MoveController, heightToPercent, percentToHeight } from "./move.js";
12
12
  import { Cmd, Report, heightParams, readHeight } from "./protocol.js";
13
+ const UNKNOWN_SETTINGS = {
14
+ firmware: null,
15
+ velocity: null,
16
+ lowPower: null,
17
+ motionMode: null,
18
+ sensitivity: null,
19
+ units: null,
20
+ };
21
+ /**
22
+ * The travel speeds that go with eco on and eco off.
23
+ *
24
+ * The app offers 28, 31, 35, 38 and 40 and nothing outside that, so these are
25
+ * its ends rather than the box's: this desk was found storing 21, below
26
+ * anything the app will produce, which says the box accepts more than the app
27
+ * offers and says nothing about what is good for it. Staying inside the range
28
+ * the manufacturer's own app uses is the conservative choice for a value that
29
+ * only takes effect after a reset, where a bad one is discovered late.
30
+ */
31
+ const ECO_VELOCITY = 28;
32
+ const FAST_VELOCITY = 40;
13
33
  export const DEFAULT_DESK_OPTIONS = {
14
34
  idlePollMs: 30_000,
15
35
  externalMoveMm: 15,
@@ -39,6 +59,7 @@ export class Desk extends EventEmitter {
39
59
  #restingMm = null;
40
60
  #memories = [null, null, null, null];
41
61
  #locked = null;
62
+ #settings = { ...UNKNOWN_SETTINGS };
42
63
  #move = null;
43
64
  #native = null;
44
65
  #nativeTimer = null;
@@ -48,6 +69,8 @@ export class Desk extends EventEmitter {
48
69
  #settleUntil = 0;
49
70
  #closing = false;
50
71
  #refreshed = false;
72
+ /** Whether the configured eco pair has been dealt with on this connection. */
73
+ #ecoApplied = false;
51
74
  constructor(transport, log, options = {}) {
52
75
  super();
53
76
  this.#transport = transport;
@@ -76,6 +99,7 @@ export class Desk extends EventEmitter {
76
99
  target,
77
100
  moving: this.#move?.controller.direction ?? this.#native?.direction ?? null,
78
101
  locked: this.#locked,
102
+ settings: { ...this.#settings },
79
103
  memories: [...this.#memories],
80
104
  };
81
105
  }
@@ -121,6 +145,12 @@ export class Desk extends EventEmitter {
121
145
  }
122
146
  this.#refreshed = true;
123
147
  this.#emitChange();
148
+ // The settings block comes after readiness, deliberately. None of it feeds
149
+ // the height or the limits, so making the desk wait on it would delay the
150
+ // first trustworthy position for the sake of a firmware string.
151
+ if (this.#transport.connected) {
152
+ await this.#transport.send(Cmd.CONNECT);
153
+ }
124
154
  }
125
155
  /**
126
156
  * Drive to a HomeKit position, 0–100.
@@ -277,6 +307,16 @@ export class Desk extends EventEmitter {
277
307
  return;
278
308
  }
279
309
  this.#log.warn('this control box does not appear to know GOTO_HEIGHT — falling back to step commands');
310
+ // Motion mode governs whether the box will drive to a position by itself,
311
+ // so it is the first thing to suspect here. Which value means which is not
312
+ // established — this desk drives happily on 0, while the published notes
313
+ // call 0 one-touch and the app's own labels disagree — so report the
314
+ // number and let whoever is reading the log compare it against the app,
315
+ // rather than asserting a mapping that has never been verified.
316
+ if (this.#settings.motionMode !== null) {
317
+ this.#log.warn(`the desk reports motion mode ${this.#settings.motionMode}; if the app shows ` +
318
+ 'one-touch mode switched off, switching it on may restore direct moves');
319
+ }
280
320
  const controller = new MoveController(native.target, from, Date.now(), this.#opts.move);
281
321
  this.#move = { controller, settle: native.settle };
282
322
  this.#tick();
@@ -460,6 +500,20 @@ export class Desk extends EventEmitter {
460
500
  this.#locked = frame.params[0] === 1;
461
501
  this.#emitChange();
462
502
  return;
503
+ case Report.UNITS:
504
+ case Report.VELOCITY:
505
+ case Report.LOW_POWER:
506
+ case Report.MOTION_MODE:
507
+ case Report.VERSION:
508
+ case Report.SENSITIVITY: {
509
+ // All six arrive together, as one frame each, in answer to CONNECT.
510
+ const value = frame.params[0];
511
+ if (value === undefined) {
512
+ return;
513
+ }
514
+ this.#onSetting(frame.command, value);
515
+ return;
516
+ }
463
517
  case Report.LIMIT_FLAGS: {
464
518
  // Bit 0 is the max limit, bit 4 the min. A limit that is not set means
465
519
  // the physical end of travel, so forget any stale value rather than
@@ -478,6 +532,82 @@ export class Desk extends EventEmitter {
478
532
  return;
479
533
  }
480
534
  }
535
+ /**
536
+ * Record one field of the settings block.
537
+ *
538
+ * The block is sent whole every time, so a field that has not changed still
539
+ * arrives; only publish when something actually differs, or a refresh of an
540
+ * untouched desk would wake every subscriber for nothing.
541
+ */
542
+ #onSetting(code, value) {
543
+ const before = { ...this.#settings };
544
+ switch (code) {
545
+ case Report.UNITS:
546
+ this.#settings.units = value === 1 ? 'inch' : 'cm';
547
+ break;
548
+ case Report.VELOCITY:
549
+ this.#settings.velocity = value;
550
+ break;
551
+ case Report.LOW_POWER:
552
+ this.#settings.lowPower = value === 1;
553
+ break;
554
+ case Report.MOTION_MODE:
555
+ this.#settings.motionMode = value;
556
+ break;
557
+ case Report.VERSION:
558
+ this.#settings.firmware = value;
559
+ break;
560
+ case Report.SENSITIVITY:
561
+ this.#settings.sensitivity = value;
562
+ break;
563
+ default:
564
+ return;
565
+ }
566
+ const key = Object.keys(before);
567
+ if (key.some((k) => before[k] !== this.#settings[k])) {
568
+ this.#emitChange();
569
+ }
570
+ void this.#applyEco();
571
+ }
572
+ /**
573
+ * Store the configured eco pair, if the desk is not already holding it.
574
+ *
575
+ * Runs once per connection and only when something differs, because the
576
+ * write is not free: the box stores it and keeps running what it was last
577
+ * reset with, so every write leaves a change primed to go off at a reset that
578
+ * may be weeks away. Writing the same values again on every reconnect would
579
+ * be harmless in effect and dishonest in the log, which is where anyone will
580
+ * go looking when the desk changes speed for no reason they can remember.
581
+ *
582
+ * Both fields have to have arrived before this can tell whether anything
583
+ * differs, which is why it hangs off the settings block rather than the
584
+ * connection.
585
+ */
586
+ async #applyEco() {
587
+ const eco = this.#opts.eco;
588
+ if (eco === undefined || this.#ecoApplied) {
589
+ return;
590
+ }
591
+ const { lowPower, velocity } = this.#settings;
592
+ if (lowPower === null || velocity === null) {
593
+ return;
594
+ }
595
+ this.#ecoApplied = true;
596
+ const wantVelocity = eco ? ECO_VELOCITY : FAST_VELOCITY;
597
+ if (lowPower === eco && velocity === wantVelocity) {
598
+ return;
599
+ }
600
+ if (!this.#transport.connected) {
601
+ return;
602
+ }
603
+ await this.#transport.send(Cmd.LOW_POWER, [eco ? 1 : 0]);
604
+ await delay(300);
605
+ await this.#transport.send(Cmd.VELOCITY, [wantVelocity]);
606
+ this.#log.warn(`eco mode ${eco ? 'on' : 'off'} and travel speed ${wantVelocity} stored on the desk ` +
607
+ `(it had ${lowPower ? 'on' : 'off'} and ${velocity}). The desk keeps running its ` +
608
+ 'old setting until it is reset by hand: drive it to the bottom and hold the down ' +
609
+ 'key until it re-homes. No command can do this.');
610
+ }
481
611
  #onHeight(heightMm) {
482
612
  this.#heightMm = heightMm;
483
613
  if (this.#move) {
@@ -528,6 +658,7 @@ export class Desk extends EventEmitter {
528
658
  // The desk may be moved by hand while we are away, so nothing we hold is
529
659
  // trustworthy until it has been asked again.
530
660
  this.#refreshed = false;
661
+ this.#ecoApplied = false;
531
662
  this.#locked = null;
532
663
  this.#endMove('disconnected');
533
664
  this.#endNative('disconnected');