Background services running properly; New Bedscale Entity implemented

This commit is contained in:
Maximilian Giller 2025-01-16 19:14:34 +01:00
parent 9aba0279d7
commit 5defaba45f
5 changed files with 106 additions and 51 deletions

View file

@ -0,0 +1 @@
from .bedscale_entity import BedscaleEntity

View file

@ -17,20 +17,30 @@ class BedscaleWeightResult:
class BedscaleEntity(Entity):
def __init__(self, *, ip_address: str, id, name, room):
def __init__(self, *, ip_address: str, id: str, name: str, room: str):
super().__init__(id=id, name=name, room=room, device_type="bedscale")
self._ip_address = ip_address
async def poll_weights(self) -> BedscaleWeightResult:
loop = asyncio.get_event_loop()
tr_request = loop.run_in_executor(None, self.__poll_scale__, "tr")
tl_request = loop.run_in_executor(None, self.__poll_scale__, "tl")
br_request = loop.run_in_executor(None, self.__poll_scale__, "br")
bl_request = loop.run_in_executor(None, self.__poll_scale__, "bl")
results = BedscaleWeightResult(
tr=await asyncio.run_in_executor(None, self.__poll_scale__, "tr"),
tl=await asyncio.run_in_executor(None, self.__poll_scale__, "tl"),
br=await asyncio.run_in_executor(None, self.__poll_scale__, "br"),
bl=await asyncio.run_in_executor(None, self.__poll_scale__, "bl"),
tr=await tr_request,
tl=await tl_request,
br=await br_request,
bl=await bl_request,
)
# TODO: Sanity checks
# TODO: Keep track of empty-bed weight
return results
def __poll_scale__(self, leg: str) -> float | None:

View file

@ -5,7 +5,7 @@ import os
from statistics import median
from typing import Optional
import requests as r
from ..hue import hue
from ...endpoints.hue import hue
import logging
file_path: str = "bettwaage.csv"

View file

@ -4,53 +4,79 @@ from fastapi import APIRouter
import os
import csv
from .handlers.bett import file_path, local_history, log_bed_weights
from bridges.bedscale import BedscaleEntity
router = APIRouter()
@router.get("/file", tags=["file"])
async def get_file():
with open(file_path, "r", encoding="UTF-8") as fp:
return HTMLResponse("\n".join(fp.readlines()))
@router.get("/history")
async def get_history(count: int = None) -> list[dict]:
points = []
with open(file_path, "r", encoding="UTF-8") as fp:
reader = csv.DictReader(fp, delimiter=";")
for row in reader:
if not row:
continue
points.append(
{
"timestamp": row["timestamp"],
"total": float(row["total"]),
"tl": float(row["tl"]),
"tr": float(row["tr"]),
"bl": float(row["bl"]),
"br": float(row["br"]),
}
bedscale = BedscaleEntity(
ip_address="http://192.168.178.110:80",
id="bedscale",
name="Bettwaage",
room="Max Zimmer",
)
if count:
return points[-count]
else:
return points
history = []
measure_delay_secs = 1
history_length_secs = 60 * 10
async def bedscale_service():
global history
global bedscale
history_max_num = int(history_length_secs / measure_delay_secs)
while True:
r = await bedscale.poll_weights()
history.append(r)
if len(history) > history_max_num:
history = history[-history_max_num:]
await asyncio.sleep(1)
@router.get("/latest")
async def get_latest():
if len(local_history) == 0:
if len(history) == 0:
return HTMLResponse(status_code=200, content="No data given yet")
return JSONResponse(local_history[-1])
return history[-1]
@router.delete("/delete", tags=["file"])
async def delete_file():
os.remove(file_path)
return "Deleted file"
# @router.get("/file", tags=["file"])
# async def get_file():
# with open(file_path, "r", encoding="UTF-8") as fp:
# return HTMLResponse("\n".join(fp.readlines()))
# @router.get("/history")
# async def get_history(count: int = None) -> list[dict]:
# points = []
# with open(file_path, "r", encoding="UTF-8") as fp:
# reader = csv.DictReader(fp, delimiter=";")
# for row in reader:
# if not row:
# continue
# points.append(
# {
# "timestamp": row["timestamp"],
# "total": float(row["total"]),
# "tl": float(row["tl"]),
# "tr": float(row["tr"]),
# "bl": float(row["bl"]),
# "br": float(row["br"]),
# }
# )
# if count:
# return points[-count]
# else:
# return points
# @router.delete("/delete", tags=["file"])
# async def delete_file():
# os.remove(file_path)
# return "Deleted file"

View file

@ -1,24 +1,42 @@
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI
import uvicorn
from endpoints.handlers.bett import log_bed_weights
from endpoints.hue import router as hue_router
from endpoints.bettwaage import router as bettwaage_router
from endpoints.bettwaage import bedscale_service, router as bettwaage_router
from endpoints.handlers.fritz import track_network_devices
app = FastAPI()
# Background task references
background_tasks = []
### Background services
loop = asyncio.new_event_loop()
loop.create_task(track_network_devices(), name="Fritz!Box Connection Tracker")
loop.create_task(log_bed_weights(), name="Polling bed-scale")
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Start background services."""
fritz_task = asyncio.create_task(track_network_devices(), name="Fritz!Box Tracker")
bedscale_task = asyncio.create_task(bedscale_service(), name="Polling bed-scale")
# Store references to the tasks
background_tasks.extend([fritz_task, bedscale_task])
yield
"""Stop background services."""
for task in background_tasks:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass # Expected when cancelling tasks
app = FastAPI(lifespan=lifespan)
# API Routes
app.include_router(hue_router, prefix="/hue", tags=["hue"])
app.include_router(bettwaage_router, prefix="/bettwaage", tags=["bett"])
if __name__ == "__main__":
# Run API server
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
# TODO: Close background services properly