Merge pull request #2757 from adafruit/bird_feeder
Code for elgato light controller
This commit is contained in:
commit
69c50a5653
1 changed files with 241 additions and 0 deletions
241
Elgato_WiFi_Light_Controller/code.py
Normal file
241
Elgato_WiFi_Light_Controller/code.py
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import os
|
||||
import ssl
|
||||
import wifi
|
||||
import socketpool
|
||||
import board
|
||||
import digitalio
|
||||
import displayio
|
||||
import adafruit_requests
|
||||
from adafruit_bitmap_font import bitmap_font
|
||||
from adafruit_display_shapes.circle import Circle
|
||||
from adafruit_display_text import bitmap_label
|
||||
from adafruit_seesaw import seesaw, rotaryio, digitalio as seesaw_digitalio, neopixel
|
||||
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
|
||||
|
||||
num_lights = 1
|
||||
light = os.getenv('ELGATO_LIGHT')
|
||||
clock_clock = ticks_ms()
|
||||
clock_timer = 3 * 1000
|
||||
|
||||
i2c = board.I2C() # uses board.SCL and board.SDA
|
||||
seesaw = seesaw.Seesaw(i2c, addr=0x36)
|
||||
encoder = rotaryio.IncrementalEncoder(seesaw)
|
||||
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
|
||||
switch = seesaw_digitalio.DigitalIO(seesaw, 24)
|
||||
switch_state = False
|
||||
pixel = neopixel.NeoPixel(seesaw, 6, 1)
|
||||
pixel.brightness = 0.2
|
||||
pixel.fill((255, 0, 0))
|
||||
print()
|
||||
print("Connecting to WiFi")
|
||||
# connect to your SSID
|
||||
try:
|
||||
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
|
||||
# pylint: disable = broad-except
|
||||
except Exception:
|
||||
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
|
||||
print("Connected to WiFi")
|
||||
pixel.fill((0, 0, 255))
|
||||
|
||||
pool = socketpool.SocketPool(wifi.radio)
|
||||
requests = adafruit_requests.Session(pool, ssl.create_default_context())
|
||||
|
||||
# buttons
|
||||
button0 = digitalio.DigitalInOut(board.D0)
|
||||
button0.direction = digitalio.Direction.INPUT
|
||||
button0.pull = digitalio.Pull.UP
|
||||
button0_state = False
|
||||
button1 = digitalio.DigitalInOut(board.D1)
|
||||
button1.direction = digitalio.Direction.INPUT
|
||||
button1.pull = digitalio.Pull.DOWN
|
||||
button1_state = False
|
||||
button2 = digitalio.DigitalInOut(board.D2)
|
||||
button2.direction = digitalio.Direction.INPUT
|
||||
button2.pull = digitalio.Pull.DOWN
|
||||
button2_state = False
|
||||
|
||||
group = displayio.Group()
|
||||
board.DISPLAY.root_group = group
|
||||
|
||||
# font for graphics
|
||||
sm_file = "/roundedHeavy-26.bdf"
|
||||
sm_font = bitmap_font.load_font(sm_file)
|
||||
# font for text only
|
||||
lg_file = "/roundedHeavy-46.bdf"
|
||||
lg_font = bitmap_font.load_font(lg_file)
|
||||
http_text = bitmap_label.Label(sm_font, text=" ")
|
||||
http_text.anchor_point = (1.0, 0.0)
|
||||
http_text.anchored_position = (board.DISPLAY.width, 0)
|
||||
group.append(http_text)
|
||||
status_text = bitmap_label.Label(sm_font, text=" ")
|
||||
status_text.anchor_point = (0.0, 0.5)
|
||||
status_text.anchored_position = (0, board.DISPLAY.height / 2)
|
||||
group.append(status_text)
|
||||
temp_text = bitmap_label.Label(lg_font, text=" K")
|
||||
temp_text.anchor_point = (1.0, 0.5)
|
||||
temp_text.anchored_position = (board.DISPLAY.width, board.DISPLAY.height / 2)
|
||||
group.append(temp_text)
|
||||
bright_text = bitmap_label.Label(lg_font, text=" %", x=board.DISPLAY.width//2, y=90)
|
||||
bright_text.anchor_point = (1.0, 1.0)
|
||||
bright_text.anchored_position = (board.DISPLAY.width, board.DISPLAY.height - 15)
|
||||
group.append(bright_text)
|
||||
onOff_circ = Circle(12, 12, 10, fill=None, stroke = 2, outline = 0xcccc00)
|
||||
group.append(onOff_circ)
|
||||
|
||||
def kelvin_to_elgato(value):
|
||||
t = value * 0.05
|
||||
t = max(min(344, int(t)), 143)
|
||||
return t
|
||||
|
||||
def elgato_to_kelvin(value):
|
||||
t = value / 0.05
|
||||
return t
|
||||
|
||||
def ctrl_light(b, t, onOff):
|
||||
url = f"http://{light}:9123/elgato/lights"
|
||||
json = {"numberOfLights":num_lights,"lights":[{"on":onOff,"brightness":b,"temperature":t}]}
|
||||
print(f"PUTting data to {url}: {json}")
|
||||
status_text.text = "sending.."
|
||||
for i in range(5):
|
||||
try:
|
||||
pixel.fill((0, 255, 0))
|
||||
r = requests.request(method="PUT", url=url, data=None, json=json,
|
||||
headers = {'Content-Type': 'application/json'}, timeout=10)
|
||||
print("-" * 40)
|
||||
print(r.status_code)
|
||||
# if PUT fails, try again
|
||||
if r.status_code != 200:
|
||||
status_text.text = "..sending.."
|
||||
pixel.fill((255, 255, 0))
|
||||
time.sleep(2)
|
||||
r = requests.request(method="PUT", url=url, data=None, json=json,
|
||||
headers = {'Content-Type': 'application/json'}, timeout=10)
|
||||
if r.status_code != 200:
|
||||
pixel.fill((255, 0, 0))
|
||||
except Exception:
|
||||
pixel.fill((255, 0, 0))
|
||||
time.sleep(2)
|
||||
if i < 5 - 1:
|
||||
continue
|
||||
raise
|
||||
break
|
||||
status_text.text = "sent!"
|
||||
light_indicator(onOff)
|
||||
pixel.fill((255, 0, 255))
|
||||
|
||||
def read_light():
|
||||
status_text.text = "reading.."
|
||||
for i in range(5):
|
||||
try:
|
||||
pixel.fill((0, 255, 0))
|
||||
r = requests.get(f"http://{light}:9123/elgato/lights")
|
||||
j = r.json()
|
||||
if r.status_code != 200:
|
||||
status_text.text = "..reading.."
|
||||
pixel.fill((255, 255, 0))
|
||||
time.sleep(2)
|
||||
r = requests.get(f"http://{light}:9123/elgato/lights")
|
||||
j = r.json()
|
||||
if r.status_code != 200:
|
||||
pixel.fill((255, 0, 0))
|
||||
except Exception:
|
||||
pixel.fill((255, 0, 0))
|
||||
time.sleep(2)
|
||||
if i < 5 - 1:
|
||||
continue
|
||||
raise
|
||||
break
|
||||
status_text.text = "read!"
|
||||
pixel.fill((255, 0, 255))
|
||||
onOff = j['lights'][0]['on']
|
||||
light_indicator(onOff)
|
||||
b = round(j['lights'][0]['brightness'] / 10) * 10
|
||||
bright_text.text = f"{b}%"
|
||||
t = j['lights'][0]['temperature']
|
||||
color_t = round(elgato_to_kelvin(t) / 100) * 100
|
||||
temp_text.text = f"{color_t}K"
|
||||
return b, color_t, t, onOff
|
||||
|
||||
def light_indicator(onOff):
|
||||
if onOff:
|
||||
onOff_circ.fill = 0xcccc00
|
||||
else:
|
||||
onOff_circ.fill = None
|
||||
|
||||
selected_light = 0
|
||||
adjust_temp = True
|
||||
last_position = 0
|
||||
|
||||
http_text.text = f"{light}"
|
||||
# get on/off state of light on start-up
|
||||
try:
|
||||
brightness, color_temp, temp, light_on = read_light()
|
||||
except Exception:
|
||||
print()
|
||||
print("Could not find your Elgato light on the network..")
|
||||
print("Make sure it is powered on and that its IP address is correct in settings.toml.")
|
||||
print()
|
||||
raise
|
||||
|
||||
while True:
|
||||
position = encoder.position
|
||||
# reset button state on release
|
||||
if not switch.value and switch_state:
|
||||
switch_state = False
|
||||
if button0.value and button0_state:
|
||||
button0_state = False
|
||||
if not button1.value and button1_state:
|
||||
button1_state = False
|
||||
if not button2.value and button2_state:
|
||||
button2_state = False
|
||||
|
||||
if position != last_position:
|
||||
if position > last_position:
|
||||
if adjust_temp:
|
||||
color_temp = color_temp + 100
|
||||
color_temp = max(min(7000, color_temp), 2900)
|
||||
temp_text.text = f"{color_temp}K"
|
||||
else:
|
||||
brightness = brightness + 10
|
||||
brightness = max(min(100, brightness), 10)
|
||||
bright_text.text = f"{brightness}%"
|
||||
else:
|
||||
if adjust_temp:
|
||||
color_temp = color_temp - 100
|
||||
color_temp = max(min(7000, color_temp), 2900)
|
||||
temp_text.text = f"{color_temp}K"
|
||||
else:
|
||||
brightness = brightness - 10
|
||||
brightness = max(min(100, brightness), 10)
|
||||
bright_text.text = f"{brightness}%"
|
||||
temp = kelvin_to_elgato(color_temp)
|
||||
last_position = position
|
||||
# switch between adjusting color temp or brightness
|
||||
if switch.value and not switch_state:
|
||||
switch_state = True
|
||||
adjust_temp = not adjust_temp
|
||||
# toggle light on/off
|
||||
if not button0.value and not button0_state:
|
||||
button0_state = True
|
||||
light_on = not light_on
|
||||
ctrl_light(brightness, temp, light_on)
|
||||
clock_clock = ticks_add(clock_clock, clock_timer)
|
||||
# update brightness/temp
|
||||
if button1.value and not button1_state:
|
||||
button1_state = True
|
||||
light_on = True
|
||||
ctrl_light(brightness, temp, light_on)
|
||||
clock_clock = ticks_add(clock_clock, clock_timer)
|
||||
# check values
|
||||
if button2.value and not button2_state:
|
||||
button2_state = True
|
||||
brightness, color_temp, temp, light_on = read_light()
|
||||
clock_clock = ticks_add(clock_clock, clock_timer)
|
||||
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
|
||||
status_text.text = "Connected"
|
||||
clock_clock = ticks_add(clock_clock, clock_timer)
|
||||
Loading…
Reference in a new issue