├── database └── README.md ├── .npmignore ├── extensions ├── mp3 │ ├── RedAlert.mp3 │ ├── CloisterBell.mp3 │ ├── church-bells.mp3 │ └── Ringing-clock.mp3 ├── blankExtension.js ├── mp3Extension.js ├── IFTTTExtension.js ├── rpioExtension.js └── sayExtension.js ├── CHANGES.md ├── typings ├── index.d.ts └── globals │ ├── node │ └── typings.json │ ├── rpio │ ├── typings.json │ └── index.d.ts │ ├── moment │ └── typings.json │ └── commander │ └── typings.json ├── typings.json ├── .gitattributes ├── lib ├── brains │ ├── controller.js │ └── bitdogBrain.js ├── eventProcessor.js ├── alarm │ ├── zone.js │ ├── alarmMode.js │ └── bitdogAlarm.js ├── zwave │ ├── zwaveScene.js │ ├── zwaveInstance.js │ ├── zwaveClass.js │ ├── zwaveHome.js │ ├── zwaveCommandClasses.js │ ├── zwaveNode.js │ └── zwaveValue.js ├── automation │ ├── weatherManager.js │ ├── bitdogAutomation.js │ ├── scheduler.js │ └── eventCapturer.js ├── systemEvents.js ├── extensionBase.js ├── ipcManager.js ├── cameras │ ├── websocketsProcess.js │ ├── videoStreamer.js │ ├── dvrManager.js │ └── videoManager.js └── constants.js ├── BitdogHub.sln ├── bin ├── motionHandler.js └── bitdoghub ├── README.md ├── package.json ├── .gitignore ├── main.js └── BitdogHub.njsproj /database/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | obj/ 2 | .ntvs_analysis.dat 3 | iotbridge.njsproj 4 | *.dll 5 | *.suo 6 | *.sln 7 | 8 | -------------------------------------------------------------------------------- /extensions/mp3/RedAlert.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitdog-io/bitdog-hub/HEAD/extensions/mp3/RedAlert.mp3 -------------------------------------------------------------------------------- /extensions/mp3/CloisterBell.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitdog-io/bitdog-hub/HEAD/extensions/mp3/CloisterBell.mp3 -------------------------------------------------------------------------------- /extensions/mp3/church-bells.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitdog-io/bitdog-hub/HEAD/extensions/mp3/church-bells.mp3 -------------------------------------------------------------------------------- /extensions/mp3/Ringing-clock.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitdog-io/bitdog-hub/HEAD/extensions/mp3/Ringing-clock.mp3 -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # June 23, 2017 2 | Changed readme to better reflect project intent. 3 | Added weather API. 4 | Fixed automation start when there is no zwave configuration. 5 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "commander": "registry:dt/commander#2.3.0+20160317120654", 4 | "moment": "registry:dt/moment#2.11.1+20161010105546", 5 | "node": "registry:dt/node#7.0.0+20170322231424", 6 | "rpio": "registry:dt/rpio#0.0.0+20160921181726" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/node/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/a4a912a0cd1849fa7df0e5d909c8625fba04e49d/node/index.d.ts", 5 | "raw": "registry:dt/node#7.0.0+20170322231424", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/a4a912a0cd1849fa7df0e5d909c8625fba04e49d/node/index.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/rpio/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/fb972b28d215806c57280990833b6e7102012056/rpio/index.d.ts", 5 | "raw": "registry:dt/rpio#0.0.0+20160921181726", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/fb972b28d215806c57280990833b6e7102012056/rpio/index.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/moment/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/637e7d6df755e785387d5269cb9287cdc51b8cb7/moment/moment.d.ts", 5 | "raw": "registry:dt/moment#2.11.1+20161010105546", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/637e7d6df755e785387d5269cb9287cdc51b8cb7/moment/moment.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/commander/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/commander/commander.d.ts", 5 | "raw": "registry:dt/commander#2.3.0+20160317120654", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/commander/commander.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /lib/brains/controller.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // controller.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /BitdogHub.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "BitdogHub", "BitdogHub.njsproj", "{38F4CFE3-F058-4502-A3AC-88A177BCE85C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {38F4CFE3-F058-4502-A3AC-88A177BCE85C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {38F4CFE3-F058-4502-A3AC-88A177BCE85C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {38F4CFE3-F058-4502-A3AC-88A177BCE85C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {38F4CFE3-F058-4502-A3AC-88A177BCE85C}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /bin/motionHandler.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | //----------------------------------------------------------------------------- 4 | // 5 | // motionHandler.js 6 | // 7 | // Copyright (c) 2015-2017 Bitdog LLC. 8 | // 9 | // SOFTWARE NOTICE AND LICENSE 10 | // 11 | // This file is part of bitdog-hub. 12 | // 13 | // bitdog-hub is free software: you can redistribute it and/or modify 14 | // it under the terms of the GNU General Public License as published 15 | // by the Free Software Foundation, either version 3 of the License, 16 | // or (at your option) any later version. 17 | // 18 | // bitdog-hub is distributed in the hope that it will be useful, 19 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | // GNU General Public License for more details. 22 | // 23 | // You should have received a copy of the GNU General Public License 24 | // along with bitdog-hub. If not, see . 25 | // 26 | //----------------------------------------------------------------------------- 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](http://s.gravatar.com/avatar/d1004ac0d5f3456f6909f9933e2980ec?s=80) 2 | 3 | # Bitdog Hub implementation for Node.js 4 | 5 | [![npm version](https://badge.fury.io/js/bitdog-hub.svg)](http://badge.fury.io/js/bitdog-hub) 6 | 7 | [![NPM](https://nodei.co/npm/bitdog-hub.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/bitdog-hub/) 8 | 9 | [![NPM](https://nodei.co/npm-dl/bitdog-hub.png?height=3)](https://nodei.co/npm/bitdog-hub/) 10 | 11 | [![GitHub version](https://badge.fury.io/gh/bitdog-io%2Fbitdog-hub.png)](http://badge.fury.io/gh/bitdog-io%2Fbitdog-hub) 12 | 13 | 14 | # Overview 15 | Bitdog Hub is a free home automation system for Node.js and Raspberry Pi. It enables the control of Z-Wave devices and local GPIO. To download 16 | the free mobile app or read more please visit our websites, [Bitdog Website](https://bitdog.io) and [Bitdog Cookbook](https://cookbook.bitdog.io) 17 | 18 | # Installation 19 | Although you can install bitdog-hub using NPM, we suggest following these [instructions](https://cookbook.bitdog.io/installing-bitdog-hub) 20 | 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitdog-hub", 3 | "version": "2.0.16", 4 | "description": "Bitdog Home Security and Automation", 5 | "keywords": [ "iot" ,"z-wave", "zwave", "raspberry pi" ,"home automation" ,"sensor", "openzwave" ], 6 | "main": "./main.js", 7 | "homepage": "https://bitdog.io", 8 | "author": { 9 | "name": "Bitdog LLC", 10 | "email": "support@bitdog.io" 11 | }, 12 | "bugs": { 13 | "email" : "support@bitdog.io", 14 | "url": "https://github.com/bitdog-io/bitdog-hub/issues" 15 | }, 16 | "license": "GPL-3.0", 17 | "engines": { 18 | "node": ">=8.4.0" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/bitdog-io/bitdog-hub" 23 | }, 24 | "bin": { 25 | "bitdoghub": "./bin/bitdoghub" 26 | }, 27 | "dependencies": { 28 | "bitdog-client": "git+https://github.com/bitdog-io/bitdog-client.git#master", 29 | "commander": "^2.9.0", 30 | "moment": "^2.17.1", 31 | "openzwave-shared": "git+https://github.com/openzwave/node-openzwave-shared.git#master", 32 | "suncalc": "^1.7.0", 33 | "tingodb": "^0.4.2", 34 | "ws": "^3.1.0", 35 | "request": "^2.83.0" 36 | }, 37 | "optionalDependencies": { 38 | "rpio": "^0.9.11" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/eventProcessor.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // eventProcessor.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | function EventProcessor() { 26 | 27 | } 28 | 29 | EventProcessor.prototype.processMessage = function (message) { 30 | this.onProcessMessage(message); 31 | } 32 | 33 | EventProcessor.prototype.onProcessMessage = function (message) { 34 | 35 | } 36 | 37 | module.exports.EventProcessor = EventProcessor; -------------------------------------------------------------------------------- /lib/alarm/zone.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zone.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | var alarmMode = require('./alarmMode.js'); 28 | 29 | function Zone() { 30 | var _name = ''; 31 | var _nodes = []; 32 | var _zwaveNodes = []; 33 | var _id = ''; 34 | var _alarmMode = alarmMode.Disarmed; 35 | 36 | this.__defineGetter__("name", function () { return _name; }); 37 | this.__defineSetter__("name", function (value) { _name = value; }); 38 | this.__defineGetter__("id", function () { return _id; }); 39 | } 40 | 41 | module.exports = Zone; -------------------------------------------------------------------------------- /lib/zwave/zwaveScene.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveScene.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | function ZWaveScene(homeId, id, label, values) { 28 | var _id = id; 29 | var _homeId = homeId; 30 | var _label = label; 31 | var _values = values; 32 | 33 | 34 | this.__defineGetter__("id", function () { return _id; }); 35 | this.__defineGetter__("homeId", function () { return _homeId; }); 36 | this.__defineGetter__("label", function () { return _label; }); 37 | this.__defineSetter__("label", function (value) { _label = value; }); 38 | this.__defineGetter__("values", function () { return _values; }); 39 | 40 | } 41 | 42 | module.exports = ZWaveScene; -------------------------------------------------------------------------------- /extensions/blankExtension.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // blankExtension.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | // Extension will inherit functions from ExtensionBase 27 | // Make sure the path to extensionBase is correct 28 | var ExtensionBase = require('../lib/extensionBase.js'); 29 | var util = require('util'); 30 | 31 | function Extension() { 32 | } 33 | // Extension inherits from ExtensionBase 34 | util.inherits(Extension, ExtensionBase); 35 | 36 | Extension.prototype.onMessage = function (message, configuration, logger) { 37 | }; 38 | 39 | Extension.prototype.onInitialize = function (configuration, logger) { 40 | }; 41 | 42 | // Export your Extension class so it can be loaded by the framework 43 | module.exports = Extension; 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | # ========================= 31 | # Operating System Files 32 | # ========================= 33 | 34 | # OSX 35 | # ========================= 36 | 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear on external disk 45 | .Spotlight-V100 46 | .Trashes 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | 55 | # Windows 56 | # ========================= 57 | 58 | # Windows image file caches 59 | Thumbs.db 60 | ehthumbs.db 61 | 62 | # Folder config file 63 | Desktop.ini 64 | 65 | # Recycle Bin used on file shares 66 | $RECYCLE.BIN/ 67 | 68 | # Windows Installer files 69 | *.cab 70 | *.msi 71 | *.msm 72 | *.msp 73 | 74 | # Windows shortcuts 75 | *.lnk 76 | obj 77 | .ntvs_analysis.dat 78 | *.dll 79 | *.suo 80 | 81 | /BitdogSA.VC.db 82 | /BitdogSA.VC.VC.opendb 83 | *.db 84 | 85 | /typings/globals/commander/index.d.ts 86 | /typings/globals/node/typings.json 87 | .ntvs_analysis.dat.tmp 88 | -------------------------------------------------------------------------------- /lib/alarm/alarmMode.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // alarmMode.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | function AlarmMode() { 27 | this.__defineGetter__('away', function () { return 'Away'; }); 28 | this.__defineGetter__('stay', function () { return 'Stay'; }); 29 | this.__defineGetter__('disarmed', function () { return 'Disarmed'; }); 30 | this.__defineGetter__('alarmed', function () { return 'Alarmed'; }); 31 | 32 | this.__defineGetter__('securityAlarm', function () { return 'Security Alarm'; }); 33 | this.__defineGetter__('safetyAlarm', function () { return 'Safety Alarm'; }); 34 | this.__defineGetter__('alarmClear', function () { return 'Alarm Cleared'; }); 35 | 36 | this.__defineGetter__('modes', function () { return [this.disarmed, this.stay, this.away]; }); 37 | 38 | this.__defineGetter__('status', function () { return [this.safetyAlarm, this.securityAlarm, this.alarmClear]; }); 39 | }; 40 | 41 | module.exports = new AlarmMode(); -------------------------------------------------------------------------------- /lib/zwave/zwaveInstance.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveInstance.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | var ZWaveValue = require('./zwaveValue.js'); 28 | 29 | function ZWaveInstance(id) { 30 | var _id = id; 31 | var _values = []; 32 | 33 | this.updateValue = function (zwaveValueInfo) { 34 | var zwaveValue = getZWaveValue(zwaveValueInfo); 35 | return zwaveValue.updateValue(zwaveValueInfo); 36 | }; 37 | 38 | this.removeValue = function (commandClassId, instanceId, valueIndex) { 39 | for (var index = 0; index < _values.length; index++) { 40 | if (_values[index].id == valueIndex) { 41 | _values.splice(index, 1); 42 | } 43 | 44 | return; 45 | } 46 | } 47 | 48 | this.getValue = function (indexId) { 49 | var zwaveValue = null; 50 | for (var index = 0; index < _values.length; index++) { 51 | if (_values[index].id == indexId) { 52 | zwaveValue = _values[index]; 53 | break; 54 | } 55 | } 56 | 57 | return zwaveValue; 58 | 59 | } 60 | 61 | function getZWaveValue(zwaveValueInfo) { 62 | for (var index = 0; index < _values.length; index++) { 63 | if (_values[index].id == zwaveValueInfo.index) 64 | return _values[index]; 65 | } 66 | 67 | var zwaveValue = new ZWaveValue(zwaveValueInfo); 68 | _values.push(zwaveValue); 69 | 70 | return zwaveValue; 71 | }; 72 | 73 | this.__defineGetter__("id", function () { return _id; }); 74 | this.__defineGetter__("values", function () { return _values; }); 75 | } 76 | 77 | module.exports = ZWaveInstance; -------------------------------------------------------------------------------- /lib/zwave/zwaveClass.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveClass.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | var ZWaveInstance = require('./zwaveInstance.js'); 28 | var zwaveCommandClasses = require('./zwaveCommandClasses.js'); 29 | 30 | 31 | function ZWaveClass(id) { 32 | var _name = zwaveCommandClasses.getCommand(id); 33 | var _id = id; 34 | var _instances = []; 35 | 36 | this.updateValue = function (zwaveValueInfo) { 37 | var zwaveInstance = getZWaveInstance(zwaveValueInfo); 38 | return zwaveInstance.updateValue(zwaveValueInfo); 39 | }; 40 | 41 | this.removeValue = function (commandClassId, instanceId, valueIndex) { 42 | for (var index = 0; index < _instances.length; index++) { 43 | if (_instances[index].id == instanceId) { 44 | var zwaveInstance = _instances[index]; 45 | zwaveInstance.removeValue(commandClassId, instanceId, valueIndex); 46 | 47 | if (zwaveInstance.values.length < 1) { 48 | _instances.splice(index, 1); 49 | } 50 | 51 | return; 52 | } 53 | } 54 | } 55 | 56 | this.getValue = function (instanceId, indexId) { 57 | var zwaveInstance = null; 58 | for (var index = 0; index < _instances.length; index++) { 59 | if (_instances[index].id == instanceId) { 60 | zwaveInstance = _instances[index]; 61 | break; 62 | } 63 | } 64 | 65 | if (zwaveInstance != null) 66 | return zwaveInstance.getValue(indexId); 67 | else 68 | return null; 69 | } 70 | 71 | function getZWaveInstance(zwaveValueInfo) { 72 | for (var index = 0; index < _instances.length; index++) { 73 | if (_instances[index].id == zwaveValueInfo.instance) 74 | return _instances[index]; 75 | } 76 | 77 | var zwaveInstance = new ZWaveInstance(zwaveValueInfo.instance); 78 | _instances.push(zwaveInstance); 79 | 80 | return zwaveInstance; 81 | }; 82 | 83 | this.__defineGetter__("name", function () { return _name; }); 84 | this.__defineGetter__("id", function () { return _id; }); 85 | this.__defineGetter__("instances", function () { return _instances; }); 86 | } 87 | 88 | module.exports = ZWaveClass; -------------------------------------------------------------------------------- /extensions/mp3Extension.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // mp3Extension.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | // Extension will inherit functions from ExtensionBase 28 | // Make sure the path to extensionBase is correct 29 | var ExtensionBase = require('../lib/extensionBase.js'); 30 | var util = require('util'); 31 | var child_process = require('child_process'); 32 | var fs = require('fs'); 33 | var path = require('path'); 34 | 35 | // Create a class called Extension 36 | function Extension() { 37 | } 38 | 39 | // Extension inherits from ExtensionBase 40 | util.inherits(Extension, ExtensionBase); 41 | 42 | // Messages from and to this hub will pass through this function. 43 | // A good spot to watch for Z-Wave messages 44 | Extension.prototype.onMessage = function (message, configuration, logger) { 45 | 46 | // Messages will go through here, check them and do something. 47 | //logger.log('User', 'Got message and handling', message); 48 | 49 | }; 50 | 51 | // This function will be called once during initialization. 52 | // Setup command and data capture here. 53 | Extension.prototype.onInitialize = function (configuration, logger) { 54 | var self = this; 55 | var filePath = path.resolve(path.dirname(__filename), './mp3'); 56 | logger.logProcessEvent('mp3Extension', 'Loading files from file path', filePath); 57 | var files = fs.readdirSync(filePath); 58 | logger.logProcessEvent('mp3Extension', 'Found files', files); 59 | 60 | // Create a custom message schema with one string property. 61 | // add a list of values to the property that are file names 62 | var playMessageSchema = this.createMessageSchema('Play') 63 | .addStringProperty('sound', '', { values: files }, 'The file to play','Sound' ); 64 | 65 | 66 | // Add a command to this hub that plays sound 67 | this.addCommand('Play Sound', playMessageSchema, function (message, configuration, logger) { 68 | var soundToPlay = message.sound; 69 | 70 | if (typeof soundToPlay !== typeof undefined && soundToPlay !== null && soundToPlay !== '') { 71 | var fileToPlay = path.resolve(filePath, soundToPlay); 72 | //child_process.spawn('omxplayer', [fileToPlay]); 73 | child_process.spawn('mplayer',[ '-af', 'volume=10:1', fileToPlay]); 74 | } 75 | 76 | }); 77 | 78 | 79 | }; 80 | 81 | // Export your Extension class so it can be loaded by the framework 82 | module.exports = Extension; 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /lib/zwave/zwaveHome.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveHome.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | var ZWaveNode = require('./zwaveNode.js'); 28 | 29 | function ZWaveHome() { 30 | var _homeId = ""; 31 | var _nodes = []; 32 | 33 | 34 | this.createNode = function (nodeId, zwave) { 35 | var zwaveNode = new ZWaveNode(nodeId, zwave); 36 | _nodes.push(zwaveNode); 37 | 38 | return zwaveNode; 39 | }; 40 | 41 | this.removeNode = function (nodeId) { 42 | var zwaveNode = null; 43 | for (var index = 0; index < _nodes.length; index++) { 44 | if (_nodes[index].id === nodeId) { 45 | zwaveNode = _nodes[index]; 46 | _nodes.splice(index, 1); 47 | break; 48 | } 49 | } 50 | 51 | return zwaveNode; 52 | }; 53 | 54 | this.updateValue = function (nodeId, zwaveValueInfo) { 55 | var zwaveNode = this.getNode(nodeId); 56 | return zwaveNode.updateValue(zwaveValueInfo); 57 | }; 58 | 59 | this.removeValue = function (nodeId, commandClassId, instanceId, index) { 60 | var zwaveNode = this.getNode(nodeId); 61 | zwaveNode.removeValue(commandClassId, instanceId, index); 62 | }; 63 | 64 | this.getValue = function (nodeId, commandClassId, instanceId, indexId) { 65 | var zwaveNode = this.getNode(nodeId); 66 | 67 | if (zwaveNode != null) 68 | return zwaveNode.getValue(commandClassId, instanceId, indexId); 69 | else 70 | return null; 71 | } 72 | 73 | this.setNodeInformation = function (nodeId, zwaveNodeInfo, ready) { 74 | var zwaveNode = this.getNode(nodeId); 75 | 76 | if (zwaveNode !== null) 77 | zwaveNode.setNodeInformation(zwaveNodeInfo,ready); 78 | 79 | return zwaveNode; 80 | }; 81 | 82 | this.getNode = function (nodeId) { 83 | for (var index = 0; index < _nodes.length; index++) { 84 | if (_nodes[index].id === nodeId) 85 | return _nodes[index]; 86 | } 87 | 88 | return null; 89 | }; 90 | 91 | this.setNodeStatus = function (nodeId, status) { 92 | for (var index = 0; index < _nodes.length; index++) { 93 | if (_nodes[index].id === nodeId) { 94 | _nodes[index].status = status; 95 | break; 96 | } 97 | } 98 | }; 99 | 100 | this.__defineGetter__("homeId", function () { return _homeId; }); 101 | this.__defineSetter__("homeId", function (homeId) { _homeId = homeId; }); 102 | this.__defineGetter__("nodes", function () { return _nodes; }); 103 | } 104 | 105 | module.exports = ZWaveHome -------------------------------------------------------------------------------- /lib/automation/weatherManager.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // weatherManager.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | var bitdogClient = require('bitdog-client'); 26 | var constants = require('../constants.js'); 27 | var coreMessageSchemas = require('../coreMessageSchemas.js'); 28 | var moment = require('moment'); 29 | 30 | function WeatherManager() { 31 | 32 | /* 33 | Has it or will it rain / snow within X hour 34 | Current Temperature is over / under 35 | Today's max temperature is over / under 36 | Today's min temperature is over / under 37 | Today's avg temperature is over / under 38 | Today's cloud cover is over / under 39 | Today's precip is over / under 40 | Current humidity is over / under 41 | Current wind is over / under 42 | Today's max wind is over / under 43 | Today's min wind is over / under 44 | Current atmospheric pressure is over / under 45 | */ 46 | 47 | var _forecasts = []; 48 | this.__defineGetter__('forecasts', function () { return _forecasts; }); 49 | 50 | } 51 | 52 | WeatherManager.prototype.processWeatherForecast = function (weatherForecast) { 53 | var self = this; 54 | 55 | weatherForecast.timedate = moment(); 56 | 57 | this.forecasts.push(weatherForecast); 58 | 59 | while (this.forecasts.length > 24 * 30) { this.forecasts.shift(); } 60 | 61 | bitdogClient.sendData('bd-weatherForecast', coreMessageSchemas.weatherForecastMessageSchema, function (message) { 62 | message.currentTemperatureC = weatherForecast.current.temp_c; 63 | message.currentTemperatureF = weatherForecast.current.temp_f; 64 | message.currentPressureIn = weatherForecast.current.pressure_in; 65 | message.currentPressureMb = weatherForecast.current.pressure_mb; 66 | message.currentWindDegree = weatherForecast.current.wind_degree; 67 | message.currentWindDirection = weatherForecast.current.wind_dir; 68 | message.currentWindKph = weatherForecast.current.wind_kph; 69 | message.currentWindMph = weatherForecast.current.wind_mph; 70 | message.todayAvgTemperatureC = weatherForecast.forecast.forecastday[0].day.avgtemp_c; 71 | message.todayAvgTemperatureF = weatherForecast.forecast.forecastday[0].day.avgtemp_f; 72 | message.todayMaxTemperatureC = weatherForecast.forecast.forecastday[0].day.maxtemp_c; 73 | message.todayMaxTemperatureF = weatherForecast.forecast.forecastday[0].day.maxtemp_f; 74 | message.todayMaxWindKph = weatherForecast.forecast.forecastday[0].day.maxwind_kph; 75 | message.todayMaxWindMph = weatherForecast.forecast.forecastday[0].day.maxwind_mph; 76 | message.todayMinTemperatureC = weatherForecast.forecast.forecastday[0].day.mintemp_c; 77 | message.todayMinTemperatureF = weatherForecast.forecast.forecastday[0].day.mintemp_f; 78 | message.todayTotalPrecipitationIn = weatherForecast.forecast.forecastday[0].day.totalprecip_in; 79 | message.todayTotalPrecipitationMm =  weatherForecast.forecast.forecastday[0].day.totalprecip_mm; 80 | }); 81 | } 82 | 83 | 84 | var weatherManager = new WeatherManager(); 85 | module.exports = weatherManager; // singleton -------------------------------------------------------------------------------- /lib/systemEvents.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // systemEvents.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | function EventInfo() { 27 | 28 | } 29 | 30 | function SystemEvents(bitdogHub, configuration, logger) { 31 | 32 | function publish(eventInfo) { 33 | bitdogHub.emit('system event', eventInfo, configuration, logger); 34 | } 35 | 36 | this.publishSystemStart = function () { 37 | var eventInfo = new EventInfo(); 38 | eventInfo.name = 'system start'; 39 | eventInfo.text = 'Bitdog Hub is starting'; 40 | 41 | publish(eventInfo); 42 | }; 43 | 44 | this.publishSystemStop = function () { 45 | var eventInfo = new EventInfo(); 46 | eventInfo.name = 'system stop'; 47 | eventInfo.text = 'Bitdog Hub is stopping, good bye'; 48 | 49 | publish(eventInfo); 50 | }; 51 | 52 | this.publishZWaveScanBegin = function () { 53 | var eventInfo = new EventInfo(); 54 | eventInfo.name = 'zwave scan begin'; 55 | eventInfo.text = 'Scanning ZWave network, please wait'; 56 | publish(eventInfo); 57 | }; 58 | 59 | this.publishZWaveScanEnd = function () { 60 | var eventInfo = new EventInfo(); 61 | eventInfo.name = 'zwave scan end'; 62 | eventInfo.text = 'Finished scanning ZWave network'; 63 | publish(eventInfo); 64 | }; 65 | 66 | this.publishSystemReady = function () { 67 | var eventInfo = new EventInfo(); 68 | eventInfo.name = 'system ready'; 69 | eventInfo.text = 'Bitdog Hub is now ready'; 70 | publish(eventInfo); 71 | }; 72 | 73 | this.publishSecurityArmAway = function () { 74 | var eventInfo = new EventInfo(); 75 | eventInfo.name = 'security arm away'; 76 | eventInfo.text = 'Bitdog security armed, set for away mode'; 77 | publish(eventInfo); 78 | }; 79 | 80 | this.publishSecurityArmStay = function () { 81 | var eventInfo = new EventInfo(); 82 | eventInfo.name = 'security arm stay'; 83 | eventInfo.text = 'Bitdog security armed, set for stay mode'; 84 | publish(eventInfo); 85 | }; 86 | 87 | this.publishSecurityDisarmed = function () { 88 | var eventInfo = new EventInfo(); 89 | eventInfo.name = 'security disarmed'; 90 | eventInfo.text = 'Bitdog security has been disarmed'; 91 | publish(eventInfo); 92 | }; 93 | 94 | this.publishZWaveAddNode = function (zwaveNode) { 95 | var eventInfo = new EventInfo(); 96 | eventInfo.name = 'zwave add node'; 97 | eventInfo.text = 'Added ZWave device, ' + zwaveNode.displayName; 98 | eventInfo.zwaveNode = zwaveNode; 99 | publish(eventInfo); 100 | }; 101 | 102 | this.publishZWaveRemoveNode = function (zwaveNode) { 103 | var eventInfo = new EventInfo(); 104 | eventInfo.name = 'zwave remove node'; 105 | eventInfo.text = 'Removed ZWave device, ' + zwaveNode.displayName; 106 | eventInfo.zwaveNode = zwaveNode; 107 | publish(eventInfo); 108 | }; 109 | } 110 | 111 | module.exports = SystemEvents; -------------------------------------------------------------------------------- /lib/extensionBase.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // extensionBase.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | function ExtensionBase() { 28 | 29 | 30 | } 31 | 32 | ExtensionBase.prototype._bitdogHub = null; 33 | 34 | ExtensionBase.prototype.__defineGetter__('bitdogHub', function () { return this._bitdogHub; }); 35 | 36 | ExtensionBase.prototype.__defineSetter__('bitdogHub', function (value) { 37 | var self = this; 38 | this._bitdogHub = value; 39 | 40 | this._bitdogHub.on('message', function (message, configuration, logger) { 41 | self.processMessage(message, configuration, logger); 42 | }); 43 | 44 | this._bitdogHub.on('system event', function (eventInfo, configuration, logger) { 45 | self.processSystemEvent(eventInfo, configuration, logger); 46 | }); 47 | }); 48 | 49 | ExtensionBase.prototype.__defineGetter__('configuration', function () { return this._bitdogHub.bitdogClient.configuration; }); 50 | 51 | ExtensionBase.prototype.__defineGetter__('constants', function () { return this._bitdogHub.bitdogClient.constants; }); 52 | 53 | 54 | ExtensionBase.prototype.onInitialize = function (configuration, logger) { 55 | }; 56 | 57 | ExtensionBase.prototype.onMessage = function (message, configuration, logger) { 58 | }; 59 | 60 | ExtensionBase.prototype.onSystemEvent = function (eventInfo, configuration, logger) { 61 | }; 62 | 63 | ExtensionBase.prototype.processMessage = function (message, configuration, logger) { 64 | try { 65 | this.onMessage(message, configuration, logger); 66 | } 67 | catch (error) { 68 | logger.logProcessEvent('Bitdog Hub', 'Process message exception: ', error); 69 | } 70 | }; 71 | 72 | ExtensionBase.prototype.processSystemEvent = function (eventInfo, configuration, logger) { 73 | try { 74 | this.onSystemEvent(eventInfo, configuration, logger); 75 | } 76 | catch (error) { 77 | logger.logProcessEvent('Bitdog Hub', 'Process system event exception: ', error); 78 | } 79 | }; 80 | 81 | ExtensionBase.prototype.addCommand = function (name, messageSchema, executeCallback, startCallback, stopCallback) { 82 | return this.bitdogHub.bitdogClient.addCommand(name, messageSchema, executeCallback, startCallback, stopCallback, false); 83 | }; 84 | 85 | ExtensionBase.prototype.addDataCollector = function (name, messageSchema, intervalMilliseconds, collectCallback) { 86 | return this.bitdogHub.bitdogClient.addDataCollector(name, messageSchema, intervalMilliseconds, collectCallback, false); 87 | }; 88 | 89 | ExtensionBase.prototype.addData = function (name, messageSchema) { 90 | return this.bitdogHub.bitdogClient.addData(name, messageSchema, false); 91 | }; 92 | 93 | 94 | ExtensionBase.prototype.createMessageSchema = function (name) { 95 | return this.bitdogHub.bitdogClient.createMessageSchema(name); 96 | }; 97 | 98 | ExtensionBase.prototype.sendData = function (name, messageSchema, callback) { 99 | return this.bitdogHub.bitdogClient.sendData(name, messageSchema, callback); 100 | }; 101 | 102 | ExtensionBase.prototype.sendIFTTTCommand = function (appId, name, callback) { 103 | return this._bitdogHub.bitdogClient.sendIFTTTCommand(appId, name, callback); 104 | }; 105 | 106 | 107 | module.exports = ExtensionBase; -------------------------------------------------------------------------------- /lib/ipcManager.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // ipcManager.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | var http = require('http'); 27 | var bitdogClient = require('bitdog-client'); 28 | var constants = require('./constants.js'); 29 | var path = require('path'); 30 | var coreMessageSchemas = require('./coreMessageSchemas.js'); 31 | 32 | _httpServer = null; 33 | _isRunning = false; 34 | _ipcPort = bitdogClient.configuration.get(constants.IPC_PORT); 35 | 36 | function IPCManager() { 37 | 38 | 39 | } 40 | 41 | IPCManager.prototype.start = function () { 42 | if (_isRunning === false) { 43 | _isRunning = true; 44 | startHttpServer(); 45 | } 46 | }; 47 | 48 | IPCManager.prototype.stop = function () { 49 | if (_isRunning === true) { 50 | _isRunning = false; 51 | 52 | if (_httpServer !== null) 53 | _httpServer.close(); 54 | } 55 | } 56 | 57 | function startHttpServer() { 58 | var ipcPort = bitdogClient.configuration.get(constants.IPC_PORT); 59 | 60 | if (ipcPort !== typeof undefined || ipcPort === null) 61 | ipcPort = 9001; 62 | 63 | _httpServer = http.createServer(function (req, res) { 64 | var message = ''; 65 | 66 | req.on('data', function (data) { 67 | message += data; 68 | }); 69 | 70 | req.on('end', function () { 71 | var result = false; 72 | try { 73 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_IPC_MANAGER, 'Received IPC message', message); 74 | message = JSON.parse(message); 75 | processIPCMessage(message); 76 | result = true; 77 | } 78 | catch (error) { 79 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_IPC_MANAGER, 'Could not parse IPC message', error); 80 | } 81 | 82 | 83 | res.setHeader('Content-Type', 'application/json'); 84 | res.end(JSON.stringify({ result: result })); 85 | }); 86 | 87 | 88 | }).listen(_ipcPort, '127.0.0.1', function () { 89 | var address = _httpServer.address().address; 90 | var port = _httpServer.address().port; 91 | 92 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_IPC_MANAGER, 'IPC HTTP listener ready', { host: address, port: port }); 93 | 94 | }); 95 | 96 | _httpServer.on('close', function () { 97 | 98 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_IPC_MANAGER, 'IPC HTTP listener stopped'); 99 | 100 | }); 101 | } 102 | 103 | function processIPCMessage(message) { 104 | 105 | switch (message.name) { 106 | case 'motion-event-start': 107 | bitdogClient.sendData('bd-videoMotionEventStart', coreMessageSchemas.videoMotionEventStartMessageSchema, function (newMessage) { 108 | }); 109 | break; 110 | case 'motion-event-end': 111 | bitdogClient.sendData('bd-videoMotionEventEnd', coreMessageSchemas.videoMotionEventEndMessageSchema, function (newMessage) { 112 | }); 113 | break; 114 | case 'motion-image-captured': 115 | bitdogClient.sendData('bd-videoMotionImageCaptured', coreMessageSchemas.videoMotionImageCapturedMessageSchema, function (newMessage) { 116 | newMessage.imageFileName = path.basename(message.imagePath); 117 | }); 118 | break; 119 | default: 120 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_IPC_MANAGER, 'IPC message invalid', message); 121 | break; 122 | } 123 | 124 | } 125 | 126 | module.exports = new IPCManager(); -------------------------------------------------------------------------------- /lib/zwave/zwaveCommandClasses.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveCommandClasses.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | function ZWaveCommandClasses() { 27 | var commandClasses = { 28 | 'No Operation': 0, 29 | 'Basic': 32, 30 | 'Controller Replication': 33, 31 | 'Application Status': 34, 32 | 'ZIP Services': 35, 33 | 'ZIP Server': 36, 34 | 'Switch Binary': 37, 35 | 'Switch Multilevel': 38, 36 | 'Switch All': 39, 37 | 'Switch Toggle Binary': 40, 38 | 'Switch Toggle Multilevel': 41, 39 | 'Chimney Fan': 42, 40 | 'Scene Activation': 43, 41 | 'Scene Actuator Configuration': 44, 42 | 'Scene Controller Configuration': 45, 43 | 'ZIP Client': 46, 44 | 'ZIP Advanced Services': 47, 45 | 'Sensor Binary': 48, 46 | 'Sensor Multilevel': 49, 47 | 'Meter': 50, 48 | 'Switch Color': 51, 49 | 'Network Management Inclusion': 52, 50 | 'Meter Pulse': 53, 51 | 'Meter Tbl Config': 60, 52 | 'Meter Tbl Monitor': 61, 53 | 'Meter Tbl Push': 62, 54 | 'Thermostat Heating': 56, 55 | 'Thermostat Mode': 64, 56 | 'Thermostat Operating State': 66, 57 | 'Thermostat Setpoint': 67, 58 | 'Thermostat Fan Mode': 68, 59 | 'Thermostat Fan State': 69, 60 | 'Climate Control Schedule': 70, 61 | 'Thermostat Setback': 71, 62 | 'Door Lock Logging': 76, 63 | 'Schedule Entry Lock': 78, 64 | 'Basic Window Covering': 80, 65 | 'MTP Window Covering': 81, 66 | 'Association Grp Info': 89, 67 | 'Device Reset Locally': 90, 68 | 'Central Scene': 91, 69 | 'IP Association': 92, 70 | 'Antitheft': 93, 71 | 'ZwavePlus Info': 94, 72 | 'Multi Instance': 96, 73 | 'Door Lock': 98, 74 | 'User Code': 99, 75 | 'Barrier Operator': 102, 76 | 'Configuration': 112, 77 | 'Alarm': 113, 78 | 'Manufacturer Specific': 114, 79 | 'Powerlevel': 115, 80 | 'Protection': 117, 81 | 'Lock': 118, 82 | 'Node Naming': 119, 83 | 'Firmware Update MD': 122, 84 | 'Grouping Name': 123, 85 | 'Remote Association Activate': 124, 86 | 'Remote Association': 125, 87 | 'Battery': 128, 88 | 'CLock': 129, 89 | 'Hail': 130, 90 | 'Wake Up': 132, 91 | 'Association': 133, 92 | 'Version': 134, 93 | 'Indicator': 135, 94 | 'Proprietary': 136, 95 | 'Language': 137, 96 | 'Time': 138, 97 | 'Time ParaMeters': 139, 98 | 'Geographic Location': 140, 99 | 'Composite': 141, 100 | 'Multi Instance Association': 142, 101 | 'Multi CMD': 143, 102 | 'Energy Production': 144, 103 | 'Manufacturer Proprietary': 145, 104 | 'Screen MD': 146, 105 | 'Screen Attributes': 147, 106 | 'Simple AV Control': 148, 107 | 'AV Content Directory MD': 149, 108 | 'AV Renderer Status': 150, 109 | 'AV Content Search MD': 151, 110 | 'Security': 152, 111 | 'AV Tagging MD': 153, 112 | 'IP Configuration': 154, 113 | 'Association Command Configuration': 155, 114 | 'Alarm Sensor': 156, 115 | 'Alarm Silence': 157, 116 | 'Sensor Configuration': 158, 117 | 'Mark': 239, 118 | 'Non Interoperable': 240 119 | }; 120 | 121 | this.getCommand = function (number) { 122 | for (var propertyName in commandClasses) { 123 | if (commandClasses[propertyName] == number) 124 | return propertyName; 125 | } 126 | 127 | return number.toString(); 128 | }; 129 | } 130 | 131 | module.exports = new ZWaveCommandClasses(); -------------------------------------------------------------------------------- /extensions/IFTTTExtension.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // IFTTTExtension.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | // Extension will inherit functions from ExtensionBase 28 | // Make sure the path to extensionBase is correct 29 | var ExtensionBase = require('../lib/extensionBase.js'); 30 | var util = require('util'); 31 | 32 | // Example Raspberry PI I/O library 33 | var rpio = require('rpio'); 34 | 35 | var API_KEY = 'test'; 36 | var ledPin = 11; // hardware pin 11 37 | var buttonPin = 13; // hardware pin 13 38 | 39 | var blinkTimer = null; 40 | 41 | // Initialize a variable to hold button state 42 | var buttonState = 'unknown'; 43 | 44 | // By default the rpio module will use /dev/gpiomem 45 | // when using simple GPIO access. 46 | // To access this device, your user will 47 | // need to be a member of the gpio group, 48 | // and you may need to configure udev with the following rule (as root): 49 | 50 | //$ cat >/etc/udev/rules.d/20-gpiomem.rules << EOF 51 | //SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660" 52 | //EOF 53 | 54 | // Create a class called Extension 55 | function Extension() { 56 | 57 | } 58 | 59 | // Extension inherits from ExtensionBase 60 | util.inherits(Extension, ExtensionBase); 61 | 62 | // Messages from and to this hub will pass through this function. 63 | // A good spot to watch for Z-Wave messages 64 | Extension.prototype.onMessage = function (message, configuration, logger) { 65 | 66 | // Messages will go through here, check them and do something. 67 | //logger.log('User', 'Got message and handling', message); 68 | 69 | }; 70 | 71 | // This function will be called once during initialization. 72 | // Setup command and data capture here. 73 | Extension.prototype.onInitialize = function (configuration, logger) { 74 | var self = this; 75 | 76 | var options = { 77 | gpiomem: true,          /* Use /dev/gpiomem */ 78 | mapping: 'physical',    /* Use the P1-P40 numbering scheme */ 79 | }; 80 | 81 | rpio.init(options); 82 | 83 | 84 | // Create a custom message schema with one string property. 85 | var onOffMessageSchema = this.createMessageSchema('OnOff') 86 | .addStringProperty('value1', 'off', { values: ['on', 'off'] }); 87 | 88 | // Open a GPIO pin for our LED, initialize to off. 89 | rpio.open(ledPin, rpio.OUTPUT, rpio.HIGH); 90 | // Open a GPIO pin for our switch 91 | rpio.open(buttonPin, rpio.INPUT, rpio.PULL_DOWN); 92 | 93 | // Create a callback function for the rpio polling routine 94 | function pollcb(pin) { 95 | // Read the state of the switch pin 96 | var newState = rpio.read(pin) ? 'on' : 'off'; 97 | 98 | // If the switch pin has transitioned to a new value... 99 | if (newState !== buttonState) { 100 | // Set the variable 101 | buttonState = newState; 102 | 103 | // Send a message to our IFTTT cloud account about our new switch state. 104 | self.sendIFTTTCommand(API_KEY,'Switch Status', function (message) { 105 | message.value1 = buttonState; 106 | }); 107 | } 108 | } 109 | 110 | // The the rpio library to poll a pin 111 | rpio.poll(buttonPin, pollcb); 112 | 113 | // Add a command to this hub that turns the LED on and off 114 | this.addCommand('Turn LED on/off', onOffMessageSchema, function (message, configuration, logger) { 115 | 116 | // check if we already have a timer running 117 | if (blinkTimer === null) { 118 | 119 | // turn the LED on by setting pin low 120 | rpio.write(ledPin, rpio.LOW); 121 | 122 | // turn off LED after 30 seconds 123 | blinkTimer = setTimeout(function () { 124 | rpio.write(ledPin, rpio.HIGH); 125 | blinkTimer = null; 126 | }, 30000); 127 | } 128 | 129 | }); 130 | 131 | 132 | }; 133 | 134 | // Export your Extension class so it can be loaded by the framework 135 | module.exports = Extension; 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // main.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | var bitdogHub = require('./lib/bitdogHub.js'); 27 | var bitdogClient = require('bitdog-client'); 28 | var program = require('commander'); 29 | var path = require('path'); 30 | var constants = require('./lib/constants.js'); 31 | var savedExtensionPaths = bitdogClient.configuration.get(constants.EXTENSION_PATHS); 32 | 33 | if (typeof savedExtensionPaths === typeof undefined || savedExtensionPaths === null) 34 | savedExtensionPaths = []; 35 | 36 | // Create serializable error objects 37 | if (!('toJSON' in Error.prototype)) { 38 | Object.defineProperty(Error.prototype, 'toJSON', { 39 | value: function () { 40 | var alt = {}; 41 | 42 | Object.getOwnPropertyNames(this).forEach(function (key) { 43 | alt[key] = this[key]; 44 | }, this); 45 | 46 | return alt; 47 | }, 48 | configurable: true, 49 | writable: true 50 | }); 51 | } 52 | 53 | process.on('SIGINT', function () { 54 | setTimeout(function () { 55 | bitdogClient.logger.logProcessEvent('Bitdog Hub', 'Calling process exit...'); 56 | process.exit(0); 57 | }, 10000); 58 | 59 | bitdogClient.logger.logProcessEvent('Bitdog Hub', 'SIGINT, stopping.'); 60 | 61 | bitdogHub.stop(); 62 | 63 | }); 64 | 65 | process.on('uncaughtException', function (error) { 66 | bitdogClient.logger.logProcessEvent('Bitdog Hub', 'Unhandled exception: ' , error); 67 | process.kill(process.pid, 'SIGINT'); 68 | }); 69 | 70 | program 71 | .version('2.0.01') 72 | .description('Bitdog Hub') 73 | .option('-l,--logpath ', 'The direcotry for log files.') 74 | .option('-c,--configpath ', 'The directory for configuration files.') 75 | .option('-t,--tail', 'Write logs to console also.') 76 | .option('-e,--extension ', 'Path to file that has extension code. Use clear to remote extension from stored configuration.'); 77 | 78 | 79 | program.parse(process.argv); 80 | 81 | if (typeof program.tail === typeof undefined) { 82 | bitdogClient.configuration.logToConsole = false; 83 | } else { 84 | bitdogClient.configuration.logToConsole = true; 85 | } 86 | 87 | if (typeof program.logpath !== typeof undefined) { 88 | var logFilePath = path.resolve(program.logpath, bitdogClient.constants.LOG_FILE_NAME); 89 | bitdogClient.configuration.logFilePath = logFilePath; 90 | } 91 | 92 | console.log("Configuration file path is " + bitdogClient.configuration.configFilePath); 93 | console.log("Logging to " + bitdogClient.configuration.logFilePath); 94 | 95 | 96 | if (typeof program.extension !== typeof undefined) { 97 | 98 | if (program.extension === 'clear') { 99 | savedExtensionPaths = []; 100 | bitdogClient.configuration.set(constants.EXTENSION_PATHS, savedExtensionPaths); 101 | } 102 | else { 103 | saveExtension(path.resolve(program.extension)); 104 | } 105 | } 106 | 107 | loadExtensions(); 108 | 109 | bitdogHub.start(); 110 | 111 | function saveExtension(extensionFilePath) { 112 | savedExtensionPaths.push(extensionFilePath); 113 | bitdogClient.configuration.set(constants.EXTENSION_PATHS, savedExtensionPaths); 114 | } 115 | 116 | function loadExtensions() { 117 | //process.chdir(__dirname); 118 | //console.log("Setting working directory to " + __dirname); 119 | var extensionFilePath = ''; 120 | 121 | for (var index = 0; index < savedExtensionPaths.length; index++) { 122 | extensionFilePath = savedExtensionPaths[index]; 123 | console.log("Loading extension at " + extensionFilePath); 124 | 125 | try { 126 | var Extension = require(extensionFilePath); 127 | 128 | var extension = new Extension(); 129 | extension.bitdogHub = bitdogHub; 130 | 131 | extension.onInitialize(bitdogClient.configuration, bitdogClient.logger); 132 | 133 | } 134 | catch (exception) { 135 | bitdogClient.logger.logProcessEvent('Bitdog Hub', 'Exception loading extension: ', { message: exception.message, stack: exception.stack }); 136 | } 137 | } 138 | } 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /extensions/rpioExtension.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // rpioExtension.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | // Extension will inherit functions from ExtensionBase 28 | // Make sure the path to extensionBase is correct 29 | var ExtensionBase = require('../lib/extensionBase.js'); 30 | var util = require('util'); 31 | 32 | // Example Raspberry PI I/O library 33 | var rpio = require('rpio'); 34 | 35 | // By default the rpio module will use /dev/gpiomem 36 | // when using simple GPIO access. 37 | // To access this device, your user will 38 | // need to be a member of the gpio group, 39 | // and you may need to configure udev with the following rule (as root): 40 | 41 | //$ cat >/etc/udev/rules.d/20-gpiomem.rules << EOF 42 | //SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660" 43 | //EOF 44 | 45 | // Create a class called Extension 46 | // This can be any name, give it a good one. 47 | function Extension() { 48 | // Initialize a variable to hold button state 49 | this.buttonState = 'unknown'; 50 | } 51 | 52 | // Extension inherits from ExtensionBase 53 | util.inherits(Extension, ExtensionBase); 54 | 55 | // Messages from and to this hub will pass through this function. 56 | // A good spot to watch for Z-Wave messages 57 | Extension.prototype.onMessage = function (message, configuration, logger) { 58 | 59 | // Messages will go through here, check them and do something. 60 | //logger.log('User', 'Got message and handling', message); 61 | 62 | }; 63 | 64 | // This function will be called once during initialization. 65 | // Setup command and data capture here. 66 | Extension.prototype.onInitialize = function (configuration, logger) { 67 | var self = this; 68 | var ledPin = 35; 69 | var buttonPin = 37; 70 | 71 | var options = { 72 | gpiomem: true,          /* Use /dev/gpiomem */ 73 | mapping: 'physical',    /* Use the P1-P40 numbering scheme */ 74 | }; 75 | 76 | rpio.init(options); 77 | 78 | 79 | // Create a custom message schema with one string property. 80 | var onOffMessageSchema = this.createMessageSchema('OnOff') 81 | .addStringProperty('value', 'off', { values: ['on', 'off'] }); 82 | 83 | // Open a GPIO pin for our LED, initialize to off. 84 | rpio.open(ledPin, rpio.OUTPUT, rpio.LOW); 85 | // Open a GPIO pin for our switch 86 | rpio.open(buttonPin, rpio.INPUT, rpio.PULL_DOWN); 87 | 88 | // Create a callback function for the rpio polling routine 89 | function pollcb(pin) { 90 | // Read the state of the switch pin 91 | var newState = rpio.read(pin) ? 'on' : 'off'; 92 | 93 | // If the switch pin has transitioned to a new value... 94 | if (newState !== self.buttonState) { 95 | // Set the variable 96 | self.buttonState = newState; 97 | 98 | // Send a message to the cloud about our new switch state. 99 | this.sendData('Switch Status', onOffMessageSchema, function (message) { 100 | message.value = self.buttonState; 101 | }); 102 | } 103 | } 104 | 105 | // The the rpio library to poll a pin 106 | // Just an example way to do it. 107 | rpio.poll(buttonPin, pollcb); 108 | 109 | // Add a command to this hub that turns the LED on and off 110 | this.addCommand('Turn LED on/off', onOffMessageSchema, function (message, configuration, logger) { 111 | 112 | // If the message contains 'off' set the GPIO pin high - depends on how the LED is wired to the GPIO pins 113 | if (message.value === 'off') { 114 | rpio.write(ledPin, rpio.HIGH); 115 | } else { 116 | // Or turn on the LED by setting the pin low. 117 | rpio.write(ledPin, rpio.LOW); 118 | } 119 | 120 | }); 121 | 122 | // Register data collector and set it to poll every 60 seconds. 123 | // Set seconds to -1 to not poll at all. 124 | this.addDataCollector('Switch Status', onOffMessageSchema, 60000, function (message, configuration, logger) { 125 | 126 | // Read the state of the switch pin 127 | self.buttonState = rpio.read(buttonPin) ? 'on' : 'off'; 128 | 129 | message.value = self.buttonState; 130 | }); 131 | }; 132 | 133 | // Export your Extension class so it can be loaded by the framework 134 | module.exports = Extension; 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /BitdogHub.njsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | BitdogHub 7 | BitdogSA 8 | 9 | 10 | 11 | Debug 12 | 2.0 13 | 38f4cfe3-f058-4502-a3ac-88a177bce85c 14 | 15 | 16 | lib\bitdogSA.js 17 | False 18 | 19 | 20 | . 21 | . 22 | v4.0 23 | {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 24 | ShowAllFiles 25 | false 26 | 27 | 28 | true 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Code 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /lib/automation/bitdogAutomation.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // bitdogAutomation.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 'use strict'; 26 | 27 | let EventProcessor = require('../eventProcessor.js').EventProcessor; 28 | let Scheduler = require('./scheduler.js'); 29 | let EventCapturer = require('./eventCapturer.js'); 30 | let util = require('util'); 31 | let bitdogClient = require('bitdog-client'); 32 | let constants = require('../constants.js'); 33 | let coreMessageSchemas = require('../coreMessageSchemas.js'); 34 | 35 | function BitdogAutomation() { 36 | 37 | 38 | let _bitdogZWave = null; 39 | let _automationConfiguration = []; 40 | let _isRunning = false; 41 | let _timer = null; 42 | let _weatherTimer = null; 43 | let _scheduler = null; 44 | let _eventCapturer = null; 45 | 46 | // Pass in the external zwave controller instance so that automation can send its own commands 47 | this.__defineGetter__('bitdogZWave', function () { return _bitdogZWave; }); 48 | this.__defineSetter__('bitdogZWave', function (value) { _bitdogZWave = value; }); 49 | 50 | this.__defineGetter__('automationConfiguration', function () { return _automationConfiguration; }); 51 | this.__defineSetter__('automationConfiguration', function (value) { _automationConfiguration = value; }); 52 | 53 | this.__defineGetter__('isRunning', function () { return _isRunning; }); 54 | this.__defineSetter__('isRunning', function (value) { _isRunning = value; }); 55 | 56 | this.__defineGetter__('scheduler', function () { return _scheduler; }); 57 | this.__defineSetter__('scheduler', function (value) { _scheduler = value; }); 58 | 59 | this.__defineGetter__('eventCapturer', function () { return _eventCapturer; }); 60 | this.__defineSetter__('eventCapturer', function (value) { _eventCapturer = value; }); 61 | 62 | this.__defineGetter__('timer', function () { return _timer; }); 63 | this.__defineSetter__('timer', function (value) { _timer = value; }); 64 | 65 | this.__defineGetter__('weatherTimer', function () { return _weatherTimer; }); 66 | this.__defineSetter__('weatherTimer', function (value) { _weatherTimer = value; }); 67 | 68 | 69 | } 70 | 71 | util.inherits(BitdogAutomation, EventProcessor); 72 | 73 | BitdogAutomation.prototype.tock = function () { 74 | if (this.isRunning === true && typeof this.scheduler !== typeof undefined && this.scheduler !== null) { 75 | //bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Tock'); 76 | try { 77 | this.scheduler.tock(); 78 | } catch (error) { 79 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Exception', error); 80 | } 81 | } 82 | } 83 | 84 | BitdogAutomation.prototype.weatherTock = function () { 85 | if (this.isRunning === true && typeof this.scheduler !== typeof undefined && this.scheduler !== null) { 86 | //bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Tock'); 87 | try { 88 | this.scheduler.weatherTock(); 89 | } catch (error) { 90 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Exception', error); 91 | } 92 | } 93 | } 94 | 95 | BitdogAutomation.prototype.onProcessMessage = function (message) { 96 | if (this.isRunning === true && typeof this.eventCapturer !== typeof undefined && this.eventCapturer !== null) { 97 | try { 98 | this.eventCapturer.onProcessMessage(message); 99 | } catch (error) { 100 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Exception', error); 101 | } 102 | } 103 | } 104 | 105 | BitdogAutomation.prototype.stop = function () { 106 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Stopping....'); 107 | clearInterval(this.timer); 108 | clearInterval(this.weatherTimer); 109 | this.isRunning = false; 110 | 111 | } 112 | 113 | BitdogAutomation.prototype.getConfiguration = function () { 114 | let configuration = bitdogClient.configuration.get(constants.AUTOMATIONS_CONFIG); 115 | 116 | if (typeof configuration === typeof undefined || configuration === null) 117 | this.automationConfiguration = []; 118 | else 119 | this.automationConfiguration = configuration; 120 | } 121 | 122 | BitdogAutomation.prototype.start = function () { 123 | 124 | if (this.isRunning === false) { 125 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Starting....'); 126 | this.getConfiguration(); 127 | 128 | this.createScheduler(); 129 | this.createEventCapturer(); 130 | this.isRunning = true; 131 | 132 | this.timer = setInterval(function () { this.tock(); }.bind(this), 30000); // 30 second tick-tock 133 | this.weatherTimer = setInterval(function () { this.weatherTock(); }.bind(this), 1000 * 60 * 60); // Get weather update one hour tick-tock 134 | 135 | setTimeout(function () { this.weatherTock(); }.bind(this), 1000); // Get first weather update now 136 | } 137 | 138 | } 139 | 140 | BitdogAutomation.prototype.restart = function () { 141 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Restarting....'); 142 | 143 | if (this.isRunning === true) { 144 | this.stop(); 145 | } 146 | 147 | this.start(); 148 | } 149 | 150 | BitdogAutomation.prototype.createScheduler = function () { 151 | this.scheduler = new Scheduler(this.automationConfiguration); 152 | 153 | } 154 | 155 | BitdogAutomation.prototype.createEventCapturer = function () { 156 | this.eventCapturer = new EventCapturer(this.automationConfiguration); 157 | 158 | } 159 | 160 | module.exports = new BitdogAutomation(); 161 | -------------------------------------------------------------------------------- /lib/cameras/websocketsProcess.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // websocketsProcess.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | // Code for this file was inspired by project http://drejkim.com/projects/edi-cam/ 25 | //----------------------------------------------------------------------------- 26 | 'use strict'; 27 | 28 | let http = require('http'); 29 | let ws = require('ws');; 30 | 31 | let _wsClientOpen = false; 32 | let _wsClient = null; 33 | let _httpServer = null; 34 | let _isRunning = false; 35 | let _url = ''; 36 | let _nodeId = ''; 37 | let _authKey = ''; 38 | let _videoStreamerPort = 9000; 39 | let _enableStream = false; 40 | let _restartTimeout = null; 41 | 42 | process.on('message', function (message) { 43 | if (typeof message != typeof undefined && typeof message.name != typeof undefined && message.name != null) { 44 | switch (message.name) { 45 | case 'start': 46 | start(message); 47 | break; 48 | case 'stop': 49 | stop(message); 50 | break; 51 | } 52 | } 53 | }); 54 | 55 | function start(message) { 56 | 57 | if (_restartTimeout !== null) 58 | clearTimeout(_restartTimeout); 59 | 60 | if (_isRunning === false) { 61 | _isRunning = true; 62 | 63 | _url = message.url; 64 | _nodeId = message.nodeId; 65 | _authKey = message.authKey; 66 | _enableStream = false; 67 | 68 | if (typeof message.videoStreamerPort !== typeof undefined) 69 | _videoStreamerPort = message.videoStreamerPort; 70 | 71 | startWebSocket(); 72 | startHttpServer(); 73 | } 74 | } 75 | 76 | function stop(message) { 77 | 78 | if (_isRunning === true) { 79 | _isRunning = false; 80 | _enableStream = false; 81 | 82 | if (_wsClient.readyState === ws.OPEN) { 83 | _wsClientOpen = false; 84 | _wsClient.close(); 85 | } 86 | 87 | if (_httpServer !== null) 88 | _httpServer.close(); 89 | } 90 | } 91 | 92 | function startWebSocket() { 93 | 94 | 95 | _wsClient = new ws(_url, { headers: { node_id: _nodeId, auth_key: _authKey } }); 96 | 97 | _wsClient.on('open', function () { 98 | _wsClientOpen = true; 99 | process.send({ name: 'ws-connection-ready' }); 100 | 101 | }); 102 | 103 | _wsClient.on('message', function (data) { 104 | 105 | try { 106 | 107 | data = JSON.parse(data); 108 | 109 | process.send({ name: 'ws-connection-data', data: data }); 110 | 111 | switch (data.name) { 112 | case 'clientCount': 113 | _enableStream = data.count > 0; 114 | process.send({ name: 'ws-connection-sending-video', data: { enableStream: _enableStream } }); 115 | break; 116 | default: 117 | process.send({ name: 'ws-connection-error', data: { message: 'unknown message name', name: data.name } }); 118 | break; 119 | } 120 | 121 | } 122 | catch (error) { 123 | process.send({ name: 'ws-connection-error', data: { url: _url, error: error, data: data } }); 124 | restartWebSocket(); 125 | } 126 | 127 | }); 128 | 129 | _wsClient.on('close', function () { 130 | process.send({ name: 'ws-connection-lost' }); 131 | restartWebSocket(); 132 | }); 133 | 134 | _wsClient.on('error', function (error) { 135 | process.send({ name: 'ws-connection-error', data: { url: _url, error: error } }); 136 | restartWebSocket(); 137 | }); 138 | } 139 | 140 | function restartWebSocket() { 141 | 142 | process.send({ name: 'ws-connection-retry' }); 143 | 144 | _wsClientOpen = false; 145 | 146 | if (_wsClient !== null) { 147 | 148 | try { 149 | if (_wsClient.readyState === ws.OPEN) { 150 | _wsClient.close(); 151 | } 152 | } 153 | catch (error) { } 154 | 155 | try { 156 | _wsClient.removeAllListeners('open'); 157 | _wsClient.removeAllListeners('message'); 158 | _wsClient.removeAllListeners('error'); 159 | } 160 | catch (error) { } 161 | 162 | _wsClient = null; 163 | } 164 | 165 | 166 | _restartTimeout = setTimeout(function () { 167 | _restartTimeout = null; 168 | 169 | if (_isRunning === true) { 170 | if (_wsClient === null) { 171 | startWebSocket(); 172 | } 173 | } else { 174 | restartWebSocket(); 175 | } 176 | 177 | }, 60000); 178 | 179 | 180 | 181 | } 182 | 183 | function startHttpServer() { 184 | 185 | _httpServer = http.createServer(function (req, res) { 186 | 187 | process.send({ name: 'http-connection-connected', host: req.socket.remoteAddress, port: req.socket.remotePort }); 188 | 189 | req.on('data', function (data) { 190 | if (_wsClientOpen === true && _enableStream === true) { 191 | try { 192 | _wsClient.send(data, { binary: true }); 193 | } 194 | catch (error) { 195 | 196 | } 197 | } 198 | }); 199 | 200 | req.on('end', function () { 201 | process.send({ name: 'http-connection-lost' }); 202 | res.end(); 203 | }); 204 | 205 | 206 | }).listen(_videoStreamerPort, '127.0.0.1', function () { 207 | let address = _httpServer.address().address; 208 | let port = _httpServer.address().port; 209 | 210 | process.send({ name: 'http-server-ready', host: address, port: port }); 211 | }); 212 | 213 | _httpServer.on('close', function () { 214 | 215 | process.send({ name: 'http-server-stopped' }); 216 | 217 | }); 218 | } 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /lib/cameras/videoStreamer.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // videoStreamer.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | // Code for this file was inspired by project http://drejkim.com/projects/edi-cam/ 25 | //----------------------------------------------------------------------------- 26 | 'use strict'; 27 | 28 | // modules 29 | let childProcess = require('child_process'); 30 | let EventEmitter = require('events').EventEmitter; 31 | let util = require('util'); 32 | let path = require('path'); 33 | let url = require('url'); 34 | 35 | let bitdogClient = require('bitdog-client'); 36 | let constants = require('../constants.js'); 37 | let websocketsProcessPath = path.resolve(__dirname , './websocketsProcess.js'); 38 | 39 | 40 | function VideoStreamer(videoHubUrl, sourcePath) { 41 | let _websocketProcess = null; 42 | let _stopping = false; 43 | 44 | 45 | this.__defineGetter__('stopping', function () { return _stopping; }); 46 | this.__defineSetter__('stopping', function (value) { _stopping = value; }); 47 | 48 | this.__defineGetter__('source', function () { return sourcePath; }); 49 | 50 | this.__defineGetter__('url', function () { return videoHubUrl; }); 51 | 52 | this.__defineGetter__('websocketProcess', function () { return _websocketProcess; }); 53 | this.__defineSetter__('websocketProcess', function (value) { _websocketProcess = value; }); 54 | 55 | } 56 | 57 | util.inherits(VideoStreamer, EventEmitter); 58 | 59 | VideoStreamer.prototype.start = function () { 60 | let self = this; 61 | 62 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Starting websocket streamer to Bitdog Cloud for ' + this.source); 63 | 64 | this.websocketProcess = childProcess.fork(websocketsProcessPath, [], { execArgv: [], cwd: process.cwd(), silent: true }); 65 | 66 | this.websocketProcess.stdout.on("data", function (data) { 67 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, data); 68 | }); 69 | 70 | this.websocketProcess.stderr.on("data", function (data) { 71 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, data); 72 | }); 73 | 74 | this.websocketProcess.on('error', function (error) { 75 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Websocket streamer error', error); 76 | }); 77 | 78 | this.websocketProcess.on('message', function (message) { 79 | 80 | if (typeof message != typeof undefined && typeof message.name != typeof undefined && message.name != null) { 81 | 82 | 83 | switch (message.name) { 84 | case 'http-server-ready': { 85 | let url = 'http://' + message.host + ':' + message.port; 86 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Local HTTP server ready for video', url); 87 | } 88 | break; 89 | case 'http-server-stopped': { 90 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Local HTTP server stopped for video'); 91 | } 92 | break; 93 | case 'http-connection-lost': 94 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Local HTTP connection lost for video'); 95 | break; 96 | case 'http-connection-connected': 97 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Local HTTP connected for video', message); 98 | break; 99 | case 'ws-connection-lost': 100 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process connection lost for video'); 101 | break; 102 | case 'ws-connection-ready': 103 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process connection ready for video'); 104 | break; 105 | case 'ws-connection-retry': 106 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process connection reconecting for video'); 107 | break; 108 | case 'ws-connection-error': 109 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process connection error', message.data); 110 | break; 111 | case 'ws-connection-data': 112 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process connection data message', message.data); 113 | break; 114 | case 'ws-connection-sending-video': 115 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process sending video', message.data); 116 | break; 117 | default: 118 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process unknown message', message); 119 | break; 120 | 121 | } 122 | } 123 | 124 | }); 125 | 126 | this.websocketProcess.on('disconnected', function () { 127 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'websocket process disconnected'); 128 | }); 129 | 130 | this.websocketProcess.send({ 131 | name: 'start', 132 | source: this.source, 133 | url: this.url + '/source/' + encodeURI(this.source), 134 | nodeId: bitdogClient.configuration.nodeId, 135 | authKey: bitdogClient.configuration.authKey, 136 | videoStreamerPort: bitdogClient.configuration.get(constants.VIDEO_STREAMER_PORT) 137 | }); 138 | 139 | } 140 | 141 | VideoStreamer.prototype.stop = function () { 142 | 143 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_STREAMER, 'Stopping websocket streamer to Bitdog Cloud for ' + this.source); 144 | 145 | if (this.stopping !== true) { 146 | this.stopping = true; 147 | 148 | this.websocketProcess.send({ 149 | name: 'stop' 150 | }); 151 | } 152 | }; 153 | 154 | module.exports = VideoStreamer; -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // constants.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | 28 | function Constants() { 29 | ////////////////// Message Classes ///////////////////// 30 | this.MESSAGE_CLASS_ZWAVE_CONFIGURATION = 'zwaveConfiguration'; 31 | this.MESSAGE_CLASS_ZWAVE_SCENE_CONFIGURATION = 'zwaveSceneConfiguration'; 32 | ////////////////// Zwave Message Schema ////////////////////// 33 | this.MESSAGE_SCHEMA_ZWAVE_NODE_STATUS = 'bitdog-zwave-node-status'; 34 | this.MESSAGE_SCHEMA_ZWAVE_VALUE = 'bitdog-zwave-value'; 35 | this.MESSAGE_SCHEMA_ZWAVE_ENABLE_POLLING = 'bitdog-zwave-enable-polling'; 36 | this.MESSAGE_SCHEMA_ZWAVE_DISABLE_POLLING = 'bitdog-zwave-disable-polling'; 37 | this.MESSAGE_SCHEMA_ZWAVE_GET_POLLING_INTENSITY = 'bitdog-zwave-get-polling-intensity'; 38 | this.MESSAGE_SCHEMA_ZWAVE_GET_POLLING_ENABLED = 'bitdog-zwave-get-polling-enabled'; 39 | this.MESSAGE_SCHEMA_ZWAVE_CONFIGURATION = 'bitdog-zwave-configuration'; 40 | this.MESSAGE_SCHEMA_ZWAVE_SCENE_CONFIGURATION = 'bitdog-zwave-scene-configuration'; 41 | this.MESSAGE_SCHEMA_ZWAVE_SET_NODE = 'bitdog-zwave-set-node'; 42 | this.MESSAGE_SCHEMA_ZWAVE_SET_NODE_LEVEL = 'bitdog-zwave-set-node-level'; 43 | this.MESSAGE_SCHEMA_ZWAVE_ACTIVATE = 'bitdog-zwave-activate'; 44 | this.MESSAGE_SCHEMA_ZWAVE_ADD_NODE = 'bitdog-zwave-add-node'; 45 | this.MESSAGE_SCHEMA_ZWAVE_NODE = 'bitdog-zwave-node'; 46 | this.MESSAGE_SCHEMA_ZWAVE_CONTROLLER_COMMAND = 'bitdog-zwave-controller-command'; 47 | this.MESSAGE_SCHEMA_ZWAVE_HEAL_NETWORK_NODE = 'bitdog-zwave-heal-network-node'; 48 | this.MESSAGE_SCHEMA_ZWAVE_RENAME = 'bitdog-zwave-rename'; 49 | this.MESSAGE_SCHEMA_ZWAVE_CONTROLLER_MODE = 'bitdog-zwave-controller-mode'; 50 | 51 | this.MESSAGE_SCHEMA_ZWAVE_SET_PERCENTAGE = 'bitdog-zwave-set-percentage'; 52 | //this.MESSAGE_SCHEMA_ZWAVE_INCREMENT_PERCENTAGE = 'bitdog-zwave-increment-percentage'; 53 | //this.MESSAGE_SCHEMA_ZWAVE_DECREMENT_PERCENTAGE = 'bitdog-zwave-decrement-percentage'; 54 | this.MESSAGE_SCHEMA_ZWAVE_SET_TEMPERTURE = 'bitdog-zwave-set-temperture'; 55 | this.MESSAGE_SCHEMA_ZWAVE_INCREMENT_DECREMENT_TEMPERTURE = 'bitdog-zwave-increment-decrement-temperture'; 56 | 57 | this.MESSAGE_SCHEMA_ZWAVE_CREATE_SCENE = 'bitdog-zwave-create-scene'; 58 | this.MESSAGE_SCHEMA_ZWAVE_REMOVE_SCENE = 'bitdog-zwave-remove-scene'; 59 | this.MESSAGE_SCHEMA_ZWAVE_ADD_SCENE_VALUE = 'bitdog-zwave-add-scene-value'; 60 | this.MESSAGE_SCHEMA_ZWAVE_REMOVE_SCENE_VALUE = 'bitdog-zwave-remove-scene-value'; 61 | this.MESSAGE_SCHEMA_ZWAVE_GET_SCENE_VALUES = 'bitdog-zwave-get-scene-values'; 62 | this.MESSAGE_SCHEMA_ZWAVE_GET_SCENES = 'bitdog-zwave-get-scenes'; 63 | this.MESSAGE_SCHEMA_ZWAVE_SCENE_EVENT = 'bitdog-zwave-scene-event'; 64 | 65 | //////////////////// Weather Message Schema /////////////////////////// 66 | this.MESSAGE_SCHEMA_WEATHER_REQUEST = 'bitdog-cloud-weather-request'; 67 | this.MESSAGE_SCHEMA_WEATHER_FORECAST = 'bitdog-cloud-weather-forecast'; 68 | 69 | ////////////////// Alarm Message Schema ////////////////////// 70 | this.MESSAGE_SCHEMA_ALARM_ARM_AWAY = 'bitdog-alarm-arm-away'; 71 | this.MESSAGE_SCHEMA_ALARM_ARM_STAY = 'bitdog-alarm-arm-stay'; 72 | this.MESSAGE_SCHEMA_ALARM_DISARM = 'bitdog-alarm-disarm'; 73 | this.MESSAGE_SCHEMA_ALARM_STATUS_CHANGED = 'bitdog-alarm-status-changed'; 74 | this.MESSAGE_SCHEMA_ALARM_EVENT = 'bitdog-alarm-event'; 75 | 76 | ////////////////// Automation Message Schema ////////////////////// 77 | this.MESSAGE_SCHEMA_SAVE_AUTOMATIONS = 'bitdog-save-automation'; 78 | this.MESSAGE_SCHEMA_AUTOMATION_EXECUTED = 'bitdog-automation-executed'; 79 | this.MESSAGE_SCHEMA_AUTOMATION_SCHEDULED = 'bitdog-automation-scheduled'; 80 | 81 | ////////////////// Zone Message Schema ////////////////////// 82 | this.MESSAGE_SCHEMA_SAVE_ZONES = 'bitdog-save-zones'; 83 | this.MESSAGE_SCHEMA_GET_ZONES = 'bitdog-get-zones'; 84 | 85 | ///////////////// GPS Message Schema /////////////////////// 86 | this.MESSAGE_SCHEMA_GPS_LOCATION = 'bitdog-gps-location'; 87 | 88 | ///////////////// Video Message Schema /////////////////////// 89 | this.MESSAGE_SCHEMA_VIDEO_SOURCE_SETTINGS = 'bitdog-video-source-settings'; 90 | this.MESSAGE_SCHEMA_GET_VIDEO_SOURCES = 'bitdog-get-video-sources'; 91 | this.MESSAGE_SCHEMA_START_VIDEO_STREAM = 'bitdog-start-video-stream'; 92 | this.MESSAGE_SCHEMA_STOP_VIDEO_STREAM = 'bitdog-stop-video-stream'; 93 | this.MESSAGE_SCHEMA_MOTION_EVENT_START = 'bitdog-motion-event-start'; 94 | this.MESSAGE_SCHEMA_MOTION_EVENT_END = 'bitdog-motion-event-end'; 95 | this.MESSAGE_SCHEMA_MOTION_IMAGE_CAPTURED = 'bitdog-motion-image-captured'; 96 | 97 | 98 | ////////////////// Configuration //////////////////////////// 99 | this.ZWAVE_CONNECTIONS_CONFIG = 'zwave:connections'; 100 | this.ZONES_CONFIG = 'zone:zones'; 101 | this.AUTOMATIONS_CONFIG = 'automation:automations'; 102 | this.AUTOMATIONS_LOCATION = 'automation:location'; 103 | this.ZWAVE_NETWORKKEY_CONFIG = 'zwave:networkKey'; 104 | this.ZWAVE_NODE_INFORMATION = 'zwave:nodeInformation'; 105 | this.ZWAVE_POLL_INTERVAL_CONFIG = 'zwave:pollInterval'; 106 | this.ZWAVE_INTERVAL_BETWEEN_POLLS_CONFIG = 'zwave:intervalBetweenPolls'; 107 | 108 | this.ALARM_NODE = 'alarm:mode'; 109 | this.VIDEO_SOURCES = 'video:sources'; 110 | this.VIDEO_ENABLED = 'video:enabled'; 111 | this.VIDEO_STREAMER_PORT = "video:streamerPort"; 112 | this.VIDEO_DVR_PATH = "video:dvrPath"; 113 | this.IPC_PORT = "ipc:port"; 114 | 115 | ////////////////// Extensions //////////////////////////////// 116 | this.EXTENSION_PATHS = 'extension:paths'; 117 | 118 | ////////////////// LOGGING ////////////////////////////////// 119 | this.LOG_PROCESS_BITDOG_SA = 'BitdogHub'; 120 | this.LOG_PROCESS_ZWAVE = 'ZWave'; 121 | this.LOG_PROCESS_BITDOG_CLIENT = 'Bitdog Client'; 122 | this.LOG_PROCESS_ALARM = 'Alarm'; 123 | this.LOG_PROCESS_BRAIN = 'Brain'; 124 | this.LOG_PROCESS_AUTOMATION = 'Automation'; 125 | this.LOG_PROCESS_VIDEO_STREAMER = 'Video Streamer'; 126 | this.LOG_PROCESS_VIDEO_MANAGER = 'Video Manager'; 127 | this.LOG_PROCESS_DVR_MANAGER = 'DVR Manager'; 128 | this.LOG_PROCESS_IPC_MANAGER = 'IPC Manager'; 129 | 130 | //////////////// CLOUD NODES //////////////////////////////// 131 | this.BITDOG_CLOUD_NODE = "00000000-0000-0000-0000-000000000005" 132 | } 133 | 134 | module.exports = new Constants(); -------------------------------------------------------------------------------- /extensions/sayExtension.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // sayExtension.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | // Extension will inherit functions from ExtensionBase 27 | // Make sure the path to extensionBase is correct 28 | var ExtensionBase = require('../lib/extensionBase.js'); 29 | var util = require('util'); 30 | var crypto = require('crypto'); 31 | var url = require('url'); 32 | var https = require('https'); 33 | var http = require('http'); 34 | var os = require('os'); 35 | var fs = require('fs'); 36 | var path = require('path'); 37 | var child_process = require('child_process'); 38 | var sayQueue = []; 39 | 40 | 41 | function Extension() { 42 | this.isPlaying = false; 43 | } 44 | // Extension inherits from ExtensionBase 45 | util.inherits(Extension, ExtensionBase); 46 | 47 | Extension.prototype.onMessage = function (message, configuration, logger) { 48 | }; 49 | 50 | Extension.prototype.onInitialize = function (configuration, logger) { 51 | var self = this; 52 | 53 | // Create a custom message schema with one string property. 54 | var playMessageSchema = this.createMessageSchema('Say') 55 | .addStringProperty('text', '', {}, 'The text to play', 'Text'); 56 | 57 | 58 | // Add a command to this hub that makes it say the provided text 59 | this.addCommand('Say', playMessageSchema, function (message, configuration, logger) { 60 | self.say(message.text, configuration, logger); 61 | 62 | }); 63 | 64 | }; 65 | 66 | Extension.prototype.onSystemEvent = function (eventInfo, configuration, logger) { 67 | this.say(eventInfo.text, configuration, logger); 68 | }; 69 | 70 | Extension.prototype.say = function (text, configuration, logger) { 71 | sayQueue.push(text); 72 | logger.logProcessEvent('Say extension', 'Enqueued', { text: text }); 73 | this.play(configuration, logger); 74 | 75 | }; 76 | 77 | Extension.prototype.getAudio = function (text, configuration, logger, successCallback, errorCallback) { 78 | 79 | var self = this; 80 | var port = null; 81 | var protocol = null; 82 | var fileName = crypto.createHash('sha256').update(text).digest('hex') + '.mp3'; 83 | var filePath = os.tmpdir() + path.sep + fileName; 84 | 85 | if (fs.existsSync(filePath) === true) { 86 | logger.logProcessEvent('Say extension', 'Found cached file', filePath); 87 | 88 | if (successCallback) 89 | successCallback(filePath); 90 | 91 | return; 92 | } 93 | 94 | var request = { 95 | nodeId: configuration.nodeId, 96 | authKey: configuration.authKey, 97 | text: text 98 | }; 99 | 100 | var requestJson = JSON.stringify(request); 101 | 102 | var headers = { 103 | 'Content-Type': 'application/json', 104 | }; 105 | 106 | var parsedUrl = url.parse(this.constants.CENTRAL_URL + '/realm/say'); 107 | 108 | if (parsedUrl.port != null) 109 | port = parsedUrl.port; 110 | else { 111 | if (parsedUrl.protocol == 'http:') 112 | port = 80; 113 | else (parsedUrl.protocol == 'https:') 114 | port = 443; 115 | } 116 | 117 | var options = { 118 | host: parsedUrl.hostname, 119 | port: port, 120 | path: parsedUrl.pathname, 121 | method: 'POST', 122 | headers: headers 123 | }; 124 | 125 | protocol = parsedUrl.protocol == 'https:' ? https : http; 126 | 127 | var request = protocol.request(options, function (response) { 128 | var fileStream = fs.createWriteStream(filePath); 129 | fileStream.on('finish', function () { 130 | fileStream.close(function () { 131 | successCallback(filePath); 132 | }); // close() is async, call cb after close completes. 133 | }); 134 | 135 | response.pipe(fileStream); 136 | 137 | }); 138 | 139 | request.on('error', function (e) { 140 | if (errorCallback) 141 | errorCallback(e); 142 | }); 143 | 144 | request.write(requestJson); 145 | request.end(); 146 | 147 | } 148 | 149 | Extension.prototype.play = function (configuration, logger) { 150 | var self = this; 151 | 152 | if (this.isPlaying === true || sayQueue.length < 1) 153 | return; 154 | 155 | this.isPlaying = true; 156 | 157 | var text = sayQueue.shift(); 158 | 159 | if (typeof text === typeof undefined || text === null) { 160 | this.isPlaying = false; 161 | return; 162 | } 163 | 164 | this.getAudio(text, configuration, logger, 165 | function (filePath) { 166 | 167 | if (typeof filePath === typeof undefined || filePath === null) { 168 | self.isPlaying = false; 169 | return; 170 | } 171 | 172 | var playerProcess = null; 173 | 174 | try { 175 | 176 | //Found that mplayer may not have correct premissions to play smoothly 177 | // ellevated premissions helps. 178 | playerProcess = child_process.spawn('mplayer', [ '-af', 'volume=10:1', filePath]); 179 | 180 | playerProcess.stdout.on('data', function (data) { 181 | //logger.logProcessEvent(`Say extension`, data); 182 | }); 183 | 184 | playerProcess.stderr.on('data', function (data) { 185 | logger.logProcessEvent('Say extension', data); 186 | }); 187 | 188 | playerProcess.on('close', function (code) { 189 | self.isPlaying = false; 190 | 191 | if (sayQueue.length > 0) { 192 | setInterval(function () { self.play(configuration, logger); }, 1000); 193 | } 194 | 195 | }); 196 | 197 | } catch (error) { 198 | logger.logProcessEvent('Say extension', error); 199 | self.isPlaying = false; 200 | 201 | if (sayQueue.length > 0) { 202 | setInterval(function () { self.play(configuration, logger); }, 1000); 203 | } 204 | 205 | } 206 | 207 | }, 208 | function (error) { 209 | logger.logProcessEvent('Say extension', 'Download error ' + error); 210 | self.isPlaying = false; 211 | if (sayQueue.length > 0) { 212 | setInterval(function () { self.play(configuration, logger); }, 1000); 213 | } 214 | }); 215 | 216 | } 217 | // Export your Extension class so it can be loaded by the framework 218 | module.exports = Extension; 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /lib/zwave/zwaveNode.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveNode.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | var bitdogClient = require('bitdog-client'); 28 | var constants = require('../constants.js'); 29 | var ZWaveClass = require('./zwaveClass.js'); 30 | 31 | function ZWaveNode(nodeId, zwave) { 32 | var _id = nodeId; 33 | var _manufacturer = ''; 34 | var _manufacturerId = ''; 35 | var _product = ''; 36 | var _productType = ''; 37 | var _productId = ''; 38 | var _type = ''; 39 | var _typeGeneric = ''; 40 | var _typeSpecific = ''; 41 | var _typeBasic = ''; 42 | var _name = ''; 43 | var _location = ''; 44 | var _classes = []; 45 | var _ready = false; 46 | var _hash = ''; 47 | var _status = 'Unknown'; 48 | var _neighbors = []; 49 | var _zwave = zwave; 50 | 51 | this.setNodeInformation = function (zwaveNodeInfo, ready) { 52 | _manufacturer = zwaveNodeInfo.manufacturer; 53 | _manufacturerId = zwaveNodeInfo.manufacturerid; 54 | _product = zwaveNodeInfo.product; 55 | _productType = zwaveNodeInfo.producttype; 56 | _productId = zwaveNodeInfo.productid; 57 | _type = zwaveNodeInfo.type; 58 | _name = zwaveNodeInfo.name; 59 | _location = zwaveNodeInfo.loc; 60 | _hash = _id + _manufacturerId + _productId; 61 | _ready = ready === true && _productType != ''; 62 | 63 | 64 | _typeGeneric = decimalToHex(zwave.getNodeGeneric(_id),2); 65 | _typeSpecific = decimalToHex(zwave.getNodeSpecific(_id),2); 66 | _typeBasic = decimalToHex(zwave.getNodeBasic(_id), 2); 67 | 68 | } 69 | 70 | this.getNodeInformation = function () { 71 | return { 72 | id: _id, 73 | manufacturer: _manufacturer, 74 | manufacturerId: _manufacturerId, 75 | product: _product, 76 | productType: _productType, 77 | productId: _productId, 78 | type: _type, 79 | name: _name, 80 | location: _location, 81 | hash: _hash, 82 | ready: _ready, 83 | displayName: this.displayName, 84 | typeGeneric: _typeGeneric, 85 | typeSpecific: _typeSpecific, 86 | typeBasic: _typeBasic 87 | }; 88 | } 89 | 90 | function decimalToHex(d, padding) { 91 | var hex = Number(d).toString(16); 92 | padding = typeof padding === typeof undefined || padding === null ? padding = 2 : padding; 93 | 94 | while (hex.length < padding) { 95 | hex = "0" + hex; 96 | } 97 | 98 | return ('0x' + hex).toUpperCase(); 99 | } 100 | 101 | this.updateValue = function (zwaveValueInfo) 102 | { 103 | var zwaveClass = getZWaveClass(zwaveValueInfo); 104 | return zwaveClass.updateValue(zwaveValueInfo); 105 | } 106 | 107 | this.removeValue = function (commandClassId, instanceId, index) { 108 | for (var index = 0; index < _classes.length; index++) { 109 | if (_classes[index].id == commandClassId) { 110 | var zwaveClass = _classes[index]; 111 | zwaveClass.removeValue(commandClassId, instanceId, index); 112 | 113 | if (zwaveClass.instances.length < 1) { 114 | _classes.splice(index, 1); 115 | } 116 | 117 | return; 118 | } 119 | } 120 | } 121 | 122 | this.getValue = function (commandClassId, instanceId, indexId) { 123 | var zwaveClass = null; 124 | for (var index = 0; index < _classes.length; index++) { 125 | if (_classes[index].id == commandClassId) { 126 | zwaveClass = _classes[index]; 127 | break; 128 | } 129 | } 130 | 131 | if (zwaveClass != null) 132 | return zwaveClass.getValue(instanceId, indexId); 133 | else 134 | return null; 135 | } 136 | 137 | this.getClass = function (commandClassId) { 138 | for (var index = 0; index < _classes.length; index++) { 139 | if (_classes[index].id == commandClassId) 140 | return _classes[index]; 141 | } 142 | 143 | return null; 144 | } 145 | 146 | function getZWaveClass(zwaveValueInfo) { 147 | for (var index = 0; index < _classes.length; index++) { 148 | if (_classes[index].id == zwaveValueInfo.class_id) 149 | return _classes[index]; 150 | } 151 | 152 | var zwaveClass = new ZWaveClass(zwaveValueInfo.class_id) 153 | _classes.push(zwaveClass); 154 | return zwaveClass; 155 | } 156 | 157 | 158 | this.__defineGetter__('id', function () { return _id; }); 159 | this.__defineGetter__('manufacturer', function () { return _manufacturer; }); 160 | this.__defineGetter__('manufacturerId', function () { return _manufacturerId; }); 161 | this.__defineGetter__('product', function () { return _product; }); 162 | this.__defineGetter__('productType', function () { return _productType; }); 163 | this.__defineGetter__('productId', function () { return _productId; }); 164 | this.__defineGetter__('type', function () { return _type; }); 165 | this.__defineGetter__('typeGeneric', function () { return _typeGeneric; }); 166 | this.__defineGetter__('typeBasic', function () { return _typeBasic; }); 167 | this.__defineGetter__('typeSpecific', function () { return _typeSpecific; }); 168 | this.__defineGetter__('name', function () { return _name; }); 169 | this.__defineSetter__('name', function (value) { _name = value; }); 170 | this.__defineGetter__('location', function () { return _location; }); 171 | this.__defineSetter__('location', function (value) { _location = value; }); 172 | this.__defineGetter__('classes', function () { return _classes; }); 173 | this.__defineGetter__('hash', function () { return _hash; }); 174 | this.__defineGetter__('ready', function () { return _ready; }); 175 | this.__defineGetter__('neighbors', function () { return _neighbors; }); 176 | this.__defineGetter__('status', function () { return _status; }); 177 | this.__defineSetter__('status', function (value) { _status = value; }); 178 | this.__defineGetter__('displayName', function () { 179 | if (_name !== '') 180 | return _name; 181 | else if (_product !== '') 182 | return _product; 183 | else if (_type !== '') 184 | return _type; 185 | else 186 | return 'Unknown'; 187 | }); 188 | } 189 | 190 | module.exports = ZWaveNode -------------------------------------------------------------------------------- /lib/zwave/zwaveValue.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // zwaveValue.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | var zwaveAlarmNotification = require('./zwaveAlarmNotification.js'); 27 | var moment = require('moment'); 28 | 29 | function ZWaveValue(zwaveValueInfo) { 30 | var _id = zwaveValueInfo.index; 31 | var _isReadOnly = zwaveValueInfo.read_only; 32 | var _isWriteOnly = zwaveValueInfo.write_only; 33 | var _type = zwaveValueInfo.type; 34 | var _label = zwaveValueInfo.label; 35 | var _help = zwaveValueInfo.help; 36 | var _units = zwaveValueInfo.units; 37 | var _valueId = zwaveValueInfo.value_id; 38 | var _genre = zwaveValueInfo.genre; 39 | var _isPolled = zwaveValueInfo.is_polled; 40 | var _values = zwaveValueInfo.values; 41 | var _min = zwaveValueInfo.min; 42 | var _max = zwaveValueInfo.max; 43 | var _firstUpdate = true; 44 | var _notificationType = null; 45 | var _status = 0; 46 | var _event = null; 47 | var _updateDateTimeUTC = null; 48 | 49 | convertNotificationValueToList(); 50 | 51 | //convertNotificationValueToList before attempting the convert and setting default value. 52 | var _value = convertType(zwaveValueInfo.value); 53 | 54 | if ( typeof _value !== typeof undefined && _value.decoded === true) { 55 | _status = _value.status; 56 | _value = _value.value; 57 | } 58 | 59 | // set the notification status 60 | zwaveValueInfo.status = _status; 61 | 62 | this.__defineGetter__("id", function () { return _id; }); 63 | this.__defineGetter__("isReadOnly", function () { return _isReadOnly; }); 64 | this.__defineGetter__("isWriteOnly", function () { return _isWriteOnly; }); 65 | this.__defineGetter__("type", function () { return _type; }); 66 | this.__defineGetter__("label", function () { return _label; }); 67 | this.__defineGetter__("help", function () { return _help; }); 68 | this.__defineGetter__("units", function () { return _units; }); 69 | this.__defineGetter__("value", function () { return _value; }); 70 | this.__defineGetter__("min", function () { return _min; }); 71 | this.__defineGetter__("max", function () { return _max; }); 72 | this.__defineGetter__("genre", function () { return _genre; }); 73 | this.__defineGetter__("isPolled", function () { return _isPolled; }); 74 | this.__defineGetter__("values", function () { return _values; }); 75 | this.__defineGetter__("valueId", function () { return _valueId; }); 76 | this.__defineGetter__("status", function () { return _status; }); 77 | this.__defineGetter__("updateDateTimeUTC", function () { return _updateDateTimeUTC; }); 78 | 79 | function convertNotificationValueToList() { 80 | _notificationType = zwaveAlarmNotification.getNotificationType(zwaveValueInfo.class_id, zwaveValueInfo.index); 81 | 82 | // If this is a notification value then get the decoded list of known values 83 | // turn this value into a list instead of a number and fill the list with all the known values. 84 | // This may be handled by OpenZWave in the future 85 | if (typeof _notificationType !== typeof undefined) { 86 | _values = []; 87 | _type = 'list'; 88 | for (_event in _notificationType.events) { 89 | _values.push(_notificationType.events[_event].value); 90 | } 91 | 92 | } 93 | } 94 | 95 | function convertType(value) { 96 | var result = value; 97 | 98 | if (typeof value === typeof undefined || value === null) { 99 | result = value; 100 | } 101 | else if (typeof value == 'string') { 102 | switch (_type) { 103 | case 'button': 104 | case 'string': 105 | case 'list': 106 | result = value; 107 | break; 108 | case 'int': 109 | case 'short': 110 | case 'byte': 111 | result = parseInt(value); 112 | break; 113 | case 'bool': 114 | result = value === 'true' || value === '1' || value === 1; 115 | break; 116 | case 'decimal': 117 | result = parseFloat(value); 118 | break; 119 | default: 120 | result = value; 121 | break; 122 | 123 | } 124 | 125 | } 126 | else if (_type === 'list' && typeof _notificationType !== typeof undefined && _notificationType !== null) { 127 | // 128 | // Change the current numeric values to more complex decoded objects values 129 | // which makes application logic easier up the stack 130 | // 131 | 132 | if (value === true) { 133 | result = _notificationType.events[1]; 134 | } else if (value === false) { 135 | result = _notificationType.events[0]; 136 | } else { 137 | result = _notificationType.events[value]; 138 | } 139 | 140 | if (typeof result !== typeof undefined) { 141 | result.decoded = true; 142 | } 143 | else { 144 | result = value; 145 | } 146 | 147 | } else { 148 | result = value; 149 | } 150 | 151 | return result; 152 | } 153 | 154 | this.updateValue = function (zwaveValueInfo) { 155 | var result = false; 156 | var convertedValue = convertType(zwaveValueInfo.value); 157 | var status = 0; 158 | 159 | // if we have converted the value to decoded message 160 | if (typeof convertedValue !== typeof undefined && convertedValue.decoded === true) { 161 | // set the status to the status of the decoded message 162 | status = convertedValue.status; 163 | // use the new decoded message name as the value instead of the orginal numerical code 164 | convertedValue = convertedValue.value; 165 | } 166 | 167 | if (_value !== convertedValue || _firstUpdate === true) { 168 | result = true; 169 | zwaveValueInfo.new = true; 170 | } 171 | else if (_updateDateTimeUTC.getTime() + 60000 < (new Date(moment.utc().format())).getTime()) { 172 | result = true; 173 | zwaveValueInfo.new = false; 174 | } else { 175 | zwaveValueInfo.new = false; 176 | } 177 | 178 | // update our state 179 | _value = convertedValue; 180 | _status = status; 181 | _firstUpdate = false; 182 | _updateDateTimeUTC = new Date(moment.utc().format()); 183 | 184 | zwaveValueInfo.value = _value; 185 | zwaveValueInfo.status = _status; 186 | zwaveValueInfo.units = _units; 187 | 188 | return result; 189 | 190 | }; 191 | 192 | // This method is used to convert incomming values to the correct type before sending to OpenZWave 193 | // If the result is decoded, use the orginal numeric value for OpenZWave 194 | this.convert = function (value) { 195 | var result = convertType(value); 196 | 197 | if (result.decoded === true) 198 | result = result.id; 199 | 200 | return result; 201 | }; 202 | 203 | } 204 | 205 | module.exports = ZWaveValue; -------------------------------------------------------------------------------- /bin/bitdoghub: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | //----------------------------------------------------------------------------- 4 | // 5 | // bitdoghub 6 | // 7 | // Copyright (c) 2015-2017 Bitdog LLC. 8 | // 9 | // SOFTWARE NOTICE AND LICENSE 10 | // 11 | // This file is part of bitdog-hub. 12 | // 13 | // bitdog-hub is free software: you can redistribute it and/or modify 14 | // it under the terms of the GNU General Public License as published 15 | // by the Free Software Foundation, either version 3 of the License, 16 | // or (at your option) any later version. 17 | // 18 | // bitdog-hub is distributed in the hope that it will be useful, 19 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | // GNU General Public License for more details. 22 | // 23 | // You should have received a copy of the GNU General Public License 24 | // along with bitdog-hub. If not, see . 25 | // 26 | //----------------------------------------------------------------------------- 27 | 28 | var os = require('os'); 29 | var path = require('path'); 30 | var program = require('commander'); 31 | var bitdogClient = require('bitdog-client'); 32 | var child_process = require('child_process'); 33 | 34 | function getPublicIPAddress() { 35 | var networkInterfaces = os.networkInterfaces( ); 36 | var index = 0; 37 | var addresses = null; 38 | var interfaceName = ''; 39 | var address = '127.0.0.1' 40 | 41 | for(interfaceName in networkInterfaces) { 42 | addresses = networkInterfaces[interfaceName] 43 | for(index = 0; index < addresses.length; index++) 44 | { 45 | address = addresses[index]; 46 | 47 | if(address.family === 'IPv4' && address.address !== '127.0.0.1') { 48 | address = address.address ; 49 | break; 50 | } 51 | } 52 | } 53 | 54 | return address; 55 | } 56 | 57 | program 58 | .version('2.0.15'); 59 | 60 | program.command('register') 61 | .description('Register this Bitdog Hub with Bitdog Cloud.') 62 | .option('-u,--username ', 'The username for the Bitdog IoT Cloud to register this hub with.') 63 | .option('-p,--passphrase ', 'The passphrase for the Bitdog IoT Cloud to register this hub with.') 64 | .option('-n,--nodename ', 'The name that will be displayed on the Bitdog IoT Cloud dashboard.') 65 | .action(function (options) { 66 | 67 | if (typeof options.passphrase === typeof undefined || typeof options.username === typeof undefined) { 68 | console.log('Please provide a username and passphrase when registering.'); 69 | this.outputHelp(); 70 | process.exit(0); 71 | } 72 | else { 73 | console.log('Registering node...') 74 | var adminManager = bitdogClient.adminManager; 75 | 76 | var nodeName = typeof options.nodename === typeof undefined ? 'New Node' : options.nodename; 77 | adminManager.registerNode(options.username, options.passphrase, nodeName, 78 | function (success) { 79 | 80 | console.log('Registration successful.'); 81 | process.exit(0); 82 | 83 | }, 84 | function (error) { 85 | console.log(error); 86 | process.exit(1); 87 | }); 88 | 89 | } 90 | 91 | 92 | }); 93 | 94 | program 95 | .command('start') 96 | .description('Start Bitdog Hub.') 97 | .option('-l,--logpath ', 'The path for log files.') 98 | .option('-c,--configpath ', 'The path for configuration files.') 99 | .option('-t,--tail', 'Write logs to console also.') 100 | .option('-e,--extension ', 'Path to file that has extension code. Use clear to remote extension from stored configuration.') 101 | .option('-d,--debug', 'Start remote debugging and wait for attachment.') 102 | .option('-p,--pidpath ', 'Specify a pid file path.') 103 | 104 | 105 | .action(function (options) { 106 | if(bitdogClient.configuration.isRegistered === false) { 107 | var readline = require('readline'); 108 | var rl = readline.createInterface(process.stdin, process.stdout); 109 | rl.question("This Bitdog Hub is not registered, try automatic registration? (y/n) ", function(answer) { 110 | rl.close(); 111 | if(answer === 'y') 112 | start(options); 113 | }); 114 | 115 | } else { 116 | start(options); 117 | } 118 | 119 | function start(options) { 120 | var fs = require('fs'); 121 | var packageFilePath = path.resolve(__dirname, '../'); 122 | var debugScriptPath = path.resolve(__dirname, 'RemoteDebug.js'); 123 | var pidFile = path.resolve(__dirname,'pid.txt'); 124 | var args = []; 125 | 126 | var opt = { 127 | stdio: 'inherit', 128 | env: process.env, 129 | cwd: process.cwd(), 130 | detached: true 131 | }; 132 | 133 | opt.env.LD_LIBRARY_PATH = '/usr/local/lib' 134 | 135 | if(options.pidpath) { 136 | console.log("Pid path option provided") 137 | pidFile = path.resolve(__dirname, options.pidpath); 138 | } 139 | 140 | console.log("Starting process with pid file at " + pidFile); 141 | 142 | if(options.debug) { 143 | console.log("Debug mode"); 144 | args.push('--inspect-brk=' + getPublicIPAddress() + ':9229'); 145 | args.push(packageFilePath); 146 | } else { 147 | args.push(packageFilePath); 148 | } 149 | 150 | if(options.tail) { 151 | args.push('--tail'); 152 | console.log("Tailing log to console"); 153 | } 154 | 155 | if(options.configpath) { 156 | args.push('--configpath'); 157 | args.push(options.configpath); 158 | 159 | } 160 | 161 | if(options.logpath) { 162 | args.push('--logpath'); 163 | args.push(options.logpath); 164 | } 165 | 166 | if(options.extension) { 167 | args.push('--extension'); 168 | args.push(options.extension); 169 | } 170 | 171 | var child = child_process.spawn('node', args, opt); 172 | var pidId = child.pid; 173 | 174 | // required so the parent can exit 175 | child.unref(); 176 | 177 | try { 178 | fs.writeFileSync(pidFile, pidId); 179 | } 180 | catch(error) { 181 | console.log("Could not write to pid file: " + error); 182 | } 183 | 184 | setTimeout(function() { 185 | try 186 | { 187 | 188 | child_process.execSync('/bin/kill -s 0 ' + pidId); 189 | console.log('Bitdog Hub started'); 190 | process.exit(0); 191 | 192 | } catch(e) { 193 | console.log('Bitdog Hub has failed to start properly, pid:' + pidId); 194 | fs.unlinkSync(pidFile); 195 | process.exit(1); 196 | } 197 | 198 | }, 10000); 199 | 200 | 201 | } 202 | 203 | }); 204 | 205 | program 206 | .command('stop') 207 | .description('Stop Bitdog Hub.') 208 | .option('-p,--pidpath ', 'Specify a pid file path.') 209 | 210 | .action(function (options) { 211 | var fs = require('fs'); 212 | var packageFilePath = path.resolve(__dirname, '../'); 213 | var pidFile = path.resolve(__dirname,'pid.txt'); 214 | 215 | if(options.pidpath) { 216 | console.log("Pid path option provided") 217 | pidFile = path.resolve(__dirname, options.pidpath); 218 | } 219 | 220 | console.log("Stopping process with pid file at " + pidFile); 221 | 222 | try { 223 | 224 | try 225 | { 226 | fs.accessSync(pidFile, fs.F_OK); 227 | } 228 | catch(e) { 229 | console.log('Process id file not found, bitdog-hub may not be running'); 230 | process.exit(1); 231 | } 232 | 233 | var pid = fs.readFileSync(pidFile); 234 | var pidId = parseInt(pid); 235 | 236 | try 237 | { 238 | process.kill(pidId,'SIGINT'); 239 | } 240 | catch(e) { 241 | } 242 | 243 | setTimeout(function() { 244 | try 245 | { 246 | child_process.execSync('/bin/kill -s 0 ' + pidId); 247 | console.log('Bitdog Hub did not stop in timely fashion'); 248 | process.exit(1); 249 | 250 | } catch(e) { 251 | console.log('Bitdog Hub has stopped'); 252 | fs.unlinkSync(pidFile); 253 | process.exit(0); 254 | } 255 | 256 | }, 20000); 257 | 258 | 259 | } catch (e) { 260 | console.log(JSON.stringify(e)); 261 | } 262 | 263 | }); 264 | 265 | program 266 | .command('help') 267 | .description('Print usage and options.') 268 | .action(function(options) { 269 | program.outputHelp(); 270 | }); 271 | 272 | 273 | program.parse(process.argv); 274 | 275 | if (!process.argv.slice(2).length) { 276 | program.outputHelp(); 277 | process.exit(1); 278 | 279 | } 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /lib/cameras/dvrManager.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // dvrManager.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | var bitdogClient = require('bitdog-client'); 27 | var constants = require('../constants.js'); 28 | 29 | var fs = require('fs'); 30 | var request = require('request'); 31 | var path = require('path'); 32 | var dvrPath = bitdogClient.configuration.get(constants.VIDEO_DVR_PATH); 33 | var moment = require('moment'); 34 | 35 | // 36 | // DVR manager watches the DVR directory for new motion capture jpegs and video mp4s. It uploads 37 | // then to the Bitdog cloud where they are saved in fault tolerant geo diversified storage. The files are made 38 | // availabe to the mobile app for security notifications and historical viewing. 39 | // 40 | function DVRManager() { 41 | var _isRunning = false; 42 | var _fileAssetUrl = ''; 43 | 44 | this.__defineGetter__('isRunning', function () { return _isRunning; }); 45 | this.__defineSetter__('isRunning', function (value) { _isRunning = value; }); 46 | 47 | this.__defineGetter__('url', function () { return _fileAssetUrl; }); 48 | this.__defineSetter__('url', function (value) { _fileAssetUrl = value; }); 49 | } 50 | 51 | DVRManager.prototype.start = function (fileAssetUrl) { 52 | if (this.isRunning === true) { 53 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Already in running state.'); 54 | return; 55 | } 56 | 57 | this.url = fileAssetUrl; 58 | 59 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Starting.'); 60 | 61 | this.isRunning = true; 62 | 63 | // Start uploading in 60 seconds 64 | setTimeout(this.uploadFiles.bind(this), 60000); 65 | 66 | }; 67 | 68 | DVRManager.prototype.stop = function () { 69 | 70 | 71 | if (this.isRunning === false) { 72 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Already in stopped state.'); 73 | return; 74 | } 75 | 76 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Stopping.'); 77 | 78 | this.isRunning = false; 79 | 80 | 81 | 82 | }; 83 | 84 | DVRManager.prototype.uploadFiles = function (index) { 85 | var file = null; 86 | var fileExt = ''; 87 | var promise = null; 88 | var self = this; 89 | 90 | if (this.isRunning === false) 91 | return; 92 | 93 | if (typeof index === typeof undefined) 94 | index = 0; 95 | 96 | var files = fs.readdirSync(dvrPath, { encoding: 'utf8' }); 97 | 98 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Found ' + files.length + ' for upload'); 99 | 100 | if (files.length > 0) { 101 | 102 | // We caught up, reset the index 103 | if (index >= files.length) 104 | index = 0; 105 | 106 | fileName = files[index] 107 | fileExt = path.extname(fileName); 108 | 109 | if (fileExt !== '.jpg' && fileExt !== '.mp4') { 110 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Ignoring file of incorrect type' + fileName); 111 | setTimeout(self.uploadFiles.bind(self), 1000, ++index); 112 | return; 113 | } 114 | 115 | var stat = fs.statSync(path.resolve(dvrPath, fileName)); 116 | var now = moment(); 117 | var lastAccess = moment(stat.mtime); 118 | var lastAccessInMinutes = now.diff(lastAccess, 'minutes',true); 119 | 120 | if ((lastAccessInMinutes < 2 && fileExt === '.mp4') || (lastAccessInMinutes < 1 && fileExt === '.jpg') ) { 121 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'File ' + fileName + ' was last accessed ' + lastAccessInMinutes + ' mintues ago. Ignoring file because its too new, maybe open'); 122 | ++index; 123 | 124 | if (index === files.length) 125 | setTimeout(self.uploadFiles.bind(self), 60000, 0); 126 | else 127 | setTimeout(self.uploadFiles.bind(self), 1000, index); 128 | 129 | return; 130 | 131 | } else { 132 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'File ' + fileName + ' was last accessed ' + lastAccessInMinutes + ' minutes ago'); 133 | } 134 | 135 | self.uploadFile(fileName,stat).then(function (result, error) { 136 | 137 | if (typeof error !== typeof undefined && error !== null) { 138 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER,'Error uploading file', error); 139 | // continue to next file if running 140 | } 141 | 142 | if (self.isRunning === true) { 143 | setTimeout(self.uploadFiles.bind(self), 1000, ++index); 144 | } 145 | 146 | }).catch(function (error) { 147 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, error); 148 | // continue to next file if running 149 | 150 | if (self.isRunning === true) { 151 | setTimeout(self.uploadFiles.bind(self), 1000, ++index); 152 | } 153 | 154 | }); 155 | 156 | 157 | } else { 158 | if (self.isRunning === true) { 159 | // No files, wait 15 seconds and check again 160 | setTimeout(self.uploadFiles.bind(self), 15000, 0); 161 | } 162 | } 163 | 164 | }; 165 | 166 | DVRManager.prototype.uploadFile = function (fileName, stat) { 167 | var self = this; 168 | var filePath = path.resolve(dvrPath, fileName); 169 | 170 | var promise = new Promise(function (resolve, reject) { 171 | try { 172 | 173 | var fileExt = path.extname(fileName); 174 | var contentType = ''; 175 | var uri = ''; 176 | 177 | switch (fileExt) { 178 | case '.jpg': 179 | contentType = 'image/jpeg'; 180 | uri = '/uploadsecurityimage'; 181 | break; 182 | case '.mp4': 183 | contentType = 'video/mp4'; 184 | uri = '/uploadsecurityvideo'; 185 | break; 186 | } 187 | 188 | var fileStream = fs.createReadStream(filePath, { flags: 'r' }); 189 | var  formData  =  { 190 |   file:  { 191 |     value:  fileStream , 192 |     options:  {  filename:  fileName ,   contentType: contentType } 193 | }, 194 | fileDateTimeUTC: moment(stat.ctime).toISOString() 195 | }; 196 | 197 | request.post({ url: self.url + uri ,  formData:  formData, headers: { node_id: bitdogClient.configuration.nodeId, auth_key: bitdogClient.configuration.authKey } }, function  (error, httpResponse, body)  { 198 | 199 | try { 200 | if  (error !== null)  { 201 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'File upload failed for ' + filePath, error); 202 | reject(error); 203 | } else { 204 | 205 | var result = null; 206 | 207 | try { 208 | result = JSON.parse(body); 209 | } catch (error){ } 210 | 211 | if (result !== null && result.Success === true ) { 212 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'Upload success, removing local file', filePath); 213 | fs.unlinkSync(filePath); 214 | resolve(result); 215 | } else { 216 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'File upload failed for ' + filePath, body); 217 | reject(error); 218 | } 219 | } 220 | } 221 | catch (error) { 222 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER, 'File upload failed for ' + filePath, error); 223 | reject(error); 224 | } 225 | }); 226 | 227 | } 228 | catch (error) { 229 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_DVR_MANAGER,'File upload failed for ' + filePath, error); 230 | reject(error); 231 | } 232 | 233 | }); 234 | 235 | return promise; 236 | }; 237 | 238 | module.exports = new DVRManager(); -------------------------------------------------------------------------------- /lib/cameras/videoManager.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // videoManager.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 'use strict'; 26 | 27 | let VideoStreamer = require('./videoStreamer.js'); 28 | let dvrManager = require('./dvrManager.js'); 29 | 30 | let url = require('url'); 31 | let https = require('https'); 32 | let http = require('http'); 33 | 34 | let bitdogClient = require('bitdog-client'); 35 | let constants = require('../constants.js'); 36 | let childProcess = require('child_process'); 37 | 38 | // 39 | // Video manager is an orchestator for multiple video sources amd streaming processes. 40 | // Each video source is given two separate processes that help stream video. One process is 41 | // ffmpeg encoder. The other process forwards the encoded video from ffmpeg to a cloud based 42 | // video hub via websockets. The video hub can broadcast the video to any securely connected viewing 43 | // applications. The cloud forwarder and video encoder are in external processes to prevent their message loops 44 | // from clogging up the hub's main message loop that handles automation and monitoring. 45 | // 46 | 47 | function VideoManager() { 48 | let _videoStreamers = []; 49 | let _isRunning = false; 50 | 51 | this.__defineGetter__('videoStreamers', function () { return _videoStreamers; }); 52 | 53 | this.__defineGetter__('isRunning', function () { return _isRunning; }); 54 | this.__defineSetter__('isRunning', function (value) { _isRunning = value; }); 55 | 56 | } 57 | 58 | VideoManager.prototype.getVideoSources = function () { 59 | let capture = null; 60 | let devices = []; 61 | 62 | let result = childProcess.execFileSync('v4l2-ctl', ['--list-devices'], { encoding: 'utf8' }); 63 | let records = result.split('\n\n'); 64 | let record = null; 65 | 66 | for (let index = 0; index < records.length; index++) { 67 | record = records[index].trim(); 68 | 69 | // Look for only loopback devices 70 | if (record.includes('loopback') === true) { 71 | capture = /(^.*):\s*(\W*\w*\W*\w*)/g.exec(record); 72 | if (capture !== null && capture.length > 2) 73 | devices.push({ description: capture[1], source: capture[2].replace('/dev/', ''), sourcePath: capture[2] }); 74 | } 75 | } 76 | 77 | return devices; 78 | }; 79 | 80 | VideoManager.prototype.start = function () { 81 | let self = this; 82 | 83 | if (this.isRunning === true) 84 | return; 85 | 86 | this.isRunning = true; 87 | 88 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Starting.'); 89 | 90 | let videoSources = this.getVideoSources(); 91 | let videoStreamer = null; 92 | 93 | if (videoSources.length > 0) { 94 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Found ' + videoSources.length + ' video sources.', videoSources); 95 | 96 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Acquiring video hub address.'); 97 | 98 | bitdogClient.getVideoHubUrl( 99 | function (url) { 100 | 101 | for (let index = 0; index < videoSources.length; index++) { 102 | videoStreamer = new VideoStreamer(url, videoSources[index].source); 103 | self.videoStreamers.push(videoStreamer); 104 | 105 | videoStreamer.start(); 106 | } 107 | }, 108 | function (error) { 109 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Could not contact Bitdog Cloud for video.', error); 110 | }, 111 | function (error) { 112 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Could not contact Bitdog Cloud for video.', error); 113 | } 114 | ); 115 | 116 | bitdogClient.getFileAssetUrl( 117 | function (url) { 118 | dvrManager.start(url); 119 | 120 | }, 121 | function (error) { 122 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Could not contact Bitdog Cloud for file asset management.', error); 123 | }, 124 | function (error) { 125 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Could not contact Bitdog Cloud for file asset management.', error); 126 | } 127 | ); 128 | } else { 129 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Did not find any video sources.'); 130 | } 131 | 132 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Posting camera configuration.'); 133 | 134 | saveCameraConfigurations(videoSources, function (result) { 135 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Posting camera configuration succeeded', result); 136 | }, function (error) { 137 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, error); 138 | }); 139 | 140 | }; 141 | 142 | VideoManager.prototype.stop = function () { 143 | 144 | if (this.isRunning === false) 145 | return; 146 | 147 | this.isRunning = false; 148 | 149 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_VIDEO_MANAGER, 'Stopping.'); 150 | 151 | for (let index = 0; index < this.videoStreamers.length; index++) { 152 | this.videoStreamers[index].stop(); 153 | } 154 | 155 | dvrManager.stop(); 156 | }; 157 | 158 | function saveCameraConfigurations(hubCameras, successCallback, errorCallback) { 159 | let self = this; 160 | let port = null; 161 | let protocol = null; 162 | 163 | let requestBody = { 164 | nodeId: bitdogClient.configuration.nodeId, 165 | authKey: bitdogClient.configuration.authKey, 166 | hubCamerasJson: JSON.stringify({ hubCameras: hubCameras }) 167 | }; 168 | 169 | let requestJson = JSON.stringify(requestBody); 170 | 171 | let headers = { 172 | 'Content-Type': 'application/json', 173 | }; 174 | 175 | let parsedUrl = url.parse(bitdogClient.constants.CENTRAL_URL + '/realm/saveHubCameras'); 176 | 177 | if (parsedUrl.port != null) 178 | port = parsedUrl.port; 179 | else { 180 | if (parsedUrl.protocol == 'http:') 181 | port = 80; 182 | else (parsedUrl.protocol == 'https:') 183 | port = 443; 184 | } 185 | 186 | let options = { 187 | host: parsedUrl.hostname, 188 | port: port, 189 | path: parsedUrl.pathname, 190 | method: 'POST', 191 | headers: headers 192 | }; 193 | 194 | protocol = parsedUrl.protocol == 'https:' ? https : http; 195 | 196 | let request = protocol.request(options, function (response) { 197 | response.setEncoding('utf-8'); 198 | 199 | let responseString = ''; 200 | 201 | response.on('data', function (data) { 202 | responseString += data; 203 | }); 204 | 205 | response.on('end', function () { 206 | 207 | if (this.statusCode == 200 && responseString.length > 1) { 208 | let resultObject = {}; 209 | 210 | try { 211 | resultObject = JSON.parse(responseString); 212 | } 213 | catch (e) { 214 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_ZWAVE, 'Error parsing JSON from saveHubCameras:', e); 215 | errorCallback(responseString); 216 | } 217 | 218 | if (resultObject.Success === true) { 219 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_ZWAVE, 'Saved hub camera configurations'); 220 | 221 | if (typeof successCallback !== typeof undefined) { 222 | successCallback(responseString); 223 | } 224 | 225 | return; 226 | } 227 | 228 | if (resultObject.Success !== true) { 229 | if (typeof errorCallback !== typeof undefined) 230 | errorCallback(responseString); 231 | return; 232 | } 233 | 234 | if (typeof errorCallback !== typeof undefined) 235 | errorCallback(responseString); 236 | 237 | } 238 | else { 239 | if (typeof errorCallback !== typeof undefined) 240 | errorCallback('Unexpected response from /realm/saveHubCameras. Status code: ' + this.statusCode + ' Response: ' + responseString); 241 | } 242 | }); 243 | }); 244 | 245 | request.on('error', function (e) { 246 | if (typeof errorCallback !== typeof undefined) 247 | errorCallback(e); 248 | }); 249 | 250 | request.write(requestJson); 251 | request.end(); 252 | 253 | } 254 | 255 | module.exports = new VideoManager(); -------------------------------------------------------------------------------- /lib/brains/bitdogBrain.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // bitdogBrain.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | var EventEmitter = require('events').EventEmitter; 27 | 28 | var util = require('util'); 29 | var path = require('path'); 30 | var bitdogClient = require('bitdog-client'); 31 | var constants = require('../constants.js'); 32 | var coreMessageSchemas = require('../coreMessageSchemas.js'); 33 | var tingodb = require('tingodb')(); 34 | var databaseDirectoryPath = path.resolve(__dirname, '../../database'); 35 | 36 | 37 | function BitdogBrain() { 38 | var _bitdogZWave = null; 39 | 40 | // Configure tingodb's folder setting 41 | this.db = new tingodb.Db(databaseDirectoryPath, {}, { memStore: false }); 42 | 43 | // Create a file for zwave value persistence 44 | this.db.createCollection('zwaveValuesDb', {}, function (err, collection) { 45 | if (err != null) { 46 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error creating database collection zwaveValuesDb', err); 47 | } else { 48 | this.zwaveValuesDb = collection; 49 | } 50 | 51 | }.bind(this)); 52 | 53 | // Create a file for zwave node configuration persistence 54 | this.db.createCollection('zwaveNodesDb', {}, function (err, collection) { 55 | if (err != null) { 56 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error creating database collection zwaveNodesDb', err); 57 | } else { 58 | this.zwaveNodesDb = collection; 59 | } 60 | 61 | }.bind(this)); 62 | 63 | // Pass in the external zwave controller instance so that brain can send its own commands 64 | this.__defineGetter__('bitdogZWave', function () { return _bitdogZWave; }); 65 | this.__defineSetter__('bitdogZWave', function (value) { _bitdogZWave = value; }); 66 | 67 | } 68 | 69 | util.inherits(BitdogBrain, EventEmitter); 70 | 71 | BitdogBrain.prototype.processMessage = function (message) { 72 | this.onProcessMessage(message); 73 | } 74 | 75 | BitdogBrain.prototype.onProcessMessage = function (message) { 76 | //bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Processing message', message); 77 | // Process inbound and outbound messages 78 | switch (message.h.c) { 79 | 80 | case 'data': 81 | { 82 | switch (message.h.n) { 83 | case 'bd-zValueChanged': 84 | this.updateZWaveValue(message); 85 | break; 86 | 87 | case 'bd-zNodeRemoved': 88 | this.deleteZWaveNode(message); 89 | break; 90 | 91 | } 92 | } 93 | break; 94 | 95 | } 96 | 97 | } 98 | 99 | BitdogBrain.prototype.parseValueId = function (valueId) { 100 | var values = valueId.split('-'); 101 | var result = { 102 | nodeId: parseInt(values[0]), 103 | classId: parseInt(values[1]), 104 | instanceId: parseInt(values[2]), 105 | indexId: parseInt(values[3]) 106 | }; 107 | 108 | return result; 109 | } 110 | 111 | BitdogBrain.prototype.updateZWaveValue = function (message) { 112 | var self = this; 113 | var key = message.d.homeId + '-' + message.d.valueId; 114 | 115 | self.zwaveValuesDb.findOne({ id: key }, {}, function (err, record) { 116 | if (err != null) { 117 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error finding data in zwaveValuesDb', err); 118 | } 119 | else { 120 | 121 | if (record != null && typeof record != typeof undefined) { 122 | self.zwaveValuesDb.update({ _id: record._id }, { id: key, value: message.d.value }, function (err, result) { 123 | if (err != null) { 124 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error updating data in zwaveValuesDb', err); 125 | } 126 | 127 | }); 128 | } 129 | else { 130 | self.zwaveValuesDb.insert({ id: key, value: message.d.value }, {}, function (err, result) { 131 | if (err != null) { 132 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error inserting data into zwaveValuesDb', err); 133 | } 134 | }); 135 | } 136 | } 137 | 138 | }); 139 | } 140 | 141 | BitdogBrain.prototype.deleteZWaveNode = function (message) { 142 | var homeId = message.d.homeId; 143 | this.deleteNode(homeId, message.d.nodeInfo); 144 | } 145 | 146 | BitdogBrain.prototype.deleteNode = function (homeId, nodeInfo) { 147 | self = this; 148 | 149 | self.zwaveNodesDb.remove({ homeId: homeId, nodeId: nodeInfo.id }, {}, function (err, record) { 150 | if (err != null) { 151 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Problem with node:', node); 152 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error deleting data in zwaveNodesDb', err); 153 | } 154 | }); 155 | } 156 | 157 | 158 | BitdogBrain.prototype.insertOrUpdateNode = function(homeId, node) { 159 | self = this; 160 | 161 | self.zwaveNodesDb.findOne({ homeId: homeId, nodeId: node.id }, {}, function (err, record) { 162 | if (err != null) { 163 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Problem with node:', node); 164 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error finding data in zwaveNodesDb', err); 165 | } 166 | else { 167 | if (record != null && typeof record != typeof undefined) { 168 | self.zwaveNodesDb.update({ homeId: homeId, nodeId: node.id }, { homeId: homeId, nodeId: node.id, node: node }, function (err, result) { 169 | if (err != null) { 170 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error updating data in zwaveNodesDb', err); 171 | } 172 | 173 | }); 174 | } 175 | else { 176 | 177 | self.zwaveNodesDb.insert({ homeId: homeId, nodeId: node.id, node: node }, {}, function (err, result) { 178 | if (err != null) { 179 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Problem with node:', node); 180 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Error inserting data into zwaveNodesDb', err); 181 | } 182 | }); 183 | 184 | self.emit('node added', node.getNodeInformation()); 185 | 186 | bitdogClient.sendData('bd-zNodeFound', coreMessageSchemas.zwaveNodeMessageSchema, function (message) { 187 | message.homeId = homeId; 188 | message.nodeInfo = node.getNodeInformation(); 189 | }); 190 | } 191 | } 192 | 193 | }); 194 | } 195 | 196 | BitdogBrain.prototype.getZWaveValue = function (homeId, nodeId, classId, instanceId, index) { 197 | var self = this; 198 | return new Promise(function (resolve, reject) { 199 | var id = homeId + '-' + nodeId + '-' + classId + instanceId + '-' + index; 200 | self.zwaveValuesDb.findOne({ id: id }, {}, function (err, record) { 201 | if (err == null) { 202 | resolve(record); 203 | } else { 204 | reject(err); 205 | } 206 | 207 | }); 208 | }); 209 | 210 | } 211 | 212 | BitdogBrain.prototype.getZWaveNode = function (homeId, nodeId) { 213 | var self = this; 214 | return new Promise(function (resolve, reject) { 215 | self.zwaveNodesDb.findOne({ homeId: homeId, nodeId: nodeId }, {}, function (err, record) { 216 | if (err == null) { 217 | resolve(record); 218 | } else { 219 | reject(err); 220 | } 221 | 222 | }); 223 | }); 224 | 225 | } 226 | //------------------------------------------------------------------------------------- 227 | 228 | BitdogBrain.prototype.setTemperature = function (homeId, nodeId, celsius) { 229 | var coolingSetpointValue = this.getZWaveValue(homeId, nodeId, 67, 1, 2); 230 | var heatingSetpointValue = this.getZWaveValue(homeId, nodeId, 67, 1, 1); 231 | var fahrenheit = ((c * 9) / 5) + 32; 232 | 233 | Promise.all([coolingSetpointValue, heatingSetpointValue]).then(function (values) { 234 | var coolingValue = values[0]; 235 | var heatingValue = values[1]; 236 | 237 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_BRAIN, 'Current themostat setpoint values', { homeId: homeId, nodeId: nodeId, cooling: coolingValue, heatingValue: heatingValue }); 238 | 239 | // Currently setting both setpoints when this generic command is called 240 | if (coolingValue.unit === 'F' || heatingValue.unit === 'F') { 241 | bitdogZWave.setValue(nodeId, 67, 1, 2, fahrenheit); 242 | bitdogZWave.setValue(nodeId, 67, 1, 1, fahrenheit); 243 | 244 | } else { 245 | bitdogZWave.setValue(nodeId, 67, 1, 2, celsius); 246 | bitdogZWave.setValue(nodeId, 67, 1, 1, celsius); 247 | } 248 | 249 | 250 | }); 251 | } 252 | 253 | 254 | module.exports = new BitdogBrain(); -------------------------------------------------------------------------------- /lib/alarm/bitdogAlarm.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // bitdogAlarm.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | 28 | var EventProcessor = require('../eventProcessor.js').EventProcessor; 29 | var Zone = require('./zone.js'); 30 | var util = require('util'); 31 | var bitdogClient = require('bitdog-client'); 32 | var alarmMode = require('./alarmMode.js'); 33 | var coreMessageSchemas = require('../coreMessageSchemas.js'); 34 | var constants = require('../constants.js'); 35 | 36 | 37 | function BitdogAlarm() { 38 | var _zones = []; 39 | var _bitdogZWave = null; 40 | var _alarmMode = alarmMode.disarmed; 41 | var _monitoredDevices = {}; 42 | var _isAlarmed = false; 43 | 44 | this.__defineGetter__('zones', function () { return _zones; }); 45 | this.__defineSetter__('zones', function (value) { _zones = value; }); 46 | 47 | this.__defineGetter__('alarmMode', function () { return _alarmMode; }); 48 | 49 | this.__defineSetter__('alarmMode', function (value) { 50 | if (typeof value !== typeof undefined && value !== null && _alarmMode !== value) { 51 | _alarmMode = value; 52 | 53 | bitdogClient.sendData('bd-alarmStatus', coreMessageSchemas.alarmStatusMessageSchema, function (message) { 54 | message.alarmMode = value; 55 | }); 56 | 57 | } 58 | }); 59 | 60 | 61 | this.__defineGetter__('monitoredDevices', function () { return _monitoredDevices; }); 62 | this.__defineSetter__('monitoredDevices', function (value) { _monitoredDevices = value; }); 63 | 64 | this.__defineGetter__('isAlarmed', function () { return _isAlarmed; }); 65 | this.__defineSetter__('isAlarmed', function (value) { _isAlarmed = value; }); 66 | 67 | // Pass in the external zwave controller instance 68 | this.__defineGetter__('bitdogZWave', function () { return _bitdogZWave; }); 69 | this.__defineSetter__('bitdogZWave', function (value) { _bitdogZWave = value; }); 70 | 71 | } 72 | 73 | util.inherits(BitdogAlarm, EventProcessor); 74 | 75 | BitdogAlarm.prototype.onProcessMessage = function (message) { 76 | var key = null; 77 | var zones = null; 78 | var nodeDisplayName = null; 79 | 80 | // We need to do full processing of the event 81 | // Even if the system is not armed for burglar because a safety event may occur 82 | if (message.c.n === bitdogClient.configuration.nodeId && typeof message.h !== typeof undefined && message.h !== null && message.h.n === 'bd-zValueChanged') { 83 | if (typeof message.d !== typeof undefined && message.d !== null && typeof message.d.homeId !== typeof undefined && message.d.homeId !== null) { 84 | // key is hub id / home id / zwave device id 85 | key = message.c.n + '/' + message.d.homeId + '/' + message.d.nodeId; 86 | 87 | // See if this device is associated to any zones by its unique key 88 | zones = this.monitoredDevices[key]; 89 | 90 | // Get the zwave devices display name 91 | nodeDisplayName = this.bitdogZWave.getNodeDisplayName(message.d.nodeId); 92 | 93 | // 'message.d.status & 1 === 1' is a bit check for the burglar bit in status - reference zwaveAlarmNotification.js 94 | // 'message.d.status & 16 === 16' is a bit check for the burglar event bit in status 95 | // burglar bit indicates a value that is stateful, like window open 96 | // burglarEvent bit indicates a transient event like keypad key pressed 97 | // statefulness matters because in the user interface we want to show a state of the device 98 | // a locked lock shows its state as ok from a security stand point, but a key pad press can set off alarm even though the door is still locked 99 | if (typeof zones !== typeof undefined) { 100 | 101 | // burglarAlarm or burglarEventAlarm 102 | if (((message.d.status & 1) === 1) || ((message.d.status & 16) === 16)) { 103 | if (this.alarmMode != alarmMode.disarmed) { 104 | this.isAlarmed = true; 105 | 106 | 107 | bitdogClient.sendData('bd-alarmEvent', coreMessageSchemas.alarmEventMessageSchema, function (newMessage) { 108 | newMessage.status = alarmMode.securityAlarm; 109 | newMessage.event = message; 110 | newMessage.zones = zones.map(function (zone) { 111 | return { name: zone.name, id: zone.id }; 112 | }); 113 | newMessage.device = nodeDisplayName; 114 | 115 | if (typeof message.d.value !== typeof undefined && message.d.value !== null) 116 | newMessage.value = message.d.value.toString(); 117 | }); 118 | } 119 | } 120 | 121 | // safety alarm should fire even when the system is not armed 122 | if ((message.d.status & 8) === 8) { 123 | this.isAlarmed = true; 124 | 125 | bitdogClient.sendData('bd-alarmEvent', coreMessageSchemas.alarmEventMessageSchema, function (newMessage) { 126 | newMessage.status = alarmMode.safetyAlarm; 127 | newMessage.event = message; 128 | newMessage.zones = zones.map(function (zone) { 129 | return { name: zone.name, id: zone.id }; 130 | }); 131 | newMessage.device = nodeDisplayName; 132 | 133 | if (typeof message.d.value !== typeof undefined && message.d.value !== null) 134 | newMessage.value = message.d.value.toString(); 135 | 136 | }); 137 | } 138 | } 139 | } 140 | } else { 141 | 142 | if (message.c.n === bitdogClient.configuration.nodeId && typeof message.h !== typeof undefined && message.h !== null && (message.h.n === 'bd-videoMotionEventStart')) { 143 | 144 | // source path not supported yet, only one camera at the moment. 145 | key = message.c.n + '//dev/video1'; // + '//' + message.d.sourcePath; 146 | 147 | // See if this device is associated to any zones by its unique key 148 | zones = this.monitoredDevices[key]; 149 | 150 | if (typeof zones !== typeof undefined) { 151 | 152 | if (this.alarmMode != alarmMode.disarmed) { 153 | this.isAlarmed = true; 154 | 155 | bitdogClient.sendData('bd-alarmEvent', coreMessageSchemas.alarmEventMessageSchema, function (newMessage) { 156 | newMessage.status = alarmMode.securityAlarm; 157 | newMessage.event = message; 158 | newMessage.zones = zones.map(function (zone) { 159 | return { name: zone.name, id: zone.id }; 160 | }); 161 | newMessage.value = 'Video Motion Detected'; 162 | }); 163 | } 164 | 165 | } 166 | 167 | } 168 | 169 | } 170 | } 171 | 172 | 173 | 174 | BitdogAlarm.prototype.armAway = function (message) { 175 | 176 | if (this.alarmMode !== alarmMode.away) { 177 | this.alarmMode = alarmMode.away; // set mode before loading zones, it matters 178 | this.loadZones(); 179 | 180 | bitdogClient.configuration.set(constants.ALARM_NODE, this.alarmMode); 181 | 182 | } 183 | 184 | bitdogClient.sendData('bd-alarmEvent', coreMessageSchemas.alarmEventMessageSchema, function (newMessage) { 185 | newMessage.status = alarmMode.alarmClear; 186 | newMessage.event = message; 187 | }); 188 | 189 | this.isAlarmed = false; 190 | }; 191 | 192 | BitdogAlarm.prototype.armStay = function (message) { 193 | 194 | if (this.alarmMode !== alarmMode.stay) { 195 | this.alarmMode = alarmMode.stay; // set mode before loading zones, it matters 196 | this.loadZones(); 197 | bitdogClient.configuration.set(constants.ALARM_NODE, this.alarmMode); 198 | 199 | } 200 | 201 | bitdogClient.sendData('bd-alarmEvent', coreMessageSchemas.alarmEventMessageSchema, function (newMessage) { 202 | newMessage.status = alarmMode.alarmClear; 203 | newMessage.event = message; 204 | }); 205 | 206 | this.isAlarmed = false; 207 | 208 | }; 209 | 210 | BitdogAlarm.prototype.disarm = function (message) { 211 | 212 | if (this.alarmMode !== alarmMode.disarmed) { 213 | this.alarmMode = alarmMode.disarmed; 214 | bitdogClient.configuration.set(constants.ALARM_NODE, this.alarmMode); 215 | } 216 | 217 | bitdogClient.sendData('bd-alarmEvent', coreMessageSchemas.alarmEventMessageSchema, function (newMessage) { 218 | newMessage.status = alarmMode.alarmClear; 219 | newMessage.event = message; 220 | }); 221 | 222 | this.isAlarmed = false; 223 | 224 | }; 225 | 226 | BitdogAlarm.prototype.loadZones = function () { 227 | var zoneIndex = 0; 228 | var deviceIndex = 0; 229 | var zone = null; 230 | var definition = null; 231 | var nodeId = null; 232 | var zwaveNodeId = null; 233 | var zoneDevices = null; 234 | var zoneDevice = null; 235 | var shouldAddDevice = false; 236 | var zones = null; 237 | 238 | this.zones = bitdogClient.configuration.get(constants.ZONES_CONFIG); 239 | this.monitoredDevices = {}; 240 | 241 | if (typeof this.zones !== typeof undefined && this.zones !== null) { 242 | for (zoneIndex = 0; zoneIndex < this.zones.length; zoneIndex++) { 243 | zone = this.zones[zoneIndex]; 244 | definition = zone.definition; 245 | if (typeof definition !== typeof undefined && definition !== null) { 246 | zoneDevices = definition.zoneDevices; 247 | if (typeof zoneDevices !== typeof undefined && zoneDevices !== null) { 248 | for (deviceIndex in zoneDevices) { 249 | shouldAddDevice = false; 250 | zoneDevice = zoneDevices[deviceIndex]; 251 | 252 | if (zoneDevice.include === true) { 253 | // some devices are not for away and stay but for safety 254 | if (typeof zoneDevice.away === typeof undefined && typeof zoneDevice.stay === typeof undefined) { 255 | shouldAddDevice = true; 256 | } else if (zoneDevice.stay === true && this.alarmMode === alarmMode.stay) { 257 | shouldAddDevice = true; 258 | } else if (zoneDevice.away === true && this.alarmMode === alarmMode.away) { 259 | shouldAddDevice = true; 260 | } 261 | } 262 | 263 | if (shouldAddDevice === true) { 264 | zones = this.monitoredDevices[deviceIndex]; 265 | 266 | if (typeof zones === typeof undefined || zones === null) { 267 | zones = []; 268 | this.monitoredDevices[deviceIndex] = zones; 269 | } 270 | 271 | zones.push(zone); 272 | } 273 | 274 | } 275 | } 276 | } 277 | 278 | } 279 | } 280 | }; 281 | 282 | module.exports = new BitdogAlarm(); -------------------------------------------------------------------------------- /lib/automation/scheduler.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // scheduler.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 'use strict'; 26 | let SunCalc = require('suncalc'); 27 | let bitdogClient = require('bitdog-client'); 28 | let constants = require('../constants.js'); 29 | let coreMessageSchemas = require('../coreMessageSchemas.js'); 30 | let moment = require('moment'); 31 | let weatherManager = require('./weatherManager.js'); 32 | 33 | function Scheduler(automationConfiguration) { 34 | 35 | let _schedules = {}; 36 | let _weatherForecast = null; 37 | let _automationConfiguration = automationConfiguration; 38 | 39 | this.__defineGetter__('automationConfiguration', function () { return _automationConfiguration; }); 40 | 41 | this.__defineGetter__('schedules', function () { return _schedules; }); 42 | this.__defineSetter__('schedules', function (value) { _schedules = value; }); 43 | 44 | this.__defineGetter__('weatherForecast', function () { return _weatherForecast; }); 45 | this.__defineSetter__('weatherForecast', function (value) { _weatherForecast = value; }); 46 | 47 | } 48 | 49 | Scheduler.prototype.createTodaysSchedule = function () { 50 | let now = new Date(); 51 | this.schedules = {}; 52 | this.schedules.dayOfWeek = this.getDayOfWeek(); 53 | 54 | let automation = null; 55 | let name = null; 56 | let automationId = null; 57 | let definition = null; 58 | let trigger = null; 59 | let targetTime = null; 60 | let index = null; 61 | let hour = null; 62 | let minute = null; 63 | let location = null; 64 | let astronomicalTimes = null; 65 | 66 | try { 67 | 68 | for (index = 0; index < this.automationConfiguration.length; index++) { 69 | automation = this.automationConfiguration[index]; 70 | name = automation.Name; 71 | automationId = automation.AutomationId; 72 | definition = automation.Definition; 73 | trigger = definition.trigger; 74 | targetTime = null; 75 | 76 | switch (trigger.triggerId) { 77 | case 'recurringTime': 78 | 79 | switch (trigger.dateTimeId) { 80 | case 'weekdays': 81 | if (trigger.weekdays[this.schedules.dayOfWeek] !== true) 82 | break; 83 | 84 | case 'everyDay': 85 | 86 | if (trigger.timeTypeId === 'specific') { 87 | hour = parseInt(trigger.hour); 88 | minute = parseInt(trigger.minute); 89 | 90 | if (trigger.amPm.toLowerCase() === 'pm') 91 | hour += 12; 92 | 93 | targetTime = moment(); 94 | targetTime.hour(hour); 95 | targetTime.minute(minute); 96 | targetTime.second(0); 97 | 98 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Target time ' + trigger.timeTypeId + ' for automation \'' + name + '\': ' + targetTime.format()); 99 | 100 | if (targetTime.isSameOrAfter(now)) { 101 | this.addAutomationToSchedule(targetTime, automation); 102 | } 103 | } 104 | else { 105 | location = bitdogClient.configuration.get(constants.AUTOMATIONS_LOCATION); 106 | 107 | if (typeof location !== typeof undefined && location !== null) { 108 | astronomicalTimes = this.getAstronomicalTimes(location.latitude, location.longitude); 109 | targetTime = moment(astronomicalTimes[trigger.timeTypeId]); 110 | 111 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Target time ' + trigger.timeTypeId + ' for automation \'' + name + '\': ' + targetTime.format()); 112 | 113 | if (typeof trigger.offsetTypeId !== typeof undefined && typeof trigger.offsetMinutes !== typeof undefined) { 114 | switch (trigger.offsetTypeId) { 115 | case 'before': 116 | targetTime.subtract(trigger.offsetMinutes, 'minutes'); 117 | break; 118 | case 'after': 119 | targetTime.add(trigger.offsetMinutes, 'minutes'); 120 | break; 121 | } 122 | 123 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Target has offset for ' + trigger.offsetMinutes + ' minutes ' + trigger.offsetTypeId + ' ' + trigger.timeTypeId + ', new target time for automation \'' + name + '\': ' + targetTime.format()); 124 | 125 | } 126 | 127 | if (targetTime.isSameOrAfter(now)) { 128 | this.addAutomationToSchedule(targetTime, automation); 129 | } 130 | } else { 131 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Location not set and cannot calculate ' + trigger.timeTypeId + ' for automation \'' + name + '\': ' + targetTime.format()); 132 | 133 | } 134 | } 135 | break; 136 | case 'everyHour': 137 | break; 138 | 139 | } 140 | 141 | break; 142 | case 'deviceValueChanges': 143 | break; 144 | case 'valueChangesAnyDevice': 145 | break; 146 | 147 | } 148 | 149 | } 150 | 151 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Calculated todays schedule', this.schedules); 152 | 153 | bitdogClient.sendData('bd-automationScheduled', coreMessageSchemas.automationScheduledMessageSchema, function (message) { 154 | let items = []; 155 | let time = null; 156 | let index = 0; 157 | let item = null; 158 | 159 | for (time in this.schedules) { 160 | if (time !== 'dayOfWeek') { 161 | for (index = 0; index < this.schedules[time].length; index++) { 162 | item = this.schedules[time][index]; 163 | items.push({ 164 | automationId: item.AutomationId, executionTime: item.scheduledExecutionDateTime 165 | }); 166 | } 167 | } 168 | } 169 | 170 | message.schedule = items; 171 | }.bind(this)); 172 | } 173 | catch (e) { 174 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Could not calculate schedule', e); 175 | 176 | } 177 | } 178 | 179 | Scheduler.prototype.addAutomationToSchedule = function (momentValue, automation) { 180 | let timeValue = momentValue.unix().toString(); 181 | automation.scheduledExecutionDateTime = momentValue.utc().format(); 182 | 183 | if (typeof this.schedules[timeValue] === typeof undefined) 184 | this.schedules[timeValue] = []; 185 | 186 | this.schedules[timeValue].push(automation); 187 | } 188 | 189 | Scheduler.prototype.tock = function () { 190 | 191 | if (this.schedules.dayOfWeek !== this.getDayOfWeek()) { 192 | this.createTodaysSchedule(); 193 | } 194 | else { 195 | let now = moment(); 196 | let fiveMinutesAgo = moment(); fiveMinutesAgo.subtract(5, 'minutes'); 197 | let schedules = []; 198 | let automation = null; 199 | let definition = null; 200 | let name = null; 201 | let executedTimes = []; 202 | let timeValue = null; 203 | let messageResult = null; 204 | let automationIndex = 0; 205 | let commandIndex = 0; 206 | 207 | for (timeValue in this.schedules) { 208 | 209 | if (timeValue === 'dayOfWeek') // ignore variable property on schedules 210 | continue; 211 | 212 | let time = moment.unix(parseInt(timeValue)); 213 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Comparing times', { time: time.format() }); 214 | 215 | // Between now and five minutes ago 216 | if (time.isSameOrAfter(fiveMinutesAgo) && time.isSameOrBefore(now)) { 217 | schedules = this.schedules[timeValue]; 218 | executedTimes.push(timeValue); 219 | 220 | 221 | for (automationIndex = 0; automationIndex < schedules.length; automationIndex++) { 222 | automation = schedules[automationIndex]; 223 | definition = automation.Definition; 224 | name = automation.Name; 225 | 226 | bitdogClient.sendData('bd-automationExecuted', coreMessageSchemas.automationExecutedMessageSchema, function (message) { 227 | message.automationId = automation.AutomationId; 228 | }); 229 | 230 | for (commandIndex = 0; commandIndex < definition.commands.length; commandIndex++) { 231 | messageResult = bitdogClient.sendMessage(definition.commands[commandIndex].message); 232 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Executed automation: \'' + name + '\' command: \'' + definition.commands[commandIndex].name + '\'', messageResult); 233 | } 234 | } 235 | 236 | } 237 | 238 | 239 | } 240 | 241 | for (let timesIndex = 0; timesIndex < executedTimes.length; timesIndex++) { 242 | timeValue = executedTimes[timesIndex]; 243 | delete this.schedules[timeValue]; 244 | } 245 | 246 | 247 | } 248 | }; 249 | 250 | Scheduler.prototype.weatherTock = function () { 251 | let location = bitdogClient.configuration.get(constants.AUTOMATIONS_LOCATION); 252 | let self = this; 253 | 254 | if (typeof location !== typeof undefined && location !== null) { 255 | bitdogClient.sendCommand(constants.BITDOG_CLOUD_NODE, 'bd-getWeatherCurrent', coreMessageSchemas.weatherRequestMessageSchema, function (message) { 256 | message.lat = location.latitude; 257 | message.long = location.longitude; 258 | }).then(function (commandResult) { 259 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Retrieved weather forecast'); 260 | self.weatherForecast = commandResult.result.value; 261 | weatherManager.processWeatherForecast(self.weatherForecast); 262 | }, function (error) { 263 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Could not retrieve weather forecast', error); 264 | }); 265 | } 266 | }; 267 | 268 | Scheduler.prototype.getDayOfWeek = function () { 269 | return moment().format('dddd').toLowerCase(); 270 | }; 271 | 272 | Scheduler.prototype.getAstronomicalTimes = function (latitude, longitude) { 273 | let todayAtNoon = new Date(); 274 | todayAtNoon.setHours(12); 275 | 276 | // Using noon to avoid the wrong day being picked when too close to midnight 277 | return SunCalc.getTimes(todayAtNoon, latitude, longitude); 278 | } 279 | 280 | module.exports = Scheduler; -------------------------------------------------------------------------------- /lib/automation/eventCapturer.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // eventCapturer.js 4 | // 5 | // Copyright (c) 2015-2017 Bitdog LLC. 6 | // 7 | // SOFTWARE NOTICE AND LICENSE 8 | // 9 | // This file is part of bitdog-hub. 10 | // 11 | // bitdog-hub is free software: you can redistribute it and/or modify 12 | // it under the terms of the GNU General Public License as published 13 | // by the Free Software Foundation, either version 3 of the License, 14 | // or (at your option) any later version. 15 | // 16 | // bitdog-hub is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | // GNU General Public License for more details. 20 | // 21 | // You should have received a copy of the GNU General Public License 22 | // along with bitdog-hub. If not, see . 23 | // 24 | //----------------------------------------------------------------------------- 25 | 26 | 27 | var SunCalc = require('suncalc'); 28 | var bitdogClient = require('bitdog-client'); 29 | var constants = require('../constants.js'); 30 | var coreMessageSchemas = require('../coreMessageSchemas.js'); 31 | var moment = require('moment'); 32 | 33 | 34 | function EventCapturer(automationConfiguration) { 35 | var _automationConfiguration = automationConfiguration; 36 | var _captureList = {}; 37 | 38 | 39 | this.__defineGetter__('automationConfiguration', function () { return _automationConfiguration; }); 40 | this.__defineGetter__('captureList', function () { return _captureList; }); 41 | 42 | 43 | this.createEventCaptureList(); 44 | 45 | } 46 | 47 | EventCapturer.prototype.createEventCaptureList = function () { 48 | for (var index = 0; index < this.automationConfiguration.length; index++) { 49 | var automation = this.automationConfiguration[index]; 50 | var name = automation.Name; 51 | var automationId = automation.AutomationId; 52 | var definition = automation.Definition; 53 | var trigger = definition.trigger; 54 | 55 | switch (trigger.triggerId) { 56 | case 'recurringTime': 57 | break; 58 | case 'deviceValueChanges': 59 | this.addAutomationToEventCapturer(trigger.reference.id, automation); 60 | break; 61 | case 'valueChangesAnyDevice': 62 | this.addAutomationToEventCapturer(trigger.classId + '/' + trigger.propertyId, automation); 63 | break; 64 | case 'sceneEvent': 65 | this.addAutomationToEventCapturer(trigger.reference.id + '/' + trigger.value, automation); 66 | break; 67 | 68 | } 69 | } 70 | 71 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Created event capture list', this.captureList); 72 | 73 | }; 74 | 75 | EventCapturer.prototype.addAutomationToEventCapturer = function (eventId, automation) { 76 | if (typeof this.captureList[eventId] === typeof undefined) 77 | this.captureList[eventId] = []; 78 | 79 | this.captureList[eventId].push(automation); 80 | }; 81 | 82 | EventCapturer.prototype.onProcessMessage = function (message) { 83 | var id = this.calculateId(message); 84 | var definition = null; 85 | var name = null; 86 | var logic = null; 87 | var trigger = null; 88 | var propertyName = null; 89 | var automations = []; 90 | var specific = this.captureList[id.specific]; 91 | var generic = this.captureList[id.generic]; 92 | 93 | if (typeof specific !== typeof undefined && specific != null) { 94 | automations = automations.concat(specific); 95 | } 96 | 97 | if (typeof generic !== typeof undefined && generic != null) { 98 | automations = automations.concat(generic); 99 | } 100 | 101 | for (var automationIndex = 0; automationIndex < automations.length; automationIndex++) { 102 | automation = automations[automationIndex]; 103 | definition = automation.Definition; 104 | trigger = definition.trigger; 105 | name = automation.Name; 106 | 107 | if (this.compare(message, trigger) === true) { 108 | 109 | if (this.shouldIgnore(definition) === false) { // This step potentially requires heavier computation, so its checked second 110 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Trigger matched message for automation: \'' + name + '\'', message); 111 | 112 | bitdogClient.sendData('bd-automationExecuted', coreMessageSchemas.automationExecutedMessageSchema, function (message) { 113 | message.automationId = automation.AutomationId; 114 | }); 115 | 116 | for (var commandIndex = 0; commandIndex < definition.commands.length; commandIndex++) { 117 | messageResult = bitdogClient.sendMessage(definition.commands[commandIndex].message); 118 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Executed automation: \'' + name + '\'', messageResult); 119 | } 120 | } 121 | else { 122 | 123 | bitdogClient.logger.logProcessEvent(constants.LOG_PROCESS_AUTOMATION, 'Trigger matched message for automation, but it has been filtered: \'' + name + '\'', message); 124 | 125 | } 126 | } 127 | } 128 | 129 | 130 | 131 | }; 132 | 133 | EventCapturer.prototype.shouldIgnore = function (definition) { 134 | var triggerFilter = definition.triggerFilter; 135 | var now = new Date(); 136 | var result = true; 137 | var location = bitdogClient.configuration.get(constants.AUTOMATIONS_LOCATION); 138 | 139 | 140 | if (triggerFilter.filterTypeId === 'always') { 141 | result = false; 142 | } else if (triggerFilter.filterTypeId === 'betweenTimes') { 143 | var startTime = this.getTargetTime(triggerFilter.start, location.latitude, location.longitude); 144 | var endTime = this.getTargetTime(triggerFilter.end, location.latitude, location.longitude); 145 | 146 | if (startTime.isSame(endTime)) { // invalid value, ignore 147 | result = true; 148 | } else if (endTime.isBefore(startTime)) { // schedule crosses days 149 | 150 | if (endTime.isAfter(now)) { // start was yesterday and we haven't ended yet 151 | result = false; // don't ignore 152 | } 153 | else if (startTime.isSameOrBefore(now)) { // we started and the end is tomorrow 154 | result = false; // don't ignore 155 | } else { 156 | result = true; // ignore 157 | } 158 | 159 | } else if (startTime.isSameOrBefore(now) && endTime.isAfter(now)) { // now is between start and end, don't ignore 160 | result = false; 161 | } 162 | } 163 | 164 | return result; 165 | 166 | } 167 | 168 | EventCapturer.prototype.getTargetTime = function (timeDefintion, latitude, longitude) { 169 | 170 | var targetTime = null; 171 | var now = new Date(); 172 | 173 | 174 | if (timeDefintion.timeTypeId === 'specific') { 175 | var hour = parseInt(timeDefintion.hour); 176 | var minute = parseInt(timeDefintion.minute); 177 | 178 | if (timeDefintion.amPm.toLowerCase() === 'pm') 179 | hour += 12; 180 | 181 | targetTime = moment(); 182 | targetTime.hour(hour); 183 | targetTime.minute(minute); 184 | targetTime.second(0); 185 | 186 | } 187 | else { 188 | 189 | var astronomicalTimes = this.getAstronomicalTimes(latitude, longitude); 190 | targetTime = moment(astronomicalTimes[timeDefintion.timeTypeId]); 191 | } 192 | 193 | return targetTime; 194 | 195 | } 196 | 197 | // This is used to determine the unique id of what is changing 198 | EventCapturer.prototype.calculateId = function (message) { 199 | var id = { specific: null, generic: null }; 200 | 201 | 202 | switch (message.h.n) { 203 | case 'bd-zValueChanged': 204 | id.specific = message.c.n + '/' + message.d.homeId + '/' + message.d.nodeId + '/' + message.d.classId + '/' + message.d.instanceId + '/' + message.d.indexId; 205 | id.generic = message.d.classId + '/' + message.d.indexId 206 | break; 207 | 208 | case 'bd-zSceneEvent': 209 | id.specific = message.c.n + '/' + message.d.homeId + '/' + message.d.nodeId + '/' + message.d.value; 210 | break; 211 | 212 | default: 213 | id.specific = message.c.n + '/' + message.h.n; 214 | // the following is not supported yet 215 | id.generic = message.h.n; 216 | break; 217 | } 218 | 219 | return id; 220 | 221 | } 222 | 223 | EventCapturer.prototype.getAstronomicalTimes = function (latitude, longitude) { 224 | var todayAtNoon = new Date(); 225 | todayAtNoon.setHours(12); 226 | 227 | // Using noon to avoid the wrong day being picked when too close to midnight 228 | return SunCalc.getTimes(todayAtNoon, latitude, longitude); 229 | } 230 | 231 | EventCapturer.prototype.compare = function (message, trigger) { 232 | var results = true; 233 | var logic = trigger.logic; 234 | 235 | if (typeof trigger.classId != typeof undefined || typeof trigger.reference.zwaveNodeId !== typeof undefined) { 236 | for (var property in logic) { 237 | switch (logic[property]) { 238 | case 'ignore': 239 | break; 240 | case '=': 241 | results = results && (message.d.value == trigger.value); 242 | break; 243 | case '!=': 244 | results = results && (message.d.value != trigger.value); 245 | break; 246 | case '>': 247 | results = results && (message.d.value > trigger.value); 248 | break; 249 | case '>=': 250 | results = results && (message.d.value >= trigger.value); 251 | break; 252 | case '<': 253 | results = results && (message.d.value < trigger.value); 254 | break; 255 | case '<=': 256 | results = results && (message.d.value <= trigger.value); 257 | break; 258 | case 'between': 259 | results = results && (message.d.value > trigger.value && message.d.value < trigger.value2); 260 | break; 261 | case 'starts with': 262 | results = results && typeof message.d.value != typeof undefined && message.d.value != null && message.d.value.startsWith(trigger.value); 263 | break; 264 | case 'ends with': 265 | results = results && typeof message.d.value != typeof undefined && message.d.value != null && message.d.value.endsWith(trigger.value); 266 | break; 267 | case 'contains': 268 | results = results && typeof message.d.value != typeof undefined && message.d.value != null && message.d.value.indexOf(trigger.value) != -1; 269 | break; 270 | case "equals": 271 | results = results && (message.d.value == trigger.value); 272 | break; 273 | 274 | } 275 | } 276 | } 277 | else { 278 | for (var property in logic) { 279 | switch (logic[property]) { 280 | case 'ignore': 281 | break; 282 | case '=': 283 | results = results && (message.d[property] == trigger.value[property]); 284 | break; 285 | case '!=': 286 | results = results && (message.d[property] != trigger.value[property]); 287 | break; 288 | case '>': 289 | results = results && (message.d[property] > trigger.value[property]); 290 | break; 291 | case '>=': 292 | results = results && (message.d[property] >= trigger.value[property]); 293 | break; 294 | case '<': 295 | results = results && (message.d[property] < trigger.value[property]); 296 | break; 297 | case '<=': 298 | results = results && (message.d[property] <= trigger.value[property]); 299 | break; 300 | case 'between': 301 | results = results && (message.d[property] > trigger.value[property] && message.d[property] < trigger.value2[property]); 302 | break; 303 | case 'starts with': 304 | results = results && typeof message.d[property] != typeof undefined && message.d[property] != null && message.d[property].startsWith(trigger.value[property]); 305 | break; 306 | case 'ends with': 307 | results = results && typeof message.d[property] != typeof undefined && message.d[property] != null && message.d[property].endsWith(trigger.value[property]); 308 | break; 309 | case 'contains': 310 | results = results && typeof message.d[property] != typeof undefined && message.d[property] != null && message.d[property].indexOf(trigger.value[property]) != -1; 311 | break; 312 | case "equals": 313 | results = results && (message.d[property] == trigger.value[property]); 314 | break; 315 | 316 | } 317 | } 318 | } 319 | 320 | return results; 321 | } 322 | 323 | module.exports = EventCapturer; -------------------------------------------------------------------------------- /typings/globals/rpio/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/fb972b28d215806c57280990833b6e7102012056/rpio/index.d.ts 3 | declare var rpio: Rpio; 4 | 5 | declare module 'rpio' { 6 | export = rpio; 7 | } 8 | 9 | interface Rpio { 10 | /** 11 | * Initialise the bcm2835 library. This will be called automatically by .open() using the default option values if not called explicitly. 12 | * @param options 13 | */ 14 | init(options: RPIO.Options): void; 15 | 16 | /** 17 | * Open a pin for input or output. Valid modes are: 18 | * INPUT: pin is input (read-only). 19 | * OUTPUT: pin is output (read-write). 20 | * PWM: configure pin for hardware PWM. 21 | * 22 | * For input pins, option can be used to configure the internal pullup or pulldown resistors using options as described in the .pud() documentation below. 23 | * 24 | * For output pins, option defines the initial isMotionDetected of the pin, rather than having to issue a separate .write() call. This can be critical for devices which must have a stable value, rather than relying on the initial floating value when a pin is enabled for output but hasn't yet been configured with a value. 25 | * @param pin 26 | * @param mode 27 | * @param options 28 | */ 29 | open(pin: number, mode: number, options?: number): void; 30 | 31 | /** 32 | * Switch a pin that has already been opened in one mode to a different mode. 33 | * This is provided primarily for performance reasons, as it avoids some of the setup work done by .open(). 34 | * @param pin 35 | * @param mode 36 | */ 37 | mode(pin: number, mode: number): void; 38 | 39 | /** 40 | * Read the current value of pin, returning either 1 (high) or 0 (low). 41 | * @param pin 42 | */ 43 | read(pin: number): number; 44 | 45 | /** 46 | * Read length bits from pin into buffer as fast as possible. If length isn't specified it defaults to buffer.length. 47 | * @param pin 48 | * @param buffer 49 | * @param length 50 | */ 51 | readbuf(pin: number, buffer: Buffer, length?: number): void; 52 | 53 | /** 54 | * Set the specified pin either high or low, using either the HIGH/LOW constants, or simply 1 or 0. 55 | * @param pin 56 | * @param value 57 | */ 58 | write(pin: number, value: number): void; 59 | 60 | /** 61 | * Write length bits to pin from buffer as fast as possible. If length isn't specified it defaults to buffer.length. 62 | 63 | * @param pin 64 | * @param buffer 65 | * @param length 66 | */ 67 | writebuf(pin: number, buffer: Buffer, length?: number): void; 68 | 69 | /** 70 | * Read the current isMotionDetected of the GPIO pad control for the specified GPIO group. On current models of Raspberry Pi there are three groups with corresponding defines: 71 | * PAD_GROUP_0_27: GPIO0 - GPIO27. Use this for the main GPIO header. 72 | * PAD_GROUP_28_45: GPIO28 - GPIO45. Use this to configure the P5 header. 73 | * PAD_GROUP_46_53: GPIO46 - GPIO53. Internal, you probably won't need this. 74 | * 75 | * The value returned will be a bit mask of the following defines: 76 | * PAD_SLEW_UNLIMITED: 0x10. Slew rate unlimited if set. 77 | * PAD_HYSTERESIS: 0x08. Hysteresis is enabled if set. 78 | * 79 | * The bottom three bits determine the drive current: 80 | * PAD_DRIVE_2mA: 0b000 81 | * PAD_DRIVE_4mA: 0b001 82 | * PAD_DRIVE_6mA: 0b010 83 | * PAD_DRIVE_8mA: 0b011 84 | * PAD_DRIVE_10mA: 0b100 85 | * PAD_DRIVE_12mA: 0b101 86 | * PAD_DRIVE_14mA: 0b110 87 | * PAD_DRIVE_16mA: 0b111 88 | * 89 | * @note Note that the pad control registers are not available via /dev/gpiomem, so you will need to use .init({gpiomem: false}) and run as root. 90 | * @param group 91 | */ 92 | readpad(group: number): number; 93 | 94 | /** 95 | * Write control settings to the pad control for group. Uses the same defines as above for .readpad(). 96 | * @param group 97 | * @param control 98 | */ 99 | writepad(group: number, control: number): void; 100 | 101 | /** 102 | * Configure the pin's internal pullup or pulldown resistors, using the following isMotionDetected constants: 103 | * PULL_OFF: disable configured resistors. 104 | * PULL_DOWN: enable the pulldown resistor. 105 | * PULL_UP: enable the pullup resistor. 106 | * 107 | * @param pin 108 | * @param state 109 | */ 110 | pud(pin: number, state: number): void; 111 | 112 | /** 113 | * Watch pin for changes and execute the callback cb() on events. cb() takes a single argument, the pin which triggered the callback. 114 | * 115 | * The optional direction argument can be used to watch for specific events: 116 | * POLL_LOW: poll for falling edge transitions to low. 117 | * POLL_HIGH: poll for rising edge transitions to high. 118 | * POLL_BOTH: poll for both transitions (the default). 119 | * 120 | * Due to hardware/kernel limitations we can only poll for changes, and the event detection only says that an event occurred, not which one. The poll interval is a 1ms setInterval() and transitions could come in between detecting the event and reading the value. Therefore this interface is only useful for events which transition slower than approximately 1kHz. 121 | * 122 | * To stop watching for pin changes, call .poll() again, setting the callback to null. 123 | * @param pin 124 | * @param cb 125 | * @param direction 126 | */ 127 | poll(pin: number, cb: RPIO.CallbackFunction, direction?: number): void; 128 | 129 | /** 130 | * Reset pin to INPUT and clear any pullup/pulldown resistors and poll events. 131 | * @param pin 132 | */ 133 | close(pin: number): void; 134 | 135 | // I²C 136 | 137 | /** 138 | * Assign pins 3 and 5 to i²c use. Until .i2cEnd() is called they won't be available for GPIO use. 139 | * 140 | * The pin assignments are: 141 | * Pin 3: SDA (Serial Data) 142 | * Pin 5: SCL (Serial Clock) 143 | */ 144 | i2cBegin(): void; 145 | 146 | /** 147 | * Configure the slave address. This is between 0 - 0x7f, and it can be helpful to 148 | * run the i2cdetect program to figure out where your devices are if you are unsure. 149 | * @param address 150 | */ 151 | i2cSetSlaveAddress(address: number): void; 152 | 153 | /** 154 | * Set the baud rate - directly set the speed in hertz. 155 | * @param baudRate 156 | */ 157 | i2cSetBaudRate(baudRate: number): void; 158 | 159 | /** 160 | * Read from the i²c slave. 161 | * Function takes a buffer and optional length argument, defaulting to the length of the buffer if not specified. 162 | * @param buffer 163 | * @param length 164 | */ 165 | i2cRead(buffer: Buffer, length?: number): void; 166 | 167 | /** 168 | * Write to the i²c slave. 169 | * Function takes a buffer and optional length argument, defaulting to the length of the buffer if not specified. 170 | * @param biffer 171 | * @param length 172 | */ 173 | i2cWrite(biffer: Buffer, length?: number): void; 174 | 175 | /** 176 | * Set the baud rate - based on a divisor of the base 250MHz rate. 177 | * @param clockDivider 178 | */ 179 | i2cSetClockDivider(clockDivider: number): void; 180 | 181 | 182 | 183 | 184 | /** 185 | * Turn off the i²c interface and return the pins to GPIO. 186 | */ 187 | i2cEnd(): void; 188 | 189 | // PWM 190 | 191 | /** 192 | * Set the PWM refresh rate. 193 | * @param clockDivider: power-of-two divisor of the base 19.2MHz rate, with a maximum value of 4096 (4.6875kHz). 194 | */ 195 | pwmSetClockDivider(clockDivider: number): void; 196 | 197 | /** 198 | * Set the PWM range for a pin. This determines the maximum pulse width. 199 | * @param pin 200 | * @param range 201 | */ 202 | pwmSetRange(pin: number, range: number): void; 203 | 204 | /** 205 | * Set the PWM width for a pin. 206 | * @param pin 207 | * @param data 208 | */ 209 | pwmSetData(pin: number, data: number): void; 210 | 211 | // SPI 212 | 213 | /** 214 | * Switch pins 119, 21, 23, 24 and 25 (GPIO7-GPIO11) to SPI mode 215 | * 216 | * Pin | Function 217 | * -----|---------- 218 | * 19 | MOSI 219 | * 21 | MISO 220 | * 23 | SCLK 221 | * 24 | CE0 222 | * 25 | CE1 223 | */ 224 | spiBegin(): void; 225 | 226 | /** 227 | * Choose which of the chip select / chip enable pins to control. 228 | * 229 | * Value | Pin 230 | * ------|--------------------- 231 | * 0 | SPI_CE0 (24 / GPIO8) 232 | * 1 | SPI_CE1 (25 / GPIO7) 233 | * 2 | Both 234 | * 235 | * @param chip 236 | */ 237 | spiChipSelect(cePin: number): void; 238 | 239 | /** 240 | * Commonly chip enable (CE) pins are active low, and this is the default. 241 | * If your device's CE pin is active high, use spiSetCSPolarity() to change the polarity. 242 | * @param cePin 243 | * @param polarity 244 | */ 245 | spiSetCSPolarity(cePin: number, polarity: number): void; 246 | 247 | /** 248 | * Set the SPI clock speed with. 249 | * @param clockDivider: an even divisor of the base 250MHz rate ranging between 0 and 65536. 250 | */ 251 | spiSetClockDivider(clockDivider: number): void; 252 | 253 | /** 254 | * Transfer data. Data is sent and received in 8-bit chunks via buffers which should be the same size. 255 | * @param txBuffer 256 | * @param rxBuffer 257 | * @param txLength 258 | */ 259 | spiTransfer(txBuffer: Buffer, rxBuffer: Buffer, txLength: number): void; 260 | 261 | /** 262 | * Send data and do not care about the data coming back. 263 | * @param txBuffer 264 | * @param txLength 265 | */ 266 | spiWrite(txBuffer: Buffer, txLength: number): void; 267 | 268 | /** 269 | * Release the pins back to general purpose use. 270 | */ 271 | spiEnd(): void; 272 | 273 | // Misc 274 | 275 | /** 276 | * Sleep for n seconds. 277 | * @param n: number of seconds to sleep 278 | */ 279 | sleep(n: number): void; 280 | 281 | /** 282 | * Sleep for n milliseconds. 283 | * @param n: number of milliseconds to sleep 284 | */ 285 | msleep(n: number): void; 286 | 287 | /** 288 | * Sleep for n microseconds. 289 | * @param n: number of microseconds to sleep 290 | */ 291 | usleep(n: number): void; 292 | 293 | 294 | // Constants: 295 | 296 | HIGH: number; 297 | LOW: number; 298 | 299 | INPUT: number; 300 | OUTPUT: number; 301 | PWM: number; 302 | 303 | PULL_OFF: number; 304 | PULL_DOWN: number; 305 | PULL_UP: number; 306 | 307 | PAD_GROUP_0_27: number; 308 | PAD_GROUP_28_45: number; 309 | PAD_GROUP_46_53: number; 310 | 311 | PAD_SLEW_UNLIMITED: number; 312 | PAD_HYSTERESIS: number; 313 | 314 | PAD_DRIVE_2mA: number; 315 | PAD_DRIVE_4mA: number; 316 | PAD_DRIVE_6mA: number; 317 | PAD_DRIVE_8mA: number; 318 | PAD_DRIVE_10mA: number; 319 | PAD_DRIVE_12mA: number; 320 | PAD_DRIVE_14mA: number; 321 | PAD_DRIVE_16mA: number; 322 | 323 | POLL_LOW: number; 324 | POLL_HIGH: number; 325 | POLL_BOTH: number; 326 | 327 | } 328 | 329 | declare namespace RPIO { 330 | 331 | interface Options { 332 | 333 | /** 334 | * There are two device nodes for GPIO access. The default is /dev/gpiomem which, when configured with gpio group access, allows users in that group to read/write directly to that device. This removes the need to run as root, but is limited to GPIO functions. 335 | * For non-GPIO functions (i²c, PWM, SPI) the /dev/mem device is required for full access to the Broadcom peripheral address range and the program needs to be executed as the root user (e.g. via sudo). If you do not explicitly call .init() when using those functions, the library will do it for you with gpiomem: false. 336 | * You may also need to use gpiomem: false if you are running on an older Linux kernel which does not support the gpiomem module. 337 | * rpio will throw an exception if you try to use one of the non-GPIO functions after already opening with /dev/gpiomem, as well as checking to see if you have the necessary permissions. 338 | * 339 | * Valid options: 340 | * true: use /dev/gpiomem for non-root but GPIO-only access 341 | * false: use /dev/mem for full access but requires root 342 | */ 343 | gpiomem?: boolean; 344 | 345 | /** 346 | * There are two naming schemes when referring to GPIO pins: 347 | * By their physical header location: Pins 1 to 26 (A/B) or Pins 1 to 40 (A+/B+) 348 | * Using the Broadcom hardware map: GPIO 0-25 (B rev1), GPIO 2-27 (A/B rev2, A+/B+) 349 | * 350 | * Confusingly however, the Broadcom GPIO map changes between revisions, so for example P3 maps to GPIO0 on Model B Revision 1 models, but maps to GPIO2 on all later models. 351 | * This means the only sane default mapping is the physical layout, so that the same code will work on all models regardless of the underlying GPIO mapping. 352 | * If you prefer to use the Broadcom GPIO scheme for whatever reason (e.g. to use the P5 header pins on the Raspberry Pi 1 revision 2.0 model which aren't currently mapped to the physical layout), you can set mapping to gpio to switch to the GPIOxx naming. 353 | * 354 | * Valid options: 355 | * gpio: use the Broadcom GPIOxx naming 356 | * physical: use the physical P01-P40 header layou 357 | */ 358 | mapping?: "gpio" | "physical"; 359 | } 360 | 361 | interface CallbackFunction { 362 | /** 363 | * @param pin: The pin which triggered the callback. 364 | */ 365 | (pin: number): void; 366 | } 367 | } 368 | --------------------------------------------------------------------------------