Run weather updates in a separate thread
This commit is contained in:
parent
5d1caff78c
commit
fc77feedb2
|
@ -1,3 +1,4 @@
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.swp
|
*.swp
|
||||||
chutney.cfg
|
chutney.cfg
|
||||||
|
weather.txt
|
||||||
|
|
36
chutney.py
36
chutney.py
|
@ -1,10 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
|
|
||||||
from character_display import CharacterDisplay
|
from character_display import CharacterDisplay
|
||||||
from signal import signal, SIGTERM
|
from signal import signal, SIGTERM
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from time_display import TimeDisplay
|
from time_display import TimeDisplay
|
||||||
from weather_display import WeatherDisplay
|
from weather_display import WeatherDisplay
|
||||||
|
from weather_updater import WeatherUpdater
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import unicornhathd as unicorn
|
import unicornhathd as unicorn
|
||||||
|
@ -14,34 +16,44 @@ except ImportError:
|
||||||
unicorn.rotation(180)
|
unicorn.rotation(180)
|
||||||
|
|
||||||
|
|
||||||
def cleanExit(unicorn):
|
WEATHER_UPDATE_INTERVAL_IN_SECONDS = 10
|
||||||
|
DISPLAY_UPDATE_INTERVAL_IN_SECONDS = 0.5
|
||||||
|
CONFIG_FILE = 'chutney.cfg'
|
||||||
|
|
||||||
|
|
||||||
|
def cleanExit(unicorn, haltEvent):
|
||||||
def _exit_(signum, frame):
|
def _exit_(signum, frame):
|
||||||
unicorn.off()
|
unicorn.off()
|
||||||
|
haltEvent.set()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
return _exit_
|
return _exit_
|
||||||
|
|
||||||
|
|
||||||
|
def weatherLoop(haltEvent):
|
||||||
|
weatherUpdater = WeatherUpdater(configFile=CONFIG_FILE)
|
||||||
|
|
||||||
|
while not haltEvent.is_set():
|
||||||
|
weatherUpdater.updateWeather()
|
||||||
|
sleep(WEATHER_UPDATE_INTERVAL_IN_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
signal(SIGTERM, cleanExit(unicorn))
|
haltEvent = threading.Event()
|
||||||
|
signal(SIGTERM, cleanExit(unicorn, haltEvent))
|
||||||
unicorn.brightness(0.3)
|
unicorn.brightness(0.3)
|
||||||
|
|
||||||
|
weatherThread = threading.Thread(target=weatherLoop, args=[haltEvent])
|
||||||
|
weatherThread.start()
|
||||||
|
|
||||||
characterDisplay = CharacterDisplay(unicorn)
|
characterDisplay = CharacterDisplay(unicorn)
|
||||||
timeDisplay = TimeDisplay(characterDisplay, topRow=15)
|
timeDisplay = TimeDisplay(characterDisplay, topRow=15)
|
||||||
weatherDisplay = WeatherDisplay(characterDisplay, topRow=9, configFile='chutney.cfg')
|
weatherDisplay = WeatherDisplay(characterDisplay, topRow=9)
|
||||||
|
|
||||||
x = 0
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
timeDisplay.showTime()
|
timeDisplay.showTime()
|
||||||
|
|
||||||
if (x == 0):
|
|
||||||
weatherDisplay.showWeather()
|
weatherDisplay.showWeather()
|
||||||
|
sleep(DISPLAY_UPDATE_INTERVAL_IN_SECONDS)
|
||||||
x = x + 1
|
|
||||||
x = x % 120
|
|
||||||
|
|
||||||
sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -16,15 +16,11 @@ class TimeDisplay:
|
||||||
self.timeDotsColumn = 7
|
self.timeDotsColumn = 7
|
||||||
self.minuteStartColumn = 9
|
self.minuteStartColumn = 9
|
||||||
|
|
||||||
self.characterDisplay.displayTimeDots(
|
|
||||||
x=self.timeDotsColumn,
|
|
||||||
y=self.topRow,
|
|
||||||
color=self.color
|
|
||||||
)
|
|
||||||
|
|
||||||
def showTime(self):
|
def showTime(self):
|
||||||
self.showHour(self.getHourDigits())
|
self.showHour(self.getHourDigits())
|
||||||
|
self.showTimeDots()
|
||||||
self.showMinute(self.getMinuteDigits())
|
self.showMinute(self.getMinuteDigits())
|
||||||
|
self.currentColor = self.color
|
||||||
|
|
||||||
def showHour(self, hour):
|
def showHour(self, hour):
|
||||||
if hour[0] != self.currentHour[0]:
|
if hour[0] != self.currentHour[0]:
|
||||||
|
@ -39,6 +35,14 @@ class TimeDisplay:
|
||||||
self.currentHour[1] = hour[1]
|
self.currentHour[1] = hour[1]
|
||||||
self.showDigit(self.hourStartColumn + 4, hour[1])
|
self.showDigit(self.hourStartColumn + 4, hour[1])
|
||||||
|
|
||||||
|
def showTimeDots(self):
|
||||||
|
if self.color != self.currentColor:
|
||||||
|
self.characterDisplay.displayTimeDots(
|
||||||
|
x=self.timeDotsColumn,
|
||||||
|
y=self.topRow,
|
||||||
|
color=self.color
|
||||||
|
)
|
||||||
|
|
||||||
def showMinute(self, minute):
|
def showMinute(self, minute):
|
||||||
if minute[0] != self.currentMinute[0]:
|
if minute[0] != self.currentMinute[0]:
|
||||||
self.currentMinute[0] = minute[0]
|
self.currentMinute[0] = minute[0]
|
||||||
|
|
|
@ -1,37 +1,24 @@
|
||||||
import colors
|
import colors
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from configparser import ConfigParser
|
|
||||||
|
|
||||||
|
|
||||||
class WeatherDisplay:
|
class WeatherDisplay:
|
||||||
def __init__(self, characterDisplay, topRow, configFile):
|
def __init__(self, characterDisplay, topRow):
|
||||||
self.characterDisplay = characterDisplay
|
self.characterDisplay = characterDisplay
|
||||||
self.topRow = topRow
|
self.topRow = topRow
|
||||||
self.currentTemperature = [9, 9]
|
self.currentTemperature = [9, 9]
|
||||||
self.color = colors.BLUE
|
self.color = colors.BLUE
|
||||||
|
self.weatherFile = 'weather.txt'
|
||||||
config = ConfigParser()
|
|
||||||
config.read(configFile)
|
|
||||||
|
|
||||||
host = config['weather'].get('host')
|
|
||||||
user = config['weather'].get('user')
|
|
||||||
password = config['weather'].get('pass')
|
|
||||||
lat = config['weather'].get('lat')
|
|
||||||
lon = config['weather'].get('lon')
|
|
||||||
|
|
||||||
self.uri = f'https://{host}/api/weather?lat={lat}&lon={lon}&units=metric'
|
|
||||||
self.auth = (user, password)
|
|
||||||
|
|
||||||
def showWeather(self):
|
def showWeather(self):
|
||||||
try:
|
try:
|
||||||
self.showTemperature(
|
self.showTemperature(
|
||||||
self.getTemperatureCharacters(self.getTemperature()))
|
self.getTemperatureCharacters(self.getTemperature()))
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
print("connection error", flush=True)
|
print('connection error', flush=True)
|
||||||
self.characterDisplay.clearRow(self.topRow)
|
self.characterDisplay.clearRow(self.topRow)
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
print("timeout", flush=True)
|
print('timeout', flush=True)
|
||||||
self.characterDisplay.clearRow(self.topRow)
|
self.characterDisplay.clearRow(self.topRow)
|
||||||
|
|
||||||
def showTemperature(self, temperature):
|
def showTemperature(self, temperature):
|
||||||
|
@ -53,7 +40,14 @@ class WeatherDisplay:
|
||||||
self.currentTemperature = temperature
|
self.currentTemperature = temperature
|
||||||
|
|
||||||
def getTemperature(self):
|
def getTemperature(self):
|
||||||
return list(filter(lambda t: t["timestep"] == 'current', requests.get(self.uri, auth=self.auth, timeout=2).json()["tomorrow"]["data"]['timelines']))[0]["intervals"][0]["values"]["temperature"]
|
# TODO - don't use a file left around from a previous run
|
||||||
|
# TODO - clear temp on errors from updater
|
||||||
|
try:
|
||||||
|
with open(self.weatherFile) as file:
|
||||||
|
return int(file.readline().strip())
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return 0
|
||||||
|
|
||||||
def getTemperatureCharacters(self, temperature):
|
def getTemperatureCharacters(self, temperature):
|
||||||
return list(str(round(temperature)))
|
return list(str(round(temperature)))
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from configparser import ConfigParser
|
||||||
|
|
||||||
|
|
||||||
|
class WeatherUpdater:
|
||||||
|
def __init__(self, configFile):
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read(configFile)
|
||||||
|
|
||||||
|
host = config['weather'].get('host')
|
||||||
|
user = config['weather'].get('user')
|
||||||
|
password = config['weather'].get('pass')
|
||||||
|
lat = config['weather'].get('lat')
|
||||||
|
lon = config['weather'].get('lon')
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
# TODO - clear weather file on init and on failure
|
||||||
|
|
||||||
|
def updateWeather(self):
|
||||||
|
try:
|
||||||
|
temperature = round(self.getTemperature())
|
||||||
|
self.persistTemperature(temperature)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print('connection error', flush=True)
|
||||||
|
self.characterDisplay.clearRow(self.topRow)
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
print('timeout', flush=True)
|
||||||
|
self.characterDisplay.clearRow(self.topRow)
|
||||||
|
|
||||||
|
def getTemperature(self):
|
||||||
|
weather = self.getWeather()
|
||||||
|
currentWeather = list(
|
||||||
|
filter(
|
||||||
|
lambda t: t['timestep'] == 'current',
|
||||||
|
weather['tomorrow']['data']['timelines']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return currentWeather[0]['intervals'][0]['values']['temperature']
|
||||||
|
|
||||||
|
def getWeather(self):
|
||||||
|
return requests.get(self.uri, auth=self.auth).json()
|
||||||
|
|
||||||
|
def persistTemperature(self, temperature):
|
||||||
|
with open(self.weatherTempFile, 'w') as file:
|
||||||
|
file.write(str(temperature))
|
||||||
|
|
||||||
|
os.replace(self.weatherTempFile, self.weatherFile)
|
Loading…
Reference in New Issue