Zum Inhalt springen

Module M13 - Tent Camera Dashboard

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

Files: packages/grow_camera.yaml (this module). packages/grow_mapping_tool.yaml gained 4 new mapping keys (camera1-camera4). dashboards/dashboard_1_configuration.yaml gained a “Cameras (M13, optional)” mapping card. dashboards/dashboard_2_control_panel.yaml gained a new “Cameras” view.

What this module is - and deliberately isn’t

A dedicated live-monitoring dashboard for up to 4 cameras (IP or USB) in the grow tent. Pure monitoring: no recording/archival, no motion detection, no security-style overlays (no REC light, no timestamp watermark) - explicitly requested as “focus on monitoring, not security gimmicks” (decision R19). It has zero effect on any control logic elsewhere in the project; Climate, Irrigation, Light, Air, CO2, and Energy all run exactly as before.

The central architecture decision: this module never manages camera connections

Home Assistant already has mature, native integrations for talking to cameras - re-implementing stream URLs, authentication, and connection testing as custom input_text helpers and automations would mean either:

  • Violating R1 (native-only, no custom backend integration) - a real connection test for an RTSP/HTTP/MJPEG stream isn’t something you can build robustly out of pure automations/templates; doing it properly would mean writing an actual custom integration with its own config flow, and the project’s own review already rejected that path once (Section 3, “a real custom integration with a config flow would be more convenient, but contradicts R1”).
  • Storing a camera’s username/password in an input_text helper, which means storing it in plaintext-visible entity state - readable by anyone with dashboard or Recorder access. Home Assistant’s own camera integrations store credentials in protected config-entry storage instead.

So this module works one layer up: you set up each camera as a normal Home Assistant camera.* entity first (see below), and this module only stores a reference to that entity, per slot:

input_text.grow_camera<N>_entity_id # e.g. "camera.tent_canopy_rtsp" - the ONLY
# thing this project stores about the
# camera's actual connection
input_text.grow_camera<N>_name # free-text display name, e.g. "Canopy"
input_boolean.grow_camera<N>_enabled # show/hide this slot on the dashboard

This is the exact same pattern already used for the GitHub Deploy-Key/PAT (never touches this project, entered directly into the Git-Pull add-on - Section 7.1) and for every other “assign a real HA entity to a Grow Assistant function” field on Dashboard 1 (Section 3’s Helper-Mapping-Konzept). Once both an IP camera and a USB camera exist as a camera.* entity, this module treats them identically - it never needs to know or care which kind a given slot originally was.

Setting up a camera in Home Assistant first (outside this project)

Do this in Home Assistant’s own UI before mapping a slot on Dashboard 1 - none of these steps involve GrowAssistant or this session in any way.

IP camera with RTSP/HTTP/MJPEG - Generic Camera

Settings -> Devices & Services -> Add Integration -> Generic Camera. Config-flow/UI-based (YAML setup for this integration has been deprecated for several Home Assistant versions). You’ll need a still-image URL and/or a stream URL, and optionally a username/password if the camera requires auth. Home Assistant validates connectivity during setup - if it fails here, the camera won’t work as a GrowAssistant slot either, so this is effectively your “test connection” step.

IP camera with ONVIF support - ONVIF

Settings -> Devices & Services -> Add Integration -> ONVIF. Supports auto-discovery and, on capable cameras, PTZ. Known limitation: the camera must expose at least one H.264 video profile - H.265-only cameras are a documented ONVIF-integration compatibility gap. Check your camera’s profile support before relying on ONVIF; Generic Camera (above) is the fallback if the camera exposes a direct RTSP/MJPEG URL independent of ONVIF.

USB camera (connected locally to the Home Assistant host)

There’s no first-class native HA integration for USB/V4L2 webcams. Two documented community paths:

  1. MotionEye add-on (recommended primary path). Install the official Home Assistant OS add-on “MotionEye”, add your USB camera as a “Local V4L2 Camera” source inside it. MotionEye republishes it as an MJPEG/RTSP stream, which you then add to Home Assistant exactly like an IP camera, via Generic Camera above. Reported in the community as more stable and lower-CPU than the alternative below - use this unless you have a specific reason not to.
  2. go2rtc (alternative). Bundled into Home Assistant Core since the 2024.11 release. Add a stream entry (streams: usbcam: "ffmpeg:device?video=/dev/video0&...") to expose the USB camera as RTSP/WebRTC. Reported to have latency advantages over MotionEye but more prone to artifacts/instability on weaker host hardware (e.g. a Raspberry Pi already busy running Home Assistant itself) - prefer MotionEye unless you’ve specifically outgrown it.

Either way, once the camera shows up as a normal camera.* entity in Home Assistant (check Settings -> Devices & Services -> Entities, filter camera.), it’s ready to be mapped exactly like an IP camera.

Mapping a slot in GrowAssistant

  1. Developer Tools -> Actions -> “Grow Assistant: Set Mapping” (script.grow_assign_mapping, same generic tool used for every other mapping field in this project - see docs/docs/mapping-tool.md).
  2. Mapping field -> “Camera 1” (through “Camera 4”).
  3. Target entity -> search for and pick your camera.* entity.
  4. Perform Action. input_text.grow_camera<N>_entity_id is set for you.
  5. On Dashboard 2’s Cameras view, give the slot a display Name and turn its Enabled toggle on.

Live view: how it actually works

Which camera a tile shows is picked dynamically at runtime from the input_text.grow_camera<N>_entity_id mapping - but stock Lovelace cards (picture-glance, picture-entity) take a literal entity: value in their YAML, they cannot template which entity to show. The only stock card that templates its content with Jinja is Markdown, and Home Assistant’s markdown renderer safely passes through basic HTML including <img> tags. So each camera tile is Jinja that resolves the mapped camera, then embeds:

<img src="{{ state_attr(eid, 'entity_picture') }}&v={{ tick }}">

entity_picture is Home Assistant’s own signed camera-proxy snapshot URL - no manual token handling needed. tick is sensor.grow_camera_refresh_tick, a trigger-based template sensor that changes every 5 seconds independent of the camera’s own state, appended as a cache-busting query parameter so the browser actually re-fetches the image on a schedule rather than only when the camera entity’s state/attributes happen to change.

This is a deliberate MVP tradeoff, not an oversight: you get a frequently refreshing snapshot (every ~5 seconds), not a continuous WebRTC-grade video feed, and tapping a tile doesn’t open Home Assistant’s native more-info/fullscreen dialog the way a real picture-glance card would - both are the price of being able to template which camera a tile shows. A HACS custom:auto-entities “cards mode” approach (using filter.template to emit a fully dynamic card object) was investigated first and rejected: it depends on a documented, open upstream bug (thomasloven/lovelace-auto-entities issue #570) when filter.template is combined with full card objects, which would make the whole view fragile against a future library update. The Markdown+<img> approach uses zero HACS cards for the live tiles themselves - only native Home Assistant functionality.

Status logic

Per slot, sensor.grow_camera<N>_status is one of 4 states, checked in this order:

  1. Not Configured - input_text.grow_camera<N>_entity_id is empty.
  2. Disabled - an entity is mapped, but input_boolean.grow_camera<N>_enabled is off.
  3. Online - the mapped camera.* entity’s own state is idle, recording, or streaming (mirrored into binary_sensor.grow_camera<N>_available).
  4. Offline - anything else (unavailable, the entity was deleted, etc.). Home Assistant’s camera domain doesn’t expose a distinct “connection error” state beyond unavailable, so a genuine connection failure and a simply-unreachable camera both show as “Offline” here - check the camera’s own integration (Generic Camera/ONVIF/MotionEye) in Home Assistant directly for the specific underlying error.

sensor.grow_camera_registry aggregates all 4 slots as one machine-readable list (cameras attribute: slot/name/entity_id/enabled/status per camera), the same pattern as sensor.grow_entity_registry (Section 3 of the Phase 0 plan) - available for any future automation/dashboard that wants a single place to read every slot’s state without querying 4 entities individually.

Dashboard layout

  • 1 Camera - the slot picked by input_select.grow_camera_focus shown large, the other 3 slots as small thumbnails below.
  • 2 Cameras - always Slots 1 and 2 side by side (fixed, not “whichever 2 are enabled” - Lovelace YAML can’t reorder cards based on which slots happen to be enabled without a fragile custom-card workaround, so this MVP keeps the mapping to physical slots simple and predictable: use Slots 1/2 for whichever two cameras you want in this layout).
  • 4 Cameras - all 4 slots in a 2x2 grid, regardless of which are actually enabled - an unconfigured/disabled slot shows its own grey placeholder tile instead of being hidden, so you always know the slot exists and can see at a glance why it’s not showing a picture.

Later expansion stages (not MVP)

  • HACS “Advanced Camera Card” (dermotduffy/advanced-camera-card) for a proper carousel/fullscreen multi-camera UI and native tap interactions
    • the same staged approach already used for mushroom/card-mod in this project (ship the simple native version first, add a HACS card later once the functional workflow has proven itself).
  • Continuous live streaming (WebRTC/camera_proxy_stream) instead of the 5-second-refresh snapshot, once verified stable across browsers/viewers.
  • Snapshot/timelapse archival (e.g. one image per hour, for a grow timelapse).
  • PTZ control, for ONVIF cameras that support it.
  • More than 4 cameras, or multiple tents at once - belongs with Phase 10’s multi-grow management, not this module.
  • Deliberately not planned, even later: motion detection or any other security-camera feature - out of scope by explicit request (“focus on monitoring, not security gimmicks”).

Rollout checklist

  1. Deploy packages/grow_camera.yaml and the updated packages/grow_mapping_tool.yaml, check config, restart (new helpers need one the first time).
  2. Re-import dashboards/dashboard_1_configuration.yaml (new “Cameras (M13, optional)” mapping card) and dashboards/dashboard_2_control_panel.yaml (new “Cameras” view).
  3. Set up each physical camera in Home Assistant itself first (Generic Camera / ONVIF / MotionEye+Generic Camera / go2rtc - see above) - confirm it shows a working camera.* entity in Settings -> Devices & Services -> Entities before mapping it here.
  4. Map each slot via Developer Tools -> Actions -> “Grow Assistant: Set Mapping” (Camera 1-4), then give it a Name and turn Enabled on, on the Cameras view.
  5. Try all 3 layouts and confirm each configured camera’s tile shows a green “Online” dot and an actually-updating image; an unconfigured slot should show a grey “Not set up” placeholder, never a broken-image icon.