1e666489e66cf0366532e3658a6f9a6bc2763a01
[kiosk.git] / myq_renderer.py
1 import pymyq
2 from aiohttp import ClientSession
3 import asyncio
4 import constants
5 import datetime
6 from dateutil.parser import parse
7 import file_writer
8 import renderer
9 import secrets
10
11
12 class garage_door_renderer(renderer.debuggable_abstaining_renderer):
13     def __init__(self, name_to_timeout_dict):
14         super(garage_door_renderer, self).__init__(name_to_timeout_dict, False)
15         self.doors = None
16         self.last_update = None
17
18     def debug_prefix(self):
19         return "myq"
20
21     def periodic_render(self, key):
22         if key == "Poll MyQ":
23             self.last_update = datetime.datetime.now()
24             return asyncio.run(self.poll_myq())
25         elif key == "Update Page":
26             return self.update_page()
27         else:
28             raise error("Unknown operaiton")
29
30     async def poll_myq(self):
31         async with ClientSession() as websession:
32             myq = await pymyq.login(
33                 secrets.myq_username, secrets.myq_password, websession
34             )
35             self.doors = myq.devices
36             return len(self.doors) > 0
37
38     def update_page(self):
39         f = file_writer.file_writer(constants.myq_pagename)
40         f.write(
41             """
42 <H1>Garage Door Status</H1>
43 <!-- Last updated at %s -->
44 <HR>
45 <TABLE BORDER=0 WIDTH=99%%>
46   <TR>
47 """
48             % self.last_update
49         )
50         html = self.do_door("Near House")
51         if html == None:
52             return False
53         f.write(html)
54
55         html = self.do_door("Middle Door")
56         if html == None:
57             return False
58         f.write(html)
59         f.write(
60             """
61   </TR>
62 </TABLE>"""
63         )
64         f.close()
65         return True
66
67     def get_state_icon(self, state):
68         if state == "open":
69             return "/icons/garage_open.png"
70         elif state == "closed":
71             return "/icons/garage_closed.png"
72         elif state == "opening":
73             return "/icons/garage_opening.png"
74         elif state == "closing":
75             return "/icons/garage_closing.png"
76         else:
77             return str(state) + ", an unknown state for the door."
78
79     def do_door(self, name):
80         for key in self.doors:
81             door = self.doors[key]
82             if door.name == name:
83                 j = self.doors[key].json
84                 state = j["state"]["door_state"]
85
86                 # "last_update": "2020-07-04T18:11:34.2981419Z"
87                 raw = j["state"]["last_update"]
88                 ts = parse(raw)
89                 tz_info = ts.tzinfo
90                 now = datetime.datetime.now(tz_info)
91                 delta = (now - ts).total_seconds()
92                 now = datetime.datetime.now()
93                 is_night = now.hour <= 7 or now.hour >= 21
94                 days = divmod(delta, constants.seconds_per_day)
95                 hours = divmod(days[1], constants.seconds_per_hour)
96                 minutes = divmod(hours[1], constants.seconds_per_minute)
97                 width = 0
98                 if is_night and door.state == "open":
99                     color = "border-color: #ff0000;"
100                     width = 15
101                 else:
102                     color = ""
103                     width = 0
104                 return """
105 <TD WIDTH=49%%>
106   <CENTER>
107   <FONT STYLE="font-size:26pt">%s<BR>
108   <IMG SRC="%s"
109        HEIGHT=250
110        STYLE="border-style: solid; border-width: %dpx; %s">
111   <BR>
112   <B>%s</B></FONT><BR>
113   for %d day(s), %02d:%02d.
114   </CENTER>
115 </TD>""" % (
116                     name,
117                     self.get_state_icon(state),
118                     width,
119                     color,
120                     state,
121                     days[0],
122                     hours[0],
123                     minutes[0],
124                 )
125         return None
126
127
128 # Test
129 x = garage_door_renderer({"Test": 1})
130 x.periodic_render("Poll MyQ")
131 x.periodic_render("Update Page")