Skip to content

Commit 349c5fa

Browse files
danieldotnlclaude
andauthored
Don't restore unavailable/unknown sentinel as native value (#594)
RestoreEntity restored state.state unconditionally, so a shutdown while the sensor was unavailable restored the literal 'unavailable' string as the native value. Recent HA core raises ValueError from SensorEntity.state when a numeric-indicating sensor (unit but no device/state class) holds a non-numeric value, crashing entity setup on add_to_platform_finish. Guard the restore against STATE_UNAVAILABLE/STATE_UNKNOWN in the base entity so sensor, binary_sensor and button all start as unknown until the first scrape. Bump version to 9.0.3. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7959463 commit 349c5fa

3 files changed

Lines changed: 29 additions & 3 deletions

File tree

custom_components/multiscrape/entity.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
from abc import abstractmethod
66

7+
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
78
from homeassistant.core import HomeAssistant, callback
89
from homeassistant.exceptions import TemplateError
910
from homeassistant.helpers.restore_state import RestoreEntity
@@ -97,7 +98,8 @@ async def async_added_to_hass(self) -> None:
9798
if not (state := await self.async_get_last_state()):
9899
return
99100
_LOGGER.debug("%s # %s # Restoring previous state: %s", self.scraper.name, self._name, state.state)
100-
self._attr_native_value = state.state
101+
if state.state not in (STATE_UNAVAILABLE, STATE_UNKNOWN):
102+
self._attr_native_value = state.state
101103

102104
for name in self._attribute_selectors:
103105
if state.attributes.get(name) is not None:

custom_components/multiscrape/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
"iot_class": "local_polling",
99
"issue_tracker": "https://github.com/danieldotnl/ha-multiscrape/issues",
1010
"requirements": ["lxml>=4.9.1", "beautifulsoup4>=4.12.2"],
11-
"version": "9.0.2"
11+
"version": "9.0.3"
1212
}

tests/test_entity.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from unittest.mock import AsyncMock, MagicMock, patch
44

55
import pytest
6-
from homeassistant.const import CONF_NAME
6+
from homeassistant.const import CONF_NAME, STATE_UNAVAILABLE, STATE_UNKNOWN
77
from homeassistant.core import HomeAssistant, State
88
from homeassistant.helpers.template import Template
99

@@ -554,6 +554,30 @@ async def test_async_added_to_hass_restores_state_and_attributes(
554554
assert sensor._attr_extra_state_attributes["test_attr"] == "saved_value"
555555

556556

557+
@pytest.mark.integration
558+
@pytest.mark.async_test
559+
@pytest.mark.timeout(10)
560+
@pytest.mark.parametrize("sentinel", [STATE_UNAVAILABLE, STATE_UNKNOWN])
561+
async def test_async_added_to_hass_does_not_restore_sentinel_states(
562+
hass: HomeAssistant, coordinator, scraper, sentinel
563+
):
564+
"""Sentinel states must not be restored as a native value.
565+
566+
A numeric sensor (unit but no device/state class) would otherwise
567+
crash on add_to_platform_finish trying to float('unavailable').
568+
"""
569+
sensor = _create_sensor(hass, coordinator, scraper)
570+
initial_value = sensor._attr_native_value
571+
572+
mock_state = State("sensor.test", sentinel)
573+
with patch.object(sensor, "async_get_last_state", new=AsyncMock(return_value=mock_state)):
574+
await sensor.async_added_to_hass()
575+
576+
# Assert - sentinel not restored, value left at its default
577+
assert sensor._attr_native_value == initial_value
578+
assert sensor._attr_native_value not in (STATE_UNAVAILABLE, STATE_UNKNOWN)
579+
580+
557581
@pytest.mark.integration
558582
@pytest.mark.async_test
559583
@pytest.mark.timeout(10)

0 commit comments

Comments
 (0)