mirror of
https://github.com/home-assistant/core.git
synced 2025-08-06 14:15:12 +02:00
Merge branch 'dev' into pglab
This commit is contained in:
@@ -939,6 +939,7 @@ omit =
|
||||
homeassistant/components/omnilogic/switch.py
|
||||
homeassistant/components/ondilo_ico/__init__.py
|
||||
homeassistant/components/ondilo_ico/api.py
|
||||
homeassistant/components/ondilo_ico/coordinator.py
|
||||
homeassistant/components/ondilo_ico/sensor.py
|
||||
homeassistant/components/onkyo/media_player.py
|
||||
homeassistant/components/onvif/__init__.py
|
||||
|
@@ -4,30 +4,35 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .hub import PulseHub
|
||||
|
||||
CONF_HUBS = "hubs"
|
||||
|
||||
PLATFORMS = [Platform.COVER, Platform.SENSOR]
|
||||
|
||||
AcmedaConfigEntry = ConfigEntry[PulseHub]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: AcmedaConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Rollease Acmeda Automate hub from a config entry."""
|
||||
hub = PulseHub(hass, config_entry)
|
||||
|
||||
if not await hub.async_setup():
|
||||
return False
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = hub
|
||||
config_entry.runtime_data = hub
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: AcmedaConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
hub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
hub = config_entry.runtime_data
|
||||
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
@@ -36,7 +41,4 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
if not await hub.async_reset():
|
||||
return False
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
@@ -9,24 +9,23 @@ from homeassistant.components.cover import (
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AcmedaConfigEntry
|
||||
from .base import AcmedaBase
|
||||
from .const import ACMEDA_HUB_UPDATE, DOMAIN
|
||||
from .const import ACMEDA_HUB_UPDATE
|
||||
from .helpers import async_add_acmeda_entities
|
||||
from .hub import PulseHub
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AcmedaConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Acmeda Rollers from a config entry."""
|
||||
hub: PulseHub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
hub = config_entry.runtime_data
|
||||
|
||||
current: set[int] = set()
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from aiopulse import Roller
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -11,17 +13,20 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import AcmedaConfigEntry
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_acmeda_entities(
|
||||
hass: HomeAssistant,
|
||||
entity_class: type,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AcmedaConfigEntry,
|
||||
current: set[int],
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Add any new entities."""
|
||||
hub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
hub = config_entry.runtime_data
|
||||
LOGGER.debug("Looking for new %s on: %s", entity_class.__name__, hub.host)
|
||||
|
||||
api = hub.api.rollers
|
||||
|
@@ -3,25 +3,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AcmedaConfigEntry
|
||||
from .base import AcmedaBase
|
||||
from .const import ACMEDA_HUB_UPDATE, DOMAIN
|
||||
from .const import ACMEDA_HUB_UPDATE
|
||||
from .helpers import async_add_acmeda_entities
|
||||
from .hub import PulseHub
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AcmedaConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Acmeda Rollers from a config entry."""
|
||||
hub: PulseHub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
hub = config_entry.runtime_data
|
||||
|
||||
current: set[int] = set()
|
||||
|
||||
|
@@ -10,16 +10,14 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
AfterShipConfigEntry = ConfigEntry[AfterShip]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AfterShipConfigEntry) -> bool:
|
||||
"""Set up AfterShip from a config entry."""
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
session = async_get_clientsession(hass)
|
||||
aftership = AfterShip(api_key=entry.data[CONF_API_KEY], session=session)
|
||||
|
||||
@@ -28,7 +26,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
except AfterShipException as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = aftership
|
||||
entry.runtime_data = aftership
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -37,7 +35,4 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
@@ -8,7 +8,6 @@ from typing import Any, Final
|
||||
from pyaftership import AfterShip, AfterShipException
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
@@ -18,6 +17,7 @@ from homeassistant.helpers.dispatcher import (
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from . import AfterShipConfigEntry
|
||||
from .const import (
|
||||
ADD_TRACKING_SERVICE_SCHEMA,
|
||||
ATTR_TRACKINGS,
|
||||
@@ -41,11 +41,11 @@ PLATFORM_SCHEMA: Final = cv.removed(DOMAIN, raise_if_present=False)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AfterShipConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up AfterShip sensor entities based on a config entry."""
|
||||
aftership: AfterShip = hass.data[DOMAIN][config_entry.entry_id]
|
||||
aftership = config_entry.runtime_data
|
||||
|
||||
async_add_entities([AfterShipSensor(aftership, config_entry.title)], True)
|
||||
|
||||
|
@@ -15,14 +15,16 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN # noqa: F401
|
||||
from .coordinator import AirNowDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
AirNowConfigEntry = ConfigEntry[AirNowDataUpdateCoordinator]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AirNowConfigEntry) -> bool:
|
||||
"""Set up AirNow from a config entry."""
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
latitude = entry.data[CONF_LATITUDE]
|
||||
@@ -44,8 +46,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# Store Entity and Initialize Platforms
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
# Listen for option changes
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
@@ -87,14 +88,9 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: AirNowConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
@@ -14,8 +13,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import AirNowDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from . import AirNowConfigEntry
|
||||
|
||||
ATTR_LATITUDE_CAP = "Latitude"
|
||||
ATTR_LONGITUDE_CAP = "Longitude"
|
||||
@@ -40,10 +38,10 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: AirNowConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: AirNowDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
|
@@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_TIME,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
@@ -26,7 +25,7 @@ from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.dt import get_time_zone
|
||||
|
||||
from . import AirNowDataUpdateCoordinator
|
||||
from . import AirNowConfigEntry, AirNowDataUpdateCoordinator
|
||||
from .const import (
|
||||
ATTR_API_AQI,
|
||||
ATTR_API_AQI_DESCRIPTION,
|
||||
@@ -116,11 +115,11 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AirNowConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up AirNow sensor entities based on a config entry."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
entities = [AirNowSensor(coordinator, description) for description in SENSOR_TYPES]
|
||||
|
||||
|
@@ -6,13 +6,14 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AirQCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
AirQConfigEntry = ConfigEntry[AirQCoordinator]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AirQConfigEntry) -> bool:
|
||||
"""Set up air-Q from a config entry."""
|
||||
|
||||
coordinator = AirQCoordinator(hass, entry)
|
||||
@@ -20,18 +21,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# Query the device for the first time and initialise coordinator.data
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# Record the coordinator in a global store
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: AirQConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
@@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
@@ -28,11 +27,10 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import AirQCoordinator
|
||||
from . import AirQConfigEntry, AirQCoordinator
|
||||
from .const import (
|
||||
ACTIVITY_BECQUEREL_PER_CUBIC_METER,
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -400,12 +398,12 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
entry: AirQConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up sensor entities based on a config entry."""
|
||||
|
||||
coordinator = hass.data[DOMAIN][config.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
entities: list[AirQSensor] = []
|
||||
|
||||
|
@@ -22,11 +22,11 @@ SCAN_INTERVAL = timedelta(minutes=6)
|
||||
|
||||
AirthingsDataCoordinatorType = DataUpdateCoordinator[dict[str, AirthingsDevice]]
|
||||
|
||||
AirthingsConfigEntry = ConfigEntry[AirthingsDataCoordinatorType]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) -> bool:
|
||||
"""Set up Airthings from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
airthings = Airthings(
|
||||
entry.data[CONF_ID],
|
||||
entry.data[CONF_SECRET],
|
||||
@@ -49,17 +49,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
@@ -10,7 +10,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@@ -27,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import AirthingsDataCoordinatorType
|
||||
from . import AirthingsConfigEntry, AirthingsDataCoordinatorType
|
||||
from .const import DOMAIN
|
||||
|
||||
SENSORS: dict[str, SensorEntityDescription] = {
|
||||
@@ -102,12 +101,12 @@ SENSORS: dict[str, SensorEntityDescription] = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: AirthingsConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Airthings sensor."""
|
||||
|
||||
coordinator: AirthingsDataCoordinatorType = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
entities = [
|
||||
AirthingsHeaterEnergySensor(
|
||||
coordinator,
|
||||
|
@@ -22,8 +22,13 @@ PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
AirthingsBLEDataUpdateCoordinator = DataUpdateCoordinator[AirthingsDevice]
|
||||
AirthingsBLEConfigEntry = ConfigEntry[AirthingsBLEDataUpdateCoordinator]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: AirthingsBLEConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Airthings BLE device from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
address = entry.unique_id
|
||||
@@ -51,7 +56,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
return data
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
coordinator: AirthingsBLEDataUpdateCoordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
@@ -61,16 +66,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: AirthingsBLEConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
@@ -24,5 +24,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["airthings-ble==0.7.1"]
|
||||
"requirements": ["airthings-ble==0.8.0"]
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
@@ -36,12 +35,10 @@ from homeassistant.helpers.entity_registry import (
|
||||
async_get as entity_async_get,
|
||||
)
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
)
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
|
||||
from . import AirthingsBLEConfigEntry, AirthingsBLEDataUpdateCoordinator
|
||||
from .const import DOMAIN, VOLUME_BECQUEREL, VOLUME_PICOCURIE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -152,15 +149,13 @@ def async_migrate(hass: HomeAssistant, address: str, sensor_name: str) -> None:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: AirthingsBLEConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Airthings BLE sensors."""
|
||||
is_metric = hass.config.units is METRIC_SYSTEM
|
||||
|
||||
coordinator: DataUpdateCoordinator[AirthingsDevice] = hass.data[DOMAIN][
|
||||
entry.entry_id
|
||||
]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
# we need to change some units
|
||||
sensors_mapping = SENSORS_MAPPING_TEMPLATE.copy()
|
||||
@@ -193,7 +188,7 @@ async def async_setup_entry(
|
||||
|
||||
|
||||
class AirthingsSensor(
|
||||
CoordinatorEntity[DataUpdateCoordinator[AirthingsDevice]], SensorEntity
|
||||
CoordinatorEntity[AirthingsBLEDataUpdateCoordinator], SensorEntity
|
||||
):
|
||||
"""Airthings BLE sensors for the device."""
|
||||
|
||||
@@ -201,7 +196,7 @@ class AirthingsSensor(
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator[AirthingsDevice],
|
||||
coordinator: AirthingsBLEDataUpdateCoordinator,
|
||||
airthings_device: AirthingsDevice,
|
||||
entity_description: SensorEntityDescription,
|
||||
) -> None:
|
||||
|
@@ -13,8 +13,10 @@ from .const import DOMAIN
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CLIMATE]
|
||||
|
||||
Airtouch5ConfigEntry = ConfigEntry[Airtouch5SimpleClient]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: Airtouch5ConfigEntry) -> bool:
|
||||
"""Set up Airtouch 5 from a config entry."""
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
@@ -30,22 +32,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
raise ConfigEntryNotReady from t
|
||||
|
||||
# Store an API object for your platforms to access
|
||||
hass.data[DOMAIN][entry.entry_id] = client
|
||||
entry.runtime_data = client
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: Airtouch5ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
client: Airtouch5SimpleClient = hass.data[DOMAIN][entry.entry_id]
|
||||
client = entry.runtime_data
|
||||
await client.disconnect()
|
||||
client.ac_status_callbacks.clear()
|
||||
client.connection_state_callbacks.clear()
|
||||
client.data_packet_callbacks.clear()
|
||||
client.zone_status_callbacks.clear()
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
@@ -34,12 +34,12 @@ from homeassistant.components.climate import (
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import Airtouch5ConfigEntry
|
||||
from .const import DOMAIN, FAN_INTELLIGENT_AUTO, FAN_TURBO
|
||||
from .entity import Airtouch5Entity
|
||||
|
||||
@@ -92,11 +92,11 @@ FAN_MODE_TO_SET_AC_FAN_SPEED = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: Airtouch5ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Airtouch 5 Climate entities."""
|
||||
client: Airtouch5SimpleClient = hass.data[DOMAIN][config_entry.entry_id]
|
||||
client = config_entry.runtime_data
|
||||
|
||||
entities: list[ClimateEntity] = []
|
||||
|
||||
|
@@ -38,6 +38,8 @@ PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
UPDATE_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
AirVisualProConfigEntry = ConfigEntry["AirVisualProData"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AirVisualProData:
|
||||
@@ -47,7 +49,9 @@ class AirVisualProData:
|
||||
node: NodeSamba
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: AirVisualProConfigEntry
|
||||
) -> bool:
|
||||
"""Set up AirVisual Pro from a config entry."""
|
||||
node = NodeSamba(entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD])
|
||||
|
||||
@@ -89,9 +93,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = AirVisualProData(
|
||||
coordinator=coordinator, node=node
|
||||
)
|
||||
entry.runtime_data = AirVisualProData(coordinator=coordinator, node=node)
|
||||
|
||||
async def async_shutdown(_: Event) -> None:
|
||||
"""Define an event handler to disconnect from the websocket."""
|
||||
@@ -110,11 +112,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: AirVisualProConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
data = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
await data.node.async_disconnect()
|
||||
await entry.runtime_data.node.async_disconnect()
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
@@ -5,12 +5,10 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import AirVisualProData
|
||||
from .const import DOMAIN
|
||||
from . import AirVisualProConfigEntry
|
||||
|
||||
CONF_MAC_ADDRESS = "mac_address"
|
||||
CONF_SERIAL_NUMBER = "serial_number"
|
||||
@@ -23,15 +21,13 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: AirVisualProConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
data: AirVisualProData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
"entry": entry.as_dict(),
|
||||
"data": data.coordinator.data,
|
||||
"data": entry.runtime_data.coordinator.data,
|
||||
},
|
||||
TO_REDACT,
|
||||
)
|
||||
|
@@ -12,7 +12,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
@@ -23,8 +22,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AirVisualProData, AirVisualProEntity
|
||||
from .const import DOMAIN
|
||||
from . import AirVisualProConfigEntry, AirVisualProEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -129,13 +127,13 @@ def async_get_aqi_locale(settings: dict[str, Any]) -> str:
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant,
|
||||
entry: AirVisualProConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up AirVisual sensors based on a config entry."""
|
||||
data: AirVisualProData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
AirVisualProSensor(data.coordinator, description)
|
||||
AirVisualProSensor(entry.runtime_data.coordinator, description)
|
||||
for description in SENSOR_DESCRIPTIONS
|
||||
)
|
||||
|
||||
|
@@ -39,6 +39,8 @@ DEFAULT_SOCKET_MIN_RETRY = 15
|
||||
|
||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||
|
||||
AmbientStationConfigEntry = ConfigEntry["AmbientStation"]
|
||||
|
||||
|
||||
@callback
|
||||
def async_wm2_to_lx(value: float) -> int:
|
||||
@@ -55,7 +57,9 @@ def async_hydrate_station_data(data: dict[str, Any]) -> dict[str, Any]:
|
||||
return data
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: AmbientStationConfigEntry
|
||||
) -> bool:
|
||||
"""Set up the Ambient PWS as config entry."""
|
||||
if not entry.unique_id:
|
||||
hass.config_entries.async_update_entry(
|
||||
@@ -74,7 +78,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
LOGGER.error("Config entry failed: %s", err)
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = ambient
|
||||
entry.runtime_data = ambient
|
||||
|
||||
async def _async_disconnect_websocket(_: Event) -> None:
|
||||
await ambient.websocket.disconnect()
|
||||
@@ -88,12 +92,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: AmbientStationConfigEntry
|
||||
) -> bool:
|
||||
"""Unload an Ambient PWS config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
ambient = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
hass.async_create_task(ambient.ws_disconnect(), eager_start=True)
|
||||
hass.async_create_task(entry.runtime_data.ws_disconnect(), eager_start=True)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
@@ -10,12 +10,12 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_NAME, EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import ATTR_LAST_DATA, DOMAIN
|
||||
from . import AmbientStationConfigEntry
|
||||
from .const import ATTR_LAST_DATA
|
||||
from .entity import AmbientWeatherEntity
|
||||
|
||||
TYPE_BATT1 = "batt1"
|
||||
@@ -379,10 +379,12 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant,
|
||||
entry: AmbientStationConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Ambient PWS binary sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][entry.entry_id]
|
||||
ambient = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
AmbientWeatherBinarySensor(
|
||||
|
@@ -5,12 +5,11 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LOCATION, CONF_UNIQUE_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import AmbientStation
|
||||
from .const import CONF_APP_KEY, DOMAIN
|
||||
from . import AmbientStationConfigEntry
|
||||
from .const import CONF_APP_KEY
|
||||
|
||||
CONF_API_KEY_CAMEL = "apiKey"
|
||||
CONF_APP_KEY_CAMEL = "appKey"
|
||||
@@ -37,12 +36,10 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: AmbientStationConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
ambient: AmbientStation = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return {
|
||||
"entry": async_redact_data(entry.as_dict(), TO_REDACT),
|
||||
"stations": async_redact_data(ambient.stations, TO_REDACT),
|
||||
"stations": async_redact_data(entry.runtime_data.stations, TO_REDACT),
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_NAME,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
@@ -30,8 +29,8 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AmbientStation
|
||||
from .const import ATTR_LAST_DATA, DOMAIN, TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX
|
||||
from . import AmbientStation, AmbientStationConfigEntry
|
||||
from .const import ATTR_LAST_DATA, TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX
|
||||
from .entity import AmbientWeatherEntity
|
||||
|
||||
TYPE_24HOURRAININ = "24hourrainin"
|
||||
@@ -661,10 +660,12 @@ SENSOR_DESCRIPTIONS = (
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant,
|
||||
entry: AmbientStationConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Ambient PWS sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][entry.entry_id]
|
||||
ambient = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
AmbientWeatherSensor(ambient, mac_address, station[ATTR_NAME], description)
|
||||
|
@@ -49,6 +49,8 @@ API_CACHED_ATTRS = {
|
||||
}
|
||||
YALEXS_BLE_DOMAIN = "yalexs_ble"
|
||||
|
||||
AugustConfigEntry = ConfigEntry["AugustData"]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up August from a config entry."""
|
||||
@@ -66,22 +68,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
|
||||
data: AugustData = hass.data[DOMAIN][entry.entry_id]
|
||||
data.async_stop()
|
||||
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
entry.runtime_data.async_stop()
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_setup_august(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, august_gateway: AugustGateway
|
||||
hass: HomeAssistant, config_entry: AugustConfigEntry, august_gateway: AugustGateway
|
||||
) -> bool:
|
||||
"""Set up the August component."""
|
||||
|
||||
@@ -95,10 +89,7 @@ async def async_setup_august(
|
||||
await august_gateway.async_authenticate()
|
||||
await august_gateway.async_refresh_access_token_if_needed()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
data = hass.data[DOMAIN][config_entry.entry_id] = AugustData(
|
||||
hass, config_entry, august_gateway
|
||||
)
|
||||
data = config_entry.runtime_data = AugustData(hass, config_entry, august_gateway)
|
||||
await data.async_setup()
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
@@ -509,12 +500,12 @@ def _restore_live_attrs(
|
||||
|
||||
|
||||
async def async_remove_config_entry_device(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
|
||||
hass: HomeAssistant, config_entry: AugustConfigEntry, device_entry: dr.DeviceEntry
|
||||
) -> bool:
|
||||
"""Remove august config entry from a device if its no longer present."""
|
||||
data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
return not any(
|
||||
identifier
|
||||
for identifier in device_entry.identifiers
|
||||
if identifier[0] == DOMAIN and data.get_device(identifier[1])
|
||||
if identifier[0] == DOMAIN
|
||||
and config_entry.runtime_data.get_device(identifier[1])
|
||||
)
|
||||
|
@@ -22,14 +22,13 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from . import AugustData
|
||||
from .const import ACTIVITY_UPDATE_INTERVAL, DOMAIN
|
||||
from . import AugustConfigEntry, AugustData
|
||||
from .const import ACTIVITY_UPDATE_INTERVAL
|
||||
from .entity import AugustEntityMixin
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -154,11 +153,11 @@ SENSOR_TYPES_DOORBELL: tuple[AugustDoorbellBinarySensorEntityDescription, ...] =
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AugustConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the August binary sensors."""
|
||||
data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
entities: list[BinarySensorEntity] = []
|
||||
|
||||
for door in data.locks:
|
||||
|
@@ -3,22 +3,20 @@
|
||||
from yalexs.lock import Lock
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AugustData
|
||||
from .const import DOMAIN
|
||||
from . import AugustConfigEntry, AugustData
|
||||
from .entity import AugustEntityMixin
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AugustConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up August lock wake buttons."""
|
||||
data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
async_add_entities(AugustWakeLockButton(data, lock) for lock in data.locks)
|
||||
|
||||
|
||||
|
@@ -11,13 +11,12 @@ from yalexs.doorbell import ContentTokenExpired, Doorbell
|
||||
from yalexs.util import update_doorbell_image_from_activity
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AugustData
|
||||
from .const import DEFAULT_NAME, DEFAULT_TIMEOUT, DOMAIN
|
||||
from . import AugustConfigEntry, AugustData
|
||||
from .const import DEFAULT_NAME, DEFAULT_TIMEOUT
|
||||
from .entity import AugustEntityMixin
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -25,11 +24,11 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AugustConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up August cameras."""
|
||||
data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
# Create an aiohttp session instead of using the default one since the
|
||||
# default one is likely to trigger august's WAF if another integration
|
||||
# is also using Cloudflare
|
||||
|
@@ -7,11 +7,10 @@ from typing import Any
|
||||
from yalexs.const import DEFAULT_BRAND
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import AugustData
|
||||
from .const import CONF_BRAND, DOMAIN
|
||||
from . import AugustConfigEntry
|
||||
from .const import CONF_BRAND
|
||||
|
||||
TO_REDACT = {
|
||||
"HouseID",
|
||||
@@ -30,10 +29,10 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: AugustConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
data: AugustData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
|
||||
return {
|
||||
"locks": {
|
||||
|
@@ -12,15 +12,13 @@ from yalexs.lock import Lock, LockStatus
|
||||
from yalexs.util import get_latest_activity, update_lock_detail_from_activity
|
||||
|
||||
from homeassistant.components.lock import ATTR_CHANGED_BY, LockEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_BATTERY_LEVEL
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from . import AugustData
|
||||
from .const import DOMAIN
|
||||
from . import AugustConfigEntry, AugustData
|
||||
from .entity import AugustEntityMixin
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -30,11 +28,11 @@ LOCK_JAMMED_ERR = 531
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AugustConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up August locks."""
|
||||
data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
async_add_entities(AugustLock(data, lock) for lock in data.locks)
|
||||
|
||||
|
||||
|
@@ -19,7 +19,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_PICTURE,
|
||||
PERCENTAGE,
|
||||
@@ -30,7 +29,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AugustData
|
||||
from . import AugustConfigEntry, AugustData
|
||||
from .const import (
|
||||
ATTR_OPERATION_AUTORELOCK,
|
||||
ATTR_OPERATION_KEYPAD,
|
||||
@@ -95,11 +94,11 @@ SENSOR_TYPE_KEYPAD_BATTERY = AugustSensorEntityDescription[KeypadDetail](
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: AugustConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the August sensors."""
|
||||
data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
entities: list[SensorEntity] = []
|
||||
migrate_unique_id_devices = []
|
||||
operation_sensors = []
|
||||
|
@@ -51,8 +51,9 @@ from homeassistant.helpers.event import async_call_later
|
||||
from homeassistant.helpers.issue_registry import async_delete_issue
|
||||
from homeassistant.loader import async_get_bluetooth
|
||||
|
||||
from . import models, passive_update_processor
|
||||
from . import passive_update_processor
|
||||
from .api import (
|
||||
_get_manager,
|
||||
async_address_present,
|
||||
async_ble_device_from_address,
|
||||
async_discovered_service_info,
|
||||
@@ -76,7 +77,6 @@ from .const import (
|
||||
CONF_ADAPTER,
|
||||
CONF_DETAILS,
|
||||
CONF_PASSIVE,
|
||||
DATA_MANAGER,
|
||||
DOMAIN,
|
||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||
LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS,
|
||||
@@ -230,10 +230,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
hass, integration_matcher, bluetooth_adapters, bluetooth_storage, slot_manager
|
||||
)
|
||||
set_manager(manager)
|
||||
|
||||
await storage_setup_task
|
||||
await manager.async_setup()
|
||||
hass.data[DATA_MANAGER] = models.MANAGER = manager
|
||||
|
||||
hass.async_create_background_task(
|
||||
_async_start_adapter_discovery(hass, manager, bluetooth_adapters),
|
||||
@@ -314,7 +312,7 @@ async def async_update_device(
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up a config entry for a bluetooth scanner."""
|
||||
manager: HomeAssistantBluetoothManager = hass.data[DATA_MANAGER]
|
||||
manager = _get_manager(hass)
|
||||
address = entry.unique_id
|
||||
assert address is not None
|
||||
adapter = await manager.async_get_adapter_from_address_or_recover(address)
|
||||
|
@@ -15,10 +15,12 @@ from habluetooth import (
|
||||
BluetoothScannerDevice,
|
||||
BluetoothScanningMode,
|
||||
HaBleakScannerWrapper,
|
||||
get_manager,
|
||||
)
|
||||
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
|
||||
from homeassistant.helpers.singleton import singleton
|
||||
|
||||
from .const import DATA_MANAGER
|
||||
from .manager import HomeAssistantBluetoothManager
|
||||
@@ -29,9 +31,10 @@ if TYPE_CHECKING:
|
||||
from bleak.backends.device import BLEDevice
|
||||
|
||||
|
||||
@singleton(DATA_MANAGER)
|
||||
def _get_manager(hass: HomeAssistant) -> HomeAssistantBluetoothManager:
|
||||
"""Get the bluetooth manager."""
|
||||
return cast(HomeAssistantBluetoothManager, hass.data[DATA_MANAGER])
|
||||
return cast(HomeAssistantBluetoothManager, get_manager())
|
||||
|
||||
|
||||
@hass_callback
|
||||
@@ -68,8 +71,6 @@ def async_discovered_service_info(
|
||||
hass: HomeAssistant, connectable: bool = True
|
||||
) -> Iterable[BluetoothServiceInfoBleak]:
|
||||
"""Return the discovered devices list."""
|
||||
if DATA_MANAGER not in hass.data:
|
||||
return []
|
||||
return _get_manager(hass).async_discovered_service_info(connectable)
|
||||
|
||||
|
||||
@@ -78,8 +79,6 @@ def async_last_service_info(
|
||||
hass: HomeAssistant, address: str, connectable: bool = True
|
||||
) -> BluetoothServiceInfoBleak | None:
|
||||
"""Return the last service info for an address."""
|
||||
if DATA_MANAGER not in hass.data:
|
||||
return None
|
||||
return _get_manager(hass).async_last_service_info(address, connectable)
|
||||
|
||||
|
||||
@@ -88,8 +87,6 @@ def async_ble_device_from_address(
|
||||
hass: HomeAssistant, address: str, connectable: bool = True
|
||||
) -> BLEDevice | None:
|
||||
"""Return BLEDevice for an address if its present."""
|
||||
if DATA_MANAGER not in hass.data:
|
||||
return None
|
||||
return _get_manager(hass).async_ble_device_from_address(address, connectable)
|
||||
|
||||
|
||||
@@ -106,8 +103,6 @@ def async_address_present(
|
||||
hass: HomeAssistant, address: str, connectable: bool = True
|
||||
) -> bool:
|
||||
"""Check if an address is present in the bluetooth device list."""
|
||||
if DATA_MANAGER not in hass.data:
|
||||
return False
|
||||
return _get_manager(hass).async_address_present(address, connectable)
|
||||
|
||||
|
||||
|
@@ -14,6 +14,7 @@ from bluetooth_adapters import (
|
||||
adapter_model,
|
||||
get_adapters,
|
||||
)
|
||||
from habluetooth import get_manager
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import onboarding
|
||||
@@ -25,7 +26,6 @@ from homeassistant.helpers.schema_config_entry_flow import (
|
||||
)
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
|
||||
from . import models
|
||||
from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN
|
||||
from .util import adapter_title
|
||||
|
||||
@@ -185,4 +185,4 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@callback
|
||||
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
|
||||
"""Return options flow support for this handler."""
|
||||
return bool(models.MANAGER and models.MANAGER.supports_passive_scan)
|
||||
return bool((manager := get_manager()) and manager.supports_passive_scan)
|
||||
|
@@ -20,6 +20,6 @@
|
||||
"bluetooth-auto-recovery==1.4.2",
|
||||
"bluetooth-data-tools==1.19.0",
|
||||
"dbus-fast==2.21.1",
|
||||
"habluetooth==2.8.0"
|
||||
"habluetooth==2.8.1"
|
||||
]
|
||||
}
|
||||
|
@@ -4,17 +4,9 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .manager import HomeAssistantBluetoothManager
|
||||
|
||||
|
||||
MANAGER: HomeAssistantBluetoothManager | None = None
|
||||
|
||||
|
||||
BluetoothChange = Enum("BluetoothChange", "ADVERTISEMENT")
|
||||
BluetoothCallback = Callable[[BluetoothServiceInfoBleak, BluetoothChange], None]
|
||||
ProcessAdvertisementCallback = Callable[[BluetoothServiceInfoBleak], bool]
|
||||
|
@@ -35,8 +35,10 @@ _API_TIMEOUT = SLOW_UPDATE_WARNING - 1
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
BondConfigEntry = ConfigEntry[BondData]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: BondConfigEntry) -> bool:
|
||||
"""Set up Bond from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
token = entry.data[CONF_ACCESS_TOKEN]
|
||||
@@ -70,7 +72,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, _async_stop_event)
|
||||
)
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = BondData(hub, bpup_subs)
|
||||
entry.runtime_data = BondData(hub, bpup_subs)
|
||||
|
||||
if not entry.unique_id:
|
||||
hass.config_entries.async_update_entry(entry, unique_id=hub.bond_id)
|
||||
@@ -97,11 +99,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: BondConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
@callback
|
||||
@@ -118,10 +118,10 @@ def _async_remove_old_device_identifiers(
|
||||
|
||||
|
||||
async def async_remove_config_entry_device(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
|
||||
hass: HomeAssistant, config_entry: BondConfigEntry, device_entry: dr.DeviceEntry
|
||||
) -> bool:
|
||||
"""Remove bond config entry from a device."""
|
||||
data: BondData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
data = config_entry.runtime_data
|
||||
hub = data.hub
|
||||
for identifier in device_entry.identifiers:
|
||||
if identifier[0] != DOMAIN or len(identifier) != 3:
|
||||
|
@@ -7,13 +7,11 @@ from dataclasses import dataclass
|
||||
from bond_async import Action, BPUPSubscriptions
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from . import BondConfigEntry
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
from .utils import BondDevice, BondHub
|
||||
|
||||
# The api requires a step size even though it does not
|
||||
@@ -243,11 +241,11 @@ BUTTONS: tuple[BondButtonEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: BondConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Bond button devices."""
|
||||
data: BondData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
bpup_subs = data.bpup_subs
|
||||
entities: list[BondButtonEntity] = []
|
||||
|
@@ -12,13 +12,11 @@ from homeassistant.components.cover import (
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from . import BondConfigEntry
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
from .utils import BondDevice, BondHub
|
||||
|
||||
|
||||
@@ -34,11 +32,11 @@ def _hass_to_bond_position(hass_position: int) -> int:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: BondConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Bond cover devices."""
|
||||
data: BondData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
bpup_subs = data.bpup_subs
|
||||
|
||||
|
@@ -5,20 +5,18 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .models import BondData
|
||||
from . import BondConfigEntry
|
||||
|
||||
TO_REDACT = {"access_token"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: BondConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
data: BondData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
return {
|
||||
"entry": {
|
||||
|
@@ -16,7 +16,6 @@ from homeassistant.components.fan import (
|
||||
FanEntity,
|
||||
FanEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_platform
|
||||
@@ -27,9 +26,9 @@ from homeassistant.util.percentage import (
|
||||
)
|
||||
from homeassistant.util.scaling import int_states_in_range
|
||||
|
||||
from .const import DOMAIN, SERVICE_SET_FAN_SPEED_TRACKED_STATE
|
||||
from . import BondConfigEntry
|
||||
from .const import SERVICE_SET_FAN_SPEED_TRACKED_STATE
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
from .utils import BondDevice, BondHub
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -39,11 +38,11 @@ PRESET_MODE_BREEZE = "Breeze"
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: BondConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Bond fan devices."""
|
||||
data: BondData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
bpup_subs = data.bpup_subs
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
@@ -10,21 +10,19 @@ from bond_async import Action, BPUPSubscriptions, DeviceType
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import BondConfigEntry
|
||||
from .const import (
|
||||
ATTR_POWER_STATE,
|
||||
DOMAIN,
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE,
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE,
|
||||
)
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
from .utils import BondDevice, BondHub
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -42,11 +40,11 @@ ENTITY_SERVICES = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: BondConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Bond light devices."""
|
||||
data: BondData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
bpup_subs = data.bpup_subs
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
@@ -9,24 +9,23 @@ from bond_async import Action, DeviceType
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import ATTR_POWER_STATE, DOMAIN, SERVICE_SET_POWER_TRACKED_STATE
|
||||
from . import BondConfigEntry
|
||||
from .const import ATTR_POWER_STATE, SERVICE_SET_POWER_TRACKED_STATE
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: BondConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Bond generic devices."""
|
||||
data: BondData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
bpup_subs = data.bpup_subs
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
@@ -9,13 +9,15 @@ from homeassistant.const import CONF_API_KEY, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN # noqa: F401
|
||||
from .coordinator import CO2SignalCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
CO2SignalConfigEntry = ConfigEntry[CO2SignalCoordinator]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: CO2SignalConfigEntry) -> bool:
|
||||
"""Set up CO2 Signal from a config entry."""
|
||||
session = async_get_clientsession(hass)
|
||||
coordinator = CO2SignalCoordinator(
|
||||
@@ -23,11 +25,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: CO2SignalConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
@@ -6,21 +6,19 @@ from dataclasses import asdict
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import CO2SignalCoordinator
|
||||
from . import CO2SignalConfigEntry
|
||||
|
||||
TO_REDACT = {CONF_API_KEY}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
hass: HomeAssistant, config_entry: CO2SignalConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: CO2SignalCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
return {
|
||||
"config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT),
|
||||
|
@@ -12,13 +12,13 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import CO2SignalConfigEntry
|
||||
from .const import ATTRIBUTION, DOMAIN
|
||||
from .coordinator import CO2SignalCoordinator
|
||||
|
||||
@@ -53,10 +53,12 @@ SENSORS = (
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant,
|
||||
entry: CO2SignalConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the CO2signal sensor."""
|
||||
coordinator: CO2SignalCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities(
|
||||
[CO2Sensor(coordinator, description) for description in SENSORS], False
|
||||
)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.notify import DOMAIN, NotifyEntity
|
||||
from homeassistant.components.notify import DOMAIN, NotifyEntity, NotifyEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -33,12 +33,15 @@ class DemoNotifyEntity(NotifyEntity):
|
||||
) -> None:
|
||||
"""Initialize the Demo button entity."""
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_supported_features = NotifyEntityFeature.TITLE
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
name=device_name,
|
||||
)
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a message to a user."""
|
||||
event_notitifcation = {"message": message}
|
||||
self.hass.bus.async_fire(EVENT_NOTIFY, event_notitifcation)
|
||||
event_notification = {"message": message}
|
||||
if title is not None:
|
||||
event_notification["title"] = title
|
||||
self.hass.bus.async_fire(EVENT_NOTIFY, event_notification)
|
||||
|
@@ -49,6 +49,7 @@ PLATFORMS = [
|
||||
Platform.NOTIFY,
|
||||
Platform.NUMBER,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.WEATHER,
|
||||
]
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pyecobee"],
|
||||
"requirements": ["python-ecobee-api==0.2.17"],
|
||||
"requirements": ["python-ecobee-api==0.2.18"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_ecobee._tcp.local."
|
||||
|
@@ -85,6 +85,6 @@ class EcobeeNotifyEntity(EcobeeBaseEntity, NotifyEntity):
|
||||
f"{self.thermostat["identifier"]}_notify_{thermostat_index}"
|
||||
)
|
||||
|
||||
def send_message(self, message: str) -> None:
|
||||
def send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a message."""
|
||||
self.data.ecobee.send_message(self.thermostat_index, message)
|
||||
|
90
homeassistant/components/ecobee/switch.py
Normal file
90
homeassistant/components/ecobee/switch.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""Support for using switch with ecobee thermostats."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import EcobeeData
|
||||
from .const import DOMAIN
|
||||
from .entity import EcobeeBaseEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the ecobee thermostat switch entity."""
|
||||
data: EcobeeData = hass.data[DOMAIN]
|
||||
|
||||
async_add_entities(
|
||||
(
|
||||
EcobeeVentilator20MinSwitch(data, index)
|
||||
for index, thermostat in enumerate(data.ecobee.thermostats)
|
||||
if thermostat["settings"]["ventilatorType"] != "none"
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
class EcobeeVentilator20MinSwitch(EcobeeBaseEntity, SwitchEntity):
|
||||
"""A Switch class, representing 20 min timer for an ecobee thermostat with ventilator attached."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = "Ventilator 20m Timer"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: EcobeeData,
|
||||
thermostat_index: int,
|
||||
) -> None:
|
||||
"""Initialize ecobee ventilator platform."""
|
||||
super().__init__(data, thermostat_index)
|
||||
self._attr_unique_id = f"{self.base_unique_id}_ventilator_20m_timer"
|
||||
self._attr_is_on = False
|
||||
self.update_without_throttle = False
|
||||
self._operating_timezone = dt_util.get_time_zone(
|
||||
self.thermostat["location"]["timeZone"]
|
||||
)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Get the latest state from the thermostat."""
|
||||
|
||||
if self.update_without_throttle:
|
||||
await self.data.update(no_throttle=True)
|
||||
self.update_without_throttle = False
|
||||
else:
|
||||
await self.data.update()
|
||||
|
||||
ventilator_off_date_time = self.thermostat["settings"]["ventilatorOffDateTime"]
|
||||
|
||||
self._attr_is_on = ventilator_off_date_time and dt_util.parse_datetime(
|
||||
ventilator_off_date_time, raise_on_error=True
|
||||
).replace(tzinfo=self._operating_timezone) >= dt_util.now(
|
||||
self._operating_timezone
|
||||
)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Set ventilator 20 min timer on."""
|
||||
await self.hass.async_add_executor_job(
|
||||
self.data.ecobee.set_ventilator_timer, self.thermostat_index, True
|
||||
)
|
||||
self.update_without_throttle = True
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Set ventilator 20 min timer off."""
|
||||
await self.hass.async_add_executor_job(
|
||||
self.data.ecobee.set_ventilator_timer, self.thermostat_index, False
|
||||
)
|
||||
self.update_without_throttle = True
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["env_canada"],
|
||||
"requirements": ["env-canada==0.6.0"]
|
||||
"requirements": ["env-canada==0.6.2"]
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import Final, TypeVar
|
||||
@@ -42,28 +41,24 @@ PLATFORMS: Final = [Platform.SENSOR]
|
||||
|
||||
_FroniusCoordinatorT = TypeVar("_FroniusCoordinatorT", bound=FroniusCoordinatorBase)
|
||||
|
||||
FroniusConfigEntry = ConfigEntry["FroniusSolarNet"]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: FroniusConfigEntry) -> bool:
|
||||
"""Set up fronius from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
fronius = Fronius(async_get_clientsession(hass), host)
|
||||
solar_net = FroniusSolarNet(hass, entry, fronius)
|
||||
await solar_net.init_devices()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = solar_net
|
||||
entry.runtime_data = solar_net
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: FroniusConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
solar_net = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
while solar_net.cleanup_callbacks:
|
||||
solar_net.cleanup_callbacks.pop()()
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_remove_config_entry_device(
|
||||
@@ -81,7 +76,6 @@ class FroniusSolarNet:
|
||||
) -> None:
|
||||
"""Initialize FroniusSolarNet class."""
|
||||
self.hass = hass
|
||||
self.cleanup_callbacks: list[Callable[[], None]] = []
|
||||
self.config_entry = entry
|
||||
self.coordinator_lock = asyncio.Lock()
|
||||
self.fronius = fronius
|
||||
@@ -151,7 +145,7 @@ class FroniusSolarNet:
|
||||
)
|
||||
|
||||
# Setup periodic re-scan
|
||||
self.cleanup_callbacks.append(
|
||||
self.config_entry.async_on_unload(
|
||||
async_track_time_interval(
|
||||
self.hass,
|
||||
self._init_devices_inverter,
|
||||
|
@@ -121,7 +121,7 @@ class FroniusCoordinatorBase(
|
||||
async_add_entities(new_entities)
|
||||
|
||||
_add_entities_for_unregistered_descriptors()
|
||||
self.solar_net.cleanup_callbacks.append(
|
||||
self.solar_net.config_entry.async_on_unload(
|
||||
self.async_add_listener(_add_entities_for_unregistered_descriptors)
|
||||
)
|
||||
|
||||
|
@@ -12,7 +12,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
POWER_VOLT_AMPERE_REACTIVE,
|
||||
@@ -44,7 +43,7 @@ from .const import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import FroniusSolarNet
|
||||
from . import FroniusConfigEntry
|
||||
from .coordinator import (
|
||||
FroniusCoordinatorBase,
|
||||
FroniusInverterUpdateCoordinator,
|
||||
@@ -60,11 +59,11 @@ ENERGY_VOLT_AMPERE_REACTIVE_HOUR: Final = "varh"
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: FroniusConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Fronius sensor entities based on a config entry."""
|
||||
solar_net: FroniusSolarNet = hass.data[DOMAIN][config_entry.entry_id]
|
||||
solar_net = config_entry.runtime_data
|
||||
|
||||
for inverter_coordinator in solar_net.inverter_coordinators:
|
||||
inverter_coordinator.add_entities_for_seen_keys(
|
||||
|
@@ -8,7 +8,7 @@ from collections.abc import Callable, Collection, Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_ON
|
||||
from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_OFF, STATE_ON
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
Event,
|
||||
@@ -24,7 +24,7 @@ from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.event import async_track_state_change_event
|
||||
|
||||
from .const import ATTR_AUTO, ATTR_ORDER, DOMAIN, GROUP_ORDER, REG_KEY
|
||||
from .registry import GroupIntegrationRegistry
|
||||
from .registry import GroupIntegrationRegistry, SingleStateType
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
|
||||
@@ -133,6 +133,7 @@ class Group(Entity):
|
||||
_attr_should_poll = False
|
||||
tracking: tuple[str, ...]
|
||||
trackable: tuple[str, ...]
|
||||
single_state_type_key: SingleStateType | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -153,7 +154,7 @@ class Group(Entity):
|
||||
self._attr_name = name
|
||||
self._state: str | None = None
|
||||
self._attr_icon = icon
|
||||
self._set_tracked(entity_ids)
|
||||
self._entity_ids = entity_ids
|
||||
self._on_off: dict[str, bool] = {}
|
||||
self._assumed: dict[str, bool] = {}
|
||||
self._on_states: set[str] = set()
|
||||
@@ -287,6 +288,7 @@ class Group(Entity):
|
||||
if not entity_ids:
|
||||
self.tracking = ()
|
||||
self.trackable = ()
|
||||
self.single_state_type_key = None
|
||||
return
|
||||
|
||||
registry: GroupIntegrationRegistry = self.hass.data[REG_KEY]
|
||||
@@ -294,16 +296,42 @@ class Group(Entity):
|
||||
|
||||
tracking: list[str] = []
|
||||
trackable: list[str] = []
|
||||
single_state_type_set: set[SingleStateType] = set()
|
||||
for ent_id in entity_ids:
|
||||
ent_id_lower = ent_id.lower()
|
||||
domain = split_entity_id(ent_id_lower)[0]
|
||||
tracking.append(ent_id_lower)
|
||||
if domain not in excluded_domains:
|
||||
trackable.append(ent_id_lower)
|
||||
if domain in registry.state_group_mapping:
|
||||
single_state_type_set.add(registry.state_group_mapping[domain])
|
||||
elif domain == DOMAIN:
|
||||
# If a group contains another group we check if that group
|
||||
# has a specific single state type
|
||||
if ent_id in registry.state_group_mapping:
|
||||
single_state_type_set.add(registry.state_group_mapping[ent_id])
|
||||
else:
|
||||
single_state_type_set.add(SingleStateType(STATE_ON, STATE_OFF))
|
||||
|
||||
if len(single_state_type_set) == 1:
|
||||
self.single_state_type_key = next(iter(single_state_type_set))
|
||||
# To support groups with nested groups we store the state type
|
||||
# per group entity_id if there is a single state type
|
||||
registry.state_group_mapping[self.entity_id] = self.single_state_type_key
|
||||
else:
|
||||
self.single_state_type_key = None
|
||||
self.async_on_remove(self._async_deregister)
|
||||
|
||||
self.trackable = tuple(trackable)
|
||||
self.tracking = tuple(tracking)
|
||||
|
||||
@callback
|
||||
def _async_deregister(self) -> None:
|
||||
"""Deregister group entity from the registry."""
|
||||
registry: GroupIntegrationRegistry = self.hass.data[REG_KEY]
|
||||
if self.entity_id in registry.state_group_mapping:
|
||||
registry.state_group_mapping.pop(self.entity_id)
|
||||
|
||||
@callback
|
||||
def _async_start(self, _: HomeAssistant | None = None) -> None:
|
||||
"""Start tracking members and write state."""
|
||||
@@ -342,6 +370,7 @@ class Group(Entity):
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Handle addition to Home Assistant."""
|
||||
self._set_tracked(self._entity_ids)
|
||||
self.async_on_remove(start.async_at_start(self.hass, self._async_start))
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
@@ -430,12 +459,14 @@ class Group(Entity):
|
||||
# have the same on state we use this state
|
||||
# and its hass.data[REG_KEY].on_off_mapping to off
|
||||
if num_on_states == 1:
|
||||
on_state = list(self._on_states)[0]
|
||||
on_state = next(iter(self._on_states))
|
||||
# If we do not have an on state for any domains
|
||||
# we use None (which will be STATE_UNKNOWN)
|
||||
elif num_on_states == 0:
|
||||
self._state = None
|
||||
return
|
||||
if self.single_state_type_key:
|
||||
on_state = self.single_state_type_key.on_state
|
||||
# If the entity domains have more than one
|
||||
# on state, we use STATE_ON/STATE_OFF
|
||||
else:
|
||||
@@ -443,9 +474,10 @@ class Group(Entity):
|
||||
group_is_on = self.mode(self._on_off.values())
|
||||
if group_is_on:
|
||||
self._state = on_state
|
||||
elif self.single_state_type_key:
|
||||
self._state = self.single_state_type_key.off_state
|
||||
else:
|
||||
registry: GroupIntegrationRegistry = self.hass.data[REG_KEY]
|
||||
self._state = registry.on_off_mapping[on_state]
|
||||
self._state = STATE_OFF
|
||||
|
||||
|
||||
def async_get_component(hass: HomeAssistant) -> EntityComponent[Group]:
|
||||
|
@@ -1,8 +1,12 @@
|
||||
"""Provide the functionality to group entities."""
|
||||
"""Provide the functionality to group entities.
|
||||
|
||||
Legacy group support will not be extended for new domains.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Protocol
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@@ -12,9 +16,6 @@ from homeassistant.helpers.integration_platform import (
|
||||
|
||||
from .const import DOMAIN, REG_KEY
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .entity import Group
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant) -> None:
|
||||
"""Set up the Group integration registry of integration platforms."""
|
||||
@@ -43,6 +44,14 @@ def _process_group_platform(
|
||||
platform.async_describe_on_off_states(hass, registry)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class SingleStateType:
|
||||
"""Dataclass to store a single state type."""
|
||||
|
||||
on_state: str
|
||||
off_state: str
|
||||
|
||||
|
||||
class GroupIntegrationRegistry:
|
||||
"""Class to hold a registry of integrations."""
|
||||
|
||||
@@ -53,8 +62,7 @@ class GroupIntegrationRegistry:
|
||||
self.off_on_mapping: dict[str, str] = {STATE_OFF: STATE_ON}
|
||||
self.on_states_by_domain: dict[str, set[str]] = {}
|
||||
self.exclude_domains: set[str] = set()
|
||||
self.state_group_mapping: dict[str, tuple[str, str]] = {}
|
||||
self.group_entities: set[Group] = set()
|
||||
self.state_group_mapping: dict[str, SingleStateType] = {}
|
||||
|
||||
@callback
|
||||
def exclude_domain(self, domain: str) -> None:
|
||||
@@ -65,12 +73,16 @@ class GroupIntegrationRegistry:
|
||||
def on_off_states(
|
||||
self, domain: str, on_states: set[str], default_on_state: str, off_state: str
|
||||
) -> None:
|
||||
"""Register on and off states for the current domain."""
|
||||
"""Register on and off states for the current domain.
|
||||
|
||||
Legacy group support will not be extended for new domains.
|
||||
"""
|
||||
for on_state in on_states:
|
||||
if on_state not in self.on_off_mapping:
|
||||
self.on_off_mapping[on_state] = off_state
|
||||
|
||||
if len(on_states) == 1 and off_state not in self.off_on_mapping:
|
||||
if off_state not in self.off_on_mapping:
|
||||
self.off_on_mapping[off_state] = default_on_state
|
||||
self.state_group_mapping[domain] = SingleStateType(default_on_state, off_state)
|
||||
|
||||
self.on_states_by_domain[domain] = on_states
|
||||
|
@@ -18,7 +18,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from .const import CONF_STATION_ID
|
||||
from .coordinator import ImgwPibDataUpdateCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
82
homeassistant/components/imgw_pib/binary_sensor.py
Normal file
82
homeassistant/components/imgw_pib/binary_sensor.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""IMGW-PIB binary sensor platform."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
|
||||
from imgw_pib.model import HydrologicalData
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import ImgwPibConfigEntry
|
||||
from .coordinator import ImgwPibDataUpdateCoordinator
|
||||
from .entity import ImgwPibEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class ImgwPibBinarySensorEntityDescription(BinarySensorEntityDescription):
|
||||
"""IMGW-PIB sensor entity description."""
|
||||
|
||||
value: Callable[[HydrologicalData], bool | None]
|
||||
|
||||
|
||||
BINARY_SENSOR_TYPES: tuple[ImgwPibBinarySensorEntityDescription, ...] = (
|
||||
ImgwPibBinarySensorEntityDescription(
|
||||
key="flood_warning",
|
||||
translation_key="flood_warning",
|
||||
device_class=BinarySensorDeviceClass.SAFETY,
|
||||
value=lambda data: data.flood_warning,
|
||||
),
|
||||
ImgwPibBinarySensorEntityDescription(
|
||||
key="flood_alarm",
|
||||
translation_key="flood_alarm",
|
||||
device_class=BinarySensorDeviceClass.SAFETY,
|
||||
value=lambda data: data.flood_alarm,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ImgwPibConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Add a IMGW-PIB binary sensor entity from a config_entry."""
|
||||
coordinator = entry.runtime_data.coordinator
|
||||
|
||||
async_add_entities(
|
||||
ImgwPibBinarySensorEntity(coordinator, description)
|
||||
for description in BINARY_SENSOR_TYPES
|
||||
if getattr(coordinator.data, description.key) is not None
|
||||
)
|
||||
|
||||
|
||||
class ImgwPibBinarySensorEntity(ImgwPibEntity, BinarySensorEntity):
|
||||
"""Define IMGW-PIB binary sensor entity."""
|
||||
|
||||
entity_description: ImgwPibBinarySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ImgwPibDataUpdateCoordinator,
|
||||
description: ImgwPibBinarySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._attr_unique_id = f"{coordinator.station_id}_{description.key}"
|
||||
self.entity_description = description
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if the binary sensor is on."""
|
||||
return self.entity_description.value(self.coordinator.data)
|
22
homeassistant/components/imgw_pib/entity.py
Normal file
22
homeassistant/components/imgw_pib/entity.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Define the IMGW-PIB entity."""
|
||||
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import ATTRIBUTION
|
||||
from .coordinator import ImgwPibDataUpdateCoordinator
|
||||
|
||||
|
||||
class ImgwPibEntity(CoordinatorEntity[ImgwPibDataUpdateCoordinator]):
|
||||
"""Define IMGW-PIB entity."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ImgwPibDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._attr_device_info = coordinator.device_info
|
@@ -1,5 +1,19 @@
|
||||
{
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"flood_warning": {
|
||||
"default": "mdi:check-circle",
|
||||
"state": {
|
||||
"on": "mdi:home-flood"
|
||||
}
|
||||
},
|
||||
"flood_alarm": {
|
||||
"default": "mdi:check-circle",
|
||||
"state": {
|
||||
"on": "mdi:home-flood"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"water_level": {
|
||||
"default": "mdi:waves"
|
||||
|
@@ -17,11 +17,10 @@ from homeassistant.const import UnitOfLength, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import ImgwPibConfigEntry
|
||||
from .const import ATTRIBUTION
|
||||
from .coordinator import ImgwPibDataUpdateCoordinator
|
||||
from .entity import ImgwPibEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
@@ -70,13 +69,9 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class ImgwPibSensorEntity(
|
||||
CoordinatorEntity[ImgwPibDataUpdateCoordinator], SensorEntity
|
||||
):
|
||||
class ImgwPibSensorEntity(ImgwPibEntity, SensorEntity):
|
||||
"""Define IMGW-PIB sensor entity."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
entity_description: ImgwPibSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
@@ -88,7 +83,6 @@ class ImgwPibSensorEntity(
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._attr_unique_id = f"{coordinator.station_id}_{description.key}"
|
||||
self._attr_device_info = coordinator.device_info
|
||||
self.entity_description = description
|
||||
|
||||
@property
|
||||
|
@@ -17,6 +17,14 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"flood_alarm": {
|
||||
"name": "Flood alarm"
|
||||
},
|
||||
"flood_warning": {
|
||||
"name": "Flood warning"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"water_level": {
|
||||
"name": "Water level"
|
||||
|
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.notify import NotifyEntity
|
||||
from homeassistant.components.notify import NotifyEntity, NotifyEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -25,6 +25,12 @@ async def async_setup_entry(
|
||||
device_name="MyBox",
|
||||
entity_name="Personal notifier",
|
||||
),
|
||||
DemoNotify(
|
||||
unique_id="just_notify_me_title",
|
||||
device_name="MyBox",
|
||||
entity_name="Personal notifier with title",
|
||||
supported_features=NotifyEntityFeature.TITLE,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -40,15 +46,19 @@ class DemoNotify(NotifyEntity):
|
||||
unique_id: str,
|
||||
device_name: str,
|
||||
entity_name: str | None,
|
||||
supported_features: NotifyEntityFeature = NotifyEntityFeature(0),
|
||||
) -> None:
|
||||
"""Initialize the Demo button entity."""
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_supported_features = supported_features
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
name=device_name,
|
||||
)
|
||||
self._attr_name = entity_name
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send out a persistent notification."""
|
||||
persistent_notification.async_create(self.hass, message, "Demo notification")
|
||||
persistent_notification.async_create(
|
||||
self.hass, message, title or "Demo notification"
|
||||
)
|
||||
|
@@ -108,6 +108,6 @@ class KNXNotify(KnxEntity, NotifyEntity):
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a notification to knx bus."""
|
||||
await self._device.set(message)
|
||||
|
@@ -13,16 +13,7 @@ import voluptuous as vol
|
||||
from homeassistant import config as conf_util
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_CLIENT_ID,
|
||||
CONF_DISCOVERY,
|
||||
CONF_PASSWORD,
|
||||
CONF_PAYLOAD,
|
||||
CONF_PORT,
|
||||
CONF_PROTOCOL,
|
||||
CONF_USERNAME,
|
||||
SERVICE_RELOAD,
|
||||
)
|
||||
from homeassistant.const import CONF_DISCOVERY, CONF_PAYLOAD, SERVICE_RELOAD
|
||||
from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import (
|
||||
ConfigValidationError,
|
||||
@@ -122,45 +113,6 @@ CONNECTION_SUCCESS = "connection_success"
|
||||
CONNECTION_FAILED = "connection_failed"
|
||||
CONNECTION_FAILED_RECOVERABLE = "connection_failed_recoverable"
|
||||
|
||||
CONFIG_ENTRY_CONFIG_KEYS = [
|
||||
CONF_BIRTH_MESSAGE,
|
||||
CONF_BROKER,
|
||||
CONF_CERTIFICATE,
|
||||
CONF_CLIENT_ID,
|
||||
CONF_CLIENT_CERT,
|
||||
CONF_CLIENT_KEY,
|
||||
CONF_DISCOVERY,
|
||||
CONF_DISCOVERY_PREFIX,
|
||||
CONF_KEEPALIVE,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_PROTOCOL,
|
||||
CONF_TLS_INSECURE,
|
||||
CONF_TRANSPORT,
|
||||
CONF_WS_PATH,
|
||||
CONF_WS_HEADERS,
|
||||
CONF_USERNAME,
|
||||
CONF_WILL_MESSAGE,
|
||||
]
|
||||
|
||||
REMOVED_OPTIONS = vol.All(
|
||||
cv.removed(CONF_BIRTH_MESSAGE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_BROKER), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CERTIFICATE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_ID), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_CERT), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_KEY), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_DISCOVERY), # Removed in HA Core 2022.3
|
||||
cv.removed(CONF_DISCOVERY_PREFIX), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_KEEPALIVE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PASSWORD), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PORT), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PROTOCOL), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_TLS_INSECURE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_USERNAME), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_WILL_MESSAGE), # Removed in HA Core 2023.4
|
||||
)
|
||||
|
||||
# We accept 2 schemes for configuring manual MQTT items
|
||||
#
|
||||
# Preferred style:
|
||||
@@ -187,7 +139,6 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
DOMAIN: vol.All(
|
||||
cv.ensure_list,
|
||||
cv.remove_falsy,
|
||||
[REMOVED_OPTIONS],
|
||||
[CONFIG_SCHEMA_BASE],
|
||||
)
|
||||
},
|
||||
|
@@ -86,7 +86,6 @@ ABBREVIATIONS = {
|
||||
"json_attr": "json_attributes",
|
||||
"json_attr_t": "json_attributes_topic",
|
||||
"json_attr_tpl": "json_attributes_template",
|
||||
"lrst_t": "last_reset_topic",
|
||||
"lrst_val_tpl": "last_reset_value_template",
|
||||
"max": "max",
|
||||
"min": "min",
|
||||
|
@@ -197,7 +197,6 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
entry: ConfigEntry | None
|
||||
_hassio_discovery: dict[str, Any] | None = None
|
||||
_reauth_config_entry: ConfigEntry | None = None
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
|
@@ -83,7 +83,7 @@ class MqttNotify(MqttEntity, NotifyEntity):
|
||||
async def _subscribe_topics(self) -> None:
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a message."""
|
||||
payload = self._command_template(message)
|
||||
await self.async_publish(
|
||||
|
@@ -58,7 +58,6 @@ from .models import (
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_EXPIRE_AFTER = "expire_after"
|
||||
CONF_LAST_RESET_TOPIC = "last_reset_topic"
|
||||
CONF_LAST_RESET_VALUE_TEMPLATE = "last_reset_value_template"
|
||||
CONF_SUGGESTED_DISPLAY_PRECISION = "suggested_display_precision"
|
||||
|
||||
@@ -101,17 +100,11 @@ def validate_sensor_state_class_config(config: ConfigType) -> ConfigType:
|
||||
|
||||
|
||||
PLATFORM_SCHEMA_MODERN = vol.All(
|
||||
# Deprecated in HA Core 2021.11.0 https://github.com/home-assistant/core/pull/54840
|
||||
# Removed in HA Core 2023.6.0
|
||||
cv.removed(CONF_LAST_RESET_TOPIC),
|
||||
_PLATFORM_SCHEMA_BASE,
|
||||
validate_sensor_state_class_config,
|
||||
)
|
||||
|
||||
DISCOVERY_SCHEMA = vol.All(
|
||||
# Deprecated in HA Core 2021.11.0 https://github.com/home-assistant/core/pull/54840
|
||||
# Removed in HA Core 2023.6.0
|
||||
cv.removed(CONF_LAST_RESET_TOPIC),
|
||||
_PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA),
|
||||
validate_sensor_state_class_config,
|
||||
)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from enum import IntFlag
|
||||
from functools import cached_property, partial
|
||||
import logging
|
||||
from typing import Any, final, override
|
||||
@@ -58,6 +59,12 @@ PLATFORM_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
class NotifyEntityFeature(IntFlag):
|
||||
"""Supported features of a notify entity."""
|
||||
|
||||
TITLE = 1
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the notify services."""
|
||||
|
||||
@@ -73,7 +80,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
component = hass.data[DOMAIN] = EntityComponent[NotifyEntity](_LOGGER, DOMAIN, hass)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{vol.Required(ATTR_MESSAGE): cv.string},
|
||||
{
|
||||
vol.Required(ATTR_MESSAGE): cv.string,
|
||||
vol.Optional(ATTR_TITLE): cv.string,
|
||||
},
|
||||
"_async_send_message",
|
||||
)
|
||||
|
||||
@@ -128,6 +138,7 @@ class NotifyEntity(RestoreEntity):
|
||||
"""Representation of a notify entity."""
|
||||
|
||||
entity_description: NotifyEntityDescription
|
||||
_attr_supported_features: NotifyEntityFeature = NotifyEntityFeature(0)
|
||||
_attr_should_poll = False
|
||||
_attr_device_class: None
|
||||
_attr_state: None = None
|
||||
@@ -162,10 +173,19 @@ class NotifyEntity(RestoreEntity):
|
||||
self.async_write_ha_state()
|
||||
await self.async_send_message(**kwargs)
|
||||
|
||||
def send_message(self, message: str) -> None:
|
||||
def send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a message."""
|
||||
raise NotImplementedError
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a message."""
|
||||
await self.hass.async_add_executor_job(partial(self.send_message, message))
|
||||
kwargs: dict[str, Any] = {}
|
||||
if (
|
||||
title is not None
|
||||
and self.supported_features
|
||||
and self.supported_features & NotifyEntityFeature.TITLE
|
||||
):
|
||||
kwargs[ATTR_TITLE] = title
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(self.send_message, message, **kwargs)
|
||||
)
|
||||
|
@@ -29,6 +29,13 @@ send_message:
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
title:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
filter:
|
||||
supported_features:
|
||||
- notify.NotifyEntityFeature.TITLE
|
||||
|
||||
persistent_notification:
|
||||
fields:
|
||||
|
@@ -35,6 +35,10 @@
|
||||
"message": {
|
||||
"name": "Message",
|
||||
"description": "Your notification message."
|
||||
},
|
||||
"title": {
|
||||
"name": "Title",
|
||||
"description": "Title for your notification message."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -7,6 +7,7 @@ from homeassistant.helpers import config_entry_oauth2_flow
|
||||
|
||||
from . import api, config_flow
|
||||
from .const import DOMAIN
|
||||
from .coordinator import OndiloIcoCoordinator
|
||||
from .oauth_impl import OndiloOauth2Implementation
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
@@ -26,8 +27,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = api.OndiloClient(hass, entry, implementation)
|
||||
coordinator = OndiloIcoCoordinator(
|
||||
hass, api.OndiloClient(hass, entry, implementation)
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
|
37
homeassistant/components/ondilo_ico/coordinator.py
Normal file
37
homeassistant/components/ondilo_ico/coordinator.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""Define an object to coordinate fetching Ondilo ICO data."""
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from ondilo import OndiloError
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from . import DOMAIN
|
||||
from .api import OndiloClient
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OndiloIcoCoordinator(DataUpdateCoordinator[list[dict[str, Any]]]):
|
||||
"""Class to manage fetching Ondilo ICO data from API."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, api: OndiloClient) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(
|
||||
hass,
|
||||
logger=_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(minutes=5),
|
||||
)
|
||||
self.api = api
|
||||
|
||||
async def _async_update_data(self) -> list[dict[str, Any]]:
|
||||
"""Fetch data from API endpoint."""
|
||||
try:
|
||||
return await self.hass.async_add_executor_job(self.api.get_all_pools_data)
|
||||
|
||||
except OndiloError as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
@@ -2,12 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from ondilo import OndiloError
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
@@ -24,14 +18,10 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
UpdateFailed,
|
||||
)
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .api import OndiloClient
|
||||
from .const import DOMAIN
|
||||
from .coordinator import OndiloIcoCoordinator
|
||||
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
@@ -78,66 +68,30 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
)
|
||||
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=5)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the Ondilo ICO sensors."""
|
||||
|
||||
api: OndiloClient = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator: OndiloIcoCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async def async_update_data() -> list[dict[str, Any]]:
|
||||
"""Fetch data from API endpoint.
|
||||
|
||||
This is the place to pre-process the data to lookup tables
|
||||
so entities can quickly look up their data.
|
||||
"""
|
||||
try:
|
||||
return await hass.async_add_executor_job(api.get_all_pools_data)
|
||||
|
||||
except OndiloError as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
# Name of the data. For logging purposes.
|
||||
name="sensor",
|
||||
update_method=async_update_data,
|
||||
# Polling interval. Will only be polled if there are subscribers.
|
||||
update_interval=SCAN_INTERVAL,
|
||||
async_add_entities(
|
||||
OndiloICO(coordinator, poolidx, description)
|
||||
for poolidx, pool in enumerate(coordinator.data)
|
||||
for sensor in pool["sensors"]
|
||||
for description in SENSOR_TYPES
|
||||
if description.key == sensor["data_type"]
|
||||
)
|
||||
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await coordinator.async_refresh()
|
||||
|
||||
entities = []
|
||||
for poolidx, pool in enumerate(coordinator.data):
|
||||
entities.extend(
|
||||
[
|
||||
OndiloICO(coordinator, poolidx, description)
|
||||
for sensor in pool["sensors"]
|
||||
for description in SENSOR_TYPES
|
||||
if description.key == sensor["data_type"]
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class OndiloICO(
|
||||
CoordinatorEntity[DataUpdateCoordinator[list[dict[str, Any]]]], SensorEntity
|
||||
):
|
||||
class OndiloICO(CoordinatorEntity[OndiloIcoCoordinator], SensorEntity):
|
||||
"""Representation of a Sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator[list[dict[str, Any]]],
|
||||
coordinator: OndiloIcoCoordinator,
|
||||
poolidx: int,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
import openai
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import conversation
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, Platform
|
||||
from homeassistant.core import (
|
||||
@@ -115,5 +114,4 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return False
|
||||
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
conversation.async_unset_agent(hass, entry)
|
||||
return True
|
||||
|
@@ -3,7 +3,7 @@
|
||||
import logging
|
||||
|
||||
DOMAIN = "openai_conversation"
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
CONF_PROMPT = "prompt"
|
||||
DEFAULT_PROMPT = """This smart home is controlled by Home Assistant.
|
||||
|
||||
|
@@ -44,6 +44,8 @@ class OpenAIConversationEntity(
|
||||
):
|
||||
"""OpenAI conversation agent."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Initialize the agent."""
|
||||
self.hass = hass
|
||||
|
@@ -69,7 +69,9 @@ class RoborockMap(RoborockCoordinatedEntity, ImageEntity):
|
||||
try:
|
||||
self.cached_map = self._create_image(starting_map)
|
||||
except HomeAssistantError:
|
||||
# If we failed to update the image on init, we set cached_map to empty bytes so that we are unavailable and can try again later.
|
||||
# If we failed to update the image on init,
|
||||
# we set cached_map to empty bytes
|
||||
# so that we are unavailable and can try again later.
|
||||
self.cached_map = b""
|
||||
self._attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
|
||||
@@ -84,7 +86,11 @@ class RoborockMap(RoborockCoordinatedEntity, ImageEntity):
|
||||
return self.map_flag == self.coordinator.current_map
|
||||
|
||||
def is_map_valid(self) -> bool:
|
||||
"""Update this map if it is the current active map, and the vacuum is cleaning or if it has never been set at all."""
|
||||
"""Update the map if it is valid.
|
||||
|
||||
Update this map if it is the currently active map, and the
|
||||
vacuum is cleaning, or if it has never been set at all.
|
||||
"""
|
||||
return self.cached_map == b"" or (
|
||||
self.is_selected
|
||||
and self.image_last_updated is not None
|
||||
@@ -134,8 +140,9 @@ class RoborockMap(RoborockCoordinatedEntity, ImageEntity):
|
||||
async def create_coordinator_maps(
|
||||
coord: RoborockDataUpdateCoordinator,
|
||||
) -> list[RoborockMap]:
|
||||
"""Get the starting map information for all maps for this device. The following steps must be done synchronously.
|
||||
"""Get the starting map information for all maps for this device.
|
||||
|
||||
The following steps must be done synchronously.
|
||||
Only one map can be loaded at a time per device.
|
||||
"""
|
||||
entities = []
|
||||
@@ -161,7 +168,8 @@ async def create_coordinator_maps(
|
||||
map_update = await asyncio.gather(
|
||||
*[coord.cloud_api.get_map_v1(), coord.get_rooms()], return_exceptions=True
|
||||
)
|
||||
# If we fail to get the map -> We should set it to empty byte, still create it, and set it as unavailable.
|
||||
# If we fail to get the map, we should set it to empty byte,
|
||||
# still create it, and set it as unavailable.
|
||||
api_data: bytes = map_update[0] if isinstance(map_update[0], bytes) else b""
|
||||
entities.append(
|
||||
RoborockMap(
|
||||
|
@@ -4,5 +4,5 @@
|
||||
"codeowners": ["@fabaff"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/serial",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["pyserial-asyncio==0.6"]
|
||||
"requirements": ["pyserial-asyncio-fast==0.11"]
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import json
|
||||
import logging
|
||||
|
||||
from serial import SerialException
|
||||
import serial_asyncio
|
||||
import serial_asyncio_fast as serial_asyncio
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
|
@@ -8,7 +8,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiounifi"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aiounifi==76"],
|
||||
"requirements": ["aiounifi==77"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
@@ -57,4 +57,6 @@ SKU_TO_BASE_DEVICE = {
|
||||
"Vital100S": "Vital100S",
|
||||
"LAP-V102S-WUS": "Vital100S", # Alt ID Model Vital100S
|
||||
"LAP-V102S-AASR": "Vital100S", # Alt ID Model Vital100S
|
||||
"LAP-V102S-WEU": "Vital100S", # Alt ID Model Vital100S
|
||||
"LAP-V102S-WUK": "Vital100S", # Alt ID Model Vital100S
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/waze_travel_time",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pywaze", "homeassistant.helpers.location"],
|
||||
"requirements": ["pywaze==1.0.0"]
|
||||
"requirements": ["pywaze==1.0.1"]
|
||||
}
|
||||
|
@@ -21,9 +21,8 @@
|
||||
"universal_silabs_flasher"
|
||||
],
|
||||
"requirements": [
|
||||
"bellows==0.38.3",
|
||||
"bellows==0.38.4",
|
||||
"pyserial==3.5",
|
||||
"pyserial-asyncio==0.6",
|
||||
"zha-quirks==0.0.115",
|
||||
"zigpy-deconz==0.23.1",
|
||||
"zigpy==0.64.0",
|
||||
|
@@ -98,6 +98,7 @@ def _entity_features() -> dict[str, type[IntFlag]]:
|
||||
from homeassistant.components.light import LightEntityFeature
|
||||
from homeassistant.components.lock import LockEntityFeature
|
||||
from homeassistant.components.media_player import MediaPlayerEntityFeature
|
||||
from homeassistant.components.notify import NotifyEntityFeature
|
||||
from homeassistant.components.remote import RemoteEntityFeature
|
||||
from homeassistant.components.siren import SirenEntityFeature
|
||||
from homeassistant.components.todo import TodoListEntityFeature
|
||||
@@ -119,6 +120,7 @@ def _entity_features() -> dict[str, type[IntFlag]]:
|
||||
"LightEntityFeature": LightEntityFeature,
|
||||
"LockEntityFeature": LockEntityFeature,
|
||||
"MediaPlayerEntityFeature": MediaPlayerEntityFeature,
|
||||
"NotifyEntityFeature": NotifyEntityFeature,
|
||||
"RemoteEntityFeature": RemoteEntityFeature,
|
||||
"SirenEntityFeature": SirenEntityFeature,
|
||||
"TodoListEntityFeature": TodoListEntityFeature,
|
||||
|
@@ -28,7 +28,7 @@ dbus-fast==2.21.1
|
||||
fnv-hash-fast==0.5.0
|
||||
ha-av==10.1.1
|
||||
ha-ffmpeg==3.2.0
|
||||
habluetooth==2.8.0
|
||||
habluetooth==2.8.1
|
||||
hass-nabucasa==0.78.0
|
||||
hassil==1.6.1
|
||||
home-assistant-bluetooth==1.12.0
|
||||
@@ -192,3 +192,8 @@ pycountry>=23.12.11
|
||||
|
||||
# scapy<2.5.0 will not work with python3.12
|
||||
scapy>=2.5.0
|
||||
|
||||
# tuf isn't updated to deal with breaking changes in securesystemslib==1.0.
|
||||
# Only tuf>=4 includes a constraint to <1.0.
|
||||
# https://github.com/theupdateframework/python-tuf/releases/tag/v4.0.0
|
||||
tuf>=4.0.0
|
||||
|
@@ -386,7 +386,7 @@ aiotankerkoenig==0.4.1
|
||||
aiotractive==0.5.6
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==76
|
||||
aiounifi==77
|
||||
|
||||
# homeassistant.components.vlc_telnet
|
||||
aiovlc==0.1.0
|
||||
@@ -413,7 +413,7 @@ aioymaps==1.2.2
|
||||
airly==1.1.0
|
||||
|
||||
# homeassistant.components.airthings_ble
|
||||
airthings-ble==0.7.1
|
||||
airthings-ble==0.8.0
|
||||
|
||||
# homeassistant.components.airthings
|
||||
airthings-cloud==0.2.0
|
||||
@@ -541,7 +541,7 @@ beautifulsoup4==4.12.3
|
||||
# beewi-smartclim==0.0.10
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.38.3
|
||||
bellows==0.38.4
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer-connected[china]==0.15.2
|
||||
@@ -804,7 +804,7 @@ enocean==0.50
|
||||
enturclient==0.2.4
|
||||
|
||||
# homeassistant.components.environment_canada
|
||||
env-canada==0.6.0
|
||||
env-canada==0.6.2
|
||||
|
||||
# homeassistant.components.season
|
||||
ephem==4.1.5
|
||||
@@ -1035,7 +1035,7 @@ ha-philipsjs==3.1.1
|
||||
habitipy==0.2.0
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
habluetooth==2.8.0
|
||||
habluetooth==2.8.1
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.78.0
|
||||
@@ -2122,12 +2122,9 @@ pyschlage==2024.2.0
|
||||
# homeassistant.components.sensibo
|
||||
pysensibo==1.0.36
|
||||
|
||||
# homeassistant.components.zha
|
||||
pyserial-asyncio-fast==0.11
|
||||
|
||||
# homeassistant.components.serial
|
||||
# homeassistant.components.zha
|
||||
pyserial-asyncio==0.6
|
||||
pyserial-asyncio-fast==0.11
|
||||
|
||||
# homeassistant.components.acer_projector
|
||||
# homeassistant.components.crownstone
|
||||
@@ -2218,7 +2215,7 @@ python-clementine-remote==1.0.1
|
||||
python-digitalocean==1.13.2
|
||||
|
||||
# homeassistant.components.ecobee
|
||||
python-ecobee-api==0.2.17
|
||||
python-ecobee-api==0.2.18
|
||||
|
||||
# homeassistant.components.etherscan
|
||||
python-etherscan-api==0.0.3
|
||||
@@ -2376,7 +2373,7 @@ pyvlx==0.2.21
|
||||
pyvolumio==0.1.5
|
||||
|
||||
# homeassistant.components.waze_travel_time
|
||||
pywaze==1.0.0
|
||||
pywaze==1.0.1
|
||||
|
||||
# homeassistant.components.weatherflow
|
||||
pyweatherflowudp==1.4.5
|
||||
|
@@ -359,7 +359,7 @@ aiotankerkoenig==0.4.1
|
||||
aiotractive==0.5.6
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==76
|
||||
aiounifi==77
|
||||
|
||||
# homeassistant.components.vlc_telnet
|
||||
aiovlc==0.1.0
|
||||
@@ -386,7 +386,7 @@ aioymaps==1.2.2
|
||||
airly==1.1.0
|
||||
|
||||
# homeassistant.components.airthings_ble
|
||||
airthings-ble==0.7.1
|
||||
airthings-ble==0.8.0
|
||||
|
||||
# homeassistant.components.airthings
|
||||
airthings-cloud==0.2.0
|
||||
@@ -466,7 +466,7 @@ base36==0.1.1
|
||||
beautifulsoup4==4.12.3
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.38.3
|
||||
bellows==0.38.4
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer-connected[china]==0.15.2
|
||||
@@ -658,7 +658,7 @@ energyzero==2.1.0
|
||||
enocean==0.50
|
||||
|
||||
# homeassistant.components.environment_canada
|
||||
env-canada==0.6.0
|
||||
env-canada==0.6.2
|
||||
|
||||
# homeassistant.components.season
|
||||
ephem==4.1.5
|
||||
@@ -849,7 +849,7 @@ ha-philipsjs==3.1.1
|
||||
habitipy==0.2.0
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
habluetooth==2.8.0
|
||||
habluetooth==2.8.1
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.78.0
|
||||
@@ -1661,12 +1661,9 @@ pyschlage==2024.2.0
|
||||
# homeassistant.components.sensibo
|
||||
pysensibo==1.0.36
|
||||
|
||||
# homeassistant.components.zha
|
||||
pyserial-asyncio-fast==0.11
|
||||
|
||||
# homeassistant.components.serial
|
||||
# homeassistant.components.zha
|
||||
pyserial-asyncio==0.6
|
||||
pyserial-asyncio-fast==0.11
|
||||
|
||||
# homeassistant.components.acer_projector
|
||||
# homeassistant.components.crownstone
|
||||
@@ -1733,7 +1730,7 @@ python-awair==0.2.4
|
||||
python-bsblan==0.5.18
|
||||
|
||||
# homeassistant.components.ecobee
|
||||
python-ecobee-api==0.2.17
|
||||
python-ecobee-api==0.2.18
|
||||
|
||||
# homeassistant.components.fully_kiosk
|
||||
python-fullykiosk==0.0.12
|
||||
@@ -1849,7 +1846,7 @@ pyvlx==0.2.21
|
||||
pyvolumio==0.1.5
|
||||
|
||||
# homeassistant.components.waze_travel_time
|
||||
pywaze==1.0.0
|
||||
pywaze==1.0.1
|
||||
|
||||
# homeassistant.components.weatherflow
|
||||
pyweatherflowudp==1.4.5
|
||||
|
@@ -214,6 +214,11 @@ pycountry>=23.12.11
|
||||
|
||||
# scapy<2.5.0 will not work with python3.12
|
||||
scapy>=2.5.0
|
||||
|
||||
# tuf isn't updated to deal with breaking changes in securesystemslib==1.0.
|
||||
# Only tuf>=4 includes a constraint to <1.0.
|
||||
# https://github.com/theupdateframework/python-tuf/releases/tag/v4.0.0
|
||||
tuf>=4.0.0
|
||||
"""
|
||||
|
||||
GENERATED_MESSAGE = (
|
||||
|
@@ -81,7 +81,7 @@ async def setup_airvisual_pro_fixture(hass, config, pro):
|
||||
return_value=pro,
|
||||
),
|
||||
patch("homeassistant.components.airvisual_pro.NodeSamba", return_value=pro),
|
||||
patch("homeassistant.components.airvisual.PLATFORMS", []),
|
||||
patch("homeassistant.components.airvisual_pro.PLATFORMS", []),
|
||||
):
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.ambient_station import DOMAIN
|
||||
from homeassistant.components.ambient_station import AmbientStationConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
@@ -11,14 +11,14 @@ from tests.typing import ClientSessionGenerator
|
||||
|
||||
async def test_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
config_entry,
|
||||
config_entry: AmbientStationConfigEntry,
|
||||
hass_client: ClientSessionGenerator,
|
||||
data_station,
|
||||
setup_config_entry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test config entry diagnostics."""
|
||||
ambient = hass.data[DOMAIN][config_entry.entry_id]
|
||||
ambient = config_entry.runtime_data
|
||||
ambient.stations = data_station
|
||||
assert (
|
||||
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
|
||||
|
@@ -8,7 +8,7 @@ from unittest.mock import ANY, AsyncMock, MagicMock, Mock, patch
|
||||
from bleak import BleakError
|
||||
from bleak.backends.scanner import AdvertisementData, BLEDevice
|
||||
from bluetooth_adapters import DEFAULT_ADDRESS
|
||||
from habluetooth import scanner
|
||||
from habluetooth import scanner, set_manager
|
||||
from habluetooth.wrappers import HaBleakScannerWrapper
|
||||
import pytest
|
||||
|
||||
@@ -1154,6 +1154,7 @@ async def test_async_discovered_device_api(
|
||||
) -> None:
|
||||
"""Test the async_discovered_device API."""
|
||||
mock_bt = []
|
||||
set_manager(None)
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.bluetooth.async_get_bluetooth",
|
||||
@@ -1169,8 +1170,10 @@ async def test_async_discovered_device_api(
|
||||
},
|
||||
),
|
||||
):
|
||||
assert not bluetooth.async_discovered_service_info(hass)
|
||||
assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22")
|
||||
with pytest.raises(RuntimeError, match="BluetoothManager has not been set"):
|
||||
assert not bluetooth.async_discovered_service_info(hass)
|
||||
with pytest.raises(RuntimeError, match="BluetoothManager has not been set"):
|
||||
assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22")
|
||||
await async_setup_with_default_adapter(hass)
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init"):
|
||||
@@ -2744,6 +2747,7 @@ async def test_async_ble_device_from_address(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start: MagicMock, macos_adapter: None
|
||||
) -> None:
|
||||
"""Test the async_ble_device_from_address api."""
|
||||
set_manager(None)
|
||||
mock_bt = []
|
||||
with (
|
||||
patch(
|
||||
@@ -2760,11 +2764,15 @@ async def test_async_ble_device_from_address(
|
||||
},
|
||||
),
|
||||
):
|
||||
assert not bluetooth.async_discovered_service_info(hass)
|
||||
assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22")
|
||||
assert (
|
||||
bluetooth.async_ble_device_from_address(hass, "44:44:33:11:23:45") is None
|
||||
)
|
||||
with pytest.raises(RuntimeError, match="BluetoothManager has not been set"):
|
||||
assert not bluetooth.async_discovered_service_info(hass)
|
||||
with pytest.raises(RuntimeError, match="BluetoothManager has not been set"):
|
||||
assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22")
|
||||
with pytest.raises(RuntimeError, match="BluetoothManager has not been set"):
|
||||
assert (
|
||||
bluetooth.async_ble_device_from_address(hass, "44:44:33:11:23:45")
|
||||
is None
|
||||
)
|
||||
|
||||
await async_setup_with_default_adapter(hass)
|
||||
|
||||
|
@@ -6,7 +6,7 @@ from aiohttp import ClientConnectionError, ClientResponseError
|
||||
from bond_async import DeviceType
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.bond.const import DOMAIN
|
||||
from homeassistant.components.bond import DOMAIN, BondData
|
||||
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_ASSUMED_STATE, CONF_ACCESS_TOKEN, CONF_HOST
|
||||
@@ -107,7 +107,7 @@ async def test_async_setup_entry_sets_up_hub_and_supported_domains(
|
||||
assert result is True
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.entry_id in hass.data[DOMAIN]
|
||||
assert isinstance(config_entry.runtime_data, BondData)
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
assert config_entry.unique_id == "ZXXX12345"
|
||||
|
||||
@@ -148,7 +148,6 @@ async def test_unload_config_entry(hass: HomeAssistant) -> None:
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.entry_id not in hass.data[DOMAIN]
|
||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
@@ -194,7 +193,6 @@ async def test_old_identifiers_are_removed(
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.entry_id in hass.data[DOMAIN]
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
assert config_entry.unique_id == "ZXXX12345"
|
||||
|
||||
@@ -238,7 +236,6 @@ async def test_smart_by_bond_device_suggested_area(
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.entry_id in hass.data[DOMAIN]
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
assert config_entry.unique_id == "KXXX12345"
|
||||
|
||||
@@ -287,7 +284,6 @@ async def test_bridge_device_suggested_area(
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.entry_id in hass.data[DOMAIN]
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
assert config_entry.unique_id == "ZXXX12345"
|
||||
|
||||
|
@@ -69,7 +69,17 @@ async def test_sending_message(hass: HomeAssistant, events: list[Event]) -> None
|
||||
await hass.services.async_call(notify.DOMAIN, notify.SERVICE_SEND_MESSAGE, data)
|
||||
await hass.async_block_till_done()
|
||||
last_event = events[-1]
|
||||
assert last_event.data[notify.ATTR_MESSAGE] == "Test message"
|
||||
assert last_event.data == {notify.ATTR_MESSAGE: "Test message"}
|
||||
|
||||
data[notify.ATTR_TITLE] = "My title"
|
||||
# Test with Title
|
||||
await hass.services.async_call(notify.DOMAIN, notify.SERVICE_SEND_MESSAGE, data)
|
||||
await hass.async_block_till_done()
|
||||
last_event = events[-1]
|
||||
assert last_event.data == {
|
||||
notify.ATTR_MESSAGE: "Test message",
|
||||
notify.ATTR_TITLE: "My title",
|
||||
}
|
||||
|
||||
|
||||
async def test_calling_notify_from_script_loaded_from_yaml(
|
||||
|
@@ -65,6 +65,9 @@ GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP = {
|
||||
"identifier": 8675309,
|
||||
"name": "ecobee",
|
||||
"modelNumber": "athenaSmart",
|
||||
"utcTime": "2022-01-01 10:00:00",
|
||||
"thermostatTime": "2022-01-01 6:00:00",
|
||||
"location": {"timeZone": "America/Toronto"},
|
||||
"program": {
|
||||
"climates": [
|
||||
{"name": "Climate1", "climateRef": "c1"},
|
||||
@@ -92,7 +95,8 @@ GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP = {
|
||||
"humidifierMode": "manual",
|
||||
"humidity": "30",
|
||||
"hasHeatPump": True,
|
||||
"ventilatorType": "none",
|
||||
"ventilatorType": "hrv",
|
||||
"ventilatorOffDateTime": "2022-01-01 6:00:00",
|
||||
},
|
||||
"equipmentStatus": "fan",
|
||||
"events": [
|
||||
|
@@ -4,6 +4,11 @@
|
||||
"identifier": 8675309,
|
||||
"name": "ecobee",
|
||||
"modelNumber": "athenaSmart",
|
||||
"utcTime": "2022-01-01 10:00:00",
|
||||
"thermostatTime": "2022-01-01 6:00:00",
|
||||
"location": {
|
||||
"timeZone": "America/Toronto"
|
||||
},
|
||||
"program": {
|
||||
"climates": [
|
||||
{ "name": "Climate1", "climateRef": "c1" },
|
||||
@@ -30,6 +35,7 @@
|
||||
"ventilatorType": "hrv",
|
||||
"ventilatorMinOnTimeHome": 20,
|
||||
"ventilatorMinOnTimeAway": 10,
|
||||
"ventilatorOffDateTime": "2022-01-01 6:00:00",
|
||||
"isVentilatorTimerOn": false,
|
||||
"hasHumidifier": true,
|
||||
"humidifierMode": "manual",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user