mirror of https://github.com/home-assistant/core
148 lines
4.7 KiB
Python
148 lines
4.7 KiB
Python
"""Handle KNX project data."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import logging
|
|
from typing import Final
|
|
|
|
from xknx import XKNX
|
|
from xknx.dpt import DPTBase
|
|
from xknx.telegram.address import DeviceAddressableType, GroupAddress, GroupAddressType
|
|
from xknxproject import XKNXProj
|
|
from xknxproject.models import (
|
|
Device,
|
|
DPTType,
|
|
GroupAddress as GroupAddressModel,
|
|
GroupAddressStyle as XknxProjectGroupAddressStyle,
|
|
KNXProject as KNXProjectModel,
|
|
ProjectInfo,
|
|
)
|
|
|
|
from homeassistant.components.file_upload import process_uploaded_file
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.storage import Store
|
|
|
|
from .const import DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
STORAGE_VERSION: Final = 1
|
|
STORAGE_KEY: Final = f"{DOMAIN}/knx_project.json"
|
|
|
|
|
|
@dataclass
|
|
class GroupAddressInfo:
|
|
"""Group address info for runtime usage."""
|
|
|
|
address: str
|
|
name: str
|
|
description: str
|
|
dpt_main: int | None
|
|
dpt_sub: int | None
|
|
transcoder: type[DPTBase] | None
|
|
|
|
|
|
def _create_group_address_info(ga_model: GroupAddressModel) -> GroupAddressInfo:
|
|
"""Convert GroupAddress dict value into GroupAddressInfo instance."""
|
|
dpt = ga_model["dpt"]
|
|
transcoder = DPTBase.transcoder_by_dpt(dpt["main"], dpt.get("sub")) if dpt else None
|
|
return GroupAddressInfo(
|
|
address=ga_model["address"],
|
|
name=ga_model["name"],
|
|
description=ga_model["description"],
|
|
transcoder=transcoder,
|
|
dpt_main=dpt["main"] if dpt else None,
|
|
dpt_sub=dpt["sub"] if dpt else None,
|
|
)
|
|
|
|
|
|
class KNXProject:
|
|
"""Manage KNX project data."""
|
|
|
|
loaded: bool
|
|
devices: dict[str, Device]
|
|
group_addresses: dict[str, GroupAddressInfo]
|
|
info: ProjectInfo | None
|
|
|
|
def __init__(
|
|
self,
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
) -> None:
|
|
"""Initialize project data."""
|
|
self.hass = hass
|
|
self._store = Store[KNXProjectModel](hass, STORAGE_VERSION, STORAGE_KEY)
|
|
|
|
self.initial_state()
|
|
|
|
def initial_state(self) -> None:
|
|
"""Set initial state for project data."""
|
|
self.loaded = False
|
|
self.devices = {}
|
|
self.group_addresses = {}
|
|
self.info = None
|
|
|
|
async def load_project(
|
|
self, xknx: XKNX, data: KNXProjectModel | None = None
|
|
) -> None:
|
|
"""Load project data from storage."""
|
|
if project := data or await self._store.async_load():
|
|
self.devices = project["devices"]
|
|
self.info = project["info"]
|
|
GroupAddress.address_format = self.get_address_format()
|
|
xknx.group_address_dpt.clear()
|
|
xknx_ga_dict: dict[DeviceAddressableType, DPTType] = {}
|
|
|
|
for ga_model in project["group_addresses"].values():
|
|
ga_info = _create_group_address_info(ga_model)
|
|
self.group_addresses[ga_info.address] = ga_info
|
|
if (dpt_model := ga_model.get("dpt")) is not None:
|
|
xknx_ga_dict[ga_model["address"]] = dpt_model
|
|
|
|
xknx.group_address_dpt.set(xknx_ga_dict)
|
|
|
|
_LOGGER.debug(
|
|
"Loaded KNX project data with %s group addresses from storage",
|
|
len(self.group_addresses),
|
|
)
|
|
self.loaded = True
|
|
|
|
async def process_project_file(
|
|
self, xknx: XKNX, file_id: str, password: str
|
|
) -> None:
|
|
"""Process an uploaded project file."""
|
|
|
|
def _parse_project() -> KNXProjectModel:
|
|
with process_uploaded_file(self.hass, file_id) as file_path:
|
|
xknxproj = XKNXProj(
|
|
file_path,
|
|
password=password,
|
|
language=self.hass.config.language,
|
|
)
|
|
return xknxproj.parse()
|
|
|
|
project = await self.hass.async_add_executor_job(_parse_project)
|
|
await self._store.async_save(project)
|
|
await self.load_project(xknx, data=project)
|
|
|
|
async def remove_project_file(self) -> None:
|
|
"""Remove project file from storage."""
|
|
await self._store.async_remove()
|
|
self.initial_state()
|
|
|
|
async def get_knxproject(self) -> KNXProjectModel | None:
|
|
"""Load the project file from local storage."""
|
|
return await self._store.async_load()
|
|
|
|
def get_address_format(self) -> GroupAddressType:
|
|
"""Return the address format for group addresses used in the project."""
|
|
if self.info:
|
|
match self.info["group_address_style"]:
|
|
case XknxProjectGroupAddressStyle.TWOLEVEL.value:
|
|
return GroupAddressType.SHORT
|
|
case XknxProjectGroupAddressStyle.FREE.value:
|
|
return GroupAddressType.FREE
|
|
return GroupAddressType.LONG
|