API is now stable-ish
This commit is contained in:
parent
2ae520f6b3
commit
0721edd431
2 changed files with 72 additions and 67 deletions
107
app.py
107
app.py
|
@ -1,13 +1,14 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from time import sleep
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from fastapi import Depends, FastAPI, HTTPException
|
from fastapi import Depends, FastAPI, HTTPException
|
||||||
from sqlmodel import SQLModel, Session, create_engine, delete, select
|
from sqlmodel import SQLModel, Session, create_engine, select
|
||||||
|
|
||||||
import netbrite as nb
|
import netbrite as nb
|
||||||
from db import (
|
from db import (
|
||||||
MessageDB,
|
MessageDB,
|
||||||
MessagePublic,
|
MessageUpdate,
|
||||||
NetBriteBase,
|
NetBriteBase,
|
||||||
NetBriteDB,
|
NetBriteDB,
|
||||||
NetBritePublic,
|
NetBritePublic,
|
||||||
|
@ -38,7 +39,6 @@ async def lifespan(_: FastAPI):
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
active_devices: dict[int, nb.NetBrite] = {}
|
active_devices: dict[int, nb.NetBrite] = {}
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,17 +65,7 @@ def load_zones_id(session: Session, device_id: int, net_dev: nb.NetBrite):
|
||||||
load_zones(zones, net_dev)
|
load_zones(zones, net_dev)
|
||||||
|
|
||||||
|
|
||||||
def load_empty(net_dev: nb.NetBrite):
|
|
||||||
message = nb.Message("clearing...")
|
|
||||||
zone = nb.Zone(0, 0, 150, 7)
|
|
||||||
|
|
||||||
net_dev.zones({"empty": zone})
|
|
||||||
net_dev.message(message, "empty")
|
|
||||||
|
|
||||||
|
|
||||||
def load_zones(zones_in: list[ZoneDB], net_dev: nb.NetBrite) -> None:
|
def load_zones(zones_in: list[ZoneDB], net_dev: nb.NetBrite) -> None:
|
||||||
load_empty(net_dev)
|
|
||||||
|
|
||||||
zones: dict[str, nb.Zone] = {}
|
zones: dict[str, nb.Zone] = {}
|
||||||
messages: dict[str, nb.Message] = {}
|
messages: dict[str, nb.Message] = {}
|
||||||
|
|
||||||
|
@ -107,12 +97,15 @@ def load_zones(zones_in: list[ZoneDB], net_dev: nb.NetBrite) -> None:
|
||||||
initial_text=default_msg,
|
initial_text=default_msg,
|
||||||
)
|
)
|
||||||
messages[zone.name] = default_msg
|
messages[zone.name] = default_msg
|
||||||
|
# print(f"{zone.name}: {default_msg.text}")
|
||||||
|
|
||||||
if zones:
|
if zones:
|
||||||
net_dev.zones(zones)
|
net_dev.zones(zones)
|
||||||
|
sleep(0.2)
|
||||||
|
|
||||||
for zone, message in messages.items():
|
for zone, message in messages.items():
|
||||||
net_dev.message(message, zone)
|
net_dev.message(message, zone)
|
||||||
|
sleep(0.2)
|
||||||
|
|
||||||
|
|
||||||
def create_default_zone(session: Session, device_id: int) -> None:
|
def create_default_zone(session: Session, device_id: int) -> None:
|
||||||
|
@ -166,12 +159,24 @@ def get_devices(session: SessionDep):
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/devices/{device_id}", response_model=NetBritePublic)
|
@app.post(
|
||||||
|
"/api/devices/{device_id}",
|
||||||
|
response_model=NetBritePublic,
|
||||||
|
description="**NOTE**: this **WILL** disconnect the device. You'll have to manually reconnect it.",
|
||||||
|
)
|
||||||
def edit_device(device_id: int, updated_device: NetBriteUpdate, session: SessionDep):
|
def edit_device(device_id: int, updated_device: NetBriteUpdate, session: SessionDep):
|
||||||
db_dev = session.get(NetBriteDB, device_id)
|
db_dev = session.get(NetBriteDB, device_id)
|
||||||
if not db_dev:
|
if not db_dev:
|
||||||
raise HTTPException(404, "Device not found")
|
raise HTTPException(404, "Device not found")
|
||||||
|
|
||||||
|
if device_id in active_devices:
|
||||||
|
try:
|
||||||
|
active_devices[device_id].sock.close()
|
||||||
|
except OSError:
|
||||||
|
print("Failed to close socket.")
|
||||||
|
|
||||||
|
del active_devices[device_id]
|
||||||
|
|
||||||
dev_data = updated_device.model_dump(exclude_unset=True)
|
dev_data = updated_device.model_dump(exclude_unset=True)
|
||||||
_ = db_dev.sqlmodel_update(dev_data)
|
_ = db_dev.sqlmodel_update(dev_data)
|
||||||
|
|
||||||
|
@ -179,7 +184,7 @@ def edit_device(device_id: int, updated_device: NetBriteUpdate, session: Session
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_dev)
|
session.refresh(db_dev)
|
||||||
|
|
||||||
return db_dev # TODO: update device
|
return db_dev
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/api/devices/{device_id}")
|
@app.delete("/api/devices/{device_id}")
|
||||||
|
@ -195,7 +200,10 @@ def delete_device(device_id: int, session: SessionDep):
|
||||||
delete.append(zone)
|
delete.append(zone)
|
||||||
|
|
||||||
if device_id in active_devices:
|
if device_id in active_devices:
|
||||||
|
try:
|
||||||
active_devices[device_id].sock.close()
|
active_devices[device_id].sock.close()
|
||||||
|
except OSError:
|
||||||
|
print("Failed to close socket.")
|
||||||
del active_devices[device_id]
|
del active_devices[device_id]
|
||||||
|
|
||||||
for i in delete:
|
for i in delete:
|
||||||
|
@ -212,7 +220,8 @@ def reconnect_device(device_id: int, session: SessionDep):
|
||||||
raise HTTPException(404, "Device not found")
|
raise HTTPException(404, "Device not found")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
active_devices[device_id] = nb.NetBrite(db_dev.address, db_dev.port)
|
new_netbrite = nb.NetBrite(db_dev.address, db_dev.port)
|
||||||
|
active_devices[device_id] = new_netbrite
|
||||||
load_zones_id(session, device_id, active_devices[device_id])
|
load_zones_id(session, device_id, active_devices[device_id])
|
||||||
return 200
|
return 200
|
||||||
except nb.NetbriteConnectionException as exc:
|
except nb.NetbriteConnectionException as exc:
|
||||||
|
@ -230,12 +239,8 @@ def sync_device(
|
||||||
if device_id not in active_devices:
|
if device_id not in active_devices:
|
||||||
raise HTTPException(500, "Device not active, try reconnecting")
|
raise HTTPException(500, "Device not active, try reconnecting")
|
||||||
|
|
||||||
db_dev = session.get(NetBriteDB, device_id)
|
|
||||||
if not db_dev:
|
|
||||||
raise HTTPException(404, "Device not found")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
load_zones(db_dev.zones, active_devices[device_id])
|
load_zones_id(session, device_id, active_devices[device_id])
|
||||||
except nb.NetbriteTransferException:
|
except nb.NetbriteTransferException:
|
||||||
del active_devices[device_id]
|
del active_devices[device_id]
|
||||||
raise HTTPException(500, "Failed to send zones. Device inactive now.")
|
raise HTTPException(500, "Failed to send zones. Device inactive now.")
|
||||||
|
@ -248,7 +253,6 @@ def sync_device(
|
||||||
)
|
)
|
||||||
def restart_device(
|
def restart_device(
|
||||||
device_id: int,
|
device_id: int,
|
||||||
session: SessionDep,
|
|
||||||
):
|
):
|
||||||
if device_id not in active_devices:
|
if device_id not in active_devices:
|
||||||
raise HTTPException(500, "Device not active, try reconnecting")
|
raise HTTPException(500, "Device not active, try reconnecting")
|
||||||
|
@ -257,23 +261,7 @@ def restart_device(
|
||||||
active_devices[device_id].reboot()
|
active_devices[device_id].reboot()
|
||||||
del active_devices[device_id]
|
del active_devices[device_id]
|
||||||
except nb.NetbriteTransferException:
|
except nb.NetbriteTransferException:
|
||||||
raise HTTPException(500, "Failed to send zones. Device inactive now.")
|
raise HTTPException(500, "Failed to send reboot command. Device inactive now.")
|
||||||
|
|
||||||
return 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
|
||||||
"/api/devices/{device_id}/reload",
|
|
||||||
description="**NOTE**: Will remove all zones and reload them with **default** messages",
|
|
||||||
)
|
|
||||||
def reload_device(device_id: int, session: SessionDep):
|
|
||||||
if device_id not in active_devices:
|
|
||||||
raise HTTPException(500, "Device not active, try reconnecting")
|
|
||||||
|
|
||||||
try:
|
|
||||||
load_zones_id(session, device_id, active_devices[device_id])
|
|
||||||
except nb.NetbriteTransferException:
|
|
||||||
raise HTTPException(500, "Failed to send zones. Device inactive now.")
|
|
||||||
|
|
||||||
return 200
|
return 200
|
||||||
|
|
||||||
|
@ -362,33 +350,46 @@ def edit_zone(zone_id: int, zone: ZoneUpdate, session: SessionDep):
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/api/zone/{zone_id}/adhoc_message",
|
"/api/zone/{zone_id}/message",
|
||||||
response_model=ZonePublic,
|
response_model=ZonePublic,
|
||||||
description="**NOTE**: this does not update the device.",
|
description="**NOTE**: this does not update the device.",
|
||||||
)
|
)
|
||||||
def edit_message(zone_id: int, message: MessagePublic, session: SessionDep):
|
def edit_message(zone_id: int, message: MessageUpdate, session: SessionDep):
|
||||||
zone_db = session.get(ZoneDB, zone_id)
|
zone_db = session.get(ZoneDB, zone_id)
|
||||||
|
|
||||||
if not zone_db:
|
if not zone_db:
|
||||||
raise HTTPException(404, "Zone not found")
|
raise HTTPException(404, "Zone not found")
|
||||||
|
|
||||||
device_id = zone_db.netbrite_id
|
if not zone_db.default_message:
|
||||||
if not device_id in active_devices:
|
db_message = MessageDB.model_validate(message)
|
||||||
raise HTTPException(500, "Device inactive")
|
|
||||||
|
|
||||||
|
session.add(db_message)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(db_message)
|
||||||
|
|
||||||
|
zone_db.default_message_id = db_message.id
|
||||||
session.add(zone_db)
|
session.add(zone_db)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(zone_db)
|
session.refresh(zone_db)
|
||||||
|
else:
|
||||||
|
db_message = zone_db.default_message
|
||||||
|
data = message.model_dump(
|
||||||
|
exclude_unset=True,
|
||||||
|
)
|
||||||
|
_ = db_message.sqlmodel_update(data, update={"id": db_message.id})
|
||||||
|
|
||||||
|
session.add(db_message)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(zone_db)
|
||||||
|
|
||||||
return zone_db
|
return zone_db
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/api/zone/{zone_id}/adhoc_message",
|
"/api/zone/{zone_id}/adhoc_message",
|
||||||
response_model=ZonePublic,
|
|
||||||
description="**NOTE**: this updates the device temporarily. Edit the zone message for permanent changes.",
|
description="**NOTE**: this updates the device temporarily. Edit the zone message for permanent changes.",
|
||||||
)
|
)
|
||||||
def adhoc_message(zone_id: int, message: MessagePublic, session: SessionDep):
|
def adhoc_message(zone_id: int, message: MessageUpdate, session: SessionDep):
|
||||||
zone_db = session.get(ZoneDB, zone_id)
|
zone_db = session.get(ZoneDB, zone_id)
|
||||||
|
|
||||||
if not zone_db:
|
if not zone_db:
|
||||||
|
@ -399,13 +400,13 @@ def adhoc_message(zone_id: int, message: MessagePublic, session: SessionDep):
|
||||||
raise HTTPException(500, "Device inactive")
|
raise HTTPException(500, "Device inactive")
|
||||||
|
|
||||||
nb_message = nb.Message(
|
nb_message = nb.Message(
|
||||||
text=message.text,
|
text=message.text or "",
|
||||||
activation_delay=message.activation_delay,
|
activation_delay=message.activation_delay or 0,
|
||||||
display_delay=message.display_delay,
|
display_delay=message.display_delay or 0,
|
||||||
display_repeat=message.display_repeat,
|
display_repeat=message.display_repeat or 0,
|
||||||
priority=message.priority,
|
priority=message.priority or nb.Priorities.OVERRIDE,
|
||||||
sound_alarm=message.sound_alarm,
|
sound_alarm=message.sound_alarm or False,
|
||||||
ttl=message.ttl,
|
ttl=message.ttl or 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
24
db.py
24
db.py
|
@ -2,25 +2,29 @@
|
||||||
from sqlmodel import Field, Relationship, SQLModel
|
from sqlmodel import Field, Relationship, SQLModel
|
||||||
from netbrite import Colors, Fonts, Priorities, ScrollSpeeds, Message
|
from netbrite import Colors, Fonts, Priorities, ScrollSpeeds, Message
|
||||||
|
|
||||||
|
MAX_WIDTH = 120
|
||||||
|
MAX_HEIGHT = 7
|
||||||
|
|
||||||
|
|
||||||
# --- Message ---
|
# --- Message ---
|
||||||
class MessageBase(SQLModel):
|
class MessageBase(SQLModel):
|
||||||
|
text: str = ""
|
||||||
activation_delay: int = 0
|
activation_delay: int = 0
|
||||||
display_delay: int = 0
|
display_delay: int = 0
|
||||||
display_repeat: int = 0
|
display_repeat: int = 0
|
||||||
priority: Priorities = Priorities.OVERRIDE
|
priority: Priorities = Priorities.OVERRIDE
|
||||||
sound_alarm: bool = False
|
sound_alarm: bool = False
|
||||||
text: str = ""
|
|
||||||
ttl: int = 0
|
ttl: int = 0
|
||||||
|
|
||||||
|
|
||||||
# class MessageUpdate(SQLModel):
|
class MessageUpdate(SQLModel):
|
||||||
# activation_delay: int | None = 0
|
text: str | None = ""
|
||||||
# display_delay: int | None = 0
|
activation_delay: int | None = 0
|
||||||
# display_repeat: int | None = 0
|
display_delay: int | None = 0
|
||||||
# priority: Priorities | None = Priorities.OVERRIDE
|
display_repeat: int | None = 0
|
||||||
# text: str | None = ""
|
priority: Priorities | None = Priorities.OVERRIDE
|
||||||
# ttl: int | None = 0
|
sound_alarm: bool = False
|
||||||
|
ttl: int | None = 0
|
||||||
|
|
||||||
|
|
||||||
class MessageDB(MessageBase, table=True):
|
class MessageDB(MessageBase, table=True):
|
||||||
|
@ -63,8 +67,8 @@ class ZoneBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
x: int = 0
|
x: int = 0
|
||||||
y: int = 0
|
y: int = 0
|
||||||
width: int = 120
|
width: int = MAX_WIDTH
|
||||||
height: int = 7
|
height: int = MAX_HEIGHT
|
||||||
scroll_speed: ScrollSpeeds = ScrollSpeeds.NORMAL
|
scroll_speed: ScrollSpeeds = ScrollSpeeds.NORMAL
|
||||||
pause_duration: int = 1000
|
pause_duration: int = 1000
|
||||||
volume: int = 4
|
volume: int = 4
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue