Skip to content

Commit

Permalink
Version 0.1.0 ready for publishing!
Browse files Browse the repository at this point in the history
  • Loading branch information
SphtKr committed Jan 21, 2016
0 parents commit 6ea276f
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 0 deletions.
5 changes: 5 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Copyright (c) 2016, Robert Blake

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# homebridge-smtpsensor

Is a plugin for [Homebridge](https://github.com/nfarina/homebridge) that creates HomeKit motion sensors (or contact sensors) that can be "tripped" by email messages with keywords in them. The plugin starts an SMTP server (does not forward messages) that you can configure devices to send emails to. It then scans the incoming emails for keywords, and triggers the correct HomeKit sensor device based on the keyword found.

This is good for various IP cameras that can send email messages when they detect motion (e.g., I'm using this with the D-Link DCS-934L). With this plugin you can expose those cameras to HomeKit as motion sensors! (Actually, this plugin exists to do essentially the same thing as my other plugin [FileSensor](https://github.com/sphtkr/homebridge-filesensor), except there was a problem with that one--Mac OS X's built-in FTP server was not sending `FsEvents` when a new file was uploaded, making the file monitoring approach impractical.)

## Configuration

Example:

"platforms": [
{
"platform": "SmtpSensor",
"port": "2525",
"sensors": [
{
"name": "Living Room Camera",
"keyword": "Living Room"
}
,
{
"name": "Kitchen Camera",
"keyword": "Kitchen"
}
]
}
]

### Global Configuration

| Key | Default | Description |
| --- | --- | --- |
| `port` | `2525` | The port on which to listen for SMTP connections. |
| `sensors` | N/A | The configuration for one or more sensors, see below. |

### Per-Sensor Configuration

This plugin is implemented as a platform so that you can create as many sensors as you want but still run only one SMTP listener on a single port. So, you must define each sensor you want to create in the `sensors` array.

| Key | Default | Description |
| --- | --- | --- |
| `name` | N/A | The display name of the sensor. **REQUIRED** |
| `keyword` | N/A | The string to look for in received email messages that triggers this sensor. This may appear in either the subject or message body (text, not HTML!). **REQUIRED** |
| `window_seconds` | `62` | The length of time that the sensor will stay triggered before falling back to its default state. |
| `sensor_type` | `"m"` | Currently either "m" for motion sensor or "c" for contact sensor. |
| `inverse` | `false` | If needed, you can invert the behavior of the sensor, for instance so that receiving an email means there is ''no'' motion or contact detected. |

### Configuring your camera or other Device

Point your SMTP sending capable device at your Homebridge server's IP and the port you configured in `config.json`. Configuring a given device is out of scope of this documentation, however keep the following in mind:

* Your device probably lets you set the frequency of mail messages. Marry this up with the `window_seconds` parameter if you don't want a lot of flapping (e.g. if your camera sends an email every 90 seconds, and the sensor falls back to "off" after the default 62 seconds, constant motion will result in on-off-on-off-on...).
* There is currently no SMTP authentication supported--the assumption is that this will run in a protected network, and since the messages are not (currently) forwarded on, there's no risk of spambots using it as a relay. So, don't try to specify a username or password, as this may prevent successful connections. If this is a problem for your device, open an issue and this may be added.
* SSL/TLS is not required. Enabling it ''should'' work and has worked in limited testing, but again: no relaying, no authentication, no encryption seems necessary. Again, open an issue if this causes you problems.

## Future

There are a number of possible enhancements, but I'm not sure what folks need.

* **Forwarding**: If you *also* want to receive the emails sent by your device, we may have to build in forwarding of messages. However, this will require enabling authentication and encryption, which is possible but non-trivial.
* **"Off" Triggering**: Currently, received mail messages only trigger "on" events, with a time-based fallback to the "off" position (or vice-versa if you use `inverse`). It would be possible to add a `keyword_off` key or similar to allow one message keyword to turn the sensor "on" and another message keyword to turn the sensor "off" again. If you need this, open an issue and describe your use case.
132 changes: 132 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
var Service;
var Characteristic;
var SMTPServer = require('smtp-server').SMTPServer;
var MailParser = require("mailparser").MailParser;
var debug = require("debug")("SmtpSensorAccessory");
var crypto = require("crypto");

module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;

homebridge.registerAccessory("homebridge-smtpsensor", "SmtpSensor", SmtpSensorAccessory);
homebridge.registerPlatform("homebridge-smtpsensor", "SmtpSensor", SmtpSensorPlatform);
}

function SmtpSensorPlatform(log, config){
this.log = log;
this.port = config["port"] || 2525;
this.sensors = config["sensors"] || [];
}

SmtpSensorPlatform.prototype = {

accessories: function(callback) {
var sensorAccessories = [];
for(var i = 0; i < this.sensors.length; i++){
var sensor = new SmtpSensorAccessory(this.log, this.sensors[i]);
sensorAccessories.push(sensor);
}
var saCount = sensorAccessories.length;
callback(sensorAccessories);

var server = new SMTPServer({
disabledCommands: ['AUTH']
,
onData: function(stream, session, callback){
debug("Data received...");
var mailparser = new MailParser();
mailparser.on("end", function(mail_object){
debug("Subject: ", mail_object.subject);
debug("Text: ", mail_object.text);
for(var i = 0; i < saCount; i++){
if(sensorAccessories[i].pattern.test(mail_object.subject)){
debug("Sensor triggered on subject!");
sensorAccessories[i].changeHandler();
}
if(sensorAccessories[i].pattern.test(mail_object.text)){
debug("Sensor triggered on body text!");
sensorAccessories[i].changeHandler();
}
}
});
stream.pipe(mailparser);
stream.on('end', callback);
}
,
onAuth: function(auth, session, callback){
debug("Authentication attempted...");
callback(null, {user: "anonymous"});
}
});
server.listen(this.port);
}
}

function SmtpSensorAccessory(log, sensorConfig) {
this.log = log;

// url info
this.keyword = sensorConfig["keyword"];
this.pattern = new RegExp(this.keyword, 'i');
this.name = sensorConfig["name"];
this.window_seconds = sensorConfig["window_seconds"] || 62;
this.sensor_type = sensorConfig["sensor_type"] || "m";
this.inverse = sensorConfig["inverse"] || false;

if(sensorConfig["sn"]){
this.sn = sensorConfig["sn"];
} else {
var shasum = crypto.createHash('sha1');
shasum.update(this.keyword);
this.sn = shasum.digest('base64');
debug('Computed SN ' + this.sn);
}
}

SmtpSensorAccessory.prototype = {

getServices: function() {

// you can OPTIONALLY create an information service if you wish to override
// the default values for things like serial number, model, etc.
var informationService = new Service.AccessoryInformation();

informationService
.setCharacteristic(Characteristic.Name, this.name)
.setCharacteristic(Characteristic.Manufacturer, "Homebridge")
.setCharacteristic(Characteristic.Model, "SMTP Sensor")
.setCharacteristic(Characteristic.SerialNumber, this.sn);

var service, changeAction;
if(this.sensor_type === "c"){
service = new Service.ContactSensor();
changeAction = function(newState){
service.getCharacteristic(Characteristic.ContactSensorState)
.setValue(newState ? Characteristic.ContactSensorState.CONTACT_DETECTED : Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);
};
} else {
service = new Service.MotionSensor();
changeAction = function(newState){
service.getCharacteristic(Characteristic.MotionDetected)
.setValue(newState);
};
}

this.changeHandler = function(){
var d = new Date();
var newState = this.inverse ? false : true;
if(this.timer == undefined){
changeAction(newState);
} else {
clearTimeout(this.timer);
}
this.timer = setTimeout(function(){
changeAction(!newState);
delete this.timer;
}, this.window_seconds * 1000);
}.bind(this);

return [informationService, service];
}
};
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "homebridge-smtpsensor",
"description": "SMTP Sensor plugin for homebridge: https://github.com/nfarina/homebridge",
"version": "0.1.0",
"repository": {
"type": "git",
"url": "git://github.com/SphtKr/homebridge-smtpsensor.git"
},
"license": "ISC",
"preferGlobal": true,
"keywords": [
"homebridge-plugin",
"motion",
"camera",
"HomeKit"
],
"engines": {
"node": ">=0.12.0",
"homebridge": ">=0.2.5"
},
"dependencies": {
"smtp-server": "^1.7.1",
"mailparser": "^0.5.3",
"debug": "^2.2.0"
}
}

0 comments on commit 6ea276f

Please sign in to comment.