- Replace rf-mapper with python -m rf_mapper throughout docs - Add note about activating venv first - Updated: CLAUDE.md, PROJECT.md, USAGE.md, CHEATSHEET.md, HOME_ASSISTANT.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
11 KiB
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:
# 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)
# 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)
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)
{
"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)
{
"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)
{
"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
- Enable integration: Set
home_assistant.enabled: truein config.yaml - Add HA automations: Copy webhook automations to HA
- Restart RF Mapper:
source venv/bin/activate && python -m rf_mapper restart - Run scan: Trigger BT scan in RF Mapper web UI
- Check HA: Verify
device_tracker.rf_*entities appear - Test new device: Clear device from DB, re-scan, verify notification
- Test departure: Wait for timeout, verify departure notification
- 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):
scanner:
id: "living_room"
name: "Living Room Scanner"
floor: 0
latitude: 50.8584
longitude: 4.3976
Pi #2 (Kitchen):
scanner:
id: "kitchen"
name: "Kitchen Scanner"
floor: 0
latitude: 50.8585
longitude: 4.3978
Pi #3 (Bedroom):
scanner:
id: "bedroom"
name: "Bedroom Scanner"
floor: 1
latitude: 50.8584
longitude: 4.3977
Room Presence Automation
# 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
# 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