homebridge-dyson-solarcycle-morph 1.7.5 → 1.7.7
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 +19 -0
- package/README.md +4 -0
- package/homebridge-ui/public/index.html +31 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.7.7] — 2026-10-14
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **"Add and pair" did nothing when the account also held a device that is not
|
|
13
|
+
a light**, such as a fan or a vacuum. Writing the "Not a Bluetooth light"
|
|
14
|
+
note into the list replaced the buttons with copies that had no click
|
|
15
|
+
handler. The list is now written once and wired afterwards.
|
|
16
|
+
- **Messages about your lights appear next to the lights.** Pairing, unpairing
|
|
17
|
+
and failures to find lights used to report in the account box above, out of
|
|
18
|
+
sight of the button that was pressed.
|
|
19
|
+
|
|
20
|
+
## [1.7.6] — 2026-10-01
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **The README shows the settings page**, in light and dark, so it is clear
|
|
25
|
+
before installing what is set where.
|
|
26
|
+
|
|
8
27
|
## [1.7.5] — 2026-09-25
|
|
9
28
|
|
|
10
29
|
### Changed
|
package/README.md
CHANGED
|
@@ -158,6 +158,10 @@ own once the link does.
|
|
|
158
158
|
|
|
159
159
|
Everything except the lights is optional.
|
|
160
160
|
|
|
161
|
+
<p align="center">
|
|
162
|
+
<img src="https://raw.githubusercontent.com/rummeyer/homebridge-dyson-solarcycle-morph/main/assets/screenshots/settings.png" width="520" alt="The settings page in the Homebridge UI: the MyDyson account, finding the lights, where they are, and one lamp's settings: address, serial, location, daytime, age adjustment and switches">
|
|
163
|
+
</p>
|
|
164
|
+
|
|
161
165
|
| Setting | |
|
|
162
166
|
|---|---|
|
|
163
167
|
| **Name** | What the plugin is called in the Homebridge log |
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
</p>
|
|
88
88
|
<button type="button" class="btn btn-secondary" id="morph-find" disabled>Find my lights</button>
|
|
89
89
|
<div id="morph-found" class="mt-3"></div>
|
|
90
|
+
<div id="morph-lights-message" class="alert morph-msg morph-hidden"></div>
|
|
90
91
|
<hr>
|
|
91
92
|
<h6>Configured</h6>
|
|
92
93
|
<div id="morph-status">…</div>
|
|
@@ -118,11 +119,13 @@
|
|
|
118
119
|
// block; the account fields above are owned here and left out of the layout.
|
|
119
120
|
homebridge.showSchemaForm();
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
// Each card has its own message box, so the answer to a button appears next
|
|
123
|
+
// to it rather than out of sight in the card above.
|
|
124
|
+
const show = (text, kind, where = 'morph-message') => {
|
|
125
|
+
$(where).textContent = text;
|
|
126
|
+
$(where).className = `alert morph-msg alert-${kind}`;
|
|
124
127
|
};
|
|
125
|
-
const clearMessage = () => { $(
|
|
128
|
+
const clearMessage = (where = 'morph-message') => { $(where).className = 'alert morph-msg morph-hidden'; };
|
|
126
129
|
|
|
127
130
|
/** The plugin's config block, created on first run. */
|
|
128
131
|
async function readConfig() {
|
|
@@ -263,9 +266,9 @@
|
|
|
263
266
|
for (const button of document.querySelectorAll('.morph-unpair')) {
|
|
264
267
|
button.addEventListener('click', () => armThenRun(button, 'Unpair', async () => {
|
|
265
268
|
const { message } = await homebridge.request('/forget', { serial: button.dataset.serial });
|
|
266
|
-
show(message, 'success');
|
|
269
|
+
show(message, 'success', 'morph-lights-message');
|
|
267
270
|
await refreshStatus();
|
|
268
|
-
}));
|
|
271
|
+
}), 'morph-lights-message');
|
|
269
272
|
}
|
|
270
273
|
}
|
|
271
274
|
|
|
@@ -275,7 +278,7 @@
|
|
|
275
278
|
* The host iframe swallows window.confirm, so the first click turns the
|
|
276
279
|
* button into the confirmation rather than opening a dialog that never shows.
|
|
277
280
|
*/
|
|
278
|
-
function armThenRun(button, label, task) {
|
|
281
|
+
function armThenRun(button, label, task, where) {
|
|
279
282
|
if (button.dataset.armed !== 'yes') {
|
|
280
283
|
button.dataset.armed = 'yes';
|
|
281
284
|
button.textContent = 'Are you sure?';
|
|
@@ -289,17 +292,17 @@
|
|
|
289
292
|
}, 5000);
|
|
290
293
|
return;
|
|
291
294
|
}
|
|
292
|
-
void busy(button, task);
|
|
295
|
+
void busy(button, task, where);
|
|
293
296
|
}
|
|
294
297
|
|
|
295
|
-
/** Run `task` with the spinner up and the button disabled
|
|
296
|
-
async function busy(button, task) {
|
|
298
|
+
/** Run `task` with the spinner up and the button disabled; failures are shown in `where`. */
|
|
299
|
+
async function busy(button, task, where) {
|
|
297
300
|
button.disabled = true;
|
|
298
301
|
homebridge.showSpinner();
|
|
299
302
|
try {
|
|
300
303
|
await task();
|
|
301
304
|
} catch (err) {
|
|
302
|
-
show(err.message ?? String(err), 'danger');
|
|
305
|
+
show(err.message ?? String(err), 'danger', where);
|
|
303
306
|
} finally {
|
|
304
307
|
homebridge.hideSpinner();
|
|
305
308
|
button.disabled = false;
|
|
@@ -327,13 +330,12 @@
|
|
|
327
330
|
|
|
328
331
|
$('morph-find').addEventListener('click', () => busy($('morph-find'), async () => {
|
|
329
332
|
clearMessage();
|
|
333
|
+
clearMessage('morph-lights-message');
|
|
330
334
|
const { lights, skipped } = await homebridge.request('/lights', { account: account() });
|
|
331
335
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
} else {
|
|
336
|
-
$('morph-found').innerHTML = lights.map((light) => {
|
|
336
|
+
let html = lights.length === 0
|
|
337
|
+
? '<span class="text-muted">No lights on this Dyson account. Register the lamp in the MyDyson app first.</span>'
|
|
338
|
+
: lights.map((light) => {
|
|
337
339
|
const reachable = Boolean(light.mac);
|
|
338
340
|
return `<div class="mb-2">
|
|
339
341
|
<code>${esc(light.serial)}</code>
|
|
@@ -348,24 +350,27 @@
|
|
|
348
350
|
</div>`;
|
|
349
351
|
}).join('');
|
|
350
352
|
|
|
351
|
-
for (const button of document.querySelectorAll('.morph-add')) {
|
|
352
|
-
button.addEventListener('click', () => void busy(button, () => addAndPair(button.dataset)));
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
353
|
// Saying what was left out is what stops someone adding their vacuum.
|
|
357
354
|
if (skipped.length) {
|
|
358
|
-
|
|
359
|
-
`<p class="text-muted small mt-3 mb-0">Not a Bluetooth light, so not offered: ` +
|
|
355
|
+
html += `<p class="text-muted small mt-3 mb-0">Not a Bluetooth light, so not offered: ` +
|
|
360
356
|
skipped.map((d) => `${esc(d.name)} (${esc(d.category ?? 'unknown')})`).join(', ') + '</p>';
|
|
361
357
|
}
|
|
362
|
-
|
|
358
|
+
|
|
359
|
+
// Written once, then wired: assigning innerHTML again afterwards (even with
|
|
360
|
+
// +=) replaces the buttons with copies that have no listeners, so they
|
|
361
|
+
// look fine and do nothing.
|
|
362
|
+
$('morph-found').innerHTML = html;
|
|
363
|
+
for (const button of document.querySelectorAll('.morph-add')) {
|
|
364
|
+
button.addEventListener('click', () =>
|
|
365
|
+
void busy(button, () => addAndPair(button.dataset), 'morph-lights-message'));
|
|
366
|
+
}
|
|
367
|
+
}, 'morph-lights-message'));
|
|
363
368
|
|
|
364
369
|
async function addAndPair({ mac, serial, name }) {
|
|
365
370
|
const configs = await readConfig();
|
|
366
371
|
const lights = configs[0].lights;
|
|
367
372
|
// Re-adding a light should correct its address, not duplicate it.
|
|
368
|
-
const existing = lights.find((l) => (l
|
|
373
|
+
const existing = lights.find((l) => (l?.serial ?? '').trim().toUpperCase() === serial.toUpperCase());
|
|
369
374
|
if (existing) {
|
|
370
375
|
existing.mac = mac;
|
|
371
376
|
} else {
|
|
@@ -374,7 +379,7 @@
|
|
|
374
379
|
await homebridge.updatePluginConfig(configs);
|
|
375
380
|
|
|
376
381
|
const { message } = await homebridge.request('/pair', { account: account(), serial });
|
|
377
|
-
show(message, 'success');
|
|
382
|
+
show(message, 'success', 'morph-lights-message');
|
|
378
383
|
await refreshStatus();
|
|
379
384
|
}
|
|
380
385
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-dyson-solarcycle-morph",
|
|
3
3
|
"displayName": "Dyson Solarcycle Morph",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.7",
|
|
5
5
|
"description": "Homebridge plugin for the Bluetooth LE controlled Dyson Solarcycle/Lightcycle Morph desk light",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Oliver Rummeyer",
|