import pvporcupine
import pytz
-import bootstrap
-import config
-import datetime_utils
+from pyutils import (
+ bootstrap,
+ config,
+)
+from pyutils.datetimes import datetime_utils
+from pyutils.files import file_utils
-import constants
+import kiosk_constants
import file_writer
import renderer_catalog
import chooser
import listen
import trigger_catalog
-import utils
cfg = config.add_commandline_args(
page = page.replace('mynorthwest', 'northwest news')
page = page.replace('myq', 'myq garage door status')
page = page.replace('gomenu', 'dinner menu')
- page = page.replace('wsdot', 'traffic')
+ page = page.replace('gmaps-seattle-unwrapped', 'traffic')
page = page.replace('gomenu', 'dinner menu')
page = page.replace('WSJNews', 'news')
page = page.replace('telma', 'telma cabin')
logger.debug(f'normalize_page output: {page}')
return page
+ logger.info(f'No exact match for f{command}; trying to guess...')
best_page = None
best_score = None
for page in page_chooser.get_page_list():
score = SequenceMatcher(None, command, npage).ratio()
if best_score is None or score > best_score:
best_page = page
+ best_score = score
assert best_page is not None
+ logger.info(f'Best guess for f{command} => {best_page} (score = {best_score})')
return best_page
def process_command(command: str, page_history: List[str], page_chooser) -> str:
- logger.debug(f'Parsing verbal command: {command}')
+ command = command.lower()
+ logger.info(f'Parsing verbal command: {command}')
page = None
if 'hold' in command:
page = page_history[0]
break
elif 'internal' in command:
if 'render' in command:
- page = constants.render_stats_pagename
+ page = kiosk_constants.render_stats_pagename
else:
- page = constants.render_stats_pagename
+ page = kiosk_constants.render_stats_pagename
elif 'weather' in command:
if 'telma' in command or 'cabin' in command:
page = 'weather-telma_3_10800.html'
elif 'twitter' in command:
page = 'twitter_10_3600.html'
elif 'traffic' in command:
- page = 'wsdot-bridges_3_none.html'
+ page = 'gmaps-seattle-unwrapped_5_none.html'
elif 'front' in command and 'door' in command:
page = 'hidden/frontdoor.html'
elif 'driveway' in command:
else:
page = guess_page(command, page_chooser)
assert page is not None
- logger.debug(f'Chose page {page}')
+ logger.info(f'Parsed to {page}')
return page
page_chooser = chooser.weighted_random_chooser_with_triggers(
trigger_catalog.get_triggers(), [filter_news_during_dinnertime]
)
- current_file = os.path.join(constants.pages_dir, "current.shtml")
- emergency_file = os.path.join(constants.pages_dir, "reload_immediately.html")
+ current_file = os.path.join(kiosk_constants.pages_dir, "current.shtml")
+ emergency_file = os.path.join(kiosk_constants.pages_dir, "reload_immediately.html")
# Main chooser loop
while True:
if triggered:
if page != page_history[0] or (swap_page_target - now) < 10.0:
logger.info(f'chooser: An emergency page reload to {page} is needed at this time.')
- swap_page_target = now + constants.emergency_refresh_period_sec
+ swap_page_target = now + kiosk_constants.emergency_refresh_period_sec
# Set current.shtml to the right page.
try:
with open(current_file, "w") as f:
- emit_wrapped(
+ emit(
f,
page,
- override_refresh_sec = constants.emergency_refresh_period_sec,
+ override_refresh_sec = kiosk_constants.emergency_refresh_period_sec,
command = command
)
logger.debug(f'chooser: Wrote {current_file}.')
# Fix this hack... maybe read the webserver logs and see if it
# actually was picked up?
- time.sleep(0.95)
+ time.sleep(0.999)
os.remove(emergency_file)
logger.debug(f'chooser: ...and removed {emergency_file}.')
logger.info(
f'chooser: Nominal choice of {page} as the next to show.'
)
- swap_page_target = now + constants.refresh_period_sec
+ swap_page_target = now + kiosk_constants.refresh_period_sec
try:
with open(current_file, "w") as f:
- emit_wrapped(f, page)
+ emit(f, page)
logger.debug(f'chooser: Wrote {current_file}.')
except Exception as e:
logger.exception(e)
time.sleep(0.5)
+def emit(f,
+ filename: str,
+ *,
+ override_refresh_sec: int = None,
+ command: str = None):
+ if 'unwrapped' not in filename:
+ logger.debug(f'Emitting {filename} wrapped.')
+ emit_wrapped(f, filename, override_refresh_sec=override_refresh_sec, command=command)
+ else:
+ logger.debug(f'Emitting {filename} raw.')
+ emit_raw(f, filename)
+
+
+def emit_raw(f, filename: str):
+ f.write(f'<!--#include virtual="{filename}"-->')
+
+
def emit_wrapped(f,
filename: str,
*,
"Bellevue", "USA", "US/Pacific", 47.610, -122.201
)
s = sun(city.observer, date=now, tzinfo=pytz.timezone("US/Pacific"))
- sunrise_mod = utils.minute_number(s["sunrise"].hour, s["sunrise"].minute)
- sunset_mod = utils.minute_number(s["sunset"].hour, s["sunset"].minute)
- now_mod = utils.minute_number(now.hour, now.minute)
+ sunrise_mod = datetime_utils.minute_number(s["sunrise"].hour, s["sunrise"].minute)
+ sunset_mod = datetime_utils.minute_number(s["sunset"].hour, s["sunset"].minute)
+ now_mod = datetime_utils.minute_number(now.hour, now.minute)
if now_mod < sunrise_mod or now_mod > (sunset_mod + 45):
return "E6B8B8"
elif now_mod < (sunrise_mod + 45) or now_mod > (sunset_mod + 120):
return float(override_refresh_sec * 1000.0)
now = datetime.now(tz=pytz.timezone("US/Pacific"))
if now.hour < 6:
- return float(constants.refresh_period_night_sec * 1000.0)
+ return float(kiosk_constants.refresh_period_night_sec * 1000.0)
else:
- return float(constants.refresh_period_sec * 1000.0)
+ return float(kiosk_constants.refresh_period_sec * 1000.0)
- age = utils.describe_age_of_file_briefly(f"pages/{filename}")
+ age = file_utils.describe_file_ctime(f"pages/{filename}")
bgcolor = pick_background_color()
if command is None:
pageid = filename
<HEAD>
<TITLE>Kitchen Kiosk</TITLE>
<LINK rel="stylesheet" type="text/css" href="style.css">
+ <meta name="google" content="notranslate">
<SCRIPT TYPE="text/javascript">
// Zoom the 'contents' div to fit without scrollbars and then make
setInterval(check_reload, 500);
</SCRIPT>
</HEAD>
-""" % constants.root_url)
+""" % kiosk_constants.root_url)
f.write(f'<BODY BGCOLOR="#{bgcolor}">')
f.write(
"""
logger.info(
'renderer: Updating internal render statistics page.'
)
- with file_writer.file_writer(constants.render_stats_pagename) as f:
+ with file_writer.file_writer(kiosk_constants.render_stats_pagename) as f:
f.write(
'''
<CENTER>
p75 = np.percentile(latency, 75)
p90 = np.percentile(latency, 90)
p99 = np.percentile(latency, 99)
- except IndexError:
- pass
- f.write(
+ f.write(
f'''
<TR>
<TD {style}>{name} </TD>
<TD {style}> p25={p25:5.2f}, p50={p50:5.2f}, p75={p75:5.2f}, p90={p90:5.2f}, p99={p99:5.2f}</TD>
</TR>
'''
- )
- f.write('</TABLE>')
+ )
+ except IndexError:
+ pass
+ f.write('</TABLE>')
def thread_invoke_renderers() -> None:
render_counts: collections.Counter = collections.Counter()
last_render: Dict[str, datetime] = {}
+ # Touch the internal render page now to signal that we're alive.
+ renderer_update_internal_stats_page(last_render, render_counts, render_times)
+
# Main renderer loop
while True:
logger.info(
# Update a page about internal stats of renderers.
renderer_update_internal_stats_page(last_render, render_counts, render_times)
logger.info('renderer: having a little nap...')
- time.sleep(constants.render_period_sec)
+ time.sleep(kiosk_constants.render_period_sec)
@bootstrap.initialize
# Have a little break and then check to make sure all threads are still alive.
logger.debug('watchdog: having a little nap.')
- time.sleep(constants.check_threads_period_sec)
+ time.sleep(kiosk_constants.check_threads_period_sec)
if __name__ == "__main__":