Presence detection with Netamo Welcome

This rule is used to set a switch item (ON / OFF) following the person presence last seen channel from Netatmo Welcome camera.

The destination item (switch) is part of the Generic Presence Detection, as a PersonSensor (Thanks Rich Koshak).

Requirements:
- Netatmo Welcome camera
- One or more Switches determines whether someone is present
- A group with all persons last seen channel (gNetatmoPresence)
- The label (of the person last seen channel) should be the uid of the switch item.

The rule:

The rule is running each 2 min via cron.

The script:

For each channel from the group, if current time is between «last seen» timestamp and «last seen» timestamp + 15 min, the person is considered to be at home. The switch is set to “ON”.

All «log info» can be removed (it is only for debugging!)

1 Like

This looks like a good candidate for a rule template. See How to write a rule template. You’ve figured out the hard part so all that’s left is a minor formatting change and adding some properties (e.g. set the Items and the timeout based on a property instead of hard codeing.

Even for Blockly, the best way to share a rule, if you don’t do a rule template, is to click on the “Code” tab and paste the text you see there into the post using code fences.

```
code goes here
```

Definitely post a screen shot of the blocks too, but with the actual code the rule can be copied and pasted instead of recreated from scratch. A rule template is even better since the user can install it like a binding and configure it like a Thing.

Thanks Rich. I will do so, but I will need time to understand how to… :slight_smile:
Waiting:

var X, msg, rawLabel, lastSeen;

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);

var dtf = Java.type("java.time.format.DateTimeFormatter");

var zdt = Java.type("java.time.ZonedDateTime");

/* Try to detect the format based on its length */
function getZonedDateTime(datetime) {
  datetime = String(datetime).replace('T', ' ')
  switch (datetime.length) {
    case 10: return zdt.parse(datetime + ' 00:00:00+00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
    case 16: return zdt.parse(datetime + ':00+00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
    case 19: return zdt.parse(datetime + '+00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
    case 25: return zdt.parse(datetime, dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
    case 23: return zdt.parse(datetime + ' +00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSS z'));
    case 26: return zdt.parse(datetime + ' +00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSSS z'));
    case 29: return zdt.parse(datetime, dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSz'));
    case 32: return zdt.parse(datetime, dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSSSSz'));
    case 28: return zdt.parse(datetime.slice(0,26) + ':' + datetime.slice(26,28), dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSz'));
    default: return zdt.parse(datetime);
  }
}

function createZonedDateTime(year, month, day, hour, minute, second, nano, offsetString, timezoneString) {
  stringToParse = '' + year;
  stringToParse += '-' + ('0' + month).slice(-2);
  stringToParse += '-' + ('0' + day).slice(-2);
  stringToParse += 'T' + ('0' + hour).slice(-2);
  stringToParse += ':' + ('0' + minute).slice(-2);
  stringToParse += ':' + ('0' + second).slice(-2);
  stringToParse += '.' + nano + offsetString + '[' + timezoneString + ']';
  return zdt.parse(stringToParse, dtf.ISO_ZONED_DATE_TIME);
}


logger.info('Starting Netatmo presence check');
var X_list = Java.from(itemRegistry.getItem('gNetatmoPresence').members);
for (var X_index in X_list) {
  X = X_list[X_index];
  msg = 'Netatmo presence: ';
  rawLabel = X.getLabel();
  lastSeen = X.getState();
  logger.info(rawLabel);
  logger.info(lastSeen);
  if ((zdt.now()) > lastSeen && zdt.now().minusMinutes(10) <= lastSeen) {
    events.sendCommand(rawLabel, 'ON');
    msg += String(rawLabel);
    msg += ' ON';
  } else {
    events.sendCommand(rawLabel, 'OFF');
    msg += String(rawLabel);
    msg += ' OFF';
  }
  logger.info(msg);
  msg = '';
  rawLabel = '';
  lastSeen = '';
}
logger.info('Ending Netatmo presence check');

Ok, I forgot that there are two “code” tabs. You’ll want to back out from the script action to the main page for the rule that shows the actions, triggers, and conditions and post the contents of the code l tab from there so that the entire time is captured.

The code you posted above is just showing the JavaScript that your Blockly “compiles” into. But I suspect you really want to show the entire rule.

We are going to make it :slight_smile:

´´´
configuration: {}
triggers:

  • id: “1”
    configuration:
    cronExpression: 0 0/2 * * * ? *
    type: timer.GenericCronTrigger
    conditions: []
    actions:

  • inputs: {}
    id: “3”
    configuration:
    blockSource: ‘XmsgrawLabellastSeeninfoStarting Netatmo presence
    checkXgNetatmoPresencemsgNetatmo presence: rawLabelLabelMyItemXlastSeenStateMyItemXinfoabcrawLabelinfoabclastSeenANDGTlastSeenLTEminusMinutes10lastSeensendCommandONnetatmoCyrilrawLabelmsgrawLabelmsg
    ONsendCommandOFFnetatmoCyrilrawLabelmsgrawLabelmsg
    OFFinfoabcmsgmsgrawLabellastSeeninfoEnding Netatmo presence
    check’
    type: application/javascript
    script: >
    var X, msg, rawLabel, lastSeen;

    var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
    
    
    var dtf = Java.type("java.time.format.DateTimeFormatter");
    
    
    var zdt = Java.type("java.time.ZonedDateTime");
    
    
    /* Try to detect the format based on its length */
    
    function getZonedDateTime(datetime) {
      datetime = String(datetime).replace('T', ' ')
      switch (datetime.length) {
        case 10: return zdt.parse(datetime + ' 00:00:00+00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
        case 16: return zdt.parse(datetime + ':00+00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
        case 19: return zdt.parse(datetime + '+00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
        case 25: return zdt.parse(datetime, dtf.ofPattern('yyyy-MM-dd HH:mm:ssz'));
        case 23: return zdt.parse(datetime + ' +00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSS z'));
        case 26: return zdt.parse(datetime + ' +00:00', dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSSS z'));
        case 29: return zdt.parse(datetime, dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSz'));
        case 32: return zdt.parse(datetime, dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSSSSz'));
        case 28: return zdt.parse(datetime.slice(0,26) + ':' + datetime.slice(26,28), dtf.ofPattern('yyyy-MM-dd HH:mm:ss.SSSSz'));
        default: return zdt.parse(datetime);
      }
    }
    
    
    function createZonedDateTime(year, month, day, hour, minute, second, nano, offsetString, timezoneString) {
      stringToParse = '' + year;
      stringToParse += '-' + ('0' + month).slice(-2);
      stringToParse += '-' + ('0' + day).slice(-2);
      stringToParse += 'T' + ('0' + hour).slice(-2);
      stringToParse += ':' + ('0' + minute).slice(-2);
      stringToParse += ':' + ('0' + second).slice(-2);
      stringToParse += '.' + nano + offsetString + '[' + timezoneString + ']';
      return zdt.parse(stringToParse, dtf.ISO_ZONED_DATE_TIME);
    }
    
    
    
    logger.info('Starting Netatmo presence check');
    
    var X_list = Java.from(itemRegistry.getItem('gNetatmoPresence').members);
    
    for (var X_index in X_list) {
      X = X_list[X_index];
      msg = 'Netatmo presence: ';
      rawLabel = X.getLabel();
      lastSeen = X.getState();
      logger.info(rawLabel);
      logger.info(lastSeen);
      if ((zdt.now()) > lastSeen && zdt.now().minusMinutes(10) <= lastSeen) {
        events.sendCommand(rawLabel, 'ON');
        msg += String(rawLabel);
        msg += ' ON';
      } else {
        events.sendCommand(rawLabel, 'OFF');
        msg += String(rawLabel);
        msg += ' OFF';
      }
      logger.info(msg);
      msg = '';
      rawLabel = '';
      lastSeen = '';
    }
    
    logger.info('Ending Netatmo presence check');
    

    type: script.ScriptAction

´´´

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.