--- /dev/null
+#!/usr/bin/env python3
+
+import datetime
+import logging
+import json
+from dateutil.parser import parse
+from typing import Dict, Optional
+
+import requests
+from pyutils.datetimes import datetime_utils
+
+import file_writer
+import globals
+import kiosk_constants
+import kiosk_secrets as secrets
+import renderer
+
+
+logger = logging.getLogger(__name__)
+
+
+class garage_door_renderer(renderer.abstaining_renderer):
+ def __init__(self, name_to_timeout_dict: Dict[str, int]) -> None:
+ super().__init__(name_to_timeout_dict)
+ self.last_update: Optional[datetime.datetime] = None
+ self.doors = {
+ "cover.ratgdo_middle_door_door": {"state": "unknown"},
+ "cover.ratgdo_near_house_door": {"state": "unknown"},
+ "cover.ratgdo_shop_door": {"state": "unknown"},
+ }
+
+ def debug_prefix(self) -> str:
+ return "ratago"
+
+ def periodic_render(self, key: str) -> bool:
+ if key == "Poll Home Assistant":
+ return self.poll_home_assistant()
+ elif key == "Update Page":
+ return self.update_page()
+ else:
+ raise Exception("Unknown operaiton")
+
+ def poll_home_assistant(self) -> bool:
+ key = secrets.HOMEASSISTANT_API_KEY
+ headers = {
+ "Authorization": f"Bearer {key}",
+ "Content-Type": "application/json",
+ }
+ for door in self.doors.keys():
+ try:
+ r = requests.get(
+ f"https://home.acknak.org/api/states/{door}",
+ headers=headers,
+ timeout=3.0,
+ )
+ if r.ok:
+ j = json.loads(r.content.decode())
+ logger.debug(j)
+ self.doors[door] = j
+ else:
+ logger.warning("Unable to get state of garage door {door}, using 'unknown'")
+ except Exception:
+ logger.exception("Unable to get state of garage door {door}, using 'unknown'")
+ self.last_update = datetime_utils.now_pacific()
+ return True
+
+ def update_page(self) -> bool:
+ with file_writer.file_writer(kiosk_constants.ratago_pagename) as f:
+ f.write(
+ f"""
+<H1>Garage Door Status</H1>
+<!-- Last updated at {self.last_update} -->
+<HR>
+<TABLE BORDER=0 WIDTH=99%>
+ <TR>
+"""
+ )
+
+ html = self.do_door("cover.ratago_near_house_door")
+ if html is None:
+ return False
+ f.write(html)
+
+ html = self.do_door("cover.ratago_middle_door_door")
+ if html is None:
+ return False
+ f.write(html)
+
+ html = self.do_door("cover.ratago_shop_door")
+ if html is None:
+ return False
+ f.write(html)
+ f.write(
+ """
+ </TR>
+</TABLE>"""
+ )
+ return True
+
+ def get_state_icon(self, state: str) -> str:
+ if state == "open":
+ return "/kiosk/images/garage_open.png"
+ elif state == "closed":
+ return "/kiosk/images/garage_closed.png"
+ elif state == "opening":
+ return "/kiosk/images/garage_opening.png"
+ elif state == "closing":
+ return "/kiosk/images/garage_closing.png"
+ else:
+ return str(state) + ", an unknown state for the door."
+
+ def do_door(self, name: str) -> Optional[str]:
+ friendly_door_names = {
+ "cover.ratgdo_middle_door_door": "Middle Door",
+ "cover.ratgdo_near_house_door": "Near House Door",
+ "cover.ratgdo_shop_door": "Workshop Door",
+ }
+ if self.doors is None:
+ return None
+
+ friendly_name = friendly_door_names.get(name, "unknown")
+ attributes = self.doors.get(name, {"state": "unknown"})
+ state = attributes.get("state", "unknown").lower()
+ since = attributes.get("last_changed", "unknown").lower()
+
+ # "last_update": "2020-07-04T18:11:34.2981419Z"
+ ts = parse(since)
+ tz_info = ts.tzinfo
+ now = datetime.datetime.now(tz_info)
+ delta = (now - ts).total_seconds()
+ duration = datetime_utils.describe_duration_briefly(int(delta))
+
+ now = datetime.datetime.now()
+ is_night = now.hour <= 7 or now.hour >= 21
+ width = 0
+ if is_night and state == "open":
+ color = "border-color: #ff0000;"
+ width = 15
+ globals.put("ratago_triggered", True)
+ else:
+ color = ""
+ width = 0
+ globals.put("ratago_triggered", False)
+ return f"""
+<TD WIDTH=32%>
+ <CENTER>
+ <FONT STYLE="font-size:26pt">{friendly_name}<BR>
+ <IMG SRC="{self.get_state_icon(state)}"
+ HEIGHT=250
+ STYLE="border-style: solid; border-width: {width}px; {color}">
+ <BR>
+ <B>{state}</B></FONT><BR>
+ for {duration}
+ </CENTER>
+</TD>"""
+
+
+# Test
+#x = garage_door_renderer({"Test": 1})
+#x.periodic_render("Poll MyQ")
+#x.periodic_render("Update Page")