WrtPresence 2023.11.0 Openwrt tracker (Openwrt->MQTT) Work in progress

Hello everyone, a new simplified version. Openwrt now sends directly to MQTT.

WrtPresence-HA-MQTT

Old version not finished. Slight problem when changing from one access point to another.

How to install

:warning: Work in progress

openwrt-presence (WrtPresence)

OpenWrt device presence detection bash script. Runs on multiple APs. Listens “passively” for events from the OpenWrt logread via syslog-ng on a master AP. Can “actively” resynchronise by running “wrtwifistareport” on slave APs every 5 minutes in the event of missed events. Events are transmitted to node-red. Node-red takes care of creating or updating your device_tracker. This is then transmitted to Homeassistant via MQTT.
There is no link with knows_device. You find your device in a WrtPresence MQTT device. You can view the attributes of each device, if your device changes access point it will update.
To delete a device, I recommend using MQTT Explorer.

OpenWrt → node-red → mqtt → homeassitant

Exemple : device_tracker discovery:

topic = homeassistant/device_tracker/ab12cd23ab12/config (unique_id = Address mac)

{
  "unique_id": "ab12cd23ab12", (Address mac)
  "name": "Computer HP", (hostname in openwrt if available in dhcp.leases otherwise mac address)
  "device": {
    "manufacturer": "Openwrt", (Can be modified in the MQTT Config node)
    "model": "Xiaomi Ax3600", (Can be modified in the MQTT Config node)
    "name": "WrtPresence", (Can be modified in the MQTT Config node)
    "identifiers": [
      "WrtPresence" (Can be modified in the MQTT Config node)
    ]
  },
  "state_topic": "openwrt/ab12cd23ab12/state", (unique_id = Address mac)
  "payload_home": "home",
  "payload_not_home": "not_home",
  "entity_category": "diagnostic",
  "json_attributes_topic": "openwrt/ab12cd23ab12/attributes" (unique_id = Address mac)
}

state = openwrt/ab12cd23ab12/state (unique_id = Address mac)

home (or not_home)

attributes = wrtpresence/ab12cd23ab12/attributes (unique_id = Address mac)

{
  "mac": "ab:12:cd:23:ab:12",
  "source_type": "WifiAP-02", (Device name Openwrt WifiAP-01 or WifiAP-02...)
  "source_ssid": "2.4ghz", (Names can be changed. See the two extract nodes (to make searching easier, write "table" in info))
  "ip": "192.x.x.x", (IP assigned in dhcp.leases)
  "host_name": "Computer HP", (hostname in openwrt if available in dhcp.leases otherwise mac address)
  "disconnected_idx": "0" (0 = connected, 1 à 10 = disconnected)
}

Installation Openwrt

The hostname is important. Apply a simple logic.


WifiAP-01
WifiAP-02
WifiAP-XX

MASTER ROUTER or ACCESS POINT

Copy the 3 files in folder ROOT.

wrtpresence
wrtpresence_main.sh
wrtwifistareport.sh

Change file permissions

chmod +x "/root/wrtpresence"
chmod +x "/root/wrtpresence_main.sh"
chmod +x "/root/wrtwifistareport.sh"

Install or remove the following packages

opkg update
opkg install bash
opkg remove logd
opkg install syslog-ng

Setting syslog-ng.conf line to etc / syslog-ng.conf (example: view in the directory File MASTER ROUTER)

line 55 change "IP node-red" to "192.XXX.XX.X"
line 56 port of your choice. Be careful if you change it, you will have to put the same one back in the syslog-input node.
line 68 By default, the line is commented out. You must uncomment it if you have slave access point.

Add this command line to LUCI / System / Startup / Local Startup

# sh /root/wrtpresence start

Setting logging parameters to LUCI / System / System / Logging

External system log server 127.0.0.1
Cron Log Level Warning

Add a scheduled task to LUCI / System / Scheduled Tasks

*/5 * * * * /bin/sh "/root/wrtwifistareport.sh" >/dev/null 2>&1

Restart Cron:

/etc/init.d/cron restart

SLAVE ACCESS POINT

Copy the files in folder ROOT.

wrtwifistareport.sh

Change file permissions

chmod +x "/root/wrtwifistareport.sh"

Setting logging parameters to LUCI / System / System / Logging

External system log server [IP ADDRESS OF MASTER ROUTER or ACCESS POINT]
Cron Log Level Warning

Add a scheduled task to LUCI / System / Scheduled Tasks

*/5 * * * * /bin/sh "/root/wrtwifistareport.sh" >/dev/null 2>&1

Restart Cron:

/etc/init.d/cron restart

Configuration MQTT

You need a mosquitto server. Normally you don’t need to change anything in this section. This is in node-red.

Configuration Node-Red

Search the palette and install node-red-contrib-syslog-input2

Import the flow below

[{"id":"c935ca7d01c176f7","type":"tab","label":"WrtPresence","disabled":false,"info":"","env":[]},{"id":"338bd89a0c3ccbdc","type":"syslog-input2","z":"c935ca7d01c176f7","name":"syslog","socktype":"udp","address":"","port":"21500","topic":"","x":330,"y":20,"wires":[["1d026c40d87342da","bb856d3cd8739cfd"]]},{"id":"34ce5b55c8510422","type":"mqtt out","z":"c935ca7d01c176f7","name":"SERVER","topic":"","qos":"","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"8739c3164bbbc25d","x":560,"y":540,"wires":[]},{"id":"b294334543a55806","type":"function","z":"c935ca7d01c176f7","name":"MQTT State","func":"msg.topic = `wrtpresence/${msg.payload.dev_id}/state`\nmsg.payload = msg.payload.location_name\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":540,"wires":[["34ce5b55c8510422"]]},{"id":"835251192a393877","type":"function","z":"c935ca7d01c176f7","name":"MQTT State attributes","func":"var a = msg.payload.hostname;\nvar b = \"*\";\nif (a == b) {\n    msg.payload.hostname = msg.payload.dev_id\n}else{\n    msg.payload.hostname = msg.payload.hostname\n}//Update host_name (*)\n\nmsg.topic = `wrtpresence/${msg.payload.dev_id}/attributes`\nmsg.payload = {\n    \"mac\": `${msg.payload.mac}`,\n    \"source_type\": `${msg.payload.source_type}`,\n    \"source_ssid\": `${msg.payload.source_ssid}`,\n    \"ip\": `${msg.payload.ip}`,\n    \"host_name\": `${msg.payload.hostname}`,\n    \"disconnected_idx\": `${msg.payload.disconnected_idx}`,\n    }\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":580,"wires":[["34ce5b55c8510422"]]},{"id":"a6c375948a0f77d2","type":"delay","z":"c935ca7d01c176f7","name":"","pauseType":"delay","timeout":"4","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":100,"y":580,"wires":[["835251192a393877"]]},{"id":"2449acd9a58aecd5","type":"delay","z":"c935ca7d01c176f7","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":100,"y":540,"wires":[["b294334543a55806"]]},{"id":"927338cfb9f3ab23","type":"function","z":"c935ca7d01c176f7","name":"MQTT Config","func":"var a = msg.payload.hostname;\nvar b = \"*\";\nif (a == b) {\n    msg.payload.hostname = msg.payload.dev_id\n}else{\n    msg.payload.hostname = msg.payload.hostname\n}//Update host_name (*)\n\nmsg.topic = `homeassistant/device_tracker/${msg.payload.dev_id}/config`\nmsg.payload = {\n    \"unique_id\": `${msg.payload.dev_id}`,\n    \"name\": `${msg.payload.hostname}`,\n    \"device\": {\n        \"manufacturer\": \"Openwrt\",\n        \"model\": \"Xiaomi Ax3600\",\n        \"name\": \"WrtPresence\",\n        \"identifiers\": [\n            \"wrtpresence\"\n        ]\n    },\n    \"state_topic\": `wrtpresence/${msg.payload.dev_id}/state`,\n    \"payload_home\": \"home\",\n    \"payload_payload_not_home\": \"not_home\",\n    \"entity_category\": \"diagnostic\",\n    \"json_attributes_topic\": `wrtpresence/${msg.payload.dev_id}/attributes`\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":500,"wires":[["34ce5b55c8510422"]]},{"id":"1d026c40d87342da","type":"switch","z":"c935ca7d01c176f7","name":"WRTASSOCIATIONSREPORT","property":"payload.tag","propertyType":"msg","rules":[{"t":"eq","v":"wrtassociationreport","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":170,"y":80,"wires":[["6e2691b580178010"]]},{"id":"6e2691b580178010","type":"change","z":"c935ca7d01c176f7","name":"Replace","rules":[{"t":"set","p":"payload","pt":"msg","to":"$replace(payload.msg, \"|\", \"\\n\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":100,"y":120,"wires":[["f256e5d1abb29460"]]},{"id":"f256e5d1abb29460","type":"split","z":"c935ca7d01c176f7","name":"","splt":"\\n","spltType":"str","arraySplt":"1","arraySpltType":"len","stream":false,"addname":"","x":90,"y":160,"wires":[["bc6aafeaff0d6d29"]]},{"id":"bc6aafeaff0d6d29","type":"switch","z":"c935ca7d01c176f7","name":"Check value \"null\"","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":130,"y":200,"wires":[["5e6122fa66de7550"]]},{"id":"5e6122fa66de7550","type":"change","z":"c935ca7d01c176f7","name":"Extract","rules":[{"t":"set","p":"payload","pt":"msg","to":"(\t  $table:={\t    \"phy0-ap0\" : \"IOT\",\t    \"phy2-ap0\" : \"2.4ghz\",\t    \"phy1-ap0\" : \"5ghz\"\t    };\t\t    payload#$i.(\t        $data:=$split($, \"=\");\t        $source:=$substring($data[0], 0, 9);\t        $ssid:=$substringAfter($data[0], \"_\");\t        $dev_id:=$split($data[1],(\":\"))~> $join('');\t        $counter:=$data[2]~>$substringBefore(\" \")~>$number();\t        $ishome:=$counter < 1 ? \"home\" : \"not_home\";\t\t        {\t          \"dev_id\": $dev_id,\t          \"source_type\": $source,\t          \"source_ssid\": $lookup($table,$ssid),\t          \"mac\": $data[1],\t          \"location_name\": $ishome,\t          \"disconnected_idx\": $counter\t        }\t    )\t)","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"$substring(payload.dev_id, 0, 12)","tot":"jsonata"},{"t":"move","p":"payload","pt":"msg","to":"associations","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":100,"y":240,"wires":[["8418b58cef1dff5e"]]},{"id":"bb856d3cd8739cfd","type":"switch","z":"c935ca7d01c176f7","name":"WRTDHCPLEASESREPORT","property":"payload.tag","propertyType":"msg","rules":[{"t":"eq","v":"wrtdhcpleasesreport","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":510,"y":80,"wires":[["cdd1e6c51560411c"]]},{"id":"cdd1e6c51560411c","type":"change","z":"c935ca7d01c176f7","name":"Replace","rules":[{"t":"set","p":"payload","pt":"msg","to":"$replace(payload.msg, \"|\", \"\\n\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":120,"wires":[["5d898e83cec6988d"]]},{"id":"5d898e83cec6988d","type":"split","z":"c935ca7d01c176f7","name":"","splt":"\\n","spltType":"str","arraySplt":"1","arraySpltType":"len","stream":false,"addname":"","x":430,"y":160,"wires":[["7087d289ed6a9e9b"]]},{"id":"7087d289ed6a9e9b","type":"switch","z":"c935ca7d01c176f7","name":"Check value \"null\"","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":470,"y":200,"wires":[["2f593bb6b72265f8"]]},{"id":"2f593bb6b72265f8","type":"change","z":"c935ca7d01c176f7","name":"Extract","rules":[{"t":"set","p":"payload","pt":"msg","to":"(\t  payload#$i.( \t    $data:=$split($, \";\");\t    $dev_id:=$split($data[0],(\":\"))~> $join('');\t\t    {\t        \"dev_id\": $dev_id,\t        \"mac\": $data[0],\t        \"ip\":   $data[1],\t        \"host\": $data[2]\t    }\t  )\t)","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"$substring(payload.dev_id, 0, 12)","tot":"jsonata"},{"t":"move","p":"payload","pt":"msg","to":"dhcp_leases","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":240,"wires":[["c72cb9dcbca2aa5a"]]},{"id":"478a797fdbaa54ba","type":"function","z":"c935ca7d01c176f7","name":"Fetch Associations","func":"if (typeof context.counter === 'undefined')\n{\n    context.counter =0;\n}\ncontext.counter ++;\nmsg.payload = context.counter;\nmsg.parts = {};\nmsg.parts.id = msg.topic;\nmsg.parts.index = 0;\nmsg.parts.count = 2;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":130,"y":360,"wires":[["44a16e18d4f145d4"]]},{"id":"c72cb9dcbca2aa5a","type":"function","z":"c935ca7d01c176f7","name":"Fetch Dhcp_Leases","func":"if (typeof context.counter === 'undefined')\n{\n    context.counter=0;\n}\ncontext.counter++;\nmsg.payload = context.counter;\nmsg.parts = {};\nmsg.parts.id = msg.topic;\nmsg.parts.index = 1;\nmsg.parts.count = 2;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":280,"wires":[["58f673a3ed994b05"]]},{"id":"44a16e18d4f145d4","type":"join","z":"c935ca7d01c176f7","name":"Connected devices join details with dhcp leases","mode":"auto","build":"object","property":"","propertyType":"full","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":320,"y":420,"wires":[["e6f5fa0bf3acfeab"]]},{"id":"e6f5fa0bf3acfeab","type":"change","z":"c935ca7d01c176f7","name":"Checks every 5 min","rules":[{"t":"move","p":"associations","pt":"msg","to":"payload","tot":"msg"},{"t":"move","p":"dhcp_leases.ip","pt":"msg","to":"payload.ip","tot":"msg"},{"t":"move","p":"dhcp_leases.host","pt":"msg","to":"payload.hostname","tot":"msg"},{"t":"delete","p":"dhcp_leases","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":460,"wires":[["2449acd9a58aecd5","a6c375948a0f77d2","927338cfb9f3ab23"]]},{"id":"58f673a3ed994b05","type":"delay","z":"c935ca7d01c176f7","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":440,"y":320,"wires":[["44a16e18d4f145d4"]]},{"id":"7f1f9f4164a7007a","type":"rbe","z":"c935ca7d01c176f7","name":"Remove Mac 2 STA ","func":"rbe","gap":"2","start":"","inout":"in","septopics":false,"property":"associations.disconnected_idx","topi":"topic","x":140,"y":320,"wires":[["478a797fdbaa54ba"]]},{"id":"8418b58cef1dff5e","type":"switch","z":"c935ca7d01c176f7","name":"Check disconnect_idx","property":"associations.disconnected_idx","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"neq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":140,"y":280,"wires":[["478a797fdbaa54ba"],["7f1f9f4164a7007a"]]},{"id":"8739c3164bbbc25d","type":"mqtt-broker","name":"Mosquitto broker","broker":"192.168.2.9","port":"1883","clientid":"node-red","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]

In the syslog node, match the port defined in syslog-ng.conf.
In the mqtt out nodes, set or select your mqtt server.

Special Thanks

A special thanks you to everyone who helped me with this project in one way or another.

WrtPresence

My initial request. Help with Node-red

Hello,

I would like the data from this file. The aim is to use it as a device_tracker.


WifiAP-01_phy1-ap0=0c:c6:xx:xx:xx:xx=10 
WifiAP-01_phy1-ap0=98:3b:xx:xx:xx:xx=0 
WifiAP-01_phy2-ap0=00:04:xx:xx:xx:xx=0 
WifiAP-01_phy2-ap0=48:e7:xx:xx:xx:xx=10 
WifiAP-02_phy0-ap0=00:23:xx:xx:xx:xx=0 
WifiAP-02_phy1-ap0=0c:c6:xx:xx:xx:xx=10 

WifiAP-01_phy0-ap0 = Router IOT
WifiAP-01_phy2-ap0 = Router 2.4ghz
WifiAP-01_phy1-ap0 = Router 5ghz
WifiAP-02_phy0-ap0 = Mesh IOT
WifiAP-02_phy2-ap0 = Mesh 2.4ghz
WifiAP-02_phy1-ap0 = Mesh 5ghz

MAC = 0c:c6:xx:xx:xx:xx

0=home
(from 0 to 4)
10=not_home
from 5 to 10

If you have an idea of how to do this

Thanks

1 Like

Is the file available to where ever your Home Assistant is running? If so, you could be a shell script that looks like this

#!/bin/bash
export value=`grep $1 /path/to/your/file.txt | cut - -f3 -d'='`

if [[ $value -lt 5 ]]
then
  echo ON
else
  echo OFF
fi

somewhere under your /config directory. Make sure to make it executable (chmod 755 <filename>)

Then create command line binary sensors that look like this:

command_line:
  - binary_sensor:
    name: Router 5ghz
    command: /config/your_shell_script.sh "WifiAP-01_phy1-ap0=0c:c6"
    device_class: presence

The shell script greps the file to match which line has the status you want, then cuts out the 3rd field (-f3) using equals signs as field delimiters (-d'=') to only get the value at the end. Then it compares that to see if it’s less than 5 (home/on) or not (away/off).

Thank you for your suggestion. It’s a good idea but I want to monitor my entire wifi network. I have several access points, with your solution I would have to write a lot of binary sensors. My idea is to use device_tracker.see.
At that moment I found a way to send it by mqtt. The file is generated on my OpenWrt router.
The script is not mine but I have started to forke the project Here

Use Node-RED ‘read file’ node (one message per line), join to an array, then it just requires a bit of processing using JSONata.

[{"id":"b62e61a6723e0b82","type":"file in","z":"8b2aaf4879a370dd","name":"Read File","filename":"/config/user_data/test.txt","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":300,"y":660,"wires":[["0201364da18f2d88"]]},{"id":"bf4f1c07a90fd806","type":"inject","z":"8b2aaf4879a370dd","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":660,"wires":[["b62e61a6723e0b82"]]},{"id":"5fc14fd48a494f74","type":"debug","z":"8b2aaf4879a370dd","name":"debug 227","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":770,"y":660,"wires":[]},{"id":"0201364da18f2d88","type":"join","z":"8b2aaf4879a370dd","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"3","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":450,"y":660,"wires":[["83c026ec43823fc0"]]},{"id":"83c026ec43823fc0","type":"change","z":"8b2aaf4879a370dd","name":"Extract","rules":[{"t":"set","p":"payload","pt":"msg","to":"(\t    $table:={\t        \"WifiAP-01_phy0-ap0\" : \"Router IOT\",\t        \"WifiAP-01_phy2-ap0\" : \"Router 2.4ghz\",\t        \"WifiAP-01_phy1-ap0\" : \"Router 5ghz\",\t        \"WifiAP-02_phy0-ap0\" : \"Mesh IOT\",\t        \"WifiAP-02_phy2-ap0\" : \"Mesh 2.4ghz\",\t        \"WifiAP-02_phy1-ap0\" : \"Mesh 5ghz\"\t        };\t        \t    payload#$i.(\t        $front:=$substringBefore($,\"=\");\t        $back:=$substringAfter($,\"=\");\t        $mac:=$substringBefore($back,\"=\");\t        $pwr:=$substringAfter($back,\"=\")~>$substringBefore(\" \")~>$number();\t        $ishome:=$pwr<5;\t        {\t            \"count\": $i+1,\t            \"device\": $lookup($table,$front),\t            \"mac\": $mac,\t            \"pwr\": $pwr,\t            \"home\": $ishome}\t    )\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":660,"wires":[["5fc14fd48a494f74"]]}]

The above assumes that the lines are formatted as given in your post so I can parse out the parts.

Change the file pathname as required.

Result - array of objects for each line with wifi device, mac address, power level, and Boolean for home or not.

Who needs scripts?

It works. Do you have a method without a read file?
My message wasn’t clear. I send the file data via mqtt.
The data is in 1 line separated by a space.

WifiAP-01_phy1-ap0=0c:c6:xx:xx:xx:xx=10 WifiAP-01_phy1-ap0=98:3b:xx:xx:xx:xx=0 WifiAP-01_phy2-ap0=00:04:xx:xx:xx:xx=0 WifiAP-01_phy2-ap0=48:e7:xx:xx:xx:xx=10 WifiAP-02_phy0-ap0=00:23:xx:xx:xx:xx=0 WifiAP-02_phy1-ap0=0c:c6:xx:xx:xx:xx=10

I did the same tests by copying the file into an accessible directory.
If I have a line it works for the device_tracker but it doesn’t understand in array mode.

Now it’s working. I just replaced join with split. Thanks again, I’ll be able to continue my tests.

@Biscuit
I’ve been searching for hours. How would you go about converting dhcp leases?

1 = ?
2 = mac
3 = ip
4 = host_name
5 = ?

"1706249051 90:dd:5d:9f:86:0d 192.168.2.32 Apple-TV-4K 01:90:dd:5d:9f:86:0d"

Thank you

Very much depends on how the data arrives, and what you want to convert it into…

If I can apply JSONata to the data field, then this is easily split into parts and manipluated. Then it is a question of what to do with each part

The first field looks like a timestamp as Unix seconds, so can be turned into milliseconds (*1000) and ends up as “2024-01-26T06:04:11.000Z” which I assume is a long term lease.

Starting with

{
  "data": "1706249051 90:dd:5d:9f:86:0d 192.168.2.32 Apple-TV-4K 01:90:dd:5d:9f:86:0d"
}

and using JSONata expression

( 
    $data:=$split(data, " ");
    $time:=$fromMillis(1000*$number($data[0]));

    {"time": $time,
     "mac":  $data[1],
     "ip":   $data[2],
     "host": $data[3],
     "mac2": $data[4]
     }
)

I end up with

{
  "time": "2024-01-26T06:04:11.000Z",
  "mac": "90:dd:5d:9f:86:0d",
  "ip": "192.168.2.32",
  "host": "Apple-TV-4K",
  "mac2": "01:90:dd:5d:9f:86:0d"
}

I like JSONata, but there are other ways to do this.

I’m sorry, I’ve been looking for hours. In such a short time you have the solution. Thank you so much. I will continue my project. I think I’ll come back to solicit you in view of your skills.

JSONata is a very powerful tool to work with and manipulate JSON structures, and JSONata expressions can be easily used in change nodes.

I always use the ‘try JSONata’ page to build my expressions - you can use the following link to load this example (I have set the input JSON to what would be msg.payload)

https://try.jsonata.org/ThBf3RH7y

The left pane is the input JSON structure
The top right pane is the JSONata expression
The lower right pane is the output

This is dynamic, and thus very easy to build and test JSONata expressions.

1 Like

I’m still at a loss. Now I have two separate streams. I would like to group the data into 1 device using the id only.

Unique ID is my mac address in 01ab02ab03ab format.
I’ve added it to msg.topic
I can’t manage to join the two into 1.

example:
file: associations
association

file: dhcp_leases
dhcp

In my mind, the result is to have for the topic id 70ee5065b97c of association and dhcp leases
a payload with :

id:
device_ssid:
mac:
location_mac:
ip:
host:

To answer your question / assist you it would help to have your Node-RED flow (the export, not just a picture), and also to have your data, again exported not just a picture.

If you have two files that you are reading, then either both have to be joined into one object, or one needs to be saved to context. Then both data sets can be examined together to cross-match, assuming that is what you are trying to do…

I’ve made sure that each node has the same output data. I want both files to complete the data. If there is no match, do nothing. The last line is empty in both files.

file associations.dto:

WifiAP-01_phy1-ap0=ac:1e:9e:67:47:99=10
WifiAP-01_phy2-ap0=c8:db:26:0b:2f:ed=0
WifiAP-01_phy2-ap0=dc:4f:22:37:ea:be=0
WifiAP-01_phy2-ap0=e4:f0:42:20:c5:7b=0

file dhcp.leases:

0 dc:4f:22:37:ea:be 192.168.2.173 Sonoff-wifi-VMC-SB *
0 c8:db:26:0b:2f:ed 192.168.2.24 Harmony-Hub-Salon 01:c8:db:26:0b:2f:ed
1698550425 ac:1e:9e:67:47:99 192.168.2.145 Redmi-Note-11 01:ac:1e:9e:67:47:99
0 e4:f0:42:20:c5:7b 192.168.2.29 Google-Mini-SdB *

[{"id":"6abc44b811846b41","type":"debug","z":"1375381b520d56e6","name":"debug 228","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1010,"y":140,"wires":[]},{"id":"2afcc8aae2056b30","type":"change","z":"1375381b520d56e6","name":"Extract","rules":[{"t":"set","p":"payload","pt":"msg","to":"(\t    $table:={\t        \"WifiAP-01_phy0-ap0\" : \"Router IOT\",\t        \"WifiAP-01_phy2-ap0\" : \"Router 2.4ghz\",\t        \"WifiAP-01_phy1-ap0\" : \"Router 5ghz\",\t        \"WifiAP-02_phy0-ap0\" : \"Mesh IOT\",\t        \"WifiAP-02_phy2-ap0\" : \"Mesh 2.4ghz\",\t        \"WifiAP-02_phy1-ap0\" : \"Mesh 5ghz\"\t        };\t        \t    payload#$i.(\t        $data:=$split($, \"=\");\t        $counter:=$data[2]~>$substringBefore(\" \")~>$number();\t        $ishome:=$counter <= 5 ? \"home\" : \"not_home\";\t        $dev_id:=$split($data[1],(\":\"))~> $join('');\t        {\t            \"id\": $dev_id,\t            \"device_ssid\":  $lookup($table,$data[0]),\t            \"mac\": $data[1],\t            \"location_name\": $ishome,\t            \"ip\": \"\",\t            \"host\": \"\"\t        }\t    )\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":100,"wires":[["6abc44b811846b41"]]},{"id":"1d53ca839a10d0d4","type":"change","z":"1375381b520d56e6","name":"Extract","rules":[{"t":"set","p":"payload","pt":"msg","to":"(\t  payload#$i.( \t    $data:=$split($, \" \");\t    $time:=$fromMillis(1000*$number($data[0]));\t    $dev_id:=$split($data[1],(\":\"))~> $join('');\t\t    {\"id\": $dev_id,\t     \"device_ssid\": \"\",\t     \"mac\": $data[1],\t     \"location_name\": \"\",\t     \"ip\":   $data[2],\t     \"host\": $data[3]\t     }\t  )\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":180,"wires":[["6abc44b811846b41"]]},{"id":"9589d8e04504934f","type":"split","z":"1375381b520d56e6","name":"Serialize device","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":460,"y":100,"wires":[["2afcc8aae2056b30"]]},{"id":"543f4119a6ff6e13","type":"split","z":"1375381b520d56e6","name":"Serialize device","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":460,"y":180,"wires":[["1d53ca839a10d0d4"]]},{"id":"cdcc6aa697a28e94","type":"file in","z":"1375381b520d56e6","name":"associations","filename":"/config/test/associations.dto","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":true,"x":270,"y":100,"wires":[["9589d8e04504934f"]]},{"id":"2af63bfc98619697","type":"file in","z":"1375381b520d56e6","name":"dhcp_leases","filename":"/config/test/dhcp.leases","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":true,"x":270,"y":180,"wires":[["543f4119a6ff6e13"]]},{"id":"153dc8c9bd580890","type":"inject","z":"1375381b520d56e6","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":140,"wires":[["cdcc6aa697a28e94","2af63bfc98619697"]]}]

@Biscuit
I’ve made good progress. I’ve found a knot that resembles my wishes. Now I just have to rebuild it. The match is working.
The help node

[{"id":"6abc44b811846b41","type":"debug","z":"1375381b520d56e6","name":"debug 228","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":650,"y":400,"wires":[]},{"id":"ea75a470bcf14f2e","type":"join","z":"1375381b520d56e6","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":970,"y":260,"wires":[["6abc44b811846b41"]]},{"id":"aedfecdeee18c3f7","type":"function","z":"1375381b520d56e6","name":"Fetch Associations","func":"if (typeof context.counter === 'undefined')\n{\n    context.counter =0;\n}\ncontext.counter ++;\nmsg.payload = context.counter;\nmsg.parts = {};\nmsg.parts.id = msg.associations.id;\nmsg.parts.index = 0;\nmsg.parts.count = 2;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":100,"wires":[["ea75a470bcf14f2e"]]},{"id":"4ca82503a5966344","type":"delay","z":"1375381b520d56e6","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":780,"y":280,"wires":[["ea75a470bcf14f2e"]]},{"id":"2afcc8aae2056b30","type":"change","z":"1375381b520d56e6","name":"Extract","rules":[{"t":"set","p":"associations","pt":"msg","to":"(\t  $table:={\t    \"WifiAP-01_phy0-ap0\" : \"Router IOT\",\t    \"WifiAP-01_phy2-ap0\" : \"Router 2.4ghz\",\t    \"WifiAP-01_phy1-ap0\" : \"Router 5ghz\",\t    \"WifiAP-02_phy0-ap0\" : \"Mesh IOT\",\t    \"WifiAP-02_phy2-ap0\" : \"Mesh 2.4ghz\",\t    \"WifiAP-02_phy1-ap0\" : \"Mesh 5ghz\"\t    };\t    payload#$i.(\t        $data:=$split($, \"=\");\t        $counter:=$data[2]~>$substringBefore(\" \")~>$number();\t        $ishome:=$counter <= 5 ? \"home\" : \"not_home\";\t        $dev_id:=$split($data[1],(\":\"))~> $join('');\t        {\t          \"id\": $dev_id,\t          \"device_ssid\":  $lookup($table,$data[0]),\t          \"mac\": $data[1],\t          \"location_name\": $ishome,\t          \"ip\": \"\",\t          \"host\": \"\"\t        }\t    )\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":100,"wires":[["aedfecdeee18c3f7"]]},{"id":"25179e7ed4c990d4","type":"function","z":"1375381b520d56e6","name":"Fetch Dhcp_Leases","func":"if (typeof context.counter === 'undefined')\n{\n    context.counter=0;\n}\ncontext.counter++;\nmsg.payload = context.counter;\nmsg.parts = {};\nmsg.parts.id = msg.dhcp_leases.id;\nmsg.parts.index = 1;\nmsg.parts.count = 2;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":180,"wires":[["4ca82503a5966344"]]},{"id":"9589d8e04504934f","type":"split","z":"1375381b520d56e6","name":"Serialize device","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":460,"y":100,"wires":[["2afcc8aae2056b30"]]},{"id":"1d53ca839a10d0d4","type":"change","z":"1375381b520d56e6","name":"Extract","rules":[{"t":"set","p":"dhcp_leases","pt":"msg","to":"(\t  payload#$i.( \t    $data:=$split($, \" \");\t    $time:=$fromMillis(1000*$number($data[0]));\t    $dev_id:=$split($data[1],(\":\"))~> $join('');\t    {\t      \"id\": $dev_id,\t      \"device_ssid\": \"\",\t      \"mac\": $data[1],\t      \"location_name\": \"\",\t      \"ip\":   $data[2],\t      \"host\": $data[3]\t    }\t  )\t)","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"dhcp_leases","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":180,"wires":[["25179e7ed4c990d4"]]},{"id":"cdcc6aa697a28e94","type":"file in","z":"1375381b520d56e6","name":"associations","filename":"/config/test/associations.dto","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":true,"x":270,"y":100,"wires":[["9589d8e04504934f"]]},{"id":"543f4119a6ff6e13","type":"split","z":"1375381b520d56e6","name":"Serialize device","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":460,"y":180,"wires":[["1d53ca839a10d0d4"]]},{"id":"153dc8c9bd580890","type":"inject","z":"1375381b520d56e6","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":140,"wires":[["cdcc6aa697a28e94","2af63bfc98619697"]]},{"id":"2af63bfc98619697","type":"file in","z":"1375381b520d56e6","name":"dhcp_leases","filename":"/config/test/dhcp.leases","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":true,"x":270,"y":180,"wires":[["543f4119a6ff6e13"]]}]

I’ve found the rest. The device_tracker updates well.

[{"id":"153dc8c9bd580890","type":"inject","z":"1375381b520d56e6","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":140,"wires":[["cdcc6aa697a28e94","2af63bfc98619697"]]},{"id":"2af63bfc98619697","type":"file in","z":"1375381b520d56e6","name":"dhcp_leases","filename":"/config/test/dhcp.leases","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":true,"x":270,"y":180,"wires":[["543f4119a6ff6e13"]]},{"id":"cdcc6aa697a28e94","type":"file in","z":"1375381b520d56e6","name":"associations","filename":"/config/test/associations.dto","filenameType":"str","format":"lines","chunk":false,"sendError":false,"encoding":"none","allProps":true,"x":270,"y":100,"wires":[["9589d8e04504934f"]]},{"id":"6abc44b811846b41","type":"debug","z":"1375381b520d56e6","name":"debug 228","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1150,"y":440,"wires":[]},{"id":"9589d8e04504934f","type":"split","z":"1375381b520d56e6","name":"Serialize device","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":460,"y":100,"wires":[["2afcc8aae2056b30"]]},{"id":"543f4119a6ff6e13","type":"split","z":"1375381b520d56e6","name":"Serialize device","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":460,"y":180,"wires":[["1d53ca839a10d0d4"]]},{"id":"2afcc8aae2056b30","type":"change","z":"1375381b520d56e6","name":"Extract","rules":[{"t":"set","p":"associations","pt":"msg","to":"(\t  $table:={\t    \"WifiAP-01_phy0-ap0\" : \"Router IOT\",\t    \"WifiAP-01_phy2-ap0\" : \"Router 2.4ghz\",\t    \"WifiAP-01_phy1-ap0\" : \"Router 5ghz\",\t    \"WifiAP-02_phy0-ap0\" : \"Mesh IOT\",\t    \"WifiAP-02_phy2-ap0\" : \"Mesh 2.4ghz\",\t    \"WifiAP-02_phy1-ap0\" : \"Mesh 5ghz\"\t    };\t\t    payload#$i.(\t        $data:=$split($, \"=\");\t        $dev_id:=$split($data[1],(\":\"))~> $join('');\t        $counter:=$data[2]~>$substringBefore(\" \")~>$number();\t        $ishome:=$counter <= 5 ? \"home\" : \"not_home\";\t\t        {\t          \"id\": $dev_id,\t          \"device_ssid\":  $lookup($table,$data[0]),\t          \"mac\": $data[1],\t          \"location_name\": $ishome\t        }\t    )\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":100,"wires":[["aedfecdeee18c3f7"]]},{"id":"1d53ca839a10d0d4","type":"change","z":"1375381b520d56e6","name":"Extract","rules":[{"t":"set","p":"dhcp_leases","pt":"msg","to":"(\t  payload#$i.( \t    $data:=$split($, \" \");\t    $dev_id:=$split($data[1],(\":\"))~> $join('');\t    $time:=$fromMillis(1000*$number($data[0]));\t\t    {\t      \"id\": $dev_id,\t      \"time\": $time,\t      \"mac\": $data[1],\t      \"ip\":   $data[2],\t      \"host\": $data[3],\t      \"mac2\": $data[4]\t    }\t  )\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":180,"wires":[["25179e7ed4c990d4"]]},{"id":"25179e7ed4c990d4","type":"function","z":"1375381b520d56e6","name":"Fetch Dhcp_Leases","func":"if (typeof context.counter === 'undefined')\n{\n    context.counter=0;\n}\ncontext.counter++;\nmsg.payload = context.counter;\nmsg.parts = {};\nmsg.parts.id = msg.dhcp_leases.id;\nmsg.parts.index = 1;\nmsg.parts.count = 2;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":180,"wires":[["4ca82503a5966344"]]},{"id":"aedfecdeee18c3f7","type":"function","z":"1375381b520d56e6","name":"Fetch Associations","func":"if (typeof context.counter === 'undefined')\n{\n    context.counter =0;\n}\ncontext.counter ++;\nmsg.payload = context.counter;\nmsg.parts = {};\nmsg.parts.id = msg.associations.id;\nmsg.parts.index = 0;\nmsg.parts.count = 2;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":100,"wires":[["ea75a470bcf14f2e"]]},{"id":"ea75a470bcf14f2e","type":"join","z":"1375381b520d56e6","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":970,"y":260,"wires":[["ed29535bad08591d"]]},{"id":"4ca82503a5966344","type":"delay","z":"1375381b520d56e6","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":780,"y":280,"wires":[["ea75a470bcf14f2e"]]},{"id":"ed29535bad08591d","type":"change","z":"1375381b520d56e6","name":"","rules":[{"t":"move","p":"associations.id","pt":"msg","to":"device.dev_id","tot":"msg"},{"t":"move","p":"associations.device_ssid","pt":"msg","to":"device.device_ssid","tot":"msg"},{"t":"move","p":"associations.mac","pt":"msg","to":"device.mac","tot":"msg"},{"t":"move","p":"associations.location_name","pt":"msg","to":"device.location_name","tot":"msg"},{"t":"move","p":"dhcp_leases.ip","pt":"msg","to":"device.ip","tot":"msg"},{"t":"move","p":"dhcp_leases.host","pt":"msg","to":"device.host_name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":960,"y":360,"wires":[["9b70f80aedb20b3c"]]},{"id":"9b70f80aedb20b3c","type":"api-call-service","z":"1375381b520d56e6","name":"See device by mac","server":"631a8a25.74d9f4","version":5,"debugenabled":true,"domain":"device_tracker","service":"see","areaId":[],"deviceId":[],"entityId":[],"data":"{\"dev_id\":\"{{device.dev_id}}\",\"mac\":\"{{device.mac}}\",\"host_name\":\"{{device.host_name}}\",\"location_name\":\"{{device.location_name}}\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"none","output_location":"payload","output_location_type":"msg","x":950,"y":460,"wires":[["6abc44b811846b41"]]},{"id":"631a8a25.74d9f4","type":"server","name":"Home Assistant","addon":true}]

An idea to ignore the last line, as it’s data-free. This generates an error. This has no effect on the process.
I will continue my “construction”. Now I would like to create a device with the SSID and tracker devices attached.

@Biscuit
I’m having trouble replacing msg.topic with path/value/path
error:

homessistant/device_tracker/(device_tracker.dev_id#$i.($data:=$;))/config & topic

ok:

(device_tracker.dev_id#$i.($data:=$;)) & topic

with fuction ok :slightly_smiling_face:

msg.topic = `homeassistant/OpenWrt/device_tracker/${msg.device_tracker.dev_id}/config`;
return msg;

Looks like you are getting a bit mixed up with the JSONata. Not sure again quite what you are trying to achieve, or what your data is at this point.

I did look at your flow but I am having problems with the file data you have provided as the data is separate lines terminated by \r (return) when I would expect \n (newline).

If your function node is working then I suggest you go with that. JSONata can do the job, but it is difficult to build code unless I can replicate your data and what you are doing with it.

Thank you again for taking the time to respond. In fact, I’m going a bit in all directions. I spend hours testing, reading, trying again and again. The good news is that I am moving forward and learning a lot. There are many things around trackers today but the reliability is not always optimal. I came across a bash on openwrt which suits me perfectly but has no connection with homeassistant.
I found a way to send the data via mqtt. But here again I don’t want to stay on a simple tracker_device and I therefore want to create a device.
Device ->Router Device->Mesh
-------------|------------- -----------|-----------
device_tracker witch attributes

Initially I was going for device_tracker see but I am limited with the attributes in node red. So I’m moving towards mqtt out.

@Biscuit I’m stuck on a request. I have two entries with x messages. Both have a common dev_id. I would like to recover those for which they are only in the first entry. Do you have any idea or way to do this?

Exemple:

Input 1 : message with dev_1
message with dev_2
message with dev_3

Input 2 : message with dev_1
message with dev_3

I would like to recover the message with dev_2.

Assuming you need to build a single message with the various input/messages…

[{"id":"855043e15a7bdbd8","type":"inject","z":"659ff45c0b65321e","name":"Dev_1","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Dev_1","payloadType":"str","x":130,"y":1460,"wires":[["a7b2fd9c1f5e7023"]]},{"id":"34b9ef6011fc5b6b","type":"inject","z":"659ff45c0b65321e","name":"Dev_2","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Dev_2","payloadType":"str","x":130,"y":1500,"wires":[["a7b2fd9c1f5e7023"]]},{"id":"a7b2fd9c1f5e7023","type":"join","z":"659ff45c0b65321e","name":"Join (when complete)","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":380,"y":1520,"wires":[["f0aa4e3360517959"]]},{"id":"fb9589174b81261f","type":"inject","z":"659ff45c0b65321e","name":"Dev_3","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Dev_3","payloadType":"str","x":130,"y":1540,"wires":[["a7b2fd9c1f5e7023"]]},{"id":"f11370b0ea8ea453","type":"inject","z":"659ff45c0b65321e","name":"Complete","props":[{"p":"complete","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":140,"y":1600,"wires":[["a7b2fd9c1f5e7023"]]},{"id":"3fedbc176e706c3c","type":"join","z":"659ff45c0b65321e","name":"Join (when complete)","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":380,"y":1740,"wires":[["8bd251880deb0a4d"]]},{"id":"fd38020140a99c46","type":"debug","z":"659ff45c0b65321e","name":"debug 244","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":890,"y":1660,"wires":[]},{"id":"5cc6706ef5c317ea","type":"inject","z":"659ff45c0b65321e","name":"Dev_1","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Dev_1","payloadType":"str","x":130,"y":1680,"wires":[["3fedbc176e706c3c"]]},{"id":"a5f36093639ea2b8","type":"inject","z":"659ff45c0b65321e","d":true,"name":"Dev_2","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Dev_2","payloadType":"str","x":130,"y":1720,"wires":[["3fedbc176e706c3c"]]},{"id":"12ff204db096f174","type":"inject","z":"659ff45c0b65321e","name":"Dev_3","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Dev_3","payloadType":"str","x":130,"y":1760,"wires":[["3fedbc176e706c3c"]]},{"id":"0eba780d5fc4559e","type":"inject","z":"659ff45c0b65321e","name":"Complete","props":[{"p":"complete","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":140,"y":1820,"wires":[["3fedbc176e706c3c"]]},{"id":"71d81dd3d13c11b0","type":"join","z":"659ff45c0b65321e","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":590,"y":1660,"wires":[["484aecc579374821"]]},{"id":"e344eea2a18743a3","type":"inject","z":"659ff45c0b65321e","name":"Complete","props":[{"p":"complete","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":420,"y":1660,"wires":[["71d81dd3d13c11b0"]]},{"id":"f0aa4e3360517959","type":"change","z":"659ff45c0b65321e","name":"Group A","rules":[{"t":"set","p":"topic","pt":"msg","to":"A","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":1560,"wires":[["71d81dd3d13c11b0"]]},{"id":"8bd251880deb0a4d","type":"change","z":"659ff45c0b65321e","name":"Group B","rules":[{"t":"set","p":"topic","pt":"msg","to":"B","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":1780,"wires":[["71d81dd3d13c11b0"]]},{"id":"484aecc579374821","type":"change","z":"659ff45c0b65321e","name":"Pick","rules":[{"t":"set","p":"payload","pt":"msg","to":"$distinct(payload.*).($ in $$.payload.A and $not($ in $$.payload.B) ? $)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":1660,"wires":[["fd38020140a99c46"]]}]
1 Like