Generic Presence Detection

presence
Tags: #<Tag:0x00007fe058b27268>

(Justus) #54

…well, there seems to be a way. If you follow one of the dexcom-pebble-watch-faces…you will find the way…

Just try a little simple reverse engineering. You will get to the share server… I do not know wether you are using the dexcom companion app. this is an official app for supporting family members or medical staff… and this is using the share server / share service.

Just a snippet of the pebble watch face code:

//use D's share API------------------------------------------//
function share(options) {

    if (options.unit == "mgdl" || options.unit == "mg/dL")
    {
        fix = 0;
        options.conversion = 1;
        options.unit = "mg/dL";
        
    } else {
        fix = 1;
        options.conversion = 0.0555;       
        options.unit = "mmol/L";
    }
    
    var host = "share1";
    if (options.region != 'outside') {
        host = "share1";
    } else {
        host = "shareous1";
    }
    options.vibe = parseInt(options.vibe, 10);
    var defaults = {
        "applicationId": "d89443d2-327c-4a6f-89e5-496bbb0317db"
        , "agent": "Dexcom Share/3.0.2.11 CFNetwork/711.2.23 Darwin/14.0.0"
        , login: 'https://' + host + '.dexcom.com/ShareWebServices/Services/General/LoginPublisherAccountByName'
        , accept: 'application/json'
        , 'content-type': 'application/json'
        , LatestGlucose: "https://"+ host + ".dexcom.com/ShareWebServices/Services/Publisher/ReadPublisherLatestGlucoseValues"
    };

    authenticateShare(options, defaults);
}

function authenticateShare(options, defaults) {   
 
    var body = {
        "password": options.password
        , "applicationId": options.applicationId || defaults.applicationId
        , "accountName": options.accountName
    };

    var http = new XMLHttpRequest();
    var url = defaults.login;
    http.open("POST", url, true);
    http.setRequestHeader("User-Agent", defaults.agent);
    http.setRequestHeader("Content-type", defaults['content-type']);
    http.setRequestHeader('Accept', defaults.accept);
    
    var data;
    http.onload = function (e) {
        if (http.status == 200) {
            data = getShareGlucoseData(http.responseText.replace(/['"]+/g, ''), defaults, options);
        } else {
                sendAuthError();           
        }
    };
    
       http.ontimeout = function () {
        sendTimeOutError(options);
    };
    
    http.onerror = function () {
        sendServerError(options);
    };

    http.send(JSON.stringify(body));

}

This is be the result of reverse engineering of the official app…and this is in fact real time.

You can find the entire code within https://github.com/tynbendad/simplecgmanalog if you are downloading the zip-file within the pebble-js-app.js file. And this is in fact no rube-goldberg-machine or algorithm.

What do you think about it? I am quite happy seing it.


(Rich Koshak) #55

If you have the time I say give it a try. I just remember for the G4 with my wife’s pebble the path was the Sensor->CGM->iPhone->Dexcom Cloud->iPhone Pebble App->Pebble

If any link in that chain broke then no readings. NightScout is even a longer chain (or it was last time I saw it IIRC):

Sensor->CGM->Android (iOS wasn’t supported at the time I think)->Dexcom Cloud->Android->NightScout Server->Android->Watch Face

For some reason, the BT connection to the Pebble was flaky and the we are out of cell network far to frequently for it to be a viable option much of the time. When FitBit bought and discontinued support for Pebble we realized the BT problems would never get better.

Anyway, long story apropos to nothing.

This posting on the NightScout github might be a tad more complete:

It’s three years old so it might not be kept up to date. But there are curl examples and everything. The hard part will be constructing the HTTP requests which is already done in the OAuth2 tutorial I posted.

If you’ve the time I say go for it. I won’t have the time to look back into this at least until September.


(Justus) #56

Ok thanks. Perhaps I will have some time (and spirit) trying a little on our summer holidays. Thanks so far.


(Rich Koshak) #57

Please do report your progress if you do tackle it.


(Nick Moad) #58

Thank you for this code, I added a manual over ride switch for the babysitter or guests. This is assuming I don’t use zwave 3in1 sensors as presence sensors…not sure yet but it was fun to play with the code anyway as I am new to openHAB.

I added the manual switch item (probably doesn’t really need to be in the group switch but it didn’t work by just setting that switch to ON when in the group switch):

Group:Switch:AND(OFF,ON) gManualPresent <present>
Switch Manual_Present <present> // master manual present switch

then added the below in to the “gpresent updated” rule

 // no one is home
    else if(gPresent.state == OFF && Present.state != OFF && Manual_Present.state == OFF)

then another rule

rule "Manual Present Switch Turned Off"
when
    Item Manual_Present received command OFF
then
    Present_Timer.sendCommand(ON)
end

The idea being that present switch won’t turn OFF if manual over ride is on. Then when I turn it off it just sets the presence timer on to resume normal operation.

Thanks again!


(Vincent Regaud) #59

Thanks @Nick_Moad
Good idea
Could you edit your post and add the code fences, please?
Thanks


(Nick Moad) #60

After thinking about this, it is probably not required to integrate it with the timers above as you could just set any rules that use the presence to check “manual presence != ON”


(lamero) #61

I’m trying to use DSC Alarm sensor with this fantastic rule. But presence never goes off. What’s wrong?

items

Switch Present
Group:Switch:AND(OFF,ON) gPresent
Switch Present_Timer { expire="15s,command=OFF" }
Switch PresentDSC (gPresent)

rule

rule "System started"
when
    System started
then
    Present.sendCommand(OFF) // assume no one is home on startup
end

rule "gPresent updated, at least one sensor changed state"
when
    Item gPresent received update
then
    logInfo("presence.rules","gPresent aggiornato")
    // someone came home
    if(gPresent.state == ON && Present.state != ON) {
        Present_Timer.postUpdate(OFF) // cancel the timer if necessary
        Present.sendCommand(ON)
    }

    // no one is home
    else if(gPresent.state == OFF && Present.state != OFF){
        Present_Timer.sendCommand(ON) // start the timer
    }
end

rule "Present_Timer expired"
when
    Item Present_Timer received command OFF
then
    Present.sendCommand(OFF)
end

rule "DSC"
when
    Item DSCAlarmMotion changed from OPEN to CLOSED
then
    PresentDSC.sendCommand(ON)
end

debug

rule "debug"
when
    Item Present received update
then
    sendTelegram("home", "Presence - " + Present.state.toString)
end

(Rich Koshak) #62

Does PresentDSC change state?


(lamero) #63

Yes

2018-10-25 23:02:17.523 [ome.event.ItemCommandEvent] - Item 'Present' received command OFF
2018-10-25 23:02:17.525 [vent.ItemStateChangedEvent] - Present changed from ON to OFF
2018-10-25 23:02:28.625 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-25 23:02:28.632 [ome.event.ItemCommandEvent] - Item 'Present' received command ON
2018-10-25 23:02:28.638 [vent.ItemStateChangedEvent] - Present changed from OFF to ON
2018-10-25 23:03:24.084 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON

(See time)


(Rich Koshak) #64

Does it generates the error every time the winner runs?

Do the Item names exactly match the way they are defined in PaperUI or your .items files? Case matters. I’m kind of at a loss what we it could be.


(lamero) #65

Does it generates the error every time the winner runs?

No errors into the log

Something wrong with expire binding. Present_Timer never expires. Reinstalled expire bindings, rebooted OH:

2018-10-26 11:26:29.533 [ome.event.ItemCommandEvent] - Item 'Present' received command OFF
2018-10-26 11:26:29.535 [vent.ItemStateChangedEvent] - Present changed from NULL to OFF
2018-10-26 11:26:51.936 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-26 11:26:51.939 [vent.ItemStateChangedEvent] - PresentDSC changed from NULL to ON
2018-10-26 11:26:51.940 [GroupItemStateChangedEvent] - gPresent changed from NULL to ON through PresentDSC
2018-10-26 11:26:51.944 [ome.event.ItemCommandEvent] - Item 'Present' received command ON
2018-10-26 11:26:51.945 [vent.ItemStateChangedEvent] - Present_Timer changed from NULL to OFF
2018-10-26 11:26:51.946 [vent.ItemStateChangedEvent] - Present changed from OFF to ON

This is the rule. See comment (“are you sure?”)

rule "System started"
when
    System started
then
    Present.sendCommand(OFF) // assume no one is home on startup
end

rule "gPresent updated, at least one sensor changed state"
when
    Item gPresent received update
then
    // someone came home
    if(gPresent.state == ON && Present.state != ON) {
        Present_Timer.postUpdate(OFF) // cancel the timer if necessary
                                     // OFF? Are you sure?
                                     // With ON it works
        Present.sendCommand(ON)
    }

    // no one is home
    else if(gPresent.state == OFF && Present.state != OFF){
        Present_Timer.sendCommand(ON) // start the timer
    }
end

rule "Present_Timer expired"
when
    Item Present_Timer received command OFF
then
    Present.sendCommand(OFF)
end

(Rich Koshak) #66

Yes I’m sure.

With expire="15s,command=OFF" the timer gets started when they Item changes state to ON. Is 15 seconds pass with the Item not receiving any other updates the binding will send the OFF command to the Item. When you postUodate(OFF), the binding sees that the Item is already in the OFF state now so it cancels the timer and no OFF command will be sent.

So the Timer expired rule only gets triggered when the Expire binding commands the item to OFF.

I’ve been running with this coffee for years so the concept is sound.


(Vincent Regaud) #67

Rich, you can’t help yourself, can you… :smile:


(Rich Koshak) #68

I was in a meeting typing on my phone. For some reason Google really thinks I mean coffee when I type code. Sigh. Guess I need more code this morning.

:wink:


(Vincent Regaud) #69

I though you came up with another idiomatic pearl!


(Udo Weber) #70

@rlkoshak @lamero Mmm, I took a look a your code and I think your

Present_Timer

get’s never canceled, because

Present.state

is still on when someone comes home before timeout, so you will never reach

Present_Timer.postUpdate(OFF) // cancel the timer if necessary

Possible solution would be to leave out

&& Present.state != ON

or if you don’t want that

Present.sendCommand(ON)

gets triggered twice, you can add an additional else if

    else if(gPresent.state == ON && Present.state ==ON){ // someone came home but timer is running
        Present_Timer.postUpdate(OFF) // cancel the timer if necessary
    }

and remove

        Present_Timer.postUpdate(OFF) // cancel the timer if necessary

in the first if.

Didn’t tested it, so maybe I’m wrong?
Greets Udo


(lamero) #71

I’m confused

ITEMS

Switch Present
Group:Switch:AND(OFF,ON) gPresent
Switch Present_Timer                              { expire="10s,command=OFF" }
Switch PresentDSC                      (gPresent)

RULE

rule "System started"
when
    System started
then
    Present.sendCommand(OFF) // assume no one is home on startup
end

rule "gPresent updated, at least one sensor changed state"
when
    Item gPresent received update
then
    logInfo("present","gPresent.state " + gPresent.state.toString)
    logInfo("present","Present.state " + Present.state.toString)
    // someone came home
    if(gPresent.state == ON && Present.state != ON) {
        logInfo("present","present 1 if")
        Present_Timer.postUpdate(OFF) // cancel the timer if necessary
        Present.sendCommand(ON)
    }

    // no one is home
    else if(gPresent.state == OFF && Present.state != OFF){
        logInfo("present","present 2 if")
        Present_Timer.sendCommand(ON) // start the timer
    }
end

rule "Present_Timer expired"
when
    Item Present_Timer received command OFF
then
    Present.sendCommand(OFF)
end

LOG

System start

2018-10-31 15:39:23.086 [ome.event.ItemCommandEvent] - Item 'Present' received command OFF
2018-10-31 15:39:23.088 [vent.ItemStateChangedEvent] - Present changed from ON to OFF

Alarm sensor triggered

2018-10-31 15:41:02.687 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-31 15:41:02.689 [INFO ] [lipse.smarthome.model.script.present] - gPresent.state ON
2018-10-31 15:41:02.690 [INFO ] [lipse.smarthome.model.script.present] - Present.state OFF
2018-10-31 15:41:02.690 [INFO ] [lipse.smarthome.model.script.present] - present 1 if
2018-10-31 15:41:02.691 [ome.event.ItemCommandEvent] - Item 'Present' received command ON
2018-10-31 15:41:02.692 [vent.ItemStateChangedEvent] - Present changed from OFF to ON

Alarm sensor triggered after 2 minutes

2018-10-31 15:43:09.434 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-31 15:43:09.435 [INFO ] [lipse.smarthome.model.script.present] - gPresent.state ON
2018-10-31 15:43:09.436 [INFO ] [lipse.smarthome.model.script.present] - Present.state ON

Alarm sensor triggered after 7 minutes

2018-10-31 15:48:22.300 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-31 15:48:22.303 [INFO ] [lipse.smarthome.model.script.present] - gPresent.state ON
2018-10-31 15:48:22.303 [INFO ] [lipse.smarthome.model.script.present] - Present.state ON

Full log

2018-10-31 15:39:23.086 [ome.event.ItemCommandEvent] - Item 'Present' received command OFF
2018-10-31 15:39:23.088 [vent.ItemStateChangedEvent] - Present changed from ON to OFF
2018-10-31 15:41:02.687 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-31 15:41:02.689 [INFO ] [lipse.smarthome.model.script.present] - gPresent.state ON
2018-10-31 15:41:02.690 [INFO ] [lipse.smarthome.model.script.present] - Present.state OFF
2018-10-31 15:41:02.690 [INFO ] [lipse.smarthome.model.script.present] - present 1 if
2018-10-31 15:41:02.691 [ome.event.ItemCommandEvent] - Item 'Present' received command ON
2018-10-31 15:41:02.692 [vent.ItemStateChangedEvent] - Present changed from OFF to ON
2018-10-31 15:43:09.434 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-31 15:43:09.435 [INFO ] [lipse.smarthome.model.script.present] - gPresent.state ON
2018-10-31 15:43:09.436 [INFO ] [lipse.smarthome.model.script.present] - Present.state ON
2018-10-31 15:48:22.300 [ome.event.ItemCommandEvent] - Item 'PresentDSC' received command ON
2018-10-31 15:48:22.303 [INFO ] [lipse.smarthome.model.script.present] - gPresent.state ON
2018-10-31 15:48:22.303 [INFO ] [lipse.smarthome.model.script.present] - Present.state ON

How alarm sensors updates their state

ITEMS

Group:Contact:OR(OPEN, CLOSED) DSCAlarmMotion <motionDetector>
Contact ZONE1_STATUS "Sensor (Zone 1)" (DSCAlarmZones, DSCAlarmMotion) { channel="dscalarm:zone:be74eaa7:zone1:zone_status" }

RULES

rule "DSC"
when
    Item DSCAlarmMotion changed from OPEN to CLOSED
then
    PresentDSC.sendCommand(ON)
end

Present_Timer never starts, never expires


(Rich Koshak) #72

There may be a timing issue here because I’ve been running with this code (or at least code based on it) for years without problem.

The way it is supposed to work is gPresent represents the state of the actual sensors and Present is a proxy for the sensors so we can use the timer to smooth out the flapping.

In my working code at home I’m using changed as the trigger, not received update, so maybe that is part of the issue.

I’m also using a count of the ON sensors instead of just gPresent.state == ON. It looks like I’ve made a number of changes since posting this.

Ah yes, I must have fixed this somewhere along the way and forgot to come back here.

when
    Item gPresent changed
then
    logDebug("present", "gPresent changed to " + gPresent.state)

    // A presence sensor is ON, set Present to ON if it is OFF and cancel the Timer if it exists
    if(gPresent.state == ON && (Present.state != ON || Present_Timer.state == ON)) {
        logDebug("present", "Someone came home")
        if(Present_Timer.state != OFF) Present_Timer.postUpdate(OFF) // cancel the timer
        if(Present.state != ON) Present.sendCommand(ON)
    }

    // All presence sensors are OFF, create a Timer, if one doesn't already exist, to turn Present to OFF if 
    // Present isn't already OFF
    else if(gPresent.state == OFF && Present.state !-= OFF && Present_Timer.state != ON) {
        logDebug("present", "Everyone is away, setting timer")
        Present_Timer.sendCommand(ON) // set timer to turn off Present
    }
end

I transcribed this and had to change the names to match the Items above. But I think this might address the issues. Let me know one way or the other and I’ll update the OP.

In your case it is important to realize that this Rule assumes that there will be one or more sensors that is constantly ON when you are home (e.g. Network binding, iCloud binding, FIND, reelyActive detection of phone or BT beacon). I don’t think it works well with motion timers.

But if I’m reading what you are trying to do incorrectly, try the changes I made above and see if ti helps.


(Udo Weber) #73

No timing issue, it is just wrong, I tested it and I’m sure. In your example on the top you will never reach the line that cancels the timer.

Can you edit your first post? Because it will miss leading other people.

rule "Group present updated, at least one sensor changed state"
when
    Item gPresent changed
then
    if(gPresent.state == ON && Present.state != ON) { // someone came home
        Present.sendCommand(ON)
    } else if(gPresent.state == ON && Present.state == ON) { // someone came home but timer is running
        Present_Timer.postUpdate(OFF) // cancel the timer
    } else if(gPresent.state == OFF && Present.state != OFF){ // last person left home
        Present_Timer.sendCommand(ON) // start the timer
    }
end

rule "Present_Timer expired"
when
    Item Present_Timer received command OFF
then
    Present.sendCommand(OFF)
end