From 65a259b9df85cdeb3ece5e8b0e3c55ce89941eba Mon Sep 17 00:00:00 2001 From: Paul Tarjan Date: Wed, 31 Dec 2025 04:52:41 -1000 Subject: [PATCH] Fix Hikvision thread safety issue when calling async_write_ha_state (#160027) --- homeassistant/components/hikvision/binary_sensor.py | 11 +++++++---- tests/components/hikvision/test_binary_sensor.py | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/hikvision/binary_sensor.py b/homeassistant/components/hikvision/binary_sensor.py index f0917c769bfe..6a354458ed32 100644 --- a/homeassistant/components/hikvision/binary_sensor.py +++ b/homeassistant/components/hikvision/binary_sensor.py @@ -24,7 +24,7 @@ from homeassistant.const import ( CONF_SSL, CONF_USERNAME, ) -from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback +from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers import config_validation as cv, issue_registry as ir from homeassistant.helpers.device_registry import DeviceInfo @@ -227,7 +227,10 @@ class HikvisionBinarySensor(BinarySensorEntity): # Register callback with pyhik self._camera.add_update_callback(self._update_callback, self._callback_id) - @callback def _update_callback(self, msg: str) -> None: - """Update the sensor's state when callback is triggered.""" - self.async_write_ha_state() + """Update the sensor's state when callback is triggered. + + This is called from pyhik's event stream thread, so we use + schedule_update_ha_state which is thread-safe. + """ + self.schedule_update_ha_state() diff --git a/tests/components/hikvision/test_binary_sensor.py b/tests/components/hikvision/test_binary_sensor.py index 5eff8508957c..7c659c1c11a7 100644 --- a/tests/components/hikvision/test_binary_sensor.py +++ b/tests/components/hikvision/test_binary_sensor.py @@ -294,6 +294,10 @@ async def test_binary_sensor_update_callback( callback_func = add_callback_call[0][0] callback_func("motion detected") + # Wait for the event loop to process the scheduled state update + # (callback uses call_soon_threadsafe to schedule update in event loop) + await hass.async_block_till_done() + # Verify state was updated state = hass.states.get("binary_sensor.front_camera_motion") assert state is not None