v10.6.0 — Image rotation 180° for ceiling-mounted indoor cameras
New: 180° image rotation switch for ceiling-mounted indoor cameras.
Adds a per-camera switch switch.bosch_<cam>_bild_180deg_drehen (Indoor only — Gen1 360 + Gen2 Indoor II) that rotates the camera image by 180° for upside-down ceiling mounting. Outdoor cameras don't get the switch since their mounting orientation is fixed by design.
Bosch firmware exposes no native rotation API, so the implementation is client-side at three layers:
What it does
- Lovelace card: applies a CSS
transform: rotate(180deg)to the<video>and<img>elements. Zero CPU, zero latency, GPU-composited — toggle is instant with no stream restart and no re-encode. - Snapshot path: rotates the JPEG via PIL in
camera.async_camera_image()before serving it through HA's camera proxy. So push notifications, NAS clip uploads, and any other consumer that reads the camera entity also see the right-way-up image (~15–30 ms per snapshot). - PTZ pan inversion (Gen1 360 only):
BoschPanNumberautomatically inverts the slider sign when the rotation switch is on, so "right" on the slider stays "right" on the user's screen even when the camera is upside-down.
State persists across HA restarts via RestoreEntity. Default OFF.
Card v2.11.1 ships alongside
The card auto-detects the rotation switch via the entity-id convention switch.<base>_bild_180deg_drehen and applies/removes the CSS class on every set hass() — no card YAML config change required.
Why client-side instead of go2rtc rotation
Researched go2rtc's #rotate=180 parameter: it invokes a full FFmpeg transpose=1,transpose=1 re-encode (~30–60% of one CPU core per 1080p stream, +80–150 ms latency, quality loss from second-generation H.264 encoding) and causes a 3–6 s blackout on every toggle (FFmpeg respawn, HLS playlist invalidation, WebRTC PeerConnection drop). H.264 SEI display-orientation and MP4 tkhd matrix are decoder hints that browsers ignore for live HLS. CSS in the card avoids all of this.
Notes
- Bosch's official position is that the cameras are not designed for ceiling mounting — for the Gen1 360, the privacy shutter is gravity-actuated and may not open/close reliably when upside-down. This is hardware, not fixable in software.
- For Lovelace cards other than the bundled
bosch-camera-card, only the snapshot path applies — live<video>elements in third-party cards won't be rotated unless they also read this switch.
Files changed
__init__.py— coordinator dict_image_rotation_180switch.py— newBoschImageRotation180Switch(RestoreEntity)camera.py—_rotate_jpeg_180PIL helper + hook inasync_camera_image()number.py—BoschPanNumbersign inversion when rotation ontranslations/de.json+en.json— "Bild 180° drehen" / "Rotate Image 180°"src/bosch-camera-card.js—_applyImageRotation180()method + CSS.rotated-180
Verified live on Innenbereich (Gen2 Indoor II) — snapshot rotation visually confirmed (timestamp overlay flipped + room contents inverted), card CSS rotation verified live in Chrome with no console errors, Pan inversion logic in place but not yet live-verified (Gen1 360 currently offline).