First it works then it doesn't (aka cannot save a specific state value from app)

I have an app that monitors the state of my A/C via a Honeywell Zigbee Thermostat.

My code worked for a number of days, Then I made a few small changes in the logging and now it doesn't work. What is confusing is the old code doesn't work now either !

  • I made the changes in a text editor
  • Loaded the new App into the Apps code page.
  • I uninstalled the old app version
  • I installed the new apps version.
    Now neither work :frowning:

it appears the code setting the "prevStop" is not working and I can't see why.

    	if(evt.value == "idle"){			// if idle we must be stopped.
        	state.prevcoolStop = state.coolStop
        	long coolStop = now()
        	state.coolStop = coolStop

I realize the 1st time through the code state.coolStop will be "null" but after that is should / did work.

Thanks
John

I changed the app name in the "definition name:".

Errors:


Note PrevStop, Start, Stop added after 16:17:15 readings

APP Status page:

CODE:

//  Percent ON Time App v004e

definition(
    name: "Percent ON Time Calculator v4",
    namespace: "hubitat",
    author: "JohnRob",
    description: "Percent ON Time Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            input "thisName", "text", title: "Name this Percent ON Time Calculator", submitOnChange: true
            if(thisName) app.updateLabel("$thisName")
            input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
            input "thermostatCtrl", "capability.thermostat", title: "Select Thermostat to Monitor", submitOnChange: true, required: true, multiple: false
            input "OutsideTemp", "capability.temperatureMeasurement", title: "Select Temperature to Log", submitOnChange: true, required: true, multiple: false
            input "SunBrightness", "capability.illuminanceMeasurement", title: "Select Illumination to Log", submitOnChange: true, required: true, multiple: false
        } // section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
    if (logEnable) log.debug (updated)
}

def initialize() {
    subscribe(thermostatCtrl,"thermostatOperatingState", handlerAC)
    subscribe(OutsideTemp,"temperature", handlerTmp)
    subscribe(SunBrightness,"illuminance", handlerIll)
}

def handlerTmp(evt) {
	state.Temperature = evt.value
}

def handlerIll(evt) {
	state.Illumination = evt.value
}

def handlerAC(evt) {

    if(evt.value == "cooling"){
        long coolStart = now()
        state.coolStart = coolStart
    }  // end if cooling

   else{
    	if(evt.value == "idle"){			// if idle we must be stopped.
        	state.prevcoolStop = state.coolStop
        	long coolStop = now()
        	state.coolStop = coolStop

// next line is line 96
        	def onTime = Math.round((Long.valueOf(state.coolStop) - Long.valueOf(state.coolStart))/1000)

        	def period = Math.round((Long.valueOf(state.coolStop) - Long.valueOf(state.prevcoolStop))/1000)

       			if (logEnable) log.debug ",Period=,${period}"
       			if (logEnable) log.debug " _G_ prevcoolStart ,${state.prev_coolStart}"

        	def percentON = Math.round(onTime/period*100)

        		if (logEnable) log.debug ",PercentOn=,${percentON}"
                if (logEnable) log.debug ",Temperature=,${state.Temperature}"
                if (logEnable) log.debug ",Illumination=,${state.Illumination}"
                if (!logEnable) log.info "v004e ,${state.Illumination}, ${state.Temperature}, ${period}, ${percentON},${state.prevcoolStop},${state.coolStart},${state.coolStop}"
        }  //if idle
	}  //else
}  // handlerAC

//  --- eof ---

I can't explain the odd behaviour of the app continuing to not work, regardless of the code in place, but I expect you will want to specifically handle a null or empty value on Line 96, catering for it as part of the calculation, either by not populating onTime until you have all the values recorded, or dealing with it using some other logic. I'm guessing it will be either the valueOf or the round methods (or maybe both) that don't like the null value.

Thank you, will do.
As update, I changed prevcoolStop to prev_coolStop with no change in issue.

You don't need all of this Long.valueOf() stuff. Just do the arithmetic directly. Also, at that particular place on line 96 you already have the local variable coolStop with the value you want.

So, try this:

int onTIME = Math.round((coolStop - state.coolStart)/1000)
int onPeriod = Math.round((coolStop - state.prevcoolStop)/1000)

In your initialize method do something like this:

if(!state.prevcoolStop) state.prevcoolStop = now()

That way, you know it always has a value, but the test prevents initialize from wiping it out after the app's been installed the first time. You should probably do this with state.coolStart also.

Cleaner but not the solution...

It seems the prev_coolStop is not sticking. The debug log shows it as a null while the events show it as a epoch value.

Note: This WAS working back at V004e and then stopped. I could see no changes that would cause this and reverted to a saved (text editor) version.

I tried shutdown (power OFF) and restarting.
I even dropped back to 2.3.2.139
Nothing gives me a clue what is happening.
I guess my next step is to revert to a backup database.

John

/*
Percent ON Time App v006a

Monitor's A/C cooling & idle times and reports:
	% cooling time over xxx hours (or cycles)
	Average Cycle time.
	Total ON time for the day (not implemented as of v000c)


TODO:
	Total %ON for the day.
	Max and Min %ON for the day


Notes:
thermostatOperatingState - ENUM
	["heating", "pending cool", "pending heat", "vent economizer", "idle", "cooling", "fan only"]

2022-06-12
	v002a working functions:
	- read thermostat, calc on time, calculate period, calc % on time.

2022-07-14
v004a adding subscription and logging of Outside temp and maybe illumination
v004c changed logging to one line with "," to easily parse in excel.
v004d added previous Start, start and idle to output line
v006a included changes suggested by bravenel https://community.hubitat.com/t/first-it-works-then-it-doesnt-aka-cannot-save-a-specific-state-value-from-app/98126/5


*/

definition(
    name: "Percent ON Time Calculator v6a",
    namespace: "hubitat",
    author: "JohnRob",
    description: "Percent ON Time Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            input "thisName", "text", title: "Name this Percent ON Time Calculator", submitOnChange: true
            if(thisName) app.updateLabel("$thisName")
            input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
            input "thermostatCtrl", "capability.thermostat", title: "Select Thermostat to Monitor", submitOnChange: true, required: true, multiple: false
            input "OutsideTemp", "capability.temperatureMeasurement", title: "Select Temperature to Log", submitOnChange: true, required: true, multiple: false
            input "SunBrightness", "capability.illuminanceMeasurement", title: "Select Illumination to Log", submitOnChange: true, required: true, multiple: false
        } // section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
    if (logEnable) log.debug (updated)
}

def initialize() {
    subscribe(thermostatCtrl,"thermostatOperatingState", handlerAC)
    subscribe(OutsideTemp,"temperature", handlerTmp)
    subscribe(SunBrightness,"illuminance", handlerIll)
	if(!state.prev_coolStop) state.prev_coolStop = now()
	if(!state.coolStart) state.coolStart = now()
	if(!state.coolStop) state.coolStop = now()

}

def handlerTmp(evt) {
	state.Temperature = evt.value
}

def handlerIll(evt) {
	state.Illumination = evt.value
}

def handlerAC(evt) {
    log.debug  ", event Value= ,${evt.value}"
    if(evt.value == "cooling"){
        long coolStart = now()
        state.coolStart = coolStart
    }  // end if cooling

   else{
    	if(evt.value == "idle"){			// if idle we must be stopped.
        	state.prev_coolStop = state.coolStop
        	long coolStop = now()
        	state.coolStop = coolStop

            log.debug "prev_coolStop, coolStart, coolStop, = ${state.prev_coolStop},${state.coolStart},${state.coolStop}"


				int onTIME = Math.round((coolStop - state.coolStart)/1000)
				int onPeriod = Math.round((coolStop - state.prevcoolStop)/1000)


       			if (logEnable) log.debug ",Period=,${period}"
       			if (logEnable) log.debug " _G_ prevcoolStart ,${state.prev_coolStart}"

        	def percentON = Math.round(onTime/period*100)

        		if (logEnable) log.debug ",PercentOn=,${percentON}"
                if (logEnable) log.debug ",Temperature=,${state.Temperature}"
                if (logEnable) log.debug ",Illumination=,${state.Illumination}"
                if (!logEnable) log.info "v006a ,${state.Illumination}, ${state.Temperature}, ${period}, ${percentON},${state.prev_coolStop},${state.coolStart},${state.coolStop}"
        }  //if idle
	}  //else
}  // handlerAC

//  --- eof ---

When you have errors, nothing should be expected to work. State may not be written after an error like in your logs. Fix those first.

Perhaps it is getting its null value from the previous state.coolStop. Add more log.debug, at each step, until you find where the null is coming from. Break it down into small steps.

Try this:

Long prev_Cool = state.coolStop|
Long coolStop = now()|

Then do the math with those two local variables. Assign both to state later.

Restoring a db is not indicated. But I assume that you have Removed this app instance, and installed a new one?

Restoring a db is not indicated. But I assume that you have Removed this app instance, and installed a new one? I've both restored and old firmware .139 then went back to .141. I restored the database from an older version then deleted the old version from APPS then APPS Code. Installed the version below.

No luck...

It appears the error is in failure to update "

long coolStop = now()

between _D and _E

/*
Percent ON Time App v006b
   notes deleted for brevity.
*/

definition(
    name: "Percent ON Time Calculator v6b",
    namespace: "hubitat",
    author: "JohnRob",
    description: "Percent ON Time Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            input "thisName", "text", title: "Name this Percent ON Time Calculator", submitOnChange: true
            if(thisName) app.updateLabel("$thisName")
            input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
            input "thermostatCtrl", "capability.thermostat", title: "Select Thermostat to Monitor", submitOnChange: true, required: true, multiple: false
            input "OutsideTemp", "capability.temperatureMeasurement", title: "Select Temperature to Log", submitOnChange: true, required: true, multiple: false
            input "SunBrightness", "capability.illuminanceMeasurement", title: "Select Illumination to Log", submitOnChange: true, required: true, multiple: false
        } // section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
    if (logEnable) log.debug (updated)
}

def initialize() {
    subscribe(thermostatCtrl,"thermostatOperatingState", handlerAC)
    subscribe(OutsideTemp,"temperature", handlerTmp)
    subscribe(SunBrightness,"illuminance", handlerIll)
	if(!state.prev_coolStop) state.prev_coolStop = now()
	if(!state.coolStart) state.coolStart = now()
	if(!state.coolStop) state.coolStop = now()

}

def handlerTmp(evt) {
	state.Temperature = evt.value
}

def handlerIll(evt) {
	state.Illumination = evt.value
}

def handlerAC(evt) {
    log.debug  ", event Value= ,${evt.value}"
    if(evt.value == "cooling"){
        long coolStart = now()
        state.coolStart = coolStart
    }  // end if cooling

   else{
    	if(evt.value == "idle"){			// if idle we must be stopped.
            log.debug "_A_ now= ${now()}"
            log.debug "_B_ ${state.coolStop}"
    		log.debug "_C_ ${state.prev_coolStop}"
        	state.prev_coolStop = state.coolStop
            log.debug "_D_ ${state.coolStop}"
        	long coolStop = now()
            log.debug "_E_prev_coolStop, coolStart, coolStop, = ${state.prev_coolStop},${state.coolStart},${state.coolStop}"

			int onTIME = Math.round((coolStop - state.coolStart)/1000)
			int onPeriod = Math.round((coolStop - state.prevcoolStop)/1000)
        	int percentON = Math.round(onTime/period*100)
        	if (logEnable) log.debug ",PercentOn=,${percentON}"
            if (logEnable) log.debug ",Temperature=,${state.Temperature}"
            if (logEnable) log.debug ",Illumination=,${state.Illumination}"
            if (!logEnable) log.info "v006b ,${state.Illumination}, ${state.Temperature}, ${period}, ${percentON},${state.prev_coolStop},${state.coolStart},${state.coolStop}"
        }  //if idle
	}  //else
}  // handlerAC

//  --- eof ---

The line calculating percentOn uses "period" instead of "onPeriod". You also had two different spellings for state.prev_coolStop (that one and state.prevCoolStop). Typos will kill your code, that's for sure. I know from painful experience.

I'm not sure what else was wrong with the code you posted above, but this code works as expected:

def handlerAC(evt) {
    log.debug  "event Value=$evt.value"
    if(evt.value == "cooling") state.coolStart = now()
    else if(evt.value == "idle") {			// if idle we must be stopped.
        state.prev_coolStop = state.coolStop
        state.coolStop = now()
 		int onTIME = (state.coolStop - state.coolStart)/1000
		int onPeriod = (state.coolStop - state.prev_coolStop)/1000
        int percentON = onTIME/onPeriod*100
		log.info "onTIME=$onTIME, onPeriod=$onPeriod, percentON=$percentON"
	}  //if idle
}  // handlerAC

Here is a sample log from it:

1 Like

Thank you for your help, however either I'm getting senile or there is something wrong with my hub.

I copied your code and:

  • deleted my app from the apps page
  • deleted the app code from the app code page
  • created a new app using your code in the app code page
  • created a app instance in the apps page.

Below are my results and the code..... I'm at a loss for how to proceed. If this were a PC I would consider wiping everything and restarting. I don't know about the hub. Or maybe there is still a way to remedy the issue.

I'm converting the code to test with a contact sensor will find more about this error with the ability of creating faster input changes.

definition(
    name: "Percent ON NOW Test v020a",
    namespace: "hubitat",
    author: "JohnRob",
    description: "Percent ON Time Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            input "thermostatCtrl", "capability.thermostat", title: "Select Thermostat to Monitor", submitOnChange: true, required: true, multiple: false
        } //  section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
    if (logEnable) log.debug (updated)
}

def initialize() {
    subscribe(thermostatCtrl,"thermostatOperatingState", handlerAC)
}


def handlerAC(evt) {
    log.debug  "event Value=$evt.value"
    if(evt.value == "cooling") state.coolStart = now()
    else if(evt.value == "idle") {			// if idle we must be stopped.
        state.prev_coolStop = state.coolStop
        state.coolStop = now()
 		int onTIME = (state.coolStop - state.coolStart)/1000
		int onPeriod = (state.coolStop - state.prev_coolStop)/1000
        int percentON = onTIME/onPeriod*100
		log.info "onTIME=$onTIME, onPeriod=$onPeriod, percentON=$percentON"
	}  //if idle
}  // handlerAC

//  --- eof ---

Take these steps: Download a backup of your hub. Soft Reset, Restore from the backup. Install this code in Apps Code. Install the app. Use virtual thermostat, and alternate its temperature above and below the Cooling Setpoint. Observe logs. Report outcome...

definition(
    name: "Percent ON Time Calculator v6b",
    namespace: "hubitat",
    author: "JohnRob",
    description: "Percent ON Time Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            input "thisName", "text", title: "Name this Percent ON Time Calculator", submitOnChange: true
            if(thisName) app.updateLabel("$thisName")
            input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
            input "thermostatCtrl", "capability.thermostat", title: "Select Thermostat to Monitor", submitOnChange: true, required: true, multiple: false
            input "OutsideTemp", "capability.temperatureMeasurement", title: "Select Temperature to Log", submitOnChange: true, required: true, multiple: false
            input "SunBrightness", "capability.illuminanceMeasurement", title: "Select Illumination to Log", submitOnChange: true, required: true, multiple: false
        } // section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
    if (logEnable) log.debug (updated)
}

def initialize() {
    subscribe(thermostatCtrl,"thermostatOperatingState", handlerAC)
    subscribe(OutsideTemp,"temperature", handlerTmp)
    subscribe(SunBrightness,"illuminance", handlerIll)
	if(!state.prev_coolStop) state.prev_coolStop = now()
	if(!state.coolStart) state.coolStart = now()
	if(!state.coolStop) state.coolStop = now()

}

def handlerTmp(evt) {
	state.Temperature = evt.value
}

def handlerIll(evt) {
	state.Illumination = evt.value
}

def handlerAC(evt) {
    log.debug  "event Value=$evt.value"
    if(evt.value == "cooling") state.coolStart = now()
    else if(evt.value == "idle") {			// if idle we must be stopped.
        state.prev_coolStop = state.coolStop
        state.coolStop = now()
 		int onTIME = (state.coolStop - state.coolStart)/1000
		int onPeriod = (state.coolStop - state.prev_coolStop)/1000
        int percentON = onTIME/onPeriod*100
		log.info "onTIME=$onTIME, onPeriod=$onPeriod, percentON=$percentON"
	}  //if idle
}  // handlerAC

Thank you the code in the above post works, thank you.** However I'm still confused.

** I even set it back to the A/C thermostat and it still worked.

I had stripped down my code to the bare minimum, changing the thermometer to a contact sensor on my desk.

After the soft reset and restore but before I tried your code. I tested the above mentioned contact code (the same calculations but with a contact input.
It did not work.

I tried changing

  • int percentON = onTIME/onPeriod*100
    to
  • int percentON = (onTIME/onPeriod)*100

It worked ! .... I thought I found the solution. But no I tired it again after doing something I don't recall. Not in the code but likely in the Apps page. I might have removed the App and code and restarted.
Not it didn't work... the same code (unedited).

So either I'm really missing something OR my hub has a "soft" issue OR there is some odd bug I've just happen to hit.

Thank you so much for your help. I'll watch it and hope I don't run into it again.

John

There is nothing wrong with your hub. Cockpit errors...

That's embarrassing :frowning:

Is it problematic if I change the Apps Code while the App is "active"?

No, I do this all of the time.

1 Like