1 #!/usr/local/bin/python3.7
6 from threading import Thread
8 from datetime import datetime
12 import renderer_catalog
15 import trigger_catalog
18 def thread_change_current():
19 page_chooser = chooser.weighted_random_chooser_with_triggers(
20 trigger_catalog.get_triggers())
25 (page, triggered) = page_chooser.choose_next_page()
28 print(('chooser[%s] - WE ARE TRIGGERED.' % utils.timestamp()))
30 print(('chooser[%s] - EMERGENCY PAGE %s LOAD NEEDED' % (
31 utils.timestamp(), page)))
32 f = open(os.path.join(constants.pages_dir,
33 "current.shtml"), "w")
37 # Notify XMLHTTP clients that they need to refresh now.
38 path = os.path.join(constants.pages_dir,
39 "reload_immediately.html")
41 f.write("Reload, suckers!")
46 swap_page_target = now + constants.refresh_period_sec
48 elif now >= swap_page_target:
49 if (page == last_page):
50 print(('chooser[%s] - nominal choice got the same page...' % (
53 print(('chooser[%s] - nominal choice of %s' % (utils.timestamp(), page)))
55 f = open(os.path.join(constants.pages_dir,
56 "current.shtml"), "w")
60 swap_page_target = now + constants.refresh_period_sec
62 print(('chooser[%s] - page does not exist?!' % (utils.timestamp())))
66 def pick_background_color():
68 if now.hour <= 6 or now.hour >= 21:
70 elif now.hour == 7 or now.hour == 20:
75 def emit_wrapped(f, filename):
76 age = utils.describe_age_of_file_briefly("pages/%s" % filename)
77 bgcolor = pick_background_color()
80 <TITLE>Kitchen Kiosk</TITLE>
81 <LINK rel="stylesheet" type="text/css" href="style.css">
82 <SCRIPT TYPE="text/javascript">
84 // Zoom the 'contents' div to fit without scrollbars and then make
86 function zoomScreen() {
89 document.getElementById("content").style.zoom = z+"%%";
90 var body = document.body;
91 var html = document.documentElement;
92 var height = Math.max(body.scrollHeight,
97 var windowHeight = window.innerHeight;
98 var width = Math.max(body.scrollWidth,
103 var windowWidth = window.innerWidth;
104 var heightRatio = height / windowHeight;
105 var widthRatio = width / windowWidth;
107 if (heightRatio <= 1.0 && widthRatio <= 1.0) {
112 document.getElementById("content").style.visibility = "visible";
115 // Load IMG tags with DATA-SRC attributes late.
116 function lateLoadImages() {
117 var image = document.getElementsByTagName('img');
118 for (var i = 0; i < image.length; i++) {
119 if (image[i].getAttribute('DATA-SRC')) {
120 image[i].setAttribute('SRC', image[i].getAttribute('DATA-SRC'));
125 // Operate the clock at the top of the page.
126 function runClock() {
127 var today = new Date();
128 var h = today.getHours();
129 var ampm = h >= 12 ? 'pm' : 'am';
131 h = h ? h : 12; // the hour '0' should be '12'
132 var m = maybeAddZero(today.getMinutes());
134 if (today.getSeconds() %% 2 == 0) {
135 colon = "<FONT STYLE='color: #%s; font-size: 4vmin; font-weight: bold'>:</FONT>";
137 document.getElementById("time").innerHTML = h + colon + m + ampm;
138 document.getElementById("date").innerHTML = today.toDateString();
139 var t = setTimeout(function(){runClock()}, 1000);
142 // Helper method for running the clock.
143 function maybeAddZero(x) {
144 return (x < 10) ? "0" + x : x;
147 // Do something on page load.
148 function addLoadEvent(func) {
149 var oldonload = window.onload;
150 if (typeof window.onload != 'function') {
151 window.onload = func;
153 window.onload = function() {
162 // Sleep thread helper.
163 const sleep = (milliseconds) => {
164 return new Promise(resolve => setTimeout(resolve, milliseconds))
168 var loadedDate = new Date();
170 addLoadEvent(zoomScreen);
171 addLoadEvent(runClock);
172 addLoadEvent(lateLoadImages);
173 addLoadEvent(function() {
177 // Reload the page after a certain amount of time has passed or
178 // immediately if told to do so.
182 var now = new Date();
183 var deltaMs = now.getTime() - loadedDate.getTime();
185 // Reload unconditionally after 22 sec.
187 window.location.reload();
190 // Reload immediately if told.
191 var xhr = new XMLHttpRequest();
193 'http://wannabe.house/kiosk/pages/reload_immediately.html');
196 if (xhr.status === 200) {
197 window.location.reload();
199 sleep(500).then(() => {
210 <TABLE style="height:100%%; width:100%%" BORDER=0>
213 <DIV id="date"> </DIV>
215 <TD ALIGN="center"><FONT COLOR=#bbbbbb>
216 <DIV id="info"></DIV></FONT>
219 <DIV id="time"> </DIV>
222 <TR STYLE="vertical-align:top">
224 <DIV ID="content" STYLE="zoom: 1; visibility: hidden;">
225 <!-- BEGIN main page contents. -->
226 <!--#include virtual=\"%s\"-->
227 <!-- END main page contents. -->
231 <FONT SIZE=2 COLOR=#bbbbbb>%s @ %s ago.</FONT>
236 </BODY>""" % (bgcolor,
237 constants.refresh_period_sec * 1000,
243 def thread_invoke_renderers():
245 print("Renderer thread[%s]: invoking all renderers in catalog..." % (
247 for r in renderer_catalog.get_renderers():
250 except Exception as e:
251 traceback.print_exc()
252 print(("renderer[%s] unknown exception, swallowing it." % (
255 traceback.print_exc()
256 print(("renderer[%s] unknown error, swallowing it." % (
258 time.sleep(constants.render_period_sec)
260 if __name__ == "__main__":
261 logging.basicConfig()
262 changer_thread = None
263 renderer_thread = None
265 if (changer_thread == None or
266 not changer_thread.is_alive()):
267 print(("chooser[%s] - (Re?)initializing chooser thread..." % utils.timestamp()))
268 changer_thread = Thread(target = thread_change_current, args=())
269 changer_thread.start()
270 if (renderer_thread == None or
271 not renderer_thread.is_alive()):
272 print(("renderer[%s] - (Re?)initializing render thread..." % utils.timestamp()))
273 renderer_thread = Thread(target = thread_invoke_renderers, args=())
274 renderer_thread.start()
276 print("Should never get here.")