Add Shelly EV charger sensors (#152722)

This commit is contained in:
Shay Levy
2025-09-28 22:45:11 +03:00
committed by GitHub
parent 281a137ff5
commit 9176867d6b
5 changed files with 301 additions and 0 deletions

View File

@@ -20,6 +20,9 @@
}
},
"sensor": {
"charger_state": {
"default": "mdi:ev-station"
},
"detected_objects": {
"default": "mdi:account-group"
},

View File

@@ -33,6 +33,7 @@ from homeassistant.const import (
UnitOfPower,
UnitOfPressure,
UnitOfTemperature,
UnitOfTime,
UnitOfVolume,
UnitOfVolumeFlowRate,
)
@@ -1489,6 +1490,41 @@ RPC_SENSORS: Final = {
state_class=SensorStateClass.MEASUREMENT,
role="water_temperature",
),
"number_work_state": RpcSensorDescription(
key="number",
sub_key="value",
translation_key="charger_state",
device_class=SensorDeviceClass.ENUM,
options=[
"charger_charging",
"charger_end",
"charger_fault",
"charger_free",
"charger_free_fault",
"charger_insert",
"charger_pause",
"charger_wait",
],
role="work_state",
),
"number_energy_charge": RpcSensorDescription(
key="number",
sub_key="value",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
role="energy_charge",
),
"number_time_charge": RpcSensorDescription(
key="number",
sub_key="value",
native_unit_of_measurement=UnitOfTime.MINUTES,
suggested_display_precision=0,
device_class=SensorDeviceClass.DURATION,
role="time_charge",
),
"presence_num_objects": RpcSensorDescription(
key="presence",
sub_key="num_objects",

View File

@@ -141,6 +141,18 @@
}
},
"sensor": {
"charger_state": {
"state": {
"charger_charging": "[%key:common::state::charging%]",
"charger_end": "Charge completed",
"charger_fault": "Error while charging",
"charger_free": "[%key:component::binary_sensor::entity_component::plug::state::off%]",
"charger_free_fault": "Can not release plug",
"charger_insert": "[%key:component::binary_sensor::entity_component::plug::state::on%]",
"charger_pause": "Charging paused by charger",
"charger_wait": "Charging paused by vehicle"
}
},
"detected_objects": {
"unit_of_measurement": "objects"
},

View File

@@ -157,6 +157,188 @@
'state': '0',
})
# ---
# name: test_rpc_shelly_ev_sensors[sensor.test_name_charger_state-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'charger_charging',
'charger_end',
'charger_fault',
'charger_free',
'charger_free_fault',
'charger_insert',
'charger_pause',
'charger_wait',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.test_name_charger_state',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Charger state',
'platform': 'shelly',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'charger_state',
'unique_id': '123456789ABC-number:200-number_work_state',
'unit_of_measurement': None,
})
# ---
# name: test_rpc_shelly_ev_sensors[sensor.test_name_charger_state-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Test name Charger state',
'options': list([
'charger_charging',
'charger_end',
'charger_fault',
'charger_free',
'charger_free_fault',
'charger_insert',
'charger_pause',
'charger_wait',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.test_name_charger_state',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'charger_charging',
})
# ---
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_duration-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.test_name_session_duration',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Session duration',
'platform': 'shelly',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '123456789ABC-number:202-number_time_charge',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
})
# ---
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_duration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Test name Session duration',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'context': <ANY>,
'entity_id': 'sensor.test_name_session_duration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '60',
})
# ---
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.test_name_session_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Session energy',
'platform': 'shelly',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '123456789ABC-number:201-number_energy_charge',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Test name Session energy',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.test_name_session_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '5.0',
})
# ---
# name: test_rpc_switch_energy_sensors[sensor.test_name_test_switch_0_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@@ -1672,6 +1672,74 @@ async def test_rpc_switch_no_returned_energy_sensor(
assert hass.states.get("sensor.test_name_test_switch_0_returned_energy") is None
async def test_rpc_shelly_ev_sensors(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
entity_registry: EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test Shelly EV sensors."""
config = deepcopy(mock_rpc_device.config)
config["number:200"] = {
"name": "Charger state",
"meta": {
"ui": {
"titles": {
"charger_charging": "Charging",
"charger_end": "End",
"charger_fault": "Fault",
"charger_free": "Free",
"charger_free_fault": "Free fault",
"charger_insert": "Insert",
"charger_pause": "Pause",
"charger_wait": "Wait",
},
"view": "label",
}
},
"options": [
"charger_free",
"charger_insert",
"charger_free_fault",
"charger_wait",
"charger_charging",
"charger_pause",
"charger_end",
"charger_fault",
],
"role": "work_state",
}
config["number:201"] = {
"name": "Session energy",
"meta": {"ui": {"unit": "Wh", "view": "label"}},
"role": "energy_charge",
}
config["number:202"] = {
"name": "Session duration",
"meta": {"ui": {"unit": "min", "view": "label"}},
"role": "time_charge",
}
monkeypatch.setattr(mock_rpc_device, "config", config)
status = deepcopy(mock_rpc_device.status)
status["number:200"] = {"value": "charger_charging"}
status["number:201"] = {"value": 5000}
status["number:202"] = {"value": 60}
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 3)
for entity in ("charger_state", "session_energy", "session_duration"):
entity_id = f"{SENSOR_DOMAIN}.test_name_{entity}"
state = hass.states.get(entity_id)
assert state == snapshot(name=f"{entity_id}-state")
entry = entity_registry.async_get(entity_id)
assert entry == snapshot(name=f"{entity_id}-entry")
async def test_block_friendly_name_sleeping_sensor(
hass: HomeAssistant,
mock_block_device: Mock,