FlightRadar24 planes overhead using Node Red

I’ve been feeding ADS-B data to FlightRadar24 since before I started using HA. I’ve always wanted to capture my local “planes overhead” data and show it on my dashboard, but have frankly been too lazy until now. I finally decided to get to work and use Node Red to my advantage, and it’s great!

Here’s how it works in a nutshell -

  1. Local API call to my Raspberry Pi running FlightRadar24’s feeder software.
  2. Parse JSON data and find the newest aircraft with “flight” info (this can be either a callsign or tail number).
  3. Use the Node Red integration to create an entity in HA with the “flight” info as the state.
  4. Add some other data as attributes (speed, lat, long, etc).
  5. Show the last flight overhead on Lovelace
  • (optional) Reboot the software every two days… it has a tendency to cease collecting data without warning.
  • (optional) Send a notification via Telegram if an aircraft is detected with an emergency squawk code.

Code for Node Red here:

[{"id":"06aeebb4b00fdca0","type":"tab","label":"Airshare","disabled":false,"info":""},{"id":"b3050475b9282833","type":"http request","z":"06aeebb4b00fdca0","name":"","method":"GET","ret":"txt","paytoqs":"ignore","url":"http://IP_OF_YOUR_FR24_FEEDER:8080/data/aircraft.json","tls":"","persist":false,"proxy":"","authType":"","x":270,"y":140,"wires":[["787407227200ba2f"]]},{"id":"4525baba29804a36","type":"inject","z":"06aeebb4b00fdca0","name":"10s","props":[{"p":"payload"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"","payloadType":"date","x":110,"y":140,"wires":[["b3050475b9282833"]]},{"id":"787407227200ba2f","type":"json","z":"06aeebb4b00fdca0","name":"","property":"payload","action":"","pretty":false,"x":430,"y":140,"wires":[["530db59b100a47d6"]]},{"id":"530db59b100a47d6","type":"function","z":"06aeebb4b00fdca0","name":"Plane data","func":"var planes = msg.payload.aircraft;\nvar zeroPlanes;\nvar arrayLength = planes.length;\nfor (var i = 0; i < arrayLength; i++) {\n    zeroPlanes = planes[i];\n    if(zeroPlanes.flight)\n    {\n        msg.hex = zeroPlanes.hex;\n        msg.squawk = zeroPlanes.squawk;\n        msg.lat = zeroPlanes.lat;\n        msg.long = zeroPlanes.lon;\n        msg.altitude = zeroPlanes.altitude;\n        msg.flight = zeroPlanes.flight;\n        msg.seen = zeroPlanes.seen;\n        msg.vert_rate = zeroPlanes.vert_rate;\n        msg.speed = zeroPlanes.speed;\n        msg.track = zeroPlanes.track;\n        msg.seen_pos = zeroPlanes.seen_pos;\n        msg.emergency = zeroPlanes.emergency;\n        break;\n    }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":140,"wires":[["c801b9245b686ae8","08e92615957d75f8"]]},{"id":"c801b9245b686ae8","type":"switch","z":"06aeebb4b00fdca0","name":"Flight number?","property":"flight","propertyType":"msg","rules":[{"t":"istype","v":"undefined","vt":"undefined"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":460,"y":240,"wires":[[],["5b78c7805ed4eecc"]]},{"id":"007262ddef25853d","type":"http request","z":"06aeebb4b00fdca0","name":"","method":"GET","ret":"txt","paytoqs":"ignore","url":"http://IP_OF_YOUR_FR24_FEEDER:8754/restart.html","tls":"","persist":false,"proxy":"","authType":"","x":370,"y":440,"wires":[[]]},{"id":"1b25d82a78478de4","type":"inject","z":"06aeebb4b00fdca0","name":"Tue/Thu/Sat at 4am","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 04 * * 2,4,6","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":160,"y":440,"wires":[["007262ddef25853d"]]},{"id":"1a8f929f4b64a21c","type":"comment","z":"06aeebb4b00fdca0","name":"Get nearby planes","info":"","x":150,"y":80,"wires":[]},{"id":"f01443e29f3f067a","type":"comment","z":"06aeebb4b00fdca0","name":"Reboot planespotter every few days","info":"","x":200,"y":380,"wires":[]},{"id":"08e92615957d75f8","type":"switch","z":"06aeebb4b00fdca0","name":"Emergency?","property":"emergency","propertyType":"msg","rules":[{"t":"istype","v":"undefined","vt":"undefined"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":450,"y":300,"wires":[[],["d189793fbe5c7bb0"]]},{"id":"d189793fbe5c7bb0","type":"function","z":"06aeebb4b00fdca0","name":"Message contents","func":"msg.payload = {\"type\": \"message\", \"content\": \"Aircraft detected with emergency! Flight: \" + msg.flight + \" Emergency: \" + msg.emergency, \"chatId\": YOUR_TELEGRAM_CHAT_ID}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":300,"wires":[["ba68262dc66b5324"]]},{"id":"ba68262dc66b5324","type":"function","z":"06aeebb4b00fdca0","name":"Your Telegram bot here!","func":"Replace this node with a \"Telegram\" node. You must install Telegram nodes in Node Red. ","outputs":1,"noerr":16,"initialize":"","finalize":"","libs":[],"x":910,"y":300,"wires":[[]]},{"id":"ce89da9c70785548","type":"comment","z":"06aeebb4b00fdca0","name":"Important note about the entity node","info":"You must install the Node Red HA integration!\n\nCheck the docs for this node. \n\nhttps://github.com/zachowj/hass-node-red","x":800,"y":200,"wires":[]},{"id":"5b78c7805ed4eecc","type":"ha-entity","z":"06aeebb4b00fdca0","name":"Planes","server":"95d0e223.bc05a","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":""},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"flight","stateType":"msg","attributes":[{"property":"Speed","value":"speed","valueType":"msg"},{"property":"Track","value":"track","valueType":"msg"},{"property":"Altitude","value":"altitude","valueType":"msg"},{"property":"Squawk","value":"squawk","valueType":"msg"},{"property":"Lat","value":"lat","valueType":"msg"},{"property":"Long","value":"long","valueType":"msg"},{"property":"Vertical rate","value":"vert_rate","valueType":"msg"},{"property":"Time since seen","value":"seen","valueType":"msg"},{"property":"Time since position change","value":"seen_pos","valueType":"msg"}],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":630,"y":240,"wires":[[]]},{"id":"95d0e223.bc05a","type":"server","name":"Home Assistant","version":1,"legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"credentials":{}}]
2 Likes

Hi Ben,

Many thanks for sharing this! I got it to work without errors and now I’m trying to understand how to use it. From what I see, this updates a dedicated node’s properties with the flight details of the flight closest to me. Is that correct? [edit: noticed after reading better that it is actually the newest detected flight] It would be great to have it create an entity for each flight with the flight number as a name, but I guess that was a lot more difficult? I did make one change; by renaming properties “lat” and “long” to “latitude” and “longitude”, the entity can be displayed on a map without any conversion.

Do you have any future plans for this? Would be happy to at least help with some ideas…

Hi Medivb,

So glad you like the Node Red flow! When posting here, my goal was for just one other person to find it cool/useful. I did make one change since posting which is adding the distance of the plane from my house (updated “Plane Data” function node code follows with a TODO item to add your house’s lat/long). Don’t forget to add a distance attribute to the entity node with a value of msg.distance.

I just changed lat and long to latitude and longitude and will check out the mapping! Thanks for the tip.

For an entity that tracks each flight, you have to install the Node Red integration. This is a requirement for the entity node to work. Once you complete that integration, your history will look something like the following and each color has an associated flight number!

I don’t currently have plans to make changes, but am 100% open to some ideas.

var planes = msg.payload.aircraft;
var zeroPlanes;
var arrayLength = planes.length;
for (var i = 0; i < arrayLength; i++) {
    zeroPlanes = planes[i];
    if(zeroPlanes.flight)
    {
        msg.hex = zeroPlanes.hex;
        msg.squawk = zeroPlanes.squawk;
        msg.lat = zeroPlanes.lat;
        msg.long = zeroPlanes.lon;
        // If lat and lon, get distance from home
        if(zeroPlanes.lat && zeroPlanes.lon){
**** TODO NEXT TWO LINES **** (remove this line)
            var homeLat = REPLACE_THIS_WITH_YOUR_HOME_LATITUDE_!!!!!!!!!!_!!!!!!!!!!;
            var homeLon = REPLACE_THIS_WITH_YOUR_HOME_LONGITUDE_!!!!!!!!!!_!!!!!!!!!!;
            var R = 6371; // km
            var dLat = (homeLat - zeroPlanes.lat) * Math.PI / 180;
            var dLon = (homeLon - zeroPlanes.lon) * Math.PI / 180;
            var lat1 = (zeroPlanes.lat) * Math.PI / 180;
            var lat2 = (homeLat) * Math.PI / 180;
            
            var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
            var d = R * c;
            var milesD = d / 1.609;
            milesD = Math.round(milesD * 10) / 10;
            msg.distance = milesD;
        }
        msg.altitude = zeroPlanes.altitude;
        msg.flight = zeroPlanes.flight;
        msg.seen = zeroPlanes.seen;
        msg.vert_rate = zeroPlanes.vert_rate;
        msg.speed = zeroPlanes.speed;
        msg.track = zeroPlanes.track;
        msg.seen_pos = zeroPlanes.seen_pos;
        msg.emergency = zeroPlanes.emergency;
        break;
    }
}

return msg;

Hi Ben,

I did install the add-on, so I am getting full benefits of the updating single sensor, which is already great. I added the distance and that seems to be working too, so awesome.

My ideal scenario would be that I would create a device for the receiver, and add entities for all actively detected planes, so I could create a radarmap with all planes on it inside HAS. I am trying to achieve that by adding an MQTT out node and having Mosquito take care of the receiving side and entity creation. This worked fine for sending all sorts of system sensor data from my PI’s, but I have not been able to get it to work for the aircraft data.

If I succeed, I will let you know!

Again, thanks for the jumpstart on this… Was really struggling to get this off the ground from scratch.

Medi

Ah I understand now, my mistake.

The only solution I’ve been able to think of is use x number of entity nodes and modify the “Plane Data” code to save info for multiple planes in the message object. Then send the info from plane 0 to the first entity node, info from plane 1 to the second entity node, and so on. The only problem with this if you are limited to x planes on the map; it’s not a dynamic solution.

I am trying to achieve that by adding an MQTT out node and having Mosquito take care of the receiving side and entity creation.

The quoted reply above seems like a good way to go.