From 91c8b1fa3606da33cd05200936225de1049e4976 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 31 Jul 2026 08:47:20 -0700 Subject: [PATCH 1/6] Update Google Health sensor units to be more reasonable (#177657) --- .../components/google_health/sensor.py | 22 +++++++ .../google_health/snapshots/test_sensor.ambr | 9 ++- tests/components/google_health/test_sensor.py | 61 +++++++++++++++++-- 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/google_health/sensor.py b/homeassistant/components/google_health/sensor.py index 109916395467e..53a1a3722dc33 100644 --- a/homeassistant/components/google_health/sensor.py +++ b/homeassistant/components/google_health/sensor.py @@ -27,6 +27,7 @@ from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util +from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM, UnitSystem from . import GoogleHealthConfigEntry from .const import DOMAIN @@ -50,6 +51,7 @@ class GoogleHealthSensorEntityDescription[ """Class describing Google Health sensor entities.""" value_fn: Callable[[Any], _ValueT] + suggested_unit_fn: Callable[[UnitSystem], str | None] | None = None ACTIVITY_SENSORS: list[ @@ -69,6 +71,11 @@ class GoogleHealthSensorEntityDescription[ value_fn=lambda data: ( data.distance.millimeters_sum / 1000.0 if data and data.distance else 0.0 ), + suggested_unit_fn=lambda units: ( + UnitOfLength.MILES + if units is US_CUSTOMARY_SYSTEM + else UnitOfLength.KILOMETERS + ), ), GoogleHealthSensorEntityDescription[GoogleHealthActivityCoordinator, float]( key="active_calories", @@ -109,6 +116,9 @@ class GoogleHealthSensorEntityDescription[ value_fn=lambda data: ( data.weight.weight_grams / 1000.0 if data and data.weight else None ), + suggested_unit_fn=lambda units: ( + UnitOfMass.POUNDS if units is US_CUSTOMARY_SYSTEM else None + ), ), GoogleHealthSensorEntityDescription[GoogleHealthBodyCoordinator, int | None]( key="resting_heart_rate", @@ -212,6 +222,9 @@ class GoogleHealthSensorEntityDescription[ if data and data.hydration and data.hydration.amount_consumed else 0.0 ), + suggested_unit_fn=lambda units: ( + UnitOfVolume.FLUID_OUNCES if units is US_CUSTOMARY_SYSTEM else None + ), ), GoogleHealthSensorEntityDescription[GoogleHealthNutritionCoordinator, float]( key="calories_consumed", @@ -345,6 +358,15 @@ def native_value(self) -> StateType: """Return the state of the sensor.""" return cast(StateType, self.entity_description.value_fn(self.coordinator.data)) + @property + @override + def suggested_unit_of_measurement(self) -> str | None: + """Return the suggested unit of measurement.""" + if (suggested_unit_fn := self.entity_description.suggested_unit_fn) is not None: + return suggested_unit_fn(self.hass.config.units) + + return super().suggested_unit_of_measurement + class GoogleHealthDeviceSensor( CoordinatorEntity[GoogleHealthDeviceCoordinator], SensorEntity diff --git a/tests/components/google_health/snapshots/test_sensor.ambr b/tests/components/google_health/snapshots/test_sensor.ambr index fc8e4cb9e3b14..a1a0471943a28 100644 --- a/tests/components/google_health/snapshots/test_sensor.ambr +++ b/tests/components/google_health/snapshots/test_sensor.ambr @@ -402,6 +402,9 @@ 'sensor': dict({ 'suggested_display_precision': 2, }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), }), 'original_device_class': , 'original_icon': None, @@ -412,7 +415,7 @@ 'supported_features': 0, 'translation_key': None, 'unique_id': '01J0BC4QM2YBRP6H5G933CETT7_distance', - 'unit_of_measurement': , + 'unit_of_measurement': , }) # --- # name: test_all_entities[sensor.google_health_distance-state] @@ -421,14 +424,14 @@ : 'distance', : 'Google Health Distance', : , - : , + : , }), 'context': , 'entity_id': 'sensor.google_health_distance', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '5000.0', + 'state': '5.0', }) # --- # name: test_all_entities[sensor.google_health_floors-entry] diff --git a/tests/components/google_health/test_sensor.py b/tests/components/google_health/test_sensor.py index 9e75cbfe83094..4530561d04a95 100644 --- a/tests/components/google_health/test_sensor.py +++ b/tests/components/google_health/test_sensor.py @@ -1,15 +1,20 @@ """Tests for Google Health sensor platform.""" from collections.abc import Awaitable, Callable -from unittest.mock import AsyncMock, patch +from typing import Any +from unittest.mock import AsyncMock, MagicMock, patch -from google_health_api.model import ListDataPointResult, _ListDataPointsModel import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.util.unit_system import ( + METRIC_SYSTEM, + US_CUSTOMARY_SYSTEM, + UnitSystem, +) from tests.common import MockConfigEntry, snapshot_platform @@ -80,12 +85,58 @@ async def test_sensor_empty_sleep( integration_setup: Callable[[], Awaitable[bool]], ) -> None: """Test sleep sensors when the sleep endpoint returns no data.""" - mock_google_health_client.sleep.list.return_value = ListDataPointResult( - _ListDataPointsModel(data_points=[]) - ) + mock_google_health_client.sleep.list.return_value = MagicMock(data_points=[]) assert await integration_setup() time_asleep_state = hass.states.get("sensor.google_health_time_asleep") assert time_asleep_state is not None assert time_asleep_state.state == "unknown" + + +@pytest.mark.parametrize( + ("unit_system", "expected_sensors"), + [ + pytest.param( + METRIC_SYSTEM, + { + "sensor.google_health_weight": (pytest.approx(80.0), "kg"), + "sensor.google_health_distance": (pytest.approx(5.0), "km"), + "sensor.google_health_water_intake": (pytest.approx(2.5), "L"), + }, + id="metric", + ), + pytest.param( + US_CUSTOMARY_SYSTEM, + { + "sensor.google_health_weight": (pytest.approx(176.37, abs=1e-2), "lb"), + "sensor.google_health_distance": ( + pytest.approx(3.11, abs=1e-2), + "mi", + ), + "sensor.google_health_water_intake": ( + pytest.approx(84.54, abs=1e-1), + "fl. oz.", + ), + }, + id="us_customary", + ), + ], +) +@pytest.mark.usefixtures("mock_google_health_client") +async def test_sensor_unit_conversions( + hass: HomeAssistant, + integration_setup: Callable[[], Awaitable[bool]], + unit_system: UnitSystem, + expected_sensors: dict[str, tuple[Any, str]], +) -> None: + """Test sensors dynamically convert states and units under different unit systems.""" + hass.config.units = unit_system + + assert await integration_setup() + + for entity_id, (expected_state, expected_unit) in expected_sensors.items(): + state = hass.states.get(entity_id) + assert state is not None + assert float(state.state) == expected_state + assert state.attributes.get("unit_of_measurement") == expected_unit From eda1098ed3eb6bf82c06525d2ef056440d0ec52d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 31 Jul 2026 17:54:32 +0200 Subject: [PATCH 2/6] Adapt shelly to set via_device_id in DeviceInfo (#177759) --- .../components/shelly/binary_sensor.py | 7 ++++- homeassistant/components/shelly/button.py | 7 ++++- homeassistant/components/shelly/climate.py | 7 ++++- homeassistant/components/shelly/entity.py | 4 +++ homeassistant/components/shelly/number.py | 7 ++++- homeassistant/components/shelly/sensor.py | 7 ++++- homeassistant/components/shelly/utils.py | 27 +++++++++++++++---- 7 files changed, 56 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/shelly/binary_sensor.py b/homeassistant/components/shelly/binary_sensor.py index 886f559729678..2aae645c3cf41 100644 --- a/homeassistant/components/shelly/binary_sensor.py +++ b/homeassistant/components/shelly/binary_sensor.py @@ -123,7 +123,12 @@ def __init__( ble_addr: str = coordinator.device.config[key]["addr"] fw_ver = coordinator.device.status[key].get("fw_ver") self._attr_device_info = get_blu_trv_device_info( - coordinator.device.config[key], ble_addr, coordinator.mac, fw_ver + coordinator.hass, + coordinator.config_entry.entry_id, + coordinator.device.config[key], + ble_addr, + coordinator.mac, + fw_ver, ) diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index 94c3e4ce26efe..4320e3098bb28 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -283,7 +283,12 @@ def __init__( self._attr_unique_id = f"{format_ble_addr(ble_addr)}-{key}-{attribute}" self._attr_device_info = get_blu_trv_device_info( - config, ble_addr, coordinator.mac, fw_ver + coordinator.hass, + coordinator.config_entry.entry_id, + config, + ble_addr, + coordinator.mac, + fw_ver, ) @rpc_call diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index 0f488216eac75..422618df9179e 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -815,7 +815,12 @@ def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None: self._attr_unique_id = f"{ble_addr}-{self.key}" fw_ver = coordinator.device.status[self.key].get("fw_ver") self._attr_device_info = get_blu_trv_device_info( - self._config, ble_addr, self.coordinator.mac, fw_ver + coordinator.hass, + coordinator.config_entry.entry_id, + self._config, + ble_addr, + self.coordinator.mac, + fw_ver, ) @property diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 58011294253a0..e94d73f8caccf 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -729,6 +729,8 @@ def get_entity_block_device_info( ) -> DeviceInfo: """Get device info for block entities.""" return get_block_device_info( + coordinator.hass, + coordinator.config_entry.entry_id, coordinator.device, coordinator.mac, coordinator.configuration_url, @@ -746,6 +748,8 @@ def get_entity_rpc_device_info( ) -> DeviceInfo: """Get device info for RPC entities.""" return get_rpc_device_info( + coordinator.hass, + coordinator.config_entry.entry_id, coordinator.device, coordinator.mac, coordinator.configuration_url, diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index 38f72dccfec64..9344c38e11e73 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -155,7 +155,12 @@ def __init__( ble_addr: str = coordinator.device.config[key]["addr"] fw_ver = coordinator.device.status[key].get("fw_ver") self._attr_device_info = get_blu_trv_device_info( - coordinator.device.config[key], ble_addr, coordinator.mac, fw_ver + coordinator.hass, + coordinator.config_entry.entry_id, + coordinator.device.config[key], + ble_addr, + coordinator.mac, + fw_ver, ) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 53cf5b9f618f3..e456061c0277f 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -195,7 +195,12 @@ def __init__( ble_addr: str = coordinator.device.config[key]["addr"] fw_ver = coordinator.device.status[key].get("fw_ver") self._attr_device_info = get_blu_trv_device_info( - coordinator.device.config[key], ble_addr, coordinator.mac, fw_ver + coordinator.hass, + coordinator.config_entry.entry_id, + coordinator.device.config[key], + ble_addr, + coordinator.mac, + fw_ver, ) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 1f932ea7a9593..906e59a1bf519 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -785,6 +785,8 @@ def get_irrigation_zone_id(device: RpcDevice, key: str) -> int | None: def get_rpc_device_info( + hass: HomeAssistant, + config_entry_id: str, device: RpcDevice, mac: str, configuration_url: str, @@ -809,7 +811,9 @@ def get_rpc_device_info( model=model_name, model_id=model, suggested_area=suggested_area, - via_device=(DOMAIN, mac), + via_device_id=dr.async_get_device_id_by_identifier( + hass, (DOMAIN, mac), config_entry_id=config_entry_id + ), configuration_url=configuration_url, ) @@ -830,20 +834,29 @@ def get_rpc_device_info( model=model_name, model_id=model, suggested_area=suggested_area, - via_device=(DOMAIN, mac), + via_device_id=dr.async_get_device_id_by_identifier( + hass, (DOMAIN, mac), config_entry_id=config_entry_id + ), configuration_url=configuration_url, ) def get_blu_trv_device_info( - config: dict[str, Any], ble_addr: str, parent_mac: str, fw_ver: str | None + hass: HomeAssistant, + config_entry_id: str, + config: dict[str, Any], + ble_addr: str, + parent_mac: str, + fw_ver: str | None, ) -> DeviceInfo: """Return device info for RPC device.""" model_id = config.get("local_name") return DeviceInfo( connections={(CONNECTION_BLUETOOTH, ble_addr)}, identifiers={(DOMAIN, ble_addr)}, - via_device=(DOMAIN, parent_mac), + via_device_id=dr.async_get_device_id_by_identifier( + hass, (DOMAIN, parent_mac), config_entry_id=config_entry_id + ), manufacturer="Shelly", model=BLU_TRV_MODEL_NAME.get(model_id) if model_id else None, model_id=config.get("local_name"), @@ -862,6 +875,8 @@ def is_block_single_device(device: BlockDevice, block: Block | None = None) -> b def get_block_device_info( + hass: HomeAssistant, + config_entry_id: str, device: BlockDevice, mac: str, configuration_url: str, @@ -886,7 +901,9 @@ def get_block_device_info( model=model_name, model_id=model, suggested_area=suggested_area, - via_device=(DOMAIN, mac), + via_device_id=dr.async_get_device_id_by_identifier( + hass, (DOMAIN, mac), config_entry_id=config_entry_id + ), configuration_url=configuration_url, ) From 0176c934176c5017c9f9925adaa1a1df8d38fd31 Mon Sep 17 00:00:00 2001 From: Przemko92 <33545571+Przemko92@users.noreply.github.com> Date: Fri, 31 Jul 2026 19:27:46 +0200 Subject: [PATCH 3/6] Add new values for Compit Binary sensor (#174239) --- .../components/compit/binary_sensor.py | 25 ++++++++- homeassistant/components/compit/icons.json | 6 +++ homeassistant/components/compit/strings.json | 6 +++ .../compit/snapshots/test_binary_sensor.ambr | 51 +++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/compit/binary_sensor.py b/homeassistant/components/compit/binary_sensor.py index d4b5eda2e827b..bf8397643bdb0 100644 --- a/homeassistant/components/compit/binary_sensor.py +++ b/homeassistant/components/compit/binary_sensor.py @@ -21,7 +21,7 @@ PARALLEL_UPDATES = 0 NO_SENSOR = "no_sensor" -ON_STATES = ["on", "yes", "charging", "alert", "exceeded"] +ON_STATES = ["on", "yes", "charging", "alert", "exceeded", "open"] DESCRIPTIONS: dict[CompitParameter, BinarySensorEntityDescription] = { CompitParameter.AIRING: BinarySensorEntityDescription( @@ -53,6 +53,18 @@ device_class=BinarySensorDeviceClass.PROBLEM, entity_category=EntityCategory.DIAGNOSTIC, ), + CompitParameter.GWC: BinarySensorEntityDescription( + key=CompitParameter.GWC.value, + translation_key="ground_heat_exchanger_attached", + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_category=EntityCategory.DIAGNOSTIC, + ), + CompitParameter.MIXER_PUMP_STATUS: BinarySensorEntityDescription( + key=CompitParameter.MIXER_PUMP_STATUS.value, + translation_key="mixer_pump_status", + device_class=BinarySensorDeviceClass.RUNNING, + entity_category=EntityCategory.DIAGNOSTIC, + ), CompitParameter.PUMP_STATUS: BinarySensorEntityDescription( key=CompitParameter.PUMP_STATUS.value, translation_key="pump_status", @@ -77,10 +89,20 @@ class CompitDeviceDescription: DEVICE_DEFINITIONS: dict[int, CompitDeviceDescription] = { + 3: CompitDeviceDescription( + name="R810", + parameters={ + CompitParameter.MIXER_PUMP_STATUS: DESCRIPTIONS[ + CompitParameter.MIXER_PUMP_STATUS + ], + }, + ), 12: CompitDeviceDescription( name="Nano Color", parameters={ + CompitParameter.AIRING: DESCRIPTIONS[CompitParameter.AIRING], CompitParameter.CO2_LEVEL: DESCRIPTIONS[CompitParameter.CO2_LEVEL], + CompitParameter.GWC: DESCRIPTIONS[CompitParameter.GWC], }, ), 78: CompitDeviceDescription( @@ -98,6 +120,7 @@ class CompitDeviceDescription: parameters={ CompitParameter.AIRING: DESCRIPTIONS[CompitParameter.AIRING], CompitParameter.CO2_LEVEL: DESCRIPTIONS[CompitParameter.CO2_LEVEL], + CompitParameter.GWC: DESCRIPTIONS[CompitParameter.GWC], }, ), 225: CompitDeviceDescription( diff --git a/homeassistant/components/compit/icons.json b/homeassistant/components/compit/icons.json index 90075efef44dd..13f95356dbc40 100644 --- a/homeassistant/components/compit/icons.json +++ b/homeassistant/components/compit/icons.json @@ -13,6 +13,12 @@ "dust_alert": { "default": "mdi:alert" }, + "ground_heat_exchanger_attached": { + "default": "mdi:heat-pump" + }, + "mixer_pump_status": { + "default": "mdi:pump" + }, "pump_status": { "default": "mdi:pump" }, diff --git a/homeassistant/components/compit/strings.json b/homeassistant/components/compit/strings.json index c555485de5a74..596156e694b61 100644 --- a/homeassistant/components/compit/strings.json +++ b/homeassistant/components/compit/strings.json @@ -46,6 +46,12 @@ "dust_alert": { "name": "Dust alert" }, + "ground_heat_exchanger_attached": { + "name": "Ground heat exchanger attached" + }, + "mixer_pump_status": { + "name": "Mixer pump" + }, "pump_status": { "name": "Pump status" }, diff --git a/tests/components/compit/snapshots/test_binary_sensor.ambr b/tests/components/compit/snapshots/test_binary_sensor.ambr index 26d626485ac0d..9d7f74b0d14f1 100644 --- a/tests/components/compit/snapshots/test_binary_sensor.ambr +++ b/tests/components/compit/snapshots/test_binary_sensor.ambr @@ -101,3 +101,54 @@ 'state': 'off', }) # --- +# name: test_binary_sensor_entities_snapshot[binary_sensor.nano_color_2_ground_heat_exchanger_attached-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.nano_color_2_ground_heat_exchanger_attached', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Ground heat exchanger attached', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ground heat exchanger attached', + 'platform': 'compit', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ground_heat_exchanger_attached', + 'unique_id': '2_GWC', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_entities_snapshot[binary_sensor.nano_color_2_ground_heat_exchanger_attached-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Nano Color 2 Ground heat exchanger attached', + }), + 'context': , + 'entity_id': 'binary_sensor.nano_color_2_ground_heat_exchanger_attached', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- From 3e8830d7038c6d7de6acf25cf5c73968285f44fa Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Fri, 31 Jul 2026 21:47:54 +0200 Subject: [PATCH 4/6] Introduce base entity for Mikrotik (#177889) --- homeassistant/components/mikrotik/entity.py | 61 ++++++++++++++++++--- homeassistant/components/mikrotik/sensor.py | 4 +- homeassistant/components/mikrotik/update.py | 4 +- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/mikrotik/entity.py b/homeassistant/components/mikrotik/entity.py index 13573bbfb195c..1f9041a7accdc 100644 --- a/homeassistant/components/mikrotik/entity.py +++ b/homeassistant/components/mikrotik/entity.py @@ -5,39 +5,82 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.util import slugify from .const import DOMAIN from .coordinator import MikrotikDataUpdateCoordinator -class MikrotikEntity[DescriptionT: EntityDescription]( - CoordinatorEntity[MikrotikDataUpdateCoordinator] -): - """Base class for Mikrotik entities.""" +class MikrotikBaseEntity(CoordinatorEntity[MikrotikDataUpdateCoordinator]): + """Base class for all Mikrotik entities.""" _attr_has_entity_name = True - entity_description: DescriptionT def __init__( self, coordinator: MikrotikDataUpdateCoordinator, - description: DescriptionT, + description: EntityDescription, ) -> None: """Initialize the entity.""" super().__init__(coordinator) self.entity_description = description self._serial = coordinator.api.serial_number - self._attr_device_info = DeviceInfo( + + def _base_device_info(self) -> DeviceInfo: + """Return the device info fields shared by all Mikrotik devices.""" + coordinator = self.coordinator + return DeviceInfo( configuration_url=URL.build( scheme="http", host=coordinator.host, ), - identifiers={(DOMAIN, self._serial)}, - name=coordinator.hostname, manufacturer="Mikrotik", model=coordinator.model, sw_version=coordinator.firmware, serial_number=self._serial, ) + + +class MikrotikEntity(MikrotikBaseEntity): + """Base class for Mikrotik entities.""" + + def __init__( + self, + coordinator: MikrotikDataUpdateCoordinator, + description: EntityDescription, + ) -> None: + """Initialize the entity.""" + super().__init__(coordinator, description) + self._attr_device_info = DeviceInfo( + **self._base_device_info(), + identifiers={(DOMAIN, self._serial)}, + name=coordinator.hostname, + ) self._attr_unique_id = f"{self._serial}_{description.key}" + + +class MikrotikDeviceEntity(MikrotikBaseEntity): + """Base class for Mikrotik device entities.""" + + def __init__( + self, + coordinator: MikrotikDataUpdateCoordinator, + description: EntityDescription, + interface: dict, + ) -> None: + """Initialize the entity.""" + super().__init__(coordinator, description) + + name = interface.get("name") + ident = f"{slugify(interface.get('mac-address'))}_{name}" + + self._attr_device_info = DeviceInfo( + **self._base_device_info(), + identifiers={(DOMAIN, ident)}, + name=name, + via_device=(DOMAIN, coordinator.api.serial_number), + ) + self._attr_unique_id = ident + self._attr_name = name + self._interface = interface diff --git a/homeassistant/components/mikrotik/sensor.py b/homeassistant/components/mikrotik/sensor.py index 4712befbde30d..acadf7c793b01 100644 --- a/homeassistant/components/mikrotik/sensor.py +++ b/homeassistant/components/mikrotik/sensor.py @@ -162,9 +162,7 @@ async def async_setup_entry( async_add_entities(sensors_list) -class MikrotikSensorEntity( - MikrotikEntity[MikrotikSensorEntityDescription], SensorEntity -): +class MikrotikSensorEntity(MikrotikEntity, SensorEntity): """Sensor device.""" entity_description: MikrotikSensorEntityDescription diff --git a/homeassistant/components/mikrotik/update.py b/homeassistant/components/mikrotik/update.py index fc9c3d4a96021..9aa717ac3a7e4 100644 --- a/homeassistant/components/mikrotik/update.py +++ b/homeassistant/components/mikrotik/update.py @@ -81,13 +81,13 @@ async def async_setup_entry( class MikrotikUpdateEntity(MikrotikEntity, UpdateEntity): """Mixin for update entity specific attributes.""" - update_description: MikrotikUpdateEntityDescription + entity_description: MikrotikUpdateEntityDescription @property @override def supported_features(self) -> UpdateEntityFeature: """Flag supported features.""" - return cast(UpdateEntityFeature, self.entity_description.supported_features) + return self.entity_description.supported_features @property def _device_path_info(self) -> dict[str, Any]: From 8706166dd229429ecdefb5158028cc95f3702e53 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 31 Jul 2026 21:56:03 +0200 Subject: [PATCH 5/6] Report a failure when a media search finds nothing to play (#177678) --- .../components/media_player/intent.py | 3 +-- tests/components/media_player/test_intent.py | 18 +++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/media_player/intent.py b/homeassistant/components/media_player/intent.py index 8e9308a33572b..f5b2573183aef 100644 --- a/homeassistant/components/media_player/intent.py +++ b/homeassistant/components/media_player/intent.py @@ -376,8 +376,7 @@ async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse ) or not (results := entity_response.result) ): - # No results found - return intent_obj.create_response() + raise intent.IntentHandleError(f"No results found for {search_query}") # 2. Play Media (first result) first_result = results[0] diff --git a/tests/components/media_player/test_intent.py b/tests/components/media_player/test_intent.py index f3064a16b14af..7fd729bb257f8 100644 --- a/tests/components/media_player/test_intent.py +++ b/tests/components/media_player/test_intent.py @@ -804,19 +804,15 @@ async def test_search_and_play_media_player_intent(hass: HomeAssistant) -> None: # Test no search results search_results.clear() - response = await intent.async_handle( - hass, - "test", - media_player_intent.INTENT_MEDIA_SEARCH_AND_PLAY, - {"search_query": {"value": "another query"}}, - ) + with pytest.raises(intent.IntentHandleError, match="No results found"): + await intent.async_handle( + hass, + "test", + media_player_intent.INTENT_MEDIA_SEARCH_AND_PLAY, + {"search_query": {"value": "another query"}}, + ) await hass.async_block_till_done() - assert response.response_type is intent.IntentResponseType.ACTION_DONE - - # A search failure is indicated by no "media" slot in the response. - assert not response.speech - assert "media" not in response.speech_slots assert len(search_calls) == 2 # Search was called again assert len(play_calls) == 1 # Play was not called again From e0ed49434201dc644c901bb9fcfb14c0423fd9f7 Mon Sep 17 00:00:00 2001 From: Sid <27780930+autinerd@users.noreply.github.com> Date: Fri, 31 Jul 2026 22:06:45 +0200 Subject: [PATCH 6/6] Bump eheimdigital to 1.7.1 (#177892) --- homeassistant/components/eheimdigital/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/eheimdigital/manifest.json b/homeassistant/components/eheimdigital/manifest.json index 6b0cb372fcbc1..32f8d0b157044 100644 --- a/homeassistant/components/eheimdigital/manifest.json +++ b/homeassistant/components/eheimdigital/manifest.json @@ -8,7 +8,7 @@ "iot_class": "local_polling", "loggers": ["eheimdigital"], "quality_scale": "platinum", - "requirements": ["eheimdigital==1.7.0"], + "requirements": ["eheimdigital==1.7.1"], "zeroconf": [ { "name": "eheimdigital._http._tcp.local.", "type": "_http._tcp.local." } ] diff --git a/requirements_all.txt b/requirements_all.txt index 2e7424e479adb..a3e2b03983f4a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -889,7 +889,7 @@ ecoaliface==0.4.0 egauge-async==0.4.0 # homeassistant.components.eheimdigital -eheimdigital==1.7.0 +eheimdigital==1.7.1 # homeassistant.components.ekeybionyx ekey-bionyxpy==1.0.1