Time stamps for time since last activated

Posted on
Mon Jul 22, 2013 1:20 pm
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Time stamps for time since last activated

I stole this from Travis's DSC alarm plugin information.

So right now, I have a time stamp that gets plugged into a variable for each motion sensor I have in the format of HH:MM.

This works great, however, a couple of issues arise. If it has been more than 24 hours, I have no idea since I don't track days. Also, it is a lot easier to discern it's been 2 hours since it last went off as opposed to it went off at 14:21 and do the math.

I have an idea on how to do this with schedules... but I would worry that the schedules would clog up the communication in the system since there are several sensors and some of them go on/off quite a bit throughout the day.

Anyone who has a fairly straightforward way of accomplishing this would be greatly appreciated!
Attachments
dscAlarmControlPageNew.png
dscAlarmControlPageNew.png (48.24 KiB) Viewed 7003 times

Posted on
Mon Jul 22, 2013 3:41 pm
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Time stamps for time since last activated

Hrm - so, you insert the last time the motion sensor was activated - is that right?

Notice, however, that you never say what you're doing with it: what it needs to get compared to, when that comparison needs to take place, etc. So I think we're missing some fundamental piece of information...

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jul 22, 2013 4:11 pm
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Time stamps for time since last activated

Ah, I suppose the subject says some of it actually - you want elapsed time since the device went on. If you use the Variable Actions->Insert Timestamp into Variable action then you can keep the full date and time when the device last went on. Then, whenever you need to calculate elapsed time you can just use that information - in Python you can use the strptime() function to convert back to a Python datetime object and subtract it from now() to give you a timedelta which you can test to see total elapsed time.

Something like this for instance:

Code: Select all
from datetime import datetime
dateString = indigo.variables[VARID].value
lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")
delta = datetime.now()-lastOn


All this can be done in AppleScript as well, but date and time calculations there always seem like a pain - well, more of a pain than using strptime to suck out a datetime object from a string anyway... ;)

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jul 22, 2013 4:23 pm
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

Correct. I'll play around with this. Thanks!

Posted on
Mon Jul 22, 2013 8:02 pm
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

This is working... however, I am ending up with a 6 decimal place seconds, so I need to manipulate the delta value before I insert it but I don't exactly know how to do that... can I get some help?

My thought is that I would like to do the number of minutes up to 59 (round less than a minute to 0 minutes), then the number of hours up to 23 and then the number of days.

To keep things simple from a display standpoint on my control page, I only want to use one unit... for instance, if it has been 1 hour, 25 minutes and 10 seconds elapsed since the last motion event, I simply want to display 1 hour and always round down.

Would it be some sort of "if then" type logic to look to see if the day has a value other than zero and populate with just that value, then hours, and then minutes? Also added a units label after each respective unit has been passed? First real try at python here.

Code: Select all
from datetime import datetime
dateString = indigo.variables[477507543].value
lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")
delta = datetime.now()-lastOn
if datetime.striptime(delta, "%d" <> "0")
   elapsed= datetime.striptime(delta, "%d"+"d")
else if datetime.striptime(delta, "%h" <> 0 )
   elapsed= datetime.striptime(delta, "%h"+"h")
else if datetime.striptime(delta, "%m" <> 0 )
   elapsed= datetime.striptime(delta, "%m"+"m")
else elapsed= datetime.striptime(delta, "0m")
indigo.variable.updateValue(1648188244, str(elapsed))

Posted on
Mon Jul 22, 2013 8:05 pm
rhanson offline
Posts: 192
Joined: Apr 30, 2013

Re: Time stamps for time since last activated

Following on to what Jay said, after you have timestamps in the appropriate variables...
The first function takes care of those microseconds.

Code: Select all
from datetime import datetime

def str2datetime(s):
    parts = s.split('.')
    dt = datetime.strptime(parts[0], "%Y-%m-%d %H:%M:%S")
    return dt.replace(microsecond=int(parts[1]))

# figure out how long it has been since some timestamp. Returns the elapsed time in seconds, up to a maximum of one day, and a negative number if the date is in the future
# note that you should pass an indigo variable here, not a string.
# _now is defined globally as the time at the start of this script; or use datetime.now()
def timeSince(indigovar):
   dt = str2datetime(indigovar.value)
   dtd = _now - dt
   if dtd.days == 0:
      return dtd.seconds
   elif dtd.days > 0:
      return (dtd.days * 86400) + dtd.seconds
   elif dtd.days == -1:
      return -(86400 - dtd.seconds)
   else:
      return -86400

# these are here just for readability, throwback to applescript days
SECONDS  = 1
MINUTES = 60 * SECONDS

_now = datetime.now()
ts_motion_entry_hall = indigo.variables[12345]
if timeSince(ts_motion_entry_hall) > 10*MINUTES:
  # do something


Posted on
Mon Jul 22, 2013 8:28 pm
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

Inching along here....

From initial setup by Jay, I believe the following is close... to what I want. I'm defining elapsedHallway (what I want to end up populating my indigo variable with, if it elapsed time does not equal 0 days, define it with how many days have elapsed and so on...but of course I'm getting a syntax error. I don't believe I need to strip off the decimals if I'm going this route and never really looking at seconds to begin with... of course feel free to correct me if I'm wrong.

Code: Select all
from datetime import datetime
dateString = indigo.variables[477507543].value
lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")


def elapsedHallway:
   if lastOn.days == 0:
      return dtd.days
   elif lastOn.hours == 0:
      return dtd.hours
   elif lastOn.minutes == 0:
      return dtd.minutes
   else:
      return 0

indigo.variable.updateValue(1648188244, str(elapsedHallway))

Posted on
Tue Jul 23, 2013 6:34 am
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

Dewster35 wrote:
Inching along here....

From initial setup by Jay, I believe the following is close... to what I want. I'm defining elapsedHallway (what I want to end up populating my indigo variable with, if it elapsed time does not equal 0 days, define it with how many days have elapsed and so on...but of course I'm getting a syntax error. I don't believe I need to strip off the decimals if I'm going this route and never really looking at seconds to begin with... of course feel free to correct me if I'm wrong.


Getting closer. Realized I need to populate the value with a unit to identify whether it is days, hours or minutes. It is at least running through and populating a variable now:

Code: Select all
from datetime import datetime
dateString = indigo.variables[477507543].value
lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")
delta = datetime.now() - lastOn

def lapsedHallway():
   if delta.day == 0:
      return (delta.day + "D")
#   elif delta.hour == 0:
#      return (delta.hour + "h")
   elif delta.minute == 0:
      return (delta.minute + "m")
   else:
     return 0
lapsedHallway()

indigo.variable.updateValue(1648188244, str(lapsedHallway()))


However, the result that it is populating my variable with is

<function lapsedHallway at 0x4277bb0>

Any help when people get time is appreciated!

Posted on
Tue Jul 23, 2013 8:45 am
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Time stamps for time since last activated

I assume that since you added the open/close parenthesis after lapsedHallway in the last line the script is now working? Your original post didn't have them - basically, the version without them was a pointer to the lapsedHallway function (that's what <function lapsedHallway at 0x4277bb0> means) but add them and it becomes a call to the function.

You could simplify the script:

Code: Select all
# import the datetime object from the datetime module (kinda silly but that's a Python oddity)
from datetime import datetime
# get the date/time string from the variable that was inserted using the Insert Timestamp into Variable
dateString = indigo.variables[477507543].value
# convert the date/time string into an actual datetime Python object
lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")
# subtract the current date/time from the converted datetime and return a timedelta object
delta = datetime.now() - lastOn

# timedelta objects only have days, seconds, and microseconds as attributes
# so you have to calculate hours and minutes then once that's done we
# set the string elapsedText to represent the human readable version
if delta.days > 0:
    # if at least a day has elapsed then
    # set the string to look like this: "2 days"
    elapsedText = "%i days" % delta.days
elif delta.seconds / (60 * 60) > 0:
    # if at least one hour (60 seconds * 60 minutes) has elapsed then
    # set the string to look like this: "2 hours"
    elapsedText = "%i hours" % (delta.seconds / (60 * 60))
elif delta.seconds / 60 > 0:
    # if at least one minute has elapsed then
    # set the string to look like this: "2 minutes"
    elapsedText = "%i minutes" % (delta.seconds / 60)
else:
    # Less than 60 seconds has passed since the last activation so we'll just
    # set the string to look like this: "now"
    elapsedText = "now"

indigo.variable.updateValue(1648188244, elapsedText)


Didn't test it but it should be close. Copiously commented to help understand what it's doing - all lines that start with # are comments and can be removed to make the script shorter. I think that's approximately what you were looking for, right?

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Jul 23, 2013 8:56 am
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

jay (support) wrote:
I assume that since you added the open/close parenthesis after lapsedHallway in the last line the script is now working? Your original post didn't have them - basically, the version without them was a pointer to the lapsedHallway function (that's what <function lapsedHallway at 0x4277bb0> means) but add them and it becomes a call to the function.

You could simplify the script:

Code: Select all
# import the datetime object from the datetime module (kinda silly but that's a Python oddity)
from datetime import datetime
# get the date/time string from the variable that was inserted using the Insert Timestamp into Variable
dateString = indigo.variables[477507543].value
# convert the date/time string into an actual datetime Python object
lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")
# subtract the current date/time from the converted datetime and return a timedelta object
delta = datetime.now() - lastOn

# timedelta objects only have days, seconds, and microseconds as attributes
# so you have to calculate hours and minutes then once that's done we
# set the string elapsedText to represent the human readable version
if delta.days > 0:
    # if at least a day has elapsed then
    # set the string to look like this: "2 days"
    elapsedText = "%i days" % delta.days
elif delta.seconds / (60 * 60) > 0:
    # if at least one hour (60 seconds * 60 minutes) has elapsed then
    # set the string to look like this: "2 hours"
    elapsedText = "%i hours" % (delta.seconds / (60 * 60))
elif delta.seconds / 60 > 0:
    # if at least one minute has elapsed then
    # set the string to look like this: "2 minutes"
    elapsedText = "%i minutes" % (delta.seconds / 60)
else:
    # Less than 60 seconds has passed since the last activation so we'll just
    # set the string to look like this: "now"
    elapsedText = "now"

indigo.variable.updateValue(1648188244, elapsedText)


Didn't test it but it should be close. Copiously commented to help understand what it's doing - all lines that start with # are comments and can be removed to make the script shorter. I think that's approximately what you were looking for, right?


Gotcha... so hours and minutes (along with weeks, months and years) don't exist... we just do some math to make it in a readable format for what we understand? I guess where I was being tripped up was that there was actually a day, hour, minute, second value that was being populated. This makes more sense even though it is a little more work for the user to get it into a friendly format.

I tested and it seems to be working as intended.

Thank you for taking the time to comment on it. Learning a new language (not that I'm any sort of applescript guru) is tough.

So now this works for a single device that I'm tracking... if I have multiple devices that I'm tracking, what would the most efficient way to do this be? I currently have a schedule in indigo that basically runs this script a little over every minute.

Posted on
Tue Jul 23, 2013 11:01 am
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Time stamps for time since last activated

Dewster35 wrote:
Gotcha... so hours and minutes (along with weeks, months and years) don't exist... we just do some math to make it in a readable format for what we understand? I guess where I was being tripped up was that there was actually a day, hour, minute, second value that was being populated. This makes more sense even though it is a little more work for the user to get it into a friendly format.


The datetime object has year, month, day, minute, second, microsecond, etc. attributes. The timedelta object, which is returned when you add or subtract datetime objects, only has days, seconds, and microseconds.

Dewster35 wrote:
So now this works for a single device that I'm tracking... if I have multiple devices that I'm tracking, what would the most efficient way to do this be? I currently have a schedule in indigo that basically runs this script a little over every minute.


So, you have a trigger for each device you care about that updates a device specific variable? For example, you could have a device called "Office Motion Sensor" which fires a trigger whenever it goes on that populates the date/time into a variable called "officeMotionSensorLastOn" and then this script populates a variable called "officeMotionSensorLastOnReadable" with the human version.

A relatively straight-forward modification to the script would perform the operation for each variable pair:

Code: Select all
# this is a list of Python tuples (something like a list) - for each device specific
# "last on" source variable there's a corresponding device specific human readable destination
# var so you construct the list by putting the ID of the first source var, comma, the ID of the
# destination var inside parenthesis and separated by commas inside square brackets (which identifies
# it as a standard Python list).
varPairList = [(SOURCEVAR1ID, DESTVAR1ID), (SOURCEVAR2ID, DESTVAR2ID)]

# now that you have the list, you just cycle through the list grabbing the source id and destination
# id and use them in the appropriate spots in the previous script
for sourceVarId, destVarId in varPairList:
    # import the datetime object from the datetime module (kinda silly but that's a Python oddity)
    from datetime import datetime
    # get the date/time string from the variable that was inserted using the Insert Timestamp into Variable
    dateString = indigo.variables[sourceVarId].value
    # convert the date/time string into an actual datetime Python object
    lastOn = datetime.strptime(dateString, "%Y-%m-%d %H:%M:%S")
    # subtract the current date/time from the converted datetime and return a timedelta object
    delta = datetime.now() - lastOn

    # timedelta objects only have days, seconds, and microseconds as attributes
    # so you have to calculate hours and minutes then once that's done we
    # set the string elapsedText to represent the human readable version
    if delta.days > 0:
        # if at least a day has elapsed then
        # set the string to look like this: "2 days"
        elapsedText = "%i days" % delta.days
    elif delta.seconds / (60 * 60) > 0:
        # if at least one hour (60 seconds * 60 minutes) has elapsed then
        # set the string to look like this: "2 hours"
        elapsedText = "%i hours" % (delta.seconds / (60 * 60))
    elif delta.seconds / 60 > 0:
        # if at least one minute has elapsed then
        # set the string to look like this: "2 minutes"
        elapsedText = "%i minutes" % (delta.seconds / 60)
    else:
        # Less than 60 seconds has passed since the last activation so we'll just
        # set the string to look like this: "now"
        elapsedText = "now"

    indigo.variable.updateValue(destVarId, elapsedText)

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Jul 23, 2013 11:14 am
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

Correct. Source and destination variable for each device... handy.

A couple of other things... is it really most efficient to be triggering this script through indigo? I realize that gives me the most "control" over it in that it is easy to turn on and off, but is it hurting the performance of indigo having it as a trigger?

Also, I assume that this is what the other fella was referring to about the microseconds and getting rid of them?

Seeing this in the event log:

Script Error embedded script: unconverted data remains: .428281
Script Error Exception Traceback (most recent call shown last):

embedded script, line 6, at top level
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/_strptime.py", line 333, in strptime
data_string[found.end():])
ValueError: unconverted data remains: .428281

Update: I believe I've solved the problems at least from the indigo side of things by doing a custom date and time stamp of the following: %Y-%m-%d %H:%M:%S

After applying that, I don't see any microseconds listed. When it pulls in the current time, does that not have microseconds in it or is it simply rounded to the nearest second?

Posted on
Wed Jul 24, 2013 3:03 pm
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

Jay... this probably got lost since it was buried in that last response. If you or Matt could comment when you get the opportunity, no rush.

"A couple of other things... is it really most efficient to be triggering this script through indigo? I realize that gives me the most "control" over it in that it is easy to turn on and off, but is it hurting the performance of indigo having it as a trigger?"

Posted on
Wed Jul 24, 2013 5:57 pm
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Time stamps for time since last activated

It's fine - particularly with Python scripts which are always executed in a separate process.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Wed Jul 24, 2013 6:02 pm
Dewster35 offline
Posts: 1030
Joined: Jul 06, 2010
Location: Petoskey, MI

Re: Time stamps for time since last activated

Do apple scripts not run in their own process? I usually try to run them not embedded whenever I am running any sort of script.

Who is online

Users browsing this forum: No registered users and 8 guests