Skip to content

Commit 69c50a5

Browse files
authored
Merge pull request #2757 from adafruit/bird_feeder
Code for elgato light controller
2 parents 6f5d246 + 403d536 commit 69c50a5

File tree

1 file changed

+241
-0
lines changed
  • Elgato_WiFi_Light_Controller

1 file changed

+241
-0
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import time
6+
import os
7+
import ssl
8+
import wifi
9+
import socketpool
10+
import board
11+
import digitalio
12+
import displayio
13+
import adafruit_requests
14+
from adafruit_bitmap_font import bitmap_font
15+
from adafruit_display_shapes.circle import Circle
16+
from adafruit_display_text import bitmap_label
17+
from adafruit_seesaw import seesaw, rotaryio, digitalio as seesaw_digitalio, neopixel
18+
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
19+
20+
num_lights = 1
21+
light = os.getenv('ELGATO_LIGHT')
22+
clock_clock = ticks_ms()
23+
clock_timer = 3 * 1000
24+
25+
i2c = board.I2C() # uses board.SCL and board.SDA
26+
seesaw = seesaw.Seesaw(i2c, addr=0x36)
27+
encoder = rotaryio.IncrementalEncoder(seesaw)
28+
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
29+
switch = seesaw_digitalio.DigitalIO(seesaw, 24)
30+
switch_state = False
31+
pixel = neopixel.NeoPixel(seesaw, 6, 1)
32+
pixel.brightness = 0.2
33+
pixel.fill((255, 0, 0))
34+
print()
35+
print("Connecting to WiFi")
36+
# connect to your SSID
37+
try:
38+
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
39+
# pylint: disable = broad-except
40+
except Exception:
41+
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
42+
print("Connected to WiFi")
43+
pixel.fill((0, 0, 255))
44+
45+
pool = socketpool.SocketPool(wifi.radio)
46+
requests = adafruit_requests.Session(pool, ssl.create_default_context())
47+
48+
# buttons
49+
button0 = digitalio.DigitalInOut(board.D0)
50+
button0.direction = digitalio.Direction.INPUT
51+
button0.pull = digitalio.Pull.UP
52+
button0_state = False
53+
button1 = digitalio.DigitalInOut(board.D1)
54+
button1.direction = digitalio.Direction.INPUT
55+
button1.pull = digitalio.Pull.DOWN
56+
button1_state = False
57+
button2 = digitalio.DigitalInOut(board.D2)
58+
button2.direction = digitalio.Direction.INPUT
59+
button2.pull = digitalio.Pull.DOWN
60+
button2_state = False
61+
62+
group = displayio.Group()
63+
board.DISPLAY.root_group = group
64+
65+
# font for graphics
66+
sm_file = "/roundedHeavy-26.bdf"
67+
sm_font = bitmap_font.load_font(sm_file)
68+
# font for text only
69+
lg_file = "/roundedHeavy-46.bdf"
70+
lg_font = bitmap_font.load_font(lg_file)
71+
http_text = bitmap_label.Label(sm_font, text=" ")
72+
http_text.anchor_point = (1.0, 0.0)
73+
http_text.anchored_position = (board.DISPLAY.width, 0)
74+
group.append(http_text)
75+
status_text = bitmap_label.Label(sm_font, text=" ")
76+
status_text.anchor_point = (0.0, 0.5)
77+
status_text.anchored_position = (0, board.DISPLAY.height / 2)
78+
group.append(status_text)
79+
temp_text = bitmap_label.Label(lg_font, text=" K")
80+
temp_text.anchor_point = (1.0, 0.5)
81+
temp_text.anchored_position = (board.DISPLAY.width, board.DISPLAY.height / 2)
82+
group.append(temp_text)
83+
bright_text = bitmap_label.Label(lg_font, text=" %", x=board.DISPLAY.width//2, y=90)
84+
bright_text.anchor_point = (1.0, 1.0)
85+
bright_text.anchored_position = (board.DISPLAY.width, board.DISPLAY.height - 15)
86+
group.append(bright_text)
87+
onOff_circ = Circle(12, 12, 10, fill=None, stroke = 2, outline = 0xcccc00)
88+
group.append(onOff_circ)
89+
90+
def kelvin_to_elgato(value):
91+
t = value * 0.05
92+
t = max(min(344, int(t)), 143)
93+
return t
94+
95+
def elgato_to_kelvin(value):
96+
t = value / 0.05
97+
return t
98+
99+
def ctrl_light(b, t, onOff):
100+
url = f"http://{light}:9123/elgato/lights"
101+
json = {"numberOfLights":num_lights,"lights":[{"on":onOff,"brightness":b,"temperature":t}]}
102+
print(f"PUTting data to {url}: {json}")
103+
status_text.text = "sending.."
104+
for i in range(5):
105+
try:
106+
pixel.fill((0, 255, 0))
107+
r = requests.request(method="PUT", url=url, data=None, json=json,
108+
headers = {'Content-Type': 'application/json'}, timeout=10)
109+
print("-" * 40)
110+
print(r.status_code)
111+
# if PUT fails, try again
112+
if r.status_code != 200:
113+
status_text.text = "..sending.."
114+
pixel.fill((255, 255, 0))
115+
time.sleep(2)
116+
r = requests.request(method="PUT", url=url, data=None, json=json,
117+
headers = {'Content-Type': 'application/json'}, timeout=10)
118+
if r.status_code != 200:
119+
pixel.fill((255, 0, 0))
120+
except Exception:
121+
pixel.fill((255, 0, 0))
122+
time.sleep(2)
123+
if i < 5 - 1:
124+
continue
125+
raise
126+
break
127+
status_text.text = "sent!"
128+
light_indicator(onOff)
129+
pixel.fill((255, 0, 255))
130+
131+
def read_light():
132+
status_text.text = "reading.."
133+
for i in range(5):
134+
try:
135+
pixel.fill((0, 255, 0))
136+
r = requests.get(f"http://{light}:9123/elgato/lights")
137+
j = r.json()
138+
if r.status_code != 200:
139+
status_text.text = "..reading.."
140+
pixel.fill((255, 255, 0))
141+
time.sleep(2)
142+
r = requests.get(f"http://{light}:9123/elgato/lights")
143+
j = r.json()
144+
if r.status_code != 200:
145+
pixel.fill((255, 0, 0))
146+
except Exception:
147+
pixel.fill((255, 0, 0))
148+
time.sleep(2)
149+
if i < 5 - 1:
150+
continue
151+
raise
152+
break
153+
status_text.text = "read!"
154+
pixel.fill((255, 0, 255))
155+
onOff = j['lights'][0]['on']
156+
light_indicator(onOff)
157+
b = round(j['lights'][0]['brightness'] / 10) * 10
158+
bright_text.text = f"{b}%"
159+
t = j['lights'][0]['temperature']
160+
color_t = round(elgato_to_kelvin(t) / 100) * 100
161+
temp_text.text = f"{color_t}K"
162+
return b, color_t, t, onOff
163+
164+
def light_indicator(onOff):
165+
if onOff:
166+
onOff_circ.fill = 0xcccc00
167+
else:
168+
onOff_circ.fill = None
169+
170+
selected_light = 0
171+
adjust_temp = True
172+
last_position = 0
173+
174+
http_text.text = f"{light}"
175+
# get on/off state of light on start-up
176+
try:
177+
brightness, color_temp, temp, light_on = read_light()
178+
except Exception:
179+
print()
180+
print("Could not find your Elgato light on the network..")
181+
print("Make sure it is powered on and that its IP address is correct in settings.toml.")
182+
print()
183+
raise
184+
185+
while True:
186+
position = encoder.position
187+
# reset button state on release
188+
if not switch.value and switch_state:
189+
switch_state = False
190+
if button0.value and button0_state:
191+
button0_state = False
192+
if not button1.value and button1_state:
193+
button1_state = False
194+
if not button2.value and button2_state:
195+
button2_state = False
196+
197+
if position != last_position:
198+
if position > last_position:
199+
if adjust_temp:
200+
color_temp = color_temp + 100
201+
color_temp = max(min(7000, color_temp), 2900)
202+
temp_text.text = f"{color_temp}K"
203+
else:
204+
brightness = brightness + 10
205+
brightness = max(min(100, brightness), 10)
206+
bright_text.text = f"{brightness}%"
207+
else:
208+
if adjust_temp:
209+
color_temp = color_temp - 100
210+
color_temp = max(min(7000, color_temp), 2900)
211+
temp_text.text = f"{color_temp}K"
212+
else:
213+
brightness = brightness - 10
214+
brightness = max(min(100, brightness), 10)
215+
bright_text.text = f"{brightness}%"
216+
temp = kelvin_to_elgato(color_temp)
217+
last_position = position
218+
# switch between adjusting color temp or brightness
219+
if switch.value and not switch_state:
220+
switch_state = True
221+
adjust_temp = not adjust_temp
222+
# toggle light on/off
223+
if not button0.value and not button0_state:
224+
button0_state = True
225+
light_on = not light_on
226+
ctrl_light(brightness, temp, light_on)
227+
clock_clock = ticks_add(clock_clock, clock_timer)
228+
# update brightness/temp
229+
if button1.value and not button1_state:
230+
button1_state = True
231+
light_on = True
232+
ctrl_light(brightness, temp, light_on)
233+
clock_clock = ticks_add(clock_clock, clock_timer)
234+
# check values
235+
if button2.value and not button2_state:
236+
button2_state = True
237+
brightness, color_temp, temp, light_on = read_light()
238+
clock_clock = ticks_add(clock_clock, clock_timer)
239+
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
240+
status_text.text = "Connected"
241+
clock_clock = ticks_add(clock_clock, clock_timer)

0 commit comments

Comments
 (0)