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

Yes, for now the entire table is tied to the options so a seperate child app is needed.

I can add an option to disable logs if that's needed. Its easy to do. An option for Variable refresh on devices that are Only Active is doable, but I need to figure it.
Checking Logs / App stats will give you an idea of the resources its using. In my case its not much.

Update: Added option to disable App based logs. But when a variable gets updated it will log that variable change, nothing I can do about that, its built into hubitat.

2 Likes

Hi @kampto

If this...

... Could be duplicated to read "update all variables every xx seconds whilst device is on", it would be perfect.

Appreciate your time so far! =.)

Here's a use case I'm trying to sort through.
Warning @kampto : I'm not a coder (nor do I play one on TV)

I'd like to be able to easily define a custom time span (i.e: 2 weeks) in which I could set up devices & let usage/active time tracking event timers freely run in order to capture everything the sensors generated during that time to create an initial general usage/activity baseline. For example, based upon using room occupancy sensors - how many hours did someone spend in various rooms over the course of those two weeks; using pressure sensors - how long has someone been sitting overall, where have they been sitting most of the time, etc.

With a captured baseline to start from I'd like to be able to (automatically) invoke simple math to determine simple things for now, such as daily/weekly/monthly/custom time frame averages, prioritization/sorting based on time, etc. Trend analysis/usage patterns is what I'm ultimately going for. A poor mans AI for sure - but its a start. Oh, & icing on the cake would be a streamlined way to graph this data (via Quick Chart?) and be able to simply drop into a dashboard (I love SharpTools).

Many more thoughts/use cases bouncing around in my fat head; any & all thoughts/comments/critiques/criticisms welcomed.

1 Like

@mark1 @kampto
I'm seeking similar data that mark1 is seeking - with the added granularity of how long was each session (vs total cumulative duration of all sessions).

My specific use case is to establish a baseline for how long my dehumidifier runs in normal operation, so that I can program the plug that controls it to turn off if it is running for 1.3 x normal or some such duration. Several humidity sensors trigger the plug to go on when the humidity is over 51% and to turn off when it gets to 47%. The dehumidifier is in the crawl space, and I've gone to great lengths to make the dehumidifier as quiet as I can.

Last summer the dehumidifier failed and iced up, so it stopped dehumidifying. Since it was no longer dehumidifying, the sensor never got to 47%, so the plug stayed on. Thus the compressor and fan on a non functioning dehumidifier ran for probably over a month, until I received the power bill and went looking for what went wrong... I want to prevent that.

I now have a notification set up to alert me if the humidity gets over x, but still, I want to automatically prevent the dehumidifier from running too long.

Me too. Hence...

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.