Started creating http api for netbrite

This commit is contained in:
Jurn Wubben 2025-08-26 11:27:45 +02:00
parent fd19c95479
commit aca30088b0
5 changed files with 205 additions and 6 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.direnv
__pycache__/
devices.db

115
app.py Normal file
View file

@ -0,0 +1,115 @@
from contextlib import asynccontextmanager
from nt import device_encoding
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
from sqlmodel import SQLModel, Session, col, create_engine, select
from netbrite import NetBrite, NetbriteConnectionException, Zone
from db import (
APIAddNetBriteDevice,
APIGetNetBriteDevice,
APIGetZone,
APIMessages,
BaseNetBriteDevice,
BaseZone,
)
sqlite_file_name = "devices.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)
@asynccontextmanager
async def lifespan(app: FastAPI):
print("creating tables")
create_db_and_tables()
yield
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
SessionDep = Annotated[Session, Depends(get_session)]
app = FastAPI(lifespan=lifespan)
devices: list[NetBrite] = []
@app.post("/api/device")
def create_device(device: APIAddNetBriteDevice, session: SessionDep):
statement = select(BaseNetBriteDevice).where(
col(BaseNetBriteDevice.address) == device.address
)
result = session.exec(statement)
if result.first() != None:
raise HTTPException(400, "Device is already added")
try:
netbrite_device = NetBrite(device.address, device.port)
devices.append(netbrite_device)
except NetbriteConnectionException as exc:
raise HTTPException(400, "Failed to connect to device")
dbdevice = BaseNetBriteDevice(address=device.address, port=device.port)
session.add(dbdevice)
session.commit()
session.refresh(dbdevice)
return device
@app.get("/api/devices")
def get_devices(session: SessionDep):
db_devices: dict[int, BaseNetBriteDevice] = {}
statement = select(BaseZone, BaseNetBriteDevice).join(BaseNetBriteDevice)
results = session.exec(statement)
for zone, device in results:
if device.id == None:
continue
if not device.id in db_devices:
connected = devices.inde
get_device = APIGetNetBriteDevice(device., zones={})
db_devices[device.id] = device
db_devices[device.id].zones_list
for device in devices:
zones: dict[str, APIGetZone] = {}
for index, zone in device.zones_list.items():
x, y, xend, yend = zone.rect
width = xend - x
height = yend - y
zones[index] = APIGetZone(
x=x,
y=y,
width=width,
height=height,
scroll_speed=zone.scroll_speed,
pause_duration=zone.pause_duration,
volume=zone.volume,
default_font=zone.default_font,
default_color=zone.default_color,
default_message=zone.initial_text,
)
return devices
@app.get("/api/devices/{device_index}")
def get_device(device_index: int):
if device_index > len(devices) - 1:
raise HTTPException(400, "Device doesn't exist")
return devices[device_index]

65
db.py Normal file
View file

@ -0,0 +1,65 @@
from sqlmodel import Field, Session, SQLModel, create_engine, select
from netbrite import Colors, Fonts, Message, Priorities, ScrollSpeeds
class BaseZone(SQLModel, table=True, echo=True):
id: int | None = Field(default=None, primary_key=True)
x: int
y: int
width: int
height: int
scroll_speed: ScrollSpeeds
pause_duration: int
volume: int
default_font: Fonts
default_color: Colors
default_message: int | None = Field(foreign_key="apimessages.id")
netbrite_device_id: int = Field(foreign_key="apinetbritedevice.id")
class APIGetZone(SQLModel, table=True, echo=True):
id: int | None = Field(default=None, primary_key=True)
x: int
y: int
width: int
height: int
scroll_speed: ScrollSpeeds
pause_duration: int
volume: int
default_font: Fonts
default_color: Colors
default_message: Message | None
class APIMessages(SQLModel, table=True, echo=True):
id: int | None = Field(default=None, primary_key=True)
activation_delay: int
display_delay: int
display_repeat: int
priority: Priorities
text: str
ttl: int
class BaseNetBriteDevice(SQLModel, table=True, echo=True):
id: int | None = Field(default=None, primary_key=True)
address: str = Field(unique=True)
port: int = 700
class APIAddNetBriteDevice(SQLModel, table=False):
address: str
port: int = 700
class APIGetNetBriteDevice(SQLModel, table=False):
address: str
port: int = 700
connected: bool
zones: dict[str, APIGetZone]

View file

@ -15,7 +15,7 @@
nativeBuildInputs = [
pkgs.entr
pkgs.fastapi-cli
(pkgs.python3.withPackages (x: [x.crc x.fastapi]))
(pkgs.python3.withPackages (x: [x.crc x.fastapi x.sqlmodel x.sqlalchemy]))
];
};
};

View file

@ -10,6 +10,14 @@ import re
DEFAULT_PORT = 700
class NetbriteConnectionException(Exception):
pass
class NetbriteTransferException(Exception):
pass
class Colors(Enum):
RED = 0x01
GREEN = 0x02
@ -101,7 +109,7 @@ class Message:
(rb"\{right\}", b"\x10\x28"),
(rb"\{pause\}", b"\x10\x05"),
(rb"\{erase\}", b"\x10\x03"),
(rb"\{serial\}", b"\x10\x09"),
(rb"\{serialnum\}", b"\x10\x09"),
(rb"\{bell\}", b"\x10\x05"),
(rb"\{red\}", b"\x10\x0c" + pack("B", Colors.RED.value)),
(rb"\{green\}", b"\x10\x0c" + pack("B", Colors.GREEN.value)),
@ -183,16 +191,26 @@ class NetBrite:
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(5000)
self.sock.settimeout(2)
self.connect()
except OSError as e:
raise ConnectionError(f"Error while opening network socket. {e}")
raise NetbriteConnectionException(
f"Error while opening network socket. {e}"
)
def connect(self):
self.sock.connect((self.address, self.port))
try:
self.sock.connect((self.address, self.port))
except OSError as e:
raise NetbriteConnectionException(
f"Error while opening network socket. {e}"
)
def tx(self, pkt: bytes):
_ = self.sock.send(pkt)
try:
_ = self.sock.send(pkt)
except OSError as e:
raise NetbriteTransferException(f"Error while opening network socket. {e}")
def message(self, msg: Message, zoneName: str):
z = self.zones_list.get(zoneName)