Nest Thermostat accesstoken

Bill, by the way, if you have an example I can learn from, it would be great ! Many thanks.

Try this:
GoogleNestThermostat_v2.plugin (3.6 KB)

plugin.Name = "GoogleNestThermostat";
plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 5000;
plugin.DefaultSettings = { "projectID": "", "clientID": "", "clientSecret": "", "refreshToken": "" };

var projectID;
var clientID;
var clientSecret;
var refreshToken;
var http = new HTTPClient();

function onChangeRequest(device, attribute, value) {
    switch (attribute) {
        case "Switch":
            device.Switch = value;
            break;
        default:
            break;
    }
}

function onConnect() {
    projectID = plugin.Settings["projectID"];
    clientID = plugin.Settings["clientID"];
    clientSecret = plugin.Settings["clientSecret"];
    refreshToken = plugin.Settings["refreshToken"];
    accessToken = null;
}

function onDisconnect() {
    console.log("disconnected");
}

function onPoll() {
    for (var deviceID in plugin.Devices) {
        var GNThermostat = plugin.Devices[deviceID];
        if (GNThermostat != null) {
            if (GNThermostat.HasSubscribers()) {
                if (accessToken == null) {
                    accessToken = HttpRefreshToken();
                }
            }

            var getInfo = HttpGet("devices/" + deviceID);
            if (!getInfo.data.error) {
                var displayName = getInfo.data.parentRelations[0].displayName
                var ambientTemperatureCelsius = Math.round(getInfo.data.traits["sdm.devices.traits.Temperature"].ambientTemperatureCelsius * 10) / 10;
                var ambientHumidityPercent = getInfo.data.traits["sdm.devices.traits.Humidity"].ambientHumidityPercent;
                var mode = getInfo.data.traits["sdm.devices.traits.ThermostatMode"].mode;
                var Setpoint = Math.round(getInfo.data.traits["sdm.devices.traits.ThermostatTemperatureSetpoint"].heatCelsius * 10) / 10;

                console.log("displayName = " + displayName + " , ambientTemperatureCelsius = " + ambientTemperatureCelsius + " , ambientHumidityPercent = " + ambientHumidityPercent + " , mode = " + mode + " , Setpoint = " + Setpoint);
            }

            GNThermostat.Temperature = ambientTemperatureCelsius;
            GNThermostat.ThermostatMode = (mode == "HEAT") ? "Heat" : "Off";
            GNThermostat.ThermostatOperatingState = ((ambientTemperatureCelsius - Setpoint) > 0) ? "Idle" : "Heating";
            GNThermostat.HeatingSetpoint = Setpoint;
        }
    }
}

function onSynchronizeDevices() {
    var GNThermostat = new Device();
    GNThermostat.Id = "GoogleNestThermostat";
    GNThermostat.DisplayName = "Google Nest Thermostat";
    GNThermostat.Capabilities = ["Switch", "TemperatureMeasurement", "ThermostatMode", "ThermostatOperatingState", "HeatingSetpoint"];
    GNThermostat.Attributes = [];
    plugin.Devices[GNThermostat.Id] = GNThermostat;
}

function HttpGet(attribute) {
    var httpHeaders = {
        headers: {
            'Authorization': "Bearer " + accessToken
        }
    };
    return http.get("https://smartdevicemanagement.googleapis.com/v1/enterprises/" + projectID + "/" + attribute, httpHeaders);
}

function HttpRefreshToken() {
    //console.log("getting accessToken = " + accessToken);
    var TokenResult = http.post("https://www.googleapis.com/oauth2/v4/token?client_id=" + clientID + "&client_secret=" + clientSecret + "&refresh_token=" + refreshToken + "&grant_type=refresh_token");
    if (!TokenResult.data.error) {
        return TokenResult.data.access_token;
    } else {
        return null;
    }
}
1 Like

Bill, this works like a charm, thanks a lot!, and also many thanks for your super fast reply.
One more thing, I am using the ThermostatTile and ThermostatDetails templates, are there any templates which will work better with the Nest thermostats?
Many many thanks again!

Good to hear. Yes, ThermostatTile & ThermostatDetails are the best templates to use for this device.

Ok perfect. f you want meanwhile we wait for the official integration of the Nest thermostat I can post the plugin for others to use it… My version will not be complete thought.

Yes. Please post it.

Ok.
Sorry for another question, I would like to change the temperature and I need to make the following executeCommand:
https://smartdevicemanagement.googleapis.com/v1/enterprises/{{project-id}}/devices/{{device-id}}:executeCommand
with the following body:
data-raw ‘{
“command” : “sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat”,
“params” : {
“heatCelsius” : 22.0
}
}’
As for the headers they are:
‘Authorization: Bearer XXXX’
‘Content-Type: text/plain’

It works in Postman but I do not know how to write it in HR, this is what I came up:

function HttpPost(deviceID, capability_attribute, attribute, value) {
var httpHeaders = {
headers: {
‘Authorization’: "Bearer " + accessToken,
‘Content-Type’: “text/plain”
}
};
var body = “{command: dm.devices.commands.” + capability_attribute + “, params: {” + attribute + " : " + value + “}}”;

return http.post('https://smartdevicemanagement.googleapis.com/v1/enterprises/' + projectID + '/devices/' + deviceID + ':executeCommand', body, httpHeaders);

}

Sorry for my lack of knowledge but I do not know how to write the call.
Could you please give me a hint whenever you have some time.
Many thanks again!

This is anoher version??

function HttpPost(deviceID, capability_attribute, attribute, value) {
var httpHeaders = {
headers: {
‘Authorization’: "Bearer " + accessToken
}
};
var body = { ‘command’: “sdm.devices.commands.” + capability_attribute, params: { attribute: value } };

return http.post('https://smartdevicemanagement.googleapis.com/v1/enterprises/' + projectID + '/devices/' + deviceID + '/executeCommand', body, httpHeaders);

}

An example of the body is:
{ “command” : “sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat”, “params” : { “heatCelsius” : 22.0 } };

When embedding code in the post, can you please use the 3 back-ticks “```” at the start & end of of your code snippets?

This stuff you are sharing is hard to read.

Sorry, I pasted the code in plain text and it look like that… I am adding the plugin itself so as you could have a better look.
I tried several changes in the syntax but I always get an error…?
In addition I added the “Eco” mode in the “SupportedThermostatModes” but it does not show up in the Deatils combo?, only Heat and Off are shown.
GoogleNestThermostat.plugin (5.4 KB)

There’s a bug in SupportedThermostatModes handling for Plugins. This will be fixed in the 3.20.0.0 release. For the time being, clear the ItemsSource property on that ComboBox & set the Items directly.

This code isn’t doing what you think it is. Object keys are constants in JavaScript. “attribute” is a constant value.

var body = { 'command': "sdm.devices.commands." + capability_attribute, params: { attribute: value } };

You need to create separate functions for your attributes. You should be doing something like this instead. Hardcode the heatCelsius value into your function.

var body = { 'command': "sdm.devices.commands." + capability_attribute, params: { heatCelsius: value } };

No luck, I am not mamaging to make it work… I am attaching the plugin again with some changes, can you please check them out?
GoogleNestThermostat.plugin (6.0 KB)
Thank you very much!

I added the list of modes manually and all of them are shown now, thanks.

Do the commands work in Postman & CURL?

Yes they do! I double check it again… I guess the problem is the syntax in HR?

By the way, the error code is 404 and accordng to API Error Code Reference | Device Access | Google Developers it can be one of these:

  • A command was executed without a valid command name in the request body. Enter a valid command.
  • An invalid or missing device name was specified. Specify a valid device.
  • An invalid or missing Project ID was specified. Specify a valid Project ID.

This is a screen capture from Postman:

That’s not the same URL you are using in the plugin. Notice they have a colon before executeCommand & you have a slash.

Hard code temporary URLs. Test 1 thermostat. Take some of the chance for errors out of this.

You’re gonna be kind of on your own with the rest of this. Look over everything carefully.

I just changed that and it works! It as giving an error before and it might have been the variables…

The principal problem was that I did not know about the fact that Object keys are constants in JavaScript…
Thanks so much Bill I really appreciate your help ;.)

1 Like