Read network data from WiFi-specific file with automatic cleanup of legacy entries
()
| 4402 | 'ip': run(['hostname', '-I']), |
| 4403 | 'uptime': run(['uptime']), |
| 4404 | 'free_m': run(['free', '-m']), |
| 4405 | 'ip_addr': run(['ip', '-br', 'addr']), |
| 4406 | 'wifi_dev': run(['iw', 'dev']), |
| 4407 | 'wifi_link': run(['iw', 'dev', 'wlan0', 'link']), |
| 4408 | }, |
| 4409 | 'sensing_service': { |
| 4410 | 'status': run(['systemctl', 'status', 'ragnar-sensing.service', '--no-pager', '-l']), |
| 4411 | 'show': run(['systemctl', 'show', 'ragnar-sensing.service', |
| 4412 | '-p', 'ActiveState', '-p', 'SubState', '-p', 'NRestarts', |
| 4413 | '-p', 'ActiveEnterTimestamp', '-p', 'ExecMainPID', '-p', 'Environment']), |
| 4414 | }, |
| 4415 | 'journal_sensing': run(['journalctl', '-u', 'ragnar-sensing.service', |
| 4416 | '-n', '400', '--no-pager']), |
| 4417 | 'journal_ragnar': run(['journalctl', '-u', 'ragnar.service', |
| 4418 | '-n', '80', '--no-pager']), |
| 4419 | 'sockets_udp': run(['ss', '-uanp']), |
| 4420 | 'udp_capture': udp, |
| 4421 | 'packet_analysis': analyze_udp(udp.get('stdout', ''), secs), |
| 4422 | 'binaries': { |
| 4423 | 'installed': '/usr/local/bin/ragnar-sensing-server', |
| 4424 | 'installed_md5': md5('/usr/local/bin/ragnar-sensing-server'), |
| 4425 | 'vendored': repo_bin, |
| 4426 | 'vendored_md5': md5(repo_bin), |
| 4427 | }, |
| 4428 | 'api': { |
| 4429 | 'status': _rusense_get('/api/v1/status'), |
| 4430 | 'nodes': _rusense_get('/api/v1/nodes'), |
| 4431 | 'mesh': _rusense_get('/api/v1/mesh'), |
| 4432 | 'adaptive_status': _rusense_get('/api/v1/adaptive/status'), |
| 4433 | 'models_active': _rusense_get('/api/v1/models/active'), |
| 4434 | 'sensing_latest': _rusense_get('/api/v1/sensing/latest'), |
| 4435 | }, |
| 4436 | 'ragnar_state': { |
| 4437 | 'geofence': geofence, |
| 4438 | 'presence_pub': presence_pub, |
| 4439 | }, |
| 4440 | 'rusense_config': {k: v for k, v in (shared_data.config or {}).items() |
| 4441 | if isinstance(k, str) and k.startswith('rusense_')}, |
| 4442 | } |
| 4443 | return jsonify(diag) |
| 4444 | |
| 4445 | |
| 4446 | @app.route('/api/rusense/sensitivity', methods=['GET', 'POST']) |
| 4447 | def rusense_sensitivity(): |
| 4448 | """Get/set the model-free detection sensitivity: the presence floor and the |
| 4449 | debounce depth the sensing engine uses. These are the RUVIEW_PRESENCE_FLOOR / |
| 4450 | RUVIEW_DEBOUNCE_FRAMES env vars; POST writes them to a systemd drop-in |
| 4451 | (ragnar-sensing.service.d/sensitivity.conf) so they survive a RuSense |
| 4452 | reinstall and cleanly override the shipped defaults, then daemon-reloads and |
| 4453 | restarts the sensing service to apply live. Webapp runs as root.""" |
| 4454 | import subprocess |
| 4455 | cfg = shared_data.config or {} |
| 4456 | # Field-tested default balance: floor 0.15 with 6-frame debounce. |
| 4457 | DEF_FLOOR, DEF_DEB = 0.15, 6 |
| 4458 | if request.method == 'GET': |
| 4459 | return jsonify({ |
| 4460 | 'floor': float(cfg.get('rusense_presence_floor', DEF_FLOOR)), |
| 4461 | 'debounce_frames': int(cfg.get('rusense_debounce_frames', DEF_DEB)), |