codetwice / homebridge-http-securitysystem

Homebridge plugin that creates a SecuritySystem device which uses configurable HTTP calls to set and get its state.
BSD 2-Clause "Simplified" License
29 stars 17 forks source link

Alarm Trigger with Hue Motion detectors #10

Closed ghost closed 7 years ago

ghost commented 7 years ago

Hello,

I would like to implement an alarm system in Homekit. I'm using a Hue Homebridge plugin for the Philips Hue motion detectors. They are visible and working in Homekit. When I activate, for an example, the state Away and one of the motion detectors detects motion nothing happens. Do I miss something? Do I need to change something on the webserver? I only have a simple Apache2 webserver.

Thanks a lot.

codetwice commented 7 years ago

Dear Mobius,

I am afraid I won't be able to help you with this issue. My plugin alone is not defining any logic regarding alarm states and actual alarms, it only acts as a relay: it simply forwards queries from your iPhone to an arbitrary web application of yours.

When you are using http-securitysystem, you need a web app to connect it to and it has to be the web app that listens for inputs and triggers responses. Basically: this plugin shows a completely 3rd party security solution as if it was homekit-compatible.

To give you an example: I am using Node-Red for defining certain automation rules of my home. Node-red itself is not homekit-compatible, but it can expose web endpoints, on which it listens to requests and provides responses based on logic you define. I have my home security logic inside node-red (it is receiving inputs from motion sensors and it is triggering alarms and lights, it can also control Philips Hue stuff for example) and I have this securitysystem homebridge-plugin running and relaying HomeKit requests to node-red web endpoints. This way my completely custom security solution seems like if it was a HomeKit device and I can use my phone or Siri to toggle it.

ghost commented 7 years ago

Dear Codetwice,

Sorry for my late response, I was for a couple of days on holidays. Yesterday I have installed your Plugin from the develop branch. The polling option is for me mandatory for the challenge to use the Philips Hue motion sensors. Interestingly I could only install the develop branch as local user without sudo and -g.

I have installed Node-Red in a virtual machine and it looks really interesting. But when I'm correctly informed, it is not possible to integrate the Hue sensors in Node Red.

I have now configured your plugin to work with my Hue sensors (homebridge-hue from ebaauw). I also made some chances in the homebridge-hue Plugin.

For everyone who is interested I added the changes Below:

First I added a new file (motion) inside my web server (/var/www/html/alarm). Inside the file HueSensor.js (/.../homebridge-hue/lib/) In the Function „HueSensor.prototype.heartbeat = function(obj) {“ I chanced and added following code (line ~580).

Original:

      this.durationTimer = null;
      this.service
        .updateCharacteristic(this.type.Characteristic, saved.value);
    }.bind(this), this.duration);
  } else {
    this.log.info(
      '%s: set homekit %s from %s%s to %s%s on %s', this.name,
        this.type.name, old.hk.value, this.type.unit,
        this.hk.value, this.type.unit, this.hk.lastupdated
    );
    this.service
      .updateCharacteristic(this.type.Characteristic, this.hk.value);
    }
}

}

Changed:

      this.durationTimer = null;
      this.service
        .updateCharacteristic(this.type.Characteristic, saved.value);
    }.bind(this), this.duration);
  } else {
    if (this.type.name == "motion") {
      var fs = require('fs');
      var alarmstatus;
      try {
        alarmstatus = JSON.parse(fs.readFileSync('/var/www/html/alarm/check', 'utf8'));
      } catch (err) {
        self.log("Error: alarmstatus = %s", alarmstatus);
      }

      if (alarmstatus !== 3) {
        this.log.info('alarmstatus ist: %s : Motion detected!', alarmstatus);
        if (alarmstatus == 0 || alarmstatus == 2) {
          this.log.info('alarmstatus ist:%s', alarmstatus);

          var fs = require('fs');
          if (this.name == "Motion Sensor Name 1" || this.name == "Motion Sensor Name 2" || this.name == "Motion Sensor Name 3" && old.hk.value == 0) {
            this.log.info(
              '%s: set homekit %s from %s%s to %s%s on %s', this.name,
                    this.type.name, old.hk.value, this.type.unit,
                    this.hk.value, this.type.unit, this.hk.lastupdated
            );
            fs.writeFile("/var/www/html/alarm/motion", 1, function(err) {
              if(err) {
                    return console.log(err);
              }
            });
          } else if (this.name == "Motion Sensor Name 1" || this.name == "Motion Sensor Name 2" || this.name == "Motion Sensor Name 3" && old.hk.value == 1) {
            this.log.info(
              '%s: set homekit %s from %s%s to %s%s on %s', this.name,
                    this.type.name, old.hk.value, this.type.unit,
                    this.hk.value, this.type.unit, this.hk.lastupdated
            );
            fs.writeFile("/var/www/html/alarm/motion", 0, function(err) {
              if(err) {
                    return console.log(err);
              }
            });
          } else {
            this.log.info(
              '%s: set homekit %s from %s%s to %s%s on %s', this.name,
                    this.type.name, old.hk.value, this.type.unit,
                    this.hk.value, this.type.unit, this.hk.lastupdated
            );
          }
        }
        if (alarmstatus == 1) { 
          this.log.info('alarmstatus ist:%s', alarmstatus);
          var fs = require('fs');
          if (this.name == "Motion Sensor Name 1" || this.name == "Motion Sensor Name 2" || this.name == "Motion Sensor Name 3" || this.name == "Motion Sensor Name 4" && old.hk.value == 0) {
            this.log.info(
              '%s: set homekit %s from %s%s to %s%s on %s', this.name,
                    this.type.name, old.hk.value, this.type.unit,
                    this.hk.value, this.type.unit, this.hk.lastupdated
            );
            fs.writeFile("/var/www/html/alarm/motion", 1, function(err) {
              if(err) {
                    return console.log(err);
              }
            });
          } else if (this.name == "Motion Sensor Name 1" || this.name == "Motion Sensor Name 2" || this.name == "Motion Sensor Name 3" || this.name == "Motion Sensor Name 4" && old.hk.value == 1) {
            this.log.info(
              '%s: set homekit %s from %s%s to %s%s on %s', this.name,
                    this.type.name, old.hk.value, this.type.unit,
                    this.hk.value, this.type.unit, this.hk.lastupdated
            );
            fs.writeFile("/var/www/html/alarm/motion", 0, function(err) {
              if(err) {
                    return console.log(err);
              }
            });
          } else {
            this.log.info(
              '%s: set homekit %s from %s%s to %s%s on %s', this.name,
                    this.type.name, old.hk.value, this.type.unit,
                    this.hk.value, this.type.unit, this.hk.lastupdated
            );
          }
        }
      } else {
        fs.writeFile("/var/www/html/alarm/motion", 0, function(err) {
          if(err) {
                return console.log(err);
          }
        });
      }
    this.service
      .updateCharacteristic(this.type.Characteristic, this.hk.value);
    }
  }
}

}

When motion will be detected with specific motion sensors, the value 1 will be written inside motion file. When the motion sensor detects no motion anymore, the value 0 will be written. Which Motion Sensor is selected is based on the alarm state (alarmstatus). State 0 and 2 is in ma setup the same (Home and Night). State 1 = Away.

In the function "HttpSecuritySystemAccessory.prototype.setTargetState = function(state, callback)" I have chanced the "if (error)" as follows:

var url = cfg.url;
var body = cfg.body;
if (url) {
    this.httpRequest(url, body, function(error, response) {
        if (error) {
            this.log("SetState function failed: %s", error.message);
            callback(error);
        } else {
            this.log("SetState function succeeded!");
            self.securityService.setCharacteristic(Characteristic.SecuritySystemCurrentState, state);
            callback(error, response, state);
            var fs = require('fs');
            fs.writeFile("/var/www/html/alarm/check", state, function(err) {
              if(err) {
                 return console.log(err);
              }
            });
        }
    }.bind(this));
} else {
    callback(null);
}

};

With this code the security state chance inside the HomeKit app will be written to the check file. I use this because I have no "intelligent" alarm system at the backend.

The chances at function "HttpSecuritySystemAccessory.prototype.getCurrentState = function(callback)" are as follows: HttpSecuritySystemAccessory.prototype.getCurrentState = function(callback) { var self = this; self.debugLog("Getting current state"); this.getState(this.urls.readCurrentState.url, this.urls.readCurrentState.body, function(err, state) { self.debugLog("Current state is %s", state); var fs = require('fs'); var motion; try { motion = JSON.parse(fs.readFileSync('/var/www/html/alarm/motion', 'utf8')); } catch (err) { self.log("Error: Motion = %s", motion); } if (motion == "") { fs.writeFile("/var/www/html/alarm/motion", 0, function(err) { if(err) { return console.log(err); } }); } if (self.previousCurrentState !== 3 && motion == "1") { state = 4; self.previousCurrentState = state; self.log("Current state changed to %s", state); } if (self.previousCurrentState !== state) { self.previousCurrentState = state; self.log("Current state changed to %s", state); }

    callback(err, state);
});

};

codetwice commented 7 years ago

Congrats and thanks for the explanation. I'd have never thought of using homebridge itself to connect devices!