Add garage indicators
This commit is contained in:
parent
b2e20763d9
commit
4cac468c01
|
@ -2,4 +2,5 @@ __pycache__/
|
||||||
*.swp
|
*.swp
|
||||||
*.tmp
|
*.tmp
|
||||||
chutney.cfg
|
chutney.cfg
|
||||||
|
garage.json
|
||||||
weather.txt
|
weather.txt
|
||||||
|
|
|
@ -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)
|
||||||
|
|
20
chutney.py
20
chutney.py
|
@ -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()
|
||||||
|
|
|
@ -4,3 +4,6 @@ user=user
|
||||||
pass=password
|
pass=password
|
||||||
lat=lat
|
lat=lat
|
||||||
lon=lon
|
lon=lon
|
||||||
|
|
||||||
|
[garage]
|
||||||
|
uri=uri
|
||||||
|
|
|
@ -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 ]
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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)
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue