skip to Main Content

EDIT: I have redone this question in its entirety as I discovered a flaw in my original: I CAN access ALL the data I need to get my output in a single call to the API.

My API call results in this JSON [abbreviated for clarity]:

{
    "lights": {
        "8": {
            "name": "Room 1 Main"
        }
    },
    "groups": {
        "1": {
            "name": "Room 1",
            "lights": [
                "8",
                "9",
                "10",
                "11",
                "12"
            ]
        }
    },
    "rules": {
        "1": {
            "name": "Light  ON",
            "conditions": [{
                    "address": "/sensors/9/state/presence",
                    "operator": "eq",
                    "value": "true"
                },
                {
                    "address": "/sensors/10/state/lightlevel",
                    "operator": "lt",
                    "value": "12000"
                }
            ],
            "actions": [{
                "address": "/lights/8/state",
                "method": "PUT",
                "body": {
                    "on": true
                }
            }]
        }
    },
    "sensors": {
        "9": {
            "name": "Room 1 Motion"
        },
        "10": {
            "name": "Room 1 Lux"
        }
    }
}

rules has references to lights, sensors, and (elsewhere) groups. I want to get output of each rule that substitutes the name of the object for the numeric reference. Using my example, output for Rule 1 would be something like:

{
    "name": "Light ON",
    "conditions": ["Room 1 Motion eq true",
        "Room 1 Lux lt 12000"
    ],
    "actions": "Room 1 Main on true"
}

So my question is: how can I iterate over each rule and then over each condition and action and use the values from sensors, lights, or groups to print that name instead of the reference?

FURTHER EDIT: Not all of the actions have on in the body. The variations are flag and status. For on and flag the permitted values are true|false and for status it’s enabled|disabled.

2

Answers


  1. Edited answer:

    script.jq:

    def find($root):
      (.address | capture("^/(?<type>\w+)/(?<name>\w+)"; "g")) as $c
      | $root[$c.type][$c.name];
    
    . as $root | .rules | [to_entries[].value | {
      name: .name, 
      conditions: [.conditions[] | "(find($root).name) (.operator) (.value)"],
      actions: [.actions[] | find($root).name + (.body | to_entries[0] | " (.key) (.value)")]
    }]
    

    This probably can be done in a much more elegant way, but it works.

    $ jq -f script.jq test.json
    [
      {
        "name": "Light  ON",
        "conditions": [
          "Room 1 Motion eq true",
          "Room 1 Lux lt 12000"
        ],
        "actions": [
          "Room 1 Main on true"
        ]
      }
    ]
    

    Differences from example output:

    1. actions is an array, because actions field in rules is an array.
    2. An array wraps the resulting objects, so that the output is valid JSON when there’re multiple rules, e.g:
    [
      {
        "name": "Light ON",
        ...
      },
      {
        "name": "Light OFF",
        ...
      }
    ]
    
    Login or Signup to reply.
  2. This response deals with the problem of
    "importing" the values of an associative bash array into JSON.
    The approach here requires one invocation of jq per bash array.

    For flexibility, we’ll define a bash function that will take the bash array name as an argument, and produce the corresponding JSON object (i.e. dictionary):

    function toJSON {
      local v="$1"
      jq -n '$ARGS.positional | [_nwise(length/2)] | transpose | map({(.[0]): .[1]}) | add' --args 
        $(eval "printf "%s\n" "${!$v[@]}"") $(eval "printf "%s\n" "${$v[@]}"")
    }
    

    You could then use this like so:

    jq --argjson sensors $(toJSON sensors) --argjson lights $(toJSON lights) ....
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search