Skip to content

Commit 2b5f989

Browse files
thecodefrenck
authored andcommitted
Add Shelly EV charger sensors (home-assistant#152722)
1 parent 4e247a6 commit 2b5f989

File tree

5 files changed

+301
-0
lines changed

5 files changed

+301
-0
lines changed

homeassistant/components/shelly/icons.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
}
2121
},
2222
"sensor": {
23+
"charger_state": {
24+
"default": "mdi:ev-station"
25+
},
2326
"detected_objects": {
2427
"default": "mdi:account-group"
2528
},

homeassistant/components/shelly/sensor.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
UnitOfPower,
3434
UnitOfPressure,
3535
UnitOfTemperature,
36+
UnitOfTime,
3637
UnitOfVolume,
3738
UnitOfVolumeFlowRate,
3839
)
@@ -1489,6 +1490,41 @@ def __init__(
14891490
state_class=SensorStateClass.MEASUREMENT,
14901491
role="water_temperature",
14911492
),
1493+
"number_work_state": RpcSensorDescription(
1494+
key="number",
1495+
sub_key="value",
1496+
translation_key="charger_state",
1497+
device_class=SensorDeviceClass.ENUM,
1498+
options=[
1499+
"charger_charging",
1500+
"charger_end",
1501+
"charger_fault",
1502+
"charger_free",
1503+
"charger_free_fault",
1504+
"charger_insert",
1505+
"charger_pause",
1506+
"charger_wait",
1507+
],
1508+
role="work_state",
1509+
),
1510+
"number_energy_charge": RpcSensorDescription(
1511+
key="number",
1512+
sub_key="value",
1513+
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
1514+
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
1515+
suggested_display_precision=2,
1516+
device_class=SensorDeviceClass.ENERGY,
1517+
state_class=SensorStateClass.TOTAL,
1518+
role="energy_charge",
1519+
),
1520+
"number_time_charge": RpcSensorDescription(
1521+
key="number",
1522+
sub_key="value",
1523+
native_unit_of_measurement=UnitOfTime.MINUTES,
1524+
suggested_display_precision=0,
1525+
device_class=SensorDeviceClass.DURATION,
1526+
role="time_charge",
1527+
),
14921528
"presence_num_objects": RpcSensorDescription(
14931529
key="presence",
14941530
sub_key="num_objects",

homeassistant/components/shelly/strings.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@
141141
}
142142
},
143143
"sensor": {
144+
"charger_state": {
145+
"state": {
146+
"charger_charging": "[%key:common::state::charging%]",
147+
"charger_end": "Charge completed",
148+
"charger_fault": "Error while charging",
149+
"charger_free": "[%key:component::binary_sensor::entity_component::plug::state::off%]",
150+
"charger_free_fault": "Can not release plug",
151+
"charger_insert": "[%key:component::binary_sensor::entity_component::plug::state::on%]",
152+
"charger_pause": "Charging paused by charger",
153+
"charger_wait": "Charging paused by vehicle"
154+
}
155+
},
144156
"detected_objects": {
145157
"unit_of_measurement": "objects"
146158
},

tests/components/shelly/snapshots/test_sensor.ambr

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,188 @@
157157
'state': '0',
158158
})
159159
# ---
160+
# name: test_rpc_shelly_ev_sensors[sensor.test_name_charger_state-entry]
161+
EntityRegistryEntrySnapshot({
162+
'aliases': set({
163+
}),
164+
'area_id': None,
165+
'capabilities': dict({
166+
'options': list([
167+
'charger_charging',
168+
'charger_end',
169+
'charger_fault',
170+
'charger_free',
171+
'charger_free_fault',
172+
'charger_insert',
173+
'charger_pause',
174+
'charger_wait',
175+
]),
176+
}),
177+
'config_entry_id': <ANY>,
178+
'config_subentry_id': <ANY>,
179+
'device_class': None,
180+
'device_id': <ANY>,
181+
'disabled_by': None,
182+
'domain': 'sensor',
183+
'entity_category': None,
184+
'entity_id': 'sensor.test_name_charger_state',
185+
'has_entity_name': True,
186+
'hidden_by': None,
187+
'icon': None,
188+
'id': <ANY>,
189+
'labels': set({
190+
}),
191+
'name': None,
192+
'options': dict({
193+
}),
194+
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
195+
'original_icon': None,
196+
'original_name': 'Charger state',
197+
'platform': 'shelly',
198+
'previous_unique_id': None,
199+
'suggested_object_id': None,
200+
'supported_features': 0,
201+
'translation_key': 'charger_state',
202+
'unique_id': '123456789ABC-number:200-number_work_state',
203+
'unit_of_measurement': None,
204+
})
205+
# ---
206+
# name: test_rpc_shelly_ev_sensors[sensor.test_name_charger_state-state]
207+
StateSnapshot({
208+
'attributes': ReadOnlyDict({
209+
'device_class': 'enum',
210+
'friendly_name': 'Test name Charger state',
211+
'options': list([
212+
'charger_charging',
213+
'charger_end',
214+
'charger_fault',
215+
'charger_free',
216+
'charger_free_fault',
217+
'charger_insert',
218+
'charger_pause',
219+
'charger_wait',
220+
]),
221+
}),
222+
'context': <ANY>,
223+
'entity_id': 'sensor.test_name_charger_state',
224+
'last_changed': <ANY>,
225+
'last_reported': <ANY>,
226+
'last_updated': <ANY>,
227+
'state': 'charger_charging',
228+
})
229+
# ---
230+
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_duration-entry]
231+
EntityRegistryEntrySnapshot({
232+
'aliases': set({
233+
}),
234+
'area_id': None,
235+
'capabilities': None,
236+
'config_entry_id': <ANY>,
237+
'config_subentry_id': <ANY>,
238+
'device_class': None,
239+
'device_id': <ANY>,
240+
'disabled_by': None,
241+
'domain': 'sensor',
242+
'entity_category': None,
243+
'entity_id': 'sensor.test_name_session_duration',
244+
'has_entity_name': True,
245+
'hidden_by': None,
246+
'icon': None,
247+
'id': <ANY>,
248+
'labels': set({
249+
}),
250+
'name': None,
251+
'options': dict({
252+
'sensor': dict({
253+
'suggested_display_precision': 0,
254+
}),
255+
}),
256+
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
257+
'original_icon': None,
258+
'original_name': 'Session duration',
259+
'platform': 'shelly',
260+
'previous_unique_id': None,
261+
'suggested_object_id': None,
262+
'supported_features': 0,
263+
'translation_key': None,
264+
'unique_id': '123456789ABC-number:202-number_time_charge',
265+
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
266+
})
267+
# ---
268+
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_duration-state]
269+
StateSnapshot({
270+
'attributes': ReadOnlyDict({
271+
'device_class': 'duration',
272+
'friendly_name': 'Test name Session duration',
273+
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
274+
}),
275+
'context': <ANY>,
276+
'entity_id': 'sensor.test_name_session_duration',
277+
'last_changed': <ANY>,
278+
'last_reported': <ANY>,
279+
'last_updated': <ANY>,
280+
'state': '60',
281+
})
282+
# ---
283+
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_energy-entry]
284+
EntityRegistryEntrySnapshot({
285+
'aliases': set({
286+
}),
287+
'area_id': None,
288+
'capabilities': dict({
289+
'state_class': <SensorStateClass.TOTAL: 'total'>,
290+
}),
291+
'config_entry_id': <ANY>,
292+
'config_subentry_id': <ANY>,
293+
'device_class': None,
294+
'device_id': <ANY>,
295+
'disabled_by': None,
296+
'domain': 'sensor',
297+
'entity_category': None,
298+
'entity_id': 'sensor.test_name_session_energy',
299+
'has_entity_name': True,
300+
'hidden_by': None,
301+
'icon': None,
302+
'id': <ANY>,
303+
'labels': set({
304+
}),
305+
'name': None,
306+
'options': dict({
307+
'sensor': dict({
308+
'suggested_display_precision': 2,
309+
}),
310+
'sensor.private': dict({
311+
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
312+
}),
313+
}),
314+
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
315+
'original_icon': None,
316+
'original_name': 'Session energy',
317+
'platform': 'shelly',
318+
'previous_unique_id': None,
319+
'suggested_object_id': None,
320+
'supported_features': 0,
321+
'translation_key': None,
322+
'unique_id': '123456789ABC-number:201-number_energy_charge',
323+
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
324+
})
325+
# ---
326+
# name: test_rpc_shelly_ev_sensors[sensor.test_name_session_energy-state]
327+
StateSnapshot({
328+
'attributes': ReadOnlyDict({
329+
'device_class': 'energy',
330+
'friendly_name': 'Test name Session energy',
331+
'state_class': <SensorStateClass.TOTAL: 'total'>,
332+
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
333+
}),
334+
'context': <ANY>,
335+
'entity_id': 'sensor.test_name_session_energy',
336+
'last_changed': <ANY>,
337+
'last_reported': <ANY>,
338+
'last_updated': <ANY>,
339+
'state': '5.0',
340+
})
341+
# ---
160342
# name: test_rpc_switch_energy_sensors[sensor.test_name_test_switch_0_energy-entry]
161343
EntityRegistryEntrySnapshot({
162344
'aliases': set({

tests/components/shelly/test_sensor.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,74 @@ async def test_rpc_switch_no_returned_energy_sensor(
16721672
assert hass.states.get("sensor.test_name_test_switch_0_returned_energy") is None
16731673

16741674

1675+
async def test_rpc_shelly_ev_sensors(
1676+
hass: HomeAssistant,
1677+
mock_rpc_device: Mock,
1678+
monkeypatch: pytest.MonkeyPatch,
1679+
entity_registry: EntityRegistry,
1680+
snapshot: SnapshotAssertion,
1681+
) -> None:
1682+
"""Test Shelly EV sensors."""
1683+
config = deepcopy(mock_rpc_device.config)
1684+
config["number:200"] = {
1685+
"name": "Charger state",
1686+
"meta": {
1687+
"ui": {
1688+
"titles": {
1689+
"charger_charging": "Charging",
1690+
"charger_end": "End",
1691+
"charger_fault": "Fault",
1692+
"charger_free": "Free",
1693+
"charger_free_fault": "Free fault",
1694+
"charger_insert": "Insert",
1695+
"charger_pause": "Pause",
1696+
"charger_wait": "Wait",
1697+
},
1698+
"view": "label",
1699+
}
1700+
},
1701+
"options": [
1702+
"charger_free",
1703+
"charger_insert",
1704+
"charger_free_fault",
1705+
"charger_wait",
1706+
"charger_charging",
1707+
"charger_pause",
1708+
"charger_end",
1709+
"charger_fault",
1710+
],
1711+
"role": "work_state",
1712+
}
1713+
config["number:201"] = {
1714+
"name": "Session energy",
1715+
"meta": {"ui": {"unit": "Wh", "view": "label"}},
1716+
"role": "energy_charge",
1717+
}
1718+
config["number:202"] = {
1719+
"name": "Session duration",
1720+
"meta": {"ui": {"unit": "min", "view": "label"}},
1721+
"role": "time_charge",
1722+
}
1723+
monkeypatch.setattr(mock_rpc_device, "config", config)
1724+
1725+
status = deepcopy(mock_rpc_device.status)
1726+
status["number:200"] = {"value": "charger_charging"}
1727+
status["number:201"] = {"value": 5000}
1728+
status["number:202"] = {"value": 60}
1729+
monkeypatch.setattr(mock_rpc_device, "status", status)
1730+
1731+
await init_integration(hass, 3)
1732+
1733+
for entity in ("charger_state", "session_energy", "session_duration"):
1734+
entity_id = f"{SENSOR_DOMAIN}.test_name_{entity}"
1735+
1736+
state = hass.states.get(entity_id)
1737+
assert state == snapshot(name=f"{entity_id}-state")
1738+
1739+
entry = entity_registry.async_get(entity_id)
1740+
assert entry == snapshot(name=f"{entity_id}-entry")
1741+
1742+
16751743
async def test_block_friendly_name_sleeping_sensor(
16761744
hass: HomeAssistant,
16771745
mock_block_device: Mock,

0 commit comments

Comments
 (0)