Started creating http api for netbrite
This commit is contained in:
parent
fd19c95479
commit
aca30088b0
5 changed files with 205 additions and 6 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
.direnv
|
.direnv
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
devices.db
|
||||||
|
|
115
app.py
Normal file
115
app.py
Normal 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
65
db.py
Normal 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]
|
|
@ -15,7 +15,7 @@
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.entr
|
pkgs.entr
|
||||||
pkgs.fastapi-cli
|
pkgs.fastapi-cli
|
||||||
(pkgs.python3.withPackages (x: [x.crc x.fastapi]))
|
(pkgs.python3.withPackages (x: [x.crc x.fastapi x.sqlmodel x.sqlalchemy]))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
24
netbrite.py
24
netbrite.py
|
@ -10,6 +10,14 @@ import re
|
||||||
DEFAULT_PORT = 700
|
DEFAULT_PORT = 700
|
||||||
|
|
||||||
|
|
||||||
|
class NetbriteConnectionException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NetbriteTransferException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Colors(Enum):
|
class Colors(Enum):
|
||||||
RED = 0x01
|
RED = 0x01
|
||||||
GREEN = 0x02
|
GREEN = 0x02
|
||||||
|
@ -101,7 +109,7 @@ class Message:
|
||||||
(rb"\{right\}", b"\x10\x28"),
|
(rb"\{right\}", b"\x10\x28"),
|
||||||
(rb"\{pause\}", b"\x10\x05"),
|
(rb"\{pause\}", b"\x10\x05"),
|
||||||
(rb"\{erase\}", b"\x10\x03"),
|
(rb"\{erase\}", b"\x10\x03"),
|
||||||
(rb"\{serial\}", b"\x10\x09"),
|
(rb"\{serialnum\}", b"\x10\x09"),
|
||||||
(rb"\{bell\}", b"\x10\x05"),
|
(rb"\{bell\}", b"\x10\x05"),
|
||||||
(rb"\{red\}", b"\x10\x0c" + pack("B", Colors.RED.value)),
|
(rb"\{red\}", b"\x10\x0c" + pack("B", Colors.RED.value)),
|
||||||
(rb"\{green\}", b"\x10\x0c" + pack("B", Colors.GREEN.value)),
|
(rb"\{green\}", b"\x10\x0c" + pack("B", Colors.GREEN.value)),
|
||||||
|
@ -183,16 +191,26 @@ class NetBrite:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.sock.settimeout(5000)
|
self.sock.settimeout(2)
|
||||||
self.connect()
|
self.connect()
|
||||||
except OSError as e:
|
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):
|
def connect(self):
|
||||||
|
try:
|
||||||
self.sock.connect((self.address, self.port))
|
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):
|
def tx(self, pkt: bytes):
|
||||||
|
try:
|
||||||
_ = self.sock.send(pkt)
|
_ = self.sock.send(pkt)
|
||||||
|
except OSError as e:
|
||||||
|
raise NetbriteTransferException(f"Error while opening network socket. {e}")
|
||||||
|
|
||||||
def message(self, msg: Message, zoneName: str):
|
def message(self, msg: Message, zoneName: str):
|
||||||
z = self.zones_list.get(zoneName)
|
z = self.zones_list.get(zoneName)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue