Swipe gesture controls, menu system, MQTT alert parameter check

This commit is contained in:
Joan
2025-12-31 13:52:21 +01:00
parent 698b61c1cb
commit 8d110c1416
2 changed files with 150 additions and 31 deletions

164
matrix.py
View File

@@ -146,33 +146,83 @@ class Matrix64Display(SampleBase):
self.mqtt = MQTTListener() self.mqtt = MQTTListener()
self.mqtt.start() self.mqtt.start()
# Menu state
self.menu_mode = False
self.menu_selection = 0
# Swipe gesture detection
self.last_button = None
self.last_button_time = 0
self.swipe_timeout = 0.4 # seconds
# Buttons # Buttons
self.button1 = Button(0, pull_up=True, hold_time=1.5) self.button0 = Button(0, pull_up=True, hold_time=1.0)
self.button2 = Button(1, pull_up=True, hold_time=1.5) self.button1 = Button(1, pull_up=True, hold_time=1.0)
self.button1.when_pressed = self.on_button1_press
self.button1.when_held = self.on_button_held self.button0.when_pressed = lambda: self.on_button_press(0)
self.button2.when_pressed = self.on_button2_press self.button1.when_pressed = lambda: self.on_button_press(1)
self.button2.when_held = self.on_button_held self.button0.when_held = self.on_button0_held
self.button1.when_held = self.on_button1_held
def show_feedback(self, message, duration=2): def show_feedback(self, message, duration=2):
self.feedback_message = message self.feedback_message = message
self.feedback_until = time.time() + duration self.feedback_until = time.time() + duration
def on_button1_press(self): def on_button_press(self, button_id):
if not self.button1.is_held: """Handle button press for swipe detection."""
self.current_view = (self.current_view + 1) % NUM_VIEWS now = time.time()
self.last_view_change = time.time()
self.show_feedback(VIEW_NAMES[self.current_view], 1.5)
def on_button2_press(self): # Check for swipe gesture
if not self.button2.is_held: if self.last_button is not None and (now - self.last_button_time) < self.swipe_timeout:
self.current_view = (self.current_view - 1) % NUM_VIEWS if self.last_button == 0 and button_id == 1:
self.last_view_change = time.time() # Swipe 0→1
self.show_feedback(VIEW_NAMES[self.current_view], 1.5) if self.menu_mode:
self.menu_selection = (self.menu_selection - 1) % NUM_VIEWS # Up
else:
self.current_view = (self.current_view + 1) % NUM_VIEWS # Next
self.last_view_change = now
self.show_feedback(VIEW_NAMES[self.current_view], 1.5)
self.last_button = None
return
elif self.last_button == 1 and button_id == 0:
# Swipe 1→0
if self.menu_mode:
self.menu_selection = (self.menu_selection + 1) % NUM_VIEWS # Down
else:
self.current_view = (self.current_view - 1) % NUM_VIEWS # Prev
self.last_view_change = now
self.show_feedback(VIEW_NAMES[self.current_view], 1.5)
self.last_button = None
return
def on_button_held(self): # Single button press (for menu select)
self.auto_cycle = not self.auto_cycle if self.menu_mode and button_id == 1:
self.show_feedback("AUTO ON" if self.auto_cycle else "AUTO OFF", 2) # Select highlighted view
self.current_view = self.menu_selection
self.menu_mode = False
self.show_feedback(VIEW_NAMES[self.current_view], 1.5)
self.last_button = None
return
self.last_button = button_id
self.last_button_time = now
def on_button0_held(self):
"""Long press button 0 = open menu."""
self.menu_mode = not self.menu_mode
if self.menu_mode:
self.menu_selection = self.current_view
self.show_feedback("Menú", 1)
else:
self.show_feedback("Cerrado", 1)
self.last_button = None
def on_button1_held(self):
"""Long press button 1 = toggle auto-cycle."""
if not self.menu_mode:
self.auto_cycle = not self.auto_cycle
self.show_feedback("AUTO ON" if self.auto_cycle else "AUTO OFF", 2)
self.last_button = None
def update_data(self): def update_data(self):
try: try:
@@ -242,6 +292,57 @@ class Matrix64Display(SampleBase):
x = (64 - msg_len) // 2 x = (64 - msg_len) // 2
graphics.DrawText(canvas, time_font, x, 38, msg_color, self.feedback_message) graphics.DrawText(canvas, time_font, x, 38, msg_color, self.feedback_message)
def draw_menu(self, canvas, fonts, colors):
"""Draw view selection menu with scrolling."""
time_font, data_font, small_font = fonts
time_color, humidity_color, hdd_color, label_color, line_color = colors
# Full black background
for y in range(64):
graphics.DrawLine(canvas, 0, y, 63, y, graphics.Color(0, 0, 0))
# Title
title = "Vistas"
title_len = len(title) * 5
title_x = (64 - title_len) // 2
graphics.DrawText(canvas, data_font, title_x, 8, time_color, title)
graphics.DrawLine(canvas, 0, 10, 63, 10, line_color)
# Menu items (3 visible at a time, selected centered)
visible_items = 4
item_height = 12
start_y = 22
# Calculate scroll offset to keep selection centered
center_slot = visible_items // 2
scroll_offset = max(0, min(self.menu_selection - center_slot, NUM_VIEWS - visible_items))
for i in range(visible_items):
view_idx = scroll_offset + i
if view_idx >= NUM_VIEWS:
break
y = start_y + i * item_height
name = VIEW_NAMES[view_idx]
if view_idx == self.menu_selection:
# Highlight selected
highlight_color = graphics.Color(0, 40, 80)
for hy in range(y - 9, y + 2):
if 0 <= hy < 64:
graphics.DrawLine(canvas, 0, hy, 63, hy, highlight_color)
text_color = graphics.Color(100, 255, 200)
graphics.DrawText(canvas, data_font, 4, y, text_color, "> " + name)
else:
text_color = graphics.Color(100, 100, 100)
graphics.DrawText(canvas, data_font, 8, y, text_color, name)
# Scroll indicators
if scroll_offset > 0:
graphics.DrawText(canvas, small_font, 58, 18, label_color, "^")
if scroll_offset + visible_items < NUM_VIEWS:
graphics.DrawText(canvas, small_font, 58, 62, label_color, "v")
def draw_view_0(self, canvas, fonts, colors): def draw_view_0(self, canvas, fonts, colors):
"""Vista Principal""" """Vista Principal"""
time_font, data_font, small_font = fonts time_font, data_font, small_font = fonts
@@ -439,20 +540,25 @@ class Matrix64Display(SampleBase):
canvas.Clear() canvas.Clear()
if self.current_view == 0: # Menu mode takes priority over views
self.draw_view_0(canvas, fonts, colors) if self.menu_mode:
elif self.current_view == 1: self.draw_menu(canvas, fonts, colors)
self.draw_view_1(canvas, fonts, colors) else:
elif self.current_view == 2: # Draw current view
self.draw_view_2(canvas, fonts, colors) if self.current_view == 0:
elif self.current_view == 3: self.draw_view_0(canvas, fonts, colors)
self.draw_view_3(canvas, fonts, colors) elif self.current_view == 1:
self.draw_view_1(canvas, fonts, colors)
elif self.current_view == 2:
self.draw_view_2(canvas, fonts, colors)
elif self.current_view == 3:
self.draw_view_3(canvas, fonts, colors)
# Camera alert overlay (highest priority) # Camera alert overlay (highest priority)
if self.mqtt.is_alert_active(): if self.mqtt.is_alert_active():
self.draw_camera_alert(canvas, fonts) self.draw_camera_alert(canvas, fonts)
# Feedback overlay # Feedback overlay (only when not in menu)
elif self.feedback_message and current_time < self.feedback_until: elif not self.menu_mode and self.feedback_message and current_time < self.feedback_until:
self.draw_feedback(canvas, fonts, colors) self.draw_feedback(canvas, fonts, colors)
else: else:
self.feedback_message = None self.feedback_message = None

View File

@@ -26,9 +26,22 @@ class MQTTListener:
print(f"MQTT connection failed: {rc}") print(f"MQTT connection failed: {rc}")
def on_message(self, client, userdata, msg): def on_message(self, client, userdata, msg):
print(f"MQTT Alert received: {msg.topic}") print(f"MQTT message received: {msg.topic}")
self.alert_active = True try:
self.alert_until = time.time() + self.alert_duration import json
payload = json.loads(msg.payload.decode())
if payload.get("alert") == True:
print("Camera alert triggered")
self.alert_active = True
self.alert_until = time.time() + self.alert_duration
elif payload.get("alert") == False:
print("Camera alert cleared")
# Don't immediately clear, let it timeout naturally
except:
# Fallback: if not JSON or parsing fails, still trigger alert
print("MQTT payload parse failed, triggering alert anyway")
self.alert_active = True
self.alert_until = time.time() + self.alert_duration
def on_disconnect(self, client, userdata, rc): def on_disconnect(self, client, userdata, rc):
print("MQTT disconnected") print("MQTT disconnected")