Optional Device in SmartApp - Logic not working?

Not sure if I have missed something, but having an issue with a SmartApp and an optional device.

I have a SmartApp that turns on/off the boiler if heat is required in any of our zones based on the TRV settings and temp in the room etc. We have 3 zones in this house, but are moving soon and will have 5 in the new place.

I have this section of code in my SmartApp to get the temp in the zones.
double zone1NewTemp = theTemp1.currentValue(“temperature”).toDouble()
double zone2NewTemp = theTemp2.currentValue(“temperature”).toDouble()
double zone3NewTemp = theTemp3.currentValue(“temperature”).toDouble()

I have duplicated this from three times to five times to prepare for the two extra zones, but if I leave as just the same thing two more times I get “Cannot invoke method currentValue() on null object” errors - as to be expected. So I have added an “if (theTemp1) double…” to the start of each line (with the correct sensor referenced). All makes sense yes?

No not in ST’s world. theTemp1 is defined and if you print that to the logs I get the name of the temp sensor fine, but if (theTemp1) seems to always return false. theTemp1.getId() returns the device ID fine. if (theTemp1 != null) also returns false.

Any ideas? Am I being an idiot or is ST just broken like always?

Nick

Everything is working fine for me. Not broken here.

Ok so this basic SmartApp to test seems to do what it should…

    definition(
    name: "Test1",
    namespace: "nickb512",
    author: "nickb512",
    description: "",
    category: "",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png"
)

preferences {
	section("Device"){
   	 	input "theswitch", "capability.switch", title: "Switch", multiple: false, required: false
  	}
}

def installed() {
    updated()
}

def updated() {
    unsubscribe()
    subscribe(app, checkSwitch)
}

def checkSwitch(evt) {
	log.debug("checkSwitch()")
    if (theswitch) log.debug("The switch is selected");
}

So there is a chance I’m going code blind. But here is my code, only thing missing is log() writes the test to log.debug but also posts it to a database of mine (so I can see why ST turned the heating on at 3am randomly, as it does). Good chance its something stupid I’m missing, but a second (or third, or fourth etc) pair of eyes would be appreciated…

definition(
    name: "HWS Controller",
    namespace: "nickb512",
    author: "nickb512",
    description: "Smart Home Thermostat",
    category: "Green Living",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")


preferences {
	section("The GUI"){
   	 	input "theGui", "capability.sensor", title: "GUI Device", multiple: true, required: true
  	}
	section("Hot Water System"){
   	 	input "hwSwitch", "capability.switch", title: "Master Hot Water Switch", multiple: false, required: true
   	 	input "hwRelay", "capability.switch", title: "Hot Water Boiler Relay", multiple: false, required: true
   	 	input "hwTemp", "capability.temperatureMeasurement", title: "Hot Water Temperature", multiple: false, required: true
  	}
	section("Central Heating System"){
   	 	input "theSwitch", "capability.switch", title: "Master Central Heating Switch", multiple: false, required: true
   	 	input "theRelay", "capability.switch", title: "Central Heating Boiler Relay", multiple: false, required: true
  	}
	section("Zone 1"){
   	 	input "theStat1", "capability.thermostat", title: "Thermostat", multiple: false, required: false
   	 	input "theTemp1", "capability.temperatureMeasurement", title: "Temp Sensor", multiple: false, required: false
  	}
	section("Zone 2"){
   	 	input "theStat2", "capability.thermostat", title: "Thermostat", multiple: false, required: false
   	 	input "theTemp2", "capability.temperatureMeasurement", title: "Temp Sensor", multiple: false, required: false
  	}
	section("Zone 3"){
   	 	input "theStat3", "capability.thermostat", title: "Thermostat", multiple: false, required: false
   	 	input "theTemp3", "capability.temperatureMeasurement", title: "Temp Sensor", multiple: false, required: false
  	}
	section("Zone 4"){
   	 	input "theStat4", "capability.thermostat", title: "Thermostat", multiple: false, required: false
   	 	input "theTemp4", "capability.temperatureMeasurement", title: "Temp Sensor", multiple: false, required: false
  	}
	section("Zone 5"){
   	 	input "theStat5", "capability.thermostat", title: "Thermostat", multiple: false, required: false
   	 	input "theTemp5", "capability.temperatureMeasurement", title: "Temp Sensor", multiple: false, required: false
  	}
}

def installed() {
	log("Installed with settings: ${settings}")
	initialize()
}

def updated() {
	log("Updated with settings: ${settings}")
	unsubscribe()
	initialize()
}

def initialize() {

    unsubscribe()
    
    subscribe(app, checkCallForHeat)
    subscribe(hwSwitch, "switch", checkHotWater)
    subscribe(theSwitch, "switch", checkCallForHeat)
    
    if (theStat1) subscribe(theStat1, "thermostatMode", checkCallForHeat)
    if (theStat1) subscribe(theStat1, "thermostatSetpoint", checkCallForHeat)
    if (theTemp1) subscribe(theTemp1, "temperature", checkCallForHeat)
    
    if (theStat2) subscribe(theStat2, "thermostatMode", checkCallForHeat)
    if (theStat2) subscribe(theStat2, "thermostatSetpoint", checkCallForHeat)
    if (theTemp2) subscribe(theTemp2, "temperature", checkCallForHeat)
    
    if (theStat3) subscribe(theStat3, "thermostatMode", checkCallForHeat)
    if (theStat3) subscribe(theStat3, "thermostatSetpoint", checkCallForHeat)
    if (theTemp3) subscribe(theTemp3, "temperature", checkCallForHeat)
    
    if (theStat4) subscribe(theStat4, "thermostatMode", checkCallForHeat)
    if (theStat4) subscribe(theStat4, "thermostatSetpoint", checkCallForHeat)
    if (theTemp4) subscribe(theTemp4, "temperature", checkCallForHeat)
    
    if (theStat5) subscribe(theStat5, "thermostatMode", checkCallForHeat)
    if (theStat5) subscribe(theStat5, "thermostatSetpoint", checkCallForHeat)
    if (theTemp5) subscribe(theTemp5, "temperature", checkCallForHeat)
    
    subscribe(theGui, "button", guiButtonHandler)
    subscribe(theSwitch, "timer", chTimerHandler)
    subscribe(hwSwitch, "timer", hwTimerHandler)
    subscribe(hwTemp, "temperature", hwTempHandler)
    
}

def checkHotWater(evt) {

	log("checkHotWater()")
	
    if (hwSwitch.currentValue("switch") == "on") {
        if (hwRelay.currentValue("switch") == "on") {
            log("Hot water required and relay on, no change")
        } else {
            log("Hot water required and relay off, turning relay on")
            hwRelay.on()
        }
    } else {
        if (hwRelay.currentValue("switch") == "on") {
            log("Hot water not required and relay on, turning relay off")
            hwRelay.off()
        } else {
            log("Hot water not required and relay off, no change")
        }
    }
	
}

def checkCallForHeat(evt) {

	log("checkCallForHeat()")
    
    boolean callForHeat = false
    String zone1state = "idle"
    String zone2state = "idle"
    String zone3state = "idle"
    String zone4state = "idle"
    String zone5state = "idle"
    
    if (theTemp1) double zone1NewTemp = theTemp1.currentValue("temperature").toDouble()
    if (theTemp2) double zone2NewTemp = theTemp2.currentValue("temperature").toDouble()
    if (theTemp3) double zone3NewTemp = theTemp3.currentValue("temperature").toDouble()
    if (theTemp4) double zone4NewTemp = theTemp4.currentValue("temperature").toDouble()
    if (theTemp5) double zone5NewTemp = theTemp5.currentValue("temperature").toDouble()
    
    if (theStat1) double zone1OldTemp = theStat1.currentValue("temperature").toDouble()
    if (theStat2) double zone2OldTemp = theStat2.currentValue("temperature").toDouble()
    if (theStat3) double zone3OldTemp = theStat3.currentValue("temperature").toDouble()
    if (theStat4) double zone4OldTemp = theStat4.currentValue("temperature").toDouble()
    if (theStat5) double zone5OldTemp = theStat5.currentValue("temperature").toDouble()
       
    if (theStat1) double zone1Setpoint = theStat1.currentValue("thermostatSetpoint").toDouble()
    if (theStat2) double zone2Setpoint = theStat2.currentValue("thermostatSetpoint").toDouble()
    if (theStat3) double zone3Setpoint = theStat3.currentValue("thermostatSetpoint").toDouble()
    if (theStat4) double zone4Setpoint = theStat4.currentValue("thermostatSetpoint").toDouble()
    if (theStat5) double zone5Setpoint = theStat5.currentValue("thermostatSetpoint").toDouble()
    
    log(zone1NewTemp)
    log(zone2NewTemp)
    log(zone3NewTemp)
    log(zone4NewTemp)
    log(zone5NewTemp)
    
    if (theSwitch.currentValue("switch") == "on") {
    
        log("Master switch on, checking zones")
    	
        if (theStat1) {
            if (theStat1.currentValue("thermostatMode") == "heat") {
                if (zone1NewTemp < zone1Setpoint) {
                    callForHeat = true
                    zone1state = "heating";
                    log("Zone 1 Call for Heat")
                } else {
                    log("Zone 1 satisfied")
                }
            } else {
                log("Zone 1 off")
            }
        }
        
        if (theStat2) {
            if (theStat2.currentValue("thermostatMode") == "heat") {
                if (zone2NewTemp < zone2Setpoint) {
                    callForHeat = true
                    zone2state = "heating";
                    log("Zone 2 Call for Heat")
                } else {
                    log("Zone 2 satisfied")
                }
            } else {
                log("Zone 2 off")
            }
        }
        
        if (theStat3) {
            if (theStat3.currentValue("thermostatMode") == "heat") {
                if (zone3NewTemp < zone3Setpoint) {
                    callForHeat = true
                    zone3state = "heating";
                    log("Zone 3 Call for Heat")
                } else {
                    log("Zone 3 satisfied")
                }
            } else {
                log("Zone 3 off")
            }
        }
        
        if (theStat4) {
            if (theStat4.currentValue("thermostatMode") == "heat") {
                if (zone4NewTemp < zone4Setpoint) {
                    callForHeat = true
                    zone4state = "heating";
                    log("Zone 4 Call for Heat")
                } else {
                    log("Zone 4 satisfied")
                }
            } else {
                log("Zone 4 off")
            }
        }
        
        if (theStat5) {
            if (theStat5.currentValue("thermostatMode") == "heat") {
                if (zone5NewTemp < zone5Setpoint) {
                    callForHeat = true
                    zone5state = "heating";
                    log("Zone 5 Call for Heat")
                } else {
                    log("Zone 5 satisfied")
                }
            } else {
                log("Zone 5 off")
            }
        }
        
    } else {
        log("Master switch off")
    }
	
    if (callForHeat) {
        if (theRelay.currentValue("switch") == "on") {
            log("Heat required and relay on, no change")
        } else {
            log("Heat required and relay off, turning relay on")
            theRelay.on()
        }
    } else {
        if (theRelay.currentValue("switch") == "on") {
            log("Heat not required and relay on, turning relay off")
            theRelay.off()
        } else {
            log("Heat not required and relay off, no change")
        }
    }
    
    if (theStat1) {
        if (zone1NewTemp != zone1OldTemp && theStat1.hasCommand("setTemp")) theStat1.setTemp(zone1NewTemp)
        if (theStat1.currentValue("thermostatOperatingState") != zone1state && theStat1.hasCommand("setState")) theStat1.setState(zone1state)
    }
    
    if (theStat2) {
    	if (zone2NewTemp != zone2OldTemp && theStat2.hasCommand("setTemp")) theStat2.setTemp(zone2NewTemp)
    	if (theStat2.currentValue("thermostatOperatingState") != zone2state && theStat2.hasCommand("setState")) theStat2.setState(zone2state)
    }
    
    if (theStat3) {
   		if (zone3NewTemp != zone3OldTemp && theStat3.hasCommand("setTemp")) theStat3.setTemp(zone3NewTemp)
    	if (theStat3.currentValue("thermostatOperatingState") != zone3state && theStat3.hasCommand("setState")) theStat3.setState(zone3state)
    }
    
    if (theStat4) {
    	if (zone4NewTemp != zone4OldTemp && theStat4.hasCommand("setTemp")) theStat4.setTemp(zone4NewTemp)
    	if (theStat4.currentValue("thermostatOperatingState") != zone4state && theStat4.hasCommand("setState")) theStat4.setState(zone4state)
    }
    
    if (theStat5) {
    	if (zone5NewTemp != zone5OldTemp && theStat5.hasCommand("setTemp")) theStat5.setTemp(zone5NewTemp)
    	if (theStat5.currentValue("thermostatOperatingState") != zone5state && theStat5.hasCommand("setState")) theStat5.setState(zone5state)
    }
    
    theGui.each { gui ->
    	log("Updating GUI")
        if (theTemp1 && zone1NewTemp != gui.currentValue("z1temp") && gui.hasCommand("z1temp")) gui.z1temp(zone1NewTemp)
        if (theTemp2 && zone2NewTemp != gui.currentValue("z2temp") && gui.hasCommand("z2temp")) gui.z2temp(zone2NewTemp)
        if (theTemp3 && zone3NewTemp != gui.currentValue("z3temp") && gui.hasCommand("z3temp")) gui.z3temp(zone3NewTemp)
        if (theTemp4 && zone4NewTemp != gui.currentValue("z4temp") && gui.hasCommand("z4temp")) gui.z4temp(zone4NewTemp)
        if (theTemp5 && zone5NewTemp != gui.currentValue("z5temp") && gui.hasCommand("z5temp")) gui.z5temp(zone5NewTemp)
        if (theStat1 && zone1Setpoint != gui.currentValue("z1set") && gui.hasCommand("z1set")) gui.z1set(zone1Setpoint)
        if (theStat2 && zone1Setpoint != gui.currentValue("z2set") && gui.hasCommand("z2set")) gui.z2set(zone2Setpoint)
        if (theStat3 && zone1Setpoint != gui.currentValue("z3set") && gui.hasCommand("z3set")) gui.z3set(zone3Setpoint)
        if (theStat4 && zone1Setpoint != gui.currentValue("z4set") && gui.hasCommand("z4set")) gui.z4set(zone4Setpoint)
        if (theStat5 && zone1Setpoint != gui.currentValue("z5set") && gui.hasCommand("z5set")) gui.z5set(zone5Setpoint)
	}
    
}

def guiButtonHandler(evt) {
	log("guiButtonHandler()")
    switch (evt.value) {
    	case "choff":
        	ch.off()
        break;
    	case "chon":
        	ch.on()
        break;
    	case "ch30m":
        	ch.on30m()
        break;
    	case "ch1h":
        	ch.on1h()
        break;
    	case "ch2h":
        	ch.on2h()
        break;
    	case "hwoff":
        	hw.off()
        break;
    	case "hwon":
        	hw.on()
        break;
    	case "hw30m":
        	hw.on30m()
        break;
    	case "hw1h":
        	hw.on1h()
        break;
    	case "hw2h":
        	hw.on2h()
        break;
    	case "z1set":
        	theStat1.thermostatSetpoint(evt.data.setpoint)
        break;
    	case "z2set":
        	theStat2.thermostatSetpoint(evt.data.setpoint)
        break;
    	case "z3set":
        	theStat3.thermostatSetpoint(evt.data.setpoint)
        break;
    	case "z4set":
        	theStat4.thermostatSetpoint(evt.data.setpoint)
        break;
    	case "z5set":
        	theStat5.thermostatSetpoint(evt.data.setpoint)
        break;
    }
}

def chTimerHandler(evt) {
	log("chTimerHandler()")
    theGui.chstate(evt.value)
}

def hwTimerHandler(evt) {
	log("hwTimerHandler()")
    theGui.hwstate(evt.value)
}

def hwTempHandler(evt) {
	log("hwTempHandler()")
    theGui.hwtemp(evt.value)
}

theGui is a custom DTH that has buttons for most HWS controls to increase WAF - so she doesn’t have to scroll through the Things list to find each thermostat, or the CH timer etc - she can do it all from one place.

Nick

Ok, penny dropped. It the variable scope.

I take it all back :slight_smile:

Nick

1 Like