homebridge-eliot-smart-control 1.6.2 → 1.6.3

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,25 @@ adheres to [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.6.3] — 2026-09-21
10
+
11
+ ### Changed
12
+
13
+ - **The hardware tools stay out of the npm package.** `tools/` is a workbench
14
+ for a Raspberry Pi with a dongle attached, not something an install needs:
15
+ 117 kB of it shipped to every user, a dozen scripts of which drive the desk
16
+ and one of which drives it to the floor. Finding a dongle, the one thing a
17
+ user might have wanted from it, is the scan button in the config UI.
18
+
19
+ ### Removed
20
+
21
+ - **Fourteen single-question probes.** Each was written to settle one point
22
+ about the protocol — whether `VELOCITY` bites, whether a memory move can be
23
+ interrupted, what `VERSION` answers — and each of those answers, with the
24
+ measurements behind it, is written up in `docs/PROTOCOL.md`. The scripts were
25
+ the scaffolding, and the reference is the thing that was being built. What
26
+ remains in `tools/` is the eight that answer a question you can still have.
27
+
9
28
  ## [1.6.2] — 2026-09-21
10
29
 
11
30
  ### Changed
@@ -411,7 +430,8 @@ First release.
411
430
  verified against hardware, including the measured travel speed and stopping
412
431
  distance.
413
432
 
414
- [Unreleased]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.6.2...HEAD
433
+ [Unreleased]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.6.3...HEAD
434
+ [1.6.3]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.6.2...v1.6.3
415
435
  [1.6.2]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.6.1...v1.6.2
416
436
  [1.6.1]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.6.0...v1.6.1
417
437
  [1.6.0]: https://github.com/rummeyer/homebridge-eliot-smart-control/compare/v1.5.2...v1.6.0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-eliot-smart-control",
3
3
  "displayName": "Eliot Smart Control",
4
- "version": "1.6.2",
4
+ "version": "1.6.3",
5
5
  "description": "Homebridge plugin for the Eliot sit-stand desk with the Smart Dongle, over Bluetooth LE",
6
6
  "license": "MIT",
7
7
  "author": "Oliver Rummeyer",
@@ -51,7 +51,6 @@
51
51
  "files": [
52
52
  "dist",
53
53
  "homebridge-ui",
54
- "tools",
55
54
  "docs",
56
55
  "!docs/*.png",
57
56
  "config.schema.json",
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * What happens when the handset is used during a plugin move? THIS MOVES THE
4
- * DESK, and expects a person to push back against it.
5
- *
6
- * npm run build && node tools/conflict-test.js <MAC>
7
- *
8
- * Drives a long way through the normal closed loop and logs every height
9
- * report and every outcome, so the control box's arbitration between two
10
- * commanders is visible afterwards.
11
- */
12
- import { Desk } from '../dist/eliot/desk.js';
13
- import { DeskLink } from '../dist/eliot/link.js';
14
-
15
- const MAC = (process.argv[2] || '').toUpperCase();
16
- if (!/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/.test(MAC)) {
17
- console.error('Usage: node tools/conflict-test.js <MAC>');
18
- process.exit(1);
19
- }
20
-
21
- const t0 = Date.now();
22
- const stamp = () => `${String((Date.now() - t0) / 1000).padStart(6)}s`;
23
- const say = (m) => console.log(`${stamp()} ${m}`);
24
- const log = { debug: () => {}, info: (m) => say(` ${m}`), warn: (m) => say(`WARN ${m}`), error: (m) => say(`ERROR ${m}`) };
25
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
26
- const waitConnected = (l, ms) =>
27
- l.connected ? Promise.resolve() : new Promise((res) => {
28
- const t = setTimeout(res, ms);
29
- l.once('connected', () => { clearTimeout(t); res(); });
30
- });
31
-
32
- const link = new DeskLink(MAC, log);
33
- const desk = new Desk(link, log, { idlePollMs: 0 });
34
-
35
- let last = null;
36
- desk.on('change', (s) => {
37
- const line = `${s.heightMm} mm ${s.position}% ziel ${s.target}% ${s.moving ?? 'steht'}`;
38
- if (line !== last) { last = line; say(` ${line}`); }
39
- });
40
-
41
- await desk.start();
42
- await waitConnected(link, 60_000);
43
- await sleep(3000);
44
-
45
- const s0 = desk.state;
46
- if (!s0.ready) { console.error('kein zustand'); await desk.close(); process.exit(1); }
47
-
48
- // A long move, well inside the limits, so there is room to fight over.
49
- const up = s0.position < 50;
50
- const target = up ? Math.min(s0.position + 40, 90) : Math.max(s0.position - 40, 10);
51
- say(`start ${s0.heightMm} mm (${s0.position}%), fahre nach ${target}% — ${up ? 'HOCH' : 'RUNTER'}`);
52
- say(`>>> JETZT AM HANDSET DIE GEGENRICHTUNG DRUECKEN: ${up ? 'RUNTER' : 'HOCH'} <<<`);
53
-
54
- const outcome = await desk.moveTo(target);
55
- say(`ERGEBNIS: ${outcome}`);
56
-
57
- await sleep(3000);
58
- const s1 = desk.state;
59
- say(`danach: ${s1.heightMm} mm (${s1.position}%), ziel ${s1.target}%, ${s1.moving ?? 'steht'}`);
60
- say(outcome === 'stalled'
61
- ? 'DEUTUNG: der regelkreis hat den widerstand erkannt und aufgegeben — gewuenscht'
62
- : outcome === 'arrived'
63
- ? 'DEUTUNG: durchgefahren — entweder wurde nicht gedrueckt, oder das plugin gewinnt'
64
- : `DEUTUNG: ${outcome} — siehe hoehenverlauf oben`);
65
-
66
- await desk.close();
67
- process.exit(0);
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * End-to-end test of Desk against real hardware. THIS MOVES THE DESK.
4
- *
5
- * npm run build && node tools/desk-test.js <MAC> <percent> [percent…]
6
- *
7
- * Drives to each position in turn through the same path HomeKit will use, and
8
- * prints every state change on the way.
9
- */
10
- import { Desk } from '../dist/eliot/desk.js';
11
- import { DeskLink } from '../dist/eliot/link.js';
12
-
13
- const MAC = (process.argv[2] || '').toUpperCase();
14
- const TARGETS = process.argv.slice(3).map(Number);
15
-
16
- if (!/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/.test(MAC) || !TARGETS.length || TARGETS.some(Number.isNaN)) {
17
- console.error('Usage: node tools/desk-test.js <MAC> <percent> [percent…]');
18
- process.exit(1);
19
- }
20
-
21
- const t0 = Date.now();
22
- const stamp = () => `${String((Date.now() - t0) / 1000).padStart(6)}s`;
23
- const say = (m) => console.log(`${stamp()} ${m}`);
24
- const log = { debug: () => {}, info: (m) => say(` ${m}`), warn: (m) => say(`WARN ${m}`), error: (m) => say(`ERROR ${m}`) };
25
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
26
-
27
- /**
28
- * `start()` resolves after the first connect attempt, successful or not — the
29
- * retry runs in the background. A tool that checks `connected` straight after
30
- * therefore reports failure whenever the first attempt misses, which it often
31
- * does when the dongle has just been released by something else.
32
- */
33
- const waitConnected = (l, ms) =>
34
- l.connected
35
- ? Promise.resolve()
36
- : new Promise((resolve) => {
37
- const timer = setTimeout(resolve, ms);
38
- l.once('connected', () => { clearTimeout(timer); resolve(); });
39
- });
40
-
41
-
42
- const link = new DeskLink(MAC, log);
43
- const desk = new Desk(link, log, { idlePollMs: 0 });
44
-
45
- let last = '';
46
- desk.on('change', (s) => {
47
- const line = `${s.position}% (${s.heightMm} mm) target ${s.target}% ${s.moving ?? 'at rest'}`;
48
- if (line !== last) {
49
- last = line;
50
- say(` ${line}`);
51
- }
52
- });
53
-
54
- await desk.start();
55
- await waitConnected(link, 60_000);
56
- await sleep(2500);
57
-
58
- if (!desk.state.connected || desk.state.position === null) {
59
- console.error('No usable state — is the Eliot app holding the dongle?');
60
- await desk.close();
61
- process.exit(1);
62
- }
63
-
64
- say(`start: ${desk.state.position}% at ${desk.state.heightMm} mm, range ${desk.minMm}-${desk.maxMm} mm`);
65
-
66
- for (const target of TARGETS) {
67
- say(`==> moveTo(${target}%)`);
68
- const outcome = await desk.moveTo(target);
69
- await sleep(2500);
70
- const s = desk.state;
71
- say(`<== ${outcome}: ${s.position}% at ${s.heightMm} mm (asked ${target}%, off by ${s.position - target}%)`);
72
- }
73
-
74
- await desk.close();
75
- process.exit(0);
package/tools/eco-test.js DELETED
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Does LOW_POWER (0x18) change the travel speed? THIS MOVES THE DESK.
4
- *
5
- * If it does, the setting can be read back by timing a move — which matters,
6
- * because the control box will not report it on this connection.
7
- *
8
- * Same direction, same distance, three times over, so the ramp cannot
9
- * flatter one setting over another.
10
- */
11
- import { DeskLink } from '../dist/eliot/link.js';
12
- import { Cmd, Report, heightParams, readHeight } from '../dist/eliot/protocol.js';
13
-
14
- const MAC = (process.argv[2] || '').toUpperCase();
15
- const LOW = 780, HIGH = 1000;
16
-
17
- const t0 = Date.now();
18
- const say = (m) => console.log(`${String((Date.now() - t0) / 1000).padStart(6)}s ${m}`);
19
- const log = { debug: () => {}, info: () => {}, warn: () => {}, error: (m) => say(`ERR ${m}`) };
20
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
21
-
22
- let height = null;
23
- const link = new DeskLink(MAC, log);
24
- link.on('frame', (f) => { if (f.command === Report.HEIGHT) height = readHeight(f.params); });
25
-
26
- await link.start();
27
- await (link.connected ? Promise.resolve() : new Promise((res) => {
28
- const t = setTimeout(res, 60000); link.once('connected', () => { clearTimeout(t); res(); });
29
- }));
30
- if (!link.connected) { console.error('nicht verbunden'); process.exit(1); }
31
-
32
- await link.send(Cmd.WAKE); await sleep(700);
33
- await link.send(Cmd.SETTINGS); await sleep(1200);
34
-
35
- async function driveTo(target) {
36
- const samples = [];
37
- const on = (f) => { if (f.command === Report.HEIGHT) samples.push([Date.now(), readHeight(f.params)]); };
38
- link.on('frame', on);
39
- await link.send(Cmd.GOTO_HEIGHT, heightParams(target));
40
- let still = 0, prev = height;
41
- while (still < 2500) {
42
- await sleep(200);
43
- if (height !== prev) { prev = height; still = 0; } else still += 200;
44
- }
45
- link.off('frame', on);
46
- // Middle 60% only: that is cruise, without either ramp.
47
- const moving = samples.filter(([, h]) => Math.abs(h - target) > 4);
48
- if (moving.length < 6) return null;
49
- const a = moving[Math.floor(moving.length * 0.2)];
50
- const b = moving[Math.floor(moving.length * 0.8)];
51
- const secs = (b[0] - a[0]) / 1000;
52
- return secs > 0 ? Math.abs(b[1] - a[1]) / secs : null;
53
- }
54
-
55
- say(`start ${height} mm`);
56
- for (const value of [0, 1, 0]) {
57
- await link.send(Cmd.GOTO_HEIGHT, heightParams(LOW));
58
- let still = 0, prev = height;
59
- while (still < 2000) { await sleep(200); if (height !== prev) { prev = height; still = 0; } else still += 200; }
60
-
61
- await link.send(Cmd.LOW_POWER, [value]);
62
- await sleep(1500);
63
- const speed = await driveTo(HIGH);
64
- say(`LOW_POWER ${value} (${value ? 'eco an' : 'eco aus'}): marschgeschwindigkeit ${speed ? speed.toFixed(1) : '?'} mm/s`);
65
- }
66
-
67
- await link.close();
68
- process.exit(0);