[RELEASE] New Virtual Thermostat With Device

I wish there was a version for humidity…virtual humidistat with device

I made a modified version of the DTH to handle the humidity. I use it to calculate average value in my Home. It works but I don’t know why in the smartthings app where all the devices are listed it shows the humidity and not the temperature.

Where we can find your DTH with humidity ?

I haven’t published it yet, it’s just a test I made.
Anyway it’s this one:

metadata {
	definition (name: "Virtual Temp/Hum Sensor", namespace: "decarlim", author: "Mario De Carli") {
	    	//capability "Actuator"
	    	//capability "Refresh"
	    	capability "Sensor"
	    	capability "Thermostat"
	    	//capability "Thermostat Heating Setpoint"
	    	capability "Thermostat Mode"
	    	capability "Thermostat Operating State"
	    	//capability "Thermostat Setpoint"
	    	capability "Temperature Measurement"
	    	capability "Relative Humidity Measurement"    	    	
	    	capability "Health Check"

	    	//command "refresh"
	    	//command "poll"
        	//command "offbtn"
	    	//command "heatbtn"
	    	command "setThermostatMode", ["string"]
	    	//command "levelUpDown"
	    	//command "levelUp"
	    	//command "levelDown"
	    	//command "heatingSetpointUp"
	    	//command "heatingSetpointDown"
	    	command "log"
	    	//command "changeMode"
	    	command "setVirtualTemperature", ["number"]
	    	command "setVirtualHumidity", ["number"]
	    	//command "setHeatingStatus", ["boolean"]
	    	//command "setEmergencyMode", ["boolean"]
	    	//command "setHeatingOff", ["boolean"]
	    	attribute "temperatureUnit", "string"
	    	attribute "targetTemp", "string"
	    	attribute "debugOn", "string"
	    	attribute "safetyTempMin", "string"
	    	attribute "safetyTempMax", "string"
	    	attribute "safetyTempExceeded", "string"
	    	attribute "maxHumidity", "number"
	    	attribute "minHumidity", "number"    	    	

	simulator {
       for (int i = 0; i <= 100; i += 10) {
           status "${i}F": "temperature: $i F"
       for (int i = 0; i <= 100; i += 10) {
           status "${i}%": "humidity: ${i}%"
	tiles(scale: 2) {
    multiAttributeTile(name:"temperature", type:"generic", width:6, height:4) {
        tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
            attributeState("temperature", label:'${currentValue}°',
 	    	    	    	[value: 31, color: "#153591"],
 	    	    	    	[value: 44, color: "#1e9cbb"],
 	    	    	    	[value: 59, color: "#90d2a7"],
 	    	    	    	[value: 74, color: "#44b621"],
 	    	    	    	[value: 84, color: "#f1d801"],
 	    	    	    	[value: 95, color: "#d04e00"],
 	    	    	    	[value: 96, color: "#bc2323"]
        tileAttribute("device.multiAttributesReport", key: "SECONDARY_CONTROL") {
            attributeState("multiAttributesReport", label:'${currentValue}' //icon:"st.Weather.weather12",
	    	    	tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
	    	    	    	attributeState("idle",    	    	backgroundColor: "#44B621")
	    	    	    	attributeState("heating",    	backgroundColor: "#FFA81E")
	    	    	    	attributeState("off",    	    	backgroundColor: "#ddcccc")
	    	    	    	attributeState("emergency",    	backgroundColor: "#e60000")
	    	    	tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
	    	    	    	attributeState("off", label:'Off')
	    	    	    	attributeState("heat", label:'Heat')
    valueTile("temperature2", "device.temperature", inactiveLabel: false) {
        state "temperature", label:'${currentValue}°', icon:"st.Weather.weather2",
 	    	    [value: 31, color: "#153591"],
 	    	    [value: 44, color: "#1e9cbb"],
 	    	    [value: 59, color: "#90d2a7"],
 	    	    [value: 74, color: "#44b621"],
 	    	    [value: 84, color: "#f1d801"],
 	    	    [value: 95, color: "#d04e00"],
 	    	    [value: 96, color: "#bc2323"]
    valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
        state "humidity", label:'${currentValue}%', unit:"%", icon:"https://raw.githubusercontent.com/bspranger/Xiaomi/master/images/XiaomiHumidity.png",
            [value: 0, color: "#FFFCDF"],
            [value: 4, color: "#FDF789"],
            [value: 20, color: "#A5CF63"],
            [value: 23, color: "#6FBD7F"],
            [value: 56, color: "#4CA98C"],
            [value: 59, color: "#0072BB"],
            [value: 76, color: "#085396"]
    valueTile("spacer", "spacer", decoration: "flat", inactiveLabel: false, width: 1, height: 1) {
	    state "default", label:''
    valueTile("lastcheckin", "device.lastCheckin", inactiveLabel: false, decoration:"flat", width: 4, height: 1) {
        state "lastcheckin", label:'Last Event:\n ${currentValue}'

    details(["temperature", "humidity", "spacer", "lastcheckin", "spacer", "spacer"])
	/*preferences {
	    	input "resetHistoryOnly", "bool", title: "Reset History Data", description: "", displayDuringSetup: false
	    	input "resetAllData", "bool", title: "Reset All Stored Event Data", description: "", displayDuringSetup: false
def shouldReportInCentigrade() {
    //there is no way to do this dynamically right now, a number of the functions that call this function are compile time evaluated :(
    	return true //Set this to true for Centigrade, false for Fahrenheit  so that enums and colors are correct (due to ST issue of compile time evaluation)
	    /*try {
        	def ts = getTemperatureScale();
    	    retVal = ts == "C"
    } finally {
    	    	return retVal
def installed() {
    log.trace "Executing 'installed'"
def configure() {
    log.trace "Executing 'configure'"
private initialize() {
    log.trace "Executing 'initialize'"

    sendEvent(name:"temperature", value: defaultTemp(), unit: unitString(), displayed: false)
	sendEvent(name:"humidity", value: defaultTemp(), displayed: false)
  	sendEvent(name:"thermostatOperatingState", value: "off")
	sendEvent(name:"thermostatMode", value: "heat")
	state.tempScale = "C"
def getTempColors() {
    	def colorMap
        //getTemperatureScale() == "C"   wantMetric()
    	if(shouldReportInCentigrade()) {
    	    	colorMap = [
    	    	    	// Celsius Color Range
    	    	    	[value: 0, color: "#153591"],
    	    	    	[value: 7, color: "#1e9cbb"],
    	    	    	[value: 15, color: "#90d2a7"],
    	    	    	[value: 23, color: "#44b621"],
    	    	    	[value: 29, color: "#f1d801"],
    	    	    	[value: 33, color: "#d04e00"],
    	    	    	[value: 36, color: "#bc2323"]
    	} else {
    	    	colorMap = [
    	    	    	// Fahrenheit Color Range
    	    	    	[value: 40, color: "#153591"],
    	    	    	[value: 44, color: "#1e9cbb"],
    	    	    	[value: 59, color: "#90d2a7"],
    	    	    	[value: 74, color: "#44b621"],
    	    	    	[value: 84, color: "#f1d801"],
    	    	    	[value: 92, color: "#d04e00"],
    	    	    	[value: 96, color: "#bc2323"]
def unitString() {  return shouldReportInCentigrade() ? "°C": "°F" }
def defaultTemp() { return shouldReportInCentigrade() ? 20 : 70 }
def getTemperature() {
    	return device.currentValue("temperature")
def getHumidity() {
    	return device.currentValue("humidity")
private void done() {
    log.trace "---- DONE ----"
def ping() {
    log.trace "Executing ping"
def parse(data) {
    log.debug "parse data: $data"
def refresh() {
    log.trace "Executing refresh"
    sendEvent(name: "thermostatMode", value: getThermostatMode())
    sendEvent(name: "thermostatOperatingState", value: getOperatingState())
    sendEvent(name: "thermostatSetpoint", value: getThermostatSetpoint(), unit: unitString())
    sendEvent(name: "temperature", value: getTemperature(), unit: unitString())
   	sendEvent(name: "humidity", value: getHumidity(), unit: unitString())
def getThermostatMode() {
    	return device.currentValue("thermostatMode")
def getOperatingState() {
    	return device.currentValue("thermostatOperatingState")
def getThermostatSetpoint() {
    	return device.currentValue("thermostatSetpoint")
def getHeatingSetpoint() {
    	return device.currentValue("heatingSetpoint")
def poll() {
def setThermostatMode(mode) {
    sendEvent(name: "thermostatMode", value: mode)
def log() {
def changeMode() {
    	def val = device.currentValue("thermostatMode") == "off" ? "heat" : "off"
    	sendEvent(name: "thermostatMode", value: val)
		return val
def setVirtualTemperature(temp) {
    	sendEvent(name:"temperature", value: temp, unit: unitString(), displayed: false)
def setVirtualHumidity(humid) {
    	sendEvent(name:"humidity", value: humid, displayed: false)
def setIndividualTemperature(temp, id, name) {
    	switch(id) {
      case 0:
        sendEvent(name:"name1", value: name, displayed: false)
       	sendEvent(name:"temp1", value: temp, unit: unitString(), displayed: false)
      case 1:
        sendEvent(name:"name2", value: name, displayed: false)
      	sendEvent(name:"temp2", value: temp, unit: unitString(), displayed: false)
      case 2:
     	sendEvent(name:"name3", value: name, displayed: false)
      	sendEvent(name:"temp3", value: temp, unit: unitString(), displayed: false)
      case 3:
        sendEvent(name:"name4", value: name, displayed: false)
      	sendEvent(name:"temp4", value: temp, unit: unitString(), displayed: false)
def clearSensorData() {
   	sendEvent(name:"name1", value: null, displayed: false)
    sendEvent(name:"temp1", value: null, unit: unitString(), displayed: false)
    sendEvent(name:"name2", value: null, displayed: false)
    sendEvent(name:"temp2", value: null, unit: unitString(), displayed: false)
    sendEvent(name:"name3", value: null, displayed: false)
    sendEvent(name:"temp3", value: null, unit: unitString(), displayed: false)
    sendEvent(name:"name4", value: null, displayed: false)
    sendEvent(name:"temp4", value: null, unit: unitString(), displayed: false)
def setHeatingStatus(bool) {
    	sendEvent(name:"thermostatOperatingState", value: bool ? "heating" : "idle")
def setEmergencyMode(bool) {
    sendEvent(name: "thermostatOperatingState", value: bool ? "emergency" : "idle")
def setHeatingOff(bool) {
    	sendEvent(name:"thermostatOperatingState", value: bool ? "off": "idle")
def setTemperatureScale(val) {
    	log.debug "set temp scale to: $val"
    	sendEvent(name:"tempScale", value: val, displayed: false)
def setTimings(int today, int yesterday) {
    String todayFormatted = new GregorianCalendar( 0, 0, 0, 0, 0, today, 0 ).time.format( 'HH:mm' )
    String yesterdayFormatted = new GregorianCalendar( 0, 0, 0, 0, 0, yesterday, 0 ).time.format( 'HH:mm' )
    sendEvent(name:"timeOnToday", value: todayFormatted, displayed: true)
    sendEvent(name:"timeOnYesterday", value: yesterdayFormatted, displayed: true)

I have the same complaint about the tiles with my ecobee integration using sandood’s Ecobee Suite Manager. The tile for an external Ecobee sensor shows “No Motion” instead of the temperature. I hope that ST is enhanced to allow choice of data shown in the tile.

You need a custom vid for that, use the cli

You need what? Sorry but I’m pretty new to this

If you want to change what it shows on the tile you need a custom vid

Ok how can I create a custom vid?

Looks really nice thanks for the effort.

Is there a way to schedule the virtual thermostat?

For instance in my home office room I would like to have 23° from 7-16 and 20° the rest of the time on workdays.

I’m currently using Fibaro Home Center 2 which has this functionality in their heating panel.
I’m using underfloor heating thus changing the temp needs to be done a couple of hours in advance.

I can create a virtual Thermostat using you DTH, but how do you link it to temperature and humidity sensors? Also how do you link it to the heating device? Is there a smart app that needs to be used with it?
I figured out how to change the display from C to F.

You need the smart app

Hi Paul_Oliver, I use webcore to gether Temperature and Humidity from other sensors and then i set those value in this virtual thermostat. And it works. My only problem is that I don’t understand how can I create a custom tile display to show temperature instead of humidity (I also would like to not show all settings that I don’t use in the details)

Mine displays the temp on the tile. You can’t hide any of the assets in detail view.

Could post a green shot (anonymized snapshot) of the WebCore piston you are using.

@Paul_Oliver there is a SmartApp that should be with the DH at the same source. Install the SmartApp, then you have to create an instance of it in the ST app.

Go to SmartApps
Click +
Go to the bottom of the list as that is where custom handlers are listed.
Run the Virtual Thermostat Manager SmartApp for creating the thermostat and the manager. During setup you identify the sensors for controlling the “current” values.

Hope that helps.

1 Like

Here it is

But I still don’t get how can I change what to show on tiles and details for my Virtual Thermostat