greendeck/greendeck/config.py
2022-12-13 14:25:50 -06:00

137 lines
3.4 KiB
Python

from typing import Annotated, Literal, Union
from pydantic import BaseModel, Field, ValidationError, root_validator, 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(YamlModel):
homeassistant: HomeAssistantConfig
streamdecks: list[StreamDeckConfig]
screens: dict[str, list[TileConfig]]