Compare commits

..

3 Commits

Author SHA1 Message Date
J. Nick Koston
a4efd31b7d Bump aioesphomeapi to 33.0.0
fixes compat warning with protobuf 6.x

changelog: https://github.com/esphome/aioesphomeapi/compare/v32.2.4...v33.0.0

Not a breaking change for HA since we are already on protobuf 6
2025-06-22 11:49:15 +02:00
G Johansson
db3090078b Remove deprecated support feature values in camera (#146988) 2025-06-22 09:31:16 +02:00
Ludovic BOUÉ
66e2fd997b Battery voltage translation key (#147238)
* Add translation_key

* Update strings.json

* Update snapshots

* Switch icon to DC

* Update snapshots
2025-06-22 09:27:44 +02:00
13 changed files with 148 additions and 234 deletions

View File

@@ -94,7 +94,6 @@ automation application.
- Provide descriptive state attributes
- Testing:
- Test location: `tests/components/{domain}/`
- Run tests with `pytest --no-header --no-summary -x <location>`
- Use pytest fixtures from `tests.common`
- Mock external dependencies
- Use snapshots for complex data

View File

@@ -498,19 +498,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Flag supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> CameraEntityFeature:
"""Return the supported features as CameraEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int:
new_features = CameraEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@cached_property
def is_recording(self) -> bool:
"""Return true if the device is recording."""
@@ -704,9 +691,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
async def async_internal_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_internal_added_to_hass()
self.__supports_stream = (
self.supported_features_compat & CameraEntityFeature.STREAM
)
self.__supports_stream = self.supported_features & CameraEntityFeature.STREAM
await self.async_refresh_providers(write_state=False)
async def async_refresh_providers(self, *, write_state: bool = True) -> None:
@@ -735,7 +720,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
self, fn: Callable[[HomeAssistant, Camera], Coroutine[None, None, _T | None]]
) -> _T | None:
"""Get first provider that supports this camera."""
if CameraEntityFeature.STREAM not in self.supported_features_compat:
if CameraEntityFeature.STREAM not in self.supported_features:
return None
return await fn(self.hass, self)
@@ -785,7 +770,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def camera_capabilities(self) -> CameraCapabilities:
"""Return the camera capabilities."""
frontend_stream_types = set()
if CameraEntityFeature.STREAM in self.supported_features_compat:
if CameraEntityFeature.STREAM in self.supported_features:
if self._supports_native_async_webrtc:
# The camera has a native WebRTC implementation
frontend_stream_types.add(StreamType.WEB_RTC)
@@ -805,8 +790,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""
super().async_write_ha_state()
if self.__supports_stream != (
supports_stream := self.supported_features_compat
& CameraEntityFeature.STREAM
supports_stream := self.supported_features & CameraEntityFeature.STREAM
):
self.__supports_stream = supports_stream
self._invalidate_camera_capabilities_cache()

View File

@@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==32.2.4",
"aioesphomeapi==33.0.0",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==2.16.0"
],

View File

@@ -57,6 +57,9 @@
"bat_replacement_description": {
"default": "mdi:battery-sync"
},
"battery_voltage": {
"default": "mdi:current-dc"
},
"flow": {
"default": "mdi:pipe"
},

View File

@@ -345,6 +345,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="PowerSourceBatVoltage",
translation_key="battery_voltage",
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,

View File

@@ -324,6 +324,9 @@
"battery_replacement_description": {
"name": "Battery type"
},
"battery_voltage": {
"name": "Battery voltage"
},
"current_phase": {
"name": "Current phase"
},

View File

@@ -7,7 +7,7 @@ import asyncio
from collections import deque
from collections.abc import Callable, Coroutine, Iterable, Mapping
import dataclasses
from enum import Enum, IntFlag, auto
from enum import Enum, auto
import functools as ft
import logging
import math
@@ -1622,31 +1622,6 @@ class Entity(
self.hass, integration_domain=platform_name, module=type(self).__module__
)
@callback
def _report_deprecated_supported_features_values(
self, replacement: IntFlag
) -> None:
"""Report deprecated supported features values."""
if self._deprecated_supported_features_reported is True:
return
self._deprecated_supported_features_reported = True
report_issue = self._suggest_report_issue()
report_issue += (
" and reference "
"https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation"
)
_LOGGER.warning(
(
"Entity %s (%s) is using deprecated supported features"
" values which will be removed in HA Core 2025.1. Instead it should use"
" %s, please %s"
),
self.entity_id,
type(self),
repr(replacement),
report_issue,
)
class ToggleEntityDescription(EntityDescription, frozen_or_thawed=True):
"""A class that describes toggle entities."""

2
requirements_all.txt generated
View File

@@ -244,7 +244,7 @@ aioelectricitymaps==0.4.0
aioemonitor==1.0.5
# homeassistant.components.esphome
aioesphomeapi==32.2.4
aioesphomeapi==33.0.0
# homeassistant.components.flo
aioflo==2021.11.0

View File

@@ -232,7 +232,7 @@ aioelectricitymaps==0.4.0
aioemonitor==1.0.5
# homeassistant.components.esphome
aioesphomeapi==32.2.4
aioesphomeapi==33.0.0
# homeassistant.components.flo
aioflo==2021.11.0

View File

@@ -41,7 +41,6 @@ from homeassistant.util import dt as dt_util
from .common import EMPTY_8_6_JPEG, STREAM_SOURCE, mock_turbo_jpeg
from tests.common import (
MockEntityPlatform,
async_fire_time_changed,
help_test_all,
import_and_test_deprecated_constant_enum,
@@ -834,30 +833,6 @@ def test_deprecated_state_constants(
import_and_test_deprecated_constant_enum(caplog, module, enum, "STATE_", "2025.10")
def test_deprecated_supported_features_ints(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test deprecated supported features ints."""
class MockCamera(camera.Camera):
@property
def supported_features(self) -> int:
"""Return supported features."""
return 1
entity = MockCamera()
entity.hass = hass
entity.platform = MockEntityPlatform(hass)
assert entity.supported_features_compat is camera.CameraEntityFeature(1)
assert "MockCamera" in caplog.text
assert "is using deprecated supported features values" in caplog.text
assert "Instead it should use" in caplog.text
assert "CameraEntityFeature.ON_OFF" in caplog.text
caplog.clear()
assert entity.supported_features_compat is camera.CameraEntityFeature(1)
assert "is using deprecated supported features values" not in caplog.text
@pytest.mark.usefixtures("mock_camera")
async def test_entity_picture_url_changes_on_token_update(hass: HomeAssistant) -> None:
"""Test the token is rotated and entity entity picture cache is cleared."""

View File

@@ -1360,7 +1360,7 @@
'state': '100',
})
# ---
# name: test_sensors[eve_contact_sensor][sensor.eve_door_voltage-entry]
# name: test_sensors[eve_contact_sensor][sensor.eve_door_battery_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@@ -1375,7 +1375,7 @@
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.eve_door_voltage',
'entity_id': 'sensor.eve_door_battery_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
@@ -1393,26 +1393,26 @@
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Voltage',
'original_name': 'Battery voltage',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'translation_key': 'battery_voltage',
'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-PowerSourceBatVoltage-47-11',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[eve_contact_sensor][sensor.eve_door_voltage-state]
# name: test_sensors[eve_contact_sensor][sensor.eve_door_battery_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Eve Door Voltage',
'friendly_name': 'Eve Door Battery voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.eve_door_voltage',
'entity_id': 'sensor.eve_door_battery_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
@@ -1932,6 +1932,65 @@
'state': '100',
})
# ---
# name: test_sensors[eve_thermo][sensor.eve_thermo_battery_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.eve_thermo_battery_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Battery voltage',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'battery_voltage',
'unique_id': '00000000000004D2-0000000000000021-MatterNodeDevice-0-PowerSourceBatVoltage-47-11',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[eve_thermo][sensor.eve_thermo_battery_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Eve Thermo Battery voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.eve_thermo_battery_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '3.05',
})
# ---
# name: test_sensors[eve_thermo][sensor.eve_thermo_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -2037,65 +2096,6 @@
'state': '10',
})
# ---
# name: test_sensors[eve_thermo][sensor.eve_thermo_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.eve_thermo_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Voltage',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000021-MatterNodeDevice-0-PowerSourceBatVoltage-47-11',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[eve_thermo][sensor.eve_thermo_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Eve Thermo Voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.eve_thermo_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '3.05',
})
# ---
# name: test_sensors[eve_weather_sensor][sensor.eve_weather_battery-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -2149,6 +2149,65 @@
'state': '100',
})
# ---
# name: test_sensors[eve_weather_sensor][sensor.eve_weather_battery_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.eve_weather_battery_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Battery voltage',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'battery_voltage',
'unique_id': '00000000000004D2-000000000000001D-MatterNodeDevice-0-PowerSourceBatVoltage-47-11',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[eve_weather_sensor][sensor.eve_weather_battery_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Eve Weather Battery voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.eve_weather_battery_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2.956',
})
# ---
# name: test_sensors[eve_weather_sensor][sensor.eve_weather_humidity-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -2314,65 +2373,6 @@
'state': '16.03',
})
# ---
# name: test_sensors[eve_weather_sensor][sensor.eve_weather_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.eve_weather_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Voltage',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '00000000000004D2-000000000000001D-MatterNodeDevice-0-PowerSourceBatVoltage-47-11',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[eve_weather_sensor][sensor.eve_weather_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Eve Weather Voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.eve_weather_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2.956',
})
# ---
# name: test_sensors[extractor_hood][sensor.mock_extractor_hood_activated_carbon_filter_condition-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -5304,7 +5304,7 @@
'state': 'CR123A',
})
# ---
# name: test_sensors[smoke_detector][sensor.smoke_sensor_voltage-entry]
# name: test_sensors[smoke_detector][sensor.smoke_sensor_battery_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@@ -5319,7 +5319,7 @@
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.smoke_sensor_voltage',
'entity_id': 'sensor.smoke_sensor_battery_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
@@ -5337,26 +5337,26 @@
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Voltage',
'original_name': 'Battery voltage',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'translation_key': 'battery_voltage',
'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-PowerSourceBatVoltage-47-11',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[smoke_detector][sensor.smoke_sensor_voltage-state]
# name: test_sensors[smoke_detector][sensor.smoke_sensor_battery_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Smoke sensor Voltage',
'friendly_name': 'Smoke sensor Battery voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.smoke_sensor_voltage',
'entity_id': 'sensor.smoke_sensor_battery_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,

View File

@@ -156,7 +156,7 @@ async def test_battery_sensor_voltage(
matter_node: MatterNode,
) -> None:
"""Test battery voltage sensor."""
entity_id = "sensor.eve_door_voltage"
entity_id = "sensor.eve_door_battery_voltage"
state = hass.states.get(entity_id)
assert state
assert state.state == "3.558"

View File

@@ -4,7 +4,6 @@ import asyncio
from collections.abc import Iterable
import dataclasses
from datetime import timedelta
from enum import IntFlag
import logging
import threading
from typing import Any
@@ -2488,31 +2487,6 @@ async def test_cached_entity_property_override(hass: HomeAssistant) -> None:
return "🤡"
async def test_entity_report_deprecated_supported_features_values(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test reporting deprecated supported feature values only happens once."""
ent = entity.Entity()
class MockEntityFeatures(IntFlag):
VALUE1 = 1
VALUE2 = 2
ent._report_deprecated_supported_features_values(MockEntityFeatures(2))
assert (
"is using deprecated supported features values which will be removed"
in caplog.text
)
assert "MockEntityFeatures.VALUE2" in caplog.text
caplog.clear()
ent._report_deprecated_supported_features_values(MockEntityFeatures(2))
assert (
"is using deprecated supported features values which will be removed"
not in caplog.text
)
async def test_remove_entity_registry(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None: