Skip to content

feat(issue-forms): add structured bug report template - #15

Open
jens62 wants to merge 404 commits into
mainfrom
feat/issue-templates
Open

jens62 wants to merge 404 commits into
mainfrom
feat/issue-templates

Conversation

@jens62

@jens62 jens62 commented Mar 12, 2026

Copy link
Copy Markdown
Owner
  • Implement YAML-based issue form
  • Include mandatory fields for software version and OS
  • Add drag-and-drop log upload section with confirmation checkbox

dkrioms and others added 30 commits February 21, 2026 12:06
MQTT: on_disconnect called self.reconnect() which didn't exist, causing
a silent AttributeError in the asyncio task. Added reconnect() coroutine
calling mqttc.reconnect(), wired via run_coroutine_threadsafe (matching
the pattern used by all other MQTT callbacks).

Circuit breaker: _polling_loop now tracks _consecutive_poll_failures.
After 5 consecutive failures the circuit opens — each subsequent
iteration sleeps 60s before the probe attempt instead of the normal
poll interval. Logs "Circuit open" once at threshold. On first success
after failures, logs "Poll recovered", resets the counter, and resets
_identification_fetched so identification is re-fetched in case the
device was power-cycled during the outage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nges

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
configuration.md: add esphome_api_connection key; replace branch-diff
  table with startup config-logging note
on-demand-ble.md: expand timing fields (ESP32/BLE/cached); add circuit
  breaker section; fix outdated note about _connect_ms including ident
mqtt.md: add centralDevice/timings, error JSON format, esphomeProxy/*
  topics (connected, error, control/connect|disconnect, config/apiConnection)
esphome.md: add esphome_api_connection config; document persistent vs
  on-demand API connection modes and invalid combination rule
webapp.md: document ESP32 proxy panel, updated timing field labels,
  BLE: Reconnect/Disconnect button labels, — (cached) query display
CLAUDE.md: reframe branch summary as merged feature summary

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…E scan

In persistent ESPHome API mode, disconnect_ble_only() intentionally skips
calling _esphome_unsub_adv() to avoid the ESP32 closing the TCP connection.
This left the old subscription active on the ESP32, causing it to log
"Only one API subscription is allowed at a time" on the next request.

Fix: call the stored _esphome_unsub_adv at the START of _connect_via_esphome(),
before any BLE connection is active. If the unsubscribe causes TCP to close,
_ensure_esphome_api_connected() reconnects it immediately after.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ly()

The previous fix called _esphome_unsub_adv() at the start of
_connect_via_esphome(), but disconnect_ble_only() was nulling the reference
first — so the cleanup block was always skipped (dead code on the happy path).

Fix: stop nulling _esphome_unsub_adv in disconnect_ble_only(). The reference
is now kept alive so _connect_via_esphome() can actually call it at the start
of the next request, before any BLE connection is active.

If the unsubscribe causes the ESP32 to close TCP, _ensure_esphome_api_connected()
reconnects it. Whether TCP closes is firmware-dependent and observable in logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AquaCleanBaseClient logs the raw error message but the error code mapping
(e.g. E7002) only reaches MQTT/SSE — never the Python log file. Noted as
a TODO item to add logger.error with the error code at the mapping point
in _on_demand_inner's finally block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After 6 hours of debugging the 'Only one API subscription is allowed at a time'
ESP32 firmware error, persistent mode is removed entirely. on-demand TCP per
request is proven stable for hours in production.

To restore persistent mode: git checkout esphome-persistent-api

Removes: esphome_api_connection config option, _ensure_esphome_api_connected(),
disconnect_ble_only(), disconnect_esp32_api(), _get_esphome_connector(),
set_esphome_api_connection(), _on_mqtt_set_esphome_api_connection(),
ESP32 mode toggle and connect/disconnect UI buttons, MQTT topic
esphomeProxy/config/apiConnection, REST endpoint /config/esphome-api-connection,
close_api param from ESPHomeAPIClient.disconnect().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove esphome_api_connection option, ESP32 API connection modes table,
and _ensure_esphome_api_connected() mentions from all documentation.
Update known issues sections to reflect on-demand-only architecture.
Note esphome-persistent-api git tag for reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The handler was removed in e002e24 but the subscribe call was missed,
causing the broker to deliver messages to a topic with no handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Calling unsub_adv() before bluetooth_device_connect() sends
UnsubscribeBluetoothLEAdvertisementsRequest, which clears api_connection_
on the ESP32. The ESP32 then disconnects any BLE client in CONNECTING state
immediately → "Disconnect before connected, disconnect scheduled" (reason 0x16).

Fix: keep the advertisement subscription alive during BLE connect; call
unsub_adv() only after await self.client.connect() returns successfully (or
after all attempts are exhausted).

Probe script esphome/esphome-aioesphomeapi-probe-v3.py confirmed all 4
has_cache × address_type combinations work when the subscription is held
across the connect. CLAUDE.md updated with findings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lifetime

unsub_adv() is synchronous — it only queues the UnsubscribeBluetoothLEAdvertisementsRequest
frame in aioesphomeapi's send buffer. The frame is flushed at the next await (e.g. inside
_post_connect()). Calling unsub_adv() at any point while BLE is active causes the ESP32
to disconnect the BLE client; the symptom depends on timing:
  - if the frame is flushed during CONNECT phase: "Disconnect before connected" (reason 0x16)
  - if the frame is flushed after connected but during notify setup: immediate disconnect
    → BluetoothGATTNotifyResponse timeout after 10s

Fix: store the callable as self._esphome_unsub_adv instead of calling it on success.
Call it in disconnect() only after await self.client.disconnect() has torn down the BLE
link, making it safe to send the unsubscribe request to the ESP32.

CLAUDE.md trap 7 updated to reflect the actual constraint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Kali production run 2026-02-21 18:01 confirms the advertisement
unsubscribe fix works: all BLE connects succeed, all ESP32 disconnects
are reason=0x00 (clean), full data flow with no errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…home_api_connection refs

_update_esphome_proxy_state now auto-clears error_hint to "" whenever
error_code is set to "E0000". Previously a connection failure (E1002)
set the hint and subsequent successful polls never cleared it, so the
webapp kept showing "Cannot reach the ESP32 proxy" long after recovery.

CLAUDE.md: removed all dead esphome_api_connection references (the
persistent TCP feature was removed; only on-demand TCP exists). Cleaned
up device_state table, config sections, traps 7–8, feature summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds opt-in persistent TCP mode for the ESPHome BLE proxy.
When esphome_api_connection = persistent, the ESP32 API TCP connection
is kept alive between on-demand BLE requests. Only the BLE link to the
Geberit is connected/disconnected per request (~150ms TCP handshake saved
on every request after the first).

Config (config.ini [ESPHOME]):
  esphome_api_connection = persistent | on-demand (default: on-demand)

Implementation:
- ESPHomeAPIClient.disconnect(close_api=False): skip TCP teardown
- BluetoothLeConnector._ensure_esphome_api_connected(): reuse or reconnect
- BluetoothLeConnector.disconnect_ble_only(): BLE down, TCP stays alive
- ApiMode._get_esphome_connector(): cached connector for persistent mode
- Runtime toggle: POST /config/esphome-api-connection, MQTT esphomeProxy/config/apiConnection
- Webui: ESP32: Switch to Persistent / On-Demand button

Naming conventions documented in CLAUDE.md and .clauderc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt mode

In persistent_api mode the ESP32 TCP connection stays alive after each
BLE disconnect. The proxy panel was incorrectly set to connected=False
in the finally block, making the webapp show DISCONNECTED between polls
even though ESP32: 0 ms proved the TCP was being reused.

Fix: only set connected=False in on-demand mode (TCP truly torn down).
In persistent mode, keep connected=True so the webapp reflects reality.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… data_received_handlers

Each poll in persistent mode was creating a new AquaCleanBaseClient which registers
frame_service.process_data on the shared connector's data_received_handlers. After N polls,
N handlers were registered, causing N 'receive complete' events per BLE notification and
ever-slower queries. Fix: create the client once alongside the connector in
_get_esphome_connector() and reuse it across polls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t client caching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndpoints

Previously _on_demand_inner re-raised BLE/ESPHome exceptions, which propagated
to FastAPI as unhandled HTTP 500. Now exceptions are stored, cleanup and BLE
status update run in finally, then _http_error(503) raises a structured
HTTPException. Also adds BLEPeripheralTimeoutError as an explicit case mapped
to E0003 (consistent with ServiceMode handling).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nses

- configuration.md: add esphome_api_connection option to [ESPHOME] section
- rest-api.md: add POST /config/esphome-api-connection, /esphome/connect,
  /esphome/disconnect; document split timing fields; document HTTP 503 error format
- mqtt.md: add esphomeProxy/enabled outbound topic; add esphomeProxy/config/apiConnection inbound topic
- esphome.md: replace outdated "fresh TCP per request" paragraph with
  esphome_api_connection table and runtime toggle info

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When aioesphomeapi drops the TCP connection after its 90-second ping
timeout, it sets api._connection = None internally but the cached
self._esphome_api reference stays non-None.  _ensure_esphome_api_connected()
was returning the dead client unconditionally, causing every subsequent
poll to fail with E7002 "Not connected to aquaclean-proxy" until the
app was restarted.

Add a liveness check via getattr(api, '_connection', None): if None,
log a warning, clear the cached client, and fall through to open a
fresh TCP connection.  The normal "Reusing existing ESP32 API connection"
fast path is preserved when the connection is still alive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…meout

Documents the root cause (aioesphomeapi sets _connection=None after 90s
ping timeout, cached self._esphome_api stays non-None) and the fix
(liveness check via getattr before reusing the cached client).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nd patch notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add pyproject.toml with all dependencies including haggis from private repo
- Fix all local imports to be fully qualified (aquaclean_console_app.xxx)
  across 20+ files in aquaclean_core, bluetooth_le, and top-level modules
- Add missing __init__.py to aquaclean_console_app/, aquaclean_core/,
  and aquaclean_core/Message/
- Add __main__.py entry point enabling python -m aquaclean_console_app
- Console script: aquaclean-bridge

Install with:
  pip install git+https://github.com/jens62/geberit-aquaclean.git

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setuptools.backends.legacy:build requires setuptools >= 68.
Debian systems with older setuptools fail with BackendUnavailable.
setuptools.build_meta is the standard backend supported since setuptools 40+
and works with PIP_NO_BUILD_ISOLATION=1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lazy (inside-function) imports were missed during the pip-installable
import qualification pass:
- bluetooth_le.LE.ESPHomeAPIClient → aquaclean_console_app.bluetooth_le...
- ErrorCodes.ErrorCode             → aquaclean_console_app.ErrorCodes...
- aquaclean_core.Api.CallClasses.GetSystemParameterList → aquaclean_console_app...

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace manual pip install with pip install git+https://github.com/...
- Add collapsible dependency table
- Show how to find config.ini after pip install
- Update run commands from 'python main.py' to 'aquaclean-bridge'
- Remove Python 3.13 haggis workaround note (handled by haggis-patched)
- Add Debian 12 / Python 3.11.2 to tested environments

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was a stub returning no errors. Now validates:
- [BLE] device_id: required, MAC address format
- [SERVICE] ble_connection: must be 'persistent' or 'on-demand'
- [ESPHOME] esphome_api_connection: must be 'persistent' or 'on-demand'
- [ESPHOME] port: integer, 1–65535
- [API] port: integer, 1–65535
- [POLL] interval: non-negative float
- [LOGGING] log_level: known log level
- [ESPHOME] log_level: known log level

Usage: aquaclean-bridge --mode cli --command check-config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
persistent is the intended mode for ESP32 proxy usage: reuses the TCP
connection between BLE requests, avoiding per-request TCP handshake and
device_info fetch overhead. on-demand was the cautious original default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jens62 and others added 27 commits March 12, 2026 09:18
- Replace empty options string with placeholder text
- Ensure 'options' input is technically "supplied" for the @main action
- Maintain github-releases strategy for final injection
- Switch from 'github-releases' to 'github-tags' to capture all versions
- Ensure pre-release and tagged versions are included in the dropdown
- Update 'options' to a single list item to satisfy validation
- Maintain 'strategy: github-tags' for dynamic injection
- Prevent the action from overwriting the entire dropdown block
- Re-add # start-release-choices and # end-release-choices
- Reset options to include 'Please select' header
- Ensure markers are present for the automation to target
- Set options to "{{...}}" to satisfy required input
- Maintain strategy: github-tags for automated version fetching
- Preserve 'Please select' and markers during workflow execution
- Add "{{...}}" placeholder to software version dropdown
- Ensure markers are present for surgical tag injection
- Replace block scalar with inline array '["{{...}}"]'
- Satisfy required input while enabling dynamic strategy
dkrioms and others added 2 commits May 6, 2026 14:43
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants