Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 65 additions & 36 deletions custom_components/brink_ventilation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,33 @@

from __future__ import annotations

# __init__.py
import asyncio
from datetime import timedelta
import logging
from datetime import timedelta
from typing import Any

import aiohttp

import voluptuous as vol
from aiohttp import ClientError as ClientError
from aiohttp import ClientResponseError as ClientResponseError
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
from homeassistant.config import ConfigType
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME, Platform
from homeassistant.const import (
CONF_PASSWORD,
CONF_SCAN_INTERVAL,
CONF_USERNAME,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import service
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .api import async_get_devices
from .const import (
DATA_CLIENT,
DATA_COORDINATOR,
Expand All @@ -27,15 +39,54 @@
PARAM_DEVICE_TYPE,
PARAM_SOFTWARE_LABEL,
)
from .coordinator import BrinkCoordinator
from .core.brink_home_cloud import BrinkAuthError, BrinkHomeCloud

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [Platform.SELECT, Platform.BINARY_SENSOR, Platform.SENSOR, Platform.FAN]
PLATFORMS = [Platform.SELECT, Platform.BINARY_SENSOR, Platform.SENSOR, Platform.FAN, Platform.NUMBER]

CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)


# pylint: disable=unused-argument, unnecessary-async
async def async_setup(
hass: HomeAssistant,
_config: ConfigType,
) -> bool:
"""Set up the Brink Ventilation integration."""

service.async_register_platform_entity_service(
hass,
DOMAIN,
"set_level",
entity_domain=FAN_DOMAIN,
schema={
vol.Required("level"): vol.All(
vol.Coerce(int),
vol.In([0, 1, 2, 3]),
),
},
func="async_set_level",
)

service.async_register_platform_entity_service(
hass,
DOMAIN,
"set_airflow",
entity_domain=FAN_DOMAIN,
schema={
vol.Required("airflow"): vol.All(
vol.Coerce(int),
vol.Range(min=0, max=300),
),
},
func="async_set_airflow",
)

return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Brink Home from a config entry."""
username = entry.data[CONF_USERNAME]
Expand All @@ -50,33 +101,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except BrinkAuthError as ex:
await brink_client.close()
raise ConfigEntryAuthFailed from ex
except aiohttp.ClientResponseError as ex:
except ClientResponseError as ex:
await brink_client.close()
if ex.status == 401:
raise ConfigEntryAuthFailed from ex
raise ConfigEntryNotReady from ex
except (aiohttp.ClientError, asyncio.TimeoutError) as ex:
except (ClientError, asyncio.TimeoutError) as ex:
await brink_client.close()
raise ConfigEntryNotReady from ex

async def async_update_data() -> dict[int, dict[str, Any]]:
async def async_update_data() -> dict[int, dict[str, object]]:
try:
return await async_get_devices(brink_client)
except BrinkAuthError as ex:
raise ConfigEntryAuthFailed from ex
except aiohttp.ClientResponseError as ex:
except ClientResponseError as ex:
if ex.status == 401:
raise ConfigEntryAuthFailed from ex
raise UpdateFailed(ex) from ex
except (aiohttp.ClientError, asyncio.TimeoutError) as ex:
except (ClientError, asyncio.TimeoutError) as ex:
raise UpdateFailed(ex) from ex

coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name=DOMAIN,
update_method=async_update_data,
update_interval=timedelta(seconds=scan_interval),
coordinator = BrinkCoordinator(
hass=hass,
config_entry=entry,
brink_client=brink_client,
scan_interval=scan_interval,
)

hass.data.setdefault(DOMAIN, {})
Expand All @@ -95,27 +145,6 @@ async def async_update_data() -> dict[int, dict[str, Any]]:
return True


async def async_get_devices(brink_client: BrinkHomeCloud) -> dict[int, dict[str, Any]]:
"""Fetch and normalize Brink systems plus the parameters this integration uses."""
systems = await brink_client.get_systems()

devices: dict[int, dict[str, Any]] = {}
for system in systems:
system_id = system["system_id"]
parameters = await brink_client.get_device_data(system_id)
devices[system_id] = {
"system_id": system_id,
"name": system.get("name") or DEFAULT_NAME,
"serial_number": system.get("serial_number"),
"gateway_state": system.get("gateway_state"),
"model": parameters.get(PARAM_DEVICE_TYPE, {}).get("value") or DEFAULT_MODEL,
"sw_version": parameters.get(PARAM_SOFTWARE_LABEL, {}).get("value"),
"parameters": parameters,
}

return devices


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Expand Down
101 changes: 101 additions & 0 deletions custom_components/brink_ventilation/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from __future__ import annotations

# api.py
import asyncio
import logging

from aiohttp import ClientError as ClientError
from aiohttp import ClientResponseError as ClientResponseError

from .const import (
DEFAULT_MODEL,
DEFAULT_NAME,
PARAM_DEVICE_TYPE,
PARAM_SOFTWARE_LABEL,
)
from .core.brink_home_cloud import BrinkHomeCloud
from .models import BrinkDeviceData

_LOGGER = logging.getLogger(__name__)
PARAM_IP_ADDRESS = "ip_address"
PARAM_DNS_SERVER = "dns_server"
PARAM_API_ONLINE = "api_is_online"


async def async_get_devices(brink_client: BrinkHomeCloud) -> dict[int, BrinkDeviceData]:
"""Fetch and normalize Brink systems plus the parameters this integration uses."""
systems = await brink_client.get_systems()

_LOGGER.debug("Brink Flair nr of systems: %s", len(systems))

# device = await brink_client.get_device(3074)

# _LOGGER.debug("Brink Flair device: %s", device)

# gateway_type_id = device.get("gatewayTypeId")
# is_online = device.get("isOnline")

# _LOGGER.debug("Brink Flair gateway_type_id: %s", gateway_type_id) # always 3 ?
# _LOGGER.debug("Brink Flair device is_online: %s", is_online)

devices: dict[int, BrinkDeviceData] = {}

for system in systems:
system_id = int(system["system_id"])
is_system_owner = system["is_system_owner"]
is_editable = system["is_editable"]
access_level = system["access_level"]
owner_group_name = system["owner_group_name"]
is_favorite = system["is_favorite"]
gateway_state = system["gateway_state"]
active_alert_count = system["active_alert_count"]
iana_time_zone = system["iana_time_zone"]
two_letter_country_code = system["two_letter_country_code"]
user_group_names = system["user_group_names"]
total_count = system["total_count"]
_LOGGER.debug("Brink Flair system [%s] properties: %s", system_id, system)
device, alerts, parameters = await asyncio.gather(
brink_client.async_get_device(system_id),
brink_client.async_get_active_alerts(system_id),
brink_client.get_device_data(system_id),
)
_LOGGER.debug("Parsed alerts: %s", alerts)
_LOGGER.debug("Brink Flair device: %s", device)
gateway_type_id = device.get("gatewayTypeId")
is_online = device.get("isOnline")
_LOGGER.debug("Brink Flair device parameters: %s", parameters)
devices[system_id] = BrinkDeviceData(
api_is_online=True,
system_id=system_id,
name=system.get("name") or DEFAULT_NAME,
internal_name=system.get("name")
or system.get("name")
or DEFAULT_NAME,
serial_number=system.get("serial_number"),
gateway_state=system.get("gateway_state"),
gateway_type_id=gateway_type_id,
device_is_online=bool(is_online),
ip_address=(
str(parameters[PARAM_IP_ADDRESS]["value"])
if PARAM_IP_ADDRESS in parameters
else None
),
dns_server=(
str(parameters[PARAM_DNS_SERVER]["value"])
if PARAM_DNS_SERVER in parameters
else None
),
model=str(
parameters.get(PARAM_DEVICE_TYPE, {}).get("value")
or DEFAULT_MODEL
),
sw_version=(
str(parameters.get(PARAM_SOFTWARE_LABEL, {}).get("value"))
if parameters.get(PARAM_SOFTWARE_LABEL, {}).get("value") is not None
else None
),
parameters=parameters,
alerts=alerts,
)

return devices
Loading