Implemented matrix and climate abstractions, and fixed startup
This commit is contained in:
parent
393b14374b
commit
d9cd7a8688
9 changed files with 141 additions and 23 deletions
|
@ -1,12 +1,34 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from config import climate_log_file, dht22_pin
|
from config import climate_log_file, dht22_pin
|
||||||
from handler.climate import Dht22Sensor
|
|
||||||
from handler.matrix import MatrixDisplay
|
|
||||||
|
|
||||||
matrix_display = MatrixDisplay()
|
from handler.matrix.matrix_display import MatrixDisplayAdapter
|
||||||
dht22_sensor = Dht22Sensor(dht22_pin)
|
from handler.climate.climate import ClimateAdapter
|
||||||
|
|
||||||
|
climate_sensor: ClimateAdapter = None
|
||||||
|
matrix_display: MatrixDisplayAdapter = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
from handler.matrix.luma_matrix_display import LumaMatrixDisplay
|
||||||
|
|
||||||
|
matrix_display = LumaMatrixDisplay()
|
||||||
|
except:
|
||||||
|
from handler.matrix.dummy_matrix_display import DummyMatrixDisplay
|
||||||
|
|
||||||
|
logging.info("Using Dummy Matrix Display")
|
||||||
|
matrix_display = DummyMatrixDisplay()
|
||||||
|
|
||||||
|
try:
|
||||||
|
from handler.climate.dht22_climate import Dht22Climate
|
||||||
|
|
||||||
|
climate_sensor = Dht22Climate(dht22_pin)
|
||||||
|
except:
|
||||||
|
from handler.climate.dummy_climate import DummyClimate
|
||||||
|
|
||||||
|
logging.info("Using Dummy Climate")
|
||||||
|
climate_sensor = DummyClimate()
|
||||||
|
|
||||||
|
|
||||||
async def log_temperature():
|
async def log_temperature():
|
||||||
|
@ -16,7 +38,7 @@ async def log_temperature():
|
||||||
f.write("timestamp,temperature,humidity\n")
|
f.write("timestamp,temperature,humidity\n")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
measurements = dht22_sensor.read()
|
measurements = climate_sensor.read()
|
||||||
if measurements is not None:
|
if measurements is not None:
|
||||||
with open(climate_log_file, "a") as f:
|
with open(climate_log_file, "a") as f:
|
||||||
f.write(
|
f.write(
|
||||||
|
|
|
@ -20,6 +20,10 @@ class ActionQueue:
|
||||||
while len(self.queued_actions) > 0:
|
while len(self.queued_actions) > 0:
|
||||||
self.state = QueueState.POPPING
|
self.state = QueueState.POPPING
|
||||||
action = self.queued_actions.pop(0)
|
action = self.queued_actions.pop(0)
|
||||||
|
|
||||||
|
if action is None:
|
||||||
|
break
|
||||||
|
|
||||||
await action[0](*(action[1]), **(action[2]))
|
await action[0](*(action[1]), **(action[2]))
|
||||||
|
|
||||||
self.state = QueueState.IDLE
|
self.state = QueueState.IDLE
|
||||||
|
|
11
src/handler/climate/climate.py
Normal file
11
src/handler/climate/climate.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class ClimateAdapter:
|
||||||
|
def __init__(self):
|
||||||
|
self.last_read = None
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
raise NotImplemented()
|
||||||
|
|
||||||
|
def get_last_read(self):
|
||||||
|
if self.last_read is None:
|
||||||
|
return self.read()
|
||||||
|
return self.last_read
|
|
@ -1,20 +1,17 @@
|
||||||
import Adafruit_DHT
|
import Adafruit_DHT
|
||||||
|
from handler.climate.climate import ClimateAdapter
|
||||||
|
|
||||||
class Dht22Sensor:
|
|
||||||
|
class Dht22Climate(ClimateAdapter):
|
||||||
def __init__(self, pin):
|
def __init__(self, pin):
|
||||||
|
super().__init__()
|
||||||
self.sensor = Adafruit_DHT.AM2302
|
self.sensor = Adafruit_DHT.AM2302
|
||||||
self.pin = pin
|
self.pin = pin
|
||||||
self.last_read = None
|
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
humidity, temperature = Adafruit_DHT.read_retry(self.sensor, self.pin)
|
humidity, temperature = Adafruit_DHT.read_retry(self.sensor, self.pin)
|
||||||
if humidity is not None and temperature is not None:
|
if humidity is not None and temperature is not None:
|
||||||
self.last_read = {'temperature': temperature, 'humidity': humidity}
|
self.last_read = {"temperature": temperature, "humidity": humidity}
|
||||||
return self.last_read
|
return self.last_read
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_last_read(self):
|
|
||||||
if self.last_read is None:
|
|
||||||
self.read()
|
|
||||||
return self.last_read
|
|
7
src/handler/climate/dummy_climate.py
Normal file
7
src/handler/climate/dummy_climate.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from handler.climate.climate import ClimateAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class DummyClimate(ClimateAdapter):
|
||||||
|
def read(self):
|
||||||
|
self.last_read = {"temperature": 22.3, "humidity": 50.1}
|
||||||
|
return self.last_read
|
40
src/handler/matrix/dummy_matrix_display.py
Normal file
40
src/handler/matrix/dummy_matrix_display.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
from handler.matrix.matrix_display import MatrixDisplayAdapter
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class DummyMatrixDisplay(MatrixDisplayAdapter):
|
||||||
|
def __init__(self, *, contrast=0, text_speed=0.02) -> None:
|
||||||
|
self.contrast = contrast
|
||||||
|
self.text_speed = text_speed
|
||||||
|
logging.info("DummyMatrixDisplay: Initialized")
|
||||||
|
|
||||||
|
def set_contrast(self, contrast: int):
|
||||||
|
"""Set contrast for all actions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
contrast (int): [0, 255]
|
||||||
|
"""
|
||||||
|
self.contrast = contrast
|
||||||
|
logging.info(f"DummyMatrixDisplay: setting contrast to {contrast}")
|
||||||
|
|
||||||
|
def show_text(self, text):
|
||||||
|
logging.info(f"DummyMatrixDisplay: Showing text '{text}'")
|
||||||
|
|
||||||
|
def flash(self, count=1, contrast=None):
|
||||||
|
if contrast:
|
||||||
|
logging.info(
|
||||||
|
f"DummyMatrixDisplay: flashing {count} times with contrast {contrast}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.info(
|
||||||
|
f"DummyMatrixDisplay: flashing {count} times with general contrast"
|
||||||
|
)
|
||||||
|
|
||||||
|
def turn_off(self):
|
||||||
|
logging.info(f"DummyMatrixDisplay: turning off")
|
||||||
|
|
||||||
|
def turn_full(self):
|
||||||
|
logging.info(f"DummyMatrixDisplay: turning full")
|
||||||
|
|
||||||
|
def show_current_time(self):
|
||||||
|
logging.info(f"DummyMatrixDisplay: showing time")
|
|
@ -5,10 +5,12 @@ from luma.core.render import canvas
|
||||||
from luma.led_matrix.device import max7219
|
from luma.led_matrix.device import max7219
|
||||||
from luma.core import legacy
|
from luma.core import legacy
|
||||||
from luma.core.virtual import viewport
|
from luma.core.virtual import viewport
|
||||||
from luma.core.legacy.font import proportional, CP437_FONT, LCD_FONT
|
from luma.core.legacy.font import proportional, CP437_FONT
|
||||||
|
|
||||||
|
from handler.matrix.matrix_display import MatrixDisplayAdapter
|
||||||
|
|
||||||
|
|
||||||
class MatrixDisplay:
|
class LumaMatrixDisplay(MatrixDisplayAdapter):
|
||||||
def __init__(self, *, contrast=0, text_speed=0.02) -> None:
|
def __init__(self, *, contrast=0, text_speed=0.02) -> None:
|
||||||
self.contrast = contrast
|
self.contrast = contrast
|
||||||
self.text_speed = text_speed
|
self.text_speed = text_speed
|
||||||
|
@ -25,7 +27,7 @@ class MatrixDisplay:
|
||||||
Args:
|
Args:
|
||||||
contrast (int): [0, 255]
|
contrast (int): [0, 255]
|
||||||
"""
|
"""
|
||||||
self.contrast = contrast
|
super().set_contrast(contrast)
|
||||||
self.device.contrast(self.contrast)
|
self.device.contrast(self.contrast)
|
||||||
|
|
||||||
def show_text(self, text):
|
def show_text(self, text):
|
27
src/handler/matrix/matrix_display.py
Normal file
27
src/handler/matrix/matrix_display.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
class MatrixDisplayAdapter:
|
||||||
|
def __init__(self, *, contrast=0, text_speed=0.02) -> None:
|
||||||
|
self.contrast = contrast
|
||||||
|
self.text_speed = text_speed
|
||||||
|
|
||||||
|
def set_contrast(self, contrast: int):
|
||||||
|
"""Set contrast for all actions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
contrast (int): [0, 255]
|
||||||
|
"""
|
||||||
|
self.contrast = contrast
|
||||||
|
|
||||||
|
def show_text(self, text):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def flash(self, count=1, contrast=None):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def turn_off(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def turn_full(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def show_current_time(self):
|
||||||
|
raise NotImplementedError()
|
22
src/main.py
22
src/main.py
|
@ -1,17 +1,18 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
from actions import dht22_sensor, display_time, log_temperature, matrix_display
|
from actions import climate_sensor, display_time, log_temperature, matrix_display
|
||||||
from config import climate_log_file
|
from config import climate_log_file
|
||||||
from handler.action_queue import ActionQueue
|
from handler.action_queue import ActionQueue
|
||||||
from handler.history import get_recent_entries
|
from handler.history import get_recent_entries
|
||||||
|
|
||||||
queue = ActionQueue()
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
queue.set_idle_action(display_time)
|
|
||||||
|
|
||||||
|
queue = ActionQueue()
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
origins = [
|
origins = [
|
||||||
|
@ -30,8 +31,6 @@ app.add_middleware(
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
asyncio.create_task(log_temperature())
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/time")
|
@app.post("/time")
|
||||||
async def start_time_loop():
|
async def start_time_loop():
|
||||||
|
@ -53,7 +52,7 @@ async def turn_off():
|
||||||
|
|
||||||
@app.post("/temperature")
|
@app.post("/temperature")
|
||||||
async def temperature():
|
async def temperature():
|
||||||
measurements = dht22_sensor.get_last_read()
|
measurements = climate_sensor.get_last_read()
|
||||||
if measurements is None:
|
if measurements is None:
|
||||||
return {"message": "Failed to read temperature"}
|
return {"message": "Failed to read temperature"}
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ async def temperature():
|
||||||
|
|
||||||
@app.post("/humidity")
|
@app.post("/humidity")
|
||||||
async def humidity():
|
async def humidity():
|
||||||
measurements = dht22_sensor.get_last_read()
|
measurements = climate_sensor.get_last_read()
|
||||||
if measurements is None:
|
if measurements is None:
|
||||||
return {"message": "Failed to read humidity"}
|
return {"message": "Failed to read humidity"}
|
||||||
|
|
||||||
|
@ -104,3 +103,12 @@ async def display_message(body: dict):
|
||||||
message_text = body.get("message")
|
message_text = body.get("message")
|
||||||
await queue.add_action_to_queue(matrix_display.show_text, message_text)
|
await queue.add_action_to_queue(matrix_display.show_text, message_text)
|
||||||
return {"message": "Message displayed"}
|
return {"message": "Message displayed"}
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await queue.set_idle_action(display_time)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.create_task(main())
|
||||||
|
asyncio.create_task(log_temperature())
|
||||||
|
|
Loading…
Reference in a new issue