scene-cycle: cycle button + dimmer + passthrough scenes #2

Merged
neonoverlord merged 3 commits from scene-cycle into main 2026-10-22 18:20:57 +00:00
3 changed files with 30 additions and 9 deletions

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@ the buttons are redrawn from that answer, never from the card's own idea of the
No build step, no bundler, no state: a PHP widget, one controller, one JS file.
]]></description>
<version>1.3.4</version>
<version>1.4.0</version>
<licence>agpl</licence>
<author>bawnet</author>
<namespace>ZigbeeCard</namespace>
+26 -1
View File
@@ -60,8 +60,24 @@
text.appendChild(sub)
btn.appendChild(icon)
btn.appendChild(text)
btn.addEventListener('click', function () { onPress(b) })
var held = false, holdTimer = null
btn.addEventListener('click', function () { if (held) { held = false; return } onPress(b) })
btn.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onPress(b) } })
if (b.hold) {
btn.addEventListener('pointerdown', function () {
held = false
holdTimer = setTimeout(function () {
held = true
if (el.classList.contains('zc-busy')) { return }
busy(true); say(b.hold + ' …')
post(b.hold).then(function (d) { say((d.reply || 'no answer').split('\n')[0], !d.ok); return refresh() }).catch(function (e) { say('no answer: ' + e, true) }).finally(function () { busy(false) })
}, 500)
})
var clr = function () { if (holdTimer) { clearTimeout(holdTimer); holdTimer = null } }
btn.addEventListener('pointerup', clr)
btn.addEventListener('pointerleave', clr)
btn.addEventListener('pointercancel', clr)
}
li.appendChild(btn)
list.appendChild(li)
rows[b.key] = { btn: btn, icon: icon, title: title, sub: sub }
@@ -138,6 +154,15 @@
r.icon.textContent = (s && s.icon) || b.icon
r.title.textContent = s ? s.label : '…'
r.sub.textContent = s ? s.now : 'asking the house'
} else if (b.kind === 'cycle') {
r.icon.textContent = b.icon
var cs = state && state.scenes && state.scene_idx != null ? state.scenes[state.scene_idx] : null
r.title.textContent = cs || b.label
r.sub.textContent = b.now
} else if (b.kind === 'dimmer') {
r.icon.textContent = b.icon
r.title.textContent = b.label
r.sub.textContent = state && state.lights_on === false ? 'off · tap = on' : b.now
} else {
r.icon.textContent = b.icon
r.title.textContent = b.label
+3 -7
View File
@@ -26,12 +26,8 @@ final class Buttons {
*/
public const ALL = [
['key' => 'climate', 'kind' => 'reading', 'icon' => '🌡', 'now' => 'asking the house'], // until the first state: then the door's last change (js)
['key' => 'scene', 'kind' => 'toggle', 'icon' => '☀️',
'on' => ['when' => 'day', 'label' => 'night', 'now' => 'lights are on day', 'send' => 'lights night', 'icon' => '🌙'],
'off' => ['when' => null, 'label' => 'day', 'now' => 'lights are on night', 'send' => 'lights day', 'icon' => '☀️']],
['key' => 'lights_on', 'kind' => 'toggle', 'icon' => '💡',
'on' => ['when' => true, 'label' => 'lights off', 'now' => 'lights are on', 'send' => 'lights off'],
'off' => ['when' => false, 'label' => 'lights on', 'now' => 'lights are off', 'send' => 'lights on']],
['key' => 'scene', 'kind' => 'cycle', 'icon' => '🎬', 'label' => 'scene', 'now' => 'tap to cycle scenes', 'send' => 'lights next'],
['key' => 'dimmer', 'kind' => 'dimmer', 'icon' => '🔆', 'label' => 'dim', 'now' => 'tap −10% · hold = off', 'send' => 'lights dim', 'hold' => 'lights off'],
['key' => 'ac', 'kind' => 'toggle', 'icon' => '❄️',
'on' => ['when' => 'ON', 'label' => 'AC off', 'now' => 'AC is on', 'send' => 'ac off'],
'off' => ['when' => 'OFF', 'label' => 'AC on', 'now' => 'AC is off', 'send' => 'ac on']],
@@ -43,7 +39,7 @@ final class Buttons {
/** Every line the page may send: the toggles' two sides, the action, and the one read. */
public const LINES = [
'lights day', 'lights night', 'lights on', 'lights off',
'lights day', 'lights night', 'lights on', 'lights off', 'lights next', 'lights dim',
'ac on', 'ac off', 'heater on', 'heater off',
'feed bella',
'state',