File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -4,8 +4,24 @@ All notable changes to this Bosch Home Assistant custom component will be docume
44
55## [ Unreleased]
66
7- Collected for 1.3.0 (pre-released as 1.3.0-beta.1). Held out of 1.2.0 because
8- none of it has been confirmed on real multi-zone hardware yet.
7+ ## [ 1.3.1] — 2026-08-17 — Quiet teardown on cloud entries
8+
9+ ### Fixed
10+ - ** No more hourly "Unable to remove unknown service bosch/debug_scan"** —
11+ ` debug_scan ` only registers on the XMPP/HTTP path, but every unload removed
12+ it unconditionally, so a POINTTAPI (cloud) entry asked Home Assistant to
13+ remove a service it never had. Harmless, but it shouted about it in
14+ @altugyurtbasi 's log every reload since April. Both services are now guarded
15+ on ` has_service ` rather than special-casing the debug one, since
16+ ` bosch.update ` has the same exposure when setup fails before registration
17+ (#7 ).
18+
19+ ## [ 1.3.0] — 2026-08-12 — Per-valve telemetry, assigned program, schedule/manual climate
20+
21+ Pre-released as 1.3.0-beta.1 and held back from 1.2.0 until someone could run
22+ it on real multi-zone hardware. @LukyHurdy1 did, on a 12-zone Czech install,
23+ and reported battery ` ok ` , signal 69%, protocol ` homematicip ` and a working
24+ warning sensor — the confirmation this release ships on (#16 ).
925
1026### Added
1127- ** Per-thermostat-valve telemetry** — each ETRV from ` /devices/list ` becomes
Original file line number Diff line number Diff line change 1111 "issue_tracker" : " https://github.com/CaseyRo/ha_bosch/issues" ,
1212 "loggers" : [" custom_components.bosch" , " bosch_thermostat_client" ],
1313 "requirements" : [" bosch-thermostat-client==0.28.2" ],
14- "version" : " 1.3.0-beta. 1" ,
14+ "version" : " 1.3.1" ,
1515 "zeroconf" : [" _buderus._tcp.local." ]
1616}
Original file line number Diff line number Diff line change @@ -192,6 +192,13 @@ async def async_handle_put(service_call: ServiceCall) -> ServiceResponse:
192192
193193
194194def async_remove_services (hass : HomeAssistant , config_entry : ConfigEntry ) -> None :
195- """Remove services."""
196- hass .services .async_remove (DOMAIN , SERVICE_DEBUG )
197- hass .services .async_remove (DOMAIN , SERVICE_UPDATE )
195+ """Remove services that this integration actually registered.
196+
197+ debug_scan only registers on the XMPP/HTTP path — a POINTTAPI entry returns
198+ from async_init() long before that call — so removing it unconditionally
199+ asks HA to drop a service that was never there and logs "Unable to remove
200+ unknown service bosch/debug_scan" on every unload and reload (#7).
201+ """
202+ for service in (SERVICE_DEBUG , SERVICE_UPDATE ):
203+ if hass .services .has_service (DOMAIN , service ):
204+ hass .services .async_remove (DOMAIN , service )
Original file line number Diff line number Diff line change 1+ """Service teardown must not remove services it never registered (#7)."""
2+ from __future__ import annotations
3+
4+ from unittest .mock import MagicMock
5+
6+ from custom_components .bosch .const import DOMAIN , SERVICE_DEBUG , SERVICE_UPDATE
7+ from custom_components .bosch .services import async_remove_services
8+
9+
10+ def _hass (registered : set [str ]) -> MagicMock :
11+ hass = MagicMock ()
12+ hass .services .has_service .side_effect = (
13+ lambda domain , service : domain == DOMAIN and service in registered
14+ )
15+ return hass
16+
17+
18+ def test_unregistered_debug_scan_is_not_removed ():
19+ """POINTTAPI entries never register debug_scan.
20+
21+ Removing it anyway makes HA log "Unable to remove unknown service
22+ bosch/debug_scan" on every unload and reload.
23+ """
24+ hass = _hass ({SERVICE_UPDATE })
25+
26+ async_remove_services (hass , MagicMock ())
27+
28+ removed = {call .args [1 ] for call in hass .services .async_remove .call_args_list }
29+ assert removed == {SERVICE_UPDATE }
30+
31+
32+ def test_registered_services_are_still_removed ():
33+ hass = _hass ({SERVICE_DEBUG , SERVICE_UPDATE })
34+
35+ async_remove_services (hass , MagicMock ())
36+
37+ removed = {call .args [1 ] for call in hass .services .async_remove .call_args_list }
38+ assert removed == {SERVICE_DEBUG , SERVICE_UPDATE }
You can’t perform that action at this time.
0 commit comments