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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions homeassistant/components/accuweather/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

from asyncio import timeout
from collections.abc import Mapping
from typing import Any
from typing import TYPE_CHECKING, Any

from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientError
from aiohttp.client_exceptions import ClientConnectorError
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession

Expand Down Expand Up @@ -55,8 +55,11 @@ async def async_step_user(
)
self._abort_if_unique_id_configured()

if TYPE_CHECKING:
assert accuweather.location_name is not None

return self.async_create_entry(
title=user_input[CONF_NAME], data=user_input
title=accuweather.location_name, data=user_input
)

return self.async_show_form(
Expand All @@ -70,9 +73,6 @@ async def async_step_user(
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(
CONF_NAME, default=self.hass.config.location_name
): str,
}
),
errors=errors,
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/accuweather/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __init__(
"""Initialize."""
self.accuweather = accuweather
self.location_key = accuweather.location_key
name = config_entry.data[CONF_NAME]
name = config_entry.data.get(CONF_NAME) or config_entry.title

if TYPE_CHECKING:
assert self.location_key is not None
Expand Down Expand Up @@ -122,7 +122,7 @@ def __init__(
self.accuweather = accuweather
self.location_key = accuweather.location_key
self._fetch_method = fetch_method
name = config_entry.data[CONF_NAME]
name = config_entry.data.get(CONF_NAME) or config_entry.title

if TYPE_CHECKING:
assert self.location_key is not None
Expand Down
80 changes: 62 additions & 18 deletions homeassistant/components/homematicip_cloud/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Support for HomematicIP Cloud devices."""

from __future__ import annotations

import logging

import voluptuous as vol

from homeassistant import config_entries
Expand All @@ -21,8 +25,11 @@
HMIPC_NAME,
)
from .hap import HomematicIPConfigEntry, HomematicipHAP
from .migration import _migrate_unique_id
from .services import async_setup_services

_LOGGER = logging.getLogger(__name__)

CONFIG_SCHEMA = vol.Schema(
{
vol.Optional(DOMAIN, default=[]): vol.All(
Expand Down Expand Up @@ -85,8 +92,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomematicIPConfigEntry)
if not await hap.async_setup():
return False

_async_remove_obsolete_entities(hass, entry, hap)

# Register on HA stop event to gracefully shutdown HomematicIP Cloud connection
hap.reset_connection_listener = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, hap.shutdown
Expand Down Expand Up @@ -119,22 +124,61 @@ async def async_unload_entry(
return await hap.async_reset()


@callback
def _async_remove_obsolete_entities(
hass: HomeAssistant, entry: HomematicIPConfigEntry, hap: HomematicipHAP
):
"""Remove obsolete entities from entity registry."""
async def async_migrate_entry(
hass: HomeAssistant, config_entry: config_entries.ConfigEntry
) -> bool:
"""Migrate the config entry from version 1 to version 2."""
if config_entry.version > 2:
return False

if config_entry.version == 1:
_LOGGER.debug("Migrating HomematicIP Cloud config entry to version 2")

# Remove obsolete entities before the bulk unique_id rewrite.
# After rewrite, old-format patterns would no longer be matchable.
# HomematicipAccesspointStatus* entities are always obsolete (removed
# in firmware 2.2.12+). HomematicipBatterySensor_{hapid} entities for
# access points are also obsolete. Those legacy access point battery
# entities do not belong to a device registry device, unlike real
# device battery sensors, so we can safely remove them before rewrite.
entity_registry = er.async_get(hass)
entries = er.async_entries_for_config_entry(
entity_registry, config_entry.entry_id
)
for entry in entries:
if entry.unique_id.startswith("HomematicipAccesspointStatus") or (
entry.unique_id.startswith("HomematicipBatterySensor_")
and entry.device_id is None
):
_LOGGER.debug(
"Removing obsolete entity: %s (%s)",
entry.entity_id,
entry.unique_id,
)
entity_registry.async_remove(entry.entity_id)

@callback
def _update_unique_id(
entity_entry: er.RegistryEntry,
) -> dict[str, str] | None:
new_unique_id = _migrate_unique_id(entity_entry.unique_id)
if new_unique_id is None:
_LOGGER.debug(
"Skipping unique_id %s (already stable format)",
entity_entry.unique_id,
)
return None
_LOGGER.debug(
"Migrating %s: %s -> %s",
entity_entry.entity_id,
entity_entry.unique_id,
new_unique_id,
)
return {"new_unique_id": new_unique_id}

if hap.home.currentAPVersion < "2.2.12":
return
await er.async_migrate_entries(hass, config_entry.entry_id, _update_unique_id)

entity_registry = er.async_get(hass)
er_entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id)
for er_entry in er_entries:
if er_entry.unique_id.startswith("HomematicipAccesspointStatus"):
entity_registry.async_remove(er_entry.entity_id)
continue
hass.config_entries.async_update_entry(config_entry, version=2)
_LOGGER.info("Migration to version 2 successful")

for hapid in hap.home.accessPointUpdateStates:
if er_entry.unique_id == f"HomematicipBatterySensor_{hapid}":
entity_registry.async_remove(er_entry.entity_id)
return True
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class HomematicipAlarmControlPanelEntity(AlarmControlPanelEntity):
| AlarmControlPanelEntityFeature.ARM_AWAY
)
_attr_code_arm_required = False
_feature_id = "alarm"

def __init__(self, hap: HomematicipHAP) -> None:
"""Initialize the alarm control panel."""
Expand Down Expand Up @@ -127,4 +128,4 @@ def available(self) -> bool:
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return f"{self.__class__.__name__}_{self._home.id}"
return f"{self._home.id}_{self._feature_id}"
64 changes: 50 additions & 14 deletions homeassistant/components/homematicip_cloud/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class HomematicipCloudConnectionSensor(HomematicipGenericEntity, BinarySensorEnt

def __init__(self, hap: HomematicipHAP) -> None:
"""Initialize the cloud connection sensor."""
super().__init__(hap, hap.home)
super().__init__(hap, hap.home, feature_id="cloud_connection")

@property
def name(self) -> str:
Expand Down Expand Up @@ -245,10 +245,18 @@ def extra_state_attributes(self) -> dict[str, Any]:
class HomematicipAccelerationSensor(HomematicipBaseActionSensor):
"""Representation of the HomematicIP acceleration sensor."""

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the acceleration sensor."""
super().__init__(hap, device, feature_id="acceleration")


class HomematicipTiltVibrationSensor(HomematicipBaseActionSensor):
"""Representation of the HomematicIP tilt vibration sensor."""

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the tilt vibration sensor."""
super().__init__(hap, device, feature_id="tilt_vibration")


class HomematicipMultiContactInterface(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP multi room/area contact interface."""
Expand All @@ -262,6 +270,7 @@ def __init__(
channel=1,
is_multi_channel=True,
channel_real_index=None,
feature_id: str = "contact",
) -> None:
"""Initialize the multi contact entity."""
super().__init__(
Expand All @@ -270,6 +279,7 @@ def __init__(
channel=channel,
is_multi_channel=is_multi_channel,
channel_real_index=channel_real_index,
feature_id=feature_id,
)

@property
Expand All @@ -286,7 +296,7 @@ class HomematicipContactInterface(HomematicipMultiContactInterface, BinarySensor

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the multi contact entity."""
super().__init__(hap, device, is_multi_channel=False)
super().__init__(hap, device, is_multi_channel=False, feature_id="contact")


class HomematicipShutterContact(HomematicipMultiContactInterface, BinarySensorEntity):
Expand All @@ -298,7 +308,9 @@ def __init__(
self, hap: HomematicipHAP, device, has_additional_state: bool = False
) -> None:
"""Initialize the shutter contact."""
super().__init__(hap, device, is_multi_channel=False)
super().__init__(
hap, device, is_multi_channel=False, feature_id="shutter_contact"
)
self.has_additional_state = has_additional_state

@property
Expand All @@ -319,6 +331,10 @@ class HomematicipMotionDetector(HomematicipGenericEntity, BinarySensorEntity):

_attr_device_class = BinarySensorDeviceClass.MOTION

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the motion detector."""
super().__init__(hap, device, feature_id="motion")

@property
def is_on(self) -> bool:
"""Return true if motion is detected."""
Expand All @@ -334,7 +350,7 @@ class HomematicipFullFlushLockControllerLocked(

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the full flush lock controller lock sensor."""
super().__init__(hap, device, post="Locked")
super().__init__(hap, device, post="Locked", feature_id="lock_locked")

@property
def is_on(self) -> bool:
Expand All @@ -359,7 +375,7 @@ class HomematicipFullFlushLockControllerGlassBreak(

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the full flush lock controller glass break sensor."""
super().__init__(hap, device, post="Glass break")
super().__init__(hap, device, post="Glass break", feature_id="glass_break")

@property
def is_on(self) -> bool:
Expand All @@ -379,6 +395,10 @@ class HomematicipPresenceDetector(HomematicipGenericEntity, BinarySensorEntity):

_attr_device_class = BinarySensorDeviceClass.PRESENCE

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the presence detector."""
super().__init__(hap, device, feature_id="presence")

@property
def is_on(self) -> bool:
"""Return true if presence is detected."""
Expand All @@ -390,6 +410,10 @@ class HomematicipSmokeDetector(HomematicipGenericEntity, BinarySensorEntity):

_attr_device_class = BinarySensorDeviceClass.SMOKE

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the smoke detector."""
super().__init__(hap, device, feature_id="smoke")

@property
def is_on(self) -> bool:
"""Return true if smoke is detected."""
Expand All @@ -410,7 +434,9 @@ class HomematicipSmokeDetectorChamberDegraded(

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize smoke detector chamber health sensor."""
super().__init__(hap, device, post="Chamber Degraded")
super().__init__(
hap, device, post="Chamber Degraded", feature_id="chamber_degraded"
)

@property
def is_on(self) -> bool:
Expand All @@ -423,6 +449,10 @@ class HomematicipWaterDetector(HomematicipGenericEntity, BinarySensorEntity):

_attr_device_class = BinarySensorDeviceClass.MOISTURE

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the water detector."""
super().__init__(hap, device, feature_id="water")

@property
def is_on(self) -> bool:
"""Return true, if moisture or waterlevel is detected."""
Expand All @@ -434,7 +464,7 @@ class HomematicipStormSensor(HomematicipGenericEntity, BinarySensorEntity):

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize storm sensor."""
super().__init__(hap, device, "Storm")
super().__init__(hap, device, "Storm", feature_id="storm")

@property
def icon(self) -> str:
Expand All @@ -454,7 +484,7 @@ class HomematicipRainSensor(HomematicipGenericEntity, BinarySensorEntity):

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize rain sensor."""
super().__init__(hap, device, "Raining")
super().__init__(hap, device, "Raining", feature_id="rain")

@property
def is_on(self) -> bool:
Expand All @@ -469,7 +499,7 @@ class HomematicipSunshineSensor(HomematicipGenericEntity, BinarySensorEntity):

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize sunshine sensor."""
super().__init__(hap, device, post="Sunshine")
super().__init__(hap, device, post="Sunshine", feature_id="sunshine")

@property
def is_on(self) -> bool:
Expand All @@ -495,7 +525,7 @@ class HomematicipBatterySensor(HomematicipGenericEntity, BinarySensorEntity):

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize battery sensor."""
super().__init__(hap, device, post="Battery")
super().__init__(hap, device, post="Battery", channel=0, feature_id="battery")

@property
def is_on(self) -> bool:
Expand All @@ -512,7 +542,7 @@ class HomematicipPluggableMainsFailureSurveillanceSensor(

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize pluggable mains failure surveillance sensor."""
super().__init__(hap, device)
super().__init__(hap, device, feature_id="mains_failure")

@property
def is_on(self) -> bool:
Expand All @@ -525,10 +555,16 @@ class HomematicipSecurityZoneSensorGroup(HomematicipGenericEntity, BinarySensorE

_attr_device_class = BinarySensorDeviceClass.SAFETY

def __init__(self, hap: HomematicipHAP, device, post: str = "SecurityZone") -> None:
def __init__(
self,
hap: HomematicipHAP,
device,
post: str = "SecurityZone",
feature_id: str = "security_zone",
) -> None:
"""Initialize security zone group."""
device.modelType = f"HmIP-{post}"
super().__init__(hap, device, post=post)
super().__init__(hap, device, post=post, feature_id=feature_id)

@property
def available(self) -> bool:
Expand Down Expand Up @@ -578,7 +614,7 @@ class HomematicipSecuritySensorGroup(

def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize security group."""
super().__init__(hap, device, post="Sensors")
super().__init__(hap, device, post="Sensors", feature_id="security")

@property
def extra_state_attributes(self) -> dict[str, Any]:
Expand Down
Loading
Loading