Last active
March 4, 2024 23:47
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"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 | |
} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"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": "" | |
} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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