Google-Nest Offical Device Access Console Finally Released!

I started making one, and got the oauth flow and simple device list working, though not yet doing anything with the actual API other than creating dummy placeholder sensors.

Home Assistant:

Python Lib:

Not sure how long it will take to get it going, but I’ll keep working on it over the next few weeks. This is the first time I have ever tried working on Home Assistant, so there is a bit of a learning curve.

17 Likes

I’m so excited!
Thanks in advance for all your efforts!

It looks like you’re making a new integration called google_nest instead of updating the existing nest integration with support for the new API. Are you on the HA developer discord? Might want to run that plan by the HA devs before wading in too deep. My experience is that they usually prefer to update existing integrations when possible so I wouldn’t want you to get weeks in and then be asked to re-write the whole thing as an update.

1 Like

Personally, I would see this as a new integration. I don’t want the existing Nest integration to change as I am running Protects, which are not available in the new API (yet). Once the functionality is 1-to-1, the old integration could be deprecated in favor of this, and could be renamed. I am even considering breaking my cameras out into a new account so that I can leverage the RTSP capabilities, and leave the Protects and my Thermostat on the existing integration. It would make sense to keep both for the short term, with the end goal of deprecating the original Nest integration at some point.

7 Likes

I’d agree with this. There are still some users on the legacy API (for as long as Google doesn’t force the migration which I expect is forthcoming). A new integration would make sense.

1 Like

I wasn’t imagining the existing Nest integration be gutted and replaced, it wouldn’t be backwards compatible if it completely changed over to the new API. A version field or something could be added so users could then say which version of Nest API they were integrating with. The integration would be fleshed out as much as possible with the new API as it currently allows and expand to be more feature complete over time as the API develops. Users using the old API would see identical behavior through upgrade.

Your point about separating the cameras is fair, I thought the same. That would be problematic although it looks like the author is working to make the integration UI-configurable and use config entries. If so then it shouldn’t be an issue to integrate with Nest twice in the same Home Assistant instance with two different accounts, one using the old API and one using the new.

To be clear, I’m not on the HA dev team so I may be totally wrong here. My only suggestion is that before embarking on a multi-week to month long project I would highly suggest running your plan by the HA dev team in Discord. Especially since it looks like the eventual PR will be quite large based just on the commits so far.

1 Like

Great suggestion! I really have only some idea of the norms of the code base given the excellent public docs but could really use some experienced developer guidance. Assuming most of us here are just speaking for ourselves, sounds like chatting on the discord would be a good way to go. Thanks!

1 Like

Could someone be so kind and share a complete flow I can import?

You can find a guide over here: Reddit - Guide: Nest Hello Doorbell Integration via Node-Red and Smart Device Management (SDM) API

Check the comments for a flow that is more dynamic. The example provides only for a single device, Nest Hello.

2 Likes

Nice! Great progress! Once:

  • Create entities for devices
  • Modify to use pull with pubsub events

are done, I’ll be one of the first to install!

Thanks for posting about the supported products, disappointing to see Next Protect isn’t there - guess I’ll stay with Works with Nest Legacy a bit longer. Official response i got from Google was “Home Automation for Nest Protect isn’t supported”.
Then followed up with “Doing this will bring all Google Nest devices under one eco-system and will also optimize its integration with 3rd party software. Once the other companies update their integration, their software will work even better than before with the Google Nest products.”

Wait and see I guess. Have fun!

1 Like

I cannot understand this statement. An alarming Nest Protect is the best source for Home Automation. Shut the gas feed to the building, turn on the sprinklers around the house, turn lights on and flash in other rooms, etc. Just some simple examples…

1 Like

It’s probably for liability reasons.
They don’t want to be responsible for not shutting off your gas valve.

[
{
        "id": "5b989228.aab31c",
        "type": "tab",
        "label": "Flow 2",
        "disabled": false,
        "info": ""
    },
    {
        "id": "1871b1cd.aac1fe",
        "type": "inject",
        "z": "5b989228.aab31c",
        "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": [
            [
                "9ee64dba.7fb9"
            ]
        ]
    },
    {
        "id": "9ee64dba.7fb9",
        "type": "function",
        "z": "5b989228.aab31c",
        "name": "Creds",
        "func": "let clientId = '--your clientid--';\nlet clientSecret = '--your client secret--';\nlet refreshToken = '--your refresh token--';\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": [
            [
                "bc3fe675.9472e8"
            ]
        ]
    },
    {
        "id": "bc3fe675.9472e8",
        "type": "www-request",
        "z": "5b989228.aab31c",
        "name": "Refresh token",
        "method": "use",
        "ret": "obj",
        "url": "",
        "follow-redirects": true,
        "persistent-http": true,
        "tls": "",
        "x": 920,
        "y": 200,
        "wires": [
            [
                "f18673a6.ce0be"
            ]
        ]
    },
    {
        "id": "f18673a6.ce0be",
        "type": "change",
        "z": "5b989228.aab31c",
        "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": "a67961d7.db2c6",
        "type": "function",
        "z": "5b989228.aab31c",
        "name": "Get Devices",
        "func": "let projectId = 'e7881bab-edca-4ffa-b406-a6fa37cc3000';\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": [
            [
                "94d9bb57.14ec38"
            ]
        ]
    },
    {
        "id": "99f4279c.7c4848",
        "type": "inject",
        "z": "5b989228.aab31c",
        "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": [
            [
                "a67961d7.db2c6"
            ]
        ]
    },
    {
        "id": "94d9bb57.14ec38",
        "type": "www-request",
        "z": "5b989228.aab31c",
        "name": "Call list devices",
        "method": "use",
        "ret": "obj",
        "url": "",
        "follow-redirects": true,
        "persistent-http": true,
        "tls": "",
        "x": 880,
        "y": 320,
        "wires": [
            [
                "cf128f84.af196"
            ]
        ]
    },
    {
        "id": "cf128f84.af196",
        "type": "change",
        "z": "5b989228.aab31c",
        "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": [
            [
                "ab154cad.04715",
                "94dcce1b.9e85",
                "7497aa97.47d814",
                "31a3cba4.a01a54",
                "31117ae9.196116",
                "df59ffbf.f7434"
            ]
        ]
    },
    {
        "id": "32507ed4.047f22",
        "type": "mqtt out",
        "z": "5b989228.aab31c",
        "name": "Send_To_Mqtt",
        "topic": "",
        "qos": "0",
        "retain": "true",
        "broker": "fe4b1d0d.d5113",
        "x": 1100,
        "y": 700,
        "wires": []
    },
    {
        "id": "ab154cad.04715",
        "type": "function",
        "z": "5b989228.aab31c",
        "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};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 250,
        "y": 480,
        "wires": [
            [
                "32507ed4.047f22"
            ]
        ]
    },
    {
        "id": "94dcce1b.9e85",
        "type": "function",
        "z": "5b989228.aab31c",
        "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                \n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 250,
        "y": 520,
        "wires": [
            [
                "44c5ab55.d5ec74"
            ]
        ]
    },
    {
        "id": "7497aa97.47d814",
        "type": "function",
        "z": "5b989228.aab31c",
        "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": 240,
        "y": 560,
        "wires": [
            [
                "32507ed4.047f22",
                "44c5ab55.d5ec74"
            ]
        ]
    },
    {
        "id": "31a3cba4.a01a54",
        "type": "function",
        "z": "5b989228.aab31c",
        "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};\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 250,
        "y": 800,
        "wires": [
            [
                "32507ed4.047f22",
                "44c5ab55.d5ec74"
            ]
        ]
    },
    {
        "id": "31117ae9.196116",
        "type": "function",
        "z": "5b989228.aab31c",
        "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": 260,
        "y": 760,
        "wires": [
            [
                "32507ed4.047f22"
            ]
        ]
    },
    {
        "id": "df59ffbf.f7434",
        "type": "function",
        "z": "5b989228.aab31c",
        "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": 270,
        "y": 720,
        "wires": [
            [
                "32507ed4.047f22"
            ]
        ]
    },
    {
        "id": "44c5ab55.d5ec74",
        "type": "delay",
        "z": "5b989228.aab31c",
        "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": 720,
        "y": 600,
        "wires": [
            [
                "32507ed4.047f22"
            ]
        ]
    },
    {
        "id": "d232f693.8bac48",
        "type": "comment",
        "z": "5b989228.aab31c",
        "name": "Trigger once an hour",
        "info": "Trigger once an hour to refresh token",
        "x": 270,
        "y": 140,
        "wires": []
    },
    {
        "id": "c8b29cbc.c261c",
        "type": "comment",
        "z": "5b989228.aab31c",
        "name": "Some needed info about your account",
        "info": "",
        "x": 610,
        "y": 140,
        "wires": []
    },
    {
        "id": "f27862c.409b5a",
        "type": "comment",
        "z": "5b989228.aab31c",
        "name": "Store token in flow variable",
        "info": "",
        "x": 1210,
        "y": 140,
        "wires": []
    },
    {
        "id": "c85e2516.533918",
        "type": "comment",
        "z": "5b989228.aab31c",
        "name": "Functions for setting the MQTT-climate",
        "info": "Functions for setting the MQTT-climate",
        "x": 290,
        "y": 420,
        "wires": []
    },
    {
        "id": "c0c7f403.f646b8",
        "type": "comment",
        "z": "5b989228.aab31c",
        "name": "Functions for setting the mqtt sensors for temp and humidity ",
        "info": "",
        "x": 360,
        "y": 680,
        "wires": []
    },
    {
        "id": "fe4b1d0d.d5113",
        "type": "mqtt-broker",
        "z": "",
        "name": "mqtt",
        "broker": "192.168.x.x",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    }
]
1 Like

It only gives you some info about your Thermostat and does not set anything yet. But it’s a start right?

1 Like

Maybe I remember incorrectly, but one of the basic features of the Nest Protect was together the Nest thermostat in Europe, that it shuts off your heating.

thanks you have saved me $5

@CentralCommand Thanks for redirecting me to the discord, that is a great resource.
I did get some early feedback on the approach and recommendation to go with one platform at a time, which was helpful to scope the initial request.

I’ve started the pull request which prompted the same discussion about updating the existing integration, so we’ll get an answer to that decision soon:

I’ve been working on adding the other traits and supports for set commands in the mean time. Thanks for all the feedback.

13 Likes

Thanks for your work @allenporter ! I’ll donate you another $5 when the project is finished!

2 Likes

Hi there !
Do you know if this New api will be useable with account enrolled in the Field test program ?