Attribute Tile value disappears

I'm still playing around with my custom virtual thermostat. It is now working OK, but while I'm still testing it I have 2 attribute tiles displayed on my dashboard. One is thermostatMode and the other thermostatOperatingState. They both display values when something has just been recently changed, but after a short period of time the thermostatOperatingState one goes blank. The device page still shows the correct value for it, so I don't think my driver code is at fault.

It's not a huge problem and I probably won't have it displayed when I'm happy it's all working as I want, but I'm intrigued as to why it disappears.

Update - a bit more testing seems to suggest it simply doesn't survive a page refresh.

Have you declared the attribute you are customizing at the top of the driver, like this attribute "weather", "string"

If the attribute doesn't exist, or the values of the attribute are outside the acceptable enums for the capability it will not show up on a reload, only on change of value.

I am not declaring the attribute at the moment. I had been using just "idle" and "heating" which were in the original device driver that I used as a basis for mine. It was only today that I discovered that "pending heat" was an acceptable value. But even "idle" and "heating" still "disappeared" in the same way.

But it does sound like that could well be the issue so I will add a declaration in and see if that solves it.

1 Like

Capability thermostat already includes attribute thermostatOperatingState in its schema. Can you post your driver code?

This is the latest:

/**
 *
 *
 */
metadata {
	// Automatically generated. Make future change here.
	definition (name: "GT Thermostat V6 ", namespace: "GT Stat", author: "GT") {
		capability "Thermostat"
        	capability "Temperature Measurement"
		capability "Sensor"
		capability "Actuator"

		command "setTemperature", ["number"]
        
	}
}
	

def installed() {
	sendEvent(name: "temperature", value: 18, unit: "C")
	sendEvent(name: "heatingSetpoint", value: 20, unit: "C")
	sendEvent(name: "thermostatSetpoint", value: 20, unit: "C")
	sendEvent(name: "thermostatMode", value: "off")
	sendEvent(name: "thermostatOperatingState", value: "idle")
	}

def parse(String description) {
}

def evaluate(temp, heatingSetpoint) {
	log.debug "evaluate($temp, $heatingSetpoint"
	def threshold = 0.2
	def current = device.currentValue("thermostatOperatingState")
	def mode = device.currentValue("thermostatMode")
	def heating = false
	def idle = false
    if (mode in ["off"]) {
        idle = true
        sendEvent(name: "thermostatOperatingState", value: "idle")
        }
        
	if (mode in ["heat"]) {
		if (heatingSetpoint - temp >= 0) {
			heating = true
			sendEvent(name: "thermostatOperatingState", value: "heating")
		}
		else if (temp - heatingSetpoint >= threshold) {
			idle = true
            sendEvent(name: "thermostatOperatingState", value: "pending heat")
		}
		sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
	}
}

def setHeatingSetpoint(Double degreesC) {
	log.debug "setHeatingSetpoint($degreesC)"
	sendEvent(name: "heatingSetpoint", value: degreesC)
	evaluate(device.currentValue("temperature"), degreesC)
}



def setThermostatMode(String value) {
	sendEvent(name: "thermostatMode", value: value)
	evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"))
}



def off() {
	sendEvent(name: "thermostatMode", value: "off")
	evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"))
}

def heat() {
	sendEvent(name: "thermostatMode", value: "heat")
	evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"))
}



def poll() {
	null
}



def setTemperature(value) {
	def ts = device.currentState("temperature")
	sendEvent(name:"temperature", value: value)
	evaluate(value, device.currentValue("heatingSetpoint"))
}

Hmmm, that all looks good.

I use the value of thermostatOperatingState in RM to switch the relay that will control my boiler and that works fine. As the value also displays correctly in the device page, is it possible that it is just a problem with how dashboard displays it?

I thought you need to have

attribute "thermostatMode", "string"

Try adding that

ThermostatMode is also included in the thermostat capability

ThermostatMode also displays correctly in dashboard already.

I set up a quick test of the built in ecobee driver.

image

image

Works fine on reload or change.

If you can pm me your devices info from the side menu "i" or review it yourself and see if thermostatOperatingState is set on page load, that would give us a clue.

This is the part off the the devices info that has the thermostat in.

{ "id": 291, "label": "Heating Thermostat", "attr": [ { "humidity": "0", "unit": "%" }, { "supportedThermostatFanModes": "[auto, circulate, on]", "unit": "null" }, { "supportedThermostatModes": "[auto, cool, emergency heat, heat, off]", "unit": "null" }, { "thermostatFanMode": "auto", "unit": "null" }, { "coolingSetpoint": "24.0", "unit": "°C" }, { "thermostatMode": "heat", "unit": "null" }, { "heatingSetpoint": "25.0", "unit": "null" }, { "thermostatOperatingState": "heating", "unit": "null" }, { "thermostatSetpoint": "25.0", "unit": "null" }, { "temperature": "22.6", "unit": "null" } ], "template": "multi" }

Ok, well, that means it "should" show on reload since all that dashboard does is take that attribute and display "heating".

Just out of curiousity, if you hit f12 and view the javascript console are there any errors or anything out of the ordinary.

And just for baseline purposes, what browser and os?

It does the same thing on my Samsung Tablet (Android 7.0 /Chrome) and PC (Windows10/Chrome). I've just tried Edge on the PC and that is the same.

f12 doesn't show anything that looks like an error to me but I'm happy to admit I don't understand any of it anway!

Since it is a custom driver can you try something that is declared like above. TstatOpState or similar and just set it to the same and see if a new custom attribute works. It should.

Curious if something in your code is setting it to empty or unsettling it somewhere. No errors in logging either?

OK, so I declared TstatOpState as you said, and everewhere in my code where I had sendevents for "thermostatOperatingState" I duplicated the line for "TstatOpState". Adding that to the dashboard TstatOpState behaved perfectly. So I then tried declaring "thermostatOperatingState" as well, just in case, but it still behaves the same. This is my new device code:

/**
 *
 *
 */
metadata {
	// Automatically generated. Make future change here.
	definition (name: "GT Thermostat V6 ", namespace: "GT Stat", author: "GT") {
		capability "Thermostat"
        	capability "Temperature Measurement"
		capability "Sensor"
		capability "Actuator"
        attribute "TstatOpState", "string"
        attribute "thermostatOperatingState", "string"
		command "setTemperature", ["number"]
        
	}
}


def installed() {
	sendEvent(name: "temperature", value: 18, unit: "C")
	sendEvent(name: "heatingSetpoint", value: 20, unit: "C")
	sendEvent(name: "thermostatSetpoint", value: 20, unit: "C")
	sendEvent(name: "thermostatMode", value: "off")
	sendEvent(name: "thermostatOperatingState", value: "idle")
	}

def parse(String description) {
}

def evaluate(temp, heatingSetpoint) {
	log.debug "evaluate($temp, $heatingSetpoint"
	def threshold = 0.2
	def current = device.currentValue("thermostatOperatingState")
	def mode = device.currentValue("thermostatMode")
	def heating = false
	def idle = false
    if (mode in ["off"]) {
        idle = true
        sendEvent(name: "thermostatOperatingState", value: "idle")
        sendEvent(name: "TstatOpState", value: "idle")
        }
        
	if (mode in ["heat"]) {
		if (heatingSetpoint - temp >= 0) {
			heating = true
			sendEvent(name: "thermostatOperatingState", value: "heating")
            sendEvent(name: "TstatOpState", value: "heating")
		}
		else if (temp - heatingSetpoint >= threshold) {
			idle = true
            sendEvent(name: "thermostatOperatingState", value: "pending heat")
            sendEvent(name: "TstatOpState", value: "pending heat")
		}
		        sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
	}
}

def setHeatingSetpoint(Double degreesC) {
	log.debug "setHeatingSetpoint($degreesC)"
	sendEvent(name: "heatingSetpoint", value: degreesC)
	evaluate(device.currentValue("temperature"), degreesC)
}




def setThermostatMode(String value) {
	sendEvent(name: "thermostatMode", value: value)
	evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"))
}



def off() {
	sendEvent(name: "thermostatMode", value: "off")
	evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"))
}

def heat() {
	sendEvent(name: "thermostatMode", value: "heat")
	evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"))
}



def poll() {
	null
}



def setTemperature(value) {
	def ts = device.currentState("temperature")
	sendEvent(name:"temperature", value: value)
	evaluate(value, device.currentValue("heatingSetpoint"))
}

I've just found this is in the log for the thermostat:

groovy.lang.MissingMethodException: No signature of method: java.math.BigDecimal.minus() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl) values: [22.4] Possible solutions: minus(java.lang.Number), minus(java.lang.Character), min(java.math.BigDecimal), plus(), signum(), find() on line 45 (setTemperature)

Ok, so we now know dashboard functions fine with a custom attribute.

Behaves the same as before or behaves same as custom attribute?

Just curious now if its something odd with the capability thermostat. Which it could be, but I would expect it to be the opposite. It would show on reload of dashboard, but a change might cause it to go blank.

If you can get to the javascript console, you can go to the network tab and monitor the updates responses and the devices endpoint call. Both will contain the changes to devices that dashboard uses to display.

Attribute values are always strings, so if you're going to do math on them they must be converted first.

The same as before, doesn't survive a refresh.