144 lines
3.6 KiB
Python
144 lines
3.6 KiB
Python
from typing import Annotated
|
|
from typing import Literal
|
|
from typing import Union
|
|
|
|
from pydantic import BaseModel
|
|
from pydantic import Field
|
|
from pydantic import ValidationError
|
|
from pydantic import root_validator
|
|
from pydantic import validator
|
|
from pydantic.color import Color
|
|
|
|
# from pydantic_yaml import YamlModel
|
|
from typing_extensions import Self
|
|
|
|
|
|
class HomeAssistantConfig(BaseModel):
|
|
token: str
|
|
hostname: str
|
|
port: int | None
|
|
secure: bool = True
|
|
|
|
@validator("port")
|
|
def check_port(cls, v):
|
|
if v <= 0 or v >= 65536:
|
|
raise ValidationError("port must be greater than 0 and less than 65536")
|
|
return v
|
|
|
|
|
|
class StreamDeckConfig(BaseModel):
|
|
index: int | None
|
|
serial_number: str | None
|
|
brightness: int | float
|
|
screensaver: int
|
|
screen: str
|
|
|
|
@root_validator
|
|
def check_index_serial_number(cls: type[Self], values: dict):
|
|
index = values.get("index")
|
|
serial_number = values.get("serial_number")
|
|
if index is None and serial_number is None:
|
|
raise ValidationError("must set either index or serial_number")
|
|
if index is not None and serial_number is not None:
|
|
raise ValidationError("cannot set both index and serial_number")
|
|
return values
|
|
|
|
@validator("brightness")
|
|
def check_brightness(cls: type[Self], v: int | float) -> int | float:
|
|
if isinstance(v, int):
|
|
if v < 0 or v > 100:
|
|
raise ValidationError("brightness")
|
|
return v
|
|
if isinstance(v, float):
|
|
if v < 0.0 or v > 1.0:
|
|
raise ValidationError("brightness")
|
|
return v
|
|
raise ValidationError("brightness must be an int or a float")
|
|
|
|
|
|
class ColorConfig(BaseModel):
|
|
foreground: Color
|
|
background: Color
|
|
|
|
|
|
# class HomeAssistantIconImageStateConfig(BaseModel):
|
|
# state: str | None
|
|
# icon: str
|
|
# up: ColorConfig
|
|
# down: ColorConfig
|
|
|
|
|
|
class ImageConfig(BaseModel):
|
|
icon: str
|
|
up: ColorConfig
|
|
down: ColorConfig
|
|
|
|
|
|
class HomeAssistantIconImageConfig(BaseModel):
|
|
type: Literal["homeassistant-icon"]
|
|
entity_id: str
|
|
states: dict[str | None, ImageConfig]
|
|
|
|
@root_validator
|
|
def check_states(cls: type[Self], values: dict) -> dict:
|
|
states = values.get("states")
|
|
if None not in states:
|
|
raise ValidationError("must define default state by using '~' in yaml")
|
|
return values
|
|
|
|
|
|
class StaticIconImageConfig(ImageConfig):
|
|
type: Literal["static-icon"]
|
|
# icon: str
|
|
# up: ColorConfig
|
|
# down: ColorConfig
|
|
|
|
|
|
class ImageTextConfig(ImageConfig):
|
|
icon: str
|
|
text: str
|
|
|
|
|
|
class StaticTextIconImageConfig(ImageTextConfig):
|
|
type: Literal["static-icon-text"]
|
|
|
|
|
|
class HomeAssistantServiceActionConfig(BaseModel):
|
|
type: Literal["homeassistant-service"]
|
|
domain: str
|
|
service: str
|
|
data: dict
|
|
|
|
|
|
class PulseAudioActionConfig(BaseModel):
|
|
type: Literal["pulseaudio-action"]
|
|
application_name: str
|
|
action: Literal["volume-up", "volume-down", "toggle-mute"]
|
|
|
|
|
|
class ActionConfig(BaseModel):
|
|
__root__: Annotated[
|
|
Union[HomeAssistantServiceActionConfig, PulseAudioActionConfig],
|
|
Field(discriminator="type"),
|
|
]
|
|
|
|
|
|
ImageTypes = Annotated[
|
|
Union[
|
|
StaticIconImageConfig, StaticTextIconImageConfig, HomeAssistantIconImageConfig
|
|
],
|
|
Field(discriminator="type"),
|
|
]
|
|
|
|
|
|
class TileConfig(BaseModel):
|
|
position: tuple[int, int]
|
|
image: ImageTypes
|
|
actions: list[ActionConfig]
|
|
|
|
|
|
class Config(BaseModel):
|
|
homeassistant: HomeAssistantConfig
|
|
streamdecks: list[StreamDeckConfig]
|
|
screens: dict[str, list[TileConfig]]
|