Skip to content

Commit 6dee633

Browse files
authored
Merge pull request #368 from Olen/fix/threshold-unavailable-crash
fix: handle unavailable threshold entities in _check_threshold
2 parents 5a3ca79 + 4f2c89a commit 6dee633

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-2
lines changed

custom_components/plant/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,10 @@ def _check_threshold(self, value, min_entity, max_entity, current_status):
989989
When already in a problem state, require the value to cross back
990990
by a margin (hysteresis band) before clearing.
991991
"""
992+
if min_entity.state in (STATE_UNKNOWN, STATE_UNAVAILABLE):
993+
return current_status
994+
if max_entity.state in (STATE_UNKNOWN, STATE_UNAVAILABLE):
995+
return current_status
992996
min_val = float(min_entity.state)
993997
max_val = float(max_entity.state)
994998
band = (max_val - min_val) * HYSTERESIS_FRACTION

custom_components/plant/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@
1717
"requirements": [
1818
"async-timeout>=4.0.2"
1919
],
20-
"version": "2026.2.1-beta7"
20+
"version": "2026.2.1-beta8"
2121
}

custom_components/plant/number.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ async def async_added_to_hass(self) -> None:
276276
state = await self.async_get_last_number_data()
277277
if not state:
278278
return
279-
self._attr_native_value = state.native_value
279+
if state.native_value is not None:
280+
self._attr_native_value = state.native_value
280281
self._attr_native_unit_of_measurement = state.native_unit_of_measurement
281282
# We track changes to our own state so we can update ourselves if state is changed
282283
# from the UI or by other means

tests/test_init.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,67 @@ async def test_conductivity_hysteresis(
17421742
await update_plant_sensors(hass, init_integration.entry_id)
17431743
assert plant.conductivity_status == STATE_OK
17441744

1745+
async def test_threshold_unavailable_preserves_status(
1746+
self,
1747+
hass: HomeAssistant,
1748+
init_integration: MockConfigEntry,
1749+
) -> None:
1750+
"""Test that unavailable threshold entities don't crash _check_threshold.
1751+
1752+
When a threshold number entity has state 'unavailable', the plant
1753+
should keep its current status rather than raising ValueError.
1754+
"""
1755+
plant = hass.data[DOMAIN][init_integration.entry_id][ATTR_PLANT]
1756+
1757+
# First establish a known state
1758+
await set_external_sensor_states(
1759+
hass,
1760+
temperature=25.0,
1761+
moisture=40.0,
1762+
conductivity=1000.0,
1763+
illuminance=5000.0,
1764+
humidity=40.0,
1765+
)
1766+
await update_plant_sensors(hass, init_integration.entry_id)
1767+
assert plant.state == STATE_OK
1768+
assert plant.moisture_status == STATE_OK
1769+
1770+
# Make the min_moisture threshold entity unavailable
1771+
hass.states.async_set(plant.min_moisture.entity_id, STATE_UNAVAILABLE)
1772+
await hass.async_block_till_done()
1773+
1774+
# Update should not crash — moisture_status should be preserved
1775+
await update_plant_sensors(hass, init_integration.entry_id)
1776+
assert plant.moisture_status == STATE_OK
1777+
1778+
async def test_threshold_unknown_preserves_status(
1779+
self,
1780+
hass: HomeAssistant,
1781+
init_integration: MockConfigEntry,
1782+
) -> None:
1783+
"""Test that unknown threshold entities don't crash _check_threshold."""
1784+
plant = hass.data[DOMAIN][init_integration.entry_id][ATTR_PLANT]
1785+
1786+
# Establish a LOW moisture state
1787+
await set_external_sensor_states(
1788+
hass,
1789+
temperature=25.0,
1790+
moisture=5.0, # Below min of 20
1791+
conductivity=1000.0,
1792+
illuminance=5000.0,
1793+
humidity=40.0,
1794+
)
1795+
await update_plant_sensors(hass, init_integration.entry_id)
1796+
assert plant.moisture_status == STATE_LOW
1797+
1798+
# Make the max_moisture threshold entity unknown
1799+
hass.states.async_set(plant.max_moisture.entity_id, STATE_UNKNOWN)
1800+
await hass.async_block_till_done()
1801+
1802+
# Update should not crash — moisture_status should stay LOW
1803+
await update_plant_sensors(hass, init_integration.entry_id)
1804+
assert plant.moisture_status == STATE_LOW
1805+
17451806

17461807
class TestYamlImport:
17471808
"""Tests for YAML configuration import (async_setup)."""

0 commit comments

Comments
 (0)