Skip to content

Commit bdb30f6

Browse files
authored
Merge branch 'master' into feature/installation-heating-settings
2 parents f8f85f9 + 7cf2c83 commit bdb30f6

11 files changed

Lines changed: 60 additions & 262 deletions

File tree

.DS_Store

-6 KB
Binary file not shown.

.github/workflows/validate.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ name: Validate
33
on:
44
push:
55
pull_request:
6-
schedule:
7-
- cron: "0 0 * * *"
86
workflow_dispatch:
7+
# No schedule. GitHub auto-disables any workflow containing one after 60
8+
# days of repository inactivity, and a disabled workflow then stops
9+
# answering push and pull_request as well — so the cron meant to catch
10+
# upstream HACS/hassfest rule changes is exactly what removes the gate.
11+
# Both adaptive-lighting validators and adaptive-cover's were lost that
12+
# way; this repo is only still active because it was pushed recently.
913

1014
jobs:
1115
hacs:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,7 @@ auth_refresh.py
9898
sync-to-ha.sh
9999
.monitor/
100100
.claude/settings.local.json
101+
102+
# Local noise and one-off probe artifacts
103+
.DS_Store
104+
boost_probe_*.json

CLAUDE.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ uv run --with bosch-thermostat-client==0.28.2 python test_easycontrol_connection
2929
pip install bosch-thermostat-client==0.28.2 tzdata ruff
3030
```
3131

32-
CI runs ruff + pytest on Python 3.12 and 3.13 via `.github/workflows/ci.yaml`.
32+
CI runs ruff + pytest on Python **3.13 only** (`.github/workflows/ci.yaml`;
33+
3.12 was dropped in `e9833db`), with a **70% coverage floor** via
34+
`--cov-fail-under=70`. Ruff is configured `select = ["E4","E7","E9","F"]`, so
35+
async and blocking-call rules are **not** enforced — don't assume a clean ruff
36+
run means no blocking I/O in the event loop.
3337

3438
## Architecture Overview
3539

@@ -47,7 +51,14 @@ This is a Home Assistant custom component (`domain: bosch`) that integrates Bosc
4751
**Path 2 — POINTTAPI (cloud JSON API, EasyControl only):**
4852
- Custom HTTP client (`pointtapi_client.py`) hitting `https://pointt-api.bosch-thermotechnology.com/pointt-api/api/v1/gateways/{device_id}/resource/`
4953
- OAuth with PKCE (`pointtapi_oauth.py`) — access + refresh tokens stored in config entry data; `ensure_valid_token()` auto-refreshes before every request
50-
- `PoinTTAPIDataUpdateCoordinator` (`pointtapi_coordinator.py`) polls ~6 root paths + one level of references every 60s, caching results as a `{path: response}` dict in `coordinator.data`
54+
- `PoinTTAPIDataUpdateCoordinator` (`pointtapi_coordinator.py`) runs on a 60s
55+
`update_interval` and issues **bulk POSTs**, not per-path GETs. The discovered
56+
path set is split into fast and slow tiers: `SLOW_RESOURCE_PREFIXES`
57+
(`/gateway`, `/energy`, `/solarCircuits`, `/devices`, `/programs`,
58+
`/system/appliance`) refresh every `SLOW_RESOURCE_REFRESH_INTERVAL` (5 min)
59+
and are served from `_slow_data` in between; hourly energy history has its own
60+
30-min interval; the reference walk re-discovers daily (`REDISCOVERY_INTERVAL`).
61+
Results cache as a `{path: response}` dict in `coordinator.data`
5162
- Entities are `CoordinatorEntity` subclasses (`pointtapi_entities.py`) that read from `coordinator.data` in `_handle_coordinator_update()`
5263
- 401/403 raises `ConfigEntryAuthFailed` → triggers the reauth flow in `config_flow.py` without deleting the entry
5364

@@ -59,7 +70,11 @@ This is a Home Assistant custom component (`domain: bosch`) that integrates Bosc
5970

6071
### Config Flow (`config_flow.py`)
6172

62-
Steps for POINTTAPI: `choose_type``easycontrol_protocol``pointtapi_device_id` (serial without dashes) → `pointtapi_oauth_open` (show login URL) → `pointtapi_oauth` (paste callback URL) → exchange code for tokens → `create_entry`.
73+
Steps for POINTTAPI: `user``easycontrol_protocol``pointtapi_oauth_open`
74+
(show login URL) → `pointtapi_oauth` (paste callback URL) → `pointtapi_gateway`
75+
(auto-discovery: the token lists the account's gateways) → `create_entry`.
76+
`pointtapi_device_id` (serial without dashes) is the **fallback** when discovery
77+
finds nothing, not the first step. There is no `choose_type` step any more.
6378

6479
Steps for XMPP/HTTP: `choose_type` → protocol → credentials form → `configure_gateway()` in executor (validates connection, extracts UUID) → `create_entry`.
6580

@@ -73,7 +88,19 @@ Tokens and all credentials are stored in `entry.data`, not `entry.options`.
7388
- `REGULAR``BoschSensor`
7489
- `"notification"``NotificationSensor`
7590
- Circuit sensors (DHW, HC, SC, ZN, DV) → `CircuitSensor`
76-
- POINTTAPI → `BoschPoinTTAPISensorEntity` (6 curated paths: outdoor temp, humidity, valve position, pressure, RSSI, update state)
91+
- POINTTAPI → `BoschPoinTTAPISensorEntity` (~50 descriptions; see the entity
92+
matrix in README.md, which is the maintained list)
93+
94+
### OpenSpec scope
95+
96+
`openspec/specs/` covers the **POINTTAPI path only**. The XMPP/HTTP path is
97+
inherited from pszafer's upstream component and is deliberately unspecified —
98+
a spec there would be archaeology, not design. Absence of `NEFIT`/`IVT` specs
99+
is a boundary, not drift.
100+
101+
Note also that `config_flow.async_step_user` hardcodes `EASYCONTROL`, so the
102+
`NEFIT`/`IVT`/`IVT_MBLAN` device types listed above cannot currently be added
103+
through the UI even though their sensor routing still exists.
77104

78105
### Constants (`const.py`)
79106

@@ -90,6 +117,11 @@ All platform signals, circuit names (`DHW`, `HC`, `SC`, `ZN`, `DV`), protocol id
90117

91118
## Version & Dependency Notes
92119

93-
- Current integration version: `1.0.0` (in `manifest.json` — bump this on releases)
120+
- Current integration version: see `manifest.json` (`version`). Bump it on releases;
121+
don't restate it here — this file said `1.0.0` for five minor releases.
94122
- `bosch-thermostat-client` is not used by and does not support POINTTAPI; the POINTTAPI path is entirely custom
123+
- `.gitmodules` pulls in `deric-bosch-client`, a vendored fork of the upstream
124+
client kept for reference when diffing protocol behaviour. Nothing in
125+
`custom_components/` imports it; a plain `git clone` without `--recursive` is
126+
fine.
95127
- `iot_class` is `cloud_polling` (covers both paths; XMPP is technically local but the manifest reflects the primary EasyControl cloud use case)

boost_probe_20260605_135612.json

Lines changed: 0 additions & 250 deletions
This file was deleted.
-836 KB
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ I've been working on a full cloud-API integration path for EasyControl devices u
88

99
- **`pointtapi_client.py`** — HTTP client for the POINTTAPI REST API (GET/PUT with auto token refresh)
1010
- **`pointtapi_oauth.py`** — OAuth2 with PKCE flow against Bosch SingleKey ID; token exchange + refresh
11-
- **`pointtapi_coordinator.py`**`DataUpdateCoordinator` that polls ~6 root paths + references every 60s, with `asyncio.timeout(120)` and proper error handling
11+
- **`pointtapi_coordinator.py`**`DataUpdateCoordinator` that bulk-POSTs a discovered path set on a 60s interval, with fast/slow tiers, with `asyncio.timeout(120)` and proper error handling
1212
- **`pointtapi_entities.py`** — All POINTTAPI entities (see below)
1313
- **`diagnostics.py`**`async_get_config_entry_diagnostics` with credential redaction
1414

0 commit comments

Comments
 (0)