diff --git a/homeassistant/components/teslemetry/binary_sensor.py b/homeassistant/components/teslemetry/binary_sensor.py index 3918484ea97..155d10e1b57 100644 --- a/homeassistant/components/teslemetry/binary_sensor.py +++ b/homeassistant/components/teslemetry/binary_sensor.py @@ -59,8 +59,21 @@ VEHICLE_DESCRIPTIONS: tuple[TeslemetryBinarySensorEntityDescription, ...] = ( key="state", polling=True, polling_value_fn=lambda x: x == TeslemetryState.ONLINE, + streaming_listener=lambda x, y: x.listen_State(y), device_class=BinarySensorDeviceClass.CONNECTIVITY, ), + TeslemetryBinarySensorEntityDescription( + key="cellular", + streaming_listener=lambda x, y: x.listen_Cellular(y), + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_category=EntityCategory.DIAGNOSTIC, + ), + TeslemetryBinarySensorEntityDescription( + key="wifi", + streaming_listener=lambda x, y: x.listen_Wifi(y), + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_category=EntityCategory.DIAGNOSTIC, + ), TeslemetryBinarySensorEntityDescription( key="charge_state_battery_heater_on", polling=True, diff --git a/homeassistant/components/teslemetry/icons.json b/homeassistant/components/teslemetry/icons.json index e03ac8eb41a..f7ce0ca1a60 100644 --- a/homeassistant/components/teslemetry/icons.json +++ b/homeassistant/components/teslemetry/icons.json @@ -1,6 +1,24 @@ { "entity": { "binary_sensor": { + "state": { + "state": { + "off": "mdi:sleep", + "on": "mdi:car-connected" + } + }, + "cellular": { + "state": { + "off": "mdi:signal-cellular-outline", + "on": "mdi:signal-cellular-3" + } + }, + "wifi": { + "state": { + "off": "mdi:wifi-off", + "on": "mdi:wifi" + } + }, "climate_state_is_preconditioning": { "state": { "off": "mdi:hvac-off", diff --git a/homeassistant/components/teslemetry/strings.json b/homeassistant/components/teslemetry/strings.json index 0115ed0eac8..a5bbcf34382 100644 --- a/homeassistant/components/teslemetry/strings.json +++ b/homeassistant/components/teslemetry/strings.json @@ -65,6 +65,12 @@ "state": { "name": "Status" }, + "cellular": { + "name": "Cellular" + }, + "wifi": { + "name": "Wi-Fi" + }, "storm_mode_active": { "name": "Storm watch active" }, diff --git a/tests/components/teslemetry/snapshots/test_binary_sensor.ambr b/tests/components/teslemetry/snapshots/test_binary_sensor.ambr index 1558004b1e9..f4656f75b9e 100644 --- a/tests/components/teslemetry/snapshots/test_binary_sensor.ambr +++ b/tests/components/teslemetry/snapshots/test_binary_sensor.ambr @@ -518,6 +518,54 @@ 'state': 'off', }) # --- +# name: test_binary_sensor[binary_sensor.test_cellular-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_cellular', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Cellular', + 'platform': 'teslemetry', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'cellular', + 'unique_id': 'LRW3F7EK4NC700000-cellular', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor[binary_sensor.test_cellular-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Test Cellular', + }), + 'context': , + 'entity_id': 'binary_sensor.test_cellular', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_binary_sensor[binary_sensor.test_charge_cable-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -2549,7 +2597,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'on', + 'state': 'unknown', }) # --- # name: test_binary_sensor[binary_sensor.test_supercharger_session_trip_planner-entry] @@ -2886,6 +2934,54 @@ 'state': 'off', }) # --- +# name: test_binary_sensor[binary_sensor.test_wi_fi-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_wi_fi', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wi-Fi', + 'platform': 'teslemetry', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wifi', + 'unique_id': 'LRW3F7EK4NC700000-wifi', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor[binary_sensor.test_wi_fi-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Test Wi-Fi', + }), + 'context': , + 'entity_id': 'binary_sensor.test_wi_fi', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_binary_sensor[binary_sensor.test_wiper_heat-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -3078,6 +3174,20 @@ 'state': 'off', }) # --- +# name: test_binary_sensor_refresh[binary_sensor.test_cellular-statealt] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Test Cellular', + }), + 'context': , + 'entity_id': 'binary_sensor.test_cellular', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_binary_sensor_refresh[binary_sensor.test_charge_cable-statealt] StateSnapshot({ 'attributes': ReadOnlyDict({ @@ -3647,7 +3757,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'on', + 'state': 'unknown', }) # --- # name: test_binary_sensor_refresh[binary_sensor.test_supercharger_session_trip_planner-statealt] @@ -3746,6 +3856,20 @@ 'state': 'on', }) # --- +# name: test_binary_sensor_refresh[binary_sensor.test_wi_fi-statealt] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Test Wi-Fi', + }), + 'context': , + 'entity_id': 'binary_sensor.test_wi_fi', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_binary_sensor_refresh[binary_sensor.test_wiper_heat-statealt] StateSnapshot({ 'attributes': ReadOnlyDict({ @@ -3759,6 +3883,12 @@ 'state': 'unknown', }) # --- +# name: test_binary_sensors_connectivity[binary_sensor.test_cellular-state] + 'on' +# --- +# name: test_binary_sensors_connectivity[binary_sensor.test_wi_fi-state] + 'off' +# --- # name: test_binary_sensors_streaming[binary_sensor.test_driver_seat_belt-state] 'off' # --- diff --git a/tests/components/teslemetry/test_binary_sensor.py b/tests/components/teslemetry/test_binary_sensor.py index 456449bb2ca..0f5588fe323 100644 --- a/tests/components/teslemetry/test_binary_sensor.py +++ b/tests/components/teslemetry/test_binary_sensor.py @@ -108,3 +108,45 @@ async def test_binary_sensors_streaming( ): state = hass.states.get(entity_id) assert state.state == snapshot(name=f"{entity_id}-state") + + +async def test_binary_sensors_connectivity( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + freezer: FrozenDateTimeFactory, + mock_vehicle_data: AsyncMock, + mock_add_listener: AsyncMock, +) -> None: + """Tests that the binary sensor entities with streaming are correct.""" + + freezer.move_to("2024-01-01 00:00:00+00:00") + + await setup_platform(hass, [Platform.BINARY_SENSOR]) + + # Stream update + mock_add_listener.send( + { + "vin": VEHICLE_DATA_ALT["response"]["vin"], + "status": "CONNECTED", + "networkInterface": "cellular", + "createdAt": "2024-10-04T10:45:17.537Z", + } + ) + mock_add_listener.send( + { + "vin": VEHICLE_DATA_ALT["response"]["vin"], + "status": "DISCONNECTED", + "networkInterface": "wifi", + "createdAt": "2024-10-04T10:45:17.537Z", + } + ) + await hass.async_block_till_done() + + # Assert the entities restored their values + for entity_id in ( + "binary_sensor.test_cellular", + "binary_sensor.test_wi_fi", + ): + state = hass.states.get(entity_id) + assert state.state == snapshot(name=f"{entity_id}-state") diff --git a/tests/components/teslemetry/test_init.py b/tests/components/teslemetry/test_init.py index 9d19b2bdae3..d2ef5c38893 100644 --- a/tests/components/teslemetry/test_init.py +++ b/tests/components/teslemetry/test_init.py @@ -14,7 +14,7 @@ from tesla_fleet_api.exceptions import ( from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL from homeassistant.components.teslemetry.models import TeslemetryData from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import STATE_OFF, STATE_ON, Platform +from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr @@ -132,7 +132,7 @@ async def test_vehicle_stream( mock_add_listener.assert_called() state = hass.states.get("binary_sensor.test_status") - assert state.state == STATE_ON + assert state.state == STATE_UNKNOWN state = hass.states.get("binary_sensor.test_user_present") assert state.state == STATE_OFF @@ -141,11 +141,15 @@ async def test_vehicle_stream( { "vin": VEHICLE_DATA_ALT["response"]["vin"], "vehicle_data": VEHICLE_DATA_ALT["response"], + "state": "online", "createdAt": "2024-10-04T10:45:17.537Z", } ) await hass.async_block_till_done() + state = hass.states.get("binary_sensor.test_status") + assert state.state == STATE_ON + state = hass.states.get("binary_sensor.test_user_present") assert state.state == STATE_ON