Skip to content

Instantly share code, notes, and snippets.

@erkr
Last active March 13, 2026 12:18
Show Gist options
  • Select an option

  • Save erkr/6dbe8c9ec6feaef8102afff3786a5bd0 to your computer and use it in GitHub Desktop.

Select an option

Save erkr/6dbe8c9ec6feaef8102afff3786a5bd0 to your computer and use it in GitHub Desktop.
Compose one device tracker for a person in Home Assistant by composing multiple stationary and gps trackers
blueprint:
name: Composite person device tracker
description: compose one tracker for a person by composing multiple stationary (BLE/Router)
and GPS trackers
domain: automation
input:
composite_name:
name: composite name
description: The name for the composite tracker. Be carefull when changing the
name afterwards, as the automation will create a composite tracker when it
doesn't exist yet! (Trackers can be deleted by removing them from known_devices.yaml)
selector:
text:
multiline: false
multiple: false
trigger_at_startup:
name: Trigger at Home Assistant startup
description: Initialize the composite tracker at startup, based on the stationary trackers.
default: true
selector:
boolean: {}
input_trackers:
name: input trackers
description: Select (bluetooth, router or GPS) device_trackers and/or (connectivity or presence) binary sensors
selector:
entity:
filter:
- domain: device_tracker
- domain: binary_sensor
device_class:
- connectivity
- presence
multiple: true
reorder: false
source_url: https://gist.github.com/erkr/6dbe8c9ec6feaef8102afff3786a5bd0#file-composite_device_tracker-yaml
triggers:
- trigger: state
entity_id: !input input_trackers
id: 'tracker'
variables:
latitude: '{{ trigger.to_state.attributes.latitude | default(0) }}'
longitude: '{{ trigger.to_state.attributes.longitude| default(0) }}'
gps_accuracy: '{{ trigger.to_state.attributes.gps_accuracy | default(10) }}'
gps_update: "{{ latitude!=0 and longitude!=0 }}"
enabled: true
- trigger: homeassistant
event: start
id: 'startup'
conditions:
- alias: Filter non-relevant stationary triggers
or:
- condition: template
value_template: "{{ trigger.id=='startup' or gps_update|default(false) }}"
- condition: template
value_template: >
{% set old_state=states(composite_tracker) %}
{% set home=['home','on'] %}
{{ trigger.to_state.state not in home
if old_state=='home' else
trigger.to_state.state in home}}
actions:
- choose:
- conditions:
- alias: special activations
or:
- condition: template
value_template: '{{ trigger is not defined or trigger.id is not defined }}'
alias: manual execution of automation
- condition: template
value_template: "{{ trigger.id=='startup' and trigger_at_startup }}"
alias: Initialize at startup
sequence:
- variables:
on_premises: >
{{ expand(input_trackers)
|rejectattr('attributes.latitude','defined')
|rejectattr('attributes.longitude','defined')
|selectattr('state','in',['home','on'])|list|count> 0 }}
new_state: '{{ ''home'' if on_premises else ''not_home'' }}'
- action: device_tracker.see
data:
dev_id: '{{composite_name}}'
location_name: '{{ new_state }}'
alias: Publish startup location name
- conditions:
- condition: template
value_template: "{{ trigger.id=='tracker' and gps_update|default(false) }}"
alias: If GPS coordinates known
sequence:
- action: device_tracker.see
data:
gps:
- '{{latitude}}'
- '{{longitude}}'
dev_id: '{{composite_name}}'
gps_accuracy: '{{gps_accuracy}}'
alias: Publish GPS
- conditions:
- condition: template
value_template: "{{ trigger.id=='tracker' and not gps_update|default(false) }}"
alias: If triggered by a stationary tracker
sequence:
- alias: If the stationary tracker reports home, or all report away
if:
- condition: template
value_template: "{{ trigger.to_state.state in ['home','on'] }}"
alias: Home comming trigger
then:
- action: device_tracker.see
data:
dev_id: '{{composite_name}}'
location_name: home
alias: Publish home
else:
- alias: If Leaving was reported by the last stationary tracker
if:
- condition: template
value_template: >
{{ expand(input_trackers)
|rejectattr('attributes.latitude','defined')
|rejectattr('attributes.longitude','defined')
|selectattr('state','in',['home','on'])|list|count == 0 }}
alias: Away was triggered by the last stationary tracker
then:
- variables:
old_state: '{{ states(composite_tracker) }}'
away_state: >
{{ old_state if (old_state not in ['home','unknown','unavailable'])
else 'not_home' }}
- action: device_tracker.see
data:
dev_id: '{{composite_name}}'
location_name: '{{ away_state }}'
alias: Publish away
trigger_variables:
nice_name: !input composite_name
composite_name: '{{ nice_name|slugify }}'
composite_tracker: "{{ 'device_tracker.'~composite_name }}"
input_trackers: !input input_trackers
trigger_at_startup: !input trigger_at_startup
mode: queued
trace:
stored_traces: 20
max: 10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment