Integrating Nora Light to Home Assistant Light [HowTo / Solved]

Hi There,

As I couldn’t find a succinct article on this below are a few considerations on how to control a Tasmota Light via Google Assistant through Nora and Home Assistant.

Prequel / Architecture
I have a number of Tasmotized (Sonoff) devices to control switches and lights around the house. The lights are controlled by Home Assistant and communicate via MQTT through Mosquitto Server. I further use NodeRed as an automation platform which also implements Nora as a means of controlling my devices through Google Assistant.

In my setup I decided for NodeRed to only communicate to Home Assistant to automate any behaviors (i.e. no direct comms to of NodeRed to devices via MQTT nodes). That way I know that I only need to worry about HA integration and can then automate any integrated device through the HA integration in NodeRed.

The problem
In this setup one of the challenges is the payloads received from Google Assistant commands interpreted by Nora for light controls. When you say “Hey Google, turn light x on.” or “Hey G, turn the light to 50% brightness” or “Hey G, make my light red” you get back a structure that isn’t immediately compatible with the HA Light_on Service inputs. It’s actually easy once done, but took a while to figure out…

The solution
The simple NodeRed flow below covers the conversion from NORA payload to HA Light.turn_on.
What it does:

  1. An Event_State monitors the Light state in HA and sends back the status to Google via the NORA Light Node. That way Google stays in sync with the current light state if changed in HA and not Google.
  2. The NORA Light node takes care of the Google Assistant commands and sends a payload back with state, brightness and color. I provide the HA light entity id as the topic in this node.
  3. A switch node splits the msg path based on the ‘on’ value from Nora:
    False is just passed through to the turn_off node.
    True is further processed as below.
  4. A scaling node scales the saturation value from 0-1 to 0-100 to be compatible with HA’s Light.turn_on expected value range.
  5. The ‘call service’ node Light.turn_on node contains the tiny bit of magic to pull out the relevant values from the incoming msg. Set the data field to a Jest expression and use:
{
  "entity_id": msg.topic, 
  "brightness": msg.payload.brightness,
  "hs_color": [
    msg.payload.color.spectrumHsv.hue, 
    msg.payload.color.spectrumHsv.saturation
  ]
}

This seems to work fine despite not using the V in HSV. (Happy for anyone to advise what to do with the V…).

The setup allows for Google Assistant control of:
a. on/off
b. brightness
c. colours
…in addition to having the light as a normal HA Light which allows full control from your phone.

Hope this helps

The full flow is here:

[
    {
        "id": "8b22f878.8bbf58",
        "type": "server-state-changed",
        "z": "f1ec0fc6.32285",
        "name": "Scarlett's Night Light State",
        "server": "9b3e0f74.a13fc",
        "version": 1,
        "exposeToHomeAssistant": false,
        "haConfig": [
            {
                "property": "name",
                "value": ""
            },
            {
                "property": "icon",
                "value": ""
            }
        ],
        "entityidfilter": "light.lamp_rgbw_scarlett",
        "entityidfiltertype": "exact",
        "outputinitially": false,
        "state_type": "habool",
        "haltifstate": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "outputs": 1,
        "output_only_on_state_change": true,
        "x": 250,
        "y": 1120,
        "wires": [
            [
                "7185d00d.5f86b"
            ]
        ]
    },
    {
        "id": "7185d00d.5f86b",
        "type": "nora-light",
        "z": "f1ec0fc6.32285",
        "devicename": "Scarlett's Night Light",
        "lightcolor": true,
        "brightnesscontrol": true,
        "turnonwhenbrightnesschanges": false,
        "passthru": false,
        "statepayload": true,
        "brightnessoverride": "",
        "roomhint": "Scarlett's Room",
        "name": "",
        "nora": "c97818ee.f9ae48",
        "topic": "light.lamp_rgbw_scarlett",
        "onvalue": "true",
        "onvalueType": "bool",
        "offvalue": "false",
        "offvalueType": "bool",
        "x": 480,
        "y": 1120,
        "wires": [
            [
                "e89d71d4.48981"
            ]
        ]
    },
    {
        "id": "e89d71d4.48981",
        "type": "switch",
        "z": "f1ec0fc6.32285",
        "name": "",
        "property": "payload.on",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 650,
        "y": 1120,
        "wires": [
            [
                "45207d5f.9ffa44"
            ],
            [
                "2cff4c8.e87f7b4"
            ]
        ]
    },
    {
        "id": "45207d5f.9ffa44",
        "type": "range",
        "z": "f1ec0fc6.32285",
        "minin": "0",
        "maxin": "1",
        "minout": "0",
        "maxout": "100",
        "action": "scale",
        "round": false,
        "property": "payload.color.spectrumHsv.saturation",
        "name": "Scale Saturation",
        "x": 800,
        "y": 1100,
        "wires": [
            [
                "89a7120b.6df35"
            ]
        ]
    },
    {
        "id": "2cff4c8.e87f7b4",
        "type": "api-call-service",
        "z": "f1ec0fc6.32285",
        "name": "Light OFF",
        "server": "9b3e0f74.a13fc",
        "version": 1,
        "debugenabled": false,
        "service_domain": "light",
        "service": "turn_off",
        "entityId": "",
        "data": "{\"entity_id\": msg.topic}",
        "dataType": "jsonata",
        "mergecontext": "",
        "output_location": "",
        "output_location_type": "none",
        "mustacheAltTags": false,
        "x": 960,
        "y": 1180,
        "wires": [
            []
        ]
    },
    {
        "id": "89a7120b.6df35",
        "type": "api-call-service",
        "z": "f1ec0fc6.32285",
        "name": "Light ON",
        "server": "9b3e0f74.a13fc",
        "version": 1,
        "debugenabled": false,
        "service_domain": "light",
        "service": "turn_on",
        "entityId": "",
        "data": "{\"entity_id\": msg.topic, \"brightness\": msg.payload.brightness,\t\"hs_color\": [\tmsg.payload.color.spectrumHsv.hue, \tmsg.payload.color.spectrumHsv.saturation]\t}",
        "dataType": "jsonata",
        "mergecontext": "",
        "output_location": "",
        "output_location_type": "none",
        "mustacheAltTags": false,
        "x": 960,
        "y": 1100,
        "wires": [
            []
        ]
    },
    {
        "id": "9b3e0f74.a13fc",
        "type": "server",
        "z": "",
        "name": "Home Assistant",
        "legacy": false,
        "addon": false,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": false,
        "cacheJson": true
    },
    {
        "id": "c97818ee.f9ae48",
        "type": "nora-config",
        "z": "",
        "name": "nora config",
        "group": "",
        "notify": false
    }
]

Hi this is for a whitebulb

{  
   "transition": 9 ,
   "brightness_pct": payload.brightness,
   "kelvin": payload.color.temperatureK
}

This goes in the data field of the service node its jasonata format.
Its good to understand the message format that comes in and how to apply it a basic message is just on or off like a socket. if you ask google to turn on the light then it turns on with the last used settings. If you ask it to turn on at a brightness of 50% then it sends the brightness attribute and if you ask for a color temperature 2700 - 6500 kelvin it will send that as well.

the dotted format is a hierarchy so payload.brightness is a simple attribute with 1 item the brightness level for color there can be several pairs but for my bulbs its just 1
payload.color.temperatureK what you are mapping to is in the first half of each line so brightness_pct value is stored in brightness: there is a difference in name for the attribute but this says use brightness value for brightness_pct color is a list of dictionary pairs.

transition is a fixed value in this case 9 thats a feature of the service call i am using and can be left out and the default would be used. missing out a line e.g brightness would mean the bulb could be turned on and the color temperature set but not the brightness the entity would use its last set value for brightness. For the op the light should have hue saturation and vibrance values since vibrance isn’t being set it will use the entities last set value. It’s really simple when you get it, but thats the hard part understanding what the data means. just make sure the data field is set for jasonata not json, json format is a bit different in syntax