diff --git a/.gitignore b/.gitignore index 67a94bc..19bfbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ __pycache__/ *.swp -*.tmp chutney.cfg -garage.json -weather.txt diff --git a/chutney.py b/chutney.py index f555121..26bfeb9 100644 --- a/chutney.py +++ b/chutney.py @@ -1,133 +1,4 @@ -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 -from time_display import TimeDisplay -from weather_display import WeatherDisplay -from weather_updater import WeatherUpdater - -try: - import unicornhathd as unicorn - unicorn.rotation(90) -except ImportError: - from unicorn_hat_sim import unicornhathd as unicorn - unicorn.rotation(180) - - -WEATHER_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() - Process(target=weatherLoop, args=[haltEvent]).start() - Process(target=garageLoop, args=[queue]).start() - signal(SIGTERM, cleanExit(unicorn, haltEvent, queue)) - - unicorn.brightness(0.3) - - characterDisplay = CharacterDisplay(unicorn) - timeDisplay = TimeDisplay(characterDisplay, topRow=15) - weatherDisplay = WeatherDisplay(characterDisplay, topRow=9) - garageDisplay = GarageDisplay(characterDisplay, topRow=3) - - while True: - timeDisplay.showTime() - weatherDisplay.showWeather() - garageDisplay.showGarageState() - sleep(DISPLAY_UPDATE_INTERVAL_IN_SECONDS) - - -def weatherLoop(haltEvent): - weatherUpdater = WeatherUpdater(configFile=CONFIG_FILE) - - while not haltEvent.is_set(): - weatherUpdater.updateWeather() - haltEvent.wait(timeout=WEATHER_UPDATE_INTERVAL_IN_SECONDS) - - weatherUpdater.clearWeather() - - -def garageLoop(queue): - garageNotify = Process(target=garageNotifyLoop, args=[queue]) - garageNotify.start() - - garageUpdater = GarageUpdater(configFile=CONFIG_FILE) - isRunning = True - - while isRunning: - garageUpdater.updateGarageState() - - try: - item = queue.get(timeout=GARAGE_UPDATE_INTERVAL_IN_SECONDS) - isRunning = item != 'halt' - except Empty: - pass - - garageUpdater.clearGarageState() - garageNotify.terminate() - - -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_ - +import chutney.runner if __name__ == '__main__': - main() + chutney.runner.main() diff --git a/chutney/__init__.py b/chutney/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/character_display.py b/chutney/character_display.py similarity index 99% rename from character_display.py rename to chutney/character_display.py index 9ecc691..69afa21 100644 --- a/character_display.py +++ b/chutney/character_display.py @@ -1,4 +1,4 @@ -import colors +import chutney.colors as colors class CharacterDisplay: diff --git a/colors.py b/chutney/colors.py similarity index 100% rename from colors.py rename to chutney/colors.py diff --git a/chutney/garage/__init__.py b/chutney/garage/__init__.py new file mode 100644 index 0000000..fc2456e --- /dev/null +++ b/chutney/garage/__init__.py @@ -0,0 +1,2 @@ +from .garage_display import GarageDisplay +from .garage_updater import GarageUpdater diff --git a/garage_display.py b/chutney/garage/garage_display.py similarity index 96% rename from garage_display.py rename to chutney/garage/garage_display.py index 921fcce..9c04dbe 100644 --- a/garage_display.py +++ b/chutney/garage/garage_display.py @@ -1,4 +1,4 @@ -import colors +import chutney.colors as colors import json @@ -13,7 +13,7 @@ class GarageDisplay: self.closedFillColor = colors.RED self.openOutlineColor = colors.WHITE self.openFillColor = colors.WHITE - self.garageFile = 'garage.json' + self.garageFile = 'data/garage.json' def showGarageState(self): state = self.getGarageState() diff --git a/garage_updater.py b/chutney/garage/garage_updater.py similarity index 90% rename from garage_updater.py rename to chutney/garage/garage_updater.py index 00a3c6f..20d6cde 100644 --- a/garage_updater.py +++ b/chutney/garage/garage_updater.py @@ -10,8 +10,8 @@ class GarageUpdater: config.read(configFile) self.uri = config['garage'].get('uri') - self.garageTempFile = 'garage.tmp' - self.garageFile = 'garage.json' + self.garageTempFile = 'data/garage.tmp' + self.garageFile = 'data/garage.json' self.persistGarageState('{"error": "init"}') def updateGarageState(self): diff --git a/chutney/runner.py b/chutney/runner.py new file mode 100644 index 0000000..31aff9a --- /dev/null +++ b/chutney/runner.py @@ -0,0 +1,129 @@ +import json +import requests +import sys + +from chutney.character_display import CharacterDisplay +from chutney.garage import GarageDisplay +from chutney.garage import GarageUpdater +from chutney.time import TimeDisplay +from chutney.weather import WeatherDisplay +from chutney.weather import WeatherUpdater +from configparser import ConfigParser +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 + +try: + import unicornhathd as unicorn + unicorn.rotation(90) +except ImportError: + from unicorn_hat_sim import unicornhathd as unicorn + unicorn.rotation(180) + + +WEATHER_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() + Process(target=weatherLoop, args=[haltEvent]).start() + Process(target=garageLoop, args=[queue]).start() + signal(SIGTERM, cleanExit(unicorn, haltEvent, queue)) + + unicorn.brightness(0.3) + + characterDisplay = CharacterDisplay(unicorn) + timeDisplay = TimeDisplay(characterDisplay, topRow=15) + weatherDisplay = WeatherDisplay(characterDisplay, topRow=9) + garageDisplay = GarageDisplay(characterDisplay, topRow=3) + + while True: + timeDisplay.showTime() + weatherDisplay.showWeather() + garageDisplay.showGarageState() + sleep(DISPLAY_UPDATE_INTERVAL_IN_SECONDS) + + +def weatherLoop(haltEvent): + weatherUpdater = WeatherUpdater(configFile=CONFIG_FILE) + + while not haltEvent.is_set(): + weatherUpdater.updateWeather() + haltEvent.wait(timeout=WEATHER_UPDATE_INTERVAL_IN_SECONDS) + + weatherUpdater.clearWeather() + + +def garageLoop(queue): + garageNotify = Process(target=garageNotifyLoop, args=[queue]) + garageNotify.start() + + garageUpdater = GarageUpdater(configFile=CONFIG_FILE) + isRunning = True + + while isRunning: + garageUpdater.updateGarageState() + + try: + item = queue.get(timeout=GARAGE_UPDATE_INTERVAL_IN_SECONDS) + isRunning = item != 'halt' + except Empty: + pass + + garageUpdater.clearGarageState() + garageNotify.terminate() + + +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_ diff --git a/chutney/time/__init__.py b/chutney/time/__init__.py new file mode 100644 index 0000000..8804a97 --- /dev/null +++ b/chutney/time/__init__.py @@ -0,0 +1 @@ +from .time_display import TimeDisplay diff --git a/time_display.py b/chutney/time/time_display.py similarity index 98% rename from time_display.py rename to chutney/time/time_display.py index 1f939fd..36624b8 100644 --- a/time_display.py +++ b/chutney/time/time_display.py @@ -1,4 +1,4 @@ -import colors +import chutney.colors as colors from datetime import datetime diff --git a/chutney/weather/__init__.py b/chutney/weather/__init__.py new file mode 100644 index 0000000..ef5fa67 --- /dev/null +++ b/chutney/weather/__init__.py @@ -0,0 +1,2 @@ +from .weather_display import WeatherDisplay +from .weather_updater import WeatherUpdater diff --git a/weather_display.py b/chutney/weather/weather_display.py similarity index 96% rename from weather_display.py rename to chutney/weather/weather_display.py index bd780a7..8eff42f 100644 --- a/weather_display.py +++ b/chutney/weather/weather_display.py @@ -1,4 +1,4 @@ -import colors +import chutney.colors as colors class WeatherDisplay: @@ -10,7 +10,7 @@ class WeatherDisplay: self.digitWidth = 4 self.currentTemperature = '' self.color = colors.ORANGE - self.weatherFile = 'weather.txt' + self.weatherFile = 'data/weather.txt' def showWeather(self): temperature = self.getTemperature() diff --git a/weather_updater.py b/chutney/weather/weather_updater.py similarity index 94% rename from weather_updater.py rename to chutney/weather/weather_updater.py index c0da6fb..ccb2af1 100644 --- a/weather_updater.py +++ b/chutney/weather/weather_updater.py @@ -17,8 +17,8 @@ class WeatherUpdater: self.uri = f'https://{host}/api/weather?lat={lat}&lon={lon}&units=metric' self.auth = (user, password) - self.weatherTempFile = 'weather.tmp' - self.weatherFile = 'weather.txt' + self.weatherTempFile = 'data/weather.tmp' + self.weatherFile = 'data/weather.txt' self.persistTemperature('init') def updateWeather(self): diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore