From 6220953eb60f045d36a9f2d9cb09d2909d359ebe Mon Sep 17 00:00:00 2001 From: kohai-ut Date: Mon, 24 Aug 2026 01:44:49 -0600 Subject: [PATCH] Fix wirelesstag dry/wet binary sensors never updating (#172592) Co-authored-by: Claude Opus 4.8 (1M context) --- .../components/wirelesstag/binary_sensor.py | 5 +- .../wirelesstag/test_binary_sensor.py | 88 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/components/wirelesstag/test_binary_sensor.py diff --git a/homeassistant/components/wirelesstag/binary_sensor.py b/homeassistant/components/wirelesstag/binary_sensor.py index b35117991690..f95ea0aa7e67 100644 --- a/homeassistant/components/wirelesstag/binary_sensor.py +++ b/homeassistant/components/wirelesstag/binary_sensor.py @@ -85,7 +85,10 @@ class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorEntity): async def async_added_to_hass(self) -> None: """Register callbacks.""" tag_id = self.tag_id - event_type = self.device_class + # Use the raw event type, not the device class: the push side dispatches + # with the library's event type, and device_class is None for some + # events (e.g. dry/wet), which would never match the dispatched signal. + event_type = self._sensor_type mac = self.tag_manager_mac self.async_on_remove( async_dispatcher_connect( diff --git a/tests/components/wirelesstag/test_binary_sensor.py b/tests/components/wirelesstag/test_binary_sensor.py new file mode 100644 index 000000000000..4394822b05ca --- /dev/null +++ b/tests/components/wirelesstag/test_binary_sensor.py @@ -0,0 +1,88 @@ +"""Tests for the Wireless Sensor Tags binary sensor platform.""" + +from unittest.mock import MagicMock, patch + +import pytest + +from homeassistant.components.wirelesstag.const import SIGNAL_BINARY_EVENT_UPDATE +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.setup import async_setup_component + +CONFIG = { + "wirelesstag": {"username": "foo@bar.com", "password": "secret"}, + "binary_sensor": { + "platform": "wirelesstag", + "monitored_conditions": ["motion", "dry", "wet"], + }, +} + +UUID = "00000000-0000-0000-0000-000000000001" +TAG_ID = 1 +MAC = "ABCDEF012345" + +EVENT_NAMES = {"motion": "Motion", "dry": "Too dry", "wet": "Too wet"} + + +def _mock_tag() -> MagicMock: + """Return a mocked wirelesstagpy SensorTag with motion/dry/wet events off.""" + tag = MagicMock() + tag.uuid = UUID + tag.tag_id = TAG_ID + tag.tag_manager_mac = MAC + tag.name = "Bedroom" + tag.is_alive = True + tag.supported_binary_events_types = list(EVENT_NAMES) + tag.battery_remaining = 0.85 + tag.battery_volts = 3.0 + tag.signal_strength = -60 + tag.is_in_range = True + tag.power_consumption = 1.5 + + events = {} + for event_type, human_readable_name in EVENT_NAMES.items(): + event = MagicMock() + event.human_readable_name = human_readable_name + event.is_state_on = False + events[event_type] = event + tag.event.__getitem__.side_effect = events.__getitem__ + return tag + + +@pytest.mark.parametrize("event_type", ["motion", "dry", "wet"]) +async def test_binary_sensor_receives_push_update( + hass: HomeAssistant, + event_type: str, +) -> None: + """Test binary sensors update from push events for every event type. + + The push side dispatches the signal keyed by the library event type, so the + entity must subscribe with the same key. Events whose device_class is None + (dry/wet) previously subscribed with "None" and never updated. + """ + tag = _mock_tag() + with patch("homeassistant.components.wirelesstag.WirelessTags") as mock_api_class: + mock_api = mock_api_class.return_value + mock_api.load_tags.return_value = {tag.uuid: tag} + + assert await async_setup_component(hass, "wirelesstag", CONFIG) + await hass.async_block_till_done() + assert await async_setup_component(hass, "binary_sensor", CONFIG) + await hass.async_block_till_done() + + entity_id = er.async_get(hass).async_get_entity_id( + "binary_sensor", "wirelesstag", f"{UUID}_{event_type}" + ) + assert entity_id is not None + assert hass.states.get(entity_id).state == STATE_OFF + + # Simulate a push notification turning the event on. + tag.event[event_type].is_state_on = True + async_dispatcher_send( + hass, SIGNAL_BINARY_EVENT_UPDATE.format(TAG_ID, event_type, MAC), tag + ) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_ON