-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlightsd.py
More file actions
executable file
·125 lines (95 loc) · 3.1 KB
/
lightsd.py
File metadata and controls
executable file
·125 lines (95 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python3
import asyncio
import json
import time
from pathlib import Path
from pywizlight import wizlight, PilotBuilder
# ---------------- CONFIG ----------------
IPS = [
"192.168.86.123",
"192.168.86.124",
]
COMMAND_FILE = Path.home() / ".lightsd" / "command.json"
LOG_FILE = Path.home() / ".lightsd" / "lightsd.log"
PRESETS = {
"warm": PilotBuilder(brightness=180, colortemp=2700),
"cool": PilotBuilder(brightness=220, colortemp=5000),
"night": PilotBuilder(brightness=30, colortemp=2200),
"sunset": PilotBuilder(brightness=120, rgb=(255,120,40)),
}
POLL_INTERVAL = 1.0 # seconds
# ---------------- HELPERS ----------------
def log(msg):
LOG_FILE.parent.mkdir(exist_ok=True)
with LOG_FILE.open("a") as f:
f.write(f"{time.strftime('%F %T')} {msg}\n")
async def get_bulbs():
return [wizlight(ip) for ip in IPS]
async def close_all(bulbs):
for b in bulbs:
await b.async_close()
# ---------------- ACTIONS ----------------
async def fade_to(mode, seconds):
bulbs = await get_bulbs()
target = PRESETS[mode]
steps = max(int(seconds * 10), 1)
delay = seconds / steps
loop = asyncio.get_event_loop()
start_time = loop.time()
try:
states = await asyncio.gather(
*[b.updateState() for b in bulbs]
)
start_bris = [s.get_brightness() or 0 for s in states]
for i in range(steps):
level = (i + 1) / steps
tasks = []
for bulb, start_bri in zip(bulbs, start_bris):
bri = int(start_bri + (target.brightness - start_bri) * level)
tasks.append(
bulb.turn_on(PilotBuilder(brightness=bri))
)
await asyncio.gather(*tasks)
await asyncio.sleep(max(0, start_time + (i + 1) * delay - loop.time()))
await asyncio.gather(
*[b.turn_on(target) for b in bulbs]
)
log(f"FADE completed -> {mode}")
finally:
await close_all(bulbs)
async def apply_preset(mode):
bulbs = await get_bulbs()
try:
for b in bulbs:
await b.turn_on(PRESETS[mode])
log(f"SET {mode}")
finally:
await close_all(bulbs)
async def turn_off():
bulbs = await get_bulbs()
try:
for b in bulbs:
await b.turn_off()
log("OFF")
finally:
await close_all(bulbs)
# ---------------- MAIN LOOP ----------------
async def daemon_loop():
log("lightsd started")
while True:
try:
if COMMAND_FILE.exists():
data = json.loads(COMMAND_FILE.read_text())
COMMAND_FILE.unlink()
action = data.get("action")
if action == "fade":
await fade_to(data["mode"], data["seconds"])
elif action == "set":
await apply_preset(data["mode"])
elif action == "off":
await turn_off()
except Exception as e:
log(f"ERROR {e}")
await asyncio.sleep(POLL_INTERVAL)
if __name__ == "__main__":
asyncio.run(daemon_loop())