Skip to content

Instantly share code, notes, and snippets.

@tigattack
Last active March 4, 2024 23:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tigattack/73be9f9df722b546c6ad4785957bd813 to your computer and use it in GitHub Desktop.
Save tigattack/73be9f9df722b546c6ad4785957bd813 to your computer and use it in GitHub Desktop.
Node-RED flows to send Home Assistant device tracker data to Owntracks via MQTT or HTTP - https://blog.tiga.tech/posts/selfhost-location-history
[
{
"id": "978f06ed.f5189",
"type": "trigger-state",
"z": "90f1f83a.381b38",
"name": "Example Device Tracker",
"server": "4bc0b8a8.1c9238",
"version": 4,
"inputs": 1,
"outputs": 2,
"exposeAsEntityConfig": "",
"entityId": "device_tracker.DEVICE_HERE",
"entityIdType": "exact",
"debugEnabled": false,
"constraints": [],
"customOutputs": [],
"outputInitially": true,
"stateType": "str",
"enableInput": true,
"exposeToHomeAssistant": false,
"haConfig": [
{
"property": "name",
"value": ""
},
{
"property": "icon",
"value": ""
}
],
"x": 130,
"y": 520,
"wires": [
[
"9b4e4feb99a9b7d4"
],
[]
]
},
{
"id": "9b4e4feb99a9b7d4",
"type": "change",
"z": "90f1f83a.381b38",
"name": "set vars",
"rules": [
{
"t": "set",
"p": "device_battery",
"pt": "msg",
"to": "sensor.DEVICE_HERE_battery_level",
"tot": "str"
},
{
"t": "set",
"p": "tracker_id",
"pt": "msg",
"to": "TRACKER_ID_HERE",
"tot": "str"
},
{
"t": "set",
"p": "user_id",
"pt": "msg",
"to": "USER_HERE",
"tot": "str"
},
{
"t": "set",
"p": "device_id",
"pt": "msg",
"to": "DEVICE_SHORT_ID_HERE",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 320,
"y": 520,
"wires": [
[
"5b3dc93c.892c5"
]
]
},
{
"id": "5b3dc93c.892c5",
"type": "delay",
"z": "90f1f83a.381b38",
"name": "rate limit 1 per 10s",
"pauseType": "timed",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "10",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"allowrate": false,
"outputs": 1,
"x": 510,
"y": 540,
"wires": [
[
"301f763b.98140a"
]
]
},
{
"id": "301f763b.98140a",
"type": "function",
"z": "90f1f83a.381b38",
"name": "device data to owntracks json",
"func": "const globalHomeAssistant = global.get('homeassistant');\n\n// Get timestamp as epoch format\nconst timestamp = Math.floor(Date.now() / 1000);\n\n// Entity ID of battery level sensor from the mobile app\nconst battery_level = globalHomeAssistant.homeAssistant.states[msg.device_battery].state;\n\n// Get tracker ID (2 chars)\nconst tracker_id = msg.tracker_id;\n\n// Set the connection type. Hardcoded to wireless since it's an unimportant metric.\nconst connection_type = 'w';\n\n// Get device GPS accuracy\nconst gps_accuracy = msg.data.event.new_state.attributes.gps_accuracy;\n\n// Get device vertical accuracy\nconst vertical_accuracy = msg.data.event.new_state.attributes.vertical_accuracy;\n\n// Get device altitude\nconst altitude = msg.data.event.new_state.attributes.altitude;\n\n// Get device latitude\nconst latitude = msg.data.event.new_state.attributes.latitude;\n\n// Get device longitude\nconst longitude = msg.data.event.new_state.attributes.longitude;\n\n// Get device speed\n// The device_tracker entity from the HASS mobile app uses m/s as the unit\n// for the speed attribute, so we multiply by 3.6 to convert to the expected km/h\nconst velocity = msg.data.event.new_state.attributes.speed * 3.6;\n\n// Get device course\nconst course = msg.data.event.new_state.attributes.course;\n\n// Construct msg.payload as a JavaScript object\nmsg.payload = {\n _type: 'location',\n acc: gps_accuracy,\n alt: altitude,\n batt: battery_level,\n conn: connection_type,\n lat: latitude,\n lon: longitude,\n tid: tracker_id,\n tst: timestamp,\n vac: vertical_accuracy,\n vel: velocity,\n cog: course,\n};\n\n// Convert msg.payload to minified JSON\nmsg.payload = JSON.stringify(msg.payload);\n\nreturn msg;\n",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 770,
"y": 540,
"wires": [
[
"8c852383ada7d61a"
]
]
},
{
"id": "8c852383ada7d61a",
"type": "http request",
"z": "90f1f83a.381b38",
"name": "POST to Owntracks",
"method": "POST",
"ret": "txt",
"paytoqs": "ignore",
"url": "http://OWNTRACKS_URL_HERE/pub",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [
{
"keyType": "other",
"keyValue": "X-Limit-U",
"valueType": "msg",
"valueValue": "user_id"
},
{
"keyType": "other",
"keyValue": "X-Limit-D",
"valueType": "msg",
"valueValue": "device_id"
}
],
"x": 1040,
"y": 540,
"wires": [
[]
]
},
{
"id": "4bc0b8a8.1c9238",
"type": "server",
"name": "Home Assistant",
"version": 5,
"addon": true,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true,
"cacheJson": true,
"heartbeat": false,
"heartbeatInterval": 30,
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": "at: ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "h23",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": true
}
]
[
{
"id": "9308b54f7f75ffdd",
"type": "trigger-state",
"z": "90f1f83a.381b38",
"name": "Example Device Tracker",
"server": "4bc0b8a8.1c9238",
"version": 4,
"inputs": 1,
"outputs": 2,
"exposeAsEntityConfig": "",
"entityId": "device_tracker.DEVICE_HERE",
"entityIdType": "exact",
"debugEnabled": false,
"constraints": [],
"customOutputs": [],
"outputInitially": true,
"stateType": "str",
"enableInput": true,
"exposeToHomeAssistant": false,
"haConfig": [
{
"property": "name",
"value": ""
},
{
"property": "icon",
"value": ""
}
],
"x": 130,
"y": 740,
"wires": [
[
"3595ec02e60879f2"
],
[]
]
},
{
"id": "3595ec02e60879f2",
"type": "change",
"z": "90f1f83a.381b38",
"name": "set vars",
"rules": [
{
"t": "set",
"p": "device_topic",
"pt": "msg",
"to": "owntracks/USER_HERE/DEVICE_SHORT_ID_HERE",
"tot": "str"
},
{
"t": "set",
"p": "device_battery",
"pt": "msg",
"to": "sensor.DEVICE_HERE_battery_level",
"tot": "str"
},
{
"t": "set",
"p": "tracker_id",
"pt": "msg",
"to": "TRACKER_ID_HERE",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 320,
"y": 740,
"wires": [
[
"5379fbde12ca52cd"
]
]
},
{
"id": "5379fbde12ca52cd",
"type": "delay",
"z": "90f1f83a.381b38",
"name": "rate limit 1 per 10s",
"pauseType": "timed",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "10",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"allowrate": false,
"outputs": 1,
"x": 510,
"y": 760,
"wires": [
[
"7d7e5526a1b19941"
]
]
},
{
"id": "7d7e5526a1b19941",
"type": "function",
"z": "90f1f83a.381b38",
"name": "device data to owntracks json",
"func": "const globalHomeAssistant = global.get('homeassistant');\n\n// Get timestamp as epoch format\nconst timestamp = Math.floor(Date.now() / 1000);\n\n// Entity ID of battery level sensor from the mobile app\nconst battery_level = globalHomeAssistant.homeAssistant.states[msg.device_battery].state;\n\n// Get tracker ID (2 chars)\nconst tracker_id = msg.tracker_id;\n\n// Set the connection type. Hardcoded to wireless since it's an unimportant metric.\nconst connection_type = 'w';\n\n// Get device GPS accuracy\nconst gps_accuracy = msg.data.event.new_state.attributes.gps_accuracy;\n\n// Get device vertical accuracy\nconst vertical_accuracy = msg.data.event.new_state.attributes.vertical_accuracy;\n\n// Get device altitude\nconst altitude = msg.data.event.new_state.attributes.altitude;\n\n// Get device latitude\nconst latitude = msg.data.event.new_state.attributes.latitude;\n\n// Get device longitude\nconst longitude = msg.data.event.new_state.attributes.longitude;\n\n// Get device speed\n// The device_tracker entity from the HASS mobile app uses m/s as the unit\n// for the speed attribute, so we multiply by 3.6 to convert to the expected km/h\nconst velocity = msg.data.event.new_state.attributes.speed * 3.6;\n\n// Get device course\nconst course = msg.data.event.new_state.attributes.course;\n\n// Construct msg.payload as a JavaScript object\nmsg.payload = {\n _type: 'location',\n acc: gps_accuracy,\n alt: altitude,\n batt: battery_level,\n conn: connection_type,\n lat: latitude,\n lon: longitude,\n tid: tracker_id,\n tst: timestamp,\n vac: vertical_accuracy,\n vel: velocity,\n cog: course,\n};\n\n// Convert msg.payload to minified JSON\nmsg.payload = JSON.stringify(msg.payload);\n\nreturn msg;\n",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 770,
"y": 760,
"wires": [
[
"7173b8da1b8076fb"
]
]
},
{
"id": "7173b8da1b8076fb",
"type": "change",
"z": "90f1f83a.381b38",
"name": "set topic",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "device_topic",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1000,
"y": 760,
"wires": [
[
"34a06109bd9fea34"
]
]
},
{
"id": "34a06109bd9fea34",
"type": "mqtt out",
"z": "90f1f83a.381b38",
"name": "mqtt owntracks",
"topic": "",
"qos": "0",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "16253ac6.f8ef75",
"x": 1180,
"y": 760,
"wires": []
},
{
"id": "4bc0b8a8.1c9238",
"type": "server",
"name": "Home Assistant",
"version": 5,
"addon": true,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true,
"cacheJson": true,
"heartbeat": false,
"heartbeatInterval": 30,
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": "at: ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "h23",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": true
},
{
"id": "16253ac6.f8ef75",
"type": "mqtt-broker",
"name": "MQTT hass",
"broker": "core-mosquitto",
"port": "1883",
"clientid": "nodered",
"autoConnect": true,
"usetls": false,
"compatmode": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
}
]
// NOTICE: You don't need this file, it's just here for reference as it's a core part of the logic in these flows.
const globalHomeAssistant = global.get('homeassistant');
// Get timestamp as epoch format
const timestamp = Math.floor(Date.now() / 1000);
// Entity ID of battery level sensor from the mobile app
const battery_level = globalHomeAssistant.homeAssistant.states[msg.device_battery].state;
// Get tracker ID (2 chars)
const tracker_id = msg.tracker_id;
// Set the connection type. Hardcoded to wireless since it's an unimportant metric.
const connection_type = 'w';
// Get device GPS accuracy
const gps_accuracy = msg.data.event.new_state.attributes.gps_accuracy;
// Get device vertical accuracy
const vertical_accuracy = msg.data.event.new_state.attributes.vertical_accuracy;
// Get device altitude
const altitude = msg.data.event.new_state.attributes.altitude;
// Get device latitude
const latitude = msg.data.event.new_state.attributes.latitude;
// Get device longitude
const longitude = msg.data.event.new_state.attributes.longitude;
// Get device speed
// The device_tracker entity from the HASS mobile app uses m/s as the unit
// for the speed attribute, so we multiply by 3.6 to convert to the expected km/h
const velocity = msg.data.event.new_state.attributes.speed * 3.6;
// Get device course
const course = msg.data.event.new_state.attributes.course;
// Construct msg.payload as a JavaScript object
msg.payload = {
_type: 'location',
acc: gps_accuracy,
alt: altitude,
batt: battery_level,
conn: connection_type,
lat: latitude,
lon: longitude,
tid: tracker_id,
tst: timestamp,
vac: vertical_accuracy,
vel: velocity,
cog: course,
};
// Convert msg.payload to minified JSON
msg.payload = JSON.stringify(msg.payload);
return msg;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment