Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Presence Detection #234

Closed
wants to merge 6 commits into from
Closed

Conversation

rkoshak
Copy link

@rkoshak rkoshak commented Sep 5, 2019

Initial implementation of the Generic Presence Detection example from the forum. I've expanded it somewhat so that it can track both overall presence as well as presence of individuals. I use Scott's automatically generated Rule Triggers trick to make that happen. Configuration takes place through three variables in configuration.py, two lists (list of sensor Items and corresponding proxy Items) as well as the flapping time.

As I type this message it occurs to me that I could use a single List of tuples which would let me have a different flapping timeout value for each person. I'll await the initial review though before I add that.


# Item and sensor are the same, cancel the flapping timer if there is
# one.
global presence_timers # For the love of Pete why?! I get "use before assigned" errors without the global
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you are assigning the value at line 136. It looks like a mistake, you should be doing presence_timers[proxy_name] I think

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes that would explain it. You are right, that is totally a bug. Wonder why this worked when I tested it? I bet I only tested it with one Timer running at a time. I'll test it some more this evening and check in the fix.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this bug should be fixed in the most recent commit. I was able to test live data with multiple Timers yesterday and it work as expected.

@rkoshak
Copy link
Author

rkoshak commented Sep 19, 2019

I was experiencing some strange behavior (i.e. Timers not being cancelled when they are supposed to be) so I simplified some of the code and and added a new Rule to synchronize the proxy Items with the current/restored sensor readings at Rule startup. I also added code where the user can set a name metadata on the proxy Item and get more human friendly logs and/or use it in TTS announcements and the like.

@volfan6415
Copy link

Rather than configured with a list of items could the script be configured to use a group that is defined?

@rkoshak
Copy link
Author

rkoshak commented Sep 30, 2019

It already works that way. The list is expected to be a list of Groups. In my current setup and in the example in the docstrings, there are three Groups. One generic group that represents when someone is home (gPresent), one specific group that represents when Rich is home (gRichPresent) and one group that represents when Jenn (gJennPresent) is home. Each of those Groups have more than one member and the script will track each of them separately.

If you only care whether someone is home, you just need the one Group (gPresent). The docs are to show how you can also separately track individual people, not just over all presence.

@boehan
Copy link

boehan commented Nov 9, 2019

@rkoshak is the most recent version in this PR actually working for you? For me there seems to be anything wrong with the lambda function, which I can't get fixed. I always get the following errors when the rule is triggered:

2019-11-09 16:02:29.024 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.Timer 3 2019-11-09T16:02:29.011+01:00: <function <lambda> at 0x5> threw an unhandled Exception: 
org.python.core.PyException: null
	at org.python.core.Py.NameError(Py.java:290) ~[?:?]
	at org.python.core.PyFrame.getglobal(PyFrame.java:265) ~[?:?]
	at org.python.pycode._pyx33.get_name$1(<script>:78) ~[?:?]
	at org.python.pycode._pyx33.call_function(<script>) ~[?:?]
	at org.python.core.PyTableCode.call(PyTableCode.java:171) ~[?:?]
	at org.python.core.PyBaseCode.call(PyBaseCode.java:139) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:413) ~[?:?]
	at org.python.pycode._pyx33.away$6(<script>:127) ~[?:?]
	at org.python.pycode._pyx33.call_function(<script>) ~[?:?]
	at org.python.core.PyTableCode.call(PyTableCode.java:171) ~[?:?]
	at org.python.core.PyBaseCode.call(PyBaseCode.java:308) ~[?:?]
	at org.python.core.PyFunction.function___call__(PyFunction.java:471) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:466) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:456) ~[?:?]
	at org.python.pycode._pyx33.f$5(<script>:106) ~[?:?]
	at org.python.pycode._pyx33.call_function(<script>) ~[?:?]
	at org.python.core.PyTableCode.call(PyTableCode.java:171) ~[?:?]
	at org.python.core.PyBaseCode.call(PyBaseCode.java:125) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:403) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:398) ~[?:?]
	at org.python.core.PyFunction.invoke(PyFunction.java:533) ~[?:?]
	at com.sun.proxy.$Proxy367.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:48) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [182:org.openhab.core.scheduler:2.5.0.M4]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [182:org.openhab.core.scheduler:2.5.0.M4]
2019-11-09 16:02:29.078 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.Timer 3 2019-11-09T16:02:29.011+01:00: <function <lambda> at 0x5> threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [182:org.openhab.core.scheduler:2.5.0.M4]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [182:org.openhab.core.scheduler:2.5.0.M4]
Caused by: org.python.core.PyException
	at org.python.core.Py.NameError(Py.java:290) ~[?:?]
	at org.python.core.PyFrame.getglobal(PyFrame.java:265) ~[?:?]
	at org.python.pycode._pyx33.get_name$1(<script>:78) ~[?:?]
	at org.python.pycode._pyx33.call_function(<script>) ~[?:?]
	at org.python.core.PyTableCode.call(PyTableCode.java:171) ~[?:?]
	at org.python.core.PyBaseCode.call(PyBaseCode.java:139) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:413) ~[?:?]
	at org.python.pycode._pyx33.away$6(<script>:127) ~[?:?]
	at org.python.pycode._pyx33.call_function(<script>) ~[?:?]
	at org.python.core.PyTableCode.call(PyTableCode.java:171) ~[?:?]
	at org.python.core.PyBaseCode.call(PyBaseCode.java:308) ~[?:?]
	at org.python.core.PyFunction.function___call__(PyFunction.java:471) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:466) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:456) ~[?:?]
	at org.python.pycode._pyx33.f$5(<script>:106) ~[?:?]
	at org.python.pycode._pyx33.call_function(<script>) ~[?:?]
	at org.python.core.PyTableCode.call(PyTableCode.java:171) ~[?:?]
	at org.python.core.PyBaseCode.call(PyBaseCode.java:125) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:403) ~[?:?]
	at org.python.core.PyFunction.__call__(PyFunction.java:398) ~[?:?]
	at org.python.core.PyFunction.invoke(PyFunction.java:533) ~[?:?]
	at com.sun.proxy.$Proxy367.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:48) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more

any ideas?

@rkoshak
Copy link
Author

rkoshak commented Nov 11, 2019

I've been running with it since before I posted it here. The lambda itself is as simple as can be. I'm not sure what could be throwing an exception from there. Put everything in away() inside a try/except and log out the contents of the exception.

def away(log, events, proxy_name, item_name, flap_time):
    """
    Called when a person has been detected away for more than the flapping time.
    Log out the away state and command the Item to OFF.
    Arguments:
        - log: Logger passed in from the presence Rule.
        - events: Access to the events Object so we can call sendCommand.
        - proxy_name: Name of the proxy Item that we want to set to away.
        - item_name: Name of the sensor Item that detects presence.
        - flap_time: Number of minutes we wait before marking the person away.
    """
    try:
        if items[proxy_name] == OFF:
            log.warn("Presence away timer expired but {} is already OFF!"
                     .format(proxy_name))
        elif items[item_name] == ON:
            log.warn("Presence away timer expired but {} is ON!".format(item_name))
        else:
            log.info("{} has been away for {} minutes, setting to away."
                    .format(get_name(proxy_name),flap_time))
            events.sendCommand(proxy_name, "OFF")
    except:
        import sys
        log.error("Error occurred in away: {}".format(sys.exec_info()[0]))

That should tell us what the error is and on which line it occurs. I can't imagine what it would be though. Perhaps proxy_name is None instead of the name of the proxy Item in which case there may be something wrong with your configuration.py settings.

@rkoshak
Copy link
Author

rkoshak commented Nov 14, 2019

This PR is ready for review

@boehan
Copy link

boehan commented Nov 21, 2019

I've been running with it since before I posted it here. The lambda itself is as simple as can be. I'm not sure what could be throwing an exception from there. Put everything in away() inside a try/except and log out the contents of the exception.

def away(log, events, proxy_name, item_name, flap_time):
    """
    Called when a person has been detected away for more than the flapping time.
    Log out the away state and command the Item to OFF.
    Arguments:
        - log: Logger passed in from the presence Rule.
        - events: Access to the events Object so we can call sendCommand.
        - proxy_name: Name of the proxy Item that we want to set to away.
        - item_name: Name of the sensor Item that detects presence.
        - flap_time: Number of minutes we wait before marking the person away.
    """
    try:
        if items[proxy_name] == OFF:
            log.warn("Presence away timer expired but {} is already OFF!"
                     .format(proxy_name))
        elif items[item_name] == ON:
            log.warn("Presence away timer expired but {} is ON!".format(item_name))
        else:
            log.info("{} has been away for {} minutes, setting to away."
                    .format(get_name(proxy_name),flap_time))
            events.sendCommand(proxy_name, "OFF")
    except:
        import sys
        log.error("Error occurred in away: {}".format(sys.exec_info()[0]))

That should tell us what the error is and on which line it occurs. I can't imagine what it would be though. Perhaps proxy_name is None instead of the name of the proxy Item in which case there may be something wrong with your configuration.py settings.

That did not help, but I think I found the problem. After adding from core.metadata import get_value to the imports it seems to be working now (will have to test this over the next days).

@rkoshak
Copy link
Author

rkoshak commented Nov 21, 2019

I'm not certain why that import is missing in the first place, but that would cause problems.
When I look at my copy it has the import but the import is clearly missing here. I messed up somewhere.

EDIT:
I talked with Scott and got permission to update this PR.

I also figured out what went wrong. In my environment I have this code in two places (I have a utility function to get_name using get_value). When I copied it over and tested it I didn't run through the get_name code apparently.

Signed-off-by: Rich Koshak <rlkoshak@gmail.com>
@rkoshak
Copy link
Author

rkoshak commented Jul 17, 2020

I've basically completely rewritten this so am closing this PR. When reviewing submissions to the helper library becomes a priority I'll resubmit. In the mean time I'll keep in in my own repo where I can continue development.

@rkoshak rkoshak closed this Jul 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants