diff --git a/character_display.py b/character_display.py index 1f48296..9ecc691 100644 --- a/character_display.py +++ b/character_display.py @@ -1,17 +1,3 @@ -# There are 4 different types of patterns used when generating a digit that is -# to be placed in a rectangle 3X5 pixels. Combinations of these are used to -# create a number pattern such as: -# * * * -# * -# * * * -# * * -# * * * -# -# 1) * * * Full Row -# 2) * * Both Sides -# 3) * Right Side -# 4) * Left Side - import colors diff --git a/chutney.py b/chutney.py index e6dbf98..f555121 100644 --- a/chutney.py +++ b/chutney.py @@ -1,10 +1,15 @@ +import json +import requests import sys from character_display import CharacterDisplay +from configparser import ConfigParser from garage_display import GarageDisplay from garage_updater import GarageUpdater from multiprocessing import Event from multiprocessing import Process +from multiprocessing import Queue +from queue import Empty from signal import signal from signal import SIGTERM from time import sleep @@ -21,22 +26,24 @@ except ImportError: WEATHER_UPDATE_INTERVAL_IN_SECONDS = 10 -GARAGE_UPDATE_INTERVAL_IN_SECONDS = 10 +GARAGE_UPDATE_INTERVAL_IN_SECONDS = 30 DISPLAY_UPDATE_INTERVAL_IN_SECONDS = 0.5 CONFIG_FILE = 'chutney.cfg' +class HaltException(Exception): + pass + + def main(): + queue = Queue() haltEvent = Event() - signal(SIGTERM, cleanExit(unicorn, haltEvent)) + Process(target=weatherLoop, args=[haltEvent]).start() + Process(target=garageLoop, args=[queue]).start() + signal(SIGTERM, cleanExit(unicorn, haltEvent, queue)) + unicorn.brightness(0.3) - weatherThread = Process(target=weatherLoop, args=[haltEvent]) - weatherThread.start() - - garageThread = Process(target=garageLoop, args=[haltEvent]) - garageThread.start() - characterDisplay = CharacterDisplay(unicorn) timeDisplay = TimeDisplay(characterDisplay, topRow=15) weatherDisplay = WeatherDisplay(characterDisplay, topRow=9) @@ -59,24 +66,68 @@ def weatherLoop(haltEvent): weatherUpdater.clearWeather() -def garageLoop(haltEvent): - garageUpdater = GarageUpdater(configFile=CONFIG_FILE) +def garageLoop(queue): + garageNotify = Process(target=garageNotifyLoop, args=[queue]) + garageNotify.start() - while not haltEvent.is_set(): + garageUpdater = GarageUpdater(configFile=CONFIG_FILE) + isRunning = True + + while isRunning: garageUpdater.updateGarageState() - haltEvent.wait(timeout=GARAGE_UPDATE_INTERVAL_IN_SECONDS) + + try: + item = queue.get(timeout=GARAGE_UPDATE_INTERVAL_IN_SECONDS) + isRunning = item != 'halt' + except Empty: + pass garageUpdater.clearGarageState() + garageNotify.terminate() -def cleanExit(unicorn, haltEvent): +def garageNotifyLoop(queue): + signal(SIGTERM, raiseHaltException()) + config = ConfigParser() + config.read(CONFIG_FILE) + + ntfy = config['garage'].get('ntfy') + topic = config['garage'].get('topic') + + try: + resp = requests.get(f'https://{ntfy}/{topic}/json', stream=True) + + for line in resp.iter_lines(): + if line: + data = json.loads(line.decode('utf-8')) + + if (data['event'] == 'message'): + queue.put('update') + except HaltException: + pass + except Exception as e: + print(e, flush=True) + finally: + resp.close() + + +def cleanExit(unicorn, haltEvent, queue): def _exit_(signum, frame): unicorn.off() haltEvent.set() + queue.put('halt') + queue.close() sys.exit(0) return _exit_ +def raiseHaltException(): + def _exit_(signum, frame): + raise HaltException() + + return _exit_ + + if __name__ == '__main__': main() diff --git a/chutney.sample.cfg b/chutney.sample.cfg index c187965..badda74 100644 --- a/chutney.sample.cfg +++ b/chutney.sample.cfg @@ -7,3 +7,5 @@ lon=lon [garage] uri=uri +ntfy=ntfy.sh +topic=topic diff --git a/garage_display.py b/garage_display.py index 5bfb74c..becbb49 100644 --- a/garage_display.py +++ b/garage_display.py @@ -18,6 +18,7 @@ class GarageDisplay: def showGarageState(self): state = self.getGarageState() + # TODO - only update the changed door so the other one doesn't flicker if state != self.currentState: self.currentState = state self.characterDisplay.clearRow(self.topRow, rowHeight=4)