/** * Tado Connect * * Copyright 2016 Stuart Buchanan * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * 05/10/2025 v3.3 Updates to reduce api calls to Tado, added option to disable presence polling. * 13/04/2025 v3.2 Updated token refresh scheduling, added faster polling option. * 08/04/2025 v3.1 Added HeatingPower, adjusted heat logic to report idle state when set temp reached. * 22/11/2023 v3.0 Adjusted to Password Grant flow * 22/11/2023 v2.9 Adjusted to OAuth 2 authentication flow * 27/04/2018 v2.8 fixed issue with null values when trying to retrieve current setpoint when null as the try and catch arent working as expected. * 27/04/2018 v2.7 Modified for Hubitat * 07/02/2018 v2.7 Added some new try catch blocks around parse capability as there were exceptioons after v2.1 occuring for air conditioners, this now works correctly * 06/02/2018 v2.6 Fixed Commands for those with Heat Cool that do not support Fan Modes * 08/06/2017 v2.5 Amended bug where Hot water type was set to WATER, Instead or HOT_WATER, with thanks to @invisiblemountain * 08/06/2017 v2.4 Added Device name to DNI, trying to avaid issue with multiple devices in a single Zone * 26/05/2017 v2.3 removed erronous jsonbody statements in the coolCommand Function. * 26/05/2017 v2.2 Corrected bug with parseCapability function as this was returning the map instead of the value, this would account for lots of strange behaviour. * 25/05/2017 v2.1 Added support for Air Condiioners which have a mandatory swing field for all Commands, corrected prevois bugs in v2.0, thanks again to @Jnick * 20/05/2017 v2.0 Added support for Air Condiioners which have a mandatory swing field in the heating & cool Commands, thanks again to @Jnick * 17/05/2017 v1.9 Corrected issue with the wrong temp unit being used on some thermostat functions when using Farenheit, many thanks again to @Jnick for getting the logs to help diagnose this. * 04/05/2017 v1.8 Corrected issue with scheduling which was introduced in v1.7 with merge of pull request. Many thanks to @Jnick for getting the logs to help diagnose this. * 17/04/2017 v1.7 General Bugfixes around Tado user presence with thanks to @sipuncher * 14/04/2017 v1.6 fixed defects in user presence device polling * 06/04/2017 v1.5 scheduled refresh of tado user status every minute (Thanks to @sipuncher for pointing out my mistake) * 03/04/2017 v1.4 Added ability to have your Tado Users created as Smarthings Virtual Presence Sensors for use in routines etc.. * 03/01/2017 v1.3 Corrected Cooling Commands and Set Points issue with incorrect DNI statement with thanks to Richard Gregg * 03/12/2016 v1.2 Corrected Values for Heating and Hot Water set Points * 03/12/2016 v1.1 Updated to Support Multiple Hubs, and fixed bug in device discovery and creation, however all device types need updated also. * 26/11/2016 V1.0 initial release */ import java.text.DecimalFormat import groovy.json.JsonSlurper import groovy.json.JsonOutput private apiUrl() { "https://my.tado.com" } private getApiUrl() { "https://my.tado.com" } private getTadoLoginUri() { "https://login.tado.com" } private getDeviceCodePath() { "/oauth2/device_authorize" } private getDeviceTokenPath() { "/oauth2/token" } private getAccessTokenUri() { "https://login.tado.com" } private getAccessTokenPath() { "/oauth2/token" } private getVendorName() { "Tado" } private getVendorIcon() { "https://dl.dropboxusercontent.com/s/fvjrqcy5xjxsr31/tado_128.png" } private getVendorAuthPath() { "/oauth2/authorize" } private getVendorTokenPath(){ "https://auth.tado.com/oauth/token" } private getClientId() { settings.clientId } private getClientSecret() { settings.clientSecret } private getRefreshToken() { state.refreshToken } private getAccessToken() { state.accessToken } private getDebugLogStatus() { settings.debugLog } //Use: true or false private getDefaultLogForceSecs() { 300 } private getCallbackUrl() { getServerUrl()+ "/oauth/callback?access_token=${state.accessToken}" } private getBuildRedirectUrl() { getServerUrl() + "/oauth/initialize?access_token=${state.accessToken}" } private getServerUrl() { return getFullApiServerUrl() } private getTokenExpOffset() { 420000 } //Miliseconds offset used from expiriry to refresh of token private fastPoll() { settings.fastPoll } private presencePoll() { settings.presencePoll } // Automatically generated. Make future change here. definition( name: "Tado (Connect)", namespace: "fuzzysb", author: "Stuart Buchanan", description: "Tado Integration, This SmartApp supports all Tado Products. (Heating Thermostats, Extension Kits, AC Cooling & Radiator Valves.)", category: "Heating", iconUrl: "https://dl.dropboxusercontent.com/s/fvjrqcy5xjxsr31/tado_128.png", iconX2Url: "https://dl.dropboxusercontent.com/s/jyad58wb28ibx2f/tado_256.png", oauth: true, singleInstance: false ) { appSetting "clientId" appSetting "clientSecret" appSetting "serverUrl" } preferences { page(name: "startPage", title: "Tado Integration", content: "startPage", install: false) page(name: "Credentials", title: "Enter authentication details", content: "credentialsPage", install: false ) page(name: "authPage", title: "Tado Authentication Verification", content: "authPage", install: false) page(name: "getAccessCodePage", title: "Finalize Tado Authentication Verification", content: "getAccessCodePage", install: false) page(name: "mainPage", title: "Tado Integration (API)", content: "mainPage") page(name: "completePage", title: "${getVendorName()} is now connected to Hubitat!", content: "completePage") page(name: "listDevices", title: "Tado Devices", content: "listDevices", install: false) page(name: "listUsers", title: "Tado Users", content: "listUsers", install: false) page(name: "advancedOptions", title: "Tado Advanced Options", content: "advancedOptions", install: false) page(name: "badCredentials", title: "Invalid Credentials", content: "badAuthPage", install: false) } mappings { path("/receivedHomeId"){action: [POST: "receivedHomeId", GET: "receivedHomeId"]} } def startPage() { log.info "In startPage" if ( !getRefreshToken() ) { return credentialsPage() } else if (state.homeId ) { return mainPage() } else { return credentialsPage() } } def credentialsPage() { log.info "In credentialsPage" //Always force debug log in the authorization flow openForcedDebugLog(getDefaultLogForceSecs()) def description def uninstallAllowed = false def clientIdProvided = false logDebug("Showing the login page" ) return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage:authPage, uninstall: false , install:false) { section("Enter Application Details...") { paragraph "Field is filled with default Client ID provided by Tado. Please update if needed, or proceed with the" paragraph "default Client ID : 1bb50063-6b0c-4d11-bd99-387f4a91cc46" paragraph "Also check https://support.tado.com/en/articles/8565472-how-do-i-authenticate-to-access-the-rest-api for further details" input(name: 'clientId', title: 'Client ID', type: 'text', required: true, defaultValue: '1bb50063-6b0c-4d11-bd99-387f4a91cc46') } } } def authPage( ) { log.info "In authPage" //Always force debug log in the authorization flow openForcedDebugLog(getDefaultLogForceSecs()) //Initiate flow and get user code for verification initiateDeviceCodeFlow() return dynamicPage(name: "Authorize", title: "Authorize with Tado", nextPage:getAccessCodePage, uninstall: false , install:false) { section("Follow the steps below, to authorize with Tado:") { paragraph "1) Please visit the URL below to authorize your device with your Tado Account:" paragraph " " + " " + state.deviceCodeVerificationUriComplete paragraph " " + " " + "User code should be set automatically. If prompted for a device/user code, please enter:" + " " + state.userCode paragraph "2) Once authorized with Tado, please click next" paragraph " " paragraph "Please authenticate within" + " " + state.deviceCodeExpiresIn + " " + "seconds or restart the process." } } } def getAccessCodePage( ) { logDebug("In getAccessCodePage" ) def deviceAccessTokenRetreived = getDeviceAccessToken() if (deviceAccessTokenRetreived) { return mainPage() } else { return badAuthPage() } } def mainPage() { logDebug("In mainPage" ) // Try to get access Token if not available if (isAccessTokenExpired()){ logDebug("Access token expired - calling token refresh" ) refreshToken() } // Call up credentials flow if access token is still no available (to allow new authentication) if (isAccessTokenExpired()){ logDebug("Access token expired - calling credentialsPage" ) return credentialsPage() } getidCommand() getTempUnitCommand() logDebug("Logging debug: ${state.homeId}" ) if (state.homeId) { return completePage() } else { return badAuthPage() } } def completePage(){ def description = " " return dynamicPage(name: "completePage", title: "Credentials Accepted! - ${getVendorName()} is now connected to Hubitat!", uninstall:true, install: true, submitOnChange: true) { // section { href url: buildRedirectUrl("receivedHomeId"), style:"embedded", required:false, title:"${getVendorName()} is now connected to Hubitat!", description:description } section('SetUp Options') { href 'listDevices', title: 'Child Devices Setup' } section('Authentication') { href 'Credentials', title: 'Reset Authentication' } section("App preferences") { input name: "debugLog", type: "bool", title: "Enable Debug Logging?", defaultValue: false, required: true, description: "testing" input name: "fastPoll", type: "bool", title: "Enable Faster Polling? (Warning - can exceed daily api limits added by Tado)", defaultValue: false, required: true input name: "presencePoll", type: "bool", title: "Enable Presence Polling? (Increases api calls to Tado)", defaultValue: true, required: true } } } def badAuthPage(){ logDebug("In badAuthPage" ) return dynamicPage(name: "badCredentials", title: "Bad Tado Credentials", install:false, uninstall:true, nextPage: Credentials) { section("") { paragraph "Authentication failed. Please repeat authentication process." } } } def advancedOptions() { logDebug("In Advanced Options" ) def options = getDeviceList() dynamicPage(name: "advancedOptions", title: "Select Advanced Options", install:true) { section("Default temperatures for thermostat actions. Please enter desired temperatures") { input("defHeatingTemp", "number", title: "Default Heating Temperature?", required: true) input("defCoolingTemp", "number", title: "Default Cooling Temperature?", required: true) } section("Tado Override Method") { input("manualmode", "enum", title: "Default Tado Manual Overide Method", options: ["TADO_MODE","MANUAL"], required: true) } section(){ if (getHubID() == null){ input( name : "myHub" ,type : "hub" ,title : "Select your hub" ,multiple : false ,required : true ,submitOnChange : true ) } else { paragraph("Tap done to finish the initial installation.") } } section("App preferences") { input name: "debugLog", type: "bool", title: "Enable debug logging?", defaultValue: false, required: true input name: "fastPoll", type: "bool", title: "Enable Faster Polling? (Warning - can exceed daily api limits added by Tado)", defaultValue: false, required: true input name: "presencePoll", type: "bool", title: "Enable Presence Polling? (Increases api calls to Tado)", defaultValue: true, required: true } } } def listDevices() { logDebug("In listDevices" ) def options = getDeviceList() dynamicPage(name: "listDevices", title: "Choose devices", install:false, uninstall:true, nextPage: listUsers) { section("Devices") { input "devices", "enum", title: "Select Device(s)", required: false, multiple: true, options: options, submitOnChange: true } } } def listUsers() { logDebug("In listUsers" ) def options = getUserList() dynamicPage(name: "listUsers", title: "Choose users you wish to create a Virtual Smarthings Presence Sensors for", install:false, uninstall:true, nextPage: advancedOptions) { section("Users") { input "users", "enum", title: "Select User(s)", required: false, multiple: true, options: options, submitOnChange: true } } } //def getToken(){ // if (!state.accessToken) { // try { // getAccessToken() // DEBUG("Creating new Access Token: $state.accessToken") // } catch (ex) { // DEBUG("Did you forget to enable OAuth in SmartApp IDE settings") // DEBUG(ex) // } // } //} def receivedHomeId() { logDebug("In receivedToken" ) def html = """ ${getVendorName()} Connection
Vendor icon connected device icon Hubitat logo

Tap 'Done' to continue to Devices.

""" render contentType: 'text/html', data: html } def buildRedirectUrl(endPoint) { logDebug("In buildRedirectUrl" ) logDebug("returning: " + getServerUrl() + "/${hubUID}/apps/${app.id}/${endPoint}?access_token=${state.accessToken}") return getServerUrl() + "/${hubUID}/apps/${app.id}/${endPoint}?access_token=${state.accessToken}" } def getDeviceList() { def TadoDevices = getZonesCommand() logDebug ("In getDeviceList") return TadoDevices.sort() } def getUserList() { def TadoUsers = getMobileDevicesCommand() if (TadoUsers != null) { return TadoUsers.sort() } } def installed() { logDebug("Installed with settings: ${settings}" ) initialize() } def updated() { logDebug("Updated with settings: ${settings}" ) unsubscribe() unschedule() initialize() } def uninstalled() { logDebug("Uninstalling Tado (Connect)" ) revokeAccessToken() removeChildDevices(getChildDevices()) log.debug "Tado (Connect) Uninstalled" } def initialize() { logDebug("Initialized with settings: ${settings}" ) // Pull the latest device info into state // getDeviceList(); def children = getChildDevices() if(settings.devices) { settings.devices.each { device -> logDebug("Devices Inspected ${device.inspect()}" ) def item = device.tokenize('|') def deviceType = item[0] def deviceId = item[1] def deviceName = item[2] def existingDevices = children.find{ d -> d.deviceNetworkId.contains(deviceId + "|" + deviceType) } logDebug("existingDevices Inspected ${existingDevices.inspect()}" ) if(!existingDevices) { logDebug("Some Devices were not found....creating Child Device ${deviceName}" ) try { if (deviceType == "HOT_WATER") { logDebug("Creating Hot Water Device ${deviceName}" ) createChildDevice("Tado Hot Water Control", deviceId + "|" + deviceType + "|" + now() + "|" + devicename, "${deviceName}", deviceName) } if (deviceType == "HEATING") { logDebug("Creating Heating Device ${deviceName}" ) createChildDevice("Tado Heating Thermostat", deviceId + "|" + deviceType + "|" + now() + "|" + devicename, "${deviceName}", deviceName) } if (deviceType == "AIR_CONDITIONING") { logDebug("Creating Air Conditioning Device ${deviceName}") createChildDevice("Tado Cooling Thermostat", deviceId + "|" + deviceType + "|" + state.accessToken + "|" + devicename, "${deviceName}", deviceName) } if (deviceType == "Home") { log.info "Creating Home Device ${deviceName}" createChildDevice("Tado Home", deviceId + "|" + deviceType + "|" + state.accessToken + "|" + devicename, "${deviceName}", deviceName) } } catch (Exception e) { log.error "Error creating device: ${e}" } } } } getUserList(); if(settings.users) { settings.users.each { user -> logDebug("Devices Inspected ${user.inspect()}" ) def item = user.tokenize('|') def userId = item[0] def userName = item[1] def existingUsers = children.find{ d -> d.deviceNetworkId.contains(userId + "|" + userName) } logDebug("existingUsers Inspected ${existingUsers.inspect()}" ) if(!existingUsers) { logDebug("Some Users were not found....creating Child presence Device ${userName}" ) try { createChildDevice("Tado User Presence", userId + "|" + userName + "|" + now(), "${userName}", userName) } catch (Exception e) { log.error "Error creating device: ${e}" } } } } // Do the initial poll getInititialDeviceInfo() unschedule() // Run token refresh runEvery5Minutes("tokenPoll") // Get Weather Refresh getWeather() runEvery5Minutes("getWeather") // Enable selective polling for faster updates. if (fastPoll()){ // Schedule it to run every minute log.info "** Fast Polling Enabled, Polling every minute **" runEvery1Minute("poll") } else { // Schedule it to run every 5 minutes log.info "Normal Polling, Polling every 5 minutes" runEvery5Minutes("poll") } if (presencePoll()){ runIn(30, "schedUserPoll") // delay schedule, spread the load } else { log.info "Presence Polling Disabled, User states will not be updated" } } def schedUserPoll(){ runEvery1Minute("userPoll") } def getInititialDeviceInfo(){ logDebug("getInititialDeviceInfo" ) // getDeviceList(); def children = getChildDevices() if(settings.devices) { settings.devices.each { device -> logDebug("Devices Inspected ${device.inspect()}" ) def item = device.tokenize('|') def deviceType = item[0] def deviceId = item[1] def deviceName = item[2] def existingDevices = children.find{ d -> d.deviceNetworkId.contains(deviceId + "|" + deviceType) } if(existingDevices) { existingDevices.getInitialDeviceinfo() } } } } def getHubID(){ def hubID if (myHub){ hubID = myHub.id } else { logDebug("hub type is ${location.hubs[0].type}" ) def hubs = location.hubs.findAll{ it.type == "PHYSICAL" } if (hubs.size() == 1){ hubID = hubs[0].id } } logDebug("Returning Hub ID: ${hubID}" ) return hubID } def tokenPoll() { // Scheduled token checking logDebug("Scheduled Token Check" ) if(refreshAccessToken()) { refreshToken(); } } def poll() { log.info "In Child Devices Poll" // getDeviceList(); def children = getChildDevices() if(settings.devices) { settings.devices.each { device -> logDebug("Devices Inspected ${device.inspect()}" ) def item = device.tokenize('|') def deviceType = item[0] def deviceId = item[1] def deviceName = item[2] def existingDevices = children.find{ d -> d.deviceNetworkId.contains(deviceId + "|" + deviceType) } if(existingDevices) { existingDevices.poll() } } } } def userPoll() { log.info "In Presence Poll" def children = getChildDevices(); if(settings.users) { settings.users.each { user -> logDebug("Devices Inspected ${user.inspect()}" ) def item = user.tokenize('|') def userId = item[0] def userName = item[1] def existingUsers = children.find{ d -> d.deviceNetworkId.contains(userId + "|" + userName) } logDebug("existingUsers Inspected ${existingUsers.inspect()}" ) if(existingUsers) { existingUsers.poll() } } } } def homePoll() { log.info "In HomePoll" def children = getChildDevices(); if(settings.home) { } } def createChildDevice(deviceFile, dni, name, label) { logDebug("In createChildDevice" ) try{ def childDevice = addChildDevice("fuzzysb", deviceFile, dni, getHubID(), [name: name, label: label, completedSetup: true]) } catch (e) { log.error "Error creating device: ${e}" } } private sendCommand(method,childDevice,args = []) { def methods = [ 'getid': [ uri: apiUrl(), path: "/api/v2/me", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'gettempunit': [ uri: apiUrl(), path: "/api/v2/homes/${state.homeId}", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'getzones': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/zones", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'getMobileDevices': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/mobileDevices", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'getcapabilities': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/zones/" + args[0] + "/capabilities", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'status': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/zones/" + args[0] + "/state", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'userStatus': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/mobileDevices", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'temperature': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/zones/" + args[0] + "/overlay", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ], body: args[1] ], 'weatherStatus': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/weather", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ], 'deleteEntry': [ uri: apiUrl(), path: "/api/v2/homes/" + state.homeId + "/zones/" + args[0] + "/overlay", requestContentType: "application/json", headers: ["Authorization": "Bearer " + state.accessToken ] ] ] def request = methods.getAt(method) logDebug("Http Params ("+request+")" ) try{ logDebug("Executing 'sendCommand' for method" + " " + method ) if (method == "getid"){ httpGet(request) { resp -> parseMeResponse(resp) } }else if (method == "gettempunit"){ httpGet(request) { resp -> parseTempResponse(resp) } }else if (method == "getzones"){ httpGet(request) { resp -> parseZonesResponse(resp) } }else if (method == "getMobileDevices"){ httpGet(request) { resp -> parseMobileDevicesResponse(resp) } }else if (method == "getcapabilities"){ httpGet(request) { resp -> parseCapabilitiesResponse(resp,childDevice) } }else if (method == "status"){ httpGet(request) { resp -> parseResponse(resp,childDevice) } }else if (method == "userStatus"){ httpGet(request) { resp -> parseUserResponse(resp,childDevice) } }else if (method == "temperature"){ httpPut(request) { resp -> parseputResponse(resp,childDevice) } }else if (method == "weatherStatus"){ logDebug("calling weatherStatus Method" ) httpGet(request) { resp -> parseweatherResponse(resp) } }else if (method == "deleteEntry"){ httpDelete(request) { resp -> parsedeleteResponse(resp,childDevice) } }else{ httpGet(request) } } catch(Exception e){ log.error("Exception: " + e) } } // Parse incoming device messages to generate events private parseMeResponse(resp) { logDebug("Executing parseMeResponse: "+resp.data) logDebug("Output status: "+resp.status) if(resp.status == 200) { logDebug("Executing parseMeResponse.successTrue") state.homeId = resp.data.homes[0].id logDebug("Got HomeID Value: " + state.homeId) }else if(resp.status == 201){ logDebug("Something was created/updated") } } private parseputResponse(resp,childDevice) { logDebug("Executing parseputResponse: "+resp.data) logDebug("Output status: "+resp.status) } private parsedeleteResponse(resp,childDevice) { logDebug("Executing parsedeleteResponse: "+resp.data) logDebug("Output status: "+resp.status) } private parseUserResponse(resp,childDevice) { def item = (childDevice.device.deviceNetworkId).tokenize('|') def userId = item[0] def userName = item[1] logDebug("Executing parseUserResponse: "+resp.data) logDebug("Output status: "+resp.status) if(resp.status == 200) { def restUsers = resp.data logDebug("Executing parseUserResponse.successTrue") logDebug("UserId is ${userId} and userName is ${userName}") for (TadoUser in restUsers) { logDebug("TadoUserId is ${TadoUser.id}") if ((TadoUser.id).toString() == (userId).toString()) { logDebug("Entering presence Assesment for User Id: ${userId}") if (TadoUser.settings.geoTrackingEnabled == true) { logDebug("GeoTracking is Enabled for User Id: ${userId}") if (TadoUser.location.atHome == true) { logDebug("Send presence Home Event Fired") childDevice?.sendEvent(name:"presence",value: "present") } else if (TadoUser.location.atHome == false) { logDebug("Send presence Away Event Fired") childDevice?.sendEvent(name:"presence",value: "not present") } } } } } else if(resp.status == 201){ logDebug("Something was created/updated") } } private parseResponse(resp,childDevice) { def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] if (deviceType == "AIR_CONDITIONING") { logDebug("Executing parseResponse: "+resp.data) logDebug("Output status: "+resp.status) def temperatureUnit = state.tempunit logDebug("Temperature Unit is ${temperatureUnit}") def humidityUnit = "%" def ACMode def ACFanSpeed def ACFanMode = "off" def thermostatSetpoint def tOperatingState if(resp.status == 200) { logDebug("Executing parseResponse.successTrue") def temperature if (temperatureUnit == "C") { temperature = (Math.round(resp.data.sensorDataPoints.insideTemperature.celsius *10 ) / 10) } else if(temperatureUnit == "F"){ temperature = (Math.round(resp.data.sensorDataPoints.insideTemperature.fahrenheit * 10) / 10) } logDebug("Read temperature: " + temperature) childDevice?.sendEvent(name:"temperature",value:temperature,unit:temperatureUnit) logDebug("Send Temperature Event Fired") def autoOperation = "OFF" if(resp.data.overlayType == null){ autoOperation = resp.data.tadoMode } else if(resp.data.overlayType == "NO_FREEZE"){ autoOperation = "OFF" }else if(resp.data.overlayType == "MANUAL"){ autoOperation = "MANUAL" } logDebug("Read tadoMode: " + autoOperation) childDevice?.sendEvent(name:"tadoMode",value:autoOperation) logDebug("Send thermostatMode Event Fired") def humidity if (resp.data.sensorDataPoints.humidity.percentage != null){ humidity = resp.data.sensorDataPoints.humidity.percentage }else{ humidity = "--" } logDebug("Read humidity: " + humidity) childDevice?.sendEvent(name:"humidity",value:humidity,unit:humidityUnit) if (resp.data.setting.power == "OFF"){ tOperatingState = "idle" ACMode = "off" ACFanMode = "off" logDebug("Read thermostatMode: " + ACMode) ACFanSpeed = "OFF" logDebug("Read tadoFanSpeed: " + ACFanSpeed) thermostatSetpoint = "--" logDebug("Read thermostatSetpoint: " + thermostatSetpoint) } else if (resp.data.setting.power == "ON"){ ACMode = (resp.data.setting.mode).toLowerCase() logDebug("thermostatMode: " + ACMode) ACFanSpeed = resp.data.setting.fanSpeed if (ACFanSpeed == null) { ACFanSpeed = "--" } if (resp.data.overlay != null){ if (resp.data.overlay.termination.type == "TIMER"){ if (resp.data.overlay.termination.durationInSeconds == "3600"){ ACMode = "emergency heat" logDebug("thermostatMode is heat, however duration shows the state is: " + ACMode) } } } switch (ACMode) { case "off": tOperatingState = "idle" break case "heat": tOperatingState = "heating" break case "emergency heat": tOperatingState = "heating" break case "cool": tOperatingState = "cooling" break case "dry": tOperatingState = "drying" break case "fan": tOperatingState = "fan only" break case "auto": tOperatingState = "heating|cooling" break } logDebug("Read thermostatOperatingState: " + tOperatingState) logDebug("Read tadoFanSpeed: " + ACFanSpeed) if (ACMode == "dry" || ACMode == "auto" || ACMode == "fan"){ thermostatSetpoint = "--" }else if(ACMode == "fan") { ACFanMode = "auto" }else{ if (temperatureUnit == "C") { thermostatSetpoint = Math.round(resp.data.setting.temperature.celsius) } else if(temperatureUnit == "F"){ thermostatSetpoint = Math.round(resp.data.setting.temperature.fahrenheit) } } logDebug("Read thermostatSetpoint: " + thermostatSetpoint) } }else{ logDebug("Executing parseResponse.successFalse") } childDevice?.sendEvent(name:"thermostatOperatingState",value:tOperatingState) logDebug("Send thermostatOperatingState Event Fired") childDevice?.sendEvent(name:"tadoFanSpeed",value:ACFanSpeed) logDebug("Send tadoFanSpeed Event Fired") childDevice?.sendEvent(name:"thermostatFanMode",value:ACFanMode) logDebug("Send thermostatFanMode Event Fired") childDevice?.sendEvent(name:"thermostatMode",value:ACMode) logDebug("Send thermostatMode Event Fired") childDevice?.sendEvent(name:"thermostatSetpoint",value:thermostatSetpoint,unit:temperatureUnit) logDebug("Send thermostatSetpoint Event Fired") childDevice?.sendEvent(name:"heatingSetpoint",value:thermostatSetpoint,unit:temperatureUnit) logDebug("Send heatingSetpoint Event Fired") childDevice?.sendEvent(name:"coolingSetpoint",value:thermostatSetpoint,unit:temperatureUnit) logDebug("Send coolingSetpoint Event Fired") } if (deviceType == "HEATING") { logDebug("Executing parseResponse: "+resp.data) logDebug("Output status: "+resp.status) def temperatureUnit = state.tempunit logDebug("Temperature Unit is ${temperatureUnit}") def humidityUnit = "%" //def ACMode //def ACFanSpeed def thermostatSetpoint def tOperatingState if(resp.status == 200) { logDebug("Executing parseResponse.successTrue") def temperature if (temperatureUnit == "C") { temperature = (Math.round(resp.data.sensorDataPoints.insideTemperature.celsius * 10 ) / 10) } else if(temperatureUnit == "F"){ temperature = (Math.round(resp.data.sensorDataPoints.insideTemperature.fahrenheit * 10) / 10) } logDebug("Read temperature: " + temperature) childDevice?.sendEvent(name: 'temperature', value: temperature, unit: temperatureUnit) logDebug("Send Temperature Event Fired") def autoOperation = "OFF" if(resp.data.overlayType == null){ autoOperation = resp.data.tadoMode } else if(resp.data.overlayType == "NO_FREEZE"){ autoOperation = "OFF" }else if(resp.data.overlayType == "MANUAL"){ autoOperation = "MANUAL" } logDebug("Read tadoMode: " + autoOperation) childDevice?.sendEvent(name: 'tadoMode', value: autoOperation) if (resp.data.setting.power == "ON"){ if (temperatureUnit == "C") { thermostatSetpoint = resp.data.setting.temperature.celsius } else if(temperatureUnit == "F"){ thermostatSetpoint = resp.data.setting.temperature.fahrenheit } logDebug("Read thermostatSetpoint: " + thermostatSetpoint) childDevice?.sendEvent(name: 'thermostatMode', value: "heat") if (temperature < thermostatSetpoint) { logDebug("Heat mode; setpoint not reached") childDevice?.sendEvent(name: 'thermostatOperatingState', value: "heating") } else { logDebug("Heat mode; setpoint reached") childDevice?.sendEvent(name: 'thermostatOperatingState', value: "idle") } logDebug("Send thermostatMode Event Fired") } else if(resp.data.setting.power == "OFF"){ thermostatSetpoint = "0" childDevice?.sendEvent(name: 'thermostatMode', value: "off") childDevice?.sendEvent(name: 'thermostatOperatingState', value: "idle") logDebug("Send thermostatMode Event Fired") } def humidity if (resp.data.sensorDataPoints.humidity.percentage != null){ humidity = resp.data.sensorDataPoints.humidity.percentage }else{ humidity = "--" } logDebug("Read humidity: " + humidity) childDevice?.sendEvent(name: 'humidity', value: humidity,unit: humidityUnit) def heatingPower heatingPower = resp.data.activityDataPoints.heatingPower.percentage childDevice?.sendEvent(name: 'heatingPower', value: heatingPower) def openWindow if (resp.data.openwindow != null){ openWindow = true } else { openWindow = false } childDevice?.sendEvent(name: 'openWindow', value: openWindow) } else{ logDebug("Executing parseResponse.successFalse") } childDevice?.sendEvent(name: 'thermostatSetpoint', value: thermostatSetpoint, unit: temperatureUnit) logDebug("Send thermostatSetpoint Event Fired") childDevice?.sendEvent(name: 'heatingSetpoint', value: thermostatSetpoint, unit: temperatureUnit) logDebug("Send heatingSetpoint Event Fired") } if (deviceType == "HOT_WATER") { logDebug("Executing parseResponse: "+resp.data) logDebug("Output status: "+resp.status) def temperatureUnit = state.tempunit logDebug("Temperature Unit is ${temperatureUnit}") def humidityUnit = "%" def ACMode def ACFanSpeed def thermostatSetpoint def tOperatingState if(resp.status == 200) { logDebug("Executing parseResponse.successTrue") def temperature if (state.supportsWaterTempControl == "true" && resp.data.tadoMode != null && resp.data.setting.power != "OFF"){ if (temperatureUnit == "C") { temperature = (Math.round(resp.data.setting.temperature.celsius * 10 ) / 10) } else if(temperatureUnit == "F"){ temperature = (Math.round(resp.data.setting.temperature.fahrenheit * 10) / 10) } logDebug("Read temperature: " + temperature) childDevice?.sendEvent(name: 'temperature', value: temperature, unit: temperatureUnit) logDebug("Send Temperature Event Fired") } else { childDevice?.sendEvent(name: 'temperature', value: "--", unit: temperatureUnit) logDebug("Send Temperature Event Fired") } def autoOperation = "OFF" if(resp.data.overlayType == null){ autoOperation = resp.data.tadoMode } else if(resp.data.overlayType == "NO_FREEZE"){ autoOperation = "OFF" }else if(resp.data.overlayType == "MANUAL"){ autoOperation = "MANUAL" } logDebug("Read tadoMode: " + autoOperation) childDevice?.sendEvent(name: 'tadoMode', value: autoOperation) if (resp.data.setting.power == "ON"){ childDevice?.sendEvent(name: 'thermostatMode', value: "heat") childDevice?.sendEvent(name: 'thermostatOperatingState', value: "heating") logDebug("Send thermostatMode Event Fired") } else if(resp.data.setting.power == "OFF"){ childDevice?.sendEvent(name: 'thermostatMode', value: "off") childDevice?.sendEvent(name: 'thermostatOperatingState', value: "idle") logDebug("Send thermostatMode Event Fired") } logDebug("Send thermostatMode Event Fired") if (state.supportsWaterTempControl == "true" && resp.data.tadoMode != null && resp.data.setting.power != "OFF"){ if (temperatureUnit == "C") { thermostatSetpoint = resp.data.setting.temperature.celsius } else if(temperatureUnit == "F"){ thermostatSetpoint = resp.data.setting.temperature.fahrenheit } logDebug("Read thermostatSetpoint: " + thermostatSetpoint) } else { thermostatSetpoint = "--" } } else{ logDebug("Executing parseResponse.successFalse") } childDevice?.sendEvent(name: 'thermostatSetpoint', value: thermostatSetpoint, unit: temperatureUnit) logDebug("Send thermostatSetpoint Event Fired") childDevice?.sendEvent(name: 'heatingSetpoint', value: thermostatSetpoint, unit: temperatureUnit) logDebug("Send heatingSetpoint Event Fired") } } private parseTempResponse(resp) { logDebug("Executing parseTempResponse: "+resp.data) logDebug("Output status: "+resp.status) if(resp.status == 200) { logDebug("Executing parseTempResponse.successTrue") def tempunitname = resp.data.temperatureUnit if (tempunitname == "CELSIUS") { logDebug("Setting Temp Unit to C") state.tempunit = "C" } else if(tempunitname == "FAHRENHEIT"){ logDebug("Setting Temp Unit to F") state.tempunit = "F" } }else if(resp.status == 201){ logDebug("Something was created/updated") } } private parseZonesResponse(resp) { logDebug("Executing parseZonesResponse: "+resp.data) logDebug("Output status: "+resp.status) if(resp.status == 200) { def restDevices = resp.data def TadoDevices = [] logDebug("Executing parseZoneResponse.successTrue") restDevices.each { Tado -> TadoDevices << ["${Tado.type}|${Tado.id}|${Tado.name}":"${Tado.name}"] } logDebug( TadoDevices) return TadoDevices }else if(resp.status == 201){ logDebug("Something was created/updated") } } private parseMobileDevicesResponse(resp) { logDebug("Executing parseMobileDevicesResponse: "+resp.data) logDebug("Output status: "+resp.status) if(resp.status == 200) { def restUsers = resp.data def TadoUsers = [] logDebug("Executing parseMobileDevicesResponse.successTrue") restUsers.each { TadoUser -> if (TadoUser.settings.geoTrackingEnabled == true) { TadoUsers << ["${TadoUser.id}|${TadoUser.name}":"${TadoUser.name}"] } } logDebug(TadoUsers) return TadoUsers }else if(resp.status == 201){ logDebug("Something was created/updated") } } private parseCapabilitiesResponse(resp,childDevice) { logDebug("Executing parseCapabilitiesResponse: "+resp.data) logDebug("Output status: " + resp.status) if(resp.status == 200) { try { logDebug("Executing parseResponse.successTrue") childDevice?.setCapabilitytadoType(resp.data.type) logDebug("Tado Type is ${resp.data.type}") if(resp.data.type == "AIR_CONDITIONING") { try { if(resp.data.AUTO || (resp.data.AUTO).toString() == "[:]"){ logDebug("settingautocapability state true") childDevice?.setCapabilitySupportsAuto("true") } else { logDebug("settingautocapability state false") childDevice?.setCapabilitySupportsAuto("false") } if(resp.data.AUTO.swings || (resp.data.AUTO.swings).toString() == "[:]") { logDebug("settingautoswingcapability state true") childDevice?.setCapabilitySupportsAutoSwing("true") } else { logDebug("settingautoswingcapability state false") childDevice?.setCapabilitySupportsAutoSwing("false") } } catch(Exception e) { logDebug("___exception parsing Auto Capabiity: " + e) } try { if(resp.data.COOL || (resp.data.COOL).toString() == "[:]"){ logDebug("setting COOL capability state true") childDevice?.setCapabilitySupportsCool("true") def coolfanmodelist = resp.data.COOL.fanSpeeds if(resp.data.COOL.swings || (resp.data.COOL.swings).toString() == "[:]") { logDebug("settingcoolswingcapability state true") childDevice?.setCapabilitySupportsCoolSwing("true") } else { logDebug("settingcoolswingcapability state false") childDevice?.setCapabilitySupportsCoolSwing("false") } if(resp.data.COOL.fanSpeeds || (resp.data.COOL.fanSpeeds).toString() == "[:]") { childDevice?.setCapabilitySupportsCoolFanSpeed("true") } else { childDevice?.setCapabilitySupportsCoolFanSpeed("false") } if(coolfanmodelist.find { it == 'AUTO' }){ logDebug("setting COOL Auto Fan Speed capability state true") childDevice?.setCapabilitySupportsCoolAutoFanSpeed("true") } else { logDebug("setting COOL Auto Fan Speed capability state false") childDevice?.setCapabilitySupportsCoolAutoFanSpeed("false") } if (state.tempunit == "C"){ childDevice?.setCapabilityMaxCoolTemp(resp.data.COOL.temperatures.celsius.max) childDevice?.setCapabilityMinCoolTemp(resp.data.COOL.temperatures.celsius.min) } else if (state.tempunit == "F") { childDevice?.setCapabilityMaxCoolTemp(resp.data.COOL.temperatures.fahrenheit.max) childDevice?.setCapabilityMinCoolTemp(resp.data.COOL.temperatures.fahrenheit.min) } } else { logDebug("setting COOL capability state false") childDevice?.setCapabilitySupportsCool("false") } } catch(Exception e) { logDebug("___exception parsing Cool Capabiity: " + e) } try { if(resp.data.DRY || (resp.data.DRY).toString() == "[:]"){ logDebug("setting DRY capability state true") childDevice?.setCapabilitySupportsDry("true") } else { logDebug("setting DRY capability state false") childDevice?.setCapabilitySupportsDry("false") } if(resp.data.DRY.swings || (resp.data.DRY.swings).toString() == "[:]") { logDebug("settingdryswingcapability state true") childDevice?.setCapabilitySupportsDrySwing("true") } else { logDebug("settingdryswingcapability state false") childDevice?.setCapabilitySupportsDrySwing("false") } } catch(Exception e) { logDebug("___exception parsing Dry Capabiity: " + e) } try { if(resp.data.FAN || (resp.data.FAN).toString() == "[:]"){ logDebug("setting FAN capability state true") childDevice?.setCapabilitySupportsFan("true") } else { logDebug("setting FAN capability state false") childDevice?.setCapabilitySupportsFan("false") } if(resp.data.FAN.swings || (resp.data.FAN.swings).toString() == "[:]") { logDebug("settingfanswingcapability state true") childDevice?.setCapabilitySupportsFanSwing("true") } else { logDebug("settingfanswingcapability state false") childDevice?.setCapabilitySupportsFanSwing("false") } } catch(Exception e) { logDebug("___exception parsing Fan Capabiity: " + e) } try { if(resp.data.HEAT || (resp.data.HEAT).toString() == "[:]"){ logDebug("setting HEAT capability state true") childDevice?.setCapabilitySupportsHeat("true") def heatfanmodelist = resp.data.HEAT.fanSpeeds if(resp.data.HEAT.swings || (resp.data.HEAT.swings).toString() == "[:]") { logDebug("settingheatswingcapability state true") childDevice?.setCapabilitySupportsHeatSwing("true") } else { logDebug("settingheatswingcapability state false") childDevice?.setCapabilitySupportsHeatSwing("false") } if(resp.data.HEAT.fanSpeeds || (resp.data.HEAT.fanSpeeds).toString() == "[:]") { childDevice?.setCapabilitySupportsHeatFanSpeed("true") } else { childDevice?.setCapabilitySupportsHeatFanSpeed("false") } if(heatfanmodelist.find { it == 'AUTO' }){ logDebug("setting HEAT Auto Fan Speed capability state true") childDevice?.setCapabilitySupportsHeatAutoFanSpeed("true") } else { logDebug("setting HEAT Auto Fan Speed capability state false") childDevice?.setCapabilitySupportsHeatAutoFanSpeed("false") } if (state.tempunit == "C"){ childDevice?.setCapabilityMaxHeatTemp(resp.data.HEAT.temperatures.celsius.max) childDevice?.setCapabilityMinHeatTemp(resp.data.HEAT.temperatures.celsius.min) } else if (state.tempunit == "F") { childDevice?.setCapabilityMaxHeatTemp(resp.data.HEAT.temperatures.fahrenheit.max) childDevice?.setCapabilityMinHeatTemp(resp.data.HEAT.temperatures.fahrenheit.min) } } else { logDebug("setting HEAT capability state false") childDevice?.setCapabilitySupportsHeat("false") } }catch(Exception e) { logDebug("___exception parsing Heat Capabiity: " + e) } } if(resp.data.type == "HEATING") { if(resp.data.type == "HEATING") { logDebug("setting HEAT capability state true") childDevice?.setCapabilitySupportsHeat("true") if (state.tempunit == "C") { childDevice?.setCapabilityMaxHeatTemp(resp.data.temperatures.celsius.max) childDevice?.setCapabilityMinHeatTemp(resp.data.temperatures.celsius.min) } else if (state.tempunit == "F") { childDevice?.setCapabilityMaxHeatTemp(resp.data.temperatures.fahrenheit.max) childDevice?.setCapabilityMinHeatTemp(resp.data.temperatures.fahrenheit.min) } } else { logDebug("setting HEAT capability state false") childDevice?.setCapabilitySupportsHeat("false") } } if(resp.data.type == "HOT_WATER") { if(resp.data.type == "HOT_WATER"){ logDebug("setting WATER capability state true") dchildDevice?.setCapabilitySupportsWater("true") if (resp.data.canSetTemperature == true){ childDevice?.setCapabilitySupportsWaterTempControl("true") if (state.tempunit == "C") { childDevice?.setCapabilityMaxHeatTemp(resp.data.temperatures.celsius.max) childDevice?.setCapabilityMinHeatTemp(resp.data.temperatures.celsius.min) } else if (state.tempunit == "F") { childDevice?.setCapabilityMaxHeatTemp(resp.data.temperatures.fahrenheit.max) childDevice?.setCapabilityMinHeatTemp(resp.data.temperatures.fahrenheit.min) } } else { childDevice?.setCapabilitySupportsWaterTempControl("false") } } else { logDebug("setting Water capability state false") childDevice?.setCapabilitySupportsWater("false") } } } catch(Exception e) { log.error("___exception: " + e) } } else if(resp.status == 201) { logDebug("Something was created/updated") } } private parseweatherResponse(resp) { logDebug("Executing parseweatherResponse: "+resp.data) logDebug("Output status: "+resp.status) def temperatureUnit = state.tempunit logDebug("Temperature Unit is ${temperatureUnit}") if(resp.status == 200) { logDebug("Executing parseResponse.successTrue") def outsidetemperature if (temperatureUnit == "C") { outsidetemperature = resp.data.outsideTemperature.celsius } else if(temperatureUnit == "F"){ outsidetemperature = resp.data.outsideTemperature.fahrenheit } logDebug("Read outside temperature: " + outsidetemperature) state.outsideTemperature = outsidetemperature state.outsideTempUnit = temperatureUnit logDebug("Weather Temperature Values Stored in State") return result }else if(resp.status == 201){ logDebug("Something was created/updated") } } def getidCommand(){ logDebug("Executing 'sendCommand.getidCommand'" ) sendCommand("getid",null,[]) } def getTempUnitCommand(){ logDebug("Executing 'sendCommand.getidCommand'" ) sendCommand("gettempunit",null,[]) } def getZonesCommand(){ logDebug("Executing 'sendCommand.getzones'" ) sendCommand("getzones",null,[]) } def getMobileDevicesCommand(){ logDebug("Executing 'sendCommand.getMobileDevices'" ) sendCommand("getMobileDevices",null,[]) } def getWeather(){ log.info "In Weather Poll" logDebug("Executing 'sendCommand.weatherStatusCommand'" ) def result = sendCommand("weatherStatus",null,[]) } def weatherStatusCommand(childDevice){ logDebug("Read outside temperature: " + state.outsideTemperature) childDevice?.sendEvent(name: 'outsidetemperature', value: state.outsideTemperature, unit: state.outsideTempUnit) logDebug("Send Outside Temperature Event Fired") } def getCapabilitiesCommand(childDevice, deviceDNI){ logDebug("childDevice is: " + childDevice.inspect()) logDebug("deviceDNI is: " + deviceDNI.inspect()) def item = deviceDNI.tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] logDebug("Executing 'sendCommand.getcapabilities'") sendCommand("getcapabilities",childDevice,[deviceId]) } private removeChildDevices(delete) { try { delete.each { deleteChildDevice(it.deviceNetworkId) log.info "Successfully Removed Child Device: ${it.displayName} (${it.deviceNetworkId})" } } catch (e) { log.error "There was an error (${e}) when trying to delete the child device" } } def parseCapabilityData(Map results){ logDebug("in parseCapabilityData") def result results.each { name, value -> if (name == "value") { logDebug("Map Name Returned, ${name} and Value is ${value}") result = value.toString() logDebug("Result is ${result}") //return result } } return result } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //Device Commands Below Here // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- def autoCommand(childDevice){ logDebug("Executing 'sendCommand.autoCommand' on device ${childDevice.device.name}") def terminationmode = settings.manualmode def traperror def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] if (deviceType == "AIR_CONDITIONING") { def capabilitySupportsAuto = parseCapabilityData(childDevice.getCapabilitySupportsAuto()) def capabilitySupportsAutoSwing = parseCapabilityData(childDevice.getCapabilitySupportsAutoSwing()) def capabilitysupported = capabilitySupportsAuto if (capabilitysupported == "true"){ logDebug("Executing 'sendCommand.autoCommand' on device ${childDevice.device.name}") def jsonbody if (capabilitySupportsAutoSwing == "true") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"AUTO", power:"ON", swing:"OFF", type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"AUTO", power:"ON", type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } sendCommand("temperature",dchildDevice,[deviceId,jsonbody]) statusCommand(device) } else { logDebug("Sorry Auto Capability not supported on device ${childDevice.device.name}") } } if(deviceType == "HEATING") { def initialsetpointtemp if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if(traperror == 0){ initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } def jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:initialsetpointtemp], type:"HEATING"], termination:[type:terminationmode]]) sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } if (deviceType == "HOT_WATER") { logDebug("Executing 'sendCommand.autoCommand'") def initialsetpointtemp def jsonbody def capabilitySupportsWaterTempControl = parseCapabilityData(childDevice.getCapabilitySupportsWaterTempControl()) if(capabilitySupportsWaterTempControl == "true"){ if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if(traperror == 0){ initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:initialsetpointtemp], type:"HOT_WATER"], termination:[type:terminationmode]]) } else { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", type:"HOT_WATER"], termination:[type:terminationmode]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } } def dryCommand(childDevice){ def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def capabilitySupportsDry = parseCapabilityData(childDevice.getCapabilitySupportsDry()) def capabilitySupportsDrySwing = parseCapabilityData(childDevice.getCapabilitySupportsDrySwing()) def capabilitysupported = capabilitySupportsDry if (capabilitysupported == "true"){ def terminationmode = settings.manualmode logDebug("Executing 'sendCommand.dryCommand' on device ${childDevice.device.name}") def jsonbody if (capabilitySupportsDrySwing == "true") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"DRY", power:"ON", swing:"OFF", type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"DRY", power:"ON", type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } else { logDebug("Sorry Dry Capability not supported on device ${childDevice.device.name}") } } def fanAuto(childDevice){ def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def capabilitySupportsFan = parseCapabilityData(childDevice.getCapabilitySupportsFan()) def capabilitySupportsFanSwing = parseCapabilityData(childDevice.getCapabilitySupportsFanSwing()) def capabilitysupported = capabilitySupportsFan if (capabilitysupported == "true"){ def terminationmode = settings.manualmode logDebug("Executing 'sendCommand.fanAutoCommand' on device ${childDevice.device.name}") def jsonbody if (capabilitySupportsFanSwing == "true") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"FAN", power:"ON", swing:"OFF", type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"FAN", power:"ON", type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } else { logDebug("Sorry Fan Capability not supported by your HVAC Device") } } def endManualControl(childDevice){ logDebug("Executing 'sendCommand.endManualControl' on device ${childDevice.device.name}") def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] sendCommand("deleteEntry",childDevice,[deviceId]) statusCommand(childDevice) } def cmdFanSpeedAuto(childDevice){ def supportedfanspeed def terminationmode = settings.manualmode def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def jsonbody def capabilitySupportsCool = parseCapabilityData(childDevice.getCapabilitySupportsCool()) def capabilitysupported = capabilitySupportsCool def capabilitySupportsCoolAutoFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsCoolAutoFanSpeed()) def fancapabilitysupported = capabilitySupportsCoolAutoFanSpeed if (fancapabilitysupported == "true"){ supportedfanspeed = "AUTO" } else { supportedfanspeed = "HIGH" } def curSetTemp = (childDevice.device.currentValue("thermostatSetpoint")) def curMode = ((childDevice.device.currentValue("thermostatMode")).toUpperCase()) if (curMode == "COOL" || curMode == "HEAT"){ if (capabilitySupportsCoolSwing == "true" || capabilitySupportsHeatSwing == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } logDebug("Executing 'sendCommand.fanSpeedAuto' to ${supportedfanspeed}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } } def cmdFanSpeedHigh(childDevice){ def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def jsonbody def supportedfanspeed = "HIGH" def terminationmode = settings.manualmode def curSetTemp = (childDevice.device.currentValue("thermostatSetpoint")) def curMode = ((childDevice.device.currentValue("thermostatMode")).toUpperCase()) if (curMode == "COOL" || curMode == "HEAT"){ if (capabilitySupportsCoolSwing == "true" || capabilitySupportsHeatSwing == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } logDebug("Executing 'sendCommand.fanSpeedAuto' to ${supportedfanspeed}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } } def cmdFanSpeedMid(childDevice){ def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def supportedfanspeed = "MIDDLE" def terminationmode = settings.manualmode def jsonbody def curSetTemp = (childDevice.device.currentValue("thermostatSetpoint")) def curMode = ((childDevice.device.currentValue("thermostatMode")).toUpperCase()) if (curMode == "COOL" || curMode == "HEAT"){ if (capabilitySupportsCoolSwing == "true" || capabilitySupportsHeatSwing == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } logDebug("Executing 'sendCommand.fanSpeedMid' to ${supportedfanspeed}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } } def cmdFanSpeedLow(childDevice){ def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def capabilitySupportsCoolSwing = parseCapabilityData(childDevice.getCapabilitySupportsCoolSwing()) def capabilitySupportsHeatSwing = parseCapabilityData(childDevice.getCapabilitySupportsHeatSwing()) def supportedfanspeed = "LOW" def terminationmode = settings.manualmode def jsonbody def curSetTemp = (childDevice.device.currentValue("thermostatSetpoint")) def curMode = ((childDevice.device.currentValue("thermostatMode")).toUpperCase()) if (curMode == "COOL" || curMode == "HEAT"){ if (capabilitySupportsCoolSwing == "true" || capabilitySupportsHeatSwing == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", swing:"OFF", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[celsius:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:curMode, power:"ON", temperature:[fahrenheit:curSetTemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } logDebug("Executing 'sendCommand.fanSpeedLow' to ${supportedfanspeed}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } } def setCoolingTempCommand(childDevice,targetTemperature){ def terminationmode = settings.manualmode def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def supportedfanspeed def capabilitySupportsCool = parseCapabilityData(childDevice.getCapabilitySupportsCool()) def capabilitySupportsCoolSwing = parseCapabilityData(childDevice.getCapabilitySupportsCoolSwing()) def capabilitysupported = capabilitySupportsCool def capabilitySupportsCoolFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsCoolFanSpeed()) def capabilitySupportsCoolAutoFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsCoolAutoFanSpeed()) def fancapabilitysupported = capabilitySupportsCoolAutoFanSpeed def jsonbody if (fancapabilitysupported == "true"){ supportedfanspeed = "AUTO" } else { supportedfanspeed = "HIGH" } if (capabilitySupportsCoolSwing == "true" && capabilitySupportsCoolFanSpeed == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", swing:"OFF", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", swing:"OFF", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsCoolSwing == "true" && capabilitySupportsCoolFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", swing:"OFF", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", swing:"OFF", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsCoolSwing == "false" && capabilitySupportsCoolFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } logDebug("Executing 'sendCommand.setCoolingTempCommand' to ${targetTemperature} on device ${childDevice.device.name}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) } def setHeatingTempCommand(childDevice,targetTemperature){ def terminationmode = settings.manualmode def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] if(deviceType == "AIR_CONDITIONING") { def capabilitySupportsHeat = parseCapabilityData(childDevice.getCapabilitySupportsHeat()) def capabilitysupported = capabilitySupportsHeat def capabilitySupportsHeatSwing = parseCapabilityData(childDevice.getCapabilitySupportsHeatSwing()) def capabilitySupportsHeatAutoFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsHeatAutoFanSpeed()) def capabilitySupportsHeatFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsHeatFanSpeed()) def fancapabilitysupported = capabilitySupportsHeatAutoFanSpeed def supportedfanspeed def jsonbody if (fancapabilitysupported == "true") { supportedfanspeed = "AUTO" } else { supportedfanspeed = "HIGH" } if (capabilitySupportsHeatSwing == "true" && capabilitySupportsHeatFanSpeed == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", swing:"OFF", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", swing:"OFF", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsHeatSwing == "true" && capabilitySupportsHeatFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", swing:"OFF", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", swing:"OFF", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsHeatSwing == "false" && capabilitySupportsHeatFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", temperature:[celsius:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", temperature:[fahrenheit:targetTemperature], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } logDebug("Executing 'sendCommand.setHeatingTempCommand' to ${targetTemperature} on device ${childDevice.device.name}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) } if(deviceType == "HEATING") { def jsonbody if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:targetTemperature], type:"HEATING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[fahrenheit:targetTemperature], type:"HEATING"], termination:[type:terminationmode]]) } logDebug("Executing 'sendCommand.setHeatingTempCommand' to ${targetTemperature} on device ${childDevice.device.name}") sendCommand("temperature",childDevice,[deviceId,jsonbody]) } if(deviceType == "HOT_WATER") { def jsonbody def capabilitySupportsWaterTempControl = parseCapabilityData(childDevice.getCapabilitySupportsWaterTempControl()) if(capabilitySupportsWaterTempControl == "true"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:targetTemperature], type:"HOT_WATER"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[fahrenheit:targetTemperature], type:"HOT_WATER"], termination:[type:terminationmode]]) } logDebug("Executing 'sendCommand.setHeatingTempCommand' to ${targetTemperature} on device ${childDevice.device.name}") sendCommand("temperature",[jsonbody]) } else { logDebug("Hot Water Temperature Capability Not Supported on device ${childDevice.device.name}" ) } } } def offCommand(childDevice){ logDebug("Executing 'sendCommand.offCommand' on device ${childDevice.device.name}") def terminationmode = settings.manualmode def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def jsonbody = new groovy.json.JsonOutput().toJson([setting:[type:deviceType, power:"OFF"], termination:[type:terminationmode]]) sendCommand("temperature",childDevice,[deviceId,jsonbody]) } def onCommand(childDevice){ logDebug("Executing 'sendCommand.onCommand'") def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] if(deviceType == "AIR_CONDITIONING") { coolCommand(childDevice) } if(deviceType == "HEATING" || deviceType == "HOT_WATER") { heatCommand(childDevice) } } def coolCommand(childDevice){ logDebug("Executing 'sendCommand.coolCommand'") def terminationmode = settings.manualmode def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] def initialsetpointtemp def supportedfanspeed def capabilitySupportsCool = parseCapabilityData(childDevice.getCapabilitySupportsCool()) def capabilitySupportsCoolSwing = parseCapabilityData(childDevice.getCapabilitySupportsCoolSwing()) def capabilitysupported = capabilitySupportsCool def capabilitySupportsCoolAutoFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsCoolAutoFanSpeed()) def capabilitySupportsCoolFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsCoolFanSpeed()) def fancapabilitysupported = capabilitySupportsCoolAutoFanSpeed def traperror if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if (fancapabilitysupported == "true"){ supportedfanspeed = "AUTO" } else { supportedfanspeed = "HIGH" } if(traperror == 0){ initialsetpointtemp = settings.defCoolingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } def jsonbody if (capabilitySupportsCoolSwing == "true" && capabilitySupportsCoolFanSpeed == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", swing:"OFF", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", swing:"OFF", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsCoolSwing == "true" && capabilitySupportsCoolFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", swing:"OFF", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", swing:"OFF", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsCoolSwing == "false" && capabilitySupportsCoolFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"COOL", power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"COOL", power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } sendCommand("temperature",childDevice,[deviceId,jsonbody]) } def heatCommand(childDevice){ logDebug("Executing 'sendCommand.heatCommand' on device ${childDevice.device.name}") def terminationmode = settings.manualmode def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] if(deviceType == "AIR_CONDITIONING") { def initialsetpointtemp def supportedfanspeed def traperror def capabilitySupportsHeat = parseCapabilityData(childDevice.getCapabilitySupportsHeat()) def capabilitySupportsHeatSwing = parseCapabilityData(childDevice.getCapabilitySupportsHeatSwing()) def capabilitysupported = capabilitySupportsHeat def capabilitySupportsHeatAutoFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsHeatAutoFanSpeed()) def capabilitySupportsHeatFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsHeatFanSpeed()) def fancapabilitysupported = capabilitySupportsHeatAutoFanSpeed if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set" ) traperror = 0 } if (fancapabilitysupported == "true") { supportedfanspeed = "AUTO" } else { supportedfanspeed = "HIGH" } if(traperror == 0) { initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } def jsonbody if (capabilitySupportsHeatSwing == "true" && capabilitySupportsHeatFanSpeed == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", swing:"OFF", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", swing:"OFF", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsHeatSwing == "true" && capabilitySupportsHeatFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", swing:"OFF", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", swing:"OFF", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsHeatSwing == "false" && capabilitySupportsHeatFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } sendCommand("temperature",childDevice,[deviceId,jsonbody]) } if(deviceType == "HEATING") { def initialsetpointtemp def traperror if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if(traperror == 0) { initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } def jsonbody if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:initialsetpointtemp], type:"HEATING"], termination:[type:terminationmode]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"HEATING"], termination:[type:terminationmode]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) } if(deviceType == "HOT_WATER") { def jsonbody def initialsetpointtemp def traperror def capabilitySupportsWaterTempControl = parseCapabilityData(childDevice.getCapabilitySupportsWaterTempControl()) if(capabilitySupportsWaterTempControl == "true"){ if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if(traperror == 0){ initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:initialsetpointtemp], type:"HOT_WATER"], termination:[type:terminationmode]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"HOT_WATER"], termination:[type:terminationmode]]) } } else { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", type:"HOT_WATER"], termination:[type:terminationmode]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) } } def emergencyHeat(childDevice){ logDebug("Executing 'sendCommand.heatCommand' on device ${childDevice.device.name}") def traperror def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] if(deviceType == "AIR_CONDITIONING") { def capabilitySupportsHeat = parseCapabilityData(childDevice.getCapabilitySupportsHeat()) def capabilitysupported = capabilitySupportsHeat def capabilitySupportsHeatSwing = parseCapabilityData(childDevice.getCapabilitySupportsHeatSwing()) def capabilitySupportsHeatAutoFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsHeatAutoFanSpeed()) def capabilitySupportsHeatFanSpeed = parseCapabilityData(childDevice.getCapabilitySupportsHeatFanSpeed()) def fancapabilitysupported = capabilitySupportsHeatAutoFanSpeed if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if (capabilitysupported == "true") { def initialsetpointtemp def supportedfanspeed if (fancapabilitysupported == "true") { supportedfanspeed = "AUTO" } else { supportedfanspeed = "HIGH" } if(traperror == 0) { initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } def jsonbody if (capabilitySupportsHeatSwing == "true" && capabilitySupportsHeatFanSpeed == "true") { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", swing:"OFF", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", swing:"OFF", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } } else if(capabilitySupportsHeatSwing == "true" && capabilitySupportsHeatFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", swing:"OFF", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", swing:"OFF", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else if(capabilitySupportsHeatSwing == "false" && capabilitySupportsHeatFanSpeed == "false"){ if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } else if(state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[mode:"HEAT", power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[type:terminationmode]]) } } else { if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", temperature:[celsius:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[fanSpeed:supportedfanspeed, mode:"HEAT", power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"AIR_CONDITIONING"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } } sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(device) } else { logDebug("Sorry Heat Capability not supported on device ${childDevice.device.name}") } } if(deviceType == "HEATING") { def initialsetpointtemp if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set" ) traperror = 0 } if(traperror == 0) { initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } def jsonbody if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:initialsetpointtemp], type:"HEATING"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"HEATING"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } (deviceType == "HOT_WATER") { def initialsetpointtemp def jsonbody def capabilitySupportsWaterTempControl = parseCapabilityData(childDevice.getCapabilitySupportsWaterTempControl()) if(capabilitySupportsWaterTempControl == "true"){ if(childDevice.device.currentValue("thermostatSetpoint")) { traperror = ((childDevice.device.currentValue("thermostatSetpoint")).intValue()) } else { logDebug("Existing Setpoint is not set") traperror = 0 } if(traperror == 0) { initialsetpointtemp = settings.defHeatingTemp } else { initialsetpointtemp = childDevice.device.currentValue("thermostatSetpoint") } if (state.tempunit == "C") { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[celsius:initialsetpointtemp], type:"HOT_WATER"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } else if (state.tempunit == "F"){ jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", temperature:[fahrenheit:initialsetpointtemp], type:"HOT_WATER"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } } else { jsonbody = new groovy.json.JsonOutput().toJson([setting:[power:"ON", type:"HOT_WATER"], termination:[durationInSeconds:"3600", type:"TIMER"]]) } sendCommand("temperature",childDevice,[deviceId,jsonbody]) statusCommand(childDevice) } } def statusCommand(childDevice){ def item = (childDevice.device.deviceNetworkId).tokenize('|') def deviceId = item[0] def deviceType = item[1] def deviceToken = item[2] logDebug("Executing 'sendCommand.statusCommand'") sendCommand("status",childDevice,[deviceId]) } def userStatusCommand(childDevice){ try{ logDebug("Executing 'sendCommand.userStatusCommand'") sendCommand("userStatus",childDevice,[]) } catch(Exception e) { log.error("Failed in setting userStatusCommand: " + e) } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Log methods // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- def logDebug(message) { if (isDebugLogActive()){ log.debug message } } def openForcedDebugLog(seconds) { if (seconds == 0){ seconds = getDefaultLogForceSecs() } def forceLogRequestedUntil = now() + (seconds * 1000) if (state.debugLogForcedUntil < forceLogRequestedUntil) { state.debugLogForcedUntil = forceLogRequestedUntil } } def isDebugLogActive() { if (now() < state.debugLogForcedUntil || getDebugLogStatus() == true ) { return true } else { return false } } def disableDebuglog() { if (debugTurnedOn) { app.updateSettings("debugLog", false) } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Password Grant methods // ------------------------------------------------------------------------------------------------------------------------------------------------------------------- def initiateDeviceCodeFlow() { def result = false logDebug("Initiating device code flow") def params = [ uri: getTadoLoginUri(), path: getDeviceCodePath(), body: [ client_id: getClientId(), scope: "offline_access" ] ] try { logDebug("Parameters for initiating device code flow:" + " " + params) httpPost(params) { resp -> logDebug("Response data:" + " " + resp.data) logDebug("Respones succes:" + " " + resp.success) if (resp && resp.data && resp.success) { logDebug("Device Code Received") state.deviceCode = resp.data.device_code state.userCode = resp.data.user_code state.deviceCodeExpiresIn = resp.data.expires_in state.deviceCodeExpires = (now() + (resp.data.expires_in * 1000)) state.deviceCodeCheckInterval = resp.data.interval state.deviceCodeVerificationUri = resp.data.verification_uri state.deviceCodeVerificationUriComplete = resp.data.verification_uri_complete logDebug("User Code:" + " " + resp.data.user_code) logDebug("Device Code Verification URL:" + " " + state.deviceCodeVerificationUriComplete) logDebug("Device code expires:" + " " + state.deviceCodeExpires) result = true } else { log.error "Failed to get Device Code" } } } catch (e) { log.error "Device Code Retreival Error: ${e}" } return result } def getDeviceAccessToken() { def result = false logDebug("Initiating get Device Access Token" ) def params = [ uri: getTadoLoginUri(), path: getDeviceTokenPath(), body: [ client_id: getClientId(), device_code: state.deviceCode, grant_type: "urn:ietf:params:oauth:grant-type:device_code" ] ] try { logDebug("Parameters for initiating get Device Access Token:" + " " + params ) httpPost(params) { resp -> logDebug("Response data:" + " " + resp.data ) logDebug("Respones succes:" + " " + resp.success ) if (resp && resp.data && resp.success) { logDebug("Access Token Received" ) state.accessToken = resp.data.access_token state.accessTokenRefreshed = now() state.accessTokenExpires = (now() + (resp.data.expires_in * 1000)) state.nextAccessTokenRefresh = now() + (resp.data.expires_in * 1000) - getTokenExpOffset() state.accessTokenTimeToLive = resp.data.expires_in state.refreshToken = resp.data.refresh_token state.tokenScope = resp.data.scope state.tokenType = resp.data.token_type state.userId = resp.data.userId logDebug("Access Token:" + " " + resp.data.access_token ) logDebug("Refresh Token:" + " " + resp.data.refresh_token ) logDebug("User ID:" + " " + state.userId ) logDebug("Token expires in:" + " " + resp.data.expires_in ) logDebug("Token expires:" + " " + state.accessTokenExpires ) result = true } else { log.error "Failed to get access code" log.error( "Parameters for get access code:" + " " + params ) if(resp){ log.error( "Response:" + " " + resp.data ) } if(resp.data){ log.error( "Response data:" + " " + resp.data ) } if(resp.success){ log.error( "Respones succes:" + " " + resp.success ) } state.accessToken = null state.refreshToken = null } } } catch (e) { log.error "Device Access Code Retreival Error: ${e}" if(resp){ log.error( "Response:" + " " + resp.data ) if(resp.data){ log.error( "Response data:" + " " + resp.data ) } if(resp.success){ log.error( "Respones succes:" + " " + resp.success ) } } state.accessToken = null state.refreshToken = null } return result } def refreshAccessToken() { if (!state.accessTokenExpires || state.nextAccessTokenRefresh <= now()) { return true } else { return false } } def isAccessTokenExpired() { if (!state.accessTokenExpires || state.accessTokenExpires <= now()) { return true } else { return false } } def refreshToken() { def result = false logDebug("Refreshing Access token" ) //Open debug log if access token is cleared previously if (isAccessTokenExpired() && settings.debugLog == false ) { def debugTurnedOn = true settings.debugLog = true } try { def params = [ uri: getAccessTokenUri(), path: getAccessTokenPath(), body: [ client_id: getClientId(), grant_type: "refresh_token", refresh_token: getRefreshToken() ] ] logDebug("Parameters for token refresh:" + " " + params ) httpPost(params) { resp -> logDebug("Response data:" + " " + resp.data ) logDebug("Respones succes:" + " " + resp.success ) if (resp && resp.data && resp.success) { logDebug("Token refreshed successfully" ) state.accessToken = resp.data.access_token state.accessTokenRefreshed = now() state.accessTokenExpires = now() + (resp.data.expires_in * 1000) state.nextAccessTokenRefresh = now() + (resp.data.expires_in * 1000) - getTokenExpOffset() state.accessTokenTimeToLive = resp.data.expires_in state.refreshToken = resp.data.refresh_token logDebug("Access Token:" + " " + resp.data.access_token ) logDebug("Access Token Saved:" + " " + state.accessToken ) logDebug("Refresh Token:" + " " + resp.data.refresh_token ) logDebug("Refresh Token Saved:" + " " + state.refreshToken ) logDebug("User ID:" + " " + state.userId ) logDebug("Token expires in:" + " " + resp.data.expires_in ) logDebug("Token expires:" + " " + state.accessTokenExpires ) result = true } else { log.error( "Failed to refresh access token using refresh token" + " " + state.refreshToken ) log.error( "Parameters for token refresh:" + " " + params ) if(resp){ log.error( "Response:" + " " + resp.data ) if(resp.data){ log.error( "Response data:" + " " + resp.data ) } if(resp.success){ log.error( "Respones succes:" + " " + resp.success ) } } result = false } } } catch (e) { log.error "Failed to refresh access token (HTTP error): ${e}" if(resp){ log.error( "Response:" + " " + resp.data ) if(resp.data){ log.error( "Response data:" + " " + resp.data ) } if(resp.success){ log.error( "Respones succes:" + " " + resp.success ) } } result = false } if (debugTurnedOn) { def debugTurnedOn = false settings.debugLog = false } return result }