Skip to content

feat: add AmbiSense presence support#1

Merged
Techposts merged 4 commits into
mainfrom
ambisense-presence-support
May 7, 2026
Merged

feat: add AmbiSense presence support#1
Techposts merged 4 commits into
mainfrom
ambisense-presence-support

Conversation

@Techposts
Copy link
Copy Markdown
Owner

Summary

AmbiSense (radar presence + LED follow-me) speaks the smartghar contract from firmware v6.2.0-alpha.2. Each unit advertises itself on _smartghar._tcp.local. as a SmartGhar hub with one virtual sub-device of kind: \"presence\". This PR teaches the integration to render the right HA entities for that kind — alongside any existing TankSync hubs in the same install.

Spec the firmware implements: docs/SMARTGHAR-PROTOCOL.md.

What changed

  • const.py: new DEVICE_KIND_PRESENCE = \"presence\", MODEL_PRESENCE, hub_model_for_product() dispatcher (TankSync → "TankSync Hub", AmbiSense → "AmbiSense Hub", unknown → "SmartGhar Hub").
  • binary_sensor.py: new SmartGharPresenceOccupancy (device_class=occupancy) per presence device. Stationary / target_count / nearest_cm / seconds_since_seen ride as extra_state_attributes so HA automations can react to combinations like "occupied AND stationary > 5 min" without separate binary sensors. Also: hub OTA-available now uses hub_model_for_product so the device entry model matches the actual product.
  • sensor.py:
    • PRESENCE_SENSORS description tuple — nearest_cm (distance, cm), target_count, seconds_since_seen (diagnostic, duration), rssi_dbm (diagnostic, disabled by default).
    • SmartGharPresenceSensor class. nearest_cm reads -1 from firmware when vacant; this surfaces as None so HA renders "Unknown" instead of "-1 cm".
    • Hub model in SmartGharHubSensor device_info now uses hub_model_for_product.
    • Setup loop iterates kind == DEVICE_KIND_PRESENCE and constructs the four sensor entities per device.
  • strings.json + translations/en.json: translation keys for occupancy, presence_nearest, presence_target_count, presence_seconds_since_seen, presence_rssi.
  • manifest.json: version 0.6.1 → 0.7.0.

Multi-device

hub_id (derived from MAC) is the integration's primary key. Two AmbiSense units on one network → two separate HA devices, two sets of entities, zero collisions. Same applies to AmbiSense + TankSync co-existing.

Test plan

  • Install this branch via HACS "Custom repository"
  • Power on at least one AmbiSense unit on v6.2.0-alpha.2 firmware
  • Verify HA's zeroconf flow auto-discovers it
  • Confirm device entry shows model "AmbiSense Hub"
  • Confirm "Presence Sensor" sub-device with: occupancy binary_sensor, distance sensor, target count sensor, seconds-since-seen diagnostic
  • Walk in front of the radar → occupancy flips on within ~3 s
  • Walk away + wait vacancy_secs (default 60) → occupancy flips off
  • Co-exist test: with a TankSync hub + an AmbiSense unit on the network, verify both render correctly with no entity-id collisions

Backward compatibility

No breaking changes for TankSync. The MODEL_HUB constant is preserved as an alias to MODEL_HUB_TANKSYNC. Existing entity unique_ids and device identifiers are untouched. Older firmware (no product field in /api/v1/info) falls back to TankSync labelling — same as today.

AmbiSense (radar presence + LED follow-me) advertises itself as a
SmartGhar hub with one virtual sub-device of kind="presence". Adding
this kind to the integration so HA renders the right entities for
each AmbiSense unit on the network — alongside any TankSync hubs.

Hub-model dispatch: pull `product` from /api/v1/info and pick the
device-registry model string accordingly. AmbiSense hubs now show as
"AmbiSense Hub" in Settings → Devices instead of "TankSync Hub".
Older firmware that doesn't include `product` in /api/v1/info falls
back to "TankSync Hub" — matches existing behaviour.

Entities added per `kind: "presence"` device:
  - binary_sensor: occupancy (device_class=occupancy) with stationary,
    target_count, nearest_cm, seconds_since_seen as attributes
  - sensor: nearest_cm (device_class=distance, unit cm)
  - sensor: target_count
  - sensor: seconds_since_seen (diagnostic, duration)
  - sensor: rssi_dbm (diagnostic, signal_strength, disabled by default)

Multi-device safe: hub_id (derived from MAC) is unique per device, so N
AmbiSense units on one network produce N separate HA devices with
zero entity-id collisions.

Tested against AmbiSense firmware v6.2.0-alpha.2 on ESP32-C3
(MAC d8:3b:da:35:06:f0). Discovery + entity creation + state updates
+ config writes (PUT /api/v1/devices/0) all behave as expected.

Spec: https://github.com/Techposts/AmbiSense/blob/v6-idf-rewrite/docs/SMARTGHAR-PROTOCOL.md

Co-existence test: TankSync Hub + AmbiSense both on same network →
two separate hubs in HA, both fully functional, no shared state.
Techposts pushed a commit to Techposts/AmbiSense that referenced this pull request May 7, 2026
AmbiSense now speaks the smartghar device protocol so the
smartghar-homeassistant custom integration auto-discovers it on the
network. Same protocol TankSync (aqualevel) already uses; the
integration PR adding presence-kind support is at
Techposts/smartghar-homeassistant#1

Discovery (netmgr.c):
- New mDNS service _smartghar._tcp on port 80 with TXT records
  (hub_id, product=ambisense, manufacturer=SmartGhar, schema=1.0,
  path=/api/v1/info). hub_id is ambisense_<MAC> so multiple
  AmbiSense units on one network show up as distinct HA devices.
- Legacy _ambisense._http._tcp service kept for older tools.

REST contract (webui.c, +152 lines):
- GET /api/v1/info — hub identity (manufacturer, product, model,
  fw_version, hub_id, host, ip, uptime_s, wifi_rssi, free_heap,
  capabilities). Mirrors aqualevel hub schema so the integration
  treats AmbiSense as another smartghar hub.
- GET /api/v1/devices — returns {devices: [...]} with one entry of
  kind="presence" for AmbiSense's standalone-hub topology
  (single-ESP32-doing-everything is exposed as a hub with one
  virtual sub-device).
- PUT /api/v1/devices/0 — config writes (vacancy_secs, radar_kind);
  returns updated device object so the integration reflects
  changes immediately. Auth-gated by admin password cookie.
- POST /api/v1/hub/identify — flash status LED for ~3 s at 5 Hz
  using status_led_oneshot(STATUS_LED_OTA, 3000). Auth-gated.
- POST /api/v1/hub/reboot — alias to /api/reboot, kept under the
  contract path so future legacy /api refactors don't break the
  integration.

Web UI (screens.tsx):
- System tab: removed the MQTT card (we explicitly chose the
  WS+mDNS path over MQTT — see commit log) and replaced with a
  SmartGhar HA integration card explaining auto-discovery + linking
  the integration repo + the protocol doc.
- Presence tab HA card: leads with smartghar integration as
  recommended; the RESTful /api/presence YAML recipe is moved
  behind a <details> as the no-MQTT-no-custom-component fallback.

Cross-product spec (docs/SMARTGHAR-PROTOCOL.md, NEW):
- Protocol contract every Techposts IoT device honors so future
  products (RidgeSync fingerprint lock, etc.) integrate with one
  small additive PR to smartghar-homeassistant.
- Documents both topology models: standalone hub (AmbiSense,
  mains-powered single ESP32) and hub+TX (TankSync, multi-sensor
  with battery-powered TX nodes via short-range RF).
- Wire details: mDNS TXT keys, all /api/v1 endpoints + JSON
  schemas, WS push contract, auth model, schema versioning rules.
- Adding a new product checklist + entity-builder template patches
  for the integration repo.
- Per-kind device JSON templates (presence, tank, lock for
  RidgeSync's design-ahead).

Build:
- ambisense.bin 0x1207a0 (~1.16 MB), 18% partition free.
  Recovered ~150 KB by dropping the MQTT component (was at 9% free
  in the v6.2.0-alpha.1 dev branch with mqtt + cJSON pulled in).
- UI bundle 98.5 KB raw, 28.8 KB gzipped.

Bench-tested clean-flash on ESP32-C3 d8:3b:da:35:06:f0:
- mDNS _smartghar._tcp advertises with all 5 TXT records.
- /api/v1/info, /api/v1/devices return valid JSON.
- /api/v1/devices/0 PUT round-trips vacancy_secs.
- /api/v1/hub/identify flashes the onboard LED visibly.
- Existing /api/* (web UI) endpoints unchanged.
Techposts added 3 commits May 7, 2026 19:08
HACS validation rejected keys in hacs.json that belong in manifest.json
(iot_class, documentation, issue_tracker) plus '_iot_class_note' which
is a custom key not in HACS schema. The same fields are already
correctly present in manifest.json; hacs.json just needs to be the
schema-allowed subset (name, render_readme, homeassistant, country).

Pre-existing config issue unrelated to the AmbiSense additions but
clearing it in the same PR avoids a separate roundtrip.
custom_components/smartghar/brand/icon.png + icon@2x.png — copied
from the existing assets/ directory (same source). HACS looks for
brand assets in this exact path; absent, validation fails with
'repository does not provide brand assets and is not listed in HA
brands repository'. This is the local-asset path; we can additionally
register in the upstream home-assistant/brands repo later for the
green checkmark in HA's device card.
@Techposts Techposts merged commit f09c049 into main May 7, 2026
2 checks passed
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.

1 participant