MCPcopy Create free account
hub / github.com/PierreGode/Ragnar / rusense_calibrate_sample

Function rusense_calibrate_sample

webapp_modern.py:1531–1575  ·  view source on GitHub ↗

Sample the engine's smoothed-motion (sm) for a few seconds and return its distribution. Used by the guided sensitivity calibration: the UI runs this once with the room EMPTY and once while the user WALKS, then sets the floor in the gap between the two. sm is derived from the classificati

()

Source from the content-addressed store, hash-verified

1529 if key not in seen:
1530 del windows[key]
1531 positions = shared_data.config.get('rusense_node_positions') or {}
1532 verdict = _rusense_geofence_verdict(positions, windows) if gf_enabled else None
1533 _rusense_notify_state['last_geofence'] = verdict
1534 if verdict and verdict.get('ok') and verdict.get('energetic') and not verdict.get('inside_perimeter'):
1535 block_motion = True
1536
1537 # ── Presence / motion / people, from the latest sensing frame ──
1538 latest = _rusense_get('/api/v1/sensing/latest')
1539 if latest:
1540 cls = latest.get('classification') or {}
1541 present_agg = bool(cls.get('presence'))
1542 # A *motionless* body makes the aggregate `presence` boolean flicker, so
1543 # the per-node classification is a useful still-body signal. BUT a single
1544 # node false-fires on its own (live: one weak/edge node reads
1545 # present_still ~4-6% of the time in an EMPTY apartment — a through-wall
1546 # neighbour or RF noise near that node), and a lone node was enough to
1547 # confirm presence → phantom alerts. So require a QUORUM: at least two
1548 # non-stale nodes must agree before the node path confirms presence (with
1549 # only one node total, fall back to that one). A real occupant perturbs
1550 # the CSI to multiple nodes and/or lights up the fused aggregate below,
1551 # so this keeps real detection while killing lone-node phantoms — the
1552 # same corroboration rule the people-count already uses. See
1553 # [[rusense-presence-hold]].
1554 node_hits = 0
1555 node_total = 0
1556 for nf in (latest.get('node_features') or []):
1557 if nf.get('stale'):
1558 continue
1559 node_total += 1
1560 ncls = nf.get('classification') or {}
1561 if ncls.get('presence') or str(ncls.get('motion_level') or '').startswith('present'):
1562 node_hits += 1
1563 # Base presence on the engine's MOTION CLASSIFIER, not the raw `presence`
1564 # boolean (a still subject flickers the boolean; motion_level is steadier).
1565 motion_level = str(cls.get('motion_level') or '')
1566 engine_present = motion_level.startswith('present') or motion_level == 'active'
1567 # Corroborated presence: require TWO independent votes before confirming.
1568 # The fused aggregate motion classifier is ONE vote; each non-stale node
1569 # is one vote. This rejects the two live failure modes seen in an EMPTY
1570 # apartment: (a) a single weak/edge node twitching alone (through-wall
1571 # neighbour / RF noise), and (b) the aggregate motion heuristic firing on
1572 # an environmental RF burst — HVAC, fridge, hallway — for 10+ s with NO
1573 # node agreeing (this alone crossed the duty gate and lingered PRESENT for
1574 # minutes in a 15-min soak). A real occupant perturbs the CSI to multiple
1575 # nodes AND/OR the aggregate, so it clears 2 votes easily. With only one
1576 # node deployed there can't be 2 votes, so fall back to "any signal".
1577 votes = (1 if engine_present else 0) + node_hits
1578 if node_total >= 2:

Callers

nothing calls this directly

Calls 6

_rusense_getFunction · 0.85
pctFunction · 0.85
appendMethod · 0.80
sortMethod · 0.80
getMethod · 0.45
sleepMethod · 0.45

Tested by

no test coverage detected