Add standalone HomeKit accessory support (e.g. Smart AC Control V3+)#40
Conversation
Extends TadoLocal to pair with standalone HomeKit accessories alongside the bridge. Each accessory gets its own pairing, but all devices appear in the same REST API and SSE stream. Changes: - bridge.py: add pair_or_load_accessory() for direct IP pairing - state.py: detect SU serial prefix as smart_ac_control device type - api.py: accept extra_pairings in initialize(), route get/put/subscribe calls to the correct pairing via aid_to_pairing mapping - __main__.py: add --accessory-ip and --accessory-pin CLI arguments - README.md, DESIGN.md: document the new feature
Standalone accessories may emit events with char_type=None for characteristics not in our tracking map. Guard against this in _handle_window_open_detection.
…crash - state.py: Default window to 0 in _save_to_history for devices without window sensors (Smart AC Control), fixing NOT NULL constraint violation - api.py: Guard against None window_lastupdate in window open detection, preventing int() TypeError on devices without window state - sync.py: Handle null zone entries in device list sync and support both list and dict response formats from Tado Cloud API
When bridge and standalone accessories share the same HomeKit aid (both have aid=1), handle_change found the wrong accessory in the cache and updated the bridge device instead of the standalone device. Now: 1. Prefer matching on both aid AND device_id to disambiguate 2. Always use the pre-resolved device_id for state updates
The Smart AC Control (V3+) subscribes to HomeKit events but doesn't actually fire CurrentTemperature events (it pushes to cloud instead). Previously, polling was skipped entirely when any events were active, leaving the AC Control temperature stuck. Now polling always runs as a safety net for silent devices.
sync_zone_states_data only pushed humidity from cloud zoneStates, ignoring insideTemperature. Standalone accessories that don't fire HomeKit temp events now get corrected by cloud sync. Also fixes humidity being passed as string instead of number.
- Fix 8 failing tests in test_localapi.py: populate aid_to_pairing so setup_persistent_events correctly maps accessories to pairings - Update cleanup test to use pairing_subscriptions (new multi-pairing API) - Update polling test: polling is now always enabled as safety net for standalone accessories that don't fire HomeKit temperature events - Add test coverage for cloud temperature syncing in test_sync.py: temperature-only, both temp+humidity, and empty sensor data cases - Run black formatter on all changed files per contributing guidelines
- Extract _connect_pairing() helper in bridge.py to replace 4 duplicated connect-from-pairing-data sequences across pair_or_load and pair_or_load_accessory - Replace hardcoded port 80 with HOMEKIT_DEFAULT_PORT constant - Remove redundant inline `from collections import defaultdict as _dd` imports in api.py (defaultdict already imported at module level)
Provides an official container option using python:3.12-slim with local source install, non-root user, persistent /data volume, and env-driven configuration. Includes forward-compatible accessory support (PR AmpScm#40).
|
Nice work @john-johansson! One question: with this PR, polling is always running and now besides humidity also the temperature is updated during cloud-sync. How precise is the temperature reading? With 0 or 1 digit. During testing of my previous contributions I saw fluctuating temperature readings in HomeAssistant when storing the poll results. My theory then was a slightly different value in polling versus events, so i only stored humidity (since a 5% offset is needed before an event is sent). Are those fluctuation visible as well with this PR? |
|
Thank you @Bekkie and good catch. I tested this with 33 hours of data across 10 zones and the results confirm your concern: With cloud temperature sync: 171 temperature blips caused by cloud-sync injecting values that differed from the local HomeKit readings. The deltas were surprisingly large -- up to 1.98°C, not just rounding differences. Each 4-hour cloud sync cycle produced a burst of spurious changes. After reverting to humidity-only: 0 temperature blips. My original motivation for adding cloud temperature was as a fallback for Smart AC Control V3+ devices that don't fire HomeKit temperature events. But the always-on HomeKit polling already handles that by reading temperature directly from the device every 60-120s over the local network -- making the cloud fallback unnecessary, and as the data shows, counterproductive. Because of this, I've reverted sync_zone_states_data back to humidity-only. |
Cloud-sync was injecting temperature values with different precision than local HomeKit reads, causing 171 spurious blips across 10 zones over 33 hours (deltas up to 1.98°C). Always-on HomeKit polling (60-120s) already provides timely local temperature for silent devices like Smart AC Control V3+, making the cloud fallback unnecessary. Reverts the temperature portion of e90907e; humidity sync retained.
|
Looks good for me @rhuijben how about you, shall I merge and increase the version number? |
Summary
--accessory-ip/--accessory-pinCLI flags; multiple accessories supportedCloses #28. Relates to #5.
Note on formatting
Some of the diff (~10% of changed lines) is
blackreformatting of touched files, as required by the contributing guidelines. The actual logic changes are smaller than the raw diff suggests. Usegit diff -wto review ignoring whitespace.Test plan
pytest -v— 388 passed, 1 skipped, 0 failedruff check tado_local/— no new violationsblack— all changed files formatted/zones/zonesendpoint