diff --git a/homeassistant/components/openevse/__init__.py b/homeassistant/components/openevse/__init__.py index b2847d8f785..b3416f5ec3e 100644 --- a/homeassistant/components/openevse/__init__.py +++ b/homeassistant/components/openevse/__init__.py @@ -10,6 +10,8 @@ from homeassistant.exceptions import ConfigEntryNotReady from .coordinator import OpenEVSEConfigEntry, OpenEVSEDataUpdateCoordinator +PLATFORMS = [Platform.SENSOR] + async def async_setup_entry(hass: HomeAssistant, entry: OpenEVSEConfigEntry) -> bool: """Set up OpenEVSE from a config entry.""" @@ -29,10 +31,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: OpenEVSEConfigEntry) -> entry.runtime_data = coordinator - await hass.config_entries.async_forward_entry_setups(entry, [Platform.SENSOR]) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True async def async_unload_entry(hass: HomeAssistant, entry: OpenEVSEConfigEntry) -> bool: """Unload a config entry.""" - return await hass.config_entries.async_unload_platforms(entry, [Platform.SENSOR]) + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/openevse/sensor.py b/homeassistant/components/openevse/sensor.py index 6321c70e22c..8bb562ef0ea 100644 --- a/homeassistant/components/openevse/sensor.py +++ b/homeassistant/components/openevse/sensor.py @@ -4,6 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass +from datetime import datetime import logging from openevsehttp.__main__ import OpenEVSE @@ -22,7 +23,15 @@ from homeassistant.const import ( ATTR_SERIAL_NUMBER, CONF_HOST, CONF_MONITORED_VARIABLES, + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS, + EntityCategory, + UnitOfElectricCurrent, + UnitOfElectricPotential, UnitOfEnergy, + UnitOfInformation, + UnitOfLength, + UnitOfPower, UnitOfTemperature, UnitOfTime, ) @@ -49,15 +58,30 @@ PARALLEL_UPDATES = 0 class OpenEVSESensorDescription(SensorEntityDescription): """Describes an OpenEVSE sensor entity.""" - value_fn: Callable[[OpenEVSE], str | float | None] + value_fn: Callable[[OpenEVSE], str | float | datetime | None] SENSOR_TYPES: tuple[OpenEVSESensorDescription, ...] = ( + # Status sensors OpenEVSESensorDescription( key="status", translation_key="status", value_fn=lambda ev: ev.status, ), + OpenEVSESensorDescription( + key="service_level", + translation_key="service_level", + device_class=SensorDeviceClass.ENUM, + options=["level_1", "level_2", "automatic"], + value_fn=lambda ev: { + "1": "level_1", + "2": "level_2", + "a": "automatic", + }.get(ev.service_level.lower()), + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + # Timing sensors OpenEVSESensorDescription( key="charge_time", translation_key="charge_time", @@ -67,6 +91,80 @@ SENSOR_TYPES: tuple[OpenEVSESensorDescription, ...] = ( state_class=SensorStateClass.MEASUREMENT, value_fn=lambda ev: ev.charge_time_elapsed, ), + OpenEVSESensorDescription( + key="vehicle_eta", + translation_key="vehicle_eta", + device_class=SensorDeviceClass.TIMESTAMP, + value_fn=lambda ev: ev.vehicle_eta, + ), + # Electrical sensors + OpenEVSESensorDescription( + key="charging_current", + translation_key="charging_current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.charging_current, + ), + OpenEVSESensorDescription( + key="charging_voltage", + translation_key="charging_voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.charging_voltage, + ), + OpenEVSESensorDescription( + key="charging_power", + translation_key="charging_power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.charging_power, + ), + OpenEVSESensorDescription( + key="current_power", + translation_key="current_power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.current_power, + ), + OpenEVSESensorDescription( + key="current_capacity", + translation_key="current_capacity", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.current_capacity, + ), + OpenEVSESensorDescription( + key="max_current", + translation_key="max_current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_category=EntityCategory.DIAGNOSTIC, + value_fn=lambda ev: ev.max_current, + ), + OpenEVSESensorDescription( + key="min_amps", + translation_key="min_amps", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.min_amps, + ), + OpenEVSESensorDescription( + key="max_amps", + translation_key="max_amps", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.max_amps, + ), + # Temperature sensors OpenEVSESensorDescription( key="ambient_temp", translation_key="ambient_temp", @@ -93,6 +191,17 @@ SENSOR_TYPES: tuple[OpenEVSESensorDescription, ...] = ( value_fn=lambda ev: ev.rtc_temperature, entity_registry_enabled_default=False, ), + OpenEVSESensorDescription( + key="esp_temp", + translation_key="esp_temp", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.esp_temperature, + ), + # Energy sensors OpenEVSESensorDescription( key="usage_session", translation_key="usage_session", @@ -111,6 +220,145 @@ SENSOR_TYPES: tuple[OpenEVSESensorDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda ev: ev.usage_total, ), + OpenEVSESensorDescription( + key="total_day", + translation_key="total_day", + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.total_day, + ), + OpenEVSESensorDescription( + key="total_week", + translation_key="total_week", + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.total_week, + ), + OpenEVSESensorDescription( + key="total_month", + translation_key="total_month", + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.total_month, + ), + OpenEVSESensorDescription( + key="total_year", + translation_key="total_year", + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.total_year, + ), + # Vehicle sensors + OpenEVSESensorDescription( + key="vehicle_soc", + translation_key="vehicle_soc", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.BATTERY, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.vehicle_soc, + ), + OpenEVSESensorDescription( + key="vehicle_range", + translation_key="vehicle_range", + native_unit_of_measurement=UnitOfLength.KILOMETERS, + device_class=SensorDeviceClass.DISTANCE, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda ev: ev.vehicle_range, + ), + # Connectivity sensors + OpenEVSESensorDescription( + key="wifi_signal", + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS, + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.wifi_signal, + ), + # Power shaper sensors + OpenEVSESensorDescription( + key="shaper_live_power", + translation_key="shaper_live_power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.shaper_live_power, + ), + OpenEVSESensorDescription( + key="shaper_available_current", + translation_key="shaper_available_current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.shaper_available_current, + ), + OpenEVSESensorDescription( + key="shaper_max_power", + translation_key="shaper_max_power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.shaper_max_power, + ), + # Safety trip count sensors + OpenEVSESensorDescription( + key="gfi_trip_count", + translation_key="gfi_trip_count", + state_class=SensorStateClass.TOTAL, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.gfi_trip_count, + ), + OpenEVSESensorDescription( + key="no_gnd_trip_count", + translation_key="no_gnd_trip_count", + state_class=SensorStateClass.TOTAL, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.no_gnd_trip_count, + ), + OpenEVSESensorDescription( + key="stuck_relay_trip_count", + translation_key="stuck_relay_trip_count", + state_class=SensorStateClass.TOTAL, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.stuck_relay_trip_count, + ), + # System diagnostic sensors + OpenEVSESensorDescription( + key="uptime", + translation_key="uptime", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.uptime, + ), + OpenEVSESensorDescription( + key="freeram", + translation_key="freeram", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ev: ev.freeram, + ), ) SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES] @@ -217,6 +465,6 @@ class OpenEVSESensor(CoordinatorEntity[OpenEVSEDataUpdateCoordinator], SensorEnt self._attr_device_info[ATTR_SERIAL_NUMBER] = unique_id @property - def native_value(self) -> StateType: + def native_value(self) -> StateType | datetime: """Return the state of the sensor.""" return self.entity_description.value_fn(self.coordinator.charger) diff --git a/homeassistant/components/openevse/strings.json b/homeassistant/components/openevse/strings.json index f3f75f541d8..44e93226ece 100644 --- a/homeassistant/components/openevse/strings.json +++ b/homeassistant/components/openevse/strings.json @@ -34,23 +34,118 @@ "ambient_temp": { "name": "Ambient temperature" }, + "available_current": { + "name": "Available current" + }, + "charge_rate": { + "name": "Charge rate" + }, "charge_time": { "name": "Charge time elapsed" }, + "charging_current": { + "name": "Charging current" + }, + "charging_power": { + "name": "Charging power" + }, + "charging_voltage": { + "name": "Charging voltage" + }, + "current_capacity": { + "name": "Current capacity" + }, + "current_power": { + "name": "Current power" + }, + "esp_temp": { + "name": "ESP temperature" + }, + "freeram": { + "name": "Free memory" + }, + "gfi_trip_count": { + "name": "GFCI trip count" + }, "ir_temp": { "name": "IR temperature" }, + "max_amps": { + "name": "Maximum amperage" + }, + "max_current": { + "name": "Maximum current" + }, + "min_amps": { + "name": "Minimum amperage" + }, + "mode": { + "name": "Mode" + }, + "no_gnd_trip_count": { + "name": "No ground trip count" + }, + "ota_update": { + "name": "OTA update" + }, "rtc_temp": { "name": "RTC temperature" }, + "service_level": { + "name": "Service level", + "state": { + "automatic": "Automatic", + "level_1": "Level 1 (120V)", + "level_2": "Level 2 (240V)" + } + }, + "shaper_available_current": { + "name": "Shaper available current" + }, + "shaper_live_power": { + "name": "Shaper live power" + }, + "shaper_max_power": { + "name": "Shaper maximum power" + }, + "smoothed_available_current": { + "name": "Smoothed available current" + }, "status": { "name": "Charging status" }, + "stuck_relay_trip_count": { + "name": "Stuck relay trip count" + }, + "total_day": { + "name": "Daily energy usage" + }, + "total_month": { + "name": "Monthly energy usage" + }, + "total_week": { + "name": "Weekly energy usage" + }, + "total_year": { + "name": "Yearly energy usage" + }, + "uptime": { + "name": "Uptime" + }, "usage_session": { "name": "Usage this session" }, "usage_total": { "name": "Total energy usage" + }, + "vehicle_eta": { + "name": "Vehicle charge completion" + }, + "vehicle_range": { + "name": "Vehicle range" + }, + "vehicle_soc": { + "name": "Vehicle state of charge" } } }, diff --git a/tests/components/openevse/conftest.py b/tests/components/openevse/conftest.py index 5a06f8a4b14..36591ce1f94 100644 --- a/tests/components/openevse/conftest.py +++ b/tests/components/openevse/conftest.py @@ -26,19 +26,64 @@ def mock_charger() -> Generator[MagicMock]: ): charger = mock.return_value charger.update = AsyncMock() - charger.status = "Charging" - charger.charge_time_elapsed = 3600 # 60 minutes in seconds - charger.ambient_temperature = 25.5 - charger.ir_temperature = 30.2 - charger.rtc_temperature = 28.7 - charger.usage_session = 15000 # 15 kWh in Wh - charger.usage_total = 500000 # 500 kWh in Wh - charger.charging_current = 32.0 charger.test_and_get = AsyncMock() charger.test_and_get.return_value = { "serial": "deadbeeffeed", "model": "openevse_wifi_v1", } + # Status sensors + charger.status = "Charging" + charger.vehicle = True + charger.mode = "STA" + charger.charge_mode = "fast" + charger.divertmode = "normal" + charger.manual_override = False + charger.ota_update = "none" + charger.service_level = "2" + # Timing sensors + charger.charge_time_elapsed = 3600 # 60 minutes in seconds + charger.vehicle_eta = None + # Electrical sensors + charger.charging_current = 32.0 + charger.charging_voltage = 240 + charger.charging_power = 7680.0 + charger.current_power = 7680 + charger.current_capacity = 32 + charger.max_current = 48 + charger.min_amps = 6 + charger.max_amps = 48 + # Divert/solar mode sensors + charger.available_current = 32.0 + charger.smoothed_available_current = 32.0 + charger.charge_rate = 32.0 + # Temperature sensors + charger.ambient_temperature = 25.5 + charger.ir_temperature = 30.2 + charger.rtc_temperature = 28.7 + charger.esp_temperature = 45.0 + # Energy sensors + charger.usage_session = 15000 # 15 kWh in Wh + charger.usage_total = 500000 # 500 kWh in Wh + charger.total_day = 10000 # 10 kWh in Wh + charger.total_week = 50000 # 50 kWh in Wh + charger.total_month = 200000 # 200 kWh in Wh + charger.total_year = 2000000 # 2000 kWh in Wh + # Vehicle sensors + charger.vehicle_soc = 75 + charger.vehicle_range = 250 + # Connectivity sensors + charger.wifi_signal = -65 + # Power shaper sensors + charger.shaper_live_power = 5000 + charger.shaper_available_current = 20.0 + charger.shaper_max_power = 11000 + # Safety trip count sensors + charger.gfi_trip_count = 0 + charger.no_gnd_trip_count = 0 + charger.stuck_relay_trip_count = 0 + # System diagnostic sensors + charger.uptime = 86400 # 1 day in seconds + charger.freeram = 50000 yield charger diff --git a/tests/components/openevse/snapshots/test_sensor.ambr b/tests/components/openevse/snapshots/test_sensor.ambr index e432d0e75f5..ea3f2876764 100644 --- a/tests/components/openevse/snapshots/test_sensor.ambr +++ b/tests/components/openevse/snapshots/test_sensor.ambr @@ -116,6 +116,120 @@ 'state': '60.0', }) # --- +# name: test_entities[sensor.openevse_mock_config_charging_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_charging_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Charging current', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charging current', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'charging_current', + 'unique_id': 'deadbeeffeed-charging_current', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_charging_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'openevse_mock_config Charging current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_charging_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '32.0', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_charging_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_charging_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Charging power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charging power', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'charging_power', + 'unique_id': 'deadbeeffeed-charging_power', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_charging_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'openevse_mock_config Charging power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_charging_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '7680.0', + }) +# --- # name: test_entities[sensor.openevse_mock_config_charging_status-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -165,6 +279,400 @@ 'state': 'Charging', }) # --- +# name: test_entities[sensor.openevse_mock_config_charging_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_charging_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Charging voltage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charging voltage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'charging_voltage', + 'unique_id': 'deadbeeffeed-charging_voltage', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_charging_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': 'openevse_mock_config Charging voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_charging_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '240', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_current_capacity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_current_capacity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Current capacity', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Current capacity', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_capacity', + 'unique_id': 'deadbeeffeed-current_capacity', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_current_capacity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'openevse_mock_config Current capacity', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_current_capacity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '32', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_current_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_current_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Current power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Current power', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power', + 'unique_id': 'deadbeeffeed-current_power', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_current_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'openevse_mock_config Current power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_current_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '7680', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_daily_energy_usage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_daily_energy_usage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Daily energy usage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Daily energy usage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'total_day', + 'unique_id': 'deadbeeffeed-total_day', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_daily_energy_usage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'openevse_mock_config Daily energy usage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_daily_energy_usage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.0', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_esp_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_esp_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'ESP temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'ESP temperature', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'esp_temp', + 'unique_id': 'deadbeeffeed-esp_temp', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_esp_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'openevse_mock_config ESP temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_esp_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '45.0', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_free_memory-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_free_memory', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Free memory', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Free memory', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'freeram', + 'unique_id': 'deadbeeffeed-freeram', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_free_memory-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'openevse_mock_config Free memory', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_free_memory', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '50000', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_gfci_trip_count-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_gfci_trip_count', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'GFCI trip count', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'GFCI trip count', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'gfi_trip_count', + 'unique_id': 'deadbeeffeed-gfi_trip_count', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[sensor.openevse_mock_config_gfci_trip_count-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'openevse_mock_config GFCI trip count', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_gfci_trip_count', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- # name: test_entities[sensor.openevse_mock_config_ir_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -222,6 +730,280 @@ 'state': '30.2', }) # --- +# name: test_entities[sensor.openevse_mock_config_maximum_amperage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_maximum_amperage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum amperage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum amperage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_amps', + 'unique_id': 'deadbeeffeed-max_amps', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_maximum_amperage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'openevse_mock_config Maximum amperage', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_maximum_amperage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '48', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_maximum_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_maximum_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Maximum current', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum current', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_current', + 'unique_id': 'deadbeeffeed-max_current', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_maximum_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'openevse_mock_config Maximum current', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_maximum_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '48', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_minimum_amperage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_minimum_amperage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Minimum amperage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Minimum amperage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'min_amps', + 'unique_id': 'deadbeeffeed-min_amps', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_minimum_amperage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'openevse_mock_config Minimum amperage', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_minimum_amperage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_monthly_energy_usage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_monthly_energy_usage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Monthly energy usage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Monthly energy usage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'total_month', + 'unique_id': 'deadbeeffeed-total_month', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_monthly_energy_usage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'openevse_mock_config Monthly energy usage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_monthly_energy_usage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '200.0', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_no_ground_trip_count-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_no_ground_trip_count', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'No ground trip count', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'No ground trip count', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'no_gnd_trip_count', + 'unique_id': 'deadbeeffeed-no_gnd_trip_count', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[sensor.openevse_mock_config_no_ground_trip_count-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'openevse_mock_config No ground trip count', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_no_ground_trip_count', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- # name: test_entities[sensor.openevse_mock_config_rtc_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -279,6 +1061,341 @@ 'state': '28.7', }) # --- +# name: test_entities[sensor.openevse_mock_config_service_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'level_1', + 'level_2', + 'automatic', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_service_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Service level', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service level', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_level', + 'unique_id': 'deadbeeffeed-service_level', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[sensor.openevse_mock_config_service_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'openevse_mock_config Service level', + 'options': list([ + 'level_1', + 'level_2', + 'automatic', + ]), + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_service_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'level_2', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_shaper_available_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_shaper_available_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Shaper available current', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Shaper available current', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'shaper_available_current', + 'unique_id': 'deadbeeffeed-shaper_available_current', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_shaper_available_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'openevse_mock_config Shaper available current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_shaper_available_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20.0', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_shaper_live_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_shaper_live_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Shaper live power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Shaper live power', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'shaper_live_power', + 'unique_id': 'deadbeeffeed-shaper_live_power', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_shaper_live_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'openevse_mock_config Shaper live power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_shaper_live_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5000', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_shaper_maximum_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_shaper_maximum_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Shaper maximum power', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Shaper maximum power', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'shaper_max_power', + 'unique_id': 'deadbeeffeed-shaper_max_power', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_shaper_maximum_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'openevse_mock_config Shaper maximum power', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_shaper_maximum_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '11000', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_signal_strength-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_signal_strength', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Signal strength', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Signal strength', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'deadbeeffeed-wifi_signal', + 'unit_of_measurement': 'dB', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_signal_strength-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'signal_strength', + 'friendly_name': 'openevse_mock_config Signal strength', + 'state_class': , + 'unit_of_measurement': 'dB', + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_signal_strength', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-65', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_stuck_relay_trip_count-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_stuck_relay_trip_count', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Stuck relay trip count', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Stuck relay trip count', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'stuck_relay_trip_count', + 'unique_id': 'deadbeeffeed-stuck_relay_trip_count', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[sensor.openevse_mock_config_stuck_relay_trip_count-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'openevse_mock_config Stuck relay trip count', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_stuck_relay_trip_count', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- # name: test_entities[sensor.openevse_mock_config_total_energy_usage-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -339,6 +1456,60 @@ 'state': '500.0', }) # --- +# name: test_entities[sensor.openevse_mock_config_uptime-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.openevse_mock_config_uptime', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Uptime', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Uptime', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'uptime', + 'unique_id': 'deadbeeffeed-uptime', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_uptime-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'openevse_mock_config Uptime', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_uptime', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '86400', + }) +# --- # name: test_entities[sensor.openevse_mock_config_usage_this_session-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -399,3 +1570,284 @@ 'state': '15.0', }) # --- +# name: test_entities[sensor.openevse_mock_config_vehicle_charge_completion-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_vehicle_charge_completion', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Vehicle charge completion', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Vehicle charge completion', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'vehicle_eta', + 'unique_id': 'deadbeeffeed-vehicle_eta', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[sensor.openevse_mock_config_vehicle_charge_completion-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'openevse_mock_config Vehicle charge completion', + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_vehicle_charge_completion', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_vehicle_range-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_vehicle_range', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Vehicle range', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Vehicle range', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'vehicle_range', + 'unique_id': 'deadbeeffeed-vehicle_range', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_vehicle_range-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'distance', + 'friendly_name': 'openevse_mock_config Vehicle range', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_vehicle_range', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '250', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_vehicle_state_of_charge-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_vehicle_state_of_charge', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Vehicle state of charge', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Vehicle state of charge', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'vehicle_soc', + 'unique_id': 'deadbeeffeed-vehicle_soc', + 'unit_of_measurement': '%', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_vehicle_state_of_charge-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'openevse_mock_config Vehicle state of charge', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_vehicle_state_of_charge', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_weekly_energy_usage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_weekly_energy_usage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Weekly energy usage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Weekly energy usage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'total_week', + 'unique_id': 'deadbeeffeed-total_week', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_weekly_energy_usage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'openevse_mock_config Weekly energy usage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_weekly_energy_usage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '50.0', + }) +# --- +# name: test_entities[sensor.openevse_mock_config_yearly_energy_usage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openevse_mock_config_yearly_energy_usage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Yearly energy usage', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Yearly energy usage', + 'platform': 'openevse', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'total_year', + 'unique_id': 'deadbeeffeed-total_year', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.openevse_mock_config_yearly_energy_usage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'openevse_mock_config Yearly energy usage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openevse_mock_config_yearly_energy_usage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2000.0', + }) +# --- diff --git a/tests/components/openevse/test_sensor.py b/tests/components/openevse/test_sensor.py index 67a656bf0d9..d117cb4a4f8 100644 --- a/tests/components/openevse/test_sensor.py +++ b/tests/components/openevse/test_sensor.py @@ -1,13 +1,13 @@ """Tests for the OpenEVSE sensor platform.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.openevse.const import DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.const import CONF_HOST, STATE_UNAVAILABLE +from homeassistant.const import CONF_HOST, STATE_UNAVAILABLE, STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er, issue_registry as ir from homeassistant.setup import async_setup_component @@ -24,8 +24,9 @@ async def test_entities( mock_charger: MagicMock, ) -> None: """Test the sensor entities.""" - mock_config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(mock_config_entry.entry_id) + with patch("homeassistant.components.openevse.PLATFORMS", [Platform.SENSOR]): + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) @@ -57,6 +58,28 @@ async def test_disabled_by_default_entities( assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION +async def test_missing_sensor_graceful_handling( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_charger: MagicMock, +) -> None: + """Test that missing sensor attributes are handled gracefully.""" + mock_charger.vehicle_soc = None + + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + + # The sensor with missing attribute should be unknown + state = hass.states.get("sensor.openevse_mock_config_vehicle_state_of_charge") + assert state is not None + assert state.state == STATE_UNKNOWN + + # Other sensors should still work + state = hass.states.get("sensor.openevse_mock_config_charging_status") + assert state is not None + assert state.state == "Charging" + + async def test_sensor_unavailable_on_coordinator_timeout( hass: HomeAssistant, mock_config_entry: MockConfigEntry,