From 6c1bf31a3cf0bdbddca64f2e15d93d76180b7c3d Mon Sep 17 00:00:00 2001 From: Willem-Jan van Rootselaar Date: Mon, 12 Jan 2026 15:44:03 +0100 Subject: [PATCH] Bump python-bsblan to version 4.1.0 (#160676) --- homeassistant/components/bsblan/climate.py | 13 ++++++---- homeassistant/components/bsblan/entity.py | 6 ++++- homeassistant/components/bsblan/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bsblan/test_climate.py | 24 +++++++++++++++++++ 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/bsblan/climate.py b/homeassistant/components/bsblan/climate.py index 71f2776d951..44767ff7bff 100644 --- a/homeassistant/components/bsblan/climate.py +++ b/homeassistant/components/bsblan/climate.py @@ -111,11 +111,17 @@ class BSBLANClimate(BSBLanEntity, ClimateEntity): return None return self.coordinator.data.state.target_temperature.value + @property + def _hvac_mode_value(self) -> int | str | None: + """Return the raw hvac_mode value from the coordinator.""" + if (hvac_mode := self.coordinator.data.state.hvac_mode) is None: + return None + return hvac_mode.value + @property def hvac_mode(self) -> HVACMode | None: """Return hvac operation ie. heat, cool mode.""" - hvac_mode_value = self.coordinator.data.state.hvac_mode.value - if hvac_mode_value is None: + if (hvac_mode_value := self._hvac_mode_value) is None: return None # BSB-Lan returns integer values: 0=off, 1=auto, 2=eco, 3=heat if isinstance(hvac_mode_value, int): @@ -125,9 +131,8 @@ class BSBLANClimate(BSBLanEntity, ClimateEntity): @property def preset_mode(self) -> str | None: """Return the current preset mode.""" - hvac_mode_value = self.coordinator.data.state.hvac_mode.value # BSB-Lan mode 2 is eco/reduced mode - if hvac_mode_value == 2: + if self._hvac_mode_value == 2: return PRESET_ECO return PRESET_NONE diff --git a/homeassistant/components/bsblan/entity.py b/homeassistant/components/bsblan/entity.py index b2effc9f3f4..5f5203ef8d0 100644 --- a/homeassistant/components/bsblan/entity.py +++ b/homeassistant/components/bsblan/entity.py @@ -29,7 +29,11 @@ class BSBLanEntityBase[_T: BSBLanCoordinator](CoordinatorEntity[_T]): connections={(CONNECTION_NETWORK_MAC, format_mac(mac))}, name=data.device.name, manufacturer="BSBLAN Inc.", - model=data.info.device_identification.value, + model=( + data.info.device_identification.value + if data.info.device_identification + else None + ), sw_version=data.device.version, configuration_url=f"http://{host}", ) diff --git a/homeassistant/components/bsblan/manifest.json b/homeassistant/components/bsblan/manifest.json index 4545e601719..fe581ef062f 100644 --- a/homeassistant/components/bsblan/manifest.json +++ b/homeassistant/components/bsblan/manifest.json @@ -7,7 +7,7 @@ "integration_type": "device", "iot_class": "local_polling", "loggers": ["bsblan"], - "requirements": ["python-bsblan==3.1.6"], + "requirements": ["python-bsblan==4.1.0"], "zeroconf": [ { "name": "bsb-lan*", diff --git a/requirements_all.txt b/requirements_all.txt index 0fd42fc11e0..f53ecd1617b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2481,7 +2481,7 @@ python-awair==0.2.5 python-blockchain-api==0.0.2 # homeassistant.components.bsblan -python-bsblan==3.1.6 +python-bsblan==4.1.0 # homeassistant.components.citybikes python-citybikes==0.3.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8b98107b353..560329e11b8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2098,7 +2098,7 @@ python-MotionMount==2.3.0 python-awair==0.2.5 # homeassistant.components.bsblan -python-bsblan==3.1.6 +python-bsblan==4.1.0 # homeassistant.components.ecobee python-ecobee-api==0.3.2 diff --git a/tests/components/bsblan/test_climate.py b/tests/components/bsblan/test_climate.py index a57d24c1dd5..cc7cbf0aea4 100644 --- a/tests/components/bsblan/test_climate.py +++ b/tests/components/bsblan/test_climate.py @@ -159,6 +159,30 @@ async def test_climate_hvac_mode_none_value( assert state.state == "unknown" +async def test_climate_hvac_mode_object_none( + hass: HomeAssistant, + mock_bsblan: AsyncMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test climate entity when hvac_mode object itself is None.""" + await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE]) + + # Set hvac_mode to None (the object itself, not just the value) + mock_bsblan.state.return_value.hvac_mode = None + + freezer.tick(timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + # State should be unknown when hvac_mode object is None + state = hass.states.get(ENTITY_ID) + assert state is not None + assert state.state == "unknown" + # preset_mode should be "none" when hvac_mode object is None + assert state.attributes["preset_mode"] == PRESET_NONE + + async def test_climate_hvac_mode_string_fallback( hass: HomeAssistant, mock_bsblan: AsyncMock,