diff --git a/homeassistant/components/vistapool/number.py b/homeassistant/components/vistapool/number.py index 0e4fa5787625..d7230f916132 100644 --- a/homeassistant/components/vistapool/number.py +++ b/homeassistant/components/vistapool/number.py @@ -57,6 +57,7 @@ NUMBER_DESCRIPTIONS: tuple[VistapoolNumberEntityDescription, ...] = ( VistapoolNumberEntityDescription( key="redox_setpoint", translation_key="redox_setpoint", + device_class=NumberDeviceClass.VOLTAGE, entity_category=EntityCategory.CONFIG, native_min_value=500, native_max_value=800, diff --git a/homeassistant/components/vistapool/quality_scale.yaml b/homeassistant/components/vistapool/quality_scale.yaml index 96224b9b8f9d..eaf772604f15 100644 --- a/homeassistant/components/vistapool/quality_scale.yaml +++ b/homeassistant/components/vistapool/quality_scale.yaml @@ -60,7 +60,13 @@ rules: docs-supported-functions: done docs-use-cases: done dynamic-devices: done - entity-device-class: todo + entity-device-class: + status: done + comment: >- + All entities use a device class where Home Assistant offers a matching + one. Chlorine, UV and the electrolysis production rate (g/h) have no + applicable class; the conductivity reading is reported without a unit, + so it cannot be labelled as a conductivity measurement. entity-translations: done exception-translations: done icon-translations: done diff --git a/homeassistant/components/vistapool/sensor.py b/homeassistant/components/vistapool/sensor.py index 5ecb8a37622f..9289d8b020f4 100644 --- a/homeassistant/components/vistapool/sensor.py +++ b/homeassistant/components/vistapool/sensor.py @@ -89,6 +89,7 @@ SENSOR_DESCRIPTIONS: tuple[VistapoolSensorEntityDescription, ...] = ( VistapoolSensorEntityDescription( key="redox_potential", translation_key="redox_potential", + device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, state_class=SensorStateClass.MEASUREMENT, value_path="modules.rx.current", diff --git a/homeassistant/components/vistapool/switch.py b/homeassistant/components/vistapool/switch.py index 333603c3cb25..f2fc99bb8963 100644 --- a/homeassistant/components/vistapool/switch.py +++ b/homeassistant/components/vistapool/switch.py @@ -5,7 +5,11 @@ from typing import Any, override from aioaquarite import AquariteError -from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -114,6 +118,8 @@ async def async_setup_entry( class VistapoolSwitch(VistapoolEntity, SwitchEntity): """Generic Vistapool switch driven by an entity description.""" + _attr_device_class = SwitchDeviceClass.SWITCH + entity_description: VistapoolSwitchEntityDescription def __init__( diff --git a/tests/components/vistapool/snapshots/test_number.ambr b/tests/components/vistapool/snapshots/test_number.ambr index f1dab3db6b31..5d5bc3411a0a 100644 --- a/tests/components/vistapool/snapshots/test_number.ambr +++ b/tests/components/vistapool/snapshots/test_number.ambr @@ -270,7 +270,7 @@ 'object_id_base': 'Redox setpoint', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Redox setpoint', 'platform': 'vistapool', @@ -285,6 +285,7 @@ # name: test_all_entities[number.my_pool_redox_setpoint-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'voltage', : 'My Pool Redox setpoint', : 800, : 500, diff --git a/tests/components/vistapool/snapshots/test_sensor.ambr b/tests/components/vistapool/snapshots/test_sensor.ambr new file mode 100644 index 000000000000..69d1d96c01a3 --- /dev/null +++ b/tests/components/vistapool/snapshots/test_sensor.ambr @@ -0,0 +1,500 @@ +# serializer version: 1 +# name: test_all_entities[sensor.my_pool_chlorine-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_chlorine', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Chlorine', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Chlorine', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'chlorine', + 'unique_id': 'ABCDEF1234567890-chlorine', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_pool_chlorine-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'My Pool Chlorine', + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_chlorine', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.2', + }) +# --- +# name: test_all_entities[sensor.my_pool_conductivity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_conductivity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Conductivity', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Conductivity', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'conductivity', + 'unique_id': 'ABCDEF1234567890-conductivity', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_pool_conductivity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'My Pool Conductivity', + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_conductivity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.5', + }) +# --- +# name: test_all_entities[sensor.my_pool_electrolysis-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_electrolysis', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Electrolysis', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Electrolysis', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'electrolysis', + 'unique_id': 'ABCDEF1234567890-electrolysis', + 'unit_of_measurement': 'g/h', + }) +# --- +# name: test_all_entities[sensor.my_pool_electrolysis-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'My Pool Electrolysis', + : , + : 'g/h', + }), + 'context': , + 'entity_id': 'sensor.my_pool_electrolysis', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5.0', + }) +# --- +# name: test_all_entities[sensor.my_pool_filtration_intel_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_filtration_intel_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Filtration intel time', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Filtration intel time', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'filtration_intel_time', + 'unique_id': 'ABCDEF1234567890-filtration_intel_time', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_pool_filtration_intel_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'My Pool Filtration intel time', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_filtration_intel_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[sensor.my_pool_ph-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_ph', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'pH', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'pH', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ABCDEF1234567890-ph', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_pool_ph-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'ph', + : 'My Pool pH', + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_ph', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '7.42', + }) +# --- +# name: test_all_entities[sensor.my_pool_redox_potential-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_redox_potential', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Redox potential', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Redox potential', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'redox_potential', + 'unique_id': 'ABCDEF1234567890-redox_potential', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_pool_redox_potential-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'voltage', + : 'My Pool Redox potential', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_redox_potential', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '707', + }) +# --- +# name: test_all_entities[sensor.my_pool_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ABCDEF1234567890-temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_pool_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'My Pool Temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25.5', + }) +# --- +# name: test_all_entities[sensor.my_pool_uv-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_pool_uv', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'UV', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'UV', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'uv', + 'unique_id': 'ABCDEF1234567890-uv', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_pool_uv-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'My Pool UV', + : , + }), + 'context': , + 'entity_id': 'sensor.my_pool_uv', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.0', + }) +# --- +# name: test_all_entities[sensor.my_pool_wi_fi_signal_strength-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.my_pool_wi_fi_signal_strength', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Wi-Fi signal strength', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wi-Fi signal strength', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rssi', + 'unique_id': 'ABCDEF1234567890-rssi', + 'unit_of_measurement': 'dBm', + }) +# --- +# name: test_all_entities[sensor.my_pool_wi_fi_signal_strength-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'signal_strength', + : 'My Pool Wi-Fi signal strength', + : , + : 'dBm', + }), + 'context': , + 'entity_id': 'sensor.my_pool_wi_fi_signal_strength', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-65', + }) +# --- diff --git a/tests/components/vistapool/snapshots/test_switch.ambr b/tests/components/vistapool/snapshots/test_switch.ambr index 225858dd63a3..88a1b12640ae 100644 --- a/tests/components/vistapool/snapshots/test_switch.ambr +++ b/tests/components/vistapool/snapshots/test_switch.ambr @@ -24,7 +24,7 @@ 'object_id_base': 'Electrolysis boost', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Electrolysis boost', 'platform': 'vistapool', @@ -39,6 +39,7 @@ # name: test_all_entities[switch.my_pool_electrolysis_boost-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Electrolysis boost', }), 'context': , @@ -74,7 +75,7 @@ 'object_id_base': 'Electrolysis cover', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Electrolysis cover', 'platform': 'vistapool', @@ -89,6 +90,7 @@ # name: test_all_entities[switch.my_pool_electrolysis_cover-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Electrolysis cover', }), 'context': , @@ -124,7 +126,7 @@ 'object_id_base': 'Filtration', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Filtration', 'platform': 'vistapool', @@ -139,6 +141,7 @@ # name: test_all_entities[switch.my_pool_filtration-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Filtration', }), 'context': , @@ -174,7 +177,7 @@ 'object_id_base': 'Relay 1', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Relay 1', 'platform': 'vistapool', @@ -189,6 +192,7 @@ # name: test_all_entities[switch.my_pool_relay_1-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Relay 1', }), 'context': , @@ -224,7 +228,7 @@ 'object_id_base': 'Relay 2', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Relay 2', 'platform': 'vistapool', @@ -239,6 +243,7 @@ # name: test_all_entities[switch.my_pool_relay_2-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Relay 2', }), 'context': , @@ -274,7 +279,7 @@ 'object_id_base': 'Relay 3', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Relay 3', 'platform': 'vistapool', @@ -289,6 +294,7 @@ # name: test_all_entities[switch.my_pool_relay_3-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Relay 3', }), 'context': , @@ -324,7 +330,7 @@ 'object_id_base': 'Relay 4', 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, 'original_name': 'Relay 4', 'platform': 'vistapool', @@ -339,6 +345,7 @@ # name: test_all_entities[switch.my_pool_relay_4-state] StateSnapshot({ 'attributes': ReadOnlyDict({ + : 'switch', : 'My Pool Relay 4', }), 'context': , diff --git a/tests/components/vistapool/test_sensor.py b/tests/components/vistapool/test_sensor.py index df745fbd0030..206bdc7f78dc 100644 --- a/tests/components/vistapool/test_sensor.py +++ b/tests/components/vistapool/test_sensor.py @@ -2,12 +2,63 @@ from __future__ import annotations +from collections.abc import Generator from typing import Any -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, patch +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 tests.common import MockConfigEntry +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.fixture +def _only_sensor_platform() -> Generator[None]: + """Restrict integration setup to the sensor platform for the snapshot.""" + with patch("homeassistant.components.vistapool.PLATFORMS", [Platform.SENSOR]): + yield + + +@pytest.mark.usefixtures("_only_sensor_platform", "entity_registry_enabled_by_default") +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, +) -> None: + """Test sensor entities when every module-gated sensor is present.""" + mock_vistapool_client.fetch_pool_data.return_value = { + "main": { + "hasCD": 1, + "hasCL": 1, + "hasPH": 1, + "hasRX": 1, + "hasUV": 1, + "hasHidro": 1, + "RSSI": -65, + "temperature": 25.5, + "version": 1, + }, + "hidro": {"is_electrolysis": True, "current": 50}, + "modules": { + "ph": {"current": "742"}, + "rx": {"current": 707}, + "cl": {"current": "120"}, + "cd": {"current": "150"}, + "uv": {"current": "100"}, + }, + } + mock_config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) async def test_sensors_default_modules(