aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-29 16:56:08 +0100
committerDanilo M. <danix@danix.xyz>2026-10-29 16:56:08 +0100
commit8657d6c72ee10380f9af61e224050b7d1623e686 (patch)
tree327d771e6d5990579f83e170f2575aadb8648fe5 /bin
parent66f8c77b853bb67166aac8ca9c3b31abe6468426 (diff)
downloadunified-desktop-theme-8657d6c72ee10380f9af61e224050b7d1623e686.tar.gz
unified-desktop-theme-8657d6c72ee10380f9af61e224050b7d1623e686.zip
bulb: `on` waits up to 30 s for the gateway
The bulb stayed off after a resume while the LED strip came back. hypridle's after_sleep_cmd runs `udt-bulb on` the moment the resume finishes, and the wired link only came up 4 s later (16:52:22 resume, 16:52:26 eth0 carrier in the log). A request sent before that fails at once, and udt-bulb gave up after one try; the strip survived because a failed cached address makes it listen for its broadcast for up to 20 s. `on` now polls the first bulb every 2 s until the gateway answers, for up to 30 s, which also covers the same race at login. `off` still makes a single attempt, since it runs as the session ends and cannot wait. The cause is read from timing, not observed: nothing logs what the after_sleep_cmd did. The next resume confirms it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'bin')
-rwxr-xr-xbin/udt-bulb35
1 files changed, 35 insertions, 0 deletions
diff --git a/bin/udt-bulb b/bin/udt-bulb
index b2eddf5..eee17bc 100755
--- a/bin/udt-bulb
+++ b/bin/udt-bulb
@@ -30,6 +30,7 @@ import secrets
import shutil
import subprocess
import sys
+import time
from pathlib import Path
CONFIG = Path.home() / ".config/udt/tradfri.json"
@@ -65,6 +66,22 @@ def coap(method, url, ident, key, payload=None):
return out.strip()
+def wait_for_gateway(url, auth, seconds, now=time.monotonic, nap=time.sleep):
+ """True once a get on url answers, False if it has not by `seconds`.
+
+ For `on`, which runs straight after a resume and at login: the wired link
+ came up 4 s after the resume finished, and a request sent before that
+ fails at once, with nothing to try again. `off` never waits, since it runs
+ as the session ends.
+ """
+ end = now() + seconds
+ while coap("get", url, *auth) is None:
+ if now() >= end:
+ return False
+ nap(2)
+ return True
+
+
def light_states(colour):
"""3311 states for #rrggbb, sent in order."""
h, s, _ = colorsys.rgb_to_hsv(*(int(colour[i:i + 2], 16) / 255 for i in (1, 3, 5)))
@@ -102,6 +119,20 @@ def selftest():
# A pastel keeps its hue and loses its paleness to the floor.
colour = light_states("#8080ff")[0]
assert (colour["5707"], colour["5708"]) == (round(HUE_MAX * 2 / 3), HUE_MAX)
+
+ # The wait for the gateway retries until it answers, and gives up at the
+ # deadline. coap and the clock are swapped for fakes.
+ global coap
+ real, answers, naps = coap, [None, None, "{}"], []
+ coap = lambda *a: answers.pop(0)
+ clock = iter(range(100))
+ try:
+ assert wait_for_gateway("u", ("i", "k"), 30, lambda: next(clock), naps.append)
+ assert len(naps) == 2, naps
+ coap = lambda *a: None
+ assert not wait_for_gateway("u", ("i", "k"), 3, lambda: next(clock), naps.append)
+ finally:
+ coap = real
print("ok")
@@ -132,6 +163,10 @@ def main():
else:
return
auth = cfg["identity"], cfg["key"]
+ first = f"coaps://{cfg['host']}:5684/{DEVICES}/{cfg['bulbs'][0]}"
+ if args == ["on"] and not wait_for_gateway(first, auth, 30):
+ print("udt-bulb: the gateway did not answer for 30 s", file=sys.stderr)
+ return
for dev in cfg["bulbs"]:
url = f"coaps://{cfg['host']}:5684/{DEVICES}/{dev}"
if not args: