# Home Assistant Integration RF Mapper integrates with Home Assistant using webhooks. RF Mapper sends events to HA, and HA automations handle the logic. ## Features | Feature | Description | HA Entity Type | |---------|-------------|----------------| | Device presence | Track WiFi/BT devices | `device_tracker.rf_*` | | New device alerts | Notify on unknown device | `automation` trigger | | Departure alerts | Notify when device leaves | `automation` trigger | | Sensor entities | Device count, nearest distance | `sensor.rf_*` | | Multi-scanner | Room-level presence detection | per-scanner sensors | ## Architecture ### Single Scanner ``` RF Mapper Home Assistant ------------------------------------------------------ [Scan] ----webhook----> /api/webhook/rf_mapper_scan |-- automation: update sensors |-- automation: device_tracker.see [New Device] --webhook--> /api/webhook/rf_mapper_new_device |-- automation: notify [Device Gone] --webhook-> /api/webhook/rf_mapper_device_gone |-- automation: notify ``` ### Multi-Scanner Setup ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Pi #1 │ │ Pi #2 │ │ Pi #3 │ │ scanner: │ │ scanner: │ │ scanner: │ │ id: living│ │ id: kitchen│ │ id: bedroom│ │ floor: 0 │ │ floor: 0 │ │ floor: 1 │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────────┼───────────────────┘ ▼ ┌───────────────┐ │ Home Assistant │ │ - Track nearest│ │ scanner/device│ │ - Room presence │ └───────────────┘ ``` ## RF Mapper Configuration Enable webhooks in `config.yaml`: ```yaml # Scanner identity (for multi-scanner support) scanner: id: "living_room" # Unique scanner ID name: "Living Room Scanner" # Human-readable name latitude: 50.8584 # Scanner position (optional, falls back to gps.latitude) longitude: 4.3976 # Scanner position (optional, falls back to gps.longitude) floor: 0 # Scanner's floor (optional, falls back to building.current_floor) home_assistant: enabled: true url: "http://192.168.129.10:8123" webhook_scan: "rf_mapper_scan" webhook_new_device: "rf_mapper_new_device" webhook_device_gone: "rf_mapper_device_gone" device_timeout_minutes: 5 ``` ### Scanner Identity Settings | Setting | Description | |---------|-------------| | `scanner.id` | Unique scanner identifier (auto-generated from hostname if empty) | | `scanner.name` | Human-readable display name (defaults to id) | | `scanner.latitude` | Scanner latitude (falls back to `gps.latitude`) | | `scanner.longitude` | Scanner longitude (falls back to `gps.longitude`) | | `scanner.floor` | Scanner's floor number (falls back to `building.current_floor`) | ### Home Assistant Settings | Setting | Description | |---------|-------------| | `enabled` | Enable/disable HA integration | | `url` | Home Assistant URL (no trailing slash) | | `webhook_scan` | Webhook ID for scan results | | `webhook_new_device` | Webhook ID for new device alerts | | `webhook_device_gone` | Webhook ID for departure alerts | | `device_timeout_minutes` | Minutes before device is considered departed | ## Home Assistant Setup ### 1. Automations (`automations.yaml`) ```yaml # Process scan results - update device trackers with scanner info - alias: "RF Mapper - Update Device Trackers" trigger: - platform: webhook webhook_id: rf_mapper_scan action: - repeat: for_each: "{{ trigger.json.devices }}" sequence: - service: device_tracker.see data: dev_id: "rf_{{ repeat.item.id | replace(':', '_') }}" source_type: "{{ 'bluetooth' if ':' in repeat.item.id else 'router' }}" attributes: friendly_name: "{{ repeat.item.name }}" rssi: "{{ repeat.item.rssi }}" distance_m: "{{ repeat.item.distance }}" floor: "{{ repeat.item.floor }}" scanner_id: "{{ trigger.json.scanner.id }}" scanner_name: "{{ trigger.json.scanner.name }}" # New device notification (includes which scanner detected it) - alias: "RF Mapper - New Device Alert" trigger: - platform: webhook webhook_id: rf_mapper_new_device action: - service: notify.persistent_notification data: title: "New Device Detected" message: > {{ trigger.json.device_type }}: {{ trigger.json.name }} ({{ trigger.json.device_id }}) detected by {{ trigger.json.scanner.name | default('unknown scanner') }} # Device departure notification (includes last scanner) - alias: "RF Mapper - Device Left" trigger: - platform: webhook webhook_id: rf_mapper_device_gone action: - service: notify.persistent_notification data: title: "Device Left" message: > {{ trigger.json.name }} last seen {{ trigger.json.last_seen }} at {{ trigger.json.last_scanner.name | default('unknown location') }} ``` ### 2. Sensor Templates (`configuration.yaml`) ```yaml template: - trigger: - platform: webhook webhook_id: rf_mapper_scan sensor: # Per-scanner device count sensor - name: "RF Scanner {{ trigger.json.scanner.id }} Device Count" unique_id: "rf_scanner_{{ trigger.json.scanner.id }}_count" state: "{{ trigger.json.device_count }}" icon: mdi:bluetooth attributes: scanner_id: "{{ trigger.json.scanner.id }}" scanner_name: "{{ trigger.json.scanner.name }}" scanner_floor: "{{ trigger.json.scanner.floor }}" # Per-scanner nearest device sensor - name: "RF Scanner {{ trigger.json.scanner.id }} Nearest" unique_id: "rf_scanner_{{ trigger.json.scanner.id }}_nearest" state: "{{ trigger.json.devices | map(attribute='distance') | min | round(1) if trigger.json.devices else 'none' }}" unit_of_measurement: "m" icon: mdi:map-marker-distance attributes: scanner_id: "{{ trigger.json.scanner.id }}" ``` ## Webhook Payload Reference ### Scan Results (`rf_mapper_scan`) ```json { "timestamp": "2026-02-01T12:34:56.789", "scan_type": "bluetooth", "scanner": { "id": "living_room", "name": "Living Room Scanner", "latitude": 50.8584, "longitude": 4.3976, "floor": 0 }, "scanner_floor": 0, "device_count": 5, "devices": [ { "id": "AA:BB:CC:DD:EE:FF", "name": "iPhone", "rssi": -65, "distance": 3.2, "floor": 0 } ] } ``` | Field | Description | |-------|-------------| | `scanner` | Full scanner identity object | | `scanner.id` | Unique scanner identifier | | `scanner.name` | Human-readable scanner name | | `scanner.latitude` | Scanner GPS latitude | | `scanner.longitude` | Scanner GPS longitude | | `scanner.floor` | Floor where scanner is located | | `scanner_floor` | (Deprecated) Same as `scanner.floor`, for backward compatibility | ### New Device (`rf_mapper_new_device`) ```json { "timestamp": "2026-02-01T12:34:56.789", "device_id": "AA:BB:CC:DD:EE:FF", "name": "Unknown Device", "device_type": "bluetooth", "manufacturer": "Apple, Inc.", "rssi": -70, "distance_m": 5.5, "scanner": { "id": "living_room", "name": "Living Room Scanner", "latitude": 50.8584, "longitude": 4.3976, "floor": 0 } } ``` ### Device Gone (`rf_mapper_device_gone`) ```json { "timestamp": "2026-02-01T12:34:56.789", "device_id": "AA:BB:CC:DD:EE:FF", "name": "iPhone", "device_type": "bluetooth", "last_seen": "2026-02-01T12:29:00.000", "last_scanner": { "id": "living_room", "name": "Living Room Scanner", "latitude": 50.8584, "longitude": 4.3976, "floor": 0 } } ``` ## Verification Steps 1. **Enable integration**: Set `home_assistant.enabled: true` in config.yaml 2. **Add HA automations**: Copy webhook automations to HA 3. **Restart RF Mapper**: `source venv/bin/activate && python -m rf_mapper restart` 4. **Run scan**: Trigger BT scan in RF Mapper web UI 5. **Check HA**: Verify `device_tracker.rf_*` entities appear 6. **Test new device**: Clear device from DB, re-scan, verify notification 7. **Test departure**: Wait for timeout, verify departure notification 8. **Check sensors**: Verify `sensor.rf_mapper_*` values update ## Troubleshooting | Issue | Solution | |-------|----------| | Webhooks not received | Check HA URL in config, ensure no firewall blocking | | No device trackers | Verify automation is enabled in HA | | Departure not triggered | Increase `device_timeout_minutes` | | Connection timeout | Check network connectivity between RF Mapper and HA | ## Multi-Scanner Setup Configure each scanner with a unique ID and position: **Pi #1 (Living Room):** ```yaml scanner: id: "living_room" name: "Living Room Scanner" floor: 0 latitude: 50.8584 longitude: 4.3976 ``` **Pi #2 (Kitchen):** ```yaml scanner: id: "kitchen" name: "Kitchen Scanner" floor: 0 latitude: 50.8585 longitude: 4.3978 ``` **Pi #3 (Bedroom):** ```yaml scanner: id: "bedroom" name: "Bedroom Scanner" floor: 1 latitude: 50.8584 longitude: 4.3977 ``` ### Room Presence Automation ```yaml # Track which room a device is in (nearest scanner) - alias: "RF Mapper - Track Room Presence" trigger: - platform: webhook webhook_id: rf_mapper_scan action: - repeat: for_each: "{{ trigger.json.devices }}" sequence: - service: input_text.set_value target: entity_id: "input_text.rf_{{ repeat.item.id | replace(':', '_') }}_room" data: value: "{{ trigger.json.scanner.id }}" ``` ## Advanced: Presence-based Automations ```yaml # Turn on lights when specific device arrives in living room - alias: "Welcome Home - Living Room" trigger: - platform: webhook webhook_id: rf_mapper_scan condition: - condition: template value_template: > {{ trigger.json.scanner.id == 'living_room' and trigger.json.devices | selectattr('id', 'eq', 'AA:BB:CC:DD:EE:FF') | list | count > 0 }} action: - service: light.turn_on target: entity_id: light.living_room # Turn off lights when device leaves a room - alias: "Room Empty Check" trigger: - platform: webhook webhook_id: rf_mapper_scan condition: - condition: template value_template: "{{ trigger.json.scanner.id == 'bedroom' and trigger.json.device_count == 0 }}" action: - service: light.turn_off target: entity_id: light.bedroom ```