Adds your Bosch Smart Home cameras (Eyes Außenkamera, 360 Innenkamera) as fully featured entities in Home Assistant. Ships with two custom Lovelace cards — one detailed view per camera and a multi-camera grid — plus live streaming, controls, snapshot retrieval, and event-driven notifications.
Supported models: Eyes Außenkamera (Gen1), Eyes Außenkamera II (Gen2), 360 Innenkamera (Gen1), Eyes Innenkamera II (Gen2) — model-specific timing and configuration is automatic.
No official API. This integration uses the reverse-engineered Bosch Cloud API, discovered via mitmproxy traffic analysis of the official Bosch Smart Camera app.
- Supported Cameras
- Disclaimer
- Prerequisites — Setting Up a New Camera
- Installation
- Setup
- Architecture
- Features
- Lovelace Cards
- Requirements
- Alarmanlage / Automation Setup
- Known Limitations — Cloudflare Tunnel tips
- Roadmap — parked features and what's under consideration
- Releases · Full changelog · GitHub Releases
- Related Projects
- License
All four current Bosch Smart Home cameras are supported. Click any camera name for the official product page.
| Camera | Generation | Type | Codec / FW seen | Highlights |
|---|---|---|---|---|
| 360° Innenkamera | Gen1 | Indoor | H.264 + AAC · FW 7.91.x | Pan/tilt motor, autofollow, IR night vision, mechanical privacy shutter |
| Eyes Innenkamera II | Gen2 | Indoor | H.264 + AAC · FW 9.40.x | Built-in 75 dB siren, Audio+ glass-break / smoke / CO, ZONES detection mode, RGB LEDs, retractable head (Privacy hardware button) |
| Eyes Außenkamera | Gen1 | Outdoor (IP66) | H.264 + AAC · FW 7.91.x | Front spotlight, motion-triggered light, ambient-light sensor, schedule-driven illumination |
| Eyes Außenkamera II | Gen2 | Outdoor (IP66) | H.264 + AAC · FW 9.40.x | Front + Top + Bottom RGB LED groups, DualRadar (motion + intrusion), wallwasher mode, mounting-elevation parameter |
Camera images: see the linked product pages above.
Model-specific timing (pre-warm, heartbeat, retries) is configured automatically — see Model-Specific Configuration for the exact values per generation.
This project is an independent, community-developed integration. It is not affiliated with, endorsed by, or connected to Robert Bosch GmbH. "Bosch" and "Bosch Smart Home" are registered trademarks of Robert Bosch GmbH.
This integration communicates with a reverse-engineered, undocumented API. Provided "as is", without warranty. Use at your own risk. The API may change or be shut down by Bosch at any time. Reverse engineering was performed solely for interoperability under § 69e UrhG and EU Directive 2009/24/EC.
Before adding a camera to this integration, it must be fully set up in the official Bosch Smart Camera app first.
- Unbox and power on the camera
- Open the Bosch Smart Camera app and follow the pairing wizard to add the camera to your account
- Wait for the firmware update — new cameras typically receive a Zero-Day update during first setup. This can take up to 1 hour. The camera's LED blinks yellow/green during the update.
- Do not unplug or restart the camera during the update
- If the LED blink pattern doesn't change after 1 hour, leave the camera alone for up to 24 hours (Bosch Support)
- The app shows the update status — wait until it reports the camera as ready
- Verify the camera works in the Bosch app — check live stream, settings, and notifications
- Then add it to Home Assistant using this integration (see Installation below)
Tip: If you're replacing an existing camera (e.g. upgrading from Gen1 to Gen2), rename the new camera in the Bosch app to match the old name before setting up the integration. This way Home Assistant creates entities with the expected names.
For more help with camera setup, see:
- Eyes Außenkamera II — Bosch Support
- Eyes Innenkamera II — Bosch Support
- Firmware Update dauert lange — Bosch Community
- Click the button above (it opens HACS pre-filled with this repo), or in HACS go to Integrations → ⋮ → Custom repositories and add
mosandlt/Bosch-Smart-Home-Camera-Tool-HomeAssistantas typeIntegration. - Click Download on the Bosch Smart Home Camera entry.
- Restart Home Assistant.
- Continue with Setup below.
HACS Default listing. A PR adding this integration to the HACS Default index is pending (
hacs/default#7247). Once merged, the "Custom repositories" step disappears — the integration appears directly under HACS → Integrations → + Explore.
If you don't use HACS, copy the integration folder into your HA config:
- Download the latest release tarball from Releases (or
git clonethis repo). - Copy
custom_components/bosch_shc_camera/into your HA config directory so the final path is:/config/custom_components/bosch_shc_camera/ - Restart Home Assistant.
- Continue with Setup below.
Lovelace card is auto-registered since v10.3.19 — the integration serves it from its own bundled
www/folder. No need to copybosch-camera-card.jsto/config/www/separately. If you have an old copy from a pre-v10.3.19 install, you can leave it (harmless) or delete it manually:rm /config/www/bosch-camera-card.js.
- Go to Settings → Integrations → + Add Integration
- Search for "Bosch Smart Home Camera"
- Your browser opens the Bosch SingleKey ID login page automatically
- Log in with your Bosch account (same credentials as the Bosch Smart Camera app)
- After login, the browser redirects back to Home Assistant automatically — no manual URL copying needed
- The integration discovers all your cameras automatically
Token renewal is automatic. The integration uses a refresh token to silently renew the Bearer token in the background — no manual action needed after initial setup.
Note: The automatic redirect uses my.home-assistant.io. If your HA instance URL is not configured there, you'll be prompted to set it up on first use.
Go to Settings → Integrations → Bosch Smart Home Camera → Configure. Every setting has a description in the UI; the table below groups them by topic so you can decide which sections to fill in.
| Setting | Description | Default |
|---|---|---|
| FCM Push | Near-instant (~2 s) event detection via Firebase Cloud Messaging. With FCM enabled the polling interval is automatically tightened to 60 s as a backup. | OFF |
| FCM Push Mode | Auto (iOS → Android → polling), iOS, Android, or Polling. Auto falls back automatically if iOS registration fails. |
Auto |
| Event check interval | Polling fallback interval (seconds). Used only when FCM Push is OFF or as a backup. | 300 (5 min) |
| Setting | Description | Default |
|---|---|---|
| Alert services — default fallback | Notify services used when a per-step service isn't set. Comma-separated list (e.g. notify.signal_messenger, notify.mobile_app_xxx). |
empty (alerts disabled) |
| Step 1 — text notification | Notify services for the instant text alert. Falls back to default. | (default) |
| Step 2 — snapshot image | Notify services for the JPG attachment ~3-5 s after the event. | (default) |
| Step 3 — video clip | Notify services for the MP4 attachment 30-90 s after the event. | (default) |
| System alerts | Notify services for token-expiry / disk-warning system messages. | (default) |
| Save alert snapshots | Keep downloaded event images / videos in /media/bosch_alerts/ after sending. If OFF, files are deleted within seconds. |
OFF |
| Setting | Description | Default |
|---|---|---|
| SMB Upload | Upload event snapshots + video clips to a SMB/CIFS share (FRITZ!Box NAS, Synology, …). | OFF |
| SMB Server | IP or hostname of the share (e.g. 192.168.1.1). |
empty |
| SMB Share | Share name (e.g. FRITZ.NAS, cameras). |
empty |
| SMB Username / SMB Password | SMB authentication credentials. | empty |
| SMB Base Path | Base folder on the share (e.g. Bosch-Kameras). |
empty |
| SMB Folder Pattern | Subfolder pattern with {year} / {month} / {day}. |
{year}/{month} |
| SMB File Pattern | File-naming pattern with {camera} / {date} / {time} / {type} / {id}. |
{camera}_{date}_{time}_{type}_{id} |
| Retention period (days) | Auto-delete files older than N days. 0 = keep forever. |
180 |
| Low disk warning (MB) | Notify when free space on the share drops below this. | 500 |
| Setting | Description | Default |
|---|---|---|
| Stream connection type | Auto (try LOCAL → fall back to REMOTE), Local (LAN only), Remote (cloud only). Can also be changed at runtime via the per-camera Stream Modus select entity. |
Auto |
HLS player buffer profile (live_buffer_mode) |
Three hls.js buffer presets that trade latency for smoothness: Latency (~4-6 s), Balanced (~8-10 s, default), Stable (~12-15 s). See HLS Buffer Tuning. | Balanced |
| Audio default ON | Whether the per-camera audio switch starts ON (stream with sound) or OFF (muted). | ON |
| Binary sensors | Expose Motion / Audio / Person alarm binary sensors (ON for 30 s after each event). | ON |
| Debug logging | Enable verbose logs (TLS-proxy RTSP exchanges, pre-warm timing, FCM payloads). Off by default to keep the log file readable. | OFF |
Both cards are auto-registered since v10.3.19 — no manual Lovelace resource entry needed. Pick one of the two cards based on whether you want one camera per card or all cameras in a grid:
- Edit dashboard → + Add card → Custom: Bosch Camera Card
- Pick the camera entity in the visual editor, or paste:
type: custom:bosch-camera-card
camera_entity: camera.bosch_garten
title: GartenSee the single-camera card reference for all options.
- Edit dashboard → + Add card → Custom: Bosch Camera Overview Card
- The minimal config has zero options — every Bosch camera is auto-discovered:
type: custom:bosch-camera-overview-card
title: Kameras
use_bosch_sort: trueSee the overview card reference for the grid layout, sort options, and per-camera overrides.
Upgrading from pre-v10.3.19? The integration auto-removes any stale
/local/bosch-camera-card.jsresource entry from Lovelace storage. The physical file in/config/www/is intentionally left in place (an integration must not modify user files there) and is harmless either way — the integration loads its own bundled copy.
graph LR
subgraph User["User"]
B["Browser / HA Companion"]
end
subgraph HA["Home Assistant"]
Card["Lovelace cards<br/>bosch-camera-card +<br/>bosch-camera-overview-card"]
Int["bosch_shc_camera<br/>(integration)"]
Stream["stream component<br/>+ FFmpeg"]
G2["bundled go2rtc"]
TLS["TLS proxy<br/>(per camera)"]
end
subgraph LAN["Local Network"]
Cam["Bosch Camera<br/>RTSPS :443"]
end
subgraph Cloud["Bosch Cloud"]
OAuth["SingleKey ID OAuth"]
API["residential.cbs.<br/>boschsecurity.com"]
CRP["RTSPS Proxy<br/>proxy-NN.live..."]
end
B --> Card
Card -->|"WS: camera/stream<br/>or webrtc/offer"| Int
Card -->|"HLS m3u8"| Stream
Card -->|"WebRTC SDP"| G2
Int -->|"OAuth"| OAuth
Int -->|"REST: video_inputs,<br/>connection, lighting…"| API
Int -->|"spawn"| TLS
TLS -->|"RTSPS<br/>(plain TCP→TLS bridge)"| Cam
TLS -.->|"RTSPS<br/>(plain TCP→TLS bridge)"| CRP
Stream -->|"rtsp://127.0.0.1"| TLS
G2 -->|"rtsp://127.0.0.1"| TLS
Since v10.3.24 the same Python TLS proxy carries both LOCAL and REMOTE — FFmpeg and go2rtc always connect to rtsp://127.0.0.1:N, the proxy decides whether to terminate TLS to the camera (LOCAL) or to the Bosch cloud proxy (REMOTE). Symmetric path means there's no scheme-switching trick (rtspx:// etc.) on the consumer side, and the cert/hostname mismatch on proxy-NN.live.cbs.boschsecurity.com is handled in one place.
In AUTO mode the integration tries LOCAL first and falls back to the cloud relay only when LAN attempts fail. Since v10.5.4 the fallback self-heals — once LAN reachability returns, the live stream is migrated back to LOCAL via Stream.update_source() without waiting for the user to re-toggle. Both LOCAL and REMOTE sessions are kept inside their respective lifetime bounds by background watchdogs (LOCAL is renewed before relay-side rotation, REMOTE is torn down cleanly ~60 s before the relay's maxSessionDuration cap so the consumer never sees a hard reset on the wire).
stateDiagram-v2
direction LR
[*] --> Idle
state "Cloud streaming" as Cloud {
REMOTE
REMOTE_fallback
}
Idle --> LOCAL: ON · auto + LAN ok
Idle --> REMOTE: ON · mode = remote
Idle --> REMOTE_fallback: ON · auto + LAN down
LOCAL --> REMOTE_fallback: 5 (indoor) / 10 (outdoor)\nconsecutive stream errors
REMOTE_fallback --> LOCAL: LAN ok · active promotion
LOCAL --> Idle: OFF
Cloud --> Idle: OFF · or lifetime watchdog
note right of REMOTE_fallback
Error counter time-decays automatically:
5 min if LAN reachable, 30 min otherwise.
Cloud-side errors do not increment the counter.
end note
/connection {type:"REMOTE"}returnsrtsps://proxy-NN.live.cbs.boschsecurity.com:42090/<hash>— the Bosch cloud proxy serves the camera over the public internet.- TLS proxy is symmetric (since v10.3.24): the same Python TLS proxy that handles LOCAL also terminates TLS to the cloud proxy for REMOTE. FFmpeg (HLS) and go2rtc (WebRTC) always connect to
rtsp://127.0.0.1:N— no scheme tricks (rtspx://), no per-consumer special-cases. The cert/hostname mismatch onproxy-NN.live.cbs.boschsecurity.com(cert SAN only covers*.residential.connect.boschsecurity.com) is handled in one place (verify_mode=CERT_NONE, check_hostname=False). - Snapshots (
/snap.jpg) use the cloud-proxy URL directly with HTTP — no TLS proxy needed since they're single-shot HTTP requests, not long-lived RTSP streams. - bufferingTime hint from
PUT /connectionis1000 msfor REMOTE (vs500 msfor LOCAL) — Bosch's server-side hint about expected latency.
| Feature | Entity type | Default |
|---|---|---|
| Camera snapshot (latest event JPEG) | camera |
enabled |
| Camera status (ONLINE/OFFLINE) | sensor |
enabled |
| Last event timestamp | sensor |
enabled |
| Events today count | sensor |
enabled |
| WiFi signal strength (%) | sensor |
enabled |
| Firmware version | sensor |
enabled |
| Ambient light level (%) | sensor |
enabled |
| LED dimmer (%) | sensor |
enabled (cameras with LED) |
| Motion sensitivity | sensor |
diagnostic |
| Audio alarm state | sensor |
diagnostic |
| Last event type | sensor |
enabled |
| Movement events today | sensor |
enabled |
| Audio events today | sensor |
enabled |
| Event detection method | sensor |
diagnostic — fcm_push / polling / disabled |
| Refresh Snapshot | button |
enabled |
| Live Stream (ON/OFF) | switch |
enabled |
| Audio (mute/unmute stream) | switch |
enabled |
| Camera LED light | switch |
enabled (cameras with LED) |
| Privacy mode | switch |
enabled |
| Notifications | switch |
enabled |
| Motion detection | switch |
disabled by default |
| Record sound | switch |
disabled by default |
| Auto-follow (360 camera) | switch |
disabled by default |
| Intercom (two-way audio) | switch |
disabled by default |
| Pan position (360 camera) | number |
enabled (±120°) |
| Audio alarm threshold | number |
disabled by default |
| Speaker level (intercom volume) | number |
disabled by default (0–100) |
| Stream quality | select |
Auto / Hoch 30 Mbps / Niedrig 1.9 Mbps (persists across restarts) |
| Stream mode | select |
Auto (Lokal → Cloud) / Nur Lokal / Nur Cloud |
| Motion sensitivity | select |
SUPER_HIGH / HIGH / MEDIUM_HIGH / MEDIUM_LOW / LOW / OFF |
| FCM Push mode | select |
Auto / iOS / Android / Polling |
| Motion detected | binary_sensor |
disabled by default |
| Audio alarm detected | binary_sensor |
disabled by default |
| Person detected | binary_sensor |
disabled by default |
| Unread events count | sensor |
disabled by default |
| Privacy sound (360 only) | switch |
enabled (config category) |
| Commissioned status | sensor |
diagnostic, disabled by default |
| Acoustic alarm (siren) | button |
disabled by default — added for every camera; cameras without a built-in siren return HTTP 442 (handled gracefully) |
| Live stream (30fps H.264 + AAC) | camera |
via Live Stream switch |
| Timestamp overlay (clock on video) | switch |
disabled by default |
| Movement notifications | switch |
disabled by default |
| Person notifications | switch |
disabled by default |
| Audio notifications | switch |
disabled by default |
| Trouble notifications | switch |
disabled by default |
| Camera alarm notifications | switch |
disabled by default |
| Firmware update status | update |
enabled — native HA update card |
| Schedule rules count | sensor |
diagnostic, disabled by default |
| Alarm Catalog (RCP 0x0c38) | sensor |
diagnostic — all alarm types supported by camera firmware (virtual, flame, smoke, glass break, audio, motion, storage) |
| Motion Zones (RCP 0x0c00/0x0c0a) | sensor |
diagnostic — motion detection zone coordinates (normalized x/y for overlay) |
| TLS Certificate (RCP 0x0b91) | sensor |
diagnostic — camera cert expiry date, issuer, key size |
| Network Services (RCP 0x0c62) | sensor |
diagnostic — active services (HTTP, HTTPS, RTSP, SNMP, UPnP, NTP, ONVIF) |
| IVA Analytics (RCP 0x0b60) | sensor |
diagnostic — analytics module inventory (detectors, versions, active state) |
| Front light with color temperature | light |
Gen2 only |
| Top LED light with RGB color picker | light |
Gen2 only |
| Bottom LED light with RGB color picker | light |
Gen2 only |
| Status LED on/off | switch |
Gen2 only |
| Motion-triggered lighting on/off | switch |
Gen2 only |
| Ambient/permanent lighting on/off | switch |
Gen2 only |
| DualRadar intrusion detection on/off | switch |
Gen2 only |
| Mounting height (meters) | number |
Gen2 only |
| Microphone recording level (0–100%) | number |
Gen2 only |
| Front light color temperature | number |
Gen2 only |
| Top LED brightness (0–100%) | number |
Gen2 only |
| Bottom LED brightness (0–100%) | number |
Gen2 only |
| Motion light sensitivity (1–5) | number |
Gen2 only |
RCP diagnostic sensors are disabled by default. Enable them in entity settings to inspect camera firmware capabilities. Gen2 cameras will automatically expose new alarm types and analytics modules.
SHC local API is not needed. All features work with just the Bosch cloud API.
No automations needed — the integration sends alerts directly:
- Instant text:
📷 Kamera: Bewegung (10:31:56)— sent immediately - Snapshot image:
📸 Kamera Snapshot+ JPEG — sent ~5s later - Video clip:
🎬 Kamera Video (245 KB)+ MP4 — sent ~30-90s later (polls until Bosch uploads the clip)
Per-step routing (v6.5.0+): each step can go to different services, multiple recipients at once. Supports Signal, Telegram, iOS/Android Companion App, or any HA notify service.
| Setting | Description | Example |
|---|---|---|
Alert services — default fallback |
Used for all steps unless overridden below | notify.signal_messenger |
System alerts |
Token expiry, disk warnings | notify.signal_messenger |
Step 1 — text notification |
Instant text on event | notify.signal_messenger, notify.mobile_app_xxx, notify.mobile_app_pixel9 |
Step 2 — snapshot image |
JPEG inline in notification | notify.signal_messenger, notify.mobile_app_xxx |
Step 3 — video clip |
MP4 attachment | notify.signal_messenger |
Save alert snapshots |
Keep files locally or delete after sending | OFF |
Delete after send |
Cleanup local files after notification sent | ON |
iOS + Android Companion App (mobile_app_*): snapshot appears directly inside the push notification as an inline image. Files are saved to /media/bosch_alerts/ and auto-deleted within seconds after sending. Signal and others receive a file path attachment instead.
Notification switch guard (v7.9.1+): Alerts respect the notification switches — if switch.bosch_{name}_notifications (master) is OFF, no alerts are sent. Type-specific switches (movement_notifications, person_notifications, audio_notifications) are also checked. The FCM push is still received (for event tracking), but the HA notification is suppressed.
Events are automatically marked as read after alert processing or download. This uses PUT /v11/events/bulk for batch updates and PUT /v11/events (with {"id": ..., "isRead": true}) for individual events, keeping the unread count in sync with the Bosch Smart Camera app.
On startup, the integration marks all currently unread events as read — clearing any backlog that accumulated while HA was offline.
The integration uses GET /v11/video_inputs/{id}/last_event as a fast-path to check for new events before fetching the full event list. This reduces unnecessary API calls — the full event list is only fetched when the last event has actually changed.
| FCM Push (recommended) | Polling (default) | |
|---|---|---|
| Event latency | ~2-3 seconds | 5 minutes (configurable) |
| How it works | Firebase Cloud Messaging push from Bosch cloud | Periodic API polling |
| Fallback | Automatic — if FCM goes down, polling continues | Always active |
| Status sensor | sensor.bosch_camera_event_detection = fcm_push |
polling |
Enable FCM Push in Settings → Configure → FCM Push. You can also select the push mode (Auto, iOS, Android, or Polling) — Auto tries iOS first, then Android, then falls back to polling. The mode can also be changed at runtime via the FCM Push Mode select entity.
Upload event snapshots and video clips directly to a SMB/CIFS network share (FRITZ!Box NAS, Synology, any Windows share, etc.). Disabled by default.
How it works:
- When an event is detected (via FCM push or polling), the integration downloads the snapshot and video clip
- Files are uploaded to the configured SMB share using the folder and file naming patterns
- Supports any SMB-compatible NAS or router with USB storage (FRITZ!Box, Synology, QNAP, Windows shares)
Configuration: Go to Settings → Integrations → Bosch Smart Home Camera → Configure and enable SMB Upload. Then fill in the server, share, and credentials.
Folder pattern variables: {year}, {month}, {day}
File pattern variables: {camera}, {date}, {time}, {type}, {id}
Example file path on NAS:
\\192.168.1.1\FRITZ.NAS\Bosch-Kameras\2026\03\Garten_2026-03-25_14-32-05_MOVEMENT_abc123.jpg
\\192.168.1.1\FRITZ.NAS\Bosch-Kameras\2026\03\Garten_2026-03-25_14-32-05_MOVEMENT_abc123.mp4
Requires the
smbprotocolPython package, which is auto-installed viamanifest.json.
To use your FRITZ!Box as a NAS for camera event storage:
-
Enable NAS on FRITZ!Box:
- Open
http://fritz.box→ Heimnetz → USB / Speicher → USB-Speicher - Enable Speicher (NAS) aktiv
- Note the share name (default:
FRITZ.NAS)
- Open
-
Create a FRITZ!Box user with NAS access:
- System → FRITZ!Box-Benutzer → Benutzer hinzufügen
- Give the user a username and password
- Under Berechtigungen, enable Zugang zu NAS-Inhalten
-
Configure in Home Assistant:
- Go to Settings → Integrations → Bosch Smart Home Camera → Configure
- Enable SMB Upload
- Fill in:
Field Value Example SMB Server FRITZ!Box IP 192.168.1.1SMB Share NAS share name FRITZ.NASSMB Username FRITZ!Box NAS user nas_userSMB Password User password your_passwordSMB Base Path Folder on NAS Bosch-KamerasSMB Folder Pattern Subfolder structure {year}/{month}SMB File Pattern File naming {camera}_{date}_{time}_{type}_{id}Retention (days) Delete files older than N days 180(6 months)Low disk warning (MB) Alert below this free space 5120(5 GB) -
Verify: After the next camera event, check your NAS at
FRITZ.NAS/Bosch-Kameras/— snapshots (.jpg) and video clips (.mp4) should appear automatically.
Tip: Works with any SMB-compatible device. For Synology, use the share name from Control Panel → Shared Folder. For Windows, use the shared folder name (e.g.
\\PC-NAME\SharedFolder).
Set Retention period (days) to automatically delete old files from the NAS. Default: 180 days (6 months). Set to 0 to keep files forever.
- Cleanup runs once per day in the background
- Deletes
.jpgand.mp4files older than the configured retention period - Only runs when SMB upload is enabled and configured
Set Low disk warning threshold (MB) to receive an alert when the NAS runs low on storage. Default: 500 MB.
- Checked once per hour
- If free space drops below the threshold, an alert is sent via:
- The configured notify service (e.g. Signal, mobile app) if set
- HA persistent notification as fallback (always shown in the sidebar)
Once events are being saved — either to the local download folder (enable_auto_download) or to a NAS via SMB upload — they appear under Media → Bosch SHC Camera in HA's built-in media browser. No extra setup needed; the integration auto-detects which backends have data.
Tree shape
- Local backend:
Camera → Date → Event - NAS backend:
Year → Month → Day → Event(matches the on-disk layout, all cameras share a day folder)
Each event title is HH:MM:SS — TYPE (Camera), e.g. 09:15:23 — MOVEMENT (Garten). MP4 clips play inline with HTTP Range support so the player can seek; the matching JPEG snapshot doubles as a thumbnail. macOS resource-fork files (._*) are filtered out — relevant for FRITZ.NAS / Time Machine targets.
When only one backend is configured the source-chooser is hidden and the tree opens straight at the meaningful content. With both backends enabled the entry root shows Lokal and NAS \server\share as siblings.
Manual filter — Media Browser source option
Settings → Devices & Services → Bosch SHC Camera → Configure → Quelle des Media Browsers:
| Value | Effect |
|---|---|
| Auto (default) | Show every backend that has data |
| Nur Lokal | Hide the NAS even if SMB upload is active |
| Nur NAS | Hide local files even if auto-download is active |
| Deaktiviert | Hide the Media Browser entry entirely |
Files are served by an authenticated /api/bosch_shc_camera/event/… view; path-traversal is blocked, only image/jpeg and video/mp4 are returned. NAS files are streamed on demand via smbprotocol — no local cache, no HA disk usage.
The integration fires events on the HA event bus for custom automations:
bosch_shc_camera_motion— movement detectedbosch_shc_camera_audio_alarm— audio alarm triggeredbosch_shc_camera_person— person detected (Gen2 DualRadar only)
Event data: camera_id, camera_name, timestamp, image_url, event_id, source (fcm_push / polling).
To inspect events live, open Developer Tools → Events (HA ≥ 2026.4: Entwicklerwerkzeuge → Ereignisse → Ereignisse abonnieren), type bosch_shc_camera_motion (the custom event names do not appear in the dropdown — you have to enter them by hand) and click Start listening.
# Example: notify on every motion at one specific camera
# HA ≥ 2026.4: use `trigger: event`; older HA: use `platform: event`
trigger:
- trigger: event
event_type: bosch_shc_camera_motion
event_data:
camera_id: 00000000-0000-0000-0000-000000000000 # from camera attributes
action:
- service: notify.mobile_app_xxx
data:
title: "Bewegung erkannt"
message: "{{ trigger.event.data.camera_name }} – {{ trigger.event.data.timestamp }}"Drop event_data: to react to all cameras and filter inside condition: via trigger.event.data.camera_id.
bosch_shc_camera_motion and sensor.bosch_<name>_movement_events_today are fed from two different paths and are not interchangeable as automation triggers:
bosch_shc_camera_motion (event bus) |
sensor.*_movement_events_today (state) |
|
|---|---|---|
| Source | FCM push (~2 s) or poll tick | Poll tick of /v11/events |
| Fires per | New event ID, max once per 60 s dedup window | Every poll tick where the daily counter increased |
| Burst handling | Polling tick with N new events fires the bus event once for the newest ID — older IDs in the same tick are not re-fired | Counter advances by N regardless of burst size |
| Best for | Live reaction with FCM Push enabled | Robust fallback when FCM is unreliable, or "any motion happened" automations |
If you primarily care about reacting to every motion and FCM occasionally drops on your network/phone, prefer a state trigger on the sensor:
trigger:
- trigger: state
entity_id: sensor.bosch_terrasse_movement_events_todayThe two can be combined: bosch_shc_camera_motion for fast reaction (~2 s via FCM) plus a state trigger on the sensor as a safety net for missed pushes.
To check FCM health: select.bosch_camera_fcm_push_mode should be auto, and sensor.bosch_<name>_event_detection should report fcm_push. If it sits on polling, FCM is not delivering and you'll see the burst-merge effect described above.
All services are available in Developer Tools → Services (or via automations/scripts):
| Service | Description | Fields |
|---|---|---|
bosch_shc_camera.trigger_snapshot |
Force immediate snapshot refresh for all cameras | — |
bosch_shc_camera.open_live_connection |
Open live stream for a specific camera | camera_id |
bosch_shc_camera.rename_camera |
Rename a camera (appears in Bosch app + HA) | camera_id, new_name |
bosch_shc_camera.invite_friend |
Send camera sharing invitation by email | email |
bosch_shc_camera.list_friends |
List all friends and camera shares (persistent notification) | — |
bosch_shc_camera.remove_friend |
Remove a friend and revoke all camera shares | friend_id |
bosch_shc_camera.get_lighting_schedule |
Read full lighting schedule (persistent notification) | camera_id |
bosch_shc_camera.delete_motion_zone |
Delete a single motion zone by index | camera_id, zone_index |
bosch_shc_camera.get_privacy_masks |
Read privacy mask zones (persistent notification) | camera_id |
bosch_shc_camera.set_privacy_masks |
Set/clear privacy mask zones (0.0–1.0 coordinates) | camera_id, masks |
bosch_shc_camera.create_rule |
Create a cloud-side schedule rule | camera_id, name, start_time, end_time, weekdays, is_active |
bosch_shc_camera.update_rule |
Update a schedule rule (change name, times, activate/deactivate) | camera_id, rule_id, name?, start_time?, end_time?, weekdays?, is_active? |
bosch_shc_camera.delete_rule |
Delete a schedule rule | camera_id, rule_id |
bosch_shc_camera.set_motion_zones |
Set motion detection zones (normalized 0.0–1.0 coordinates) | camera_id, zones |
bosch_shc_camera.get_motion_zones |
Read motion zones from cloud API (persistent notification) | camera_id |
bosch_shc_camera.share_camera |
Share cameras with a friend (time-limited) | friend_id, camera_ids, days? |
Examples:
# Rename a camera
service: bosch_shc_camera.rename_camera
data:
camera_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
new_name: "Garten Kamera"
# Invite a friend to share cameras
service: bosch_shc_camera.invite_friend
data:
email: "friend@example.com"
# List all camera shares
service: bosch_shc_camera.list_friends
# Remove a friend (get friend_id from list_friends)
service: bosch_shc_camera.remove_friend
data:
friend_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# Create a schedule rule (notifications active 8am-8pm weekdays)
service: bosch_shc_camera.create_rule
data:
camera_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name: "Weekday Schedule"
start_time: "08:00:00"
end_time: "20:00:00"
weekdays: [1, 2, 3, 4, 5]
is_active: true
# Update a rule (deactivate it)
service: bosch_shc_camera.update_rule
data:
camera_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
rule_id: "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
is_active: false
# Set motion detection zones (list of normalized rectangles)
service: bosch_shc_camera.set_motion_zones
data:
camera_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zones:
- { x: 0.0, y: 0.3, w: 0.67, h: 0.7 }
- { x: 0.63, y: 0.42, w: 0.28, h: 0.58 }
# Share cameras with a friend for 30 days
service: bosch_shc_camera.share_camera
data:
friend_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
camera_ids:
- "cam-id-1"
- "cam-id-2"
days: 30Tip: Find the
camera_idin the camera entity's attributes (Developer Tools → States →camera.bosch_*→camera_idattribute).
examples/automation_ios_push_alert.yaml— iPhone push (time-sensitive)examples/automation_signal_alert.yaml— Signal text messageblueprints/bosch_camera_signal_alert.yaml— configurable blueprint
A growing collection of bilingual (EN + DE) automation snippets that cover common Bosch-camera scenarios. Every file is self-contained and explains its placeholders inline, so you can copy → adapt entity IDs → drop into your automations.yaml.
| Category | What's in there |
|---|---|
| Motion-light control (silence the porch spotlight when you don't want it flaring) | manual "dinner mode" helper · door / window sensor trigger · presence sensor (mmWave / PIR / BLE) · time + sunset-driven schedule · Gen1 instant-off fallback · Gen1 hardware power-cut · production-grade door + privacy + light coordination with mode: restart, day/night-aware delays, HA-restart recovery |
| Privacy & away mode | indoor privacy auto-toggle by who's home · arm/disarm all cameras when the house empties out · per-person precise cleanup (only turn off what they had on) |
| Smart notifications | snapshot + push with image · weather-aware (skip during storms) · doorbell-style auto-display on a wall tablet · sleep mode (quiet at night, but real intruder pattern still wakes you) · vacation deterrent with random light flashes · escalating offline alert (silent → info → critical based on outage duration) |
| Garage & vehicles | combine the driveway camera with the garage-door cover entity to detect "vehicle arriving" / "vehicle leaving" — optional AI vehicle classification (own car / delivery / unknown) |
| AI vision | classify motion via Gemini / GPT-4o / Claude / local Ollama → push only for person/vehicle/package, ignore pets · package-delivery detection · daily AI summary of camera events · TTS visitor greeting |
→ Browse the full example library — index, generation matrix (Gen1 vs Gen2), placeholder reference, and combination patterns.
The integration ships two custom cards, both auto-registered (since v10.3.19 — no manual Lovelace resource entry needed). They share the same code bundle (bosch-camera-card.js) and version, so a single resource URL serves both.
| Card | Use case | Versioning |
|---|---|---|
custom:bosch-camera-card |
One Bosch camera per card. The full feature surface — live HLS / WebRTC video, snapshot, stream/audio/light/privacy/notifications switches, pan controls (360 only), notification-type accordion, motion-zone overlay, schedule editor, alarm controls (Gen2 Indoor II only). | Card v2.10.21 |
custom:bosch-camera-overview-card |
All Bosch cameras at once. Auto-discovers every camera via attributes.brand === "Bosch" and renders a responsive tile grid. Sort order is Live → Privat → Offline with colored outlines per tier (green / orange / grey), or by Bosch-app priority if use_bosch_sort: true. Each tile is a full bosch-camera-card underneath, so per-camera overrides work the same way. |
Overview v1.1.0 |
Card version: v2.10.21 — Companion App + external-endpoint detection skips WebRTC and uses native HLS directly (covers Cloudflare-Tunnel/iOS where UDP can't traverse), desktop browsers externally still try WebRTC normally; info banner while streaming over the tunnel, stale-state guard against accidental toggles after Companion-App backgrounding, Bosch-app sort option, hls.js buffer profiles, hardware-privacy auto-teardown, Gen2 polygon overlays, privacy mask overlay, simplified offline view
The detailed reference for each card follows below — start with bosch-camera-card (the building block) and jump to bosch-camera-overview-card at the bottom.
┌──────────────────────────────────┐
│ ● Garten [streaming]│
│ ┌────────────────────────────┐ │
│ │ Live video / snapshot │ │
│ │ Last: 2026-03-19 09:32 │ │
│ └────────────────────────────┘ │
│ [ 📸 Snapshot ] [ 📹 Stream ] [ ⛶ ] │
│ [ 🔊 ton / video ] [ 💡 Licht ] [ 🔒 Privat ] │
│ [ 🔔 Benachrichtigungen ] │
│ [ 🎙 Gegensprechanlage ] │
│ [ ◀ ] [ ■ ] [ ▶ ] ← pan │
│ Qualität: [Auto ▼] │
│ ▼ Benachrichtigungs-Typen │
│ ▼ Erweitert │
│ ▼ Diagnose │
│ ▼ Zeitpläne & Zonen │
└──────────────────────────────────┘
| Mode | Description |
|---|---|
| Stream OFF | Snapshot image, auto-refreshed every 60 s (visible) / 30 min (background tab). Immediate refresh on tab focus. |
| Stream ON | Live HLS video (30fps H.264 + AAC-LC). Uses go2rtc and HA's camera stream WS. Audio toggle controls mute/unmute. Loading overlay with status updates during connection. Auto-recovers from stream disconnects. Audio quality is higher than the official Bosch app — the Bosch mobile app downsamples audio for cellular bandwidth, while this integration delivers the unmodified AAC-LC stream straight from the camera. |
| Button | Function |
|---|---|
| 📸 Snapshot | Force-fetch a fresh image immediately |
| 📹 Live Stream | Toggle stream ON/OFF |
| 🔊 Ton | Toggle audio mute/unmute during live stream |
| 💡 Licht | Toggle camera LED light (outdoor camera) |
| 🔒 Privat | Toggle privacy mode (covers lens) |
| 🔔 Benachrichtigungen | Toggle push notifications |
| 🎙 Gegensprechanlage | Toggle intercom / two-way audio |
| ◀ ▶ Pan | Pan left/right (CAMERA_360 only) |
Collapsible accordion sections (auto-hidden when entities not available):
- Benachrichtigungs-Typen — per-type notification toggles: movement, person, audio, trouble, camera alarm
- Erweitert — timestamp overlay, auto-follow, motion detection, record sound, privacy sound
- Diagnose — WiFi signal %, firmware version, ambient light %, movement/audio events today
- Zeitpläne & Zonen — schedule rules list with AN/AUS toggle per rule + delete button, motion zone overlay toggle, motion zone count (RCP)
- Consistent snapshot refresh — backend frame interval is shorter than the card's poll interval, so every card request always returns a fresh frame (no jitter).
- HLS auto-recovery — hls.js soft errors recover automatically; fatal errors trigger a full reconnect after 2 s. Buffer-stall detection seeks to the live edge on the first two stalls and does a full reconnect on the third (
bosch-camera-card: 3 buffer stalls, reconnecting HLS). - hls.js CDN load hardening — the card loads hls.js from jsdelivr with a pinned version + subresource-integrity hash (
hls.js@1.6.16+ matchingsha384). The previous floating@1range broke silently whenever jsdelivr shipped a new patch release; updates now require an explicit version + hash bump. - Cred-rotation refresh — Bosch rotates the per-session digest creds on every
PUT /connection LOCAL. The heartbeat parses each response, caches the newuser/password, rebuilds the cachedrtspsUrl, and callsStream.update_source()so the next reconnect uses fresh creds. A reactive 401 rescue (max 1 per 5 min per cam) covers the rare cases where the proactive refresh missed a tick. Together they keep AUTO-mode streams on LAN even after long idle gaps (HLS consumer disconnect → reconnect would otherwise hit HTTP 401). - Session renewal — REMOTE proxy hashes expire after ~60 s; the backend opens a new connection before expiry and hands the card a fresh URL via
Stream.update_source(). LOCAL streams survive the Gen2 Outdoor firmware's ~65 s RTSP TCP reset via a transparent FFmpeg reconnect on the same TLS proxy port with the same Digest credentials (~2 s gap, HLS output continues). - TLS-proxy circuit breaker — when the camera goes physically offline (privacy hardware button, power cut, Wi-Fi drop), the proxy stops retrying after 5 consecutive connect failures within 30 s instead of looping forever. The coordinator decides whether to rebuild via
try_live_connection()once the camera is reachable again. - Hardware-privacy auto-teardown — when the camera's physical privacy button is pressed (or the Bosch app toggles privacy), the coordinator detects the OFF→ON transition and tears down the live session, the same path as a user-toggle. No more stuck
state: streamingor endless reconnect loop. - "Connecting" badge — amber badge with fast pulse while HLS is negotiating. Clears to blue "streaming" once video plays. Safety timeout hides the overlay after 120 s if the video never produces a frame, keeping the snapshot visible underneath.
- Stream uptime counter — badge shows
00:47/1:23while streaming, updating every 2 s. Proves session renewal keeps the stream alive past 60 s. - Frame Δt in debug line — shows actual ms between frames (
Δ2003ms) — live verification that 2 s intervals are consistent. - Snap error retry — a failed snap.jpg during streaming triggers one immediate 500 ms retry instead of waiting for the next 2 s timer tick.
- Connection type badge — shows "LAN" (green) or "Cloud" (gray) in the header while streaming.
The integration supports three connection modes, configurable in Settings → Configure → Stream connection type or at runtime via the Stream Modus select entity:
| Mode | Description |
|---|---|
| Auto (recommended) | Try local LAN first, automatically fall back to Bosch cloud proxy on failure. |
| Local | Direct LAN only — no internet required. Uses a TLS proxy (TCP→TLS + RTSP transport rewrite) since FFmpeg can't handle RTSPS + Digest auth + self-signed cert natively. TCP keep-alive on all proxy sockets. |
| Remote | Always via Bosch cloud proxy. Faster snapshots (~0.4–1.9 s). Sessions run for up to 60 minutes. |
Every camera gets a sensor.bosch_{name}_stream_status entity that exposes the current live stream state as a persistent HA sensor:
| State | Meaning |
|---|---|
idle |
Stream is off |
warming_up |
LOCAL pre-warm running — waiting for camera encoder to initialise |
connecting |
RTSP URL obtained, FFmpeg connecting |
streaming |
Active, via local LAN |
streaming_remote |
Active, via Bosch cloud proxy |
The card reads this sensor on every hass update — so opening a dashboard while the stream is already warming up correctly shows the overlay and snapshot background without needing a toggle click (cold-open fix). You can also use the sensor in automations to react to stream state changes.
The card badge progresses idle → warming_up / connecting (yellow) → streaming (blue) when you flip the live-stream switch on. How long that first transition takes depends on the connection mode and the camera model — the LOCAL path has a deliberate pre-warm to wake the camera's H.264 encoder before exposing the RTSP URL to FFmpeg, while REMOTE is just a cloud-proxy handshake.
| Camera / mode | Typical time to first frame | Why |
|---|---|---|
| Any camera · Remote (Cloud) | ~5–10 s | PUT /connection REMOTE → cloud proxy URL exposed immediately → FFmpeg opens rtsps://proxy-NN.live.cbs.boschsecurity.com:443/... → first HLS segment in 3–5 s. No pre-warm. |
| Gen1 360 Innenkamera · Local | ~30–35 s | min_total_wait = 25 s from PUT /connection LOCAL before the RTSP URL is exposed (models.py INDOOR), then ~5–10 s for FFmpeg pre-buffer. |
| Gen2 Eyes Innenkamera II · Local | ~30–35 s | Same indoor timing profile (HOME_Eyes_Indoor, min_total_wait = 25 s). |
| Gen1 Eyes Außenkamera · Local | ~40–45 s | Outdoor encoder is slower; min_total_wait = 35 s + pre_warm_retries = 8 × 5 s retry window (models.py OUTDOOR) + ~5–10 s FFmpeg buffer. |
| Gen2 Eyes Außenkamera II · Local | ~40–45 s | Same outdoor profile (HOME_Eyes_Outdoor). |
| Any camera · Auto with working LAN | same as Local | Auto picks LOCAL when LAN is reachable. |
| Any camera · Auto, LAN unreachable | ~100 s outdoor, ~40 s indoor, then + ~5 s for REMOTE | pre_warm_rtsp() tries each retry with a ~10 s TLS-handshake timeout plus pre_warm_retry_wait between attempts, so the worst case is pre_warm_retries × (~10 s TLS timeout + pre_warm_retry_wait): outdoor 8 × (10 + 5) = ~120 s, indoor 3 × (10 + 3) = ~39 s. On exhaustion _try_live_connection_inner tears LOCAL down, sets _stream_fell_back[cam_id], and continues to REMOTE (v10.3.2+). Measured end-to-end on a live HA 2026.4.3: patched Gen2 Outdoor target IP to 192.0.2.1 (RFC 5737 TEST-NET) → user-visible fallback after 101 s with WARNING: LOCAL pre-warm failed … Falling back to REMOTE.. |
| Any camera · Any mode, after 2 failed 60-s watchdog ticks | ~2 min recovery | If FFmpeg opens LOCAL cleanly but the stream goes half-dead later, _stream_health_watchdog saturates the error counter on the second failing tick and forces the next try_live_connection to REMOTE. Worst-case end-to-end recovery ~2 min (v10.3.2+). |
Renewals after the initial startup take roughly 2/3 of the min_total_wait (camera encoder already warm), so ~17 s indoor, ~23 s outdoor. The TLS proxy can service a re-opened session during that window without user-visible interruption (Stream.update_source() hot-swap).
Values are configurable per model in custom_components/bosch_shc_camera/models.py if you need to tune them for a slower network or a specific firmware; the defaults above are empirically measured and known-good.
When go2rtc is available, the card uses WebRTC (~2 s latency) instead of HLS (~12 s latency).
Setup (HA 2024.11+):
Since Home Assistant 2024.11, go2rtc is built-in — no separate add-on or installation needed. Just make sure go2rtc: is in your configuration.yaml (added by default_config). Do NOT install go2rtc as a separate add-on — this can cause conflicts.
On stream start, the integration automatically registers the RTSP URL with go2rtc. The card detects WebRTC support and uses it. If WebRTC fails, it falls back to HLS automatically.
How it works:
- On stream start, the integration registers the RTSP URL with go2rtc's API (port 1984 inside HA container)
- The card checks
camera/capabilities— ifweb_rtcis available, it creates anRTCPeerConnection - Full ICE candidate exchange via HA's
camera/webrtc/offerwebsocket - On stream stop, the registration is removed from go2rtc
- If WebRTC fails (go2rtc not running, network issue), falls back to HLS automatically
A separate JavaScript resource (bosch-camera-autoplay-fix.js) monitors all camera cards and auto-recovers from common issues:
| Issue | Detection | Recovery |
|---|---|---|
| Chrome autoplay block | Video paused with readyState ≥ 2 | Play muted |
| Dead HLS stream | readyState = 0 for 20 s | Request new HLS URL via camera/stream WS |
| Hidden video element | display:none while stream ON | Show video, start HLS |
| Buffer stall | 3 consecutive bufferStalledError |
Full HLS reconnect |
| Video freeze | currentTime unchanged for 15 s |
Seek to live edge or restart |
The watchdog gets entity IDs directly from HA states, so it works even when the card's JavaScript is cached.
The Live Stream switch cannot be turned ON while Privacy Mode is active (camera shutter is closed). Since v10.4.6 this is enforced at four levels so there's no bypass path:
BoschLiveStreamSwitch.availablereturnsFalsewhile privacy is on → the entity greys out in the UI.- An attempted service call raises a
ServiceValidationError→ HA shows a clean toast in the UI, no persistent notification clutter. BoschAudioSwitch._apply_audio_changeandcoordinator.try_live_connection()both early-exit with a logged warning if privacy is active.- When privacy gets enabled while a stream is already running — including via the camera's hardware privacy button or the Bosch app — the coordinator detects the OFF→ON transition and tears down the live session automatically (v10.4.10).
The first coordinator tick after HA restart skips events and slow-tier API calls (WiFi, ambient light, RCP, motion, etc.). This reduces startup from ~2 minutes to ~15 seconds. Full data loads on the second tick (60 s later).
Camera timing and behavior is configured per model via CameraModelConfig. Indoor cams keep an active 30 s heartbeat (the cred-refresh path doubles as a session keepalive), while Gen1/Gen2 outdoor cams have heartbeat disabled (= renewal_interval) because the Outdoor firmware rotates digest creds on every PUT and would invalidate the running RTSP session.
| Parameter | 360 Innenkamera (Gen1) | Eyes Innenkamera II (Gen2) | Eyes Außenkamera (Gen1) | Eyes Außenkamera II (Gen2) | Purpose |
|---|---|---|---|---|---|
| Heartbeat interval | 30 s | 30 s | 3600 s (≈ off) | 3600 s (≈ off) | PUT /connection keepalive + cred refresh |
| Pre-warm delay | 1 s | 1 s | 2 s | 2 s | Wait before first RTSP DESCRIBE |
| Pre-warm retries | 3 | 3 | 8 | 8 | Max DESCRIBE attempts |
| Min total wait | 25 s | 25 s | 35 s | 35 s | Minimum time before exposing RTSP URL |
| Renewal interval | 3500 s | 3500 s | 3600 s | 3600 s | Proactive session renewal (safety net) |
| Max session duration | 3600 s | 3600 s | 3600 s | 3600 s | Sent in RTSP URL maxSessionDuration= (Bosch default hint is 60 s but cams accept 3600) |
The card's HLS.js configuration is tuned to prevent HA's stream component from killing FFmpeg, and since v10.4.7 it's selectable via the HLS player buffer profile (live_buffer_mode) option in the integration settings:
| Profile | liveSync / maxLatency / maxBuffer / maxMaxBuffer / lowLatencyMode |
Lag | Trade-off |
|---|---|---|---|
| Latency | 3 / 6 / 10 / 20 / true |
~4–6 s | Lowest delay, may stutter on flaky Wi-Fi |
| Balanced (default) | 4 / 8 / 14 / 22 / false |
~8–10 s | Robust against typical Wi-Fi hiccups |
| Stable | 6 / 12 / 22 / 30 / false |
~12–15 s | Smooth even on weak links |
maxBufferLengthcap — All three modes stay below HA'sOUTPUT_IDLE_TIMEOUT(30 s). If hls.js buffered ≥ 30 s it would stop requesting segments → HA thinks nobody's watching → kills FFmpeg → freeze.- HLS keepalive timer (20 s) — Periodically calls
hls.startLoad()as a safety net. - SRI integrity hash — hls.js is loaded from jsdelivr with a pinned
hls.js@1.6.16+ matchingsha384. Any drift (jsdelivr patch release) blocks the load instead of running an unverified bundle.
The player buffer profile is independent of the Reaktion info field on the card, which shows the Bosch-API server-side bufferingTime hint (~500 ms LOCAL, ~1000 ms REMOTE) and is unrelated to the client-side hls.js buffer.
# Minimal config — everything else defaults from camera_entity
type: custom:bosch-camera-card
camera_entity: camera.bosch_garten
# With optional title
type: custom:bosch-camera-card
camera_entity: camera.bosch_garten
title: Garten
# Compact "minimal layout" — hides all advanced controls behind the ⋮ button.
# Visible: image + info row (Status / Verbindung / Reaktion) + primary buttons
# (Snapshot, Live Stream, ⋮ Overflow, Fullscreen) + Privacy toggle.
# Tap ⋮ to progressively reveal audio/light/notifications/accordions/pan/etc.
type: custom:bosch-camera-card
camera_entity: camera.bosch_garten
title: Garten
minimal: trueAll entity IDs are auto-derived from camera_entity. Buttons and sections are hidden automatically when entities don't exist. The Reaktion slot in the info row reads the buffering_time_ms attribute exposed by the camera entity (Bosch cloud-issued, ~500 ms on LOCAL and ~1000 ms on REMOTE); it stays — while the stream is idle. The Verbindung slot reads connection_type and shows LAN, Cloud, or —.
type: grid
columns: 2
cards:
- type: custom:bosch-camera-card
camera_entity: camera.bosch_garten
title: Garten
- type: custom:bosch-camera-card
camera_entity: camera.bosch_kamera
title: KameraIntroduced in v10.3.0. Auto-discovers every Bosch camera on the HA instance (attributes.brand === "Bosch") and renders a responsive tile grid — no manual entity-list maintenance needed when you add or remove a camera. Each tile is itself a full bosch-camera-card, so all single-camera features (live HLS / WebRTC, snapshot, privacy/audio/light switches, etc.) work inside the grid.
┌──────────────────────────────────────────────────────────────┐
│ Kameras 4/4 │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ ● Terrasse │ │ ● Innenbereich │ ← green │
│ │ [live tile] │ │ [live tile] │ outline │
│ └─────────────────────┘ └─────────────────────┘ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 🔒 Eingang │ │ ○ Garten │ ← orange │
│ │ [privacy on] │ │ [offline] │ & grey │
│ └─────────────────────┘ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
- Header — optional
titleplus a4/4style count showing how many cameras are visible. - Tile — full single-camera card per cell. Each tile carries a colored outline based on the camera's tier:
- Green — online, privacy off (live).
- Orange — online, privacy on (lens covered).
- Grey — offline / unreachable.
Two-level sort: tiers come first (always live → privat → offline), then within each tier either alphabetic (default) or Bosch-app-priority (since v10.4.10).
| Mode | YAML | Behavior |
|---|---|---|
| Alphabetic (default) | use_bosch_sort: false (or omit) |
Inside each tier, sort by friendly_name using German collation. |
| Bosch-app priority | use_bosch_sort: true |
Inside each tier, sort by the bosch_priority attribute (GET /v11/video_inputs priority field — that's also what PUT /v11/video_inputs/order from the Bosch app sets). Cameras without a numeric priority fall back to alphabetic at the end of their tier, so foreign / non-Bosch include: entries don't disappear. |
The online_offline_view: false option hides the offline tier entirely — only online cameras (green and orange tiles) are rendered.
The grid creates one bosch-camera-card per discovered camera using sensible defaults. To customise a specific tile (different title, attached automations, faster refresh, etc.) pass it through overrides:
overrides:
camera.bosch_terrasse:
automations:
- automation.alarmanlage
camera.bosch_garten:
refresh_interval_streaming: 3
title: Eingang (Gen1)
minimal: true # compact layout for this tile onlyEach override is deep-merged into the child card's setConfig(). The wrapping camera_entity is always set automatically — no need to repeat it. The top-level card_defaults map applies the same options to every tile (e.g. card_defaults: { minimal: true } makes the whole grid compact).
# Minimal — auto-discovers every Bosch camera, alphabetic order
type: custom:bosch-camera-overview-card
# Full options
type: custom:bosch-camera-overview-card
title: Kameras # optional header
online_offline_view: true # default true; false = hide offline tier
columns: auto # "auto" | 1 | 2 | 3 | 4 (default "auto")
min_width: 360px # cell min-width when columns: auto (default 360px)
gap: 12px # grid gap (default 12px)
use_bosch_sort: true # default false; true = follow Bosch-app priority
minimal: false # default false; true = compact tiles for the whole grid
exclude: # skip these entity_ids
- camera.bosch_test
include: # bypass auto-discovery and use exactly these
- camera.bosch_terrasse
- camera.bosch_innenbereich
overrides: # per-camera setConfig() overrides
camera.bosch_terrasse:
automations:
- automation.alarmanlage
card_defaults: # base options applied to every tile (overrides win over these)
refresh_interval_streaming: 5- Viewports > 640 px —
columns: autopacks as many cells as fitmin_widthper row; explicitcolumns: 1|2|3|4forces that count. - Viewports ≤ 640 px — always one column regardless of
columns, so cards stay legible on phones. The gridgapshrinks to 8 px in this mode. - Full-width dashboards — to escape the sections-view max-width clamp (which limits standard dashboards to ~870 px), put the overview card in a view with
panel: true. It then uses the full browser width.
- Discovery is live. When a camera is added or removed, the overview re-discovers on the next
hassupdate — no card config edit required. - Tier transitions are smooth. When a camera's privacy switch flips or it goes offline, only the changed tile re-mounts; the others stay (no full grid rebuild).
bosch_priorityattribute. Exposed on every Bosch camera entity (camera.bosch_*). Visible in Developer Tools → States. You can also use it in templates / sensors outside the card, e. g. to drive a different layout.
- Home Assistant 2024.1+
- Python packages:
requests,firebase-messaging,smbprotocol(auto-installed via manifest) - For live video: go2rtc (built into HA) or ffplay/mpv
The Eyes Innenkamera II (Gen2) adds a built-in alarm system with integrated 75 dB siren. Here's how to wire it into a typical HA alarm automation alongside your existing cameras:
| Entity | Purpose |
|---|---|
switch.bosch_{name}_alarmanlage |
Arm / disarm the built-in intrusion system (PUT /intrusionSystem/arming). Derived state from alarmStatus.intrusionSystem (INACTIVE / ACTIVE). |
switch.bosch_{name}_sirene |
Main 75 dB siren on/off (alarm_settings.alarmMode). Disabling this lets you use the other alarm features without actually firing the siren. |
switch.bosch_{name}_pre_alarm |
Pre-alarm red-LED warning before the siren fires (alarm_settings.preAlarmMode). |
switch.bosch_{name}_audio_plus |
Sound-level event detection (ambient-noise threshold — "Geräusche" toggle in the iOS app). Free tier — this is NOT the paid Audio+ premium (glass-break / smoke / CO). |
sensor.bosch_{name}_alarm_status |
INACTIVE / ACTIVE / UNKNOWN — state machine for the alarm. Attributes include alarm_type (NONE when idle), siren_duration_s, activation_delay_s, pre_alarm_duration_s. |
number.bosch_{name}_sirenen_dauer |
Siren duration in seconds (alarm_settings.alarmDelayInSeconds, 10–300). |
number.bosch_{name}_alarm_verzogerung |
Activation delay in seconds (alarmActivationDelaySeconds, 0–600). |
number.bosch_{name}_pre_alarm_dauer |
Pre-alarm LED-warning duration (preAlarmDelayInSeconds, 0–300). |
number.bosch_{name}_power_led |
White Power-LED brightness 0–4 (not 0–100% — the iOS slider is misleading, the API only accepts 5 discrete steps). |
Several settings only work when the camera is actively recording (privacy OFF):
switch.bosch_{name}_einbrucherkennung(intrusion detection)select.bosch_{name}_erkennungsmodus(detection mode:ALL_MOTIONS/ONLY_HUMANS/ZONES)number.bosch_{name}_microphone_level
When privacy is ON, the Bosch cloud API returns HTTP 443 "sh:camera.in.privacy.mode" on reads/writes to these endpoints, so the entities show as unavailable. The integration caches the last-known-good values — so if you've ever had privacy OFF since HA started, the cached settings remain visible. If the camera has been in privacy mode since the HA restart, the entities stay unavailable until you turn off privacy once.
Note on event clips: Bosch records clips only when the camera is actively monitoring. If all cameras are in privacy mode, videoClipUploadStatus=Unavailable is returned for every event — you'll get the text + snapshot alert but no video attachment. This is not a bug in the integration.
If you already have an alarm automation that toggles privacy_mode on your other cameras based on presence / schedule / garage door, just add the new camera's privacy switch alongside:
- alias: "Alle weg → Alarmanlage aktivieren + Kameras freigeben"
sequence:
- action: switch.turn_off
target:
entity_id:
- switch.bosch_terrasse_privacy_mode # Gen2 Outdoor
- switch.bosch_innenbereich_privacy_mode # Gen2 Indoor II (new in v9.1.10)
- switch.bosch_kamera_privacy_mode # Gen1 360
- action: switch.turn_on
target:
entity_id: switch.bosch_innenbereich_alarmanlage # arm the built-in siren- alias: "Innenkamera → Person erkannt"
triggers:
- platform: state
entity_id: binary_sensor.bosch_innen_person
to: "on"
conditions:
- condition: state
entity_id: switch.bosch_innen_alarmanlage
state: "on" # only when armed
actions:
- action: notify.mobile_app_xxx # replace with your notify service
data:
message: "🚨 Person im Innenbereich erkannt"
# Optional: fire the siren (remove this line to silent-alarm)
# - action: switch.turn_on
# target:
# entity_id: switch.bosch_innen_sireneThe custom Lovelace card automatically shows the new alarm rows (Alarmanlage, Sirene, Pre-Alarm, Geräusch-Erkennung, Power-LED) when the alarm entities exist and the alarm system is gated behind the presence of switch.{base}_alarmanlage. No card config changes needed:
type: custom:bosch-camera-card
camera_entity: camera.bosch_innenbereich
title: "Eyes Innenkamera II"Everything renders automatically when the integration detects a Gen2 Indoor II.
The integration ships cf_unbuffer.py — a runtime patch that rewrites HA's HLS view classes to send Transfer-Encoding: chunked (no Content-Length) for .m4s segments and text/event-stream content-type for .m3u8 playlists. This ensures cloudflared's shouldFlush() triggers immediately instead of buffering the full segment before forwarding.
On iOS (Companion App and Mobile Safari), the card detects the platform automatically and skips the WebRTC attempt — going straight to native HLS via video.src. This avoids the 5 s ICE timeout that WebRTC would incur over a Cloudflare Tunnel (UDP cannot traverse the HTTP tunnel). The card shows an info banner while streaming on iOS.
Verify the unbuffer patch is active:
curl -sI https://your-ha.example.com/api/hls/<token>/segment/0.m4s
# Expected: Transfer-Encoding: chunked AND no Content-LengthForce cloudflared off QUIC onto HTTP/2 — QUIC over cellular is fragile (regular failed to accept QUIC stream: timeout errors). HA → Settings → Add-ons → Cloudflared → Configuration: add --protocol=http2 to run_parameters, restart the add-on. Verify in the add-on log: Initial protocol http2. Costs nothing, helps WebSocket and large-response stability.
Features investigated or intentionally parked — listed here so the direction is visible. Not planned for active development; open an issue if any item matters to you and we'll pick it up based on demand.
- Motion-zone editor (read-only) — local read access via RCP+ (
0x0c0a+0x0c00) is technically possible using the per-sessioncbs-…user fromPUT /connection LOCAL. A read-only viewer is feasible today; full write support requires capabilities not yet exposed locally. - Motion-zone editor with write support — Gen2
/zones, Gen1/motion_sensitive_areas - Rules editor (
/v11/video_inputs/{id}/rules) — adjust event rules from the HA UI - Camera sharing (
/v11/friends) — manage shared access from HA - Live thumbnail via local RCP+ — opcode
0x099eis reachable, but the local XML endpoint returns<err>0x60</err>for theF_DATAreads we tried (the cloud proxy uses binary TLV on the same path). Use case is narrow anyway: the card already shows live HLS as soon as the LOCAL session is up. low_bandwidth: truecard option — would suppress HLS autostart, showing a ▶ overlay until tapped (mobile data saver). Earlier attempts in card v2.10.3–v2.10.5 introduced stream-startup races and were reverted. Parked because the HA Companion App handles bandwidth on its own.
Latest stable: v10.7.0 — see the GitHub release page for full notes: v10.7.0 release notes →
| All releases | GitHub Releases page — every tagged version with notes + downloadable assets |
| Full history | CHANGELOG.md — same notes, browseable inside the repo |
| Card version | tracked in src/bosch-camera-card.js (CARD_VERSION) and mirrored in custom_components/bosch_shc_camera/const.py (CARD_VERSION) |
| Integration version | custom_components/bosch_shc_camera/manifest.json (version) |
- Bosch Smart Home Camera — Python CLI Tool — standalone CLI with full API access, live stream, RCP protocol, FCM push
- Bosch Smart Home Camera — Python Frontend (concept) — planned NiceGUI web dashboard — community interest welcome
MIT — see source files.
