JSON Arrays to Home Assistant

I have been experimenting with Node red now for about a year and half and one of my puzzling requirements was to get the JSON array data into a Home Assistant flex-table-card.
Finally today, the penny dropped and I thought I would share this as it might help someone else with a similar requirement.
In this example I am collecting my aircraft data from a local source http call, from a raspberry pi and dongle.
I have no doubt that my solution is probably not the most elegant but it does work and I can use this to add a flow for tidal / weather data and many other sources.
Please add helpful suggestions for improvements as I am still on a huge learning curve.

[{"id":"3daf489a2c776883","type":"tab","label":"Flow 4","disabled":false,"info":"","env":[]},{"id":"03f12f98e4015b77","type":"inject","z":"3daf489a2c776883","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":220,"wires":[["75d18a2971266927"]]},{"id":"f091f62ec73b9396","type":"comment","z":"3daf489a2c776883","name":"","info":"To send object data to HA\n","x":140,"y":180,"wires":[]},{"id":"dadc2bd65aab6b77","type":"change","z":"3daf489a2c776883","name":"Payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.aircraft","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":220,"wires":[["0f10781016c6976a"]]},{"id":"9a872eccad589e97","type":"ha-api","z":"3daf489a2c776883","name":"sensor","server":"40c50b05.cc8cc4","version":1,"debugenabled":false,"protocol":"http","method":"post","path":"/api/states/{{entity_id}}","data":"","dataType":"json","responseType":"json","outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"results"}],"x":790,"y":220,"wires":[["7ba1bbdfa1031f49"]]},{"id":"0f10781016c6976a","type":"function","z":"3daf489a2c776883","name":"Array","func":"var newArray;\nvar number = 0;\nfor(var i = 0; i < msg.payload.length; i++) {\n    var newMsg = {};\n    newMsg.payload = {\n      data: {\n      entity_id: `sensor.airnum` +[i],\n      state: Date.now(),\n      attributes: {\n        'hex': msg.payload[i].hex,\n        'flight': msg.payload[i].flight,\n        'height': msg.payload[i].alt_baro,\n        'speed': msg.payload[i].gs,\n        'dir': msg.payload[i].track,\n        'lat': msg.payload[i].lat,\n        'lon': msg.payload[i].lon,\n        friendly_name: 'air'+ [i],\n        icon: 'mdi:pulse'\n      }\n    }\n  }\n    node.send(newMsg);\n}\nreturn null;   ","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":220,"wires":[["bed29c84443f2721"]]},{"id":"bed29c84443f2721","type":"change","z":"3daf489a2c776883","name":"Set ID","rules":[{"t":"set","p":"entity_id","pt":"msg","to":"payload.data.entity_id","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":220,"wires":[["9a872eccad589e97"]]},{"id":"7ba1bbdfa1031f49","type":"debug","z":"3daf489a2c776883","name":"debug 136","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":930,"y":220,"wires":[]},{"id":"75d18a2971266927","type":"http request","z":"3daf489a2c776883","name":"Aircraft","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://192.168.1.126:8080/data/aircraft.json","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":280,"y":220,"wires":[["dadc2bd65aab6b77"]]},{"id":"40c50b05.cc8cc4","type":"server","name":"Home Assistant","addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":true,"heartbeatInterval":"30","statusSeparator":"","enableGlobalContextStore":false}]

Can not run your flow as your http request is local, so we have no data.
It is best to capture the output of the http request and add it to an inject node, then others could run your flow and offer suggestions. You can capture the request node payload using the below info. You may need to shorten the test data as the forum has limits.
Your example flow would not need the http request node either if you supplied your example as suggested.

There’s a great page in the docs (Working with messages : Node-RED) that will explain how to use the debug panel to find the right path/value to any data item.

Pay particular attention to the part about the buttons that appear under your mouse pointer when you over hover a debug message property in the sidebar.

BX00Cy7yHi

The data collected from the http call is quite long per aircraft and some of it is not very useful so I elected to list only 7 objects per flight as attributes and the time of the call.
I have listed that as two aircraft in the inject node, which should give you the gist of what it does. I think that is what you meant.

[{"id":"dadc2bd65aab6b77","type":"change","z":"3daf489a2c776883","name":"Payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.aircraft","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":220,"wires":[["0f10781016c6976a"]]},{"id":"9a872eccad589e97","type":"ha-api","z":"3daf489a2c776883","name":"sensor","server":"40c50b05.cc8cc4","version":1,"debugenabled":false,"protocol":"http","method":"post","path":"/api/states/{{entity_id}}","data":"","dataType":"json","responseType":"json","outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"results"}],"x":1110,"y":220,"wires":[["7ba1bbdfa1031f49"]]},{"id":"0f10781016c6976a","type":"function","z":"3daf489a2c776883","name":"Array","func":"var newArray;\nvar number = 0;\nfor(var i = 0; i < msg.payload.length; i++) {\n    var newMsg = {};\n    newMsg.payload = {\n      data: {\n      entity_id: `sensor.airnum` +[i],\n      state: Date.now(),\n      attributes: {\n        'hex': msg.payload[i].hex,\n        'flight': msg.payload[i].flight,\n        'height': msg.payload[i].alt_baro,\n        'speed': msg.payload[i].gs,\n        'dir': msg.payload[i].track,\n        'lat': msg.payload[i].lat,\n        'lon': msg.payload[i].lon,\n        friendly_name: 'air'+ [i],\n        icon: 'mdi:pulse'\n      }\n    }\n  }\n    node.send(newMsg);\n}\nreturn null;   ","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":220,"wires":[["bed29c84443f2721"]]},{"id":"bed29c84443f2721","type":"change","z":"3daf489a2c776883","name":"Set ID","rules":[{"t":"set","p":"entity_id","pt":"msg","to":"payload.data.entity_id","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":220,"wires":[["9a872eccad589e97"]]},{"id":"7ba1bbdfa1031f49","type":"debug","z":"3daf489a2c776883","name":"debug 136","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1250,"y":220,"wires":[]},{"id":"805ba7be4d7ac8f6","type":"inject","z":"3daf489a2c776883","name":"DATA EXAMPLE","props":[{"p":"payload.aircraft[0].hex","v":"400e15","vt":"str"},{"p":"payload.aircraft[0].flight","v":"AMBER1  ","vt":"str"},{"p":"payload.aircraft[0].alt_baro","v":"19000","vt":"num"},{"p":"payload.aircraft[0].gs","v":"359.1","vt":"num"},{"p":"payload.aircraft[0].track","v":"181.4","vt":"num"},{"p":"payload.aircraft[0].lat","v":"50.93704","vt":"num"},{"p":"payload.aircraft[0].lon","v":"-4.011612","vt":"num"},{"p":"payload.now","v":"1696431883.2","vt":"num"},{"p":"payload.aircraft[1].hex","v":"4012da","vt":"str"},{"p":"payload.aircraft[1].flight","v":"IOS515R ","vt":"str"},{"p":"payload.aircraft[1].alt_baro","v":"8050","vt":"num"},{"p":"payload.aircraft[1].gs","v":"127.1","vt":"num"},{"p":"payload.aircraft[1].track","v":"234.9","vt":"num"},{"p":"payload.aircraft[1].lat","v":"50.559641","vt":"num"},{"p":"payload.aircraft[1].lon","v":"-3.943721","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":500,"y":220,"wires":[["dadc2bd65aab6b77"]]},{"id":"40c50b05.cc8cc4","type":"server","name":"Home Assistant","addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":true,"heartbeatInterval":"30","statusSeparator":"","enableGlobalContextStore":false}]

Just for completeness here is the custom: flex-table-card, data used in HA

type: custom:flex-table-card
strict: true
entities:
include: sensor.airnum*
columns:

  • name: Hex
    data: hex
  • name: Flight
    data: flight
  • name: Height
    data: height
  • name: Speed
    data: speed
  • name: Dir
    data: dir
  • name: Lat
    data: lat
  • name: Lon
    data: lon

and here is a screenshot of a live HA table

HI
When i said give sample data i did not expect you to do it the hard way, You could of copied the whole of msg.payload using the debug sidebar and just pasted it in, look at the inject i supplied.

Nothing wrong with what you did in your flow except there is no need to use JSONata J: to move aircrafts object.

I have show in my example how you could of use split node and simplified your Javascript. As always many ways to skin a cat.

example flow

[{"id":"805ba7be4d7ac8f6","type":"inject","z":"d1395164b4eec73e","name":"DATA EXAMPLE","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"aircraft\":[{\"hex\":\"400e15\",\"flight\":\"AMBER1  \",\"alt_baro\":19000,\"gs\":359.1,\"track\":181.4,\"lat\":50.93704,\"lon\":-4.011612},{\"hex\":\"4012da\",\"flight\":\"IOS515R \",\"alt_baro\":8050,\"gs\":127.1,\"track\":234.9,\"lat\":50.559641,\"lon\":-3.943721}],\"now\":1696431883.2}","payloadType":"json","x":120,"y":920,"wires":[["dadc2bd65aab6b77"]]},{"id":"dadc2bd65aab6b77","type":"change","z":"d1395164b4eec73e","name":"Payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.aircraft","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":920,"wires":[["8321021d16b5e4e9"]]},{"id":"8321021d16b5e4e9","type":"split","z":"d1395164b4eec73e","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":450,"y":920,"wires":[["0f10781016c6976a"]]},{"id":"0f10781016c6976a","type":"function","z":"d1395164b4eec73e","name":"Array","func":"msg.payload = {\n  data: {\n    entity_id: `sensor.airnum` + msg.parts.index,\n    state: Date.now(),\n    attributes: {\n      'hex': msg.payload.hex,\n      'flight': msg.payload.flight,\n      'height': msg.payload.alt_baro,\n      'speed': msg.payload.gs,\n      'dir': msg.payload.track,\n      'lat': msg.payload.lat,\n      'lon': msg.payload.lon,\n      friendly_name: 'air'+ msg.parts.index,\n      icon: 'mdi:pulse'\n    }\n  }\n}\nmsg.entity_id = msg.payload.data.entity_id;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":920,"wires":[["7ba1bbdfa1031f49"]]},{"id":"7ba1bbdfa1031f49","type":"debug","z":"d1395164b4eec73e","name":"debug 136","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":730,"y":920,"wires":[]}]

Here is the low code way just for future ideas.

[{"id":"805ba7be4d7ac8f6","type":"inject","z":"d1395164b4eec73e","name":"DATA EXAMPLE","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"aircraft\":[{\"hex\":\"400e15\",\"flight\":\"AMBER1  \",\"alt_baro\":19000,\"gs\":359.1,\"track\":181.4,\"lat\":50.93704,\"lon\":-4.011612},{\"hex\":\"4012da\",\"flight\":\"IOS515R \",\"alt_baro\":8050,\"gs\":127.1,\"track\":234.9,\"lat\":50.559641,\"lon\":-3.943721}],\"now\":1696431883.2}","payloadType":"json","x":120,"y":920,"wires":[["dadc2bd65aab6b77"]]},{"id":"dadc2bd65aab6b77","type":"change","z":"d1395164b4eec73e","name":"Payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.aircraft","tot":"msg"},{"t":"set","p":"timestamp","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":920,"wires":[["8321021d16b5e4e9"]]},{"id":"8321021d16b5e4e9","type":"split","z":"d1395164b4eec73e","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":450,"y":920,"wires":[["b625c3d8cd89a702"]]},{"id":"b625c3d8cd89a702","type":"template","z":"d1395164b4eec73e","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n  \"data\": {\n    \"entity_id\": \"sensor.airnum{{parts.index}}\",\n    \"state\": {{timestamp}},\n    \"attributes\": {\n      \"hex\": \"{{payload.hex}}\",\n      \"flight\":\"{{payload.flight}}\",\n      \"height\": {{payload.alt_baro}},\n      \"speed\":{{payload.gs}},\n      \"dir\": {{payload.track}},\n      \"lat\": {{payload.lat}},\n      \"lon\": {{payload.lon}},\n      \"friendly_name\": \"air{{parts.index}}\",\n      \"icon\": \"mdi:pulse\"\n    }\n  }\n}","output":"json","x":600,"y":920,"wires":[["368914872b4e266c"]]},{"id":"368914872b4e266c","type":"change","z":"d1395164b4eec73e","name":"","rules":[{"t":"set","p":"entity_id","pt":"msg","to":"payload.data.entity_id","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":770,"y":920,"wires":[["7ba1bbdfa1031f49"]]},{"id":"7ba1bbdfa1031f49","type":"debug","z":"d1395164b4eec73e","name":"debug 136","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":730,"y":980,"wires":[]}]

Thanks,
Ah stupid me, I didn't think I could just copy and past the debug like that.
Yes I know I didn't have to use JSNOATA to move to the .aircraft bit, I suppose I did it just to make it simpler to read in the first instance and maybe I should have reverted back.
So thanks for your help and I am learning all the time, but I love solving the puzzles by myself

Thanks for your two ideas, they are very helpful indeed and I didn't see those variations when doing this.
I suppose lack of knowledge is to blame and also it's difficult, when you get set on an idea you're stuck in the tramlines and it's difficult to veer off. This has all been another Eureka moment for me

1 Like

I have disvovered one flaw in my flow. As there is not a constant number of aircraft some old data gets left behind in my table that is not current.
For example, if there are 7 aircraft recorded and say only 5 the next time the http source is called, there will be two aircraft that are not current at the bottom of the list. I somehow need to delete all or at least the extra entities. More trickery required.

Sorry I am not a HA user, but there must be a way to blank the table before sending data. Have you fully read the docs for the HA table. You may be better off asking on the HA forum as there are not many HA users here.

I thought maybe there might be some way to remove the data from the entities before processing the next batch. I will have to have a play around with both to solve the last bit.
I have set my 6 day tide set set to run using your mod to my flow, which runs nicely and as it is always 6 days there is no issue with out of date events.

So for those who are interested, the final answer to the puzzle to get the data into Home Assistant proved to be a little trickier than I thought.
I abandoned my my last attempt because there was always obsolete data left behind, by using a separate entity for each aircraft in my table.

So with a filter to remove incomplete data from the reception of aircraft

[{"id":"19341a3840fd970d","type":"function","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"Filter","func":"if (typeof msg.payload.flight == 'undefined' ) {\n    msg.payload.flight = \"none\"    \n} else {\n}\n\nif (typeof msg.payload.lat == 'undefined' ) {\n    msg.payload.lat = 0    \n} else {\n}  \n\nif (typeof msg.payload.lon == 'undefined' ) {\n    msg.payload.lon = 0    \n} else {\n}   \nreturn msg;        ","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":460,"wires":[["b68798ee8b45137c"]]},{"id":"b68798ee8b45137c","type":"switch","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"FL?","property":"payload.flight","propertyType":"msg","rules":[{"t":"eq","v":"none","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":410,"y":460,"wires":[[],["1dd36fa4691bd8f3"]]},{"id":"1dd36fa4691bd8f3","type":"switch","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"LA?","property":"payload.lat","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":530,"y":460,"wires":[[],["f34e695288054fde"]]},{"id":"f34e695288054fde","type":"switch","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"LO?","property":"payload.lon","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":650,"y":460,"wires":[[],["805efb922b817f37"]]}]

I then collected the data as before and included a routine to determine azimuth, elevation and distance, based on my Lat, Lon and height ASL

[{"id":"841db51631d5025d","type":"function","z":"d41d834573c2bcff","g":"8607a103b47572b5","name":"abs pos","func":"//Credit: https://javascript.plainenglish.io/calculating-azimuth-distance-and-altitude-from-a-pair-of-gps-locations-36b4325d8ab0\n\n\n\nfunction ParseAngle(ang, limit) {\n    var angle = parseFloat(ang);\n    if (isNaN(angle) || (angle < -limit) || (angle > limit)) {\n        node.error(\"Invalid angle value.\", msg);\n        return null;\n    } else {\n        return angle;\n    }\n}\n\nfunction ParseElevation(el) {\n    var angle = parseFloat(el);\n    if (isNaN(angle)) {\n        node.error(\"Invalid elevation value.\", msg);\n        return null;\n    } else {\n        return angle;\n    }\n}\n\nfunction ParseLocation(pos) {\n    var lat = ParseAngle(pos.lat, 90.0);\n    var location = null;\n    if (lat != null) {\n        var lon = ParseAngle(pos.lon, 180.0);\n        if (lon != null) {\n            var alt = ParseElevation(pos.alt);\n            if (alt != null) {\n                location = { lat: lat, lon: lon, alt: alt };\n            }\n        }\n    }\n    return location;\n}\n\nfunction EarthRadiusInMeters(latitudeRadians) {\n    // latitudeRadians is geodetic, i.e. that reported by GPS.\n    // http://en.wikipedia.org/wiki/Earth_radius\n    var a = 6378137.0;  // equatorial radius in meters\n    var b = 6356752.3;  // polar radius in meters\n    var cos = Math.cos(latitudeRadians);\n    var sin = Math.sin(latitudeRadians);\n    var t1 = a * a * cos;\n    var t2 = b * b * sin;\n    var t3 = a * cos;\n    var t4 = b * sin;\n    return Math.sqrt((t1 * t1 + t2 * t2) / (t3 * t3 + t4 * t4));\n}\n\nfunction GeocentricLatitude(lat) {\n    // Convert geodetic latitude 'lat' to a geocentric latitude 'clat'.\n    // Geodetic latitude is the latitude as given by GPS.\n    // Geocentric latitude is the angle measured from center of Earth between a point and the equator.\n    // https://en.wikipedia.org/wiki/Latitude#Geocentric_latitude\n    var e2 = 0.00669437999014;\n    var clat = Math.atan((1.0 - e2) * Math.tan(lat));\n    return clat;\n}\n\nfunction LocationToPoint(c) {\n    // Convert (lat, lon, alt) to (x, y, z).\n    var lat = c.lat * Math.PI / 180.0;\n    var lon = c.lon * Math.PI / 180.0;\n    var radius = EarthRadiusInMeters(lat);\n    var clat = GeocentricLatitude(lat);\n\n    var cosLon = Math.cos(lon);\n    var sinLon = Math.sin(lon);\n    var cosLat = Math.cos(clat);\n    var sinLat = Math.sin(clat);\n    var x = radius * cosLon * cosLat;\n    var y = radius * sinLon * cosLat;\n    var z = radius * sinLat;\n\n    // We used geocentric latitude to calculate (x,y,z) on the Earth's ellipsoid.\n    // Now we use geodetic latitude to calculate normal vector from the surface, to correct for elevation.\n    var cosGlat = Math.cos(lat);\n    var sinGlat = Math.sin(lat);\n\n    var nx = cosGlat * cosLon;\n    var ny = cosGlat * sinLon;\n    var nz = sinGlat;\n\n    x += c.alt * nx;\n    y += c.alt * ny;\n    z += c.alt * nz;\n\n    return { x: x, y: y, z: z, radius: radius, nx: nx, ny: ny, nz: nz };\n}\n\nfunction Distance(ap, bp) {\n    var dx = ap.x - bp.x;\n    var dy = ap.y - bp.y;\n    var dz = ap.z - bp.z;\n    return Math.sqrt(dx * dx + dy * dy + dz * dz);\n}\n\nfunction RotateGlobe(b, a, bradius, aradius) {\n    // Get modified coordinates of 'b' by rotating the globe so that 'a' is at lat=0, lon=0.\n    var br = { lat: b.lat, lon: (b.lon - a.lon), alt: b.alt };\n    var brp = LocationToPoint(br);\n\n    // Rotate brp cartesian coordinates around the z-axis by a.lon degrees,\n    // then around the y-axis by a.lat degrees.\n    // Though we are decreasing by a.lat degrees, as seen above the y-axis,\n    // this is a positive (counterclockwise) rotation (if B's longitude is east of A's).\n    // However, from this point of view the x-axis is pointing left.\n    // So we will look the other way making the x-axis pointing right, the z-axis\n    // pointing up, and the rotation treated as negative.\n\n    var alat = GeocentricLatitude(-a.lat * Math.PI / 180.0);\n    var acos = Math.cos(alat);\n    var asin = Math.sin(alat);\n\n    var bx = (brp.x * acos) - (brp.z * asin);\n    var by = brp.y;\n    var bz = (brp.x * asin) + (brp.z * acos);\n\n    return { x: bx, y: by, z: bz, radius: bradius };\n}\n\nfunction NormalizeVectorDiff(b, a) {\n    // Calculate norm(b-a), where norm divides a vector by its length to produce a unit vector.\n    var dx = b.x - a.x;\n    var dy = b.y - a.y;\n    var dz = b.z - a.z;\n    var dist2 = dx * dx + dy * dy + dz * dz;\n    if (dist2 == 0) {\n        return null;\n    }\n    var dist = Math.sqrt(dist2);\n    return { x: (dx / dist), y: (dy / dist), z: (dz / dist), radius: 1.0 };\n}\n\nfunction Calculate(pos1, pos2) {\n   \n    var result = {\n        from: pos1,\n        to: pos2,\n    };\n\n    var a = ParseLocation(pos1);\n    if (a != null) {\n        var b = ParseLocation(pos2);\n        if (b != null) {\n            var ap = LocationToPoint(a);\n            var bp = LocationToPoint(b);\n            var distKm = 0.001 * Distance(ap, bp);\n            distKm = distKm.toFixed(0);\n            result.distanceKm = distKm;\n            var distNm = distKm *0.539956803;\n            distNm = distNm.toFixed(0);\n            result.distanceNm = distNm;\n\n            // Let's use a trick to calculate azimuth:\n            // Rotate the globe so that point A looks like latitude 0, longitude 0.\n            // We keep the actual radii calculated based on the oblate geoid,\n            // but use angles based on subtraction.\n            // Point A will be at x=radius, y=0, z=0.\n            // Vector difference B-A will have dz = N/S component, dy = E/W component.\n            var br = RotateGlobe(b, a, bp.radius, ap.radius);\n            if (br.z * br.z + br.y * br.y > 1.0e-6) {\n                var theta = Math.atan2(br.z, br.y) * 180.0 / Math.PI;\n                var azimuth = 90.0 - theta;\n                if (azimuth < 0.0) {\n                    azimuth += 360.0;\n                }\n                if (azimuth > 360.0) {\n                    azimuth -= 360.0;\n                }\n                azimuth = azimuth.toFixed(0);\n                result.azimuth = azimuth;\n            }\n\n            var bma = NormalizeVectorDiff(bp, ap);\n            if (bma != null) {\n                // Calculate altitude, which is the angle above the horizon of B as seen from A.\n                // Almost always, B will actually be below the horizon, so the altitude will be negative.\n                // The dot product of bma and norm = cos(zenith_angle), and zenith_angle = (90 deg) - altitude.\n                // So altitude = 90 - acos(dotprod).\n                var altitude = 90.0 - (180.0 / Math.PI) * Math.acos(bma.x * ap.nx + bma.y * ap.ny + bma.z * ap.nz);\n                altitude = altitude.toFixed(0);\n                result.elev_angle = altitude;\n            }\n        }\n    }\n    return result;\n}\nmsg.payload = Calculate(msg.payload.from, msg.payload.to);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":940,"y":840,"wires":[["7b789d08e5aabfcb"]]},{"id":"7b789d08e5aabfcb","type":"function","z":"d41d834573c2bcff","g":"8607a103b47572b5","name":"together","func":"//str to num\nvar distanceKm = (parseFloat(msg.payload.distanceKm));\nvar distanceNm = (parseFloat(msg.payload.distanceNm));\nvar azimuth = (parseFloat(msg.payload.azimuth));\nvar elev_angle = (parseFloat(msg.payload.elev_angle));\nvar alt = (parseFloat(msg.payload.to.alt));\nvar dir = Number(msg.payload.to.dir).toFixed(0);\ndir = (parseFloat(dir));\nvar flight = (msg.payload.to.flight).trim();\n\nmsg.payload = {\n    hex: msg.payload.to.hex,\n    flight: flight,\n    //callsign: msg.payload.to.callsign,\n    //iata: msg.payload.to.iata,\n    //codes: msg.payload.to.codes,\n    height: msg.payload.to.height,\n    speed: msg.payload.to.speed.toFixed(0),\n    dir: dir,\n    lat: Number(msg.payload.to.lat).toFixed(2),\n    lon: Number(msg.payload.to.lon).toFixed(2),\n    alt: alt,\n    km: distanceKm,\n    nm: distanceNm,\n    azimuth: azimuth,\n    elev_angle: elev_angle,\n    state: msg.payload.to.state,\n    ts: msg.payload.to.ts,\n    last_updated: msg.payload.to.last_updated,\n\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1080,"y":840,"wires":[["6d91f1a274d1f046"]]},{"id":"05dffa0d3f42da1e","type":"function","z":"d41d834573c2bcff","g":"8607a103b47572b5","name":"Set LOC","func":"\n//home position\nvar from = {\n    lat: 50.2774255,\n    lon: -3.7715363,\n    alt: 22,\n}\n\nvar to = {\n    hex: msg.to.hex,\n    flight: msg.to.flight.trim(),\n    callsign: msg.payload.callsign,\n    //iata: msg.payload._airport_codes_iata,\n    //codes: msg.payload.airport_codes,\n    height: msg.to.height,\n    alt: msg.to.height,\n    speed: msg.to.speed,\n    dir: msg.to.dir,\n    lat: msg.to.lat,\n    lon: msg.to.lon,\n    state: msg.to.state,\n    ts: msg.to.ts,\n    last_updated: msg.to.last_updated,\n\n}\n\n\nmsg.payload = {\n    from: from,\n    to: to,\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":800,"y":840,"wires":[["841db51631d5025d"]]}]

but this time I posted a xxx.json file directly to the Home Assistant www. folder as an html resource (thanks to kbrown01 for his input.

[{"id":"8ba66f53f448bfc6","type":"join","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"key","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"1","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":270,"y":580,"wires":[["a8f8e3c8252c5065"]]},{"id":"91395230bdeaaa1b","type":"json","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"","property":"payload","action":"str","pretty":false,"x":650,"y":580,"wires":[["2494bb6753a4da9d"]]},{"id":"a8f8e3c8252c5065","type":"change","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"airplanes","rules":[{"t":"set","p":"payload","pt":"msg","to":"{   \"airplanes\" : payload }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":580,"wires":[["91395230bdeaaa1b"]]},{"id":"2494bb6753a4da9d","type":"file","z":"d41d834573c2bcff","g":"4f45e06be3158e59","name":"Output","filename":"/config/www/community/skyware/airdata.json","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"setbymsg","x":790,"y":580,"wires":[["d5a167db2aeda143"]]}]

Using the REST platform I completed the operation in the config.yaml file with a sensor

  - platform: rest
    name: planes
    resource: http://192.168.1.130:8123/local/community/skyware/airdata.json
    value_template: >
      {{ value_json.airplanes | length }}
    method: GET
    scan_interval: 60
    json_attributes:
      - "airplanes"
      - "flight"
      - "height"
      - "speed"
      - "dir"
      - "lat"
      - "lon"
      - "alt"
      - "km"
      - "nm"
      - "azimuth"
      - "elev_angle"

Job done!!!

This opens up huge possibilities to me by including JSON files with attributes generated with Node-Red and placing the output directly into HA.

I hope you might be inspired by this enjoy

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.