Develop a driver - (Venstar Thermostat Driver Development)

Right... Missed the uppercase... fixed... Also fixed the modes used by the dashboards (should not affect anything except removing referenced to fanOn and fanAuto).

You rules are a little confusing... what are "Therm.fan.on" and "Therm.fan.auto"??... I assume they are virtual devices (holdovers from before the driver worked properly??)... but I'm not sure...

New version

/**
 *  Venstar Hubitat Driver
 *
 *  Copyright 2014 Scottin Pollock
 *  Modifications copyright (C) 2019 David Tarin
 *  Fixes, refactoring & functionality copyright (C) 2019 CybrMage
 *
 *  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.
 *
 */

import groovy.json.JsonSlurper

metadata {
	definition (name: "Venstar Thermostat", namespace: "Thermostat", author: "CybrMage, Scottin Pollock, David Tarin") {
		capability "Actuator"
		capability "Initialize"
		capability "Polling"
		capability "Refresh"
		capability "Sensor"
		capability "Thermostat"
		capability "RelativeHumidityMeasurement"
		capability "PresenceSensor"
		
		attribute "thermostatFanOperatingState","enum",["off","on"]
	}

	preferences {
		section("Driver config") {
			input "thermostatIP", "text", title: "Thermostat IP", required: false
			def pollRate = ["1" : "Poll every minute", "5" : "Poll every 5 minutes", "10" : "Poll every 10 minutes", "15" : "Poll every 15 minutes", "30" : "Poll every 30 minutes", "60" : "Poll every hour", "180" : "Poll every 3 hours"]
			input ("Poll_Rate", "enum", title: "Device Poll Rate", options: pollRate, defaultValue: "10")
			input name: "sensorEnable", type: "bool", title: "Enable sensor child devices", defaultValue: false
			input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
			input name: "testMode", type: "bool", title: "Enable offline test mode", defaultValue: false
		}
	}
}

private getVERSION() { "v0.13" }

// parse events into attributes
def parseJsonData(result) {
	if (logEnable) log.debug("parseJsonData: data is: $result")
	if (result.success != null){
		//Do nothing as nothing can be done. (runIn doesn't appear to work here and apparently you can't make outbound calls here)
		if (logEnable) log.debug "parseJsonData: getapi()/postapi() data indicates success"
		return
	}
	if (result.error != null){
		if (logEnable) log.debug "parseJsonData: getapi()/postapi() data indicates error: ${result.reason}"
		return
	}
	if (result.mode != null){
		if (logEnable) log.debug "parseJsonData: parsing result.mode"
		def mode = getModeMap()[result.mode]
		state.thermostatMode = result.mode
		if(device.currentState("thermostatMode")?.value != mode){
			sendEvent(name: "thermostatMode", value: mode, descriptionText: "thermostatMode set to ${mode}", isStateChange: true)
		}
	}
	if (result.state != null){
		if (logEnable) log.debug "parseJsonData: parsing result.state"
		def mode = getOperatingModeMap()[result.state]
		state.thermostatOperatingState = result.state
		if(device.currentState("thermostatOperatingState")?.value != mode){
			sendEvent(name: "thermostatOperatingState", value: mode, descriptionText: "thermostatOperatingState set to ${mode}", isStateChange: true)
		}
	}
	if (result.fan != null){
		if (logEnable) log.debug "parseJsonData: parsing result.fan"
		def fan = getFanModeMap()[result.fan]
		state.thermostatFanMode = result.fan
		if (device.currentState("thermostatFanMode")?.value != fan){
			sendEvent(name: "thermostatFanMode", value: fan, descriptionText: "thermostatFanMode set to ${fan}", isStateChange: true)
		}
	}
	if (result.fanstate != null){
		if (logEnable) log.debug "parseJsonData: parsing result.fanstate"
		def mode = (result.fanstate == 0)?"off":"on"
		state.thermostatFanOperatingState = result.fanstate
		if(device.currentState("thermostatFanOperatingState")?.value != mode){
			sendEvent(name: "thermostatFanOperatingState", value: mode, descriptionText: "thermostatFanOperatingState set to ${mode}", isStateChange: true)
		}
	}
	if (result.tempunits != null){
		if (logEnable) log.debug "parseJsonData: parsing result.tempunits"
		def temperatureScale = ((result.tempunits as Integer) == 0) ? "F" : "C"
		state.temperatureScale = temperatureScale
		if (device.currentState("temperatureScale")?.value != temperatureScale.toString()){
			sendEvent(name: "temperatureScale", value: temperatureScale, descriptionText: "temperatureScale set to ${temperatureScale}", isStateChange: true)
		}
	} else {
		if (result.spacetemp != null){
			def temp = result.spacetemp
			if (temp < 32) {
				state.temperatureScale = "C"
			} else {
				state.temperatureScale = "F"
			}
		} else {
			state.temperatureScale = "F"
		}
		sendEvent(name: "temperatureScale", value: temperatureScale, descriptionText: "temperatureScale set to ${temperatureScale}", isStateChange: true)
	}
	if (result.cooltempmin != null){
		if (logEnable) log.debug "parseJsonData: parsing result.cooltempmin"
		state.cooltempmin = result.cooltempmin
	}
	if (result.cooltempmax != null){
		if (logEnable) log.debug "parseJsonData: parsing result.cooltempmax"
		state.cooltempmax = result.cooltempmax
	}
	if (result.heattempmin != null){
		if (logEnable) log.debug "parseJsonData: parsing result.heattempmin"
		state.heattempmin = result.heattempmin
	}
	if (result.heattempmax != null){
		if (logEnable) log.debug "parseJsonData: parsing result.heattempmax"
		state.heattempmax = result.heattempmax
	}
	if (result.cooltemp != null){
		if (logEnable) log.debug "parseJsonData: parsing result.cooltemp"
		def cooltemp = (getTemperatureHE(result.cooltemp) as Float).round(1)
		state.cooltemp = result.cooltemp
		if (device.currentState("coolingSetpoint")?.value != cooltemp.toString()){
			sendEvent(name: "coolingSetpoint", value: cooltemp, descriptionText: "coolingSetpoint set to ${cooltemp}", isStateChange: true)
		}
	}
	if (result.heattemp != null){
		if (logEnable) log.debug "parseJsonData: parsing result.heattemp"
		def heattemp = (getTemperatureHE(result.heattemp) as Float).round(1)
		state.heattemp = result.heattemp
		if (device.currentState("heatingSetpoint")?.value != heattemp.toString()){
			sendEvent(name: "heatingSetpoint", value: heattemp, , descriptionText: "Heating setpoint set to ${heattemp}", isStateChange: true)
		}
	}
	if (result.spacetemp != null){
		if (logEnable) log.debug "parseJsonData: parsing result.spacetemp"
		def temp = (getTemperatureHE(result.spacetemp) as Float).round(1)
		state.temperature = result.spacetemp
		if (device.currentState("temperature")?.value != spacetemp.toString()){
			sendEvent(name: "temperature", value: temp, descriptionText: "temperature set to ${temp}", isStateChange: true)
		}
	}
	if (result.state != null){
		if ( state.thermostatOperatingState == 2) {
			state.thermostatSetpoint = state.cooltemp
		} else {
			state.thermostatSetpoint = state.heattemp
		}
	    sendEvent(name: "thermostatSetpoint", value: (getTemperatureHE(state.thermostatSetpoint) as Float).round(1), descriptionText: "thermostatSetpoint set to ${(getTemperatureHE(state.thermostatSetpoint) as Float).round(1)}", isStateChange: true)
	}
	if (result.away != null){
		if (logEnable) log.debug "parseJsonData: parsing result.away"
		def mode = getAwayModes()[result.away]
		state.presence = result.away
		if(device.currentState("presence")?.value != mode){
			sendEvent(name: "presence", value: mode, descriptionText: "presence set to ${mode}", isStateChange: true)
		}
	}
	if (result.hum != null){
		if (logEnable) log.debug "parseJsonData: parsing result.hum"
		state.humidity = result.hum
		if(device.currentState("humidity")?.value != mode){
			sendEvent(name: "humidity", value: state.humidity, descriptionText: "humidity set to ${mode}", isStateChange: true)
		}
	}
	if (result.sensors != null) {
		if (logEnable) log.debug "parseJsonData: parsing SENSOR data"
		result.sensors.each { sensor ->
			def sensorDNI = device.deviceNetworkId + "-ep" + sensor.name.replaceAll("\\s","").replaceAll("\\W","")
			def targetChild = getChildDevice(sensorDNI)
			if (targetChild != null) {
				if (logEnable) log.debug "parseJsonData: updating SENSOR data for "+sensorDNI
				if (sensor.temp != null) targetChild.setTemperature(sensor.temp)
				if (sensor.hum != null) targetChild.setHumidity(sensor.hum)
			} else {
				if (logEnable) log.warn "parseJsonData: child SENSOR does not exist for "+sensorDNI
			}
		}
	}
}

def poll() {
  if (logEnable) log.debug("poll: Executing 'poll'")
  sendEvent(descriptionText: "poll keep alive", isStateChange: false)  // workaround to keep polling from being shut off
  refresh()
}

def modes() {
  ["off", "heat", "cool", "auto"]
}

def parseDescriptionAsMap(description) {
  description.split(",").inject([:]) { map, param ->
    def nameAndValue = param.split(":")
    map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
  }
}

def getAwayModes() { [
  0:"present",
  1:"not present"
]}

def getModeMap() { [
  0:"off",
  2:"cool",
  1:"heat",
  3:"auto"
]}

def getOperatingModeMap() { [
	0: "idle",
	1: "heating",
	2: "cooling",
	3: "lockout",
	4: "error"
]}

def getFanModeMap() {[
	0:"auto",
	1:"on"
]}

def clampTemp(temp) {
	def T1 = Math.floor(temp)
	def T2 = temp - T1
	def T3 = 0
	if ((T2 >= 0.25) && (T2 <= 0.75)) {
		T3 = 0.5
	} else if (T2 > 0.75) {
		T3 = 1.0
	}
	def T4 = T1 + T3
	if (logEnable) log.debug("  clampTemp: triming value ${temp} to ${T4}")
	return T4
}

def getTemperatureHE(value) {
	if (logEnable) log.debug("  getTemperatureHE: testing for temperature scale conversion for temperature value ${value}")
	def scaleHE = getTemperatureScale()
	def scaleVS = (device.currentState("temperatureScale")?.value)?(device.currentState("temperatureScale")?.value):state.temperatureScale
	if (logEnable) log.debug("  getTemperatureHE: temperatureScale - HE = ${scaleHE}  VS = ${scaleVS}")
	if ( !scaleHE || !scaleVS || (scaleHE == scaleVS)) {
		value = clampTemp(value)
		if (logEnable) log.debug("  getTemperatureHE: no temperature scale conversion required for temperature value ${value}")
		return value
	}
	if ((scaleVS == "F") && (scaleHE == "C")) {
		// F to C
		def tempC = (((value-32)*5.0)/9.0)
		tempC = clampTemp(tempC)
		if (logEnable) log.warn("  getTemperatureHE: temperature scale conversion required. temperature value ${value}F = ${tempC}C")
		return tempC
	} else {
		// C to F
		def tempF = (((value * 9.0)/5.0)+32)
		tempF = clampTemp(tempF)
		if (logEnable) log.warn("  getTemperatureHE: temperature scale conversion required. temperature value ${value}C = ${tempF}F")
		return tempF
	}
}

def getTemperatureVS(value) {
	if (logEnable) log.debug("  getTemperatureVS: testing for temperature scale conversion for temperature value ${value}")
	def scaleHE = getTemperatureScale()
	def scaleVS = (device.currentState("temperatureScale")?.value)?(device.currentState("temperatureScale")?.value):state.temperatureScale
	if (logEnable) log.debug("  getTemperatureVS: temperatureScale - VS = ${scaleVS}  HE = ${scaleHE}")
	if ( !scaleHE || !scaleVS || (scaleHE == scaleVS)) {
		value = clampTemp(value)
		if (logEnable) log.debug("  getTemperatureVS: no temperature scale conversion required for temperature value ${value}")
		return value
	}
	if ((scaleHE == "F") && (scaleVS == "C")) {
		// F to C
		def tempC = (((value-32)*5.0)/9.0)
		tempC = clampTemp(tempC)
		if (logEnable) log.warn("  getTemperatureVS: temperature scale conversion required. temperature value ${value}F = ${tempC}C")
		return tempC
	} else {
		// C to F
		def tempF = (((value * 9.0)/5.0)+32)
		tempF = clampTemp(tempF)
		if (logEnable) log.warn("  getTemperatureVS: temperature scale conversion required. temperature value ${value}C = ${tempF}F")
		return tempF
	}
}

// handle commands
def getValidHeatSetpoint(newTemp) {
	if (logEnable) log.debug("  getValidHeatSetpoint: testing heat setpoint for temperature value ${newTemp}")
	if (!state.heattempmin || !state.heattempmax) { 
		if (logEnable) log.debug("  getValidHeatSetpoint: no restriction for temperature value ${newTemp}")
		return newTemp 
	}
	def minTemp = state.heattempmin as Integer
	def maxTemp = state.heattempmax as Integer
	if (newTemp < minTemp) {
		if (logEnable) log.warn("  getValidHeatSetpoint: heat setpoint restricted to minimum temperature value ${minTemp}")
		return minTemp
	} else if (newTemp > maxTemp) {
		if (logEnable) log.warn("  getValidHeatSetpoint: heat setpoint restricted to maximum temperature value ${maxTemp}")
		return maxTemp
	} else {
		if (logEnable) log.debug("  getValidHeatSetpoint: no restriction for temperature value ${newTemp}")
		return newTemp
	}
}

def getValidCoolSetpoint(newTemp) {
	if (logEnable) log.debug("  getValidCoolSetpoint: testing cool setpoint for temperature value ${newTemp}")
	if (!state.cooltempmin || !state.cooltempmax) { 
		if (logEnable) log.debug("getValidCoolSetpoint: no restriction for temperature value ${newTemp}")
		return newTemp 
	}
	def minTemp = state.cooltempmin as Integer
	def maxTemp = state.cooltempmax as Integer
	if (newTemp < minTemp) {
		if (logEnable) log.warn("  getValidCoolSetpoint: cool setpoint restricted to minimum temperature value ${minTemp}")
		return minTemp
	} else if (newTemp > maxTemp) {
		if (logEnable) log.warn("  getValidCoolSetpoint: cool setpoint restricted to maximum temperature value ${maxTemp}")
		return maxTemp
	} else {
		if (logEnable) log.debug("  getValidCoolSetpoint: no restriction for temperature value ${newTemp}")
		return newTemp
	}
}

def setCommonSetpoint() {
	if ( state.thermostatOperatingState == 2) {
		state.thermostatSetpoint = state.cooltemp
	} else {
		state.thermostatSetpoint = state.heattemp
	}
	def newTemp = clampTemp(getTemperatureHE(state.thermostatSetpoint))
	if (logEnable) log.debug "  setCommonSetpoint: Executing 'setCommonSetpoint' with ${newTemp} (originally - ${state.thermostatSetpoint})"
    sendEvent(name: "thermostatSetpoint", value: newTemp, descriptionText: "thermostatSetpoint set to ${newTemp}", isStateChange: true)
}

def setHeatingSetpoint(degrees) {
	if (logEnable) log.debug "Executing 'setHeatingSetpoint' with ${degrees}"
	def degreesHE = clampTemp(degrees)
	def degreesVS = getTemperatureVS(degreesHE)
	state.heattemp = getValidHeatSetpoint(degreesVS)
	if (state.heattemp != degreesVS) {
		// temperature was restricted - convert restricted temperature back to HE format
		degreesHE = getTemperatureHE(degreesVS)
	}
	if (logEnable) log.debug "processing 'setHeatingSetpoint' with ${degreesHE} (originally ${degrees})"
	setCommonSetpoint()
	sendEvent(name: "heatingSetpoint", value: degreesHE, descriptionText: "Heating setpoint set to ${degreesHE}", isStateChange: true)
	postapi()
}

def setCoolingSetpoint(degrees) {
	if (logEnable) log.debug "Executing 'setCoolingSetpoint' with ${degrees}"
	def degreesHE = clampTemp(degrees)
	def degreesVS = getTemperatureVS(degreesHE)
	state.cooltemp = getValidCoolSetpoint(degreesVS)
	if (state.cooltemp != degreesVS) {
		// temperature was restricted - convert restricted temperature back to HE format
		degreesHE = getTemperatureHE(degreesVS)
	}
	if (logEnable) log.debug "processing 'setCoolingSetpoint' with ${degreesHE} (originally ${degrees})"
	sendEvent(name: "coolingSetpoint", value: degreesHE, descriptionText: "Cooling setpoint set to ${degreesHE}", isStateChange: true)
	setCommonSetpoint()
	postapi()
}

def off() {
	if (logEnable) log.debug "Executing 'off'"
	state.thermostatMode = 0
	sendEvent(name: "thermostatMode", value: getModeMap()[0], descriptionText: "Thermostat Mode set to ${getModeMap()[0]}", isStateChange: true)
	postapi()
}

def heat() {
	if (logEnable) log.debug "Executing 'heat'"
	state.thermostatMode = 1
	sendEvent(name: "thermostatMode", value: getModeMap()[1], descriptionText: "Thermostat Mode set to ${getModeMap()[1]}", isStateChange: true)
	postapi()
}

def cool() {
	if (logEnable) log.debug "Executing 'cool'"
	state.thermostatMode = 2
	sendEvent(name: "thermostatMode", value: getModeMap()[2], descriptionText: "Thermostat Mode set to ${getModeMap()[2]}", isStateChange: true)
	postapi()
}

def auto() {
	if (logEnable) log.debug "Executing 'auto'"
	state.thermostatMode = 3
	sendEvent(name: "thermostatMode", value: getModeMap()[3], descriptionText: "Thermostat Mode set to ${getModeMap()[3]}", isStateChange: true)
	postapi()
}

def emergencyHeat() {
	if (logEnable) log.error "'emergencyHeat' is not supported by this device"
}

def setSchedule(String json) {
	if (logEnable) log.error "'setSchedule' is not supported by this device driver"
}

def setThermostatMode(String newMode) {
	if (logEnable) log.debug("setThermostatMode(${newMode})")
	switch(newMode) {
		case "off":
			off()
			break
		case "heat":
			heat()
			break
		case "cool":
			cool()
			break
		case "auto":
			auto()
			break
		default:
			if (logEnable) log.error("setThermostatMode: mode '${newMode}' is not supported by this device")
			break
	}
}

def fanOn() {
	if (logEnable) log.debug "Executing 'fanOn'"
	state.thermostatFanMode = 1
	sendEvent(name: "thermostatFanMode", value: getFanModeMap()[1], descriptionText: "Thermostat Fan Mode set to ${getFanModeMap()[1]}", isStateChange: true)
	postapi()
}

def fanAuto() {
	if (logEnable) log.debug "Executing 'fanAuto'"
	state.thermostatFanMode = 0
	sendEvent(name: "thermostatFanMode", value: getFanModeMap()[0], descriptionText: "Thermostat Fan Mode set to ${getFanModeMap()[0]}", isStateChange: true)
	postapi()
}

def fanCirculate() {
	if (logEnable) log.error("'fanCirculate' is not supported by this device")
}

def setThermostatFanMode(String newMode) {
	if (logEnable) log.debug("setThermostatFanMode(${newMode.trim()})")
	switch(newMode.trim()) {
		case "on":
		case "fanOn":
			fanOn()
			break
		case "auto":
		case "fanAuto":
			fanAuto()
			break
		default:
			if (logEnable) log.error("setThermostatFanMode: mode '${newMode}' is not supported by this device")
			break
	}
}

def refresh() {
	if (logEnable) log.debug "Executing 'refresh'"
	getapi()
	if (sensorEnable) {
		if (logEnable) log.warn "Executing 'SENSOR refresh'"
		getSensors()
	}
}

private getapi() {
	def uri = "http://"+getHostAddress()+"/query/info"
	if (logEnable) log.debug("getapi: Executing get api to " + uri)
	if (testMode) uri = "http://127.0.0.1/api/json/utc/now"

	try {
		def requestParams =
		[
			uri:  uri,
			requestContentType: "application/x-www-form-urlencoded",
			contentType: "application/json"
		]
		httpGet(requestParams) { resp ->
			if (resp.status == 200) {
				if (logEnable) log.debug "getapi: httpGet returned: \n${resp.data}"
				parseJsonData(resp.data)
			} else {
				log.error("getapi: httpPost returned http failure code ${resp.status}")
			}
			if (logEnable) log.debug "getapi: getapi() completed"
		}
	} catch (Exception e) {
		if (testMode && (e.message == "Connect to 127.0.0.1:80 [/127.0.0.1] failed: Connection refused (Connection refused)")) {
			return parseJsonData(testDATA())
		}
		log.error "getapi: Call to getapi() failed: ${e.message}"
	}
}

private createModeChangeCommand(){
	def command = "mode=" + state.thermostatMode +
		"&fan=" + state.thermostatFanMode +
		"&heattemp=" + state.heattemp + 
		"&cooltemp=" + state.cooltemp
	return command
}

private postapi() {
	if (logEnable) log.info("postapi: Executing API Control update")
	def uri = "http://"+getHostAddress()+"/control"
	def command = createModeChangeCommand()

	if (logEnable) {
		if (testMode) {
			log.info("postapi: Test Mode: POST request to [${uri}] with command [${command}]")
		} else {
			log.info("postapi: Sending on POST request to [${uri}] with command [${command}]")
		}
	}
	if (testMode) {
		return getapi()
	}

	def postParams = [
		uri: uri,
		contentType: "application/json",
		requestContentType: "application/x-www-form-urlencoded",
		body : command
	]

	try {
		httpPost(postParams) { resp ->
			if (resp.status == 200) {
				if (logEnable) log.debug "postapi: httpPost returned: \n${resp.data}"
				if (resp.data.success == true) {
					if(logEnable) log.debug("postapi: httpGet returned success")
					// update the thermostat state
					getapi()
				} else{
					log.error("postapi: httpGet returned failed")
				}
			} else {
				log.error("postapi: httpPost returned http failure code ${resp.status}")
			}
		}
	} catch (Exception e) {
		log.error "postapi: Call to postapi(${command}) failed: ${e.message}"
	}
}

//helper methods
private getHostAddress() {
  return settings.thermostatIP
}

// driver default methods
def updated() {
  log.debug("updated: Venstar Driver ${VERSION} - Updating parameters")
  initialize()
}

def installed() {
  log.debug("installed: Venstar Driver ${VERSION} - Installed")
}

def initialize() {
	log.debug("initialize: Venstar Driver ${VERSION} - Initializing device ${device.getDeviceNetworkId()}")
	unschedule()
	sendEvent(name: "temperatureDisplayScale", value: getTemperatureScale(), descriptionText: "temperatureDisplayScale set to ${getTemperatureScale()}", isStateChange: true)
	sendEvent(name: "supportedThermostatFanModes", value: ["auto","on"], isStateChange: true)
	sendEvent(name: "supportedThermostatModes", value: ["off","heat","cool","auto"], isStateChange: true)
  
	if (getHostAddress()) {
		// get the initial thermostat state
		if (sensorEnable) {
			def sensorData = getSensors(true)
			createSensors(sensorData)
		} else {
			// remove child sensors if needed
			getChildDevices().each { sensor ->
				if (logEnable) log.debug ("removing disabled sensor ${sensor.deviceNetworkId}")
				deleteChildDevice(sensor.deviceNetworkId)
			}
		}
		getapi()
	} else {
		log.debug("initialize: Venstar IP Address not set")
		return
	}
	def poll = Poll_Rate ? Poll_Rate : "10"
	switch(poll) {
		case "1" :
			runEvery1Minute(refresh)
			log.debug("initialize: Poll Rate set to every 1 minute")
			break
		case "5" :
			runEvery5Minutes(refresh)
			log.debug("initialize: Poll Rate set to every 5 minutes")
			break
		case "10" :
			runEvery10Minutes(refresh)
			log.debug("initialize: Poll Rate set to every 10 minutes")
			break
		case "15" :
			runEvery15Minutes(refresh)
			log.debug("initialize: Poll Rate set to every 15 minutes")
			break
		case "30" :
			runEvery30Minutes(refresh)
			log.debug("initialize: Poll Rate set to every 30 minutes")
			break
		case "60" :
			runEvery1hour(refresh)
			log.debug("initialize: Poll Rate set to every 1 hour")
			break
		case "30" :
			runEvery3hours(refresh)
			log.debug("initialize: Poll Rate set to every 3 hours")
			break
	}
}

private getSensors(dataOnly = false) {
	def uri = "http://"+getHostAddress()+"/query/sensors"
	log.debug("getapi: Executing get api to " + uri)
	if (testMode) uri = "http://127.0.0.1/query/sensors"

	try {
		def requestParams =
		[
			uri:  uri,
			requestContentType: "application/x-www-form-urlencoded",
			contentType: "application/json"
		]
		httpGet(requestParams) { resp ->
			if (resp.status == 200) {
				if (logEnable) log.debug "getSensors: httpGet returned: \n${resp.data}"
				parseJsonData(resp.data)
			} else {
				log.error("getSensors: httpGet returned http failure code ${resp.status}")
			}
			if (logEnable) log.debug "getSensors: getSensors() completed"
		}
	} catch (Exception e) {
		if (testMode && (e.message == "Connect to 127.0.0.1:80 [/127.0.0.1] failed: Connection refused (Connection refused)")) {
			if (dataOnly) return testSensorDATA()
			return parseJsonData(testSensorDATA())
		}
		log.error "getSensors: Call to getSensors() failed: ${e.message}"
	}
}

def findChild(childDNI) {
	getChildDevices()?.find { it.deviceNetworkId == childDNI}
}

def createSensors(sensorData) {
	if (logEnable) log.debug("createSensors: Parsing sensor data: " + sensorData)
	def parentDNI = device.getDeviceNetworkId()
	if (sensorData.sensors) {
		sensorData.sensors.each { sensor ->
			def sensorName = sensor.name.replaceAll("\\s","").replaceAll("\\W","")
			def sensorDNI = "${device.deviceNetworkId}-ep${sensorName}"
			if (sensor.temp != null) {
				if (logEnable) log.debug("createSensors: Found temperature sensor device - " + sensor.name + "  DNI: "+sensorDNI)
				def childDevice = findChild(sensorDNI)
				if (childDevice == null) {
					if (logEnable) log.debug("createSensors: creating temperature Child "+sensorDNI)
					childDevice = addChildDevice("hubitat", "Virtual Temperature Sensor", sensorDNI, [label: "${device.displayName} (temp sensor - ${sensor.name})", isComponent: false])
				} else {
					if (logEnable) log.debug("createSensors: temperatureChild ${sensorDNI} already exists")
				}
				childDevice.setTemperature(sensor.temp)
			} else if (sensor.hum != null) {
				if (logEnable) log.debug("createSensors: Found humidity sensor device - " + sensor.name + "  DNI: "+sensorDNI)
				def childDevice = findChild(sensorDNI)
				if (childDevice == null) {
					if (logEnable) log.debug("createSensors: creating humidity Child "+sensorDNI)
					childDevice = addChildDevice("hubitat", "Virtual Humidity Sensor", "${device.deviceNetworkId}-ep${sensorName}", [label: "${device.displayName} (temp sensor - ${sensor.name})", isComponent: false])
				} else {
					if (logEnable) log.debug("createSensors: humidity Child ${sensorDNI} already exists")
				}
				childDevice.setHumidity(sensor.hum)
			} else {
				if (logEnable) log.debug("createSensors: Found unknown sensor device - " + sensor.name)
			}
		}
	}
}

def testDATA() { 
	def data = '{"name":"Test Data","mode":1,"state":0,"fan":0,"fanstate":1,"tempunits":1,"schedule":0,"schedulepart":255,"away":0,"spacetemp":21.0,"heattemp":21.0,"cooltemp":8.0,"cooltempmin":1.5,"cooltempmax":37.0,"heattempmin":1.5,"heattempmax":37.0,"setpointdelta":2.0,"availablemodes":1}'
	def slurper = new JsonSlurper()
	def result = slurper.parseText(data)
	result.mode = state.thermostatMode ?: 0
	result.state = state.thermostatMode ?: 0
	result.fan = state.thermostatFanMode ?: 0
	result.fanstate = (state.thermostatFanMode && (state.thermostatMode > 0)) ?: 0
	if (state.heattemp != null) result.heattemp = state.heattemp
	if (state.cooltemp!= null) result.cooltemp = state.cooltemp
	return result
}

def testAlertDATA() { 
	def data = '{"alerts": [{"name": "Air Filter","active": false},{"name": "UV Lamp","active": false},{"name": "Service","active": false}]}'
	def slurper = new JsonSlurper()
	def result = slurper.parseText(data)
	return result
}

def testSensorDATA() { 
	def data = '{"sensors": [{"name": "Thermostat","temp": 77},{"name": "Outdoor","temp": 0}]}'
	def slurper = new JsonSlurper()
	def result = slurper.parseText(data)
	return result
}
1 Like