from time import time from astral import LocationInfo from astral.sun import sun from datetime import date, datetime from zoneinfo import ZoneInfo import requests from grove.gpio import GPIO from grove.display.jhd1802 import JHD1802 shelly_server = "https://shelly-237-eu.shelly.cloud" shelly_token = "M2M5YTYxdWlkEFD82A2301693D62637E530B4CDDC0DC6E2CDD8F01BE5E5102000B92638A2DA74019E6A81E6D17E0" shelly_deviceId = "8cbfea9fd6d0" def setShellyPlugState(deviceId: str, state: bool) -> dict: """ Set the state of a Shelly plug/switch device. Args: deviceId: The Shelly device id state: True to turn on, False to turn off Returns: Response from the API as a dictionary """ url = f"{shelly_server}/v2/devices/api/set/switch" params = {"auth_key": shelly_token} payload = { "id": deviceId, "on": state } response = requests.post(url, params=params, json=payload) if response.status_code == 200: return {"success": True} else: return response.json() city = LocationInfo("Seinäjoki", "Finland", "Europe/Helsinki", 62.7900, 22.8400) s = sun(city.observer, date=date.today()) print(f"City: {city.name}, {city.region}") print(f"Timezone: {city.timezone}") print(f"Latitude: {city.latitude:.6f}; Longitude: {city.longitude:.6f}") def isSunUp() -> bool: now = datetime.now(city.tzinfo) return s["sunrise"] <= now <= s["sunset"] # sunset and sunrise class AlarmClock: ledGpio: GPIO buttonGpio: GPIO relayGpio: GPIO lastKnownButtonState: bool = False lcd: JHD1802 lastButtonPressTime: float = 0.0 lastButtonStateChangeTime: float = 0.0 debounceDelay: float = 0.05 # 50ms debounce delay longPressThreshold: float = 1.0 # 1 second for long press buttonPressStartTime: float = 0.0 configMode: str = "normal" # modes: normal, set_hour, set_minute alarmHour: int = 7 alarmMinute: int = 0 def __init__(self, pins: dict = {}): self.ledGpio = GPIO(pins.get("led", 5), GPIO.OUT) self.buttonGpio = GPIO(pins.get("button", 6), GPIO.IN) self.relayGpio = GPIO(pins.get("relay", 16), GPIO.OUT) self.lcd = JHD1802() self.setLcdText("AlarmClock Init") def start(self): print("AlarmClock started") while True: self.loop() def setRelayState(self, state: bool): self.relayGpio.write(1 if state else 0) def loop(self): currentButtonState = not self.buttonGpio.read() currentTime = time() if (self.configMode == "normal"): currentMinute = datetime.now(ZoneInfo(city.timezone)).minute currentHour = datetime.now(ZoneInfo(city.timezone)).hour currrentSecond = datetime.now(ZoneInfo(city.timezone)).second self.setLcdText(f"Time {currentHour:02d}:{currentMinute:02d}:{currrentSecond:02d}\nAlarm {self.alarmHour:02d}:{self.alarmMinute:02d}") # Only process button state change if debounce delay has passed if currentButtonState != self.lastKnownButtonState: if (currentTime - self.lastButtonStateChangeTime) >= self.debounceDelay: self.lastKnownButtonState = currentButtonState self.lastButtonStateChangeTime = currentTime self.onButtonPress("button", currentButtonState) # Check for long press while button is held if currentButtonState and self.buttonPressStartTime > 0: pressDuration = currentTime - self.buttonPressStartTime if pressDuration >= self.longPressThreshold: self.onLongPress() self.buttonPressStartTime = 0.0 # Reset to prevent repeated triggers def onButtonPress(self, button: str, state: bool): print(f"Button '{button}' pressed state: {state}") currentTime = time() if button == "button": if state: # Button pressed down self.buttonPressStartTime = currentTime else: # Button released if self.buttonPressStartTime > 0: pressDuration = currentTime - self.buttonPressStartTime self.buttonPressStartTime = 0.0 # Only handle as short press if it wasn't a long press if pressDuration < self.longPressThreshold: self.onShortPress() def onShortPress(self): """Handle short button press based on current mode""" if self.configMode == "normal": # Normal mode: toggle relay self.setRelayState(not self.relayGpio.read()) setShellyPlugState(shelly_deviceId, self.relayGpio.read() == 1) elif self.configMode == "set_hour": # Increment hour self.alarmHour = (self.alarmHour + 1) % 24 self.setLcdText(f"Set Hour: {self.alarmHour:02d}") elif self.configMode == "set_minute": # Increment minute self.alarmMinute = (self.alarmMinute + 1) % 60 self.setLcdText(f"Set Min: {self.alarmMinute:02d}") def onLongPress(self): """Handle long button press - cycle through configuration modes""" if self.configMode == "normal": self.configMode = "set_hour" self.setLcdText(f"Set Hour: {self.alarmHour:02d}") print(f"Entering hour setting mode") elif self.configMode == "set_hour": self.configMode = "set_minute" self.setLcdText(f"Set Min: {self.alarmMinute:02d}") print(f"Switching to minute setting mode") elif self.configMode == "set_minute": self.configMode = "normal" self.setLcdText(f"Saved: {self.alarmHour:02d}:{self.alarmMinute:02d}") print(f"Alarm time saved: {self.alarmHour:02d}:{self.alarmMinute:02d}") lastLcdText: str = "" def setLcdText(self, text: str): if text == self.lastLcdText: return self.lastLcdText = text self.lcd.clear() rows = text.split("\n") for i, row in enumerate(rows): if i == 0: self.lcd.setCursor(0, 0) self.lcd.write(f"{(" "+row):<17}") elif i == 1: self.lcd.setCursor(1, 0) self.lcd.write(row) #print(f"LCD: {text}") pins = { "button": 6, "led": 5, "buzzer": 12, } alarm_clock = AlarmClock(pins) alarm_clock.start()