Compare commits

..

6 Commits

3 changed files with 125 additions and 36 deletions

View File

@@ -2,3 +2,4 @@ NTFY_Token=
device_name= device_name=
logging_level=DEBUG logging_level=DEBUG
shutdown_timer=30 shutdown_timer=30
HA_Token=

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.env .env
venv

129
main.py
View File

@@ -4,6 +4,7 @@ import os
from dotenv import load_dotenv from dotenv import load_dotenv
import logging import logging
import signal import signal
import time
load_dotenv() load_dotenv()
@@ -29,9 +30,18 @@ if not logger.handlers:
ntfy_token = os.environ['NTFY_Token'] ntfy_token = os.environ['NTFY_Token']
device_name = os.environ['device_name'] device_name = os.environ['device_name']
shutdown_timer = int(os.environ['shutdown_timer']) shutdown_timer = int(os.environ['shutdown_timer'])
ha_token = os.environ['HA_Token']
####################################################
#Globals
running = True running = True
inverter_online = True
last_online = time.monotonic()
grid_online = True
def handle_shutdown(signum, frame): def handle_shutdown(signum, frame):
global running global running
logger.info("Received shutdown signal, exiting...") logger.info("Received shutdown signal, exiting...")
@@ -40,43 +50,120 @@ def handle_shutdown(signum, frame):
signal.signal(signal.SIGTERM, handle_shutdown) signal.signal(signal.SIGTERM, handle_shutdown)
signal.signal(signal.SIGINT, 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 check_inverter(status: bool):
global inverter_online
global last_online
if status:
logger.debug("Inverter Online")
if not inverter_online:
logger.info("Inverter restored")
inverter_online = True
last_online = time.monotonic()
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(): def main():
with serial.Serial(device_name, 115200, timeout=1) as ser:
inverter_offline_counter = 0
shutdown_triggered = False shutdown_triggered = False
try:
with serial.Serial(device_name, 115200, timeout=1) as ser:
global last_online
last_logged = -1
while running: while running:
line = ser.readline().decode(errors="ignore").strip() line = ser.readline().decode(errors="ignore").strip()
if line == "": if line == "":
logger.warning("No serial data") logger.warning("No serial data")
continue continue
elif line == "0":
logger.debug("Inverter Online") if line not in ("00", "01", "10", "11"):
inverter_offline_counter = 0 logger.warning(f"Invalid serial data: {line}")
elif inverter_offline_counter >= shutdown_timer: continue
inverter_status = line[0] == "0"
grid_status = line[1] == "0"
check_inverter(inverter_status)
check_grid(grid_status)
if not inverter_online:
sec = int(time.monotonic() - last_online)
if sec != last_logged:
logger.info(f"Offline for {sec}s")
last_logged = sec
if sec >= shutdown_timer:
shutdown_triggered = True shutdown_triggered = True
break break
else: except serial.SerialException as e:
inverter_offline_counter += 1 logger.error(f"Serial error: {e}")
logger.info(f"Running on UPS reserve, offline for {inverter_offline_counter}s")
if shutdown_triggered: if shutdown_triggered:
logger.info(f"Inverter offline for {shutdown_timer} seconds") logger.info(f"Inverter offline for {shutdown_timer} seconds")
try: send_message("Inverter offline", "Hydrogen running on reserve power, shutting down")
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") os.system("shutdown now")
else: else:
logger.info("Exited cleanly (service stop)") logger.info("Exited cleanly (service stop)")
if __name__ == "__main__": if __name__ == "__main__":
main() main()