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
48 changes: 48 additions & 0 deletions homeassistant/components/alexa_devices/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
import homeassistant.helpers.entity_registry as er

from .const import _LOGGER, DOMAIN
from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
from .utils import async_update_unique_id
Expand Down Expand Up @@ -58,6 +60,40 @@ class AmazonBinarySensorEntityDescription(BinarySensorEntityDescription):
),
)

DEPRECATED_BINARY_SENSORS: Final = (
AmazonBinarySensorEntityDescription(
key="bluetooth",
entity_category=EntityCategory.DIAGNOSTIC,
translation_key="bluetooth",
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="babyCryDetectionState",
translation_key="baby_cry_detection",
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="beepingApplianceDetectionState",
translation_key="beeping_appliance_detection",
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="coughDetectionState",
translation_key="cough_detection",
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="dogBarkDetectionState",
translation_key="dog_bark_detection",
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="waterSoundsDetectionState",
translation_key="water_sounds_detection",
is_on_fn=lambda device, key: False,
),
)


async def async_setup_entry(
hass: HomeAssistant,
Expand All @@ -68,6 +104,8 @@ async def async_setup_entry(

coordinator = entry.runtime_data

entity_registry = er.async_get(hass)

# Replace unique id for "detectionState" binary sensor
await async_update_unique_id(
hass,
Expand All @@ -77,6 +115,16 @@ async def async_setup_entry(
"detectionState",
)

# Clean up deprecated sensors
for sensor_desc in DEPRECATED_BINARY_SENSORS:
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{sensor_desc.key}"
if entity_id := entity_registry.async_get_entity_id(
BINARY_SENSOR_DOMAIN, DOMAIN, unique_id
):
_LOGGER.debug("Removing deprecated entity %s", entity_id)
entity_registry.async_remove(entity_id)

known_devices: set[str] = set()

def _check_device() -> None:
Expand Down
9 changes: 8 additions & 1 deletion homeassistant/components/alexa_devices/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
from .utils import alexa_api_call, async_update_unique_id
from .utils import (
alexa_api_call,
async_remove_dnd_from_virtual_group,
async_update_unique_id,
)

PARALLEL_UPDATES = 1

Expand Down Expand Up @@ -60,6 +64,9 @@ async def async_setup_entry(
hass, coordinator, SWITCH_DOMAIN, "do_not_disturb", "dnd"
)

# Remove DND switch from virtual groups
await async_remove_dnd_from_virtual_group(hass, coordinator)

known_devices: set[str] = set()

def _check_device() -> None:
Expand Down
20 changes: 20 additions & 0 deletions homeassistant/components/alexa_devices/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from functools import wraps
from typing import Any, Concatenate

from aioamazondevices.const import SPEAKER_GROUP_FAMILY
from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData

from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.entity_registry as er
Expand Down Expand Up @@ -61,3 +63,21 @@ async def async_update_unique_id(

# Update the registry with the new unique_id
entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id)


async def async_remove_dnd_from_virtual_group(
hass: HomeAssistant,
coordinator: AmazonDevicesCoordinator,
) -> None:
"""Remove entity DND from virtual group."""
entity_registry = er.async_get(hass)

for serial_num in coordinator.data:
unique_id = f"{serial_num}-do_not_disturb"
entity_id = entity_registry.async_get_entity_id(
DOMAIN, SWITCH_DOMAIN, unique_id
)
is_group = coordinator.data[serial_num].device_family == SPEAKER_GROUP_FAMILY
if entity_id and is_group:
entity_registry.async_remove(entity_id)
_LOGGER.debug("Removed DND switch from virtual group %s", entity_id)
6 changes: 1 addition & 5 deletions homeassistant/components/google_assistant_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def async_setup_entry(
except aiohttp.ClientResponseError as err:
if 400 <= err.status < 500:
raise ConfigEntryAuthFailed(
"OAuth session is not valid, reauth required"
translation_domain=DOMAIN, translation_key="reauth_required"
) from err
raise ConfigEntryNotReady from err
except aiohttp.ClientError as err:
Expand All @@ -76,10 +76,6 @@ async def async_unload_entry(
hass: HomeAssistant, entry: GoogleAssistantSDKConfigEntry
) -> bool:
"""Unload a config entry."""
if not hass.config_entries.async_loaded_entries(DOMAIN):
for service_name in hass.services.async_services_for_domain(DOMAIN):
hass.services.async_remove(DOMAIN, service_name)

conversation.async_unset_agent(hass, entry)

return True
Expand Down
10 changes: 8 additions & 2 deletions homeassistant/components/google_assistant_sdk/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID, CONF_ACCESS_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
from homeassistant.helpers.event import async_call_later

Expand Down Expand Up @@ -68,7 +68,13 @@ async def async_send_text_commands(
) -> list[CommandResponse]:
"""Send text commands to Google Assistant Service."""
# There can only be 1 entry (config_flow has single_instance_allowed)
entry: GoogleAssistantSDKConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
entries = hass.config_entries.async_loaded_entries(DOMAIN)
if not entries:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_loaded",
)
entry: GoogleAssistantSDKConfigEntry = entries[0]

session = entry.runtime_data.session
try:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/google_assistant_sdk/services.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Support for Google Assistant SDK."""
"""Services for the Google Assistant SDK integration."""

from __future__ import annotations

Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/google_assistant_sdk/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,14 @@
}
},
"exceptions": {
"entry_not_loaded": {
"message": "Entry not loaded"
},
"grpc_error": {
"message": "Failed to communicate with Google Assistant"
},
"reauth_required": {
"message": "Credentials are invalid, re-authentication required"
}
}
}
2 changes: 1 addition & 1 deletion homeassistant/components/letpot/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["letpot"],
"quality_scale": "bronze",
"quality_scale": "silver",
"requirements": ["letpot==0.6.2"]
}
5 changes: 4 additions & 1 deletion homeassistant/components/letpot/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ rules:
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: todo
log-when-unavailable:
status: done
comment: |
Logging handled by library when (un)available once (push) or coordinator (pull).
parallel-updates: done
reauthentication-flow: done
test-coverage: done
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/matter/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@
"off": "mdi:lock-off"
}
},
"speaker_mute": {
"default": "mdi:volume-high",
"state": {
"on": "mdi:volume-mute",
"off": "mdi:volume-high"
}
},
"evse_charging_switch": {
"default": "mdi:ev-station"
},
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/matter/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def _update_from_device(self) -> None:
),
entity_class=MatterNumber,
required_attributes=(clusters.LevelControl.Attributes.OnLevel,),
not_device_type=(device_types.Speaker,),
# allow None value to account for 'default' value
allow_none_value=True,
),
Expand Down
7 changes: 4 additions & 3 deletions homeassistant/components/matter/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
clusters.PumpConfigurationAndControl.Enums.ControlModeEnum.kUnknownEnumValue: None,
}

HUMIDITY_SCALING_FACTOR = 100
TEMPERATURE_SCALING_FACTOR = 100


Expand Down Expand Up @@ -308,7 +309,7 @@ def _update_from_device(self) -> None:
key="TemperatureSensor",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
device_to_ha=lambda x: x / 100,
device_to_ha=lambda x: x / TEMPERATURE_SCALING_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
Expand Down Expand Up @@ -344,7 +345,7 @@ def _update_from_device(self) -> None:
key="HumiditySensor",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
device_to_ha=lambda x: x / 100,
device_to_ha=lambda x: x / HUMIDITY_SCALING_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
Expand Down Expand Up @@ -1136,7 +1137,7 @@ def _update_from_device(self) -> None:
key="ThermostatLocalTemperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
device_to_ha=lambda x: x / 100,
device_to_ha=lambda x: x / TEMPERATURE_SCALING_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/matter/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,9 @@
"power": {
"name": "Power"
},
"speaker_mute": {
"name": "Mute"
},
"child_lock": {
"name": "Child lock"
},
Expand Down
19 changes: 18 additions & 1 deletion homeassistant/components/matter/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ def _update_from_device(self) -> None:
device_types.Refrigerator,
device_types.RoboticVacuumCleaner,
device_types.RoomAirConditioner,
device_types.Speaker,
),
),
MatterDiscoverySchema(
Expand Down Expand Up @@ -242,6 +241,24 @@ def _update_from_device(self) -> None:
device_types.Speaker,
),
),
MatterDiscoverySchema(
platform=Platform.SWITCH,
entity_description=MatterNumericSwitchEntityDescription(
key="MatterMuteToggle",
translation_key="speaker_mute",
device_to_ha={
True: False, # True means volume is on, so HA should show mute as off
False: True, # False means volume is off (muted), so HA should show mute as on
}.get,
ha_to_device={
False: True, # HA showing mute as off means volume is on, so send True
True: False, # HA showing mute as on means volume is off (muted), so send False
}.get,
),
entity_class=MatterNumericSwitch,
required_attributes=(clusters.OnOff.Attributes.OnOff,),
device_type=(device_types.Speaker,),
),
MatterDiscoverySchema(
platform=Platform.SWITCH,
entity_description=MatterNumericSwitchEntityDescription(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/raspyrfm/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "assumed_state",
"loggers": ["raspyrfm_client"],
"quality_scale": "legacy",
"requirements": ["raspyrfm-client==1.2.8"]
"requirements": ["raspyrfm-client==1.2.9"]
}
9 changes: 7 additions & 2 deletions homeassistant/components/recorder/migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2490,7 +2490,7 @@ def __init__(
start_schema_version: int,
migration_changes: dict[str, int],
) -> None:
"""Initialize a new BaseRunTimeMigration.
"""Initialize a new BaseMigration.

:param initial_schema_version: The schema version the database was created with.
:param start_schema_version: The schema version when starting the migration.
Expand Down Expand Up @@ -2964,7 +2964,12 @@ def migrate_data_impl(self, instance: Recorder) -> DataMigrationStatus:
_drop_foreign_key_constraints(
session_maker, instance.engine, TABLE_STATES, "event_id"
)
except (InternalError, OperationalError):
except (InternalError, OperationalError) as err:
_LOGGER.debug(
"Could not drop foreign key constraint on states.event_id, "
"will try again later",
exc_info=err,
)
fk_remove_ok = False
else:
fk_remove_ok = True
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/shelly/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
MODEL_VINTAGE_V2,
MODEL_WALL_DISPLAY,
MODEL_WALL_DISPLAY_X2,
MODEL_WALL_DISPLAY_XL,
)

from homeassistant.components.number import NumberMode
Expand Down Expand Up @@ -261,6 +262,7 @@ class BLEScannerMode(StrEnum):
DEVICES_WITHOUT_FIRMWARE_CHANGELOG = (
MODEL_WALL_DISPLAY,
MODEL_WALL_DISPLAY_X2,
MODEL_WALL_DISPLAY_XL,
MODEL_MOTION,
MODEL_MOTION_2,
MODEL_VALVE,
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tuya/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,10 @@ def is_closed(self) -> bool | None:
self._current_state is not None
and (current_state := self.device.status.get(self._current_state))
is not None
and current_state != "stop"
):
return self.entity_description.current_state_inverse is not (
current_state in (True, "fully_close")
current_state in (True, "close", "fully_close")
)

return None
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt

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

2 changes: 0 additions & 2 deletions script/hassfest/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,6 @@
"obihai": {"homeassistant": {"pyobihai"}},
# https://github.com/iamkubi/pydactyl
"pterodactyl": {"homeassistant": {"py-dactyl"}},
# https://github.com/markusressel/raspyrfm-client
"raspyrfm": {"homeassistant": {"raspyrfm-client"}},
# https://github.com/sstallion/sensorpush-api
"sensorpush_cloud": {
"homeassistant": {"sensorpush-api"},
Expand Down
Loading
Loading