Update Device Attribute based on its tag key-value

Hello All,

referring to below images, i have this case where i need to update device attributes value based on their defined tag (IOID).

the attributes values received as an array “Elements” as below image, however the device attributes are not ordered in specific order under “Elements” Array, and more attributes may be added to this array in future.

So I need dynamic way to loop over the “Elements” Array, read the IOID received, find which device attribute has this IOID value, then update the attribute value.

Any Idea? Suggestion?


Good question! We are working on a potential solution and will be back shortly.

I figured out a way to do this without using a Loop Node.

Given an Elements array whose items are objects of the following shape …

{
  "IOID": 71,
  "Name": "GNSS Status",
  "Value": 3
}

… and given a device object whose attributes property is also in array form …

{
  "attributes": [
    {
      "name": "gnssStatus",
      "dataType": "number",
      "attributeTags": {
        "IOID": "71"
      }
    }
  ]
}

… You can build a state object for use in the Device: State Node using a JSON Template and the {{valueByKey}} helper as follows …

{
    {{#each Elements}}
      "{{valueByKey @root.device.attributes (lower IOID) 'attributeTags.IOID' 'name'}}": {{Value}}{{#unless @last}},{{/unless}}
    {{/each}}
}

There’s a lot to unpack here …

  • {{#each Elements}} - Iterates over the Elements array. You will need to change the path of Elements to whatever your use case dictates. I can’t see what that value would be from your screenshot but if looks like you are looping over an outer Data property, so it would probably be something like working.currentDataIteration.value.Elements.
  • "{{valueByKey @root.device.attributes (lower IOID) 'attributeTags.IOID' 'name'}}" - This is the meat of it, for finding the attribute name for a given device that corresponds to the IOID property in the current Elements iteration …
    • @root.device.attributes - The @root breaks out of the current {{#each}} iteration context and goes to find the device object’s attributes array on the full payload. You will need to change this path to match your use case.
    • (lower IOID) - The IOID here is the value of the IOID property within the Elements object. The (lower) portion is necessary to cast that value to a string, so that we can match it against the device, where the IOID is also stored as a string.
    • 'attributeTags.IOID' - This is the path to the object property that we want to compare the IOID value to in the device attributes array. You should not have to change this based on your screenshots.
    • This is what to return from the device attributes array given our match. This is the name of the attribute - exactly what we’re after here.
  • {{Value}} - This is the numerical value we want to report for that object key we just generated. This is coming from the current iteration of Elements.
  • {{#unless @last}},{{/unless}} - This is necessary to properly construct our JSON object, as we must put a comma at the end of every line except the last one.

Attached is a simple application export that demonstrates this, with an Application Workflow and a device. Your use case appears to be within an Edge Workflow, but the same solution applies.

Let us know if you have any questions.

export-6397491d6d455256a221247b-1670862469767-forums4841.zip (2.6 KB)

1 Like

Dear Dylan,

Thanks for the comprehensive solution !
I applied your suggestion and it seems pretty works.

Hello Dylan,

for the same situation, I am trying to merge below two nodes into one Device State Node, But I got an issue doing this related to Json formatting, kindly your advise


The Application File is Here:
export-fleetManagementSystemCloned.zip (9.0 KB)

You should be able to combine the two together like so (shortened and pseudo-code) …

{
  {{#each data.count.value.Elements}}
    "{{valueByKey @root.working.DeviceID.attributes (...)": {{jsonEncode myValue}},
  {{/each}}
  "Altitude": "{{data.count.value.Altitude}}",
  ... other specifically set properties
}

Thanks Dylan,

i applied the same logic in “Edge Workflow” but it doesn’t recognize this part

{{#each data.count.value.Elements}}
"{{valueByKey @root.working.DeviceID.attributes (lower IOID) 'attributeTags.IOID' 'name'}}": {{Value}},{{#unless @last}}{{/unless}}
{{/each}}

i got below error in device log, is it related to “Edge Workflow” or “Peripheral: Get Node”? because it works perfectly in Application Workflow and “Device: Get Node”


You can read about the “Additional messages received” message here and the “One or more reported attributes” message here.

The second message indicates some sort of error in the{{#each}} helper that is building your state JSON object; probably a payload path not pointing to the right value. There is a payload tester built into the workflow engine that allows you to provide a JSON template and a payload to render that against to view what the output will be; I recommend using that tool to get to the bottom of your issue.

Without having your actual JSON template in hand and a payload against which it is evaluating, it’s difficult for us to see where the error is occurring.

One important note: There is a bug in GEA versions v1.32.0 through v1.34.0 with the {{@last}} helper recommended in this solution. Use of the {{@last}} helper is fixed in Application Workflows and other cloud / browser-based uses, as well as in GEA version 1.35.0.

Thanks Dylan,

The problem has been resolved after upgrading the Losant Edge Agent.