diff --git a/.env.example b/.env.example index 636b1f3..1a3109e 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ NTFY_Token= device_name= logging_level=DEBUG -shutdown_timer=30 \ No newline at end of file +shutdown_timer=30 +HA_Token= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2eea525..50a19c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +venv \ No newline at end of file diff --git a/main.py b/main.py index 49ffb79..f5c1876 100644 --- a/main.py +++ b/main.py @@ -30,9 +30,18 @@ if not logger.handlers: ntfy_token = os.environ['NTFY_Token'] device_name = os.environ['device_name'] shutdown_timer = int(os.environ['shutdown_timer']) +ha_token = os.environ['HA_Token'] +#################################################### +#Globals running = True +inverter_online = True +last_online = time.monotonic() + +grid_online = True + + def handle_shutdown(signum, frame): global running logger.info("Received shutdown signal, exiting...") @@ -41,41 +50,97 @@ def handle_shutdown(signum, frame): signal.signal(signal.SIGTERM, handle_shutdown) signal.signal(signal.SIGINT, handle_shutdown) +def send_message(title: str, text: str): + try: + requests.post("https://ntfy.fieryeagle.org/Internet-Alerts", + data=text.encode("utf-8"), + headers={ + "Title": title, + "Authorization": f"Bearer {ntfy_token}" + }, + timeout=5) + except Exception as e: + logger.error(f"Failed to send notification: {e}") -def main(): - with serial.Serial(device_name, 115200, timeout=1) as ser: - shutdown_triggered = False +def check_inverter(status: bool): + global inverter_online + global last_online + + if status: + logger.debug("Inverter Online") inverter_online = True last_online = time.monotonic() - receiving_data = True - last_logged = -1 - try: + return True + + else: + if inverter_online: + logger.info("Running on UPS reserve") + inverter_online = False + return False + +def check_grid(status: bool): + global grid_online + + if status: + logger.debug("Grid Online") + if not grid_online: + logger.info("Grid restored") + send_message("Grid back online", "Systems restored") + try: + requests.post("https://home.fieryeagle.org/api/states/binary_sensor.grid_power", + headers={ + "Authorization": f"Bearer {ha_token}", + "Content-Type": "application/json" + }, + json={"state": "on"}, + timeout=5 + ) + except Exception as e: + logger.error(f"Could not update HA sensor: {e}") + grid_online = True + return True + + else: + if grid_online: + logger.info("Running on Inverter reserve") + send_message("Grid power offline", "Running on Inverter reserve") + try: + requests.post("https://home.fieryeagle.org/api/states/binary_sensor.grid_power", + headers={ + "Authorization": f"Bearer {ha_token}", + "Content-Type": "application/json" + }, + json={"state": "off"}, + timeout=5 + ) + except Exception as e: + logger.error(f"Could not update HA sensor: {e}") + grid_online = False + return False + +def main(): + shutdown_triggered = False + try: + with serial.Serial(device_name, 115200, timeout=1) as ser: + global last_online + + last_logged = -1 + while running: line = ser.readline().decode(errors="ignore").strip() + if line == "": + logger.warning("No serial data") + continue - if line != "" and not receiving_data: - receiving_data = True - logger.info("Serial data restored") + if line not in ("00", "01", "10", "11"): + logger.warning(f"Invalid serial data: {line}") + continue - if line == "0": - logger.debug("Inverter Online") - if not inverter_online: - logger.info("Inverter restored") - inverter_online = True - last_online = time.monotonic() + inverter_status = line[0] == "0" + grid_status = line[1] == "0" - elif line == "1": - if inverter_online: - inverter_online = False - logger.info("Running on UPS reserve") - - elif line == "": - if receiving_data: - receiving_data = False - logger.warning("No serial data") - - else: - logger.warning(f"Unexpected serial data: {line}") + check_inverter(inverter_status) + check_grid(grid_status) if not inverter_online: sec = int(time.monotonic() - last_online) @@ -83,30 +148,20 @@ def main(): if sec != last_logged: logger.info(f"Offline for {sec}s") last_logged = sec - + if sec >= shutdown_timer: shutdown_triggered = True break - if shutdown_triggered: - logger.info(f"Inverter offline for {shutdown_timer} seconds") - try: - requests.post( - "https://ntfy.fieryeagle.org/Internet-Alerts", - data="Inverter offline, shutting down".encode("utf-8"), - headers={ - "Title": "Hydrogen running on reserve power", - "Authorization": f"Bearer {ntfy_token}" - }, - timeout=5 - ) - except Exception as e: - logger.error(f"Failed to send notification: {e}") - os.system("shutdown now") - else: - logger.info("Exited cleanly (service stop)") - except serial.SerialException as e: - logger.error(f"Serial device error: {e}") - raise + except serial.SerialException as e: + logger.error(f"Serial error: {e}") + + if shutdown_triggered: + logger.info(f"Inverter offline for {shutdown_timer} seconds") + send_message("Inverter offline", "Hydrogen running on reserve power, shutting down") + os.system("shutdown now") + else: + logger.info("Exited cleanly (service stop)") + if __name__ == "__main__": main() \ No newline at end of file