App: Device Tracker, Multiple Device On/Off Times, On Counts, Notifier, Battery Levels, Switches, Contacts, Temperatures, Power, etc.. With variables access

Great work! I modified the child app code to add tracking of my Nest thermostats when in either heating or cooling modes with just a little effort. Thank you @kampto ! Below are the few sections of the code that I modified. Had to make some minor tweaks because I wanted to track only heating or cooling at one time, and both would call the offHandler() when the thermostat goes back to idle. I use [RELEASE] Google SDM API - Nest integration to link my Nest devices.

 section {
       label title: "<b>1. Enter a name for this child App</b>", required: true, submitOnChange: true, width: 4
       input name: "capabilitySelect", type: "enum", title: "<b>2. Select a capability you want to track.</b>", required: true, description: "Default = SWITCH On", defaultValue: "1", multiple: false, options:[["1":"SWITCH On"], ["2":"CONTACT Open"], ["3":"CONTACT Closed"], ["4":"POWER Monitor"], ["5":"THERM COOLING Monitor"], ["6":"THERM HEATING Monitor"]], width: 6, submitOnChange: true, displayDuringSetup: false
       if (capabilitySelect == "1") {input "lights", "capability.switch", title: "<b>3. Select Switch Devices to Track Switch On Time</b>", required: true, multiple: true, submitOnChange: true, width: 6}
        else if (capabilitySelect == "2") {input "lights", "capability.contactSensor", title: "<b>3. Select Contact Devices to Track Contact Open Time</b>", required: true, multiple: true, submitOnChange: true, width: 6}
        else if (capabilitySelect == "3") {input "lights", "capability.contactSensor", title: "<b>3. Select Contact Devices to Track Contact Closed Time</b>", required: true, multiple: true, submitOnChange: true, width: 6}
        else if (capabilitySelect == "4") {input "lights", "capability.powerMeter", title: "<b>3. Select Power Meter Devices to Track On Time</b>", required: true, multiple: true, submitOnChange: true, width: 6}
        else if (capabilitySelect == "5") {input "lights", "capability.thermostatOperatingState", title: "<b>3. Select Thermostats to Track Cooling Time</b>", required: true, multiple: true, submitOnChange: true, width: 6}
        else if (capabilitySelect == "6") {input "lights", "capability.thermostatOperatingState", title: "<b>3. Select Thermostats to Track Heating Time</b>", required: true, multiple: true, submitOnChange: true, width: 6}
        else {input "lights", "capability.switch", title: "<b>3. Select Switch Devices to Track Switch On Time</b>", multiple: true, submitOnChange: true, width: 6}
        
          lights.each {dev ->
			    if(!state.lights["$dev.id"]) {
                    if (capabilitySelect == "1") {state.lights["$dev.id"] = [start: dev.currentSwitch == "on" ? now() : 0, total: 0, var: "", time: ""]}
                    else if (capabilitySelect == "2") {state.lights["$dev.id"] = [start: dev.currentContact == "open" ? now() : 0, total: 0, var: "", time: ""]}
                    else if (capabilitySelect == "3") {state.lights["$dev.id"] = [start: dev.currentContact == "closed" ? now() : 0, total: 0, var: "", time: ""]}
                    else if (capabilitySelect == "4") {state.lights["$dev.id"] = [start: dev.currentPower >= powerThreshold ? now() : 0, total: 0, var: "", time: ""]}
                    else if (capabilitySelect == "5") {state.lights["$dev.id"] = [startCool: dev.currentThermostatOperatingState == "cooling" ? now() : 0, total: 0, var: "", time: ""]}
                    else if (capabilitySelect == "6") {state.lights["$dev.id"] = [startHeat: dev.currentThermostatOperatingState == "heating" ? now() : 0, total: 0, var: "", time: ""]}
                    endif
				    state.lightsList += dev.id   
                    }
			    }
                
String displayTable() {
  	if(state.reset) {
		def dev = lights.find{"$it.id" == state.reset}
        if (capabilitySelect == "1") {state.lights[state.reset].start = dev.currentSwitch == "on" ? now() : 0}
        else if (capabilitySelect == "2") {state.lights[state.reset].start = dev.currentContact == "open" ? now() : 0}
        else if (capabilitySelect == "3") {state.lights[state.reset].start = dev.currentContact == "closed" ? now() : 0}
        else if (capabilitySelect == "4") {state.lights[state.reset].start = dev.currentPower >= powerThreshold ? now() : 0}
        else if (capabilitySelect == "5") {state.lights[state.reset].startCool = dev.currentThermostatOperatingState == "cooling" ? now() : 0}
        else if (capabilitySelect == "6") {state.lights[state.reset].startHeat = dev.currentThermostatOperatingState == "heating" ? now() : 0}
        endif
 if (capabilitySelect == "1") { str += "<td style='color:${dev.currentSwitch == "on" ? "green" : "red"}'>$time</td>" + "<td title='State $dev'>$dev.currentSwitch</td>"}
        else if (capabilitySelect == "2") {str += "<td style='color:${dev.currentContact == "open" ? "green" : "red"}'>$time</td>" + "<td title='State $dev'>$dev.currentContact</td>" }
        else if (capabilitySelect == "3") {str += "<td style='color:${dev.currentContact == "closed" ? "green" : "red"}'>$time</td>" + "<td title='State $dev'>$dev.currentContact</td>"} 
        else if (capabilitySelect == "4") {str += "<td style='color:${dev.currentPower >= powerThreshold ? "green" : "red"}'>$time</td>" + "<td title='State $dev'>$dev.currentPower</td>"} 
        else if (capabilitySelect == "5") {str += "<td style='color:${dev.currentThermostatOperatingState == "cooling" ? "green" : "red"}'>$time</td>" + "<td title='State $dev'>$dev.currentThermostatOperatingState</td>" }
        else if (capabilitySelect == "6") {str += "<td style='color:${dev.currentThermostatOperatingState == "heating" ? "green" : "red"}'>$time</td>" + "<td title='State $dev'>$dev.currentThermostatOperatingState</td>" }    
        endif 
void initialize() {
     if (capabilitySelect == "1") {
	subscribe(lights, "switch.on", onHandler)
	subscribe(lights, "switch.off", offHandler)
    }
    else if (capabilitySelect == "2") {
	subscribe(lights, "contact.open", onHandler)
    subscribe(lights, "contact.closed", offHandler)
    }
    else if (capabilitySelect == "3") {
	subscribe(lights, "contact.open", offHandler)
	subscribe(lights, "contact.closed", onHandler)
    }
    else if (capabilitySelect == "4") { //// Added ver 1.0.2
	subscribe(lights, "power.< powerThreshold", offHandler)
	subscribe(lights, "power.>= powerThreshold", onHandler)  
    }
    else if (capabilitySelect == "5") {
	subscribe(lights, "thermostatOperatingState.cooling", onHandler)
    subscribe(lights, "thermostatOperatingState.idle", offHandler)
    }
    else if (capabilitySelect == "6") {
	subscribe(lights, "thermostatOperatingState.heating", onHandler)
    subscribe(lights, "thermostatOperatingState.idle", offHandler)
    }
    endif
void offHandler(evt) {
    if (state.curTrack == "cool") {
        if (capabilitySelect == "5") {
            state.lights[evt.device.id].total += now() - state.lights[evt.device.id].startCool
        }
        else {
            return
        }
        endif
    } 
    else if (state.curTrack == "heat") {
        if (capabilitySelect == "6") {
            state.lights[evt.device.id].total += now() - state.lights[evt.device.id].startHeat
        }
        else {
            return
        }
        endif
    }
    else {
        state.lights[evt.device.id].total += now() - state.lights[evt.device.id].start
    }
    endif  
void resetTimers(evt = null) {
    if (logEnableBool) {log.debug "App: ${app.label} - All Timers Reset to 0 Happening ..........."}
	state.lights.each{k, v ->
		def dev = lights.find{"$it.id" == k}
        if (capabilitySelect == "1") {state.lights[k].start = dev.currentSwitch == "on" ? now() : 0}
        else if (capabilitySelect == "2") {state.lights[k].start = dev.currentContact == "open" ? now() : 0}
        else if (capabilitySelect == "3") {state.lights[k].start = dev.currentContact == "closed" ? now() : 0}
        else if (capabilitySelect == "4") {state.lights[k].start = dev.currentPower >= powerThreshold ? now() : 0} //// Added ver 1.0.2
        else if (capabilitySelect == "5") {state.lights[k].startCool = dev.currentThermostatOperatingState == "cooling" ? now() : 0}
        else if (capabilitySelect == "6") {state.lights[k].startHeat = dev.currentThermostatOperatingState == "heating" ? now() : 0}
        endif
		state.lights[k].time = new Date().format("MM-dd-yyyy ${location.timeFormat == "12" ? "h:mm:ss a" : "HH:mm:ss"}")
		state.lights[k].total = 0
    }
	if(resetVar) setGlobalVar(resetVar, false)
    refreshHandler() //// Added ver 1.0.3
}

Added Updates in Seconds. And variables only get updated when device in Active/On

This you can do using the attached variable and building a Rule in Rule Machine. Will need to use a Number variable and track in total Seconds so Rule Machine can use the variable. Set rule to do something if total seconds threshold is reached.

2 Likes

@kampto Thank you. You wrote

This you can do using the attached variable and building a Rule in Rule Machine. Will need to use a Number variable and track in total Seconds so Rule Machine can use the variable. Set rule to do something if total seconds threshold is reached.

I don't understand where I would look to review how long my dehumid runs each time it runs. Where is that data stored? Is it in the log for this RM rule?

You need to go to Settings / Hub Variables and make a Number Variable. On time will be stored there. Then build a rule to do something with the variable. So like this one I just build as an example. If ContactClosedTimeNum variable reaches 3600 seconds turn off the switch.

Rule Example

Variable is added to the tracker app.

You can also add the variable to the dashboard in a tile to look at it without opening the app.

@kampto

Magic, thank q - I'll take a look in a bit.

We have some improvements now on github child app

  1. You can turn off all hub logging
  2. You can choose what items to do Auto periodic variable updates in Minutes or Seconds, and they only update when 'On' which was a request.
  3. Now has a cumulative time tracker that can be viewed in the app and not part of the regular resets. I suppose it could keep counting forever till manually reset.
  4. And a few other things

2 Likes

This works really well - thank you.

Is it possible to have the cumulative stored as a seperate variable? And also allow this to be reset in the same way as the standard total?

If so, that's my energy monitoring solution at 100% with the help of webcore and this app.

Cheers!

It is, the table is running out of real estate on the X-Axis, :rofl:
Will see what I can do.

1 Like

And now you see why my variables are on a whole different page for the energy app :rofl::rofl:

2 Likes

I meant a seperate linked variable for the cumulative.

With a seperate way of resetting the cumulative, from outside the app.

Cheers!

/ cheeky requests

@djh_wolf For remote resets: Is it Ok if the same existing boolean variable (your Radreset) can be toggled in the app to reset both Active and Cumulative or just the Active(like it is now)? Then same variable can be used, just toggle what it resets in app.
Like this:

I could stuff all the device times and counts into a single string variable if anyone knows if there's a way to parse the pieces out in RM and/or webcore. Although might be to complicated to use. Otherwise I will need to add a separate variable column to carry the cumulative time data.
Or toggle what the existing variable holds, Active or Cumulative
And there will certainly be a use case to put the 'Counts On' number into a variable at some point. :smiley:

@kampto

For my usage, it would need to have one variable for current, another for cumulative, and have it possible to reset both seperately please.

A 'count on' would be amazing, again, with the data accessible externally (with reset options).

Ticks all my boxes =)

Hi @kampto
The new version (1.1.3) was the first one that updating the child app code ended up with an error and made me delete and re-create the child app instance.
Just wanted to let you know.
Thank you for the work you have done.

1 Like

@Kampto - I got this error msg. when trying to update the latest Child app (1.1.3). Does the error rest with me? I haven't done anything to the existing Parent app installed poreviously that was working with your prior Child app installed. Thoughts?

I have not seen that. Maybe delete both child and parent Apps code and try adding new again?
Note: There's a lot of changes so deleting the child app as stated above and recreating after pasting new code will probably be necessary.

Ditto

Had a bit of time to fill in some requests for the app. Now has variables for tracking and resetting Device total On counts, periodic times, and cumulative times. And some other stuff.
If you have an existing child app set up you probably need to delete it, repaste the child app github code and remake child app.

2 Likes

@kampto

amazing, that's exactly what I meant. I'll test it when I get a chance, but initially it looks ace!

You. Legend!

Cheers.

Thx @kampto. Appreciate what you’ve created here & your willingness to code new features. I look fwd. to unlocking its potential even further.

Added Voltage and Temperature tracking. Im using Voltage to see how long my solar battery's stay in float mode if at all.
Same as Power need to set a threshold value where the tracking starts. Fixed a bug with the power tracking.
Temperature example:

2 Likes