Skip to content

Commit

Permalink
Add LED transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexwohlbruck committed May 3, 2022
1 parent e70fa17 commit f7c9a83
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 41 deletions.
6 changes: 4 additions & 2 deletions client/src/views/Lamps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ v-container
v-img.mt-5.mb-10(src='@/assets/undraw_signal_searching.svg' width='300')


.d-flex.flex-column.align-center(v-else)
.d-flex.flex-column.align-center(v-else style='gap: 1em;')
v-card.lamp-card.mb-4.pa-6.small-container(
v-for='(group, groupId) in groups'
:key='groupId'
:style='`background: ${gradient(group.group.state)}; box-shadow: ${shadow(group.group.state)};`'
)
.d-flex.flex-column.mb-4
.text-h6 {{ group.group.groupId }}
.text-subtitle-2 {{ group.lamps.length }} {{ group.lamps.length === 1 ? 'lamp' : 'lamps' }}
.text-subtitle-2
| {{ group.lamps.length }} {{ group.lamps.length === 1 ? 'lamp' : 'lamps' }}
| - {{ group.group.accessCode }}

lamp(v-for='lamp in group.lamps' :key='lamp._id' :lamp='lamp')

Expand Down
29 changes: 20 additions & 9 deletions micro/app/commander.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from app.config import reset_config, add_config, load_config
from machine import reset
from app.led import set_color, turn_off, set, get_color_gradient, rgb_to_hue, hex_to_rgb, polylinear_gradient, rgb_to_hex, kelvin_to_rgb
from app.led import set_color, turn_off, transition_to, get_color_gradient, rgb_to_hue, hex_to_rgb, polylinear_gradient, rgb_to_hex, kelvin_to_rgb
from app.server import start_server
from time import sleep
from time import sleep, ticks_ms

# User defined config variables
BRIGHTNESS = 1
NIGHT_MODE = True
MOTION_DETECTION = True
Expand All @@ -17,6 +18,7 @@

last_room_is_lit = True
last_motion_detected = True
time_since_interactive = ticks_ms()

message_queue = []

Expand Down Expand Up @@ -70,18 +72,20 @@ def deactivate(color=(0,0,0)):
def activate_local():
global last_colors
state = state_from_color_list(last_colors, brightness=BRIGHTNESS)
set(state)
# set(state)
transition_to(state, 50)

# Deactivate LEDs from last stored colors
def deactivate_local():
global last_colors
global reading_light_on
if reading_light_on:
turn_on_reading_light(last_colors)
turn_on_reading_light()
else:
print(last_colors)
state = state_from_color_list(last_colors, BRIGHTNESS * .05)
set(state)
# set(state)
transition_to(state, 10)

def turn_on_reading_light():
global READING_LIGHT_COLOR_TEMPERATURE
Expand All @@ -95,7 +99,7 @@ def turn_off_reading_light():
print('off')
global reading_light_on
reading_light_on = False
turn_off()
deactivate_local()

def toggle_reading_light():
global reading_light_on
Expand All @@ -106,7 +110,6 @@ def toggle_reading_light():

# Triggered by input.py when the room becomes bright
def room_is_lit(val):
print('room is lit' + str(val))
global last_room_is_lit
last_room_is_lit = val

Expand All @@ -119,17 +122,25 @@ def room_is_lit(val):

# Triggered by input.py when motion is detected
def motion_detected(val):
print('motion detected' + str(val))
global last_motion_detected
last_motion_detected = val
dequeue()

# Triggered by input.py when user is interacting with device
def user_is_interacting():
global time_since_interactive
time_since_interactive = ticks_ms()

# If room is lid and motion detected, dequeue pending messages
def dequeue():
global last_room_is_lit
global last_motion_detected
print(last_room_is_lit, last_motion_detected)
if last_room_is_lit and last_motion_detected:

# Check if user has interacted with device in the last 30 seconds
user_has_interacted = ticks_ms() - time_since_interactive < 30000

if user_has_interacted or (last_room_is_lit and last_motion_detected):
# Wait 1 second between dequeuing
while len(message_queue) > 0:
message = message_queue.pop(0)
Expand Down
15 changes: 11 additions & 4 deletions micro/app/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from machine import Pin, ADC
from app.rotary.rotary_irq_esp import RotaryIRQ
from app.led import set_color, rgb_to_hex, hsl_to_rgb, turn_off
from app.commander import activate, toggle_reading_light, deactivate, factory_reset, room_is_lit, motion_detected
from app.commander import activate, toggle_reading_light, deactivate, factory_reset, room_is_lit, motion_detected, user_is_interacting, MINIMUM_LIGHT_LEVEL

ROTARY_STEPS = 32
LIGHT_SENSITIVITY = 3400
# LIGHT_SENSITIVITY = 3400
LIGHT_SENSITIVITY_MAX = 4095
LIGHT_SENSITIVITY_MIN = 100
SENSITIVITY_RANGE = 100
OUTLIER_RANGE = 35
DOUBLE_PRESS_WAIT = 350 # Usually 500, I like it a little quicker
Expand Down Expand Up @@ -67,6 +69,7 @@ def input_watcher():

# Pushbutton
if pushbutton_new != pushbutton_old:
user_is_interacting()
if pushbutton_new == 0:
# Pressed
pressed_time = ticks_ms()
Expand Down Expand Up @@ -104,6 +107,7 @@ def input_watcher():

# Rotary input
if rotary_old != rotary_new:
user_is_interacting()
rotary_old = rotary_new
val = int((rotary_new / ROTARY_STEPS) * 360)
last_color = hsl_to_rgb(val, 1, 0.5)
Expand All @@ -118,6 +122,7 @@ def input_watcher():

# Light sensor
light_values.append(light_new)
# print(light_new)
if len(light_values) > light_avg_max:
light_values.pop(0)

Expand All @@ -127,11 +132,13 @@ def input_watcher():
if light_new <= light_avg + OUTLIER_RANGE and light_new >= light_avg - OUTLIER_RANGE:

# # If light level passes sensitivity threshold, update reads_dark
if reads_dark and light_new > (LIGHT_SENSITIVITY + SENSITIVITY_RANGE):
threshold = ((LIGHT_SENSITIVITY_MAX - LIGHT_SENSITIVITY_MIN) * MINIMUM_LIGHT_LEVEL) + LIGHT_SENSITIVITY_MIN

if reads_dark and light_new > (threshold + SENSITIVITY_RANGE):
reads_dark = False
past_threshold = True

elif not reads_dark and light_new < (LIGHT_SENSITIVITY - SENSITIVITY_RANGE):
elif not reads_dark and light_new < (threshold - SENSITIVITY_RANGE):
reads_dark = True
light_time = ticks_ms()
past_threshold = True
Expand Down
52 changes: 30 additions & 22 deletions micro/app/led.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,17 +376,22 @@ def set(state):
np.write()

# TODO: Fade new color
def set_color(color, brightness=None, top=False):
def set_color(color, brightness=None, top=False, duration=100):
global effect
effect = None
brightness = brightness or DEFAULT_BRIGHTNESS

# If top is set, get top eighth of leds
leds = range((led_count // 8) * 7, led_count) if top else range(led_count)

state = copy()
for i in leds:
np[i] = tuple(int(p * brightness) for p in color)
np.write()
state[i] = tuple(int(p * brightness) for p in color)

if duration == 0 or top:
set(state)
else:
transition_to(state, duration)
# start_new_thread(transition_to, (state, duration))

def turn_off():
set([(0,0,0)] * led_count)
Expand All @@ -413,25 +418,28 @@ def set_gradient(colors, fade=True):

## Effects

# # TODO: Transition from state1 to state 2 with a smooth fade
# # TODO: Transition from state1 to state 2 with a smooth fade for X milliseconds
# # state: list of rgb tuples [(R, G, B)]
# def transition(state1, state2, duration):
# step_duration = 1 # duration of each step in ms
# global effect
# effect = EFFECT_TRANSITION
# state1 = state1 or copy()
# state2 = state2 or copy()

# steps = int(duration * 1000 / step_duration)

# for i in range(steps):
# progress = i / steps
# for j in range(led_count):
# state1[j] = tuple(int(s1 + (s2 - s1) * progress) for s1, s2 in zip(state1[j], state2[j]))
# set(state1)
# sleep_ms(step_duration)
# set(state2)
# effect = None
def transition(state1, state2, duration):
step_duration = 1 # duration of each step in ms
global effect
effect = EFFECT_TRANSITION
state1 = state1 or copy()
state2 = state2 or copy()

steps = int(duration / step_duration)

for i in range(steps):
progress = i / steps
for j in range(led_count):
state1[j] = tuple(int(s1 + (s2 - s1) * progress) for s1, s2 in zip(state1[j], state2[j]))
set(state1)
sleep_ms(step_duration)
set(state2)
effect = None

def transition_to(state, duration):
transition(copy(), state, duration)


# Turn off all LEDs and then fade them in
Expand Down
5 changes: 5 additions & 0 deletions micro/app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
import app.uwebsockets.client as wsclient
from app.config import get_device_id, get_config_item, load_config
from app.wifi import disconnect_wifi
from app.led import set_color, pulse

MAX_RECONNECT_ATTEMPTS = 5
color = (0, 255, 0)

class WebSocket():
def __init__(self, uri, callback):
set_color(color)
pulse()
print('Connecting to server')
self.uri = uri
self.callback = callback
self.reconnect_attempts = 0
self.connect()
set_color(color)

def connect(self):
self.websocket = wsclient.connect(self. uri)
Expand Down
8 changes: 5 additions & 3 deletions micro/app/wifi.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import network
from time import sleep
from app.config import load_config, get_config_item, add_config
from app.led import flash, pulse, set_color
from app.led import pulse, set_color

sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
color = (255, 50, 0)

def scan_wifi():
wifis = sta_if.scan()
Expand Down Expand Up @@ -60,7 +61,8 @@ def connect_wifi_from_config():
# Returns True if the connection was successful
def connect_wifi(ssid, password):
print('Connecting to ' + ssid + '...')
set_color((255, 50, 0))
set_color(color)
pulse()

sta_if.connect(ssid, password)

Expand All @@ -76,7 +78,7 @@ def connect_wifi(ssid, password):

else:
print('Successfully connected to ' + ssid + '\n')
flash()
set_color(color)

wifis = get_config_item('wifi')

Expand Down
Binary file added other-files/graphics/Logo full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion server/services/lamps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ export const sendCommand = async (lampId: string, state: LampState) => {
data: response
})

await lamp.save() // Save in background
await lamp.save()

return response
}
Expand Down
9 changes: 9 additions & 0 deletions server/websockets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ router.ws('/', async (ws: WebSocket, req: express.Request) => {
}
}

// TODO: Send current group state when lamp connects for the first time
// ws.send(JSON.stringify({
// name: 'GROUP_STATE_CHANGED',
// data: {
// groupId,
// state: newState,
// }
// }))

// Handle incoming messages with event handlers
ws.on('message', (message: any) => {
try {
Expand Down

0 comments on commit f7c9a83

Please sign in to comment.