Skip to content

Add standalone HomeKit accessory support (e.g. Smart AC Control V3+)#40

Merged
Bekkie merged 10 commits intoAmpScm:mainfrom
john-johansson:feature/standalone-accessory-support
Mar 5, 2026
Merged

Add standalone HomeKit accessory support (e.g. Smart AC Control V3+)#40
Bekkie merged 10 commits intoAmpScm:mainfrom
john-johansson:feature/standalone-accessory-support

Conversation

@rh-john
Copy link

@rh-john rh-john commented Mar 1, 2026

Summary

  • Adds support for standalone HomeKit accessories (e.g. Tado Smart AC Control V3+) that pair independently from the bridge
  • New --accessory-ip / --accessory-pin CLI flags; multiple accessories supported
  • All devices appear in the same REST API and SSE stream regardless of which pairing they belong to
  • Cloud temperature sync now covers both temperature and humidity (standalone accessories don't fire HomeKit temp events)
  • Polling always runs as safety net for silent devices
  • Includes bug fixes: window NULL constraint, NoneType crashes, aid collision between bridge and accessory
  • Full test coverage for new behavior; all 388 tests pass; formatted with black

Closes #28. Relates to #5.

Note on formatting

Some of the diff (~10% of changed lines) is black reformatting of touched files, as required by the contributing guidelines. The actual logic changes are smaller than the raw diff suggests. Use git diff -w to review ignoring whitespace.

Test plan

  • pytest -v — 388 passed, 1 skipped, 0 failed
  • ruff check tado_local/ — no new violations
  • black — all changed files formatted
  • Manual: pair a standalone Smart AC Control V3+ alongside the bridge and verify zones appear in /zones
  • Manual: verify cloud temperature sync updates the standalone accessory via /zones endpoint

john-johansson and others added 8 commits March 1, 2026 01:59
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)
@rh-john rh-john marked this pull request as ready for review March 1, 2026 01:36
rh-john pushed a commit to john-johansson/TadoLocal that referenced this pull request Mar 1, 2026
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).
@Bekkie
Copy link
Collaborator

Bekkie commented Mar 3, 2026

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?

@john-johansson
Copy link
Contributor

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.
@Bekkie
Copy link
Collaborator

Bekkie commented Mar 5, 2026

Looks good for me @rhuijben how about you, shall I merge and increase the version number?

@Bekkie Bekkie merged commit 818cd94 into AmpScm:main Mar 5, 2026
5 checks passed
@rh-john rh-john deleted the feature/standalone-accessory-support branch March 8, 2026 13:17
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.

Air conditioning

3 participants