Google-Nest Offical Device Access Console Finally Released!

@allenporter thanks for your hard work on this, I’m very excited. I agree with @msmits2011 - if you have a donate link or something then I’m very happy to show my appreciation. Had a quick look at your github project page but couldn’t see one.

1 Like

@mbuscher - could you show how each element is set up as when I look at the devices flow I keep getting an error when I look at the debug console on the devices:

error: object

code: 401

message: "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project."

status: “UNAUTHENTICATED”

You can verify your parameters using the following command, obviously with your values for the 2 placeholders:

$ curl \
-H "Content-Type: application/json" \
-H "Authorization: Bearer VALID_ACCESS_TOKEN" \
https://smartdevicemanagement.googleapis.com/v1/enterprises/VALID_PROJECT_ID/devices

For grins, when I tested this command prior to pasting it here, I had omitted the word Bearer in the authorization header, which gave me the error message you reported. So check your syntax.

Edit: showing command as ‘preformatted text’ rather than as blockquote, because the latter substituted double-quotes.

problem solved as I had to also set the token http node to return a parsed JSON object

following for updates, thanks!

1 Like

Thanks for the work here @allenporter! keep it up!

1 Like

According to this article, Google is working on it?

1 Like

Thanks for that, but seeing the publish date of the article I am a bit sceptical about it.

Otherwise the example at the end was actually a feature of the Works With Nest program together with Philips Hue and I believe with LIFX as well.

I have updated the sequence created by @KosmixLB. It adda a temperature set topic. The state is not reflected till the next refresh of devices which happens every minute. This only works for setting the heat point.

[
    {
        "id": "698d3ddd.eb57bc",
        "type": "tab",
        "label": "Climate",
        "disabled": false,
        "info": ""
    },
    {
        "id": "87be74b8.550008",
        "type": "inject",
        "z": "698d3ddd.eb57bc",
        "name": "Run once an hour",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "3600",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 290,
        "y": 200,
        "wires": [
            [
                "6f10d0ec.3a6ff"
            ]
        ]
    },
    {
        "id": "6f10d0ec.3a6ff",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Creds",
        "func": "let clientSecret = '';\nlet clientId = '';\nlet refreshToken = '';\nmsg.url = \"https://www.googleapis.com/oauth2/v4/token\";\nmsg.method = \"POST\";\nmsg.payload = {\n    \"client_id\": clientId,\n    \"client_secret\": clientSecret,\n    \"refresh_token\": refreshToken,\n    \"grant_type\": 'refresh_token'\n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 590,
        "y": 200,
        "wires": [
            [
                "89a171e7.7d9188"
            ]
        ]
    },
    {
        "id": "89a171e7.7d9188",
        "type": "www-request",
        "z": "698d3ddd.eb57bc",
        "name": "Refresh token",
        "method": "use",
        "ret": "obj",
        "url": "",
        "follow-redirects": true,
        "persistent-http": true,
        "tls": "",
        "x": 920,
        "y": 200,
        "wires": [
            [
                "917ffc41.6c2008"
            ]
        ]
    },
    {
        "id": "917ffc41.6c2008",
        "type": "change",
        "z": "698d3ddd.eb57bc",
        "name": "token",
        "rules": [
            {
                "t": "set",
                "p": "access_token",
                "pt": "flow",
                "to": "payload.access_token",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1170,
        "y": 200,
        "wires": [
            []
        ]
    },
    {
        "id": "f11d4cbb.f9937",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Get Devices",
        "func": "let projectId = '';\nmsg.url = \"https://smartdevicemanagement.googleapis.com/v1/enterprises/\" + projectId + \"/devices\"\nmsg.method = \"GET\";\nmsg.headers = {\n    'Authorization': 'Bearer ' + flow.get('access_token'),\n    'Content-Type': 'application/json'\n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 630,
        "y": 320,
        "wires": [
            [
                "ee9525bd.c25b98"
            ]
        ]
    },
    {
        "id": "3c9e4af4.7fcb46",
        "type": "inject",
        "z": "698d3ddd.eb57bc",
        "name": "Trigger one a minute to get update",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "60",
        "crontab": "",
        "once": false,
        "onceDelay": "1",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 340,
        "y": 320,
        "wires": [
            [
                "f11d4cbb.f9937"
            ]
        ]
    },
    {
        "id": "ee9525bd.c25b98",
        "type": "www-request",
        "z": "698d3ddd.eb57bc",
        "name": "Call list devices",
        "method": "use",
        "ret": "obj",
        "url": "",
        "follow-redirects": true,
        "persistent-http": true,
        "tls": "",
        "x": 880,
        "y": 320,
        "wires": [
            [
                "2e420238.297a7e"
            ]
        ]
    },
    {
        "id": "2e420238.297a7e",
        "type": "change",
        "z": "698d3ddd.eb57bc",
        "name": "Get device id and put list devices in PL",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.devices[0]",
                "tot": "jsonata"
            },
            {
                "t": "set",
                "p": "device",
                "pt": "flow",
                "to": "payload[0].parentRelations[0].displayName",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1190,
        "y": 320,
        "wires": [
            [
                "97d1423e.97e05",
                "2fb1380e.a1502",
                "f8150650.633818",
                "b071ffcc.f9aab8",
                "6640d937.101a78",
                "d0d1b134.a1ea5"
            ]
        ]
    },
    {
        "id": "a0c1f56c.f8738",
        "type": "mqtt out",
        "z": "698d3ddd.eb57bc",
        "name": "Send_To_Mqtt",
        "topic": "",
        "qos": "0",
        "retain": "true",
        "broker": "1faf99cd.61b8f6",
        "x": 1220,
        "y": 620,
        "wires": []
    },
    {
        "id": "97d1423e.97e05",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Config_Topic",
        "func": "var device_name = flow.get('device');\nvar trunc_device_name =  device_name.replace(/ /g, '');\nvar trunc_lower_device_name = trunc_device_name.toLowerCase();\nvar basetopic = \"homeassistant/climate/\" + trunc_lower_device_name\nvar modes = msg.payload.traits['sdm.devices.traits.ThermostatMode'].availableModes.map(v => v.toLowerCase());\nmsg.topic = basetopic + \"/config\";\nmsg.payload = {\n                \"name\": \"Thermostat\" + trunc_lower_device_name,\n                \"mode_stat_t\": basetopic + \"/state\",\n                \"mode_stat_tpl\": \"{{ value_json.mode }}\", \n                \"avty_t\": basetopic + \"/available\",\n                \"payload_available\": \"online\",\n                \"payload_not_available\": \"offline\",\n                \"curr_temp_t\": basetopic + \"/state\", \n                \"curr_temp_tpl\": \"{{ value_json.current_temp }}\", \n                \"min_temp\": msg.payload.traits['sdm.devices.traits.ThermostatEco'].heatCelsius,\n                \"max_temp\": msg.payload.traits['sdm.devices.traits.ThermostatEco'].coolCelsius,\n                \"temp_step\": 0.5,\n                \"modes\": modes,\n                \"unique_id\": msg.payload.name,\n                \"temp_unit\": \"C\",\n                \"temp_cmd_t\": basetopic + \"/temperature/set\",\n                \"temp_stat_t\": basetopic + \"/state\",\n                \"temp_stat_tpl\": \"{{ value_json.heat_temp }}\"\n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 270,
        "y": 420,
        "wires": [
            [
                "a0c1f56c.f8738"
            ]
        ]
    },
    {
        "id": "2fb1380e.a1502",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "State_Topic",
        "func": "var device_name = flow.get('device');\nvar trunc_device_name =  device_name.replace(/ /g, '');\nvar trunc_lower_device_name = trunc_device_name.toLowerCase();\nvar basetopic = \"homeassistant/climate/\" + trunc_lower_device_name\nvar Strmode = msg.payload.traits['sdm.devices.traits.ThermostatMode'].mode;\nvar lowermode = Strmode.toLowerCase();\nmsg.topic = basetopic + \"/state\";\nmsg.payload = {\n                \"mode\": lowermode,\n                \"current_temp\": msg.payload.traits['sdm.devices.traits.Temperature'].ambientTemperatureCelsius,\n                \"heat_temp\": msg.payload.traits[\"sdm.devices.traits.ThermostatTemperatureSetpoint\"].heatCelsius\n                \n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 270,
        "y": 460,
        "wires": [
            [
                "917c2748.7d8208"
            ]
        ]
    },
    {
        "id": "f8150650.633818",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Availabilty",
        "func": "var device_name = flow.get('device');\nvar trunc_device_name =  device_name.replace(/ /g, '');\nvar trunc_lower_device_name = trunc_device_name.toLowerCase();\nvar basetopic = \"homeassistant/climate/\" + trunc_lower_device_name\n\nvar StrPayload = msg.payload.traits[\"sdm.devices.traits.Connectivity\"].status;\nvar StrLowerPayload = StrPayload.toLowerCase();\nmsg.topic = basetopic + \"/available\";\nmsg.payload = StrLowerPayload;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 260,
        "y": 500,
        "wires": [
            [
                "a0c1f56c.f8738",
                "917c2748.7d8208"
            ]
        ]
    },
    {
        "id": "d0d1b134.a1ea5",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Sensorstate",
        "func": "var device_name = flow.get('device');\nvar trunc_device_name =  device_name.replace(/ /g, '');\nvar trunc_lower_device_name = trunc_device_name.toLowerCase(); \nvar basetopic = \"homeassistant/sensor/\" + trunc_lower_device_name;\nmsg.topic = basetopic + \"/state\";\nmsg.payload = { \n            \"humidity\": msg.payload.traits[\"sdm.devices.traits.Humidity\"].ambientHumidityPercent,\n            \"temperature\" : msg.payload.traits[\"sdm.devices.traits.Temperature\"].ambientTemperatureCelsius,\n            \"target\": msg.payload.traits[\"sdm.devices.traits.ThermostatTemperatureSetpoint\"].heatCelsius\n};\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 230,
        "y": 800,
        "wires": [
            [
                "a0c1f56c.f8738",
                "917c2748.7d8208"
            ]
        ]
    },
    {
        "id": "b071ffcc.f9aab8",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Config Humidity",
        "func": "var device_name = flow.get('device');\nvar trunc_device_name =  device_name.replace(/ /g, '');\nvar trunc_lower_device_name = trunc_device_name.toLowerCase();\nvar basetopic = \"homeassistant/sensor/\" + trunc_lower_device_name ;\nmsg.topic = basetopic + \"humididty/config\";\nmsg.payload = {\n               \"device_class\": \"humidity\",\n               \"name\": \"Nest Humidity\",\n               \"unique_id\": trunc_lower_device_name + \"humidity\" ,\n               \"unit_of_measurement\": \"%\",\n               \"state_topic\": basetopic + \"/state\",\n               \"value_template\": \"{{ value_json.humidity }}\" \n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 240,
        "y": 760,
        "wires": [
            [
                "a0c1f56c.f8738"
            ]
        ]
    },
    {
        "id": "6640d937.101a78",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "Config Temperature",
        "func": "var device_name = flow.get('device');\nvar trunc_device_name =  device_name.replace(/ /g, '');\nvar trunc_lower_device_name = trunc_device_name.toLowerCase();\nvar basetopic = \"homeassistant/sensor/\" + trunc_lower_device_name;\nmsg.topic = basetopic + \"temp/config\";\nmsg.payload = {\n               \"device_class\": \"temperature\",\n               \"name\": \"Nest Temperature\",\n               \"unique_id\": trunc_lower_device_name + \"temp\" ,\n               \"unit_of_measurement\": \"°C\",\n               \"state_topic\": basetopic + \"/state\",\n               \"value_template\": \"{{ value_json.temperature }}\" \n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 250,
        "y": 720,
        "wires": [
            [
                "a0c1f56c.f8738"
            ]
        ]
    },
    {
        "id": "917c2748.7d8208",
        "type": "delay",
        "z": "698d3ddd.eb57bc",
        "name": "Wait 5 seconds for the config topics to be processed",
        "pauseType": "delay",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 600,
        "y": 580,
        "wires": [
            [
                "a0c1f56c.f8738"
            ]
        ]
    },
    {
        "id": "c75a6128.362b8",
        "type": "comment",
        "z": "698d3ddd.eb57bc",
        "name": "Trigger once an hour",
        "info": "Trigger once an hour to refresh token",
        "x": 270,
        "y": 140,
        "wires": []
    },
    {
        "id": "512ea84c.d28248",
        "type": "comment",
        "z": "698d3ddd.eb57bc",
        "name": "Some needed info about your account",
        "info": "",
        "x": 610,
        "y": 140,
        "wires": []
    },
    {
        "id": "968825dd.cc985",
        "type": "comment",
        "z": "698d3ddd.eb57bc",
        "name": "Store token in flow variable",
        "info": "",
        "x": 1210,
        "y": 140,
        "wires": []
    },
    {
        "id": "6d5d1fe4.c41f",
        "type": "comment",
        "z": "698d3ddd.eb57bc",
        "name": "Functions for setting the MQTT-climate",
        "info": "Functions for setting the MQTT-climate",
        "x": 330,
        "y": 380,
        "wires": []
    },
    {
        "id": "c87f1c2b.ea958",
        "type": "comment",
        "z": "698d3ddd.eb57bc",
        "name": "Functions for setting the mqtt sensors for temp and humidity ",
        "info": "",
        "x": 360,
        "y": 680,
        "wires": []
    },
    {
        "id": "88028f9a.c18638",
        "type": "mqtt in",
        "z": "698d3ddd.eb57bc",
        "name": "",
        "topic": "homeassistant/climate/livingroom/temperature/set",
        "qos": "2",
        "datatype": "auto",
        "broker": "1faf99cd.61b8f6",
        "x": 350,
        "y": 900,
        "wires": [
            [
                "e1ccf9a8.d4224"
            ]
        ]
    },
    {
        "id": "e1ccf9a8.d4224",
        "type": "function",
        "z": "698d3ddd.eb57bc",
        "name": "",
        "func": "let projectId = '';\nlet deviceId = '';\nmsg.url = \"https://smartdevicemanagement.googleapis.com/v1/enterprises/\" + projectId + \"/devices/\" + deviceId+\":executeCommand\"\nmsg.method = \"POST\";\nmsg.headers = {\n    'Authorization': 'Bearer ' + flow.get('access_token'),\n    'Content-Type': 'application/json'\n};\nmsg.payload = {\n    \"command\" : \"sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat\",\n    \"params\" : {\n        \"heatCelsius\" : Number(msg.payload)\n        \n    }\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 680,
        "y": 900,
        "wires": [
            [
                "1e9be464.9b1a9c",
                "e578edc4.33ae"
            ]
        ]
    },
    {
        "id": "1e9be464.9b1a9c",
        "type": "www-request",
        "z": "698d3ddd.eb57bc",
        "name": "Set Temp",
        "method": "use",
        "ret": "obj",
        "url": "",
        "follow-redirects": true,
        "persistent-http": true,
        "tls": "",
        "x": 880,
        "y": 900,
        "wires": [
            [
                "e578edc4.33ae"
            ]
        ]
    },
    {
        "id": "e578edc4.33ae",
        "type": "debug",
        "z": "698d3ddd.eb57bc",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 690,
        "y": 820,
        "wires": []
    },
    {
        "id": "1faf99cd.61b8f6",
        "type": "mqtt-broker",
        "z": "",
        "name": "mqtt",
        "broker": "192.168.86.240",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": ""
    }
]
4 Likes

I had to use the following syntax for it to see my device within the change function node:

payload.parentRelations[0].displayName

Other than that awesome

@allenporter Any idea what’s holding back the review for the pull request?

Hi!
The devs have overall been pretty helpful in feedback on the first round. My general impression is the Home Assistant devs are fairly busy and appreciate volunteer reviews (and in fact ask you do reviews for others as part of their pull request).

I would suggest any other volunteer reviews might be welcome to help do initial passes to improve any code quality, leasing the review burden.

In this case, this is landed on the owner of the existing nest integration. I figured I’d give them some time before poking more. Meanwhile I can start working on other parts of the integration. This version has events flowing via pubsub, and so I could start exploring the camera integration, or climate next.

5 Likes

Damn shame Google STILL doesn’t allow API access to the temperature sensor. At this rate, I will likely replace my NEST with another product long before this functionality ever shows up in the API, let alone in HA. :frowning:

1 Like

This is one of the features I was hoping for in the previous Nest API that never happened.

Camera Integration would be awesome!

3 Likes

Climate, too!

Currently on the fence about replacing my nest thermostat with an ecobee… I have been saved by NOT upgrading my nest account before the switch but at this point I don’t see myself purchasing any more Nest products other than the minis.

Maybe if this API gets updated I can carry on a bit longer with the nest thermostat.

1 Like

How to use this code in Home Assistant today? https://github.com/allenporter/python-google-nest-sdm

1 Like

Following currently using starling and HA to get everything in HomeKit

Would be nice if camera support + streaming capability was added, so not just still image view

1 Like