This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A Home Assistant custom integration for BTicino Classe 100X/300X video intercom systems. Communicates with the BTicino/Netatmo cloud API via the pybticino library. Uses a persistent WebSocket connection for real-time call events and periodic polling (every 5 minutes) for state synchronization.
Only works with Netatmo cloud-connected devices (Home+Security app), not the legacy BTicino cloud.
There is no build system, test suite, or linter configured in this repository. The code runs as a Home Assistant custom component loaded from custom_components/bticino_intercom/.
To validate the integration structure locally, you can run the same checks CI uses:
- HassFest (HA manifest validation):
docker run --rm -v $(pwd):/github/workspace ghcr.io/home-assistant/hassfest - HACS validation: runs via
hacs/action@mainin CI
CI runs on every push/PR (.github/workflows/hacs.yaml). Releases are triggered by pushing a v* tag (.github/workflows/release.yaml).
All code lives in custom_components/bticino_intercom/. Key data flow:
-
config_flow.py- UI-based setup: authenticates viapybticino.AuthHandler, fetches home topology viaAsyncAccount, lets user select a home. Supportslight_as_lockoption (uses lighting relay instead of dedicated lock module). -
__init__.py- Entry setup: createsAuthHandler+AsyncAccount+WebsocketClient+BticinoIntercomCoordinator. Manages the WebSocket connection lifecycle with smart retry backoff (separate schedules for boot vs runtime). Registers shutdown handlers to cleanly cancel the WebSocket task. -
coordinator.py(BticinoIntercomCoordinator) - Central data hub extending HA'sDataUpdateCoordinator. Two data paths:- Polling:
_async_update_data()fetches topology, home status, and event history from the API - Push:
_handle_websocket_message()processes real-time RTC events (call/rescind/terminate), updates state immediately viaasync_set_updated_data(), then requests a full refresh for consistency
- Polling:
-
Entity platforms consume coordinator data:
lock.py- Door locks (BNDL modules), optimistic state with relock delaybinary_sensor.py- External unit call status (BNEU modules), auto-off after 30s timeoutsensor.py- Last call event sensor tied to the bridge modulelight.py- Staircase lights (BNSL modules), auto-off after 10scamera.py- Camera entity with image caching (5 min)
-
const.py- Module subtype constants (SUBTYPE_DOORLOCK,SUBTYPE_EXTERNAL_UNIT,SUBTYPE_STAIRCASE_LIGHT) and their platform mappings. Event types for logbook integration.
- Module subtypes: BTicino devices are identified by variant subtypes:
bndl_doorlock,bneu_external_unit,bnsl_staircase_light. The bridge module is identified by having a MAC address format ID. - WebSocket events: RTC events (
call,rescind,terminate) are dispatched via HA's dispatcher system (SIGNAL_CALL_RECEIVED) and fired as logbook events. - Data storage:
hass.data[DOMAIN][entry.entry_id]holds the coordinator, websocket client, and websocket task references keyed by constants defined in__init__.py.
The integration supports WebRTC live video streaming via the Netatmo cloud.
- Camera entities: One per BNEU (external unit) module. Each external unit in the BTicino topology gets its own camera entity with independent WebRTC streaming.
- Signaling: Uses pybticino's
SignalingClient, which connects towss://app-ws.netatmo.net/appws/. This is a separate WebSocket from the push event WebSocket (/ws/). The signaling client handles offer/answer exchange for WebRTC session establishment. - Two modes:
- Offer mode (on-demand): HA sends SDP offer to the device via Netatmo cloud, receives SDP answer back. Used for live viewing from the dashboard.
- Answer mode (incoming call): Device sends SDP offer via push WebSocket when someone rings. HA can respond with an SDP answer to join the call. (Documented but not yet implemented in camera entity.)
- Audio: The device mic requires real RTP audio packets to activate (not just SDP
sendrecvnegotiation). A custom Lovelace card from bticino_ha_extras is needed to send audio and unmute playback, since HA's built-in camera player mutes audio by design. - SDP manipulation:
camera.pycontains SDP manipulation methods (direction forcing, codec reordering) that were explored during development. These are currently disabled because the audio issue is RTP-related, not SDP-related. - Session timeout: ~30s in offer mode (device-side behavior, not configurable from the client).
- Event entity:
event.pyprovidesEventDeviceClass.DOORBELLalongside the existingbinary_sensorfor doorbell ring detection (dual entity pattern, following UniFi's approach). - Browser compatibility: Chrome/Chromium only. Firefox is NOT supported due to a device firmware bug: the BTicino BNC1 uses hardcoded Chrome-compatible RTP payload type numbers (PT=111/Opus, PT=109/H264) regardless of SDP negotiation. Firefox uses different PTs and drops the mismatched packets. DTLS and SRTP work correctly in Firefox — the issue is exclusively at the RTP payload type layer. See
docs/firefox-webrtc-investigation.mdfor the full analysis.
For protocol details, see the pybticino WebRTC documentation.
The integration persists incoming call events (snapshots/vignettes) to disk so they remain viewable after the Azure SAS URLs expire (typically within minutes).
- Storage:
<config>/bticino_intercom/events/<entry_id>/<module_id>/for images,helpers.storage.Storefor metadata - Trigger:
incoming_callWebSocket push →coordinator._process_status_event()→EventHistoryStore.async_record_call()downloads images immediately - Closing: RTC
terminate/rescind→coordinator._close_history_event()marks the record as closed - Retention: configurable via options flow (
history_enabled,history_retention_days,history_max_events), defaults 30 days / 500 events - Browsing: exposed via HA Media Source (
media-source://bticino_intercom/...) and authenticated HTTP view (/api/bticino_intercom/image/...) - Files:
history.py(store),media_source.py(browse + HTTP view)
When the integration logger is at DEBUG level, a bticino_intercom.inject_test_event service is registered. It injects a fake incoming_call push into the coordinator, triggering the full event pipeline (snapshot download, history recording, entity updates, logbook events).
Protected by HA authentication. Optional service_data parameters:
device_id: module to attribute the event to (defaults to first available module)session_id: custom session ID (defaults totest-<timestamp>)snapshot_url: image URL to download as snapshot (defaults to picsum.photos)vignette_url: image URL to download as vignette (defaults to picsum.photos)
Activate with:
logger:
logs:
custom_components.bticino_intercom: debugVersion is tracked in manifest.json (version field). The sole runtime dependency is pybticino>=1.0.