Add garage indicators

This commit is contained in:
Mike Cifelli 2022-12-26 19:37:50 -05:00
parent b2e20763d9
commit 4cac468c01
9 changed files with 155 additions and 9 deletions

1
.gitignore vendored
View File

@ -2,4 +2,5 @@ __pycache__/
*.swp *.swp
*.tmp *.tmp
chutney.cfg chutney.cfg
garage.json
weather.txt weather.txt

View File

@ -14,15 +14,15 @@
import colors import colors
BLACK = [0, 0, 0]
class CharacterDisplay: class CharacterDisplay:
def __init__(self, unicorn, minX=0, maxX=15): def __init__(self, unicorn, minX=0, maxX=15, minY=0, maxY=15):
self.unicorn = unicorn self.unicorn = unicorn
self.minX = minX self.minX = minX
self.maxX = maxX self.maxX = maxX
self.minY = minY
self.maxY = maxY
self.digits = { self.digits = {
'0': self.displayZero, '0': self.displayZero,
@ -49,8 +49,8 @@ class CharacterDisplay:
self.unicorn.show() self.unicorn.show()
def clearRow(self, y): def clearRow(self, y, rowHeight=5):
for row in range(y, y-5, -1): for row in range(y, y-rowHeight, -1):
for col in range(self.minX, self.maxX + 1): for col in range(self.minX, self.maxX + 1):
self.setPixel(col, row, colors.BLACK) self.setPixel(col, row, colors.BLACK)
@ -77,6 +77,22 @@ class CharacterDisplay:
self.setPixel(x+1, y-4, colors.BLACK) self.setPixel(x+1, y-4, colors.BLACK)
self.unicorn.show() self.unicorn.show()
def displaySquare(self, x, y, outlineColor, fillColor):
self.fullLine(x, y, outlineColor)
self.leftSide(x, y-1, outlineColor)
self.leftSide(x, y-2, outlineColor)
self.fullLine(x, y-3, outlineColor)
self.rightSide(x+1, y, outlineColor)
self.rightSide(x+1, y-1, outlineColor)
self.rightSide(x+1, y-2, outlineColor)
self.rightSide(x+1, y-3, outlineColor)
self.leftSide(x+1, y-1, fillColor)
self.leftSide(x+1, y-2, fillColor)
self.leftSide(x+2, y-1, fillColor)
self.leftSide(x+2, y-2, fillColor)
self.unicorn.show()
def displayZero(self, x, y, color): def displayZero(self, x, y, color):
self.fullLine(x, y, color) self.fullLine(x, y, color)
self.bothSides(x, y-1, color) self.bothSides(x, y-1, color)
@ -162,5 +178,5 @@ class CharacterDisplay:
self.setPixel(start+2, row, color) self.setPixel(start+2, row, color)
def setPixel(self, x, y, color): def setPixel(self, x, y, color):
if x >= self.minX and x <= self.maxX: if x >= self.minX and x <= self.maxX and y >= self.minY and y <= self.maxY:
self.unicorn.set_pixel(x, y, *color) self.unicorn.set_pixel(x, y, *color)

View File

@ -1,6 +1,8 @@
import sys import sys
from character_display import CharacterDisplay from character_display import CharacterDisplay
from garage_display import GarageDisplay
from garage_updater import GarageUpdater
from multiprocessing import Event from multiprocessing import Event
from multiprocessing import Process from multiprocessing import Process
from signal import signal from signal import signal
@ -18,7 +20,8 @@ except ImportError:
unicorn.rotation(180) unicorn.rotation(180)
WEATHER_UPDATE_INTERVAL_IN_SECONDS = 60 WEATHER_UPDATE_INTERVAL_IN_SECONDS = 10
GARAGE_UPDATE_INTERVAL_IN_SECONDS = 10
DISPLAY_UPDATE_INTERVAL_IN_SECONDS = 0.5 DISPLAY_UPDATE_INTERVAL_IN_SECONDS = 0.5
CONFIG_FILE = 'chutney.cfg' CONFIG_FILE = 'chutney.cfg'
@ -31,13 +34,18 @@ def main():
weatherThread = Process(target=weatherLoop, args=[haltEvent]) weatherThread = Process(target=weatherLoop, args=[haltEvent])
weatherThread.start() weatherThread.start()
garageThread = Process(target=garageLoop, args=[haltEvent])
garageThread.start()
characterDisplay = CharacterDisplay(unicorn) characterDisplay = CharacterDisplay(unicorn)
timeDisplay = TimeDisplay(characterDisplay, topRow=15) timeDisplay = TimeDisplay(characterDisplay, topRow=15)
weatherDisplay = WeatherDisplay(characterDisplay, topRow=9) weatherDisplay = WeatherDisplay(characterDisplay, topRow=9)
garageDisplay = GarageDisplay(characterDisplay, topRow=3)
while True: while True:
timeDisplay.showTime() timeDisplay.showTime()
weatherDisplay.showWeather() weatherDisplay.showWeather()
garageDisplay.showGarageState()
sleep(DISPLAY_UPDATE_INTERVAL_IN_SECONDS) sleep(DISPLAY_UPDATE_INTERVAL_IN_SECONDS)
@ -51,6 +59,16 @@ def weatherLoop(haltEvent):
weatherUpdater.clearWeather() weatherUpdater.clearWeather()
def garageLoop(haltEvent):
garageUpdater = GarageUpdater(configFile=CONFIG_FILE)
while not haltEvent.is_set():
garageUpdater.updateGarageState()
haltEvent.wait(timeout=GARAGE_UPDATE_INTERVAL_IN_SECONDS)
garageUpdater.clearGarageState()
def cleanExit(unicorn, haltEvent): def cleanExit(unicorn, haltEvent):
def _exit_(signum, frame): def _exit_(signum, frame):
unicorn.off() unicorn.off()

View File

@ -4,3 +4,6 @@ user=user
pass=password pass=password
lat=lat lat=lat
lon=lon lon=lon
[garage]
uri=uri

View File

@ -1,4 +1,6 @@
BLACK = [0, 0, 0 ] BLACK = [0, 0, 0 ]
WHITE = [255, 255, 255]
GRAY = [50, 50, 50 ]
RED = [255, 0, 0 ] RED = [255, 0, 0 ]
PINK = [255, 0, 64 ] PINK = [255, 0, 64 ]
GREEN = [0, 255, 0 ] GREEN = [0, 255, 0 ]

68
garage_display.py Normal file
View File

@ -0,0 +1,68 @@
import colors
import json
class GarageDisplay:
def __init__(self, characterDisplay, topRow):
self.characterDisplay = characterDisplay
self.topRow = topRow
self.westDoorStartColumn = 0
self.eastDoorStartColumn = 5
self.currentState = {}
self.closedOutlineColor = colors.RED
self.closedFillColor = colors.GRAY
self.openOutlineColor = colors.WHITE
self.openFillColor = colors.BLUE
self.garageFile = 'garage.json'
def showGarageState(self):
state = self.getGarageState()
if state != self.currentState:
self.currentState = state
self.characterDisplay.clearRow(self.topRow, rowHeight=4)
if self.isGarageStateValid(state):
self.showState(state)
def getGarageState(self):
try:
with open(self.garageFile) as file:
return json.load(file)
except Exception as e:
print(e, flush=True)
return {'error': True}
def isGarageStateValid(self, state):
return not 'error' in state
def showState(self, state):
if state["west-door"] == "closed":
self.characterDisplay.displaySquare(
x=self.westDoorStartColumn,
y=self.topRow,
outlineColor=self.closedOutlineColor,
fillColor=self.closedFillColor
)
else:
self.characterDisplay.displaySquare(
x=self.westDoorStartColumn,
y=self.topRow,
outlineColor=self.openOutlineColor,
fillColor=self.openFillColor
)
if state["east-door"] == "closed":
self.characterDisplay.displaySquare(
x=self.eastDoorStartColumn,
y=self.topRow,
outlineColor=self.closedOutlineColor,
fillColor=self.closedFillColor
)
else:
self.characterDisplay.displaySquare(
x=self.eastDoorStartColumn,
y=self.topRow,
outlineColor=self.openOutlineColor,
fillColor=self.openFillColor
)

38
garage_updater.py Normal file
View File

@ -0,0 +1,38 @@
import os
import requests
from configparser import ConfigParser
class GarageUpdater:
def __init__(self, configFile):
config = ConfigParser()
config.read(configFile)
self.uri = config['garage'].get('uri')
self.garageTempFile = 'garage.tmp'
self.garageFile = 'garage.json'
self.persistGarageState('{"error": "init"}')
def updateGarageState(self):
try:
state = self.getGarageState()
self.persistGarageState(state)
except requests.exceptions.ConnectionError as e:
print(e, flush=True)
self.persistGarageState('{"error": "connection"}')
except requests.exceptions.Timeout as e:
print(e, flush=True)
self.persistGarageState('{"error": "timeout"}')
def clearGarageState(self):
self.persistGarageState('{"error": "halted"}')
def getGarageState(self):
return requests.get(self.uri, timeout=3).text
def persistGarageState(self, state):
with open(self.garageTempFile, 'w') as file:
file.write(state)
os.replace(self.garageTempFile, self.garageFile)

View File

@ -9,7 +9,7 @@ class WeatherDisplay:
self.oneDigitStartColumn = 13 self.oneDigitStartColumn = 13
self.digitWidth = 4 self.digitWidth = 4
self.currentTemperature = '' self.currentTemperature = ''
self.color = colors.BLUE self.color = colors.PINK
self.weatherFile = 'weather.txt' self.weatherFile = 'weather.txt'
def showWeather(self): def showWeather(self):

View File

@ -47,7 +47,7 @@ class WeatherUpdater:
return currentWeather[0]['intervals'][0]['values']['temperature'] return currentWeather[0]['intervals'][0]['values']['temperature']
def getWeather(self): def getWeather(self):
return requests.get(self.uri, auth=self.auth).json() return requests.get(self.uri, auth=self.auth, timeout=3).json()
def persistTemperature(self, temperature): def persistTemperature(self, temperature):
with open(self.weatherTempFile, 'w') as file: with open(self.weatherTempFile, 'w') as file: