6 from threading import Thread
8 from datetime import datetime
12 import renderer_catalog
15 import trigger_catalog
19 def filter_news_during_dinnertime(page):
21 is_dinnertime = now.hour >= 17 and now.hour <= 20
22 return not is_dinnertime or not (
25 or "mynorthwest" in page
33 def thread_change_current():
34 page_chooser = chooser.weighted_random_chooser_with_triggers(
35 trigger_catalog.get_triggers(), [filter_news_during_dinnertime]
41 (page, triggered) = page_chooser.choose_next_page()
44 print("chooser[%s] - WE ARE TRIGGERED." % utils.timestamp())
47 "chooser[%s] - EMERGENCY PAGE %s LOAD NEEDED"
48 % (utils.timestamp(), page)
51 f = open(os.path.join(constants.pages_dir, "current.shtml"), "w")
55 print("chooser[%s] - page does not exist?!" % (utils.timestamp()))
58 swap_page_target = now + constants.refresh_period_sec
60 # Also notify XMLHTTP clients that they need to refresh now.
61 path = os.path.join(constants.pages_dir, "reload_immediately.html")
63 f.write("Reload, suckers!")
66 # Fix this hack... maybe read the webserver logs and see if it
67 # actually was picked up?
71 elif now >= swap_page_target:
75 "chooser[%s] - nominal choice got the same page..."
80 print("chooser[%s] - nominal choice of %s" % (utils.timestamp(), page))
82 f = open(os.path.join(constants.pages_dir, "current.shtml"), "w")
86 print("chooser[%s] - page does not exist?!" % (utils.timestamp()))
89 swap_page_target = now + constants.refresh_period_sec
93 def pick_background_color():
95 if now.hour <= 6 or now.hour >= 21:
97 elif now.hour == 7 or now.hour == 20:
103 def emit_wrapped(f, filename):
104 age = utils.describe_age_of_file_briefly("pages/%s" % filename)
105 bgcolor = pick_background_color()
109 <TITLE>Kitchen Kiosk</TITLE>
110 <LINK rel="stylesheet" type="text/css" href="style.css">
111 <SCRIPT TYPE="text/javascript">
113 // Zoom the 'contents' div to fit without scrollbars and then make
115 function zoomScreen() {
118 document.getElementById("content").style.zoom = z+"%%";
119 var body = document.body;
120 var html = document.documentElement;
121 var height = Math.max(body.scrollHeight,
126 var windowHeight = window.innerHeight;
127 var width = Math.max(body.scrollWidth,
132 var windowWidth = window.innerWidth;
133 var heightRatio = height / windowHeight;
134 var widthRatio = width / windowWidth;
136 if (heightRatio <= 1.0 && widthRatio <= 1.0) {
141 document.getElementById("content").style.visibility = "visible";
144 // Load IMG tags with DATA-SRC attributes late.
145 function lateLoadImages() {
146 var image = document.getElementsByTagName('img');
147 for (var i = 0; i < image.length; i++) {
148 if (image[i].getAttribute('DATA-SRC')) {
149 image[i].setAttribute('SRC', image[i].getAttribute('DATA-SRC'));
154 // Operate the clock at the top of the page.
155 function runClock() {
156 var today = new Date();
157 var h = today.getHours();
158 var ampm = h >= 12 ? 'pm' : 'am';
160 h = h ? h : 12; // the hour '0' should be '12'
161 var m = maybeAddZero(today.getMinutes());
163 if (today.getSeconds() %% 2 == 0) {
164 colon = "<FONT STYLE='color: #%s; font-size: 4vmin; font-weight: bold'>:</FONT>";
166 document.getElementById("time").innerHTML = h + colon + m + ampm;
167 document.getElementById("date").innerHTML = today.toDateString();
168 var t = setTimeout(function(){runClock()}, 1000);
171 // Helper method for running the clock.
172 function maybeAddZero(x) {
173 return (x < 10) ? "0" + x : x;
176 // Do something on page load.
177 function addLoadEvent(func) {
178 var oldonload = window.onload;
179 if (typeof window.onload != 'function') {
180 window.onload = func;
182 window.onload = function() {
191 // Sleep thread helper.
192 const sleep = (milliseconds) => {
193 return new Promise(resolve => setTimeout(resolve, milliseconds))
196 var loadedDate = new Date();
198 addLoadEvent(zoomScreen);
199 addLoadEvent(runClock);
200 addLoadEvent(lateLoadImages);
202 // Runs the countdown line at the bottom and is responsible for
203 // normal page reloads caused by the expiration of a timer.
204 (function countdown() {
207 var now = new Date();
208 var deltaMs = now.getTime() - loadedDate.getTime();
210 var remainingMs = (totalMs - deltaMs);
212 if (remainingMs > 0) {
213 var hr = document.getElementById("countdown");
214 var width = (remainingMs / (totalMs - 5000)) * 100.0;
216 hr.style.visibility = "visible";
217 hr.style.width = " ".concat(width, "%%");
218 hr.style.backgroundColor = "maroon";
221 // Reload unconditionally after 22 sec.
222 window.location.reload();
225 // Brief sleep before doing it all over again.
226 sleep(50).then(() => {
232 // Periodically checks for emergency reload events.
236 var xhr = new XMLHttpRequest();
238 'http://wannabe.house/kiosk/pages/reload_immediately.html');
241 if (xhr.status === 200) {
242 window.location.reload();
244 sleep(500).then(() => {
255 <TABLE style="height:100%%; width:100%%" BORDER=0>
258 <DIV id="date"> </DIV>
260 <TD ALIGN="center"><FONT COLOR=#bbbbbb>
261 <DIV id="info"></DIV></FONT>
264 <DIV id="time"> </DIV>
267 <TR STYLE="vertical-align:top">
269 <DIV ID="content" STYLE="zoom: 1; visibility: hidden;">
270 <!-- BEGIN main page contents. -->
271 <!--#include virtual=\"%s\"-->
272 <!-- END main page contents. -->
275 <DIV STYLE="position: absolute; top:1030px; width:99%%">
277 <FONT SIZE=2 COLOR=#bbbbbb>%s @ %s ago.</FONT>
279 <HR id="countdown" STYLE="width:0px;
286 background-color:#ffffff;">
294 constants.refresh_period_sec * 1000,
303 def thread_invoke_renderers():
306 "renderer[%s]: invoking all renderers in catalog..." % (utils.timestamp())
308 for r in renderer_catalog.get_renderers():
312 except Exception as e:
313 traceback.print_exc()
315 "renderer[%s] unknown exception in %s, swallowing it."
316 % (utils.timestamp(), r.get_name())
319 traceback.print_exc()
321 "renderer[%s] unknown error in %s, swallowing it."
322 % (utils.timestamp(), r.get_name())
324 delta = time.time() - now
327 "renderer[%s]: Warning: %s's rendering took %5.2fs."
328 % (utils.timestamp(), r.get_name(), delta)
331 "renderer[%s]: thread having a little break for %ds..."
332 % (utils.timestamp(), constants.render_period_sec)
334 time.sleep(constants.render_period_sec)
337 if __name__ == "__main__":
338 logging.basicConfig()
339 changer_thread = None
340 renderer_thread = None
342 if changer_thread == None or not changer_thread.is_alive():
344 "MAIN[%s] - (Re?)initializing chooser thread..." % (utils.timestamp())
346 changer_thread = Thread(target=thread_change_current, args=())
347 changer_thread.start()
348 if renderer_thread == None or not renderer_thread.is_alive():
349 print("MAIN[%s] - (Re?)initializing render thread..." % (utils.timestamp()))
350 renderer_thread = Thread(target=thread_invoke_renderers, args=())
351 renderer_thread.start()
353 print("Should never get here.")