Skip to content
Merged
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
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ homeassistant.components.humidifier.*
homeassistant.components.husqvarna_automower.*
homeassistant.components.hydrawise.*
homeassistant.components.hyperion.*
homeassistant.components.hypontech.*
homeassistant.components.ibeacon.*
homeassistant.components.idasen_desk.*
homeassistant.components.image.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 4 additions & 33 deletions homeassistant/components/advantage_air/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
"""Advantage Air climate integration."""

from datetime import timedelta
import logging
from advantage_air import advantage_air

from advantage_air import ApiError, advantage_air

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import ADVANTAGE_AIR_RETRY, DOMAIN
from .models import AdvantageAirData
from .coordinator import AdvantageAirCoordinator, AdvantageAirDataConfigEntry
from .services import async_setup_services

type AdvantageAirDataConfigEntry = ConfigEntry[AdvantageAirData]

ADVANTAGE_AIR_SYNC_INTERVAL = 15
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.CLIMATE,
Expand All @@ -32,9 +23,6 @@
Platform.UPDATE,
]

_LOGGER = logging.getLogger(__name__)
REQUEST_REFRESH_DELAY = 0.5

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)


Expand All @@ -57,27 +45,10 @@ async def async_setup_entry(
retry=ADVANTAGE_AIR_RETRY,
)

async def async_get():
try:
return await api.async_get()
except ApiError as err:
raise UpdateFailed(err) from err

coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=entry,
name="Advantage Air",
update_method=async_get,
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
request_refresh_debouncer=Debouncer(
hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False
),
)

coordinator = AdvantageAirCoordinator(hass, entry, api)
await coordinator.async_config_entry_first_refresh()

entry.runtime_data = AdvantageAirData(coordinator, api)
entry.runtime_data = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

Expand Down
32 changes: 20 additions & 12 deletions homeassistant/components/advantage_air/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import AdvantageAirDataConfigEntry
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData

PARALLEL_UPDATES = 0

Expand All @@ -24,19 +24,23 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir Binary Sensor platform."""

instance = config_entry.runtime_data
coordinator = config_entry.runtime_data

entities: list[BinarySensorEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirFilter(instance, ac_key))
entities.append(AdvantageAirFilter(coordinator, ac_key))
for zone_key, zone in ac_device["zones"].items():
# Only add motion sensor when motion is enabled
if zone["motionConfig"] >= 2:
entities.append(AdvantageAirZoneMotion(instance, ac_key, zone_key))
entities.append(
AdvantageAirZoneMotion(coordinator, ac_key, zone_key)
)
# Only add MyZone if it is available
if zone["type"] != 0:
entities.append(AdvantageAirZoneMyZone(instance, ac_key, zone_key))
entities.append(
AdvantageAirZoneMyZone(coordinator, ac_key, zone_key)
)
async_add_entities(entities)


Expand All @@ -47,9 +51,9 @@ class AdvantageAirFilter(AdvantageAirAcEntity, BinarySensorEntity):
_attr_entity_category = EntityCategory.DIAGNOSTIC
_attr_name = "Filter"

def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an Advantage Air Filter sensor."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_unique_id += "-filter"

@property
Expand All @@ -63,9 +67,11 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):

_attr_device_class = BinarySensorDeviceClass.MOTION

def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone Motion sensor."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = f"{self._zone['name']} motion"
self._attr_unique_id += "-motion"

Expand All @@ -81,9 +87,11 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
_attr_entity_registry_enabled_default = False
_attr_entity_category = EntityCategory.DIAGNOSTIC

def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone MyZone sensor."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = f"{self._zone['name']} myZone"
self._attr_unique_id += "-myzone"

Expand Down
20 changes: 11 additions & 9 deletions homeassistant/components/advantage_air/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
ADVANTAGE_AIR_STATE_ON,
ADVANTAGE_AIR_STATE_OPEN,
)
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData

ADVANTAGE_AIR_HVAC_MODES = {
"heat": HVACMode.HEAT,
Expand Down Expand Up @@ -90,16 +90,16 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir climate platform."""

instance = config_entry.runtime_data
coordinator = config_entry.runtime_data

entities: list[ClimateEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirAC(instance, ac_key))
entities.append(AdvantageAirAC(coordinator, ac_key))
for zone_key, zone in ac_device["zones"].items():
# Only add zone climate control when zone is in temperature control
if zone["type"] > 0:
entities.append(AdvantageAirZone(instance, ac_key, zone_key))
entities.append(AdvantageAirZone(coordinator, ac_key, zone_key))
async_add_entities(entities)


Expand All @@ -114,9 +114,9 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
_attr_name = None
_support_preset = ClimateEntityFeature(0)

def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an AdvantageAir AC unit."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)

self._attr_preset_modes = [ADVANTAGE_AIR_MYZONE]

Expand Down Expand Up @@ -282,9 +282,11 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
_attr_max_temp = 32
_attr_min_temp = 16

def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an AdvantageAir Zone control."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = self._zone["name"]

@property
Expand Down
59 changes: 59 additions & 0 deletions homeassistant/components/advantage_air/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Coordinator for the Advantage Air integration."""

from __future__ import annotations

from datetime import timedelta
import logging
from typing import Any

from advantage_air import ApiError, advantage_air

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN

ADVANTAGE_AIR_SYNC_INTERVAL = 15
REQUEST_REFRESH_DELAY = 0.5

_LOGGER = logging.getLogger(__name__)

type AdvantageAirDataConfigEntry = ConfigEntry[AdvantageAirCoordinator]


class AdvantageAirCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Advantage Air coordinator."""

config_entry: AdvantageAirDataConfigEntry

def __init__(
self,
hass: HomeAssistant,
config_entry: AdvantageAirDataConfigEntry,
api: advantage_air,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name="Advantage Air",
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
request_refresh_debouncer=Debouncer(
hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False
),
)
self.api = api

async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from the API."""
try:
return await self.api.async_get()
except ApiError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_failed",
translation_placeholders={"error": str(err)},
) from err
24 changes: 13 additions & 11 deletions homeassistant/components/advantage_air/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

from . import AdvantageAirDataConfigEntry
from .const import ADVANTAGE_AIR_STATE_CLOSE, ADVANTAGE_AIR_STATE_OPEN
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirThingEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData

PARALLEL_UPDATES = 0

Expand All @@ -26,24 +26,24 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir cover platform."""

instance = config_entry.runtime_data
coordinator = config_entry.runtime_data

entities: list[CoverEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
for zone_key, zone in ac_device["zones"].items():
# Only add zone vent controls when zone in vent control mode.
if zone["type"] == 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
if things := instance.coordinator.data.get("myThings"):
entities.append(AdvantageAirZoneVent(coordinator, ac_key, zone_key))
if things := coordinator.data.get("myThings"):
for thing in things["things"].values():
if thing["channelDipState"] in [1, 2]: # 1 = "Blind", 2 = "Blind 2"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.BLIND)
AdvantageAirThingCover(coordinator, thing, CoverDeviceClass.BLIND)
)
elif thing["channelDipState"] in [3, 10]: # 3 & 10 = "Garage door"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.GARAGE)
AdvantageAirThingCover(coordinator, thing, CoverDeviceClass.GARAGE)
)
async_add_entities(entities)

Expand All @@ -58,9 +58,11 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
| CoverEntityFeature.SET_POSITION
)

def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone Vent."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = self._zone["name"]

@property
Expand Down Expand Up @@ -106,12 +108,12 @@ class AdvantageAirThingCover(AdvantageAirThingEntity, CoverEntity):

def __init__(
self,
instance: AdvantageAirData,
coordinator: AdvantageAirCoordinator,
thing: dict[str, Any],
device_class: CoverDeviceClass,
) -> None:
"""Initialize an Advantage Air Things Cover."""
super().__init__(instance, thing)
super().__init__(coordinator, thing)
self._attr_device_class = device_class

@property
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/advantage_air/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: AdvantageAirDataConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
data = config_entry.runtime_data.coordinator.data
data = config_entry.runtime_data.data

# Return only the relevant children
return {
Expand Down
Loading
Loading