-- ********* Nest API communication Module Version 1.0 Created by Erwin van der Zwart ********* -- -- Resident script on 60 seconds, if you call the API faster you can get blocked due API limits -- -- *************************************** SET PARAMETERS ************************************* -- -- !! DO NOT KEEP THIS ON FOR EACH RUN !! Create access token, run once with this parameter on true and set to 'false' when you have the access token. !! DO NOT KEEP THIS ON FOR EACH RUN !! Create_Token = false -- See further down the device settings in your account -- *************************************** END PARAMETERS ************************************* -- -- *************************** MODULE TO RECEIVE AUT TOKEN NEEDED ONCE ************************ -- -- Load modules require('json') require('ssl.https') if Create_Token == true then -- First time actions: -- 1 Create a developer account on https://developer.nest.com, !! use the same email address and password from your normal NEST account on https://home.nest.com/ !! -- 2 Create a product on https://developer.nest.com/products -- 3 Select permissions on device you want to control (read or read/write) and keep the field Redirect URI empty! Because we need PIN based .. -- 4 Fill in the text like: "Allows homeLYnk to control the thermostat" or something like that .. -- 5 Create product and open the details after creation -- 6 On the right are Product ID, Product Secret, Authorization URL and Access Token URL -- 7 Change inside this URL the word CLIENT_ID with the Product ID from previous step: https://home.nest.com/login/oauth2?client_id=CLIENT_ID&state=FOO -- 8 The URL would look something like this: https://home.nest.com/login/oauth2?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&state=FOO -- 9 Past the new URL in the browser, accept the agreement and copy the PIN code from the page (the pincode is valid for 1 time, if you fail next steps you might need to create a new one) --10 Fill in the next parameters: --11 Add your products in your normal NEST account on https://home.nest.com/ Product_ID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' -- Change to your Product ID revieved in step 6 Product_Secret = 'xxxxxxxxxxxxxxxxxxxxxxxxx' -- Change to your Product Secret revieved in step 6 Pincode = 'XXXXXXXX' -- Change to your pincode revieved in step 9 !! Can be used once !! See text step 9 -- Receive Authentication string response = {} res, stat, hdrs = ssl.https.request({ url = "https://api.home.nest.com/oauth2/access_token?client_id=" .. Product_ID .. "&code=" .. Pincode .. "&client_secret=" .. Product_Secret .. "&grant_type=authorization_code", method = 'POST', sink = ltn12.sink.table(response), }) -- Handle response if stat == 400 then log("Error, Pincode, Product_ID or Product_Secret is not valid, try with new pincode and check Id en Secret") elseif stat == 200 then response_table = json.decode(response[1]) Access_Token = response_table.access_token log(Access_Token) else log(stat) end end -- Copy your access token from the log and past it here between the '': Access_Token = 'c.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' -- **************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************* -- -- ************************** SCROLL DOWN TO ENTER THE DEVICE PARAMETERS ********************** -- -- function to update only on change grp.updatechange = function(addr, value, dpt) if not cacheupdate then cacheupdate = {} end if value ~= cacheupdate[ addr ] then cacheupdate[ addr ] = value grp.update(addr, value, dpt) end end -- function write only on change grp.writechange = function(addr, value, dpt) if not cachewrite then cachewrite = {} end if value ~= cachewrite[ addr ] then cachewrite[ addr ] = value grp.write(addr, value, dpt) end end -- function changed object value to Nest when using grp.writechange grp.checkwritechange = function(addr, devtype, id, object) value = grp.getvalue(addr) -- Safety check to avoid wrong value on thermostat if object == 'target_temperature_c' then if value < 9 then value = 9 end if value > 32 then value = 32 end end reply = 150 if cachewrite then if value ~= cachewrite[ addr ] then body_value = {[object] = value} body_value = json.encode(body_value) res = set_device(devtype, id, body_value) if res[1] == body_value then reply = 200 cachewrite[ addr ] = value else reply = 403 end end else reply = 100 end return reply end -- function changed object value to Nest when using grp.updatechange grp.checkupdatechange = function(addr, devtype, id, object) value = grp.getvalue(addr) -- Safety check to avoid wrong value on thermostat if object == 'target_temperature_c' then if value < 9 then value = 9 end if value > 32 then value = 32 end end if object == 'away_temperature_low_c' then if value < 4 then value = 4 end if value > 21 then value = 21 end end reply = 150 if cacheupdate then if value ~= cacheupdate[ addr ] then body_value = {[object] = value} body_value = json.encode(body_value) res = set_device(devtype, id, body_value) if res[1] == body_value then reply = 200 cacheupdate[ addr ] = value else reply = 403 end end else reply = 100 end return reply end -- If URL is not declared then create it (this is needed for keeping redirect URL in next loop) if Nest_Url == nil then Nest_Url = 'https://developer-api.nest.com/devices.json?auth=' .. Access_Token .. '' end -- If URL is not declared then create it (this is needed for keeping redirect URL in next loop) if Nest_Url_Structures == nil then Nest_Url_Structures = 'https://developer-api.nest.com/structures.json?auth=' .. Access_Token .. '' end -- Fuction to get device data from your NEST account function request_devices() local url, res, stat, hdrs -- Make API call on default URL res, stat, hdrs = ssl.https.request(Nest_Url) -- Check if there is a redirect request from NEST server if stat == 307 then -- Replace basic Nest URL with redirect URL received from NEST if Nest_Url ~= hdrs.location then Nest_Url = hdrs.location end -- Redirect and make API call with new URL res = ssl.https.request(Nest_Url) end return json.decode(res) end -- Fuction to get structures data from your NEST account function request_structures() local url, res, stat, hdrs -- Make API call on default URL res, stat, hdrs = ssl.https.request(Nest_Url_Structures) -- Check if there is a redirect request from NEST server if stat == 307 then -- Replace basic Nest URL with redirect URL received from NEST if Nest_Url_Structures ~= hdrs.location then Nest_Url_Structures = hdrs.location end -- Redirect and make API call with new URL res = ssl.https.request(Nest_Url_Structures) end return json.decode(res) end --log(Nest_Url) --log(Nest_Url_Structures) -- Fuction to set device data to your NEST account function set_device(device, device_id, body_value) Nest_Device_Url = "https://developer-api.nest.com/devices/" .. device .. "/" .. device_id .. "?auth=" .. Access_Token response = {} res, stat, hdrs = ssl.https.request({ url = Nest_Device_Url, method = 'PUT', sink = ltn12.sink.table(response), headers = { ['content-length'] = #body_value, ['content-type'] = 'application/json', }, source = ltn12.source.string(body_value), }) if stat == 307 then -- Replace basic Nest device URL with redirect URL received from NEST if Nest_Device_Url ~= hdrs.location then Nest_Device_Url = hdrs.location end res, stat, hdrs = ssl.https.request({ url = Nest_Device_Url, method = 'PUT', sink = ltn12.sink.table(response), headers = { ['content-length'] = #body_value, ['content-type'] = 'application/json', }, source = ltn12.source.string(body_value), }) end return response end -- Get device data from your NEST account res, devices_data = pcall(request_devices) if devices_data ~= nil then log(devices_data) for index, devicesdata in pairs(devices_data) do if devicesdata == 'blocked' then message = 'Communication with nest is blocked (devices), probably because there are to many API calls, please lower your script interval speed to 60 seconds' log(message) --grp.update('8/0/0', message) else message = 'Communication with nest is OK' log(message) --grp.update('8/0/0', message) end if devicesdata ~= nil then for index1, devicessubdata in pairs(devicesdata) do -- **************************** DON'T CHANGE ANYTHING ABOVE THIS LINE ************************* -- -- **************************** ENTER HERE THE NEST DEVICE PARAMETERS ************************* -- --[[ DEVICE 1 -- Setpoints Kantoor -- *************************************************************** -- if devicessubdata.name == "Office (Kantoor)" then -- Make a read / write link to nest object target_temperature_c with update command to HL target_address = '8/0/1' res = grp.checkupdatechange(target_address, 'thermostats', devicessubdata.device_id, 'target_temperature_c') if res ~= 200 then grp.updatechange(target_address, devicessubdata.target_temperature_c, dt.float16) end -- Make a read only link to nest object devicessubdata.humidity with write command to HL grp.writechange('8/0/2', devicessubdata.ambient_temperature_c, dt.float16) -- Make a read only link to nest object devicessubdata.humidity with update command to HL grp.updatechange('8/0/3', devicessubdata.humidity, dt.scale) -- Make a read / write link to nest object away_temperature_low_c with write command to HL target_address = '8/0/4' res = grp.checkwritechange(target_address, 'thermostats', devicessubdata.device_id, 'away_temperature_low_c') if res ~= 200 then grp.writechange(target_address, devicessubdata.away_temperature_low_c, dt.float16) end -- Make a read only link to nest object devicessubdata.hvac_state with update command to HL grp.updatechange('8/0/5', devicessubdata.hvac_state, dt.string) end -- End Setpoints Kantoor -- *********************************************************** -- -- DEVICE 2 -- Setpoints Livingroom -- *************************************************************** -- if devicessubdata.name == "Livingroom" then -- Make a read / write link to nest object target_temperature_c with update command to HL target_address = '8/0/11' res = grp.checkupdatechange(target_address, 'thermostats', devicessubdata.device_id, 'target_temperature_c') if res ~= 200 then grp.updatechange(target_address, devicessubdata.target_temperature_c, dt.float16) end -- Make a read only link to nest object devicessubdata.humidity with write command to HL grp.writechange('8/0/12', devicessubdata.ambient_temperature_c, dt.float16) -- Make a read only link to nest object devicessubdata.humidity with update command to HL grp.updatechange('8/0/13', devicessubdata.humidity, dt.scale) -- Make a read / write link to nest object away_temperature_low_c with write command to HL target_address = '8/0/14' res = grp.checkwritechange(target_address, 'thermostats', devicessubdata.device_id, 'away_temperature_low_c') if res ~= 200 then grp.writechange(target_address, devicessubdata.away_temperature_low_c, dt.float16) end -- Make a read only link to nest object devicessubdata.hvac_state with update command to HL grp.updatechange('8/0/15', devicessubdata.hvac_state, dt.string) end -- End Setpoints Kantoor -- *********************************************************** -- -- DEVICE 3 -- copy rules from above or create new ones, you can re-use the functions as above but check the function itself to be sure -- you don't write values that Nest can't handle ]] -- **************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************* -- end end end end -- Get structures data from your NEST account res, structures_data = pcall(request_structures) --log(structures_data) if structures_data ~= nil then --log(structures_data) for index, structuredata in pairs(structures_data) do if structuredata == 'blocked' then message = 'Communication with nest is blocked (structures), probably because there are to many API calls, please lower your script interval speed to 60 seconds' log(message) --grp.update('8/0/0', message) end if structuredata ~= nil then if structuredata.name == 'Woonkamer' then -- Make a read only link to nest object devicessubdata.hvac_state with update command to HL --grp.updatechange('8/0/6', structuredata.away, dt.string) log(structuredata.away, dt.string) end for index1, structuresubdata in pairs(structuredata) do -- **************************** DON'T CHANGE ANYTHING ABOVE THIS LINE ************************* -- -- **************************** ENTER HERE THE NEST DEVICE PARAMETERS ************************* -- --log(structuresubdata) -- **************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************* -- end end end end