Call Service

Integrations in Home Assistant provide service calls that can be used, for example, to set target temperature on heaters and air-conditioning units. All service calls can be found and tested within Home Assistant Developer toolbox, and it is also useful to check the integration documentation to identify exactly what is required for a successful service call.

All service calls require:

  • a domain (integration platform) such as 'climate'
  • a service call within this domain, such as 'set_temperature'
  • a target entity or entities (or area or device containing entities) to apply the service to
  • a JSON data object, containing any additional required and optional parameters

The data object will vary, depending on the integration and service call, from an empty object {} to something much more complex. Constructing this data object correctly is key to a successful service call, and JSONata rather than mustache templating should ideally be used to achieve this.

Note: The data field used in a service call must be a valid JSON object.

  • Mustache templates are delimited with {{msg.payload}}. Although acceptable in most JSON input fields, this is invalid syntax in JSONata expressions and must not be used.
  • Home Assistant configuration uses YAML constructs, containing space-tabulation for objects and '-' for arrays. This format is incompatible with both JSON and JSONata, but YAML constructs can be converted to JSON.
  • The WebSocket nodes Data UI field option typically offers two input methods:
    • {} for JSON - this must be strict JSON formed with all literals and string keys. Mustache templates may work successfully in simple strings. JSONata will not work here. The JSON option cannot accept anything other than literals and Mustache templates.
    • J: for JSONata (expression) - this must evaluate to strict JSON, but can be formed with expressions in both the object keys (evaluating as strings) and the object values. In JSONata, literals evaluate as themselves, so the JSONata option can accept a JSON literal construct.

Passing variables to WebSocket nodes in summary

JSONata in Node-RED nodes accesses the incoming message as a JSON object. Thus msg.payload is referenced just as payload and is evaluated as the value of that key. The leading 'msg.' is not required, and {{ payload }} is invalid JSONata syntax.

Node-RED typically uses msg.payload to pass values between nodes, however any field can be used. Most WebSocket nodes have output properties that default to set msg.data to the entity details, and therefore in the flow following (not in) the node, data.state will typically provide the state value of the entity that was the subject of the preceding WebSocket node.

Within the WebSocket node itself, the entity data is accessed using the special JSONata $entity() function, hence $entity().state would be used. In a similar manner, the $entities() function can reference any Home Assistant entity, and $entities('sensor.date_time').state would return the state value of the given date_time sensor.

In JSONata, as well as using message fields or the entity functions, Node-RED provides functions to access environment variables using $env('ENV_NAME'). Global context can be read using $globalContext(name[, store]) and flow context likewise as $flowContext(name[, store]).

JSONata and the Call Service node

For Node-RED flows with a Call Service node there are three locations where the Data object can be defined.

  • Directly in the Call Service node UI 'Data' field, using the JSONata option.
  • In a preceding Change node, passing the Service Call settings in a msg.payload object.
  • In a flow trigger node, such as Events: state node, generated as an output msg.payload object.

There are advantages and drawbacks to each approach, and personal choice as well as the particular case and need will influence your flow design.

screenshot

Here are three examples, each using JSONata to set the required Data object, and one example of using JSONata to process service call return data.

[{"id":"0050317002075c30","type":"group","z":"776c027950fc8c3f","name":"Call Service node - build a data object / process call return","style":{"label":true,"color":"#000000"},"nodes":["a67d69634829a418","86cbf80d.5a9cf8","8aad6643.9ee7a8","897a31873370f8cb","3bbb02bc34fb543a","6601ecbcd16c5aa3","fe24231bb58e259d","b0b87dc3a978e67e","8602b67dfe66a5e3","fe9baddadc8fea32","fd65cb1e1c933b3a","0acd153a39f25184","be9ef2598f33324a","8446a03e6d2b76a3","3c75c1ae78ed9894","9fc179c38d22f267","c84c9a405862bb69","f0d2bc4cc11a7608"],"x":34,"y":39,"w":1212,"h":442},{"id":"a67d69634829a418","type":"api-call-service","z":"776c027950fc8c3f","g":"0050317002075c30","name":"","server":"","version":5,"debugenabled":false,"domain":"climate","service":"set_temperature","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1030,"y":220,"wires":[[]]},{"id":"86cbf80d.5a9cf8","type":"api-call-service","z":"776c027950fc8c3f","g":"0050317002075c30","name":"","server":"","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.kitchen"],"data":"{\"brightness\": $min([$entities(\"light.kitchen\").attributes.brightness + $number($entities(\"input_number.brightness\").state), 255])}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"none","output_location":"payload","output_location_type":"msg","x":730,"y":120,"wires":[[]]},{"id":"8aad6643.9ee7a8","type":"server-state-changed","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Remote Button","server":"","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"sensor.button","entityIdType":"exact","outputInitially":false,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":520,"y":120,"wires":[["86cbf80d.5a9cf8"]]},{"id":"897a31873370f8cb","type":"server-state-changed","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Input Number (Temp)","server":"","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"input_number.help_temp_number","entityIdType":"exact","outputInitially":false,"stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":540,"y":220,"wires":[["fe24231bb58e259d"]]},{"id":"3bbb02bc34fb543a","type":"inject","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Manual Test","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":530,"y":80,"wires":[["86cbf80d.5a9cf8"]]},{"id":"6601ecbcd16c5aa3","type":"comment","z":"776c027950fc8c3f","g":"0050317002075c30","name":"1a - JSONata builds object in Call Service node \\n Increase light brightness with remote","info":"","x":240,"y":100,"wires":[]},{"id":"fe24231bb58e259d","type":"change","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Service Call Settings","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t   \"target\": {\"entity_id\": $globalContext(\"AClist\")},\t   \"data\": {\t       \"temperature\": payload,\t       \"hvac_mode\": payload>20 ? \"heat\" : \"cool\"\t   }\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":220,"wires":[["a67d69634829a418"]]},{"id":"b0b87dc3a978e67e","type":"api-call-service","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Send notification","server":"","version":5,"debugenabled":false,"domain":"notify","service":"mobile_app_geoff_phone","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":790,"y":300,"wires":[[]]},{"id":"8602b67dfe66a5e3","type":"server-state-changed","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Last person to leave","server":"","version":5,"outputs":2,"exposeAsEntityConfig":"","entityId":"person.","entityIdType":"substring","outputInitially":false,"stateType":"str","ifState":"\"home\" in $entities().*[$substringBefore(entity_id,\".\")=\"person\"].state","ifStateType":"jsonata","ifStateOperator":"jsonata","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"{\t    \"data\": {\t        \"message\": \"The \" & $join($entities().*[state = \"on\" and entity_id ~> /^light|^switch/].attributes.friendly_name, \", \") & \" are on.\",\t        \"title\": \"Things Left On \" & $entity().attributes.friendly_name\t    }\t}","valueType":"jsonata"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":530,"y":300,"wires":[[],["b0b87dc3a978e67e"]]},{"id":"fe9baddadc8fea32","type":"api-call-service","z":"776c027950fc8c3f","g":"0050317002075c30","name":"","server":"","version":5,"debugenabled":false,"domain":"weather","service":"get_forecasts","areaId":[],"deviceId":[],"entityId":["weather.forecast_home"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"results","propertyType":"msg","value":"","valueType":"results"}],"queue":"none","x":360,"y":440,"wires":[["8446a03e6d2b76a3"]]},{"id":"fd65cb1e1c933b3a","type":"inject","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Set Forecast type","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"data\": {\"type\": \"hourly\"}}","payloadType":"jsonata","x":160,"y":440,"wires":[["fe9baddadc8fea32"]]},{"id":"0acd153a39f25184","type":"debug","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Start Hour","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"$substringAfter(payload.time,\"T\")~>$substringBefore(\"+\")","targetType":"jsonata","statusVal":"$substringAfter(payload.time,\"T\")~>$substringBefore(\"+\")","statusType":"auto","x":1130,"y":440,"wires":[]},{"id":"be9ef2598f33324a","type":"comment","z":"776c027950fc8c3f","g":"0050317002075c30","name":"1d - JSONata extracts details from service call return \\n using Inject, Change, and Debug nodes","info":"","x":250,"y":380,"wires":[]},{"id":"8446a03e6d2b76a3","type":"change","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Extract forecast time and arrays of hourly temp, cloud, and rain","rules":[{"t":"set","p":"payload","pt":"msg","to":"results.*.forecast{\t   \"time\": $[0].datetime,\t   \"temp\": $.temperature,\t   \"cloud\": $.cloud_coverage,\t   \"rain\": $.precipitation\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":440,"wires":[["0acd153a39f25184","3c75c1ae78ed9894"]]},{"id":"3c75c1ae78ed9894","type":"debug","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Payload","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1120,"y":400,"wires":[]},{"id":"9fc179c38d22f267","type":"comment","z":"776c027950fc8c3f","g":"0050317002075c30","name":"1b - JSONata builds object in Change Node \\n Set a/c target temperature and mode","info":"","x":230,"y":200,"wires":[]},{"id":"c84c9a405862bb69","type":"comment","z":"776c027950fc8c3f","g":"0050317002075c30","name":"1c - JSONata builds object in Event: State node \\n Send notification when leaving home","info":"","x":240,"y":300,"wires":[]},{"id":"f0d2bc4cc11a7608","type":"inject","z":"776c027950fc8c3f","g":"0050317002075c30","name":"Turn Off","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\t   \"domain\": \"homeassistant\",\t   \"service\": \"turn_off\",\t   \"target\": {\t       \"area_id\": [\"bedroom\"]\t   },\t   \"data\": {} \t}","payloadType":"jsonata","x":820,"y":180,"wires":[["a67d69634829a418"]]}]

Setting the Data field

Settings a data property from a Home Assistant entity

Example: A Call Service node that sets the target temperature of a climate entity to the current temperature of a sensor entity.

{
  "temperature": $entities("sensor.living_room_temperature").state
}

Increase lights brightness with remote

Example: A remote with a button that when clicked increases the brightness of a given light by an amount that is set from an input_number inside Home Assistant.

JSONata expression in the Call Service node UI Data field. Nothing special is required as input to this node as all the settings are completed within the node itself.

{
  "brightness": $min([
    $entities("light.kitchen").attributes.brightness +
      $number($entities("input_number.brightness").state),
    255
  ])
}

Setting target temperature on air-conditioning unit

Example: Set the target temperature (and mode) for air-conditioning unit from the input msg.payload value.

Here a Change node is used to build the msg.payload object required to set the Call Service node parameters, including the nested data field. In a Change node, the special WebSocket $entity() and $entities() functions are not available, however we can still perform a read from global context using the Node-RED $globalContext() function. These Node-RED functions can be used to bring in values from context and environment variables.

{
   "target": {"entity_id": $globalContext("AClist")},
   "data": {
       "temperature": payload,
       "hvac_mode": payload>20 ? "heat" : "cool"
   }
}

The Call Service node can accept from one to all of the UI parameters in msg.payload, and where given these each take precedence over any UI settings. Default settings can therefore be set in the UI, and can then be over-riden by the input message, including when using settings from Inject nodes as shown in the example.

Notification of lights left on when leaving home

Example: Get notified when lights or switches are left on when you leave.

This example uses JSONata in two places in the Events: state node. The state value test is a JSONata expression that must return true for the node to output a message (upper output). This code selects an array of all entity states for entities starting with 'person', and will return true if this array contains one or more with state "home". The expression will therefore only return false when no-one is home, and we can use the lower (condition fail) output rather than using the JSONata $not() function to look for 'not home'.

"home" in $entities().*[$substringBefore(entity_id,".")="person"].state

JSONata is also then used in the output properties to create the required object for a notification service call. It uses $entities().* with a filter looking for those with "light" or "switch" in the entity_id and state "on", then selecting the friendly_name, and joining all those found into a string list in the message.

The message title uses $entity().attributes.friendly_name to add the name of the person who just left home to the message title.

{
    "data": {
        "message": "The " &
          $join($entities().*[state = "on" and entity_id ~>
          /^light|^switch/].attributes.friendly_name, ", ") &
          " are on.",
        "title": "Things Left On " & $entity().attributes.friendly_name
    }
}

In this example all the parameter setup is performed in the Events: state node, so the Call Service node can accept all the required parameters just from the input msg.payload object.

Processing a service call return

Home Assistant service calls can provide data returned as a result of the call, and JSONata is an ideal tool to manage any resulting JSON object and array structures. This example uses JSONata to extract and manipulate weather forecasts, providing a summary in a modified format.

Example: The forecast service call now returns an array of forecasts. JSONata is used to iterate over all forecasts, returning an new object for each, with a time and arrays for temperature, cloud coverage, and precipitation only.

results.*.forecast{
   "time": $[0].datetime,
   "temp": $.temperature,
   "cloud": $.cloud_coverage,
   "rain": $.precipitation
}

Also see: