1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Fix nobo_hub KeyError when a zone or component is removed (#169378)

This commit is contained in:
Øyvind Matheson Wergeland
2026-04-28 20:30:53 +02:00
committed by GitHub
parent d62f136c58
commit 8fd3d0bb44
6 changed files with 53 additions and 3 deletions
@@ -145,6 +145,11 @@ class NoboZone(NoboBaseEntity, ClimateEntity):
@callback
def _read_state(self) -> None:
"""Read the current state from the hub. These are only local calls."""
if self._id not in self._nobo.zones:
# Zone removed via the Nobø app; mark unavailable.
self._attr_available = False
return
self._attr_available = True
state = self._nobo.get_current_zone_mode(self._id, dt_util.now())
self._attr_hvac_mode = HVACMode.AUTO
self._attr_preset_mode = PRESET_NONE
@@ -94,6 +94,7 @@ class NoboGlobalSelector(NoboBaseEntity, SelectEntity):
@callback
def _read_state(self) -> None:
"""Read the current state from the hub. These are only local calls."""
for override in self._nobo.overrides.values():
if override["target_type"] == nobo.API.OVERRIDE_TARGET_GLOBAL:
self._attr_current_option = self._modes[override["mode"]]
@@ -136,6 +137,12 @@ class NoboProfileSelector(NoboBaseEntity, SelectEntity):
@callback
def _read_state(self) -> None:
"""Read the current state from the hub. These are only local calls."""
if self._id not in self._nobo.zones:
# Zone removed via the Nobø app; mark unavailable.
self._attr_available = False
return
self._attr_available = True
self._profiles = {
profile["week_profile_id"]: profile["name"].replace("\xa0", " ")
for profile in self._nobo.week_profiles.values()
@@ -71,6 +71,11 @@ class NoboTemperatureSensor(NoboBaseEntity, SensorEntity):
@callback
def _read_state(self) -> None:
"""Read the current state from the hub. This is a local call."""
if self._id not in self._nobo.components:
# Component removed via the Nobø app; mark unavailable.
self._attr_available = False
return
self._attr_available = True
value = self._nobo.get_current_component_temperature(self._id)
if value is None:
self._attr_native_value = None
+12 -1
View File
@@ -26,7 +26,7 @@ from homeassistant.components.nobo_hub.const import (
CONF_OVERRIDE_TYPE,
OVERRIDE_TYPE_NOW,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@@ -186,6 +186,17 @@ async def test_set_preset_with_override_type_now(
)
@pytest.mark.usefixtures("init_integration")
async def test_zone_removed_marks_unavailable(
hass: HomeAssistant,
mock_nobo_hub: MagicMock,
) -> None:
"""A zone removed via the Nobø app must not crash and goes unavailable."""
mock_nobo_hub.zones.pop("1")
await fire_hub_update(hass, mock_nobo_hub)
assert hass.states.get(CLIMATE_ENTITY).state == STATE_UNAVAILABLE
@pytest.mark.usefixtures("init_integration")
async def test_set_temperature_updates_zone(
hass: HomeAssistant,
+12 -1
View File
@@ -11,7 +11,7 @@ from homeassistant.components.select import (
DOMAIN as SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
@@ -136,3 +136,14 @@ async def test_week_profile_push_update(
mock_nobo_hub.zones["1"]["week_profile_id"] = "1"
await fire_hub_update(hass, mock_nobo_hub)
assert hass.states.get(PROFILE_ENTITY).state == "Weekend"
@pytest.mark.usefixtures("init_integration")
async def test_zone_removed_marks_week_profile_unavailable(
hass: HomeAssistant,
mock_nobo_hub: MagicMock,
) -> None:
"""A zone removed via the Nobø app must not crash and goes unavailable."""
mock_nobo_hub.zones.pop("1")
await fire_hub_update(hass, mock_nobo_hub)
assert hass.states.get(PROFILE_ENTITY).state == STATE_UNAVAILABLE
+12 -1
View File
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@@ -55,3 +55,14 @@ async def test_temperature_push_update(
mock_nobo_hub.get_current_component_temperature.return_value = "19.3"
await fire_hub_update(hass, mock_nobo_hub)
assert hass.states.get(TEMPERATURE_ENTITY).state == "19.3"
@pytest.mark.usefixtures("init_integration")
async def test_component_removed_marks_unavailable(
hass: HomeAssistant,
mock_nobo_hub: MagicMock,
) -> None:
"""A component removed via the Nobø app must not crash and goes unavailable."""
mock_nobo_hub.components.pop("200000059091")
await fire_hub_update(hass, mock_nobo_hub)
assert hass.states.get(TEMPERATURE_ENTITY).state == STATE_UNAVAILABLE