├── .gitignore ├── .gitmodules ├── .jshintrc ├── .travis.yml ├── CONTRIBUTING.md ├── DEVELOPERS.md ├── Dockerfile ├── Dockerfile-apphost ├── Dockerfile-apphost-arm ├── HISTORY.md ├── INSTALLATION.md ├── LICENSE ├── README.md ├── apps ├── index.js ├── lib │ ├── audio.js │ ├── cv.js │ ├── gpio.js │ ├── index.js │ ├── init.js │ ├── led.js │ ├── send.js │ ├── sensor.js │ ├── service.js │ ├── setting.js │ ├── store.js │ ├── util.js │ └── zigbee.js ├── matrix.js ├── package.json └── storage │ ├── matrix_online.wav │ ├── shia-labeouf-orig.mp3 │ └── shia-labeouf.mp3 ├── bin ├── reset.js └── set.js ├── config ├── env │ ├── dev.js │ ├── device.js │ ├── docker-local.js │ ├── docker.js │ ├── hardcode.js │ ├── home.js │ ├── index.js │ ├── local.js │ ├── production.js │ ├── rc.js │ └── stage.js ├── index.js └── path.js ├── debian ├── TODO ├── changelog ├── compat ├── control ├── copyright ├── dist │ └── matrixio-os.service ├── download-bundled.sh ├── matrixio-os.install ├── postinst ├── postrm ├── prerm ├── rules └── util │ ├── cleanup.sh │ └── create.sh ├── docker-compose.yml ├── docker-entrypoint.sh ├── dockertest.js ├── index.js ├── initmatrix.sh ├── lib ├── device │ ├── bluetooth │ │ ├── Characteristics │ │ │ ├── action.js │ │ │ ├── compareAuthParams.js │ │ │ ├── configurationNotify.js │ │ │ ├── readDetails.js │ │ │ ├── scanNetworks.js │ │ │ ├── sendIdAndSecret.js │ │ │ └── setNetwork.js │ │ ├── blockCharacteristic.js │ │ ├── index.js │ │ └── service.js │ ├── drivers │ │ ├── accelerometer.js │ │ ├── altitude.js │ │ ├── detection.js │ │ ├── gesture.js │ │ ├── gpio.js │ │ ├── gyroscope.js │ │ ├── humidity.js │ │ ├── ir.js │ │ ├── led.js │ │ ├── magnetometer.js │ │ ├── mic.js │ │ ├── pressure.js │ │ ├── recognition.js │ │ ├── temperature.js │ │ ├── uv.js │ │ ├── voice.js │ │ └── zigbee.js │ ├── gpio.js │ ├── heartbeat.js │ ├── index.js │ ├── maintain.js │ ├── malos.js │ ├── network.js │ ├── port.js │ ├── sensor.js │ ├── service.js │ ├── system.js │ └── wifi.js ├── event │ ├── app.js │ ├── device.js │ ├── index.js │ ├── sensors.js │ ├── server.js │ ├── service.js │ ├── token.js │ └── util.js ├── index.js └── service │ ├── application.js │ ├── auth.js │ ├── component.js │ ├── cypher.js │ ├── firebase.js │ ├── grpc.js │ ├── index.js │ ├── lifecycle.js │ ├── manager.js │ ├── protobuf.js │ ├── stream.js │ ├── timers.js │ ├── token.js │ ├── track.js │ ├── tracking.js │ ├── ves.js │ └── zeromq.js ├── package-lock.json ├── package.json └── test ├── _old ├── api.test.js ├── app-lifecycle.test.js ├── bluetooth.test.js ├── cdma.test.js ├── configuration.test.js ├── dns.test.js ├── sensor.test.js ├── socket.test.js └── wifi.test.js ├── _runner.js ├── api.test.js ├── app.test.js ├── auth.test.js ├── base.test.js ├── bin.test.js ├── component.test.js ├── config.test.js ├── encrypt.test.js ├── event.test.js ├── fixtures ├── test.matrix │ ├── app.js │ ├── config.json │ ├── config.yaml │ ├── index.js │ └── package.json └── test.proto └── stdtestimage.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.log* 3 | node_modules 4 | db 5 | tmp 6 | assets/* 7 | .env 8 | .envz 9 | .e* 10 | apps/*.matrix -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "proto"] 2 | path = proto 3 | url = http://github.com/matrix-io/protocol-buffers.git 4 | [submodule "appsfortesting"] 5 | path = test/fixtures/testapps 6 | url = http://github.com/matrix-io/matrix-srcjs-apps.git 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": false, 7 | "curly": false, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "maxdepth": 8, 14 | "maxparams": 4, 15 | "asi": true, 16 | "noarg": true, 17 | "quotmark": "single", 18 | "regexp": true, 19 | "undef": true, 20 | "unused": true, 21 | "strict": false, 22 | "trailing": true, 23 | "smarttabs": true, 24 | "laxcomma": true, 25 | "mocha": true, 26 | "node": true, 27 | "-W058": true, 28 | "-W003": true, 29 | "globals": { 30 | "async": true, 31 | "angular": true, 32 | "app": true, 33 | "$": true, 34 | "_": true, 35 | "debug": true, 36 | "debugLog": true, 37 | "exec": true, 38 | "Matrix": true, 39 | "error": true, 40 | "log": true, 41 | "warn": true, 42 | "ulog": true, 43 | "matrix": true 44 | } 45 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.7" 4 | sudo: required 5 | env: 6 | - CXX=g++-4.8 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - g++-4.8 13 | - gcc-4.8 14 | - libzmq3-dev 15 | - libudev-dev 16 | 17 | before_install: 18 | - npm install -g node-gyp 19 | 20 | before_script: 21 | - git submodule update --init 22 | - cp -r test/fixtures/test.matrix ../apps/ 23 | 24 | script: 25 | npm run debug-test 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Matrix OS 2 | 3 | Great, thanks for your interest. 4 | 5 | One place we always need help is documentation. 6 | 7 | http://github.com/matrix-io/matrix-documentation 8 | 9 | If you are trying to solve an issue, please see if people are talking about it in our community forums. 10 | 11 | http://community.matrix.one/ 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:5.12 2 | 3 | MAINTAINER Sean Canton 4 | 5 | RUN apt-get update && apt-get install -yq libzmq3-dev \ 6 | && apt-get clean && rm -rf /var/tmp/* 7 | 8 | COPY . /matrix 9 | RUN chmod +x /matrix/docker-entrypoint.sh 10 | 11 | WORKDIR matrix/ 12 | 13 | RUN rm -r node_modules 14 | RUN npm install 15 | 16 | EXPOSE 80 17 | ENTRYPOINT ["/matrix/docker-entrypoint.sh"] 18 | CMD ["node"] 19 | -------------------------------------------------------------------------------- /Dockerfile-apphost: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:6 2 | # FROM node:6.7.0 3 | RUN mkdir /apps 4 | VOLUME /apps 5 | -------------------------------------------------------------------------------- /Dockerfile-apphost-arm: -------------------------------------------------------------------------------- 1 | FROM hypriot/rpi-node:6-slim 2 | RUN mkdir /apps 3 | VOLUME /apps 4 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | ## Unreleased 7 | ### Added 8 | - Reconnect if heartbeat isn't recieved by MXSS 9 | - MATRIX_BEAT_TIME, MATRIX_BEAT_COUNT env vars for above 10 | 11 | ## [0.16.0] 12 | ### Fixed 13 | - Refresh MXSS issues, added async, adjusted delay mechanism 14 | - Old / gone packages 15 | - Bluetooth bugs 16 | 17 | ### Added 18 | - Binary function set for managing device config 19 | - Package lock 20 | 21 | ### Removed 22 | - Bleno dep 23 | - App Send Log message 24 | 25 | ## [0.15.0] 26 | ### Added 27 | - Wakeword (requires external model currently) 28 | ### Fixed 29 | - Refresh tokens now should work so MOS will stay open for hours 30 | 31 | ## [0.14.4] 32 | ### Changed 33 | - Remove upgrade check for all but core. 34 | 35 | ### Fixed 36 | - Constructor problem with recognition 37 | 38 | ## [0.14.3] 39 | ### Added 40 | - Install Instructions 41 | - Bluetooth Auth bypass for local configuration 42 | 43 | ## [0.14.2] 44 | ### Added 45 | - Documentation and error messages 46 | - Upgrade message after ASCII 47 | ### Fixed 48 | - Restart apps after install. Defer to firebase status for application activity 49 | - Services bug 50 | ### Changed 51 | - Check for app storage directory on startup 52 | - Travis Fix. Remove node 6, copy tests into apps 53 | 54 | ## [0.14.1] 55 | ### Fixed 56 | - Can now stop apps without services 57 | 58 | ## [0.14.0] 59 | ### Changed 60 | - Force a single service to be utilized at a time 61 | 62 | ### Fixed 63 | - Deploy and restart apps now work 64 | - Stop apps from launching if config fails validation 65 | 66 | ## [0.13.0] 67 | ### Changed 68 | - Updated recog code for es6's new attitude towards constructors 69 | - Upgrade node to 6 in travis tests and package.json 70 | 71 | ### Remove 72 | - Remove auto upgrade code. It stunk. 73 | 74 | ### Added 75 | - MOS as as service in debian 76 | 77 | ### Fixed 78 | - network typo 79 | 80 | ## [0.12.0] 81 | ### Changed 82 | - Fixed network issues 83 | - Removed some errant debugs 84 | 85 | ## [0.11.2] 86 | ### Fixed 87 | - Applications properly clean up event handlers on exit. 88 | 89 | ## [0.11.1] 90 | ### Removed 91 | - Deprecated applicationConfigs on register 92 | 93 | ### Added 94 | - Emit app-config to mxss on app start 95 | - Save device wifi info to firebase 96 | 97 | ### Changed 98 | - Reenabled service.then 99 | 100 | ## [0.11.0] 101 | ### Changed 102 | - Removed .init in App API for services. Replaced with services 103 | - Updated test apps and documentation 104 | - Depreciated init for sensors, replaced with `sensor`. init still works 105 | 106 | ## [0.10.0] 107 | Queue the magic. 108 | 109 | ### Added 110 | - Recognition support in apps 111 | - Bluetooth Support for Pairing Workflow(!!) 112 | - Save credentials to device database 113 | - Binary script in `bin/` for local reset 114 | 115 | # Changed 116 | - Preventing apps from sending data without dataTypes entry 117 | - Init has been divided into service and sensor 118 | - Login sequence adapted to start with bluetooth pair if no id/secret 119 | - New Pretty Loader 120 | 121 | ### Removed 122 | - matrix.init is deprecated, but will not fail yet 123 | 124 | ## [0.9.4] 125 | ### Fixed 126 | - Can use debugger without app conflicts on port 127 | - Routing for data rich events from dashboard 128 | ### Changed 129 | - Zigbee Port Number 130 | - Handling for Dashboard events 131 | 132 | ## [0.9.0] 133 | ### Added 134 | - Zigbee Support 135 | - Passing image buffers to apps 136 | - Docker virtualization for apps ( uses DOCKER_APPS flag for now ) 137 | - Wifi Support 138 | - Basic Python implementation 139 | - Distinct sensor support for gyroscope, accelerometer, and magnetometer 140 | - Adding images to detection payload 141 | - Message in case of no device id / secret 142 | 143 | ### Changed 144 | - Prevent apps that fail on init from being added to activeApplications 145 | - Stopped restart on config change, will require manual restart now 146 | 147 | ### Fixed 148 | - Removed application folder on uninstall 149 | - Tests work again 150 | 151 | ## [0.7.0] 152 | ### Added 153 | - Enable servo write 154 | - Enable GPIO Read / write 155 | - Add testing and Travis 156 | 157 | ### Removed 158 | - More test applications 159 | - appapi test file 160 | 161 | ## [0.6.4] 162 | 163 | ### Added 164 | - NO_INSTALL flag to skip new installs, cli tests 165 | - 'matrix-ready' IPC event for cli tests 166 | - Error checking for upgrades 167 | 168 | ### Changed 169 | - Prevent multiple kill signals 170 | 171 | ## [0.6.3] 172 | 173 | ### Added 174 | - Mic basics 175 | - Support application updates 176 | 177 | 178 | ## [0.6.2] 179 | ### Added 180 | - Added stubs for GPIO, IR and Microphone 181 | - Basic Microphone data coming in (needs work) 182 | - Restart application if configuration changes 183 | - Clear app status on device exit 184 | - Check for existence before starting app 185 | 186 | ### Changed 187 | - Stop crash if app folder doesn't exist on start 188 | - Stop app on uninstall / install / deploy 189 | 190 | 191 | ## [0.6.1] 192 | 193 | 194 | ### Changed 195 | Set app status to inactive on boot 196 | 197 | ## [0.6.0] 198 | ### Added 199 | - gesture driver 200 | - detection driver 201 | - service for integrations 202 | - component test 203 | 204 | ### Removed 205 | - face driver 206 | 207 | ### Changed 208 | - face goes through detection driver 209 | - Start versioning history 210 | - Deployment improvements 211 | - Firebase improvements 212 | 213 | ## [0.5.12] 214 | Fixed and sped up heart beat 215 | Enabled `SUN_MODE` to use white leds from `matrix.led`. Will be useful when the lights are covered. 216 | Enable gestures `matrix.init('thumb-up')` 217 | 218 | ## [0.5.11] 219 | Improve event filter handling 220 | Add eventfilter to upgrade 221 | Exit after dependency upgrade 222 | 223 | ## [0.5.10] 224 | Rearrange starting init to prioritize the upgrade check after API 225 | 226 | 227 | ## [0.5.9] 228 | More deployment fixes 229 | Upgrade version skips for testing 230 | 231 | ## [0.5.6] 232 | Fix deploy issues 233 | Upgrade matrix and Dependencies 234 | 235 | ## [0.5.4] 236 | ### Added 237 | * Support Upgrade Checking 238 | * Support Application install on Init 239 | * Start History 240 | -------------------------------------------------------------------------------- /INSTALLATION.md: -------------------------------------------------------------------------------- 1 | # MATRIX OS INSTALLATION 2 | 3 | ## Raspberry Pi 4 | 5 | Please follow the instructions at https://matrix-io.github.io/matrix-documentation/ 6 | 7 | ## Ubuntu 16.04 LTS 8 | 9 | ``` 10 | #libudev is the only ubu specific requirement 11 | apt install -y git npm libudev-dev g++ libzmq3-dev cmake 12 | npm install -g n 13 | n 6.7.0 14 | git clone http://github.com/matrix-io/matrix-os 15 | cd matrix-os 16 | git submodule update --init 17 | npm install 18 | npm rebuild 19 | 20 | # start matrix os 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > MATRIX OS (MOS) has been deprecated. We are no longer providing support for it and it may cease to function at any time. Please refer to our new library [MATRIX Lite](https://github.com/matrix-io/matrix-lite-js) to easily program your MATRIX Device with just one line of code. 2 | 3 | # MATRIX OS 4 | 5 | [![Build Status](https://travis-ci.org/matrix-io/matrix-os.svg?branch=master)](https://travis-ci.org/matrix-io/matrix-os) 6 | 7 | ![matrix os launches](http://i.makeagif.com/media/5-10-2016/TDceSN.gif) 8 | 9 | # Overview 10 | MATRIX Open Source is a platform for running applications on the MATRIX Creator. 11 | 12 | ###### Bugs 13 | https://github.com/matrix-io/matrix-os/issues 14 | 15 | ###### Questions 16 | http://community.matrix.one 17 | 18 | ###### Documentation & Installation Instructions 19 | https://creator.matrix.one/#!/develop/start 20 | 21 | ## MATRIX Applications 22 | MATRIX applications are built in JavaScript to logically connect sensor and computer vision data streams with outputs, such as LED lights, sending voltages, integrations and data for dashboards and analysis. 23 | 24 | An example application would recognize your face and unlock your front door. A security gesture could be added for a basic level of access control. A 2 factor QR Code provided over the phone or fingerprint reader could be integrated more security. 25 | 26 | Using [configuration files](https://matrix-io.github.io/matrix-documentation/Configuration/examples/), an application end-user could customize an application for their specific use case. Adding API keys or custom phrases. 27 | 28 | #### Why JavaScript? 29 | Lots of people already know JavaScript, and there is a rich module ecosystem. Part of our goal is to put IoT into the hands of more people. 30 | 31 | #### What about MY language? 32 | Don't worry. We like other languages too, all it will need is a supporting SDK. Coming soon. 33 | 34 | #### Applications? 35 | MATRIX Applications can be used in the traditional manner of an app, single user, single device. We believe swarming meshes of interconnected data streams will be a more common pattern. Most devices will use many applications at once, and many will operate entirely without direct manipulation by an end user. We have built [inter-app messaging protocols](https://matrix-io.github.io/matrix-documentation/API/cross-talk) to help this. 36 | 37 | #### Open Source 38 | We built MATRIX on top of open source software and we are releasing it for everyone to see under the hood and make contributions😎. We believe in open access to information, especially for the IoT space, where security and privacy are of the utmost importance. 39 | 40 | ## Great, how do I use the thing over the internet? 41 | Right now, you need a MATRIX Creator, available at [creator.matrix.one](). 42 | 43 | Then follow the installation instructions above. 44 | -------------------------------------------------------------------------------- /apps/index.js: -------------------------------------------------------------------------------- 1 | var appName = require('path').basename(__dirname).split('.')[0]; 2 | 3 | matrix = require('../matrix.js'); 4 | 5 | matrix.startApp(appName); 6 | 7 | require('./app.js'); -------------------------------------------------------------------------------- /apps/lib/audio.js: -------------------------------------------------------------------------------- 1 | var lib = { 2 | // do something here 3 | }; 4 | 5 | module.exports = lib; -------------------------------------------------------------------------------- /apps/lib/cv.js: -------------------------------------------------------------------------------- 1 | var opencv; 2 | 3 | var lib = { 4 | config: function(camera, appOptions) { 5 | var options = { 6 | 'height': 640, 7 | 'width': 480, 8 | 'minSize': 20, //50, 9 | 'maxSize': 400, //-1, 10 | 'drawObjects': false, 11 | 'processAge': false, //should be minimized to "age" 12 | 'processGender': true, // should be "gender" 13 | 'processEmotion': true, // should be "emotion" 14 | 'processDwell':true, // should be "dwell" 15 | 'show': false, // "show should = preview" 16 | 'save': false, // should be "store", or "save" 17 | 'debug': false, 18 | 'processUniques': true, //should be just "uniques" 19 | 'frameRate': 5, //number of frames per second 20 | 'device': 0, //this seems like duplicate code, could be normalized 21 | 'bufferImage': null, 22 | 'detection': { // should just be part of configuration (e.g. face), see above 23 | type: "humans", 24 | detector: 3 25 | }, 26 | "directory": __dirname + "/../../opencv-node-sdk/admobilize-detection-manager/admobilize-detection/data" 27 | //should have a default path that works, seems to never work with default path 28 | }; 29 | if(options === undefined || options === null) { 30 | // do nothing 31 | } else { 32 | options.processAge = appOptions.age || false; 33 | options.processGender = appOptions.gender || false; 34 | options.processEmotion = appOptions.emotion || false; 35 | options.processDwell = appOptions.dwell || false; 36 | if(appOptions.type === 'face') { 37 | options.detection = { type: 'humans', detector: 3 }; 38 | } 39 | } 40 | 41 | //set the camera 42 | if(camera === undefined || camera === null) { 43 | options.camera = 'localCamera'; 44 | } else { 45 | options.camera = camera; 46 | } 47 | 48 | //update options 49 | return options; 50 | }, 51 | 52 | init: function(camera, appOptions) { 53 | console.log('open cv init'); 54 | 55 | try { 56 | opencv = require('opencv-node-sdk'); 57 | } catch(e) { 58 | console.error('MATRIX CV could not initialize. Please make sure the MATRIX OS has been updated -- see error:', e); 59 | } 60 | 61 | var options = lib.config(camera, appOptions); 62 | var cv = new opencv({ "cameraId" : options.camera }); 63 | cv.setConfiguration(options,function(){ 64 | log('start camera') 65 | 66 | cv.startCamera(0, function(err){ 67 | if(err) { console.error(err); } else { 68 | log('start detect'); 69 | cv.startContinuousDetection(); 70 | } 71 | }); 72 | }); 73 | return cv; 74 | } 75 | } 76 | 77 | module.exports = lib; 78 | -------------------------------------------------------------------------------- /apps/lib/gpio.js: -------------------------------------------------------------------------------- 1 | 2 | // pin # = index 3 | var pinHandlers = []; //Callback for each pin 4 | var pinWriteHandlers = []; //Callback for each pin write 5 | var readPins = []; //Pins that are being notified 6 | var writePins = []; 7 | 8 | var listenerStarted = false; 9 | 10 | function setListener() { 11 | if (!listenerStarted) { //Only start listening once 12 | listenerStarted = true; 13 | process.on('message', function (message) { 14 | if (message.eventType == 'gpio-emit') { 15 | 16 | if (message.payload.type == 'read') { 17 | var resultPins = message.payload.values; 18 | readPins.forEach(function (element) { 19 | var value = 0; 20 | if (resultPins.hasOwnProperty(element)) { //Only assign a value to that pin if a value is specified 21 | value = resultPins[element]; 22 | } 23 | pinHandlers[element](value); 24 | }); 25 | 26 | } else if (message.payload.type == 'write') { 27 | console.log('WRITE LISTENER!:', message); 28 | /*var resultPins = message.payload.values; 29 | readPins.forEach(function (element) { 30 | var value = 0; 31 | if (resultPins.hasOwnProperty(element)) { //Only assign a value to that pin if a value is specified 32 | value = resultPins[element]; 33 | } 34 | pinHandlers[element](value); 35 | }); 36 | */ 37 | //pinWriteHandlers[element]; 38 | } 39 | } 40 | }) 41 | }; 42 | }; 43 | 44 | module.exports = { 45 | read: function (pin, cb) { 46 | if (!_.isFunction(cb)) { 47 | throw new Error('matrix.gpio.read requires a callback'); 48 | } 49 | 50 | readPins.push(pin); 51 | readPins = _.uniq(readPins); 52 | pinHandlers[pin] = cb; 53 | 54 | process.send({ 55 | type: 'gpio-open', 56 | pin: pin 57 | }); 58 | 59 | setListener(); 60 | }, 61 | 62 | write: function (pin, value, cb) { 63 | console.log("PIN WRITE: ", pin, value); 64 | pinWriteHandlers[pin] = cb; 65 | process.send({ 66 | type: 'gpio-write', 67 | pin: pin, 68 | value: value 69 | }); 70 | //process.send('gpio-write', { pin: pin, value: value }); 71 | setListener(); 72 | }, 73 | servo: function(pin, angle){ 74 | process.send({ 75 | type: 'gpio-write', 76 | servo: true, 77 | pin: pin, 78 | value: angle 79 | }); 80 | }, 81 | open: function () { 82 | 83 | }, 84 | close: function () { } 85 | } 86 | -------------------------------------------------------------------------------- /apps/lib/index.js: -------------------------------------------------------------------------------- 1 | /* Everything having to do with the computer */ 2 | 3 | var f = {}; 4 | var files = require('fs').readdirSync(__dirname); 5 | 6 | //remove self 7 | files.splice(files.indexOf(require('path').basename(__filename)), 1); 8 | 9 | files.forEach(function(file) { 10 | f[file.slice(0,-3)] = require('./' + file); 11 | }); 12 | 13 | module.exports = f; -------------------------------------------------------------------------------- /apps/lib/init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Init will be depreciated shortly. It is clumsy. 3 | */ 4 | 5 | 6 | var EventFilter = require('matrix-eventfilter').EventFilter; 7 | var applyFilter = require('matrix-eventfilter').apply; 8 | 9 | module.exports = function(name, options) { 10 | var self = this; 11 | 12 | if (_.isUndefined(options)) { 13 | options = {}; 14 | } 15 | 16 | //find if this init is for a detection 17 | if (!_.isNull(name.match(/(face|demographics|vehicle|palm|pinch|fist|thumb-up)/))) { 18 | 19 | console.log('Initialize Service:'.blue, name); 20 | 21 | // find the service definition 22 | var service = _.mapValues(self.config.services, function(v) { 23 | if (v.engine == name || v.type == name) { 24 | return { type: v.type, engine: v.engine } 25 | } 26 | }); 27 | 28 | process.send({ type: 'service-init', name: name, engine: service.engine, options: options }); 29 | return { 30 | then: function(cb) { 31 | console.log('setup service app listener') 32 | process.on('message', function(data) { 33 | if (data.eventType === 'service-emit') { 34 | cb(data.payload); 35 | } 36 | }) 37 | } 38 | } 39 | } else { 40 | 41 | // this init is for a sensor name 42 | return initSensor(name, options); 43 | } 44 | 45 | } 46 | 47 | // name can be array or string 48 | function initSensor(name, options) { 49 | console.log('Initialize Sensor:'.blue, name); 50 | 51 | var filter, sensorOptions; 52 | 53 | var sensors = []; 54 | // kick off sensor readers 55 | if (_.isArray(name)) { 56 | sensors.concat(name) 57 | } else { 58 | sensors.push(name); 59 | } 60 | 61 | var returnObj = function() { 62 | console.log(this, ' is a multi typed Matrix data object, please specify a child data souce using key-value notation ( obj.sensor or obj[\'sensor\'])') 63 | return {}; 64 | }; 65 | 66 | // handle many sensors 67 | _.forEach(sensors, function(s) { 68 | 69 | // break down options by key if necessary 70 | // { gyro: {}, face: {} .... } 71 | if (options.hasOwnProperty(s)) { 72 | sensorOptions = options.s; 73 | } else { 74 | sensorOptions = options; 75 | } 76 | 77 | // kick off sensor init 78 | process.send({ 79 | type: 'sensor-init', 80 | name: s, 81 | options: sensorOptions 82 | }); 83 | }); 84 | 85 | // # matrix.init(sensor || CV) 86 | 87 | // prepare local chaining filter 88 | filter = new EventFilter(name); 89 | 90 | // then is a listener for messages from sensors 91 | // FIXME: Issue with app only storing one process at a time 92 | // console.log('sensor err >> looking into fix'); 93 | var then = function(cb) { 94 | 95 | var result; 96 | console.log('setup app sensor listener'); 97 | // recieves from events/sensors 98 | process.on('message', function(m) { 99 | 100 | if (_.isUndefined(m)) { 101 | return console.log('[M]->[m] Empty Message From matrix.init'.red, name, options); 102 | } 103 | 104 | if (m.eventType === 'sensor-emit') { 105 | console.log('[M]->[m](%s):', name, m); 106 | // TODO: filter multiple sensors 107 | if (sensors.indexOf(m.sensor) > -1) { 108 | 109 | //TODO: when sensors fail to deliver, fail here gracefully 110 | m = _.omit(m, 'eventType'); 111 | m.payload.type = m.sensor; 112 | 113 | // console.log('sensor:', m.sensor, '-> app'.blue, name, m); 114 | // if there is no filter, don't apply 115 | if (filter.filters.length > 0) { 116 | result = applyFilter(filter, m.payload); 117 | } else { 118 | result = m.payload; 119 | } 120 | 121 | if (result !== false && !_.isUndefined(result)) { 122 | // LORE: switched from err first to promise style 123 | // provides .then(function(data){}) 124 | cb(result); 125 | } 126 | } 127 | // console.log('applying filter:', filter.json()); 128 | } 129 | 130 | }); 131 | } 132 | 133 | 134 | _.extend(filter, { 135 | then: then 136 | }); 137 | 138 | if (_.isArray(name)) { 139 | 140 | //overload function with error message above throwing if no key specified 141 | returnObj[s] = filter; 142 | } else { 143 | // singles 144 | returnObj = filter; 145 | } 146 | 147 | 148 | return returnObj; 149 | } -------------------------------------------------------------------------------- /apps/lib/send.js: -------------------------------------------------------------------------------- 1 | module.exports = function (message) { 2 | //console.log('[M](' + this.appName + ') send ->', message); //TODO debug this? 3 | if (_.isNull(message) || _.isUndefined(message)) { 4 | return error('null message from matrix.send') 5 | } 6 | 7 | //fast track this outside 8 | if (_.isArray(message)) { 9 | // add routing information 10 | _.each(message, (m) => { 11 | m.type = this.dataType || matrix.config.name; 12 | m.appName = matrix.config.name; 13 | m.appVersion = 0; 14 | }) 15 | return process.send({ 16 | type: 'app-emit', 17 | payload: message 18 | }) 19 | } 20 | // if (!message.hasOwnProperty('data')){ 21 | // message = { data: message }; 22 | // } 23 | 24 | var type, msgObj = {}; 25 | if (this.hasOwnProperty('dataType')) { 26 | type = this.dataType; 27 | 28 | //reset variable for next send 29 | delete this.dataType; 30 | } else { 31 | type = this.appName; 32 | // return error('No TYPE specified in matrix.send. Use matrix.type().send()') 33 | } 34 | 35 | // check config dataTypes for type (array or object lookup) 36 | var dataTypes = this.config.dataTypes; 37 | 38 | if (!_.isPlainObject(dataTypes) || _.isEmpty(dataTypes)) { 39 | return error('matrix.send used without dataTypes defined in config') 40 | } 41 | 42 | /* TYPES WHICH ARE UNDEFINED IN CONFIG SHALL NOT PASS . 43 | Only pass config.dataType defined types \^/| 44 | TODO: depreciate when dynamic schema works again _/ \|_*/ 45 | 46 | if (!dataTypes.hasOwnProperty(type) && type !== this.appName) { 47 | console.log(type, 'not found in config datatypes'); 48 | } else if (type === this.appName) { 49 | // no .type() used 50 | msgObj = message; 51 | } else { 52 | 53 | //regex containing object 54 | var re = require('matrix-app-config-helper').regex; 55 | if (_.isPlainObject(dataTypes[type])) { 56 | // nested datatype structure 57 | _.each(dataTypes[type], function (f, key) { 58 | console.log(key + ': ' + message[key] + ' (' + f + ' > ' + typeof message[key] + ')'); //key: message[key] (Type > typeof) 59 | if (message.hasOwnProperty(key) && !_.isUndefined(message[key])) { 60 | // check that the data is formatted correctly 61 | if ( 62 | (f.match(re.string) && _.isString(message[key])) || 63 | (f.match(re.integer) && _.isInteger(message[key])) || 64 | (f.match(re.float) && ( 65 | // accept nulls and zeros 66 | _.isNull(message[key]) || message[key] === 0.0 || 67 | // and real floats 68 | parseFloat(message[key]) === message[key] 69 | )) || 70 | (f.match(re.boolean) && _.isBoolean(message[key])) 71 | ) { } else { 72 | error(key, 'not formatted correctly\n', type, message) 73 | } 74 | if (f.match(re.float) && (_.isNull(message[key]) || message[key] === 0 || message[key] === '0')) { 75 | // force zero floats to null, bc they will cast as int on db write 76 | message[key] = null; 77 | } 78 | } else { 79 | // stops apps from sending keys not present in schema 80 | error(key, 'is not present in config.dataTypes') 81 | } 82 | }) 83 | msgObj = message; 84 | } else { 85 | 86 | // not defined yet, TODO: enable this flow later 87 | var format = dataTypes[type]; 88 | if ((format === 'string' && _.isString(message)) || 89 | (format === 'float' && (parseFloat(message) === message)) || 90 | (format === 'int' && _.isInteger(message))) { 91 | msgObj.value = message; 92 | } else if (format === 'object' && _.isPlainObject(message)) { 93 | msgObj = message; 94 | } else { 95 | console.log('Type', type, 'data not correctly formatted.') 96 | console.log('Expecting:', format); 97 | console.log('Recieved:', message); 98 | } 99 | } 100 | } 101 | 102 | msgObj.time = Date.now(); 103 | msgObj.type = type; 104 | process.send({ 105 | type: 'app-emit', 106 | payload: msgObj 107 | }); 108 | 109 | } -------------------------------------------------------------------------------- /apps/lib/sensor.js: -------------------------------------------------------------------------------- 1 | var EventFilter = require('matrix-eventfilter').EventFilter; 2 | var applyFilter = require('matrix-eventfilter').apply; 3 | 4 | // name can be array or string 5 | function initSensor(name, options) { 6 | console.log('Initialize Sensor:'.blue, name); 7 | 8 | if (_.isUndefined(options)) { 9 | options = {}; 10 | } 11 | 12 | var filter, sensorOptions; 13 | 14 | var sensors = []; 15 | // kick off sensor readers 16 | if (_.isArray(name)) { 17 | sensors.concat(name) 18 | } else { 19 | sensors.push(name); 20 | } 21 | 22 | var returnObj = function() { 23 | console.log(this, ' is a multi typed Matrix data object, please specify a child data souce using key-value notation ( obj.sensor or obj[\'sensor\'])') 24 | return {}; 25 | }; 26 | 27 | // handle many sensors 28 | _.forEach(sensors, function(s) { 29 | 30 | // break down options by key if necessary 31 | // { gyro: {}, face: {} .... } 32 | if (options.hasOwnProperty(s)) { 33 | sensorOptions = options.s; 34 | } else { 35 | sensorOptions = options; 36 | } 37 | 38 | // kick off sensor init 39 | process.send({ 40 | type: 'sensor-init', 41 | name: s, 42 | options: sensorOptions 43 | }); 44 | }); 45 | 46 | // # matrix.init(sensor || CV) 47 | 48 | // prepare local chaining filter 49 | filter = new EventFilter(name); 50 | 51 | // then is a listener for messages from sensors 52 | // FIXME: Issue with app only storing one process at a time 53 | // console.log('sensor err >> looking into fix'); 54 | var then = function(cb) { 55 | console.log('setup sensor listener') 56 | var result; 57 | // recieves from events/sensors 58 | process.on('message', function(m) { 59 | 60 | if (_.isUndefined(m)) { 61 | return console.log('[M]->[m] Empty Message From matrix.init'.red, name, options); 62 | } 63 | 64 | if (m.eventType === 'sensor-emit') { 65 | console.log('[M]->[m](%s):', name, m); 66 | // TODO: filter multiple sensors 67 | if (sensors.indexOf(m.sensor) > -1) { 68 | 69 | //TODO: when sensors fail to deliver, fail here gracefully 70 | m = _.omit(m, 'eventType'); 71 | m.payload.type = m.sensor; 72 | 73 | // console.log('sensor:', m.sensor, '-> app'.blue, name, m); 74 | // if there is no filter, don't apply 75 | if (filter.filters.length > 0) { 76 | result = applyFilter(filter, m.payload); 77 | } else { 78 | result = m.payload; 79 | } 80 | 81 | if (result !== false && !_.isUndefined(result)) { 82 | // LORE: switched from err first to promise style 83 | // provides .then(function(data){}) 84 | cb(result); 85 | } 86 | } 87 | // console.log('applying filter:', filter.json()); 88 | } 89 | 90 | }); 91 | } 92 | 93 | 94 | _.extend(filter, { 95 | then: then 96 | }); 97 | 98 | if (_.isArray(name)) { 99 | 100 | //overload function with error message above throwing if no key specified 101 | returnObj[s] = filter; 102 | } else { 103 | // singles 104 | returnObj = filter; 105 | } 106 | 107 | 108 | return returnObj; 109 | } 110 | 111 | module.exports = initSensor; -------------------------------------------------------------------------------- /apps/lib/setting.js: -------------------------------------------------------------------------------- 1 | var set = function (settings) { 2 | console.log(settings, '=>', matrix.config.settings); 3 | 4 | Object.keys(settings).forEach(k => { 5 | if (!matrix.config.settings.hasOwnProperty(k)) { 6 | return console.error('Cannot add settings which are not registered in the config.yaml'); 7 | } 8 | if (_.isFunction(settings[k]) || _.isPlainObject(settings[k])) { 9 | return console.error('Settings can only be strings or integers.'); 10 | } 11 | }); 12 | 13 | // converge 14 | _.merge(matrix.config.settings, settings); 15 | 16 | process.send({ 17 | type: 'update-setting', 18 | settings: settings 19 | }); 20 | 21 | }; 22 | 23 | module.exports = set; -------------------------------------------------------------------------------- /apps/lib/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a new Store Manager 3 | * @param {nedb.Datastore} db - nedb instance of the database. 4 | */ 5 | function Store(db) { 6 | this.db = db; 7 | } 8 | 9 | /** 10 | * Query an entry on database 11 | * @param {String} key The name of the entry that you want to get. 12 | * @param {Function} _cb A callback function that will be called after the query execution. 13 | */ 14 | Store.prototype.get = function(key, _cb) { 15 | const q = {}; 16 | q[key] = { $exists: true }; 17 | this.db.findOne(q, function(err, res) { 18 | if (err) { return _cb(err); } 19 | if (res === null) { return _cb(null, res); } 20 | 21 | return _cb(null, res[key]); 22 | }); 23 | } 24 | 25 | /** 26 | * Insert an entry on database 27 | * @param {String} key The name of the entry that you want to get. 28 | * @param {String} value The value of the entry that you want to get. 29 | * @param {Function} _cb A callback function that will be called after the query execution. 30 | */ 31 | Store.prototype.set = function(key, value, _cb) { 32 | const obj = {}; 33 | obj[key] = value; 34 | 35 | const q = {}; 36 | q[key] = { $exists: true }; 37 | 38 | this.db.update(q, obj, {multi: true, upsert: true}, _cb); 39 | } 40 | 41 | /** 42 | * Remove an entry on database 43 | * @param {String} key The name of the entry that you want to delete. 44 | * @param {Function} _cb A callback function that will be called after the query execution. 45 | */ 46 | Store.prototype.delete = function(key, _cb) { 47 | const q = {}; 48 | q[key] = { $exists: true }; 49 | this.db.remove(q, _cb); 50 | } 51 | 52 | module.exports = Store; -------------------------------------------------------------------------------- /apps/lib/util.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | /** 5 | * Is this application configured for this event? 6 | * @param {string} ev - event name to test for 7 | * @return {Boolean} - is in the config 8 | */ 9 | hasEvent: (ev) => { 10 | return ( matrix.config.hasOwnProperty('events') && 11 | matrix.config.events.indexOf(ev) > -1 ) 12 | } 13 | } -------------------------------------------------------------------------------- /apps/lib/zigbee.js: -------------------------------------------------------------------------------- 1 | // provides matrix.zigbee 2 | // 3 | 4 | function hueLookup(name) { 5 | if (_.isInteger(name)) { 6 | // map 360 to int32 0-360 -> 0-255 7 | return Math.round(name * (255 / 360)); 8 | } 9 | 10 | var tc = require('tinycolor2'); 11 | 12 | var hue = tc(name).toHsv().h; 13 | var rgb = tc(name).toRgb(); 14 | if (rgb.r === rgb.b && rgb.g === rgb.b) { 15 | console.log('White or grey is not supported and will default to yellow'); 16 | hue = tc('yellow').toHsv().h; 17 | } 18 | return Math.round(hue * (255 / 360)); 19 | } 20 | 21 | // 0-100 -> 0-255 22 | function levelConvert(level) { 23 | return Math.round(level * 2.55); 24 | } 25 | 26 | module.exports = { 27 | discover: function() { 28 | process.send({ type: 'zigbee-net-cmd', cmd: 'discover' }); 29 | }, 30 | 31 | reset: function() { 32 | process.send({ type: 'zigbee-net-cmd', cmd: 'reset' }); 33 | }, 34 | 35 | light: function() { 36 | return { 37 | color: function(hue, time) { 38 | process.send({ type: 'zigbee-light-cmd', cmd: 'color-set', payload: { hue: hueLookup(hue), time: time } }); 39 | }, 40 | colorSpin: function(hue, time, direction) { 41 | process.send({ type: 'zigbee-light-cmd', cmd: 'color-spin', payload: { hue: hueLookup(hue), time: time, direction: direction } }); 42 | }, 43 | colorMove: function(hue, time, direction) { 44 | process.send({ type: 'zigbee-light-cmd', cmd: 'color-move', payload: { hue: hueLookup(hue), time: time, direction: direction } }); 45 | }, 46 | off: function() { 47 | process.send({ type: 'zigbee-light-cmd', cmd: 'off' }); 48 | }, 49 | on: function() { 50 | process.send({ type: 'zigbee-light-cmd', cmd: 'on' }); 51 | }, 52 | toggle: function() { 53 | process.send({ type: 'zigbee-light-cmd', cmd: 'toggle' }) 54 | }, 55 | // saturation: function (level, time, direction) { 56 | // process.send({ type: 'zigbee-light-cmd', cmd: 'saturation-move', payload: { saturation: level, time: time, direction: 0 } }); 57 | // }, 58 | fadeIn: function(time) { 59 | process.send({ type: 'zigbee-light-cmd', cmd: 'fade-on', payload: { time: time, direction: 0 } }); 60 | }, 61 | fadeOut: function(time) { 62 | process.send({ type: 'zigbee-light-cmd', cmd: 'fade-off', payload: { time: time, direction: 1 } }); 63 | }, 64 | level: function(level, time, direction) { 65 | process.send({ type: 'zigbee-light-cmd', cmd: 'level-move', payload: { level: level, time: time, direction: 0 } }); 66 | }, 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /apps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matrix-test-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "matrix-app-config-helper": "https://github.com/matrix-io/matrix-app-config-helper/tarball/master", 13 | "matrix-eventfilter": "https://github.com/matrix-io/matrix-eventfilter/tarball/master", 14 | "matrix-firebase": "https://github.com/matrix-io/matrix-firebase/tarball/master", 15 | "matrix-node-sdk": "https://github.com/matrix-io/matrix-node-sdk/tarball/master" 16 | } 17 | } -------------------------------------------------------------------------------- /apps/storage/matrix_online.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrix-os/004e8e4d0a9e70983b66bfb482c18fe52612384e/apps/storage/matrix_online.wav -------------------------------------------------------------------------------- /apps/storage/shia-labeouf-orig.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrix-os/004e8e4d0a9e70983b66bfb482c18fe52612384e/apps/storage/shia-labeouf-orig.mp3 -------------------------------------------------------------------------------- /apps/storage/shia-labeouf.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrix-os/004e8e4d0a9e70983b66bfb482c18fe52612384e/apps/storage/shia-labeouf.mp3 -------------------------------------------------------------------------------- /bin/reset.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var envs = ['dev', 'production', 'rc']; 3 | 4 | Matrix = {}; //Need to fake the global 5 | _ = require('lodash'); 6 | var DataStore = require('nedb'); 7 | var config = require(__dirname + '/../config'); 8 | var deviceDB = new DataStore({ 9 | filename: config.path.db.device, 10 | autoload: true 11 | }); 12 | 13 | var env; 14 | var query = { 15 | id: { $exists: true }, 16 | secret: { $exists: true } 17 | } 18 | 19 | //If an env is provided, use it 20 | if (process.argv.length > 2 && _.includes(envs, process.argv[2])) { 21 | env = process.argv[2]; 22 | query.env = env; //Add specific environment if found 23 | } 24 | 25 | deviceDB.find(query, function (err, result) { 26 | if (err) { 27 | console.error('Err:', err.red); 28 | process.exit(1); 29 | } 30 | 31 | if (_.isUndefined(result)) { 32 | console.log('Nothing to reset'); 33 | } else { 34 | result.forEach(function (device) { 35 | deviceDB.remove({ _id: device._id }, function (err, result) { 36 | if (!err) { 37 | if (_.isUndefined(env)) { 38 | console.log('Successful device data reset!'); 39 | } else { 40 | console.log('Successful device data reset for environement', env + '!'); 41 | } 42 | } 43 | }); 44 | }); 45 | } 46 | }); -------------------------------------------------------------------------------- /bin/set.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var envs = ['dev', 'production', 'rc']; 3 | 4 | Matrix = {}; //Need to fake the global 5 | _ = require('lodash'); 6 | var DataStore = require('nedb'); 7 | var async = require('async'); 8 | var config = require(__dirname + '/../config'); 9 | var deviceDB = new DataStore({ 10 | filename: config.path.db.device, 11 | autoload: true 12 | }); 13 | 14 | var env, id, secret; 15 | 16 | //If an env is provided, use it 17 | if (process.argv.length > 4 && _.includes(envs, process.argv[2])) { 18 | 19 | env = process.argv[2]; 20 | id = process.argv[3]; 21 | secret = process.argv[4]; 22 | 23 | var insertQuery = { 24 | id: id, 25 | secret: secret, 26 | env: env 27 | } 28 | 29 | var findQuery = { 30 | id: { $exists: true }, 31 | secret: { $exists: true }, 32 | env: env 33 | } 34 | 35 | async.series([ 36 | function (next) { 37 | deviceDB.find(findQuery, function (err, result) { 38 | if (err) { 39 | console.error('Err:', err.red); 40 | next(err); 41 | } 42 | 43 | if (_.isUndefined(result) || result.length < 1) { 44 | //console.log('Nothing to delete'); 45 | next(); 46 | } else { 47 | //console.log('Results found!', result); 48 | result.forEach(function (device) { 49 | deviceDB.remove({ _id: device._id }, function (err) { 50 | if (err) console.log('Unable to delete data for env', env + '!'); 51 | next(err); 52 | }); 53 | }); 54 | } 55 | }); 56 | }, 57 | function (next) { 58 | console.log('Inserting...'); 59 | deviceDB.insert(insertQuery, function (err) { 60 | if (err) console.log('Unable to update device data'); 61 | else console.log('Device configuration set!'); 62 | next(err); 63 | }); 64 | } 65 | 66 | ], function (err) { 67 | if (err) console.log(err.message); 68 | process.exit(0); 69 | }); 70 | } else { 71 | console.log('Parameters required: node set.js '); 72 | console.log(' Allowed envs: dev, rc, production'); 73 | process.exit(1); 74 | } -------------------------------------------------------------------------------- /config/env/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'dev', 3 | 4 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 5 | url: { 6 | api: 'https://dev-api.admobilize.com', 7 | streaming: 'http://dev-mxss.admobilize.com' 8 | }, 9 | debug: true 10 | } -------------------------------------------------------------------------------- /config/env/device.js: -------------------------------------------------------------------------------- 1 | require('./index.js'); 2 | 3 | module.exports = { 4 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 5 | name: 'device', 6 | url: { 7 | api: 'https://dev-api.admobilize.com', 8 | streaming: 'http://home:3000' 9 | }, 10 | debug: true 11 | } -------------------------------------------------------------------------------- /config/env/docker-local.js: -------------------------------------------------------------------------------- 1 | require('./index.js'); 2 | 3 | module.exports = { 4 | name: 'docker', 5 | deviceId: process.env['MATRIX_DEVICE_ID'], 6 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 7 | url: { 8 | api: process.env['MATRIX_API_SERVER'], 9 | streaming: mxss:80 10 | }, 11 | debug: process.env['DEBUG'] 12 | } 13 | -------------------------------------------------------------------------------- /config/env/docker.js: -------------------------------------------------------------------------------- 1 | require('./index.js'); 2 | 3 | module.exports = { 4 | name: 'docker', 5 | deviceId: process.env['MATRIX_DEVICE_ID'], 6 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 7 | url: { 8 | api: process.env['MATRIX_API_SERVER'], 9 | streaming: process.env['MATRIX_STREAMING_SERVER'] 10 | }, 11 | debug: process.env['DEBUG'] 12 | } 13 | -------------------------------------------------------------------------------- /config/env/hardcode.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'hardcode', 3 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 4 | url: { 5 | api: 'https://dev-api.admobilize.com', 6 | streaming: 'http://104.197.139.81' 7 | }, 8 | debug: true 9 | } -------------------------------------------------------------------------------- /config/env/home.js: -------------------------------------------------------------------------------- 1 | require('./index.js'); 2 | 3 | module.exports = { 4 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 5 | name: 'local', 6 | url: { 7 | api: 'https://dev-api.admobilize.com', 8 | streaming: 'http://192.168.0.3:3000' 9 | }, 10 | debug: true 11 | } -------------------------------------------------------------------------------- /config/env/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrix-os/004e8e4d0a9e70983b66bfb482c18fe52612384e/config/env/index.js -------------------------------------------------------------------------------- /config/env/local.js: -------------------------------------------------------------------------------- 1 | require('./index.js'); 2 | 3 | module.exports = { 4 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 5 | name: 'local', 6 | url: { 7 | api: 'https://dev-api.admobilize.com', 8 | streaming: 'http://localhost:3000' 9 | }, 10 | debug: true 11 | } -------------------------------------------------------------------------------- /config/env/production.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'production', 3 | url: { 4 | api: 'https://api.admobilize.com', 5 | streaming: 'https://mxss.admobilize.com' 6 | }, 7 | debug: false, 8 | 9 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 10 | } -------------------------------------------------------------------------------- /config/env/rc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'rc', 3 | url: { 4 | api: 'https://api.admobilize.com', 5 | streaming: 'https://mxss.admobilize.com' 6 | }, 7 | debug: false, 8 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'] 9 | } -------------------------------------------------------------------------------- /config/env/stage.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'stage', 3 | url: { 4 | api: 'http://stage-demo.admobilize.com', 5 | streaming: 'http://stage-mxss.admobilize.com:80' 6 | }, 7 | debug: false, 8 | 9 | deviceSecret: process.env['MATRIX_DEVICE_SECRET'], 10 | } 11 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | var f = { 2 | fakeApp: process.env['START_APP'] 3 | }; 4 | var fs = require('fs'); 5 | 6 | var files = fs.readdirSync(__dirname); 7 | 8 | //remove self 9 | files.splice(files.indexOf(require('path').basename(__filename)), 1); 10 | // files.splice(files.indexOf('store.db'), 1); 11 | // files.splice(files.indexOf('api-store.db'), 1); 12 | 13 | files.forEach(function (file) { 14 | // require localized to this file 15 | if (fs.statSync(__dirname + '/' + file).isFile()) { 16 | f[file.slice(0, -3)] = require('./' + file); 17 | } 18 | }); 19 | 20 | 21 | var configs = _.pick(process.env, [ 22 | // 'ADMATRIX_API_SERVER', 23 | // 'ADMATRIX_STREAMING_SERVER', 24 | // 'ADMATRIX_CLIENT_ID', 25 | // 'ADMATRIX_CLIENT_SECRET', 26 | // 'ADMATRIX_DEVICE_ID', 27 | // 'ADMATRIX_DEVICE_NAME', 28 | // 'ADMATRIX_USER', 29 | // 'ADMATRIX_PASSWORD', 30 | // // support matrix too, prioritize these 31 | // 'MATRIX_API_SERVER', 32 | // 'MATRIX_STREAMING_SERVER', 33 | 'MATRIX_CLIENT_ID', 34 | 'MATRIX_DEVICE_SECRET', 35 | 'MATRIX_DEVICE_ID', 36 | 'MATRIX_DEVICE_NAME', 37 | 'NODE_ENV' 38 | ]); 39 | 40 | configs = _.mapKeys(configs, function (v, k) { 41 | var k = k.replace('ADMATRIX', '').replace('MATRIX', ''); 42 | return _.camelCase(k); 43 | }); 44 | // 45 | // debug('ENVS >>>>'.blue, configs); 46 | 47 | _.extend(Matrix, configs); 48 | 49 | 50 | f.jwt = { secret: process.env.JWT_SECRET }; 51 | 52 | f.version = JSON.parse(fs.readFileSync(__dirname + '/../package.json')).version; 53 | 54 | f.local = require('./env'); 55 | f.version = JSON.parse(fs.readFileSync(__dirname + '/../package.json')).version; 56 | f.heartbeatInterval = 10000; 57 | f.spaceLimit = 5000000; 58 | f.splashInterval = 30; 59 | f.sensorRefresh = process.env['MATRIX_SENSOR_REFRESH'] || 2500; 60 | // not in yet 61 | f.sensorSockets = true; 62 | f.socketCheckDelay = 1000; 63 | f.socketRefreshTimeout = 10; //seconds 64 | f.registrationUUID = 'b1a6752152eb4d36e13e357d7c225466'; 65 | f.configurationUUID = 'b1a6752152eb4d36e13e357d7c225467'; 66 | f.envs = configs; 67 | 68 | // sends with registration, determines how long the server will send pings 69 | f.serverHeartbeat = parseInt(process.env['MATRIX_CHECK_TIME']) || 60000; 70 | // after missing this many checks, reconnect to mxss 71 | f.maxSkipBeats = parseInt(process.env['MATRIX_CHECK_COUNT']) || 3; 72 | 73 | // how long to auto write cache 74 | f.writeInterval = 10000; 75 | // how long to output write logs - hourly 76 | f.writeLogInterval = 1000 * 60 * 60; 77 | 78 | // for device component info 79 | f.components = {}; 80 | 81 | // for send throttling 82 | Matrix.lastWriteTime = Date.now(); 83 | Matrix.sendCache = []; 84 | Matrix.rateLimit = 500; 85 | 86 | 87 | // TODO: Figure out where file storage happens 88 | f.paths = { root: __dirname }; 89 | 90 | module.exports = f; 91 | -------------------------------------------------------------------------------- /config/path.js: -------------------------------------------------------------------------------- 1 | var os = os ? os : require('os'); //Added for bin folder commands as those don't have globals 2 | var path = require('path'); 3 | var root = path.join(__dirname, '../'); 4 | var tmpFolder = os.tmpdir(); 5 | 6 | // for db and apps folders 7 | if (process.env.MATRIX_MODE === 'service') { 8 | root = '/var/matrix-os/store/'; 9 | } 10 | 11 | var p = { 12 | root: root, 13 | db: { 14 | device: root + 'db/device.db', 15 | config: root + 'db/config.db', 16 | user: root + 'db/user.db', 17 | service: root + 'db/service.db', 18 | pending: root + 'db/pending.db' 19 | }, 20 | pendingFiles: root + 'public/pending_files', 21 | update: tmpFolder + '/matrix-update/', 22 | backup: tmpFolder + '/matrix-backup/', 23 | apps: root + 'apps', 24 | protos: root + 'proto', 25 | appLog: root + 'apps/app.log' 26 | }; 27 | 28 | if (process.env.MATRIX_MODE === 'service') { 29 | p.proto = '/usr/share/matrix-os/proto/'; 30 | } 31 | 32 | 33 | module.exports = p; 34 | -------------------------------------------------------------------------------- /debian/TODO: -------------------------------------------------------------------------------- 1 | - Specify MOS license. 2 | - Add permissions for bluetooth and WiFi for matrix-os user. 3 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | matrixio-os (0.14.2-1) unstable; urgency=medium 2 | 3 | * Initial release. 4 | 5 | -- MATRIX Labs Tue, 6 Jun 2017 22:56:53 +0000 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: matrixio-os 2 | Section: embedded 3 | Priority: optional 4 | Maintainer: MATRIX Labs 5 | Build-Depends: debhelper (>= 9) 6 | Standards-Version: 3.9.5 7 | Homepage: https://github.com/matrix-io/matrix-os 8 | 9 | Package: matrixio-os 10 | Architecture: any 11 | Depends: bluetooth, bluez, libusb-1.0-0-dev, libudev-dev 12 | Description: MATRIX Open Source is a platform for running applications on the MATRIX Creator. 13 | MATRIX Open Source is a platform for running applications on the MATRIX Creator. 14 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: matrix-os 3 | Source: https://www.admobilize.com/ 4 | 5 | Files: * 6 | Copyright: 2017 7 | License: 8 | The software for MOS is open source and can be found in this 9 | repository: https://github.com/matrix-io/matrix-os -------------------------------------------------------------------------------- /debian/dist/matrixio-os.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=MATRIX OS Service 3 | After=matrixio-malos.target 4 | 5 | [Service] 6 | User=matrixio-os 7 | Environment=MATRIX_MODE=service 8 | ExecStart=/usr/lib/matrix-os/bin/node /usr/lib/matrix-os/index.js 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /debian/download-bundled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf dist/node && mkdir -p dist/node 4 | curl -SL https://nodejs.org/dist/v6.11.4/node-v6.11.4-linux-armv7l.tar.gz | tar xz -C dist/node --strip-components 1 5 | 6 | export PATH=$PATH:$(pwd)/dist/node/bin 7 | ./dist/node/bin/npm install --prefix dist/ .. 8 | -------------------------------------------------------------------------------- /debian/matrixio-os.install: -------------------------------------------------------------------------------- 1 | index.js usr/lib/matrix-os/ 2 | #package.json usr/lib/matrix-os/ 3 | config/ usr/lib/matrix-os/ 4 | lib/ usr/lib/matrix-os/ 5 | apps/ var/matrix-os/store/ 6 | 7 | debian/dist/matrixio-os.service /lib/systemd/system/ 8 | debian/dist/node usr/lib/matrix-os/ 9 | debian/dist/node_modules/ usr/lib/matrix-os/ 10 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | MATRIX_OS_LIB_PATH=/usr/lib/matrix-os 4 | MATRIX_OS_STORE_PATH=/var/matrix-os/store 5 | NODE_BINARY=${MATRIX_OS_LIB_PATH}/nodejs/bin/node 6 | MATRIX_USER=matrixio-os 7 | 8 | useradd -M $MATRIX_USER # Create user without a home directory 9 | usermod -L $MATRIX_USER # Disable user login 10 | 11 | 12 | mkdir -p ${MATRIX_OS_STORE_PATH}/db 13 | 14 | touch ${MATRIX_OS_STORE_PATH}/db/service.db 15 | touch ${MATRIX_OS_STORE_PATH}/db/pending.db 16 | touch ${MATRIX_OS_STORE_PATH}/db/device.db 17 | 18 | chown -R $MATRIX_USER. ${MATRIX_OS_STORE_PATH} 19 | 20 | mkdir -p ${MATRIX_OS_LIB_PATH}/bin 21 | ln -s ${NODE_BINARY} ${MATRIX_OS_LIB_PATH}/bin/node 22 | 23 | # Allow MATRIX OS nodejs to use bluetooth / network 24 | usermod -G bluetooth -a $MATRIX_USER 25 | setcap cap_net_raw+eip ${NODE_BINARY} 26 | 27 | # Enable MATRIX OS service at startup 28 | systemctl enable matrixio-os.service 29 | # Start matrix-os 30 | systemctl start matrixio-os.service 31 | -------------------------------------------------------------------------------- /debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | MATRIX_OS_LIB_PATH=/usr/lib/matrix-os 4 | MATRIX_OS_STORE_PATH=/var/matrix-os 5 | MATRIX_USER=matrixio-os 6 | 7 | rm -rf $MATRIX_OS_LIB_PATH 8 | rm -rf $MATRIX_OS_STORE_PATH 9 | rm /lib/systemd/system/matrix-os.service 10 | 11 | userdel -r $MATRIX_USER 12 | -------------------------------------------------------------------------------- /debian/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Stop, disable and remove matrix-os service 4 | systemctl stop matrixio-os.service 5 | systemctl disable matrixio-os.service 6 | 7 | 8 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #DH_VERBOSE = 1 5 | 6 | # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* 7 | DPKG_EXPORT_BUILDFLAGS = 1 8 | include /usr/share/dpkg/default.mk 9 | 10 | # see FEATURE AREAS in dpkg-buildflags(1) 11 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 12 | 13 | # see ENVIRONMENT in dpkg-buildflags(1) 14 | # package maintainers to append CFLAGS 15 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 16 | # package maintainers to append LDFLAGS 17 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 18 | 19 | 20 | # main packaging script based on dh7 syntax 21 | %: 22 | dh $@ 23 | 24 | # debmake generated override targets 25 | # This is example for Cmake (See http://bugs.debian.org/641051 ) 26 | #override_dh_auto_configure: 27 | # dh_auto_configure -- \ 28 | # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /debian/util/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo systemctl stop matrixio-os.service 4 | sudo systemctl disable matrixio-os.service 5 | sudo rm /lib/systemd/system/matrixio-os.service 6 | sudo rm -rf /var/matrix-os/ /usr/lib/matrix-os/ 7 | sudo userdel -r matrixio-os 8 | -------------------------------------------------------------------------------- /debian/util/create.sh: -------------------------------------------------------------------------------- 1 | tar cvf matrix-os-0.14.2.orig.tar matrix-os-0.14.2/ 2 | mv matrix-os-0.14.2.orig.tar matrix-os_0.14.2.orig.tar 3 | gzip matrix-os_0.14.2.orig.tar 4 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # MatrixOS development compose file 2 | # It does not define MXSS dependency intentionally so it can use either a locally running 3 | # instance or a remotely running instance, see env.MATRIX_STREAMING_SERVER 4 | version: '2' 5 | services: 6 | 7 | # install modules and compile - single run 8 | mos: 9 | build: . 10 | command: node index.js 11 | networks: 12 | - default 13 | ports: 14 | - 80:80 15 | external_links: 16 | - mxss:mxss 17 | volumes: 18 | # Mount current directory under /matrix in the container so you can edit code 19 | # locally and have the changes available inside a running container 20 | - ./:/matrix 21 | 22 | # @TODO (Sean) Please review which device mappings make sense for OSX here, we then 23 | # review Linux 24 | # devices: 25 | # - "/dev/ttyUSB0:/dev/ttyUSB0" 26 | environment: 27 | # Reinstall all modules when running docker-entrypoint.sh, helps in avoiding 28 | # ugly bugs related with bad node_modules 29 | NODE_ENV: docker 30 | DEBUG: '*,-engine' 31 | REINSTALL_NODE_MODULES: 1 32 | env_file: 33 | # Define a .env file containing the following environment variables 34 | # and tweak those according to your current development needs: 35 | # - MATRIX_API_SERVER= 36 | # development server is available @ https://dev-api.admobilize.com 37 | # - MATRIX_STREAMING_SERVER= 38 | # * development server is available @ http://dev-mxss.admobilize.com:80 39 | # * local running MXSS can be addressed by setting mxss:80 40 | # - MATRIX_DEVICE_ID= 41 | # - MATRIX_DEVICE_SECRET= 42 | # 43 | # NOTE 1: it is rude to commit this file as it will overwrite other people devel 44 | # settings 45 | # NOTE 2: there are zsh extensions to load these files automatically to your environment 46 | # when you cd to a directory. Check the gist below for an example 47 | # https://gist.github.com/maciekrb/55c2d8f8dfddb8564c1ed926d605251d 48 | - ./.env 49 | 50 | # default build. run mos first to install. 51 | mos_dev: 52 | build: . 53 | command: node index.js 54 | networks: 55 | - default 56 | ports: 57 | - 80:80 58 | volumes: 59 | # Mount current directory under /matrix in the container so you can edit code 60 | # locally and have the changes available inside a running container 61 | - ./:/matrix 62 | - ./../matrix-node-sdk/:/matrix/node_modules/matrix-node-sdk 63 | - ./../matrix-eventfilter/:/matrix/node_modules/matrix-eventfilter 64 | - ./../matrix-app-config-helper/:/matrix/node_modules/matrix-app-config-helper 65 | - ./../matrix-firebase/:/matrix/node_modules/matrix-firebase 66 | environment: 67 | NODE_ENV: docker 68 | DEBUG: '*,-engine' 69 | REINSTALL_NODE_MODULES: 0 70 | env_file: 71 | - ./.env 72 | 73 | networks: 74 | default: 75 | external: 76 | name: matrixos 77 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Useful to avoid weird bugs in development. This env var is set to true 5 | # in the docker-compose.yml file so you always run with the modules set 6 | # in the package.json. It is also useful to run with nodemon while developing. 7 | if [ "$REINSTALL_NODE_MODULES" = "1" ]; then 8 | npm install -g nodemon 9 | rm -rf node_modules 10 | npm install 11 | fi 12 | 13 | if [ "$1" = 'node' ]; then 14 | exec node index.js 15 | else 16 | exec "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /dockertest.js: -------------------------------------------------------------------------------- 1 | var proc = require('child_process'); 2 | var pwd = process.cwd(); 3 | var dockerImages = proc.execSync("docker images | cut -d' ' -f1"); 4 | console.log(dockerImages.toString()); 5 | // check for image 'matrix-apphost' in docker images 6 | if ( dockerImages.toString().indexOf('matrix-apphost') === -1 ){ 7 | // builds docker image for app hosting, see package.json 8 | try { 9 | var p = proc.execSync('docker build -t matrix-apphost -f Dockerfile-apphost .') 10 | 11 | } catch (e){ 12 | console.error(e); 13 | } 14 | console.log('Built!'); 15 | } 16 | // fire up docker image 17 | var name = 'clock'; 18 | var p = proc.spawn('docker', ['run', '-v', pwd+'/apps:/apps', '-i', 'matrix-apphost', 'node','/apps/'+name+'.matrix/index.js']); 19 | 20 | p.stdout.on('data', function(d){ 21 | console.log(d.toString()); 22 | }); 23 | 24 | p.stderr.on('data', (d) => { console.log(d.toString())}); 25 | 26 | // console.log(p); 27 | -------------------------------------------------------------------------------- /initmatrix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: MATRIX OS 4 | # Required-Start: $all 5 | # Required-Stop: 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Manage MATRIX OS Start / Stop 9 | ### END INIT INFO 10 | 11 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin 12 | 13 | . /lib/init/vars.sh 14 | . /lib/lsb/init-functions 15 | # If you need to source some other scripts, do it here 16 | 17 | case "$1" in 18 | start) 19 | log_begin_msg "Starting MATRIX OS" 20 | node /home/pi/matrix-os/index.js 21 | log_end_msg $? 22 | exit 0 23 | ;; 24 | stop) 25 | log_begin_msg "Stopping MATRIX OS" 26 | 27 | # do something to kill the service or cleanup or nothing 28 | sudo pkill -15 node 29 | log_end_msg $? 30 | exit 0 31 | ;; 32 | *) 33 | echo "Usage: /etc/init.d/ {start|stop}" 34 | exit 1 35 | ;; 36 | esac 37 | 38 | 39 | ## Install 40 | # cp initmatrix.sh -> pi 41 | # chmod -x initmatrix.sh 42 | # mv initmatrix /etc/init.d/matrix 43 | # sudo update-rc.d matrix defaults 44 | # 45 | # logs - sudo systemctl status matrix -ln 20 46 | -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/action.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var debug = debugLog('bt-char-action'); 3 | var uuid = '2A23'; 4 | 5 | var ActionCharacteristic = function () { 6 | ActionCharacteristic.super_.call(this, { 7 | uuid: uuid, 8 | properties: ['write', 'notify'] 9 | }); 10 | }; 11 | 12 | util.inherits(ActionCharacteristic, Matrix.device.bluetooth.BlockCharacteristic); 13 | 14 | function action(options) { 15 | var err; 16 | if (_.isUndefined(options) || options === '') { 17 | err = new Error('Empty BLE payload sent'); 18 | } else if (Matrix.device.bluetooth.auth) { 19 | try { 20 | debug('Parsing string received'); 21 | options = JSON.parse(options); 22 | } catch (error) { 23 | return debug('Failed to parse :('); 24 | } 25 | 26 | if (_.has(options, 'action')) { 27 | if (options.action === 'reset') { 28 | Matrix.deviceId = undefined; 29 | Matrix.deviceSecret = undefined; 30 | } else { 31 | err = new Error('Unknown action [' + options.action + '] requested'); 32 | } 33 | } else { 34 | err = new Error('No action requested'); 35 | } 36 | } else { 37 | err = new Error('Client hasn\'t authenticated'); 38 | debug('BLE configuration not authorized', uuid); 39 | } 40 | 41 | Matrix.device.bluetooth.emitter.emit('actionFinished', uuid, err); 42 | 43 | } 44 | 45 | var ActionCharacteristic = new ActionCharacteristic(); 46 | ActionCharacteristic.on('newData', action.bind(ActionCharacteristic)); 47 | module.exports = ActionCharacteristic; -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/compareAuthParams.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var debug = debugLog('bt-char-compare'); 3 | var uuid = '2A17'; 4 | 5 | var CompareAuthParamsCharacteristic = function () { 6 | CompareAuthParamsCharacteristic.super_.call(this, { 7 | uuid: uuid, 8 | properties: ['write', 'notify'] 9 | }); 10 | }; 11 | 12 | util.inherits(CompareAuthParamsCharacteristic, Matrix.device.bluetooth.BlockCharacteristic); 13 | 14 | function compareAuthParam(options) { 15 | var err; 16 | var auth = false; 17 | if (_.isUndefined(options) || options === '') { 18 | err = new Error('Empty BLE payload sent'); 19 | } else { 20 | debug('Auth encrypted params:', options); 21 | options = Matrix.service.cypher.decrypt(options, ''); 22 | debug('Auth decrypted params:', options); 23 | if (!_.isUndefined(Matrix.deviceId)) { 24 | try { 25 | options = JSON.parse(options); 26 | } catch (error) { 27 | err = new Error('Unable to parse content ' + options); 28 | } 29 | 30 | if (!err && !_.has(options, 'secret')) { 31 | err = new Error('Parameter missing ' + JSON.stringify(options)); 32 | } 33 | 34 | auth = options.secret === Matrix.deviceSecret; 35 | } else { 36 | err = new Error('Device needs to be registered'); 37 | } 38 | } 39 | 40 | return Matrix.device.bluetooth.emitter.emit('configurationAuth', err, uuid, auth); 41 | } 42 | 43 | var compareAuthParamsCharacteristic = new CompareAuthParamsCharacteristic(); 44 | compareAuthParamsCharacteristic.on('newData', compareAuthParam.bind(compareAuthParamsCharacteristic)); 45 | module.exports = compareAuthParamsCharacteristic; -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/configurationNotify.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var uuid = '2A18'; 3 | 4 | var Notify = function () { 5 | Notify.super_.call(this, { 6 | uuid: uuid, 7 | properties: ['notify'], 8 | secure: [] 9 | }); 10 | } 11 | 12 | util.inherits(Notify, Matrix.device.bluetooth.BlockCharacteristic); 13 | module.exports = new Notify(); -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/readDetails.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var debug = debugLog('bt-char-details'); 3 | var uuid = '2A16'; 4 | 5 | var ReadDetailsCharacteristic = function () { 6 | ReadDetailsCharacteristic.super_.call(this, { 7 | uuid: uuid, 8 | properties: ['read'] 9 | }); 10 | }; 11 | 12 | util.inherits(ReadDetailsCharacteristic , Matrix.device.bluetooth.BlockCharacteristic); 13 | 14 | ReadDetailsCharacteristic.prototype.getData = function (callback) { 15 | if (!Matrix.device.bluetooth.auth) { 16 | debug('BLE configuration not authorized', uuid); 17 | callback(Matrix.device.bluetooth.BlockCharacteristic.RESULT_UNLIKELY_ERROR); 18 | } else { 19 | var status = Matrix.device.wifi.status(); 20 | if (!_.has(status, 'wpa_state') || status.wpa_state !== 'COMPLETED') { 21 | status = {}; 22 | } 23 | 24 | status = Matrix.service.cypher.encrypt(JSON.stringify(status), ''); 25 | callback(status); 26 | } 27 | }; 28 | 29 | module.exports = new ReadDetailsCharacteristic(); -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/scanNetworks.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var debug = debugLog('bt-char-scan'); 3 | var uuid = '2A23'; 4 | 5 | var ScanNetworksCharacteristic = function () { 6 | ScanNetworksCharacteristic.super_.call(this, { 7 | uuid: uuid, 8 | properties: ['read'] 9 | }); 10 | }; 11 | 12 | util.inherits(ScanNetworksCharacteristic, Matrix.device.bluetooth.BlockCharacteristic); 13 | 14 | ScanNetworksCharacteristic.prototype.getData = function (callback) { 15 | if (Matrix.device.bluetooth.auth) { 16 | Matrix.device.wifi.scan(function (err, scanResults) { 17 | if(err) { 18 | debug('Wifi scan error', err.message); 19 | return callback(Matrix.device.bluetooth.BlockCharacteristic.RESULT_UNLIKELY_ERROR); 20 | } else { 21 | var ssids = _.map(scanResults, 'ssid'); 22 | console.log('Scan requested: ', ssids); 23 | if(_.isUndefined(ssids) || _.isEmpty(ssids)) scanResults = []; 24 | scanResults = Matrix.service.cypher.encrypt(JSON.stringify(scanResults), ''); 25 | return callback(scanResults); 26 | } 27 | }); 28 | } else { 29 | debug('BLE configuration not authorized', uuid); 30 | return callback(Matrix.device.bluetooth.BlockCharacteristic.RESULT_UNLIKELY_ERROR); 31 | } 32 | }; 33 | 34 | module.exports = new ScanNetworksCharacteristic(); -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/sendIdAndSecret.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var debug = debugLog('bt-char-send'); 3 | var uuid = '2A21'; 4 | 5 | var SendIdAndSecretCharacteristic = function () { 6 | SendIdAndSecretCharacteristic.super_.call(this, { 7 | uuid: uuid, 8 | properties: ['write', 'notify'] 9 | }); 10 | }; 11 | 12 | util.inherits(SendIdAndSecretCharacteristic, Matrix.device.bluetooth.BlockCharacteristic); 13 | 14 | function authenticateDevice(options) { 15 | var err; 16 | if (_.isUndefined(options) || options === '') { 17 | err = new Error('Empty BLE payload sent'); 18 | } else if (!_.isUndefined(Matrix.deviceId)) { 19 | err = new Error('Device is already configured with id ' + Matrix.deviceId); 20 | } else { 21 | try { 22 | options = JSON.parse(options); 23 | } catch (error) { 24 | err = new Error('Unable to parse content ' + options); 25 | } 26 | 27 | if (!err && (!_.has(options, 'id') || !_.has(options, 'secret') || !_.has(options, 'env'))) { 28 | err = new Error('Parameter missing ' + JSON.stringify(options)); 29 | } else if (Matrix.env !== options.env) { 30 | err = new Error('Environment missmatch ' + options.env); 31 | } 32 | } 33 | if(err) debug('Error sending ID and Secret: ' + err.message); 34 | return Matrix.device.bluetooth.emitter.emit('deviceAuth', err, uuid, options); 35 | } 36 | 37 | var sendIdAndSecretCharacteristic = new SendIdAndSecretCharacteristic(); 38 | sendIdAndSecretCharacteristic.on('newData', authenticateDevice.bind(sendIdAndSecretCharacteristic)); 39 | module.exports = sendIdAndSecretCharacteristic; -------------------------------------------------------------------------------- /lib/device/bluetooth/Characteristics/setNetwork.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var debug = debugLog('bt-char-network'); 3 | var uuid = '2A22'; 4 | 5 | var SetNetworkCharacteristic = function () { 6 | SetNetworkCharacteristic.super_.call(this, { 7 | uuid: uuid, 8 | properties: ['write', 'notify'] 9 | }); 10 | }; 11 | 12 | util.inherits(SetNetworkCharacteristic, Matrix.device.bluetooth.BlockCharacteristic); 13 | 14 | function setNetwork(options) { 15 | var err; 16 | //TODO This could use some refactoring 17 | if (_.isUndefined(options) || options === '') { 18 | err = new Error('Empty BLE payload sent'); 19 | Matrix.device.bluetooth.emitter.emit('actionFinished', err, uuid); 20 | } else { 21 | options = Matrix.service.cypher.decrypt(options, ''); 22 | if (Matrix.device.bluetooth.auth) { 23 | try { 24 | debug('Parsing string received'); 25 | options = JSON.parse(options); 26 | } catch (error) { 27 | return debug('Failed to parse :('); 28 | } 29 | 30 | if (!_.has(options, 'ssid')) { 31 | err = new Error('Missing parameters'); 32 | Matrix.device.bluetooth.emitter.emit('setNetwork', err, uuid); 33 | } else { 34 | console.log('Connecting device to Wifi network', options.ssid, '...'); 35 | if (!_.has(options, 'password')) { 36 | Matrix.device.wifi.connectOpen(options.ssid, function (err, result) { 37 | if (!err) { 38 | if (!result){ 39 | err = new Error('Unable to connect to the network', options.ssid); 40 | console.log('Network connection failed'); 41 | } else { 42 | console.log('Network connection successful!'); 43 | } 44 | } else { 45 | console.log('Error connection to network'); 46 | } 47 | Matrix.device.bluetooth.emitter.emit('setNetwork', err, uuid); 48 | }); 49 | } else { 50 | Matrix.device.wifi.connect(options.ssid, options.password, function (err, result) { 51 | if (!err) { 52 | if (!result){ 53 | err = new Error('Unable to connect to the network', options.ssid); 54 | console.log('Network connection failed'); 55 | } else { 56 | console.log('Network connection successful!'); 57 | } 58 | } else { 59 | console.log('Error connection to network'); 60 | } 61 | Matrix.device.bluetooth.emitter.emit('setNetwork', err, uuid); 62 | }); 63 | } 64 | 65 | } 66 | } else { 67 | debug('BLE configuration not authorized', uuid); 68 | err = new Error('Client hasn\'t authenticated'); 69 | Matrix.device.bluetooth.emitter.emit('actionFinished', err, uuid); 70 | } 71 | } 72 | 73 | } 74 | 75 | var setNetworkCharacteristic = new SetNetworkCharacteristic(); 76 | setNetworkCharacteristic.on('newData', setNetwork.bind(setNetworkCharacteristic)); 77 | module.exports = setNetworkCharacteristic; -------------------------------------------------------------------------------- /lib/device/bluetooth/blockCharacteristic.js: -------------------------------------------------------------------------------- 1 | var bleno = require('bleno'); 2 | var util = require('util') 3 | var packageIndex = 0; 4 | var packageArray = []; 5 | var hourZoneData = ''; 6 | var partialData = ''; 7 | var dataSize = 0; 8 | var blockSize = 512; 9 | var headerDelimiter = '&'; 10 | var async = require('async'); 11 | var debug = debugLog('bt-characteristic'); 12 | 13 | var Characteristic = function (options) { 14 | Characteristic.super_.call(this, options); 15 | if (options.getData) { 16 | this.getData = options.getData; 17 | } 18 | }; 19 | 20 | util.inherits(Characteristic, bleno.Characteristic); 21 | 22 | Characteristic.prototype.onReadRequest = function (offset, callback) { 23 | debug('READ data: ', offset); 24 | var self = this; 25 | 26 | //If this is the first time, generate the message 27 | if (packageArray.length === 0) { 28 | self.generateMessage(function () { 29 | self.generate(offset, callback); 30 | }); 31 | } else { 32 | if (offset === 0) { 33 | packageIndex++; 34 | } 35 | self.generate(offset, callback); 36 | } 37 | }; 38 | 39 | 40 | Characteristic.prototype.generate = function (offset, callback) { 41 | if (_needsReset()) { 42 | this.generateMessage(function () { 43 | returnResponse(offset, callback); 44 | }); 45 | } else { 46 | returnResponse(offset, callback); 47 | } 48 | } 49 | 50 | 51 | function returnResponse(offset, callback) { 52 | if (_.isUndefined(offset) || _.isUndefined(packageArray) || !_.has(packageArray, packageIndex) || !_.has(packageArray[packageIndex], 'length')) { 53 | debug('Faulty response request'); 54 | return callback(bleno.Characteristic.RESULT_UNLIKELY_ERROR); 55 | } 56 | if (offset > packageArray[packageIndex].length) { 57 | _reset(); 58 | return callback(bleno.Characteristic.RESULT_INVALID_OFFSET); 59 | } 60 | return callback(bleno.Characteristic.RESULT_SUCCESS, new Buffer(packageArray[packageIndex].slice(offset))); 61 | } 62 | 63 | 64 | Characteristic.prototype.onWriteRequest = function (data, offset, withoutResponse, callback) { 65 | debug('WRITE data: ', data, offset); 66 | debug('partialData:', partialData); 67 | var info = data.toString(); 68 | if (info.indexOf(headerDelimiter) > -1 && offset === 0) { 69 | partialData = info.substring(info.lastIndexOf(headerDelimiter) + 1); 70 | dataSize = parseInt(info.substring(info.indexOf(headerDelimiter) + 1, info.lastIndexOf(headerDelimiter)), 10) 71 | } else { 72 | partialData += info; 73 | } 74 | if (dataSize === partialData.length) { 75 | hourZoneData = partialData; 76 | partialData = ''; 77 | dataSize = 0; 78 | this.emit('newData', hourZoneData); 79 | } else if (dataSize < partialData.length) { 80 | partialData = ''; 81 | dataSize = 0; 82 | this.emit('error', 'message size was bigger that the size sent ' + partialData); 83 | return callback(bleno.Characteristic.RESULT_UNLIKELY_ERROR); 84 | } 85 | return callback(bleno.Characteristic.RESULT_SUCCESS); 86 | }; 87 | 88 | 89 | Characteristic.prototype.onSubscribe = function (maxValueSize, updateValueCallback) { 90 | this.on('updateResponse', function (response) { 91 | _sendNotificationBlocks(response, updateValueCallback); 92 | }); 93 | } 94 | 95 | 96 | function _sendNotificationBlocks(response, updateCallback) { 97 | var responseBlocks = _generateBlocks(response, 20); 98 | async.eachSeries(responseBlocks, function iterator(block, nextBlock) { 99 | updateCallback(new Buffer(block)); 100 | nextBlock(); 101 | }); 102 | } 103 | 104 | 105 | Characteristic.prototype.generateMessage = function (callback) { 106 | 107 | if (this.getData) { 108 | this.getData(function (data) { 109 | packageArray = _generateBlocks(data); 110 | packageIndex = 0; 111 | callback(); 112 | }); 113 | } else { 114 | this.emit('error', 'this characteristic is not a write one ' + partialData); 115 | callback(new Error('No getData function')); 116 | } 117 | 118 | } 119 | 120 | Characteristic.prototype.clearMessage = function () { 121 | return _reset(); 122 | } 123 | 124 | Characteristic.prototype.clear = function () { 125 | _reset(); 126 | this.removeAllListeners('updateResponse'); 127 | return; 128 | } 129 | 130 | function _reset() { 131 | packageIndex = 0; 132 | packageArray = []; 133 | } 134 | 135 | 136 | function _needsReset() { 137 | return packageIndex >= packageArray.length && packageArray.length !== 0; 138 | } 139 | 140 | 141 | function _generateBlocks(message, blockLimit) { 142 | if (_.isUndefined(blockLimit)) blockLimit = blockSize; 143 | if (!message || !message.hasOwnProperty('length')) { 144 | return []; 145 | } else { 146 | var header = headerDelimiter + message.length + headerDelimiter; 147 | return _chunkStringToArray(header + message, blockLimit); 148 | } 149 | } 150 | 151 | function _chunkStringToArray(str, length) { 152 | return str.match(new RegExp('.{1,' + length + '}', 'g')); 153 | } 154 | 155 | module.exports = Characteristic; -------------------------------------------------------------------------------- /lib/device/bluetooth/service.js: -------------------------------------------------------------------------------- 1 | var optional = require('optional'); 2 | var bleno = optional('bleno'); 3 | var util = require('util'); 4 | 5 | var Service = function (options) { 6 | Service.super_.call(this, options); 7 | }; 8 | 9 | var blenoPrimaryService; 10 | if (bleno) blenoPrimaryService = bleno.PrimaryService; 11 | 12 | util.inherits(Service, blenoPrimaryService); -------------------------------------------------------------------------------- /lib/device/drivers/accelerometer.js: -------------------------------------------------------------------------------- 1 | var SenseDriver, DeviceDriver; 2 | 3 | var debug = debugLog('accelerometer') 4 | 5 | module.exports = { 6 | commands: ['accelerometer'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver 10 | SenseDriver = Matrix.service.protobuf.malos.sense; 11 | }, 12 | read: function (buffer) { 13 | var a = SenseDriver.Imu.decode(buffer); 14 | return { x: a.accelX, y: a.accelY, z: a.accelZ }; 15 | }, 16 | prepare: function (options, cb) { 17 | if (_.isFunction(options)) { 18 | cb = options; 19 | options = {}; 20 | } 21 | if (_.isUndefined(options)) { 22 | options = {}; 23 | } 24 | 25 | if (!_.has(options, 'refresh')) { 26 | options.refresh = 1.0; 27 | } else if (parseFloat(options.refresh) === options.refresh) { 28 | options.refresh = options.refresh / 1000 29 | } 30 | if (!_.has(options, 'timeout')) { 31 | options.timeout = 15.0; 32 | } else if (parseFloat(options.timeout) === options.timeout) { 33 | options.timeout = options.timeout / 1000 34 | } 35 | 36 | // map options to protobuf config 37 | var config = new DeviceDriver.DriverConfig 38 | // 2 seconds between updates. 39 | config.delayBetweenUpdates = options.refresh; 40 | // Stop sending updates 6 seconds after pings. 41 | config.timeoutAfterLastPing = options.timeout; 42 | debug('gyro start') 43 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 44 | }, 45 | ping: function () { 46 | if (_.has(Matrix.components, 'accelerometer')) { 47 | Matrix.components.accelerometer.ping(); 48 | } else { 49 | console.log('Accelerometer available, not activated.'); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /lib/device/drivers/altitude.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('altitude'); 4 | 5 | module.exports = { 6 | // init runs automatically, wait for app to request component creation 7 | init: function () { 8 | DeviceDriver = Matrix.service.protobuf.malos.driver; 9 | SenseDriver = Matrix.service.protobuf.malos.sense; 10 | }, 11 | read: function (buffer) { 12 | return { 13 | value: SenseDriver.Pressure.decode(buffer).altitude 14 | }; 15 | }, 16 | prepare: function (options, cb) { 17 | if (_.isFunction(options)) { 18 | cb = options; 19 | options = {}; 20 | } 21 | if (_.isUndefined(options)) { 22 | options = {}; 23 | } 24 | 25 | if (!_.has(options, 'refresh')) { 26 | options.refresh = 1.0; 27 | } else if (parseFloat(options.refresh) === options.refresh) { 28 | options.refresh = options.refresh / 1000; 29 | } 30 | if (!_.has(options, 'timeout')) { 31 | options.timeout = 15.0; 32 | } else if (parseFloat(options.timeout) === options.timeout) { 33 | options.timeout = options.timeout / 1000; 34 | } 35 | 36 | // map options to protobuf config 37 | var config = new DeviceDriver.DriverConfig; 38 | // 2 seconds between updates. 39 | config.delayBetweenUpdates = options.refresh; 40 | // Stop sending updates 6 seconds after pings. 41 | config.timeoutAfterLastPing = options.timeout; 42 | debug('altitude start'); 43 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 44 | }, 45 | ping: function () { 46 | if (_.has(Matrix.components, 'altitude')) { 47 | Matrix.components.altitude.ping(); 48 | } else { 49 | console.log('Temperature available, not activated.'); 50 | } 51 | } 52 | }; -------------------------------------------------------------------------------- /lib/device/drivers/detection.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, VisionDriver, EyeDriver, VisionService; 2 | 3 | var debug = debugLog('detection'); 4 | 5 | module.exports = { 6 | commands: ['face', 'demographics'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | VisionDriver = Matrix.service.protobuf.vision; 10 | VisionService = Matrix.service.protobuf.vision_service; 11 | EyeDriver = Matrix.service.protobuf.malos.maloseye; 12 | DeviceDriver = Matrix.service.protobuf.malos.driver; 13 | 14 | //enums 15 | EventTags = Matrix.service.protobuf.vision.EventTag; 16 | }, 17 | // not technically async, needs for facialRecognition 18 | read: function (buffer) { 19 | var detect = VisionDriver.VisionResult.toObject(VisionDriver.VisionResult.decode(buffer), { 20 | enums: String 21 | }); 22 | debug('read>', detect); 23 | 24 | _.each(detect.vision_event, function (v) { 25 | debug('>v_e', _.findKey(EventTags, v.tag), v.trackingId); 26 | if (v.tag === EventTags['TRACKING_START']) { 27 | Matrix.service.track.add(v.trackingId); 28 | } else if (v.tag === EventTags['TRACKING_END']) { 29 | Matrix.service.track.remove(v.trackingId); 30 | } 31 | } 32 | ); 33 | 34 | 35 | // unlike other sensors, this one is a collection 36 | return _.map(detect.rectDetection, (d) => { 37 | var o = { 38 | location: d.location, 39 | tag: d.tag, 40 | image: d.image, 41 | // image_small: d.image_small.toString() 42 | }; 43 | 44 | if (_.has(d, 'trackingId')) { 45 | o.trackId = parseInt(d.trackingId, 10); 46 | } 47 | 48 | if (_.has(d, 'facialRecognition')) { 49 | o.demographics = _.reduce(d.facialRecognition, function (r, v, k) { 50 | // translate from { tag: 'EMOTION', emotion: 'HAPPY' to { emotion: 'happy' } 51 | debug('v:', v); 52 | 53 | if (_.has(v, "tag")) { 54 | // simple values 55 | var tag = v.tag.toLowerCase(); 56 | r[tag] = v[tag]; 57 | 58 | // complex values 59 | if (tag === 'head_pose') { 60 | r.pose = {}; 61 | r.pose.yaw = v.poseYaw; 62 | r.pose.roll = v.poseRoll; 63 | r.pose.pitch = v.posePitch; 64 | } else if (tag === 'gender' && _.has(v, 'gender')) { 65 | r.gender = v.gender; 66 | } else if (tag === 'emotion' && _.has(v, 'emotion')) { 67 | r.emotion = v.emotion; 68 | } 69 | } else if (_.has(v, 'age')) { // age is being received with only the age property, not the tag 70 | r.age = v.age; 71 | } 72 | return r; 73 | }, {}); 74 | } 75 | return o; 76 | }); 77 | }, 78 | prepare: function (options, cb) { 79 | if (_.isFunction(options)) { 80 | cb = options; 81 | options = {}; 82 | } 83 | if (_.isUndefined(options)) { 84 | options = {}; 85 | } 86 | 87 | if (!_.has(options, 'refresh')) { 88 | options.refresh = 1.0; 89 | } else if (parseFloat(options.refresh) === options.refresh) { 90 | options.refresh = options.refresh / 1000; 91 | } 92 | if (!_.has(options, 'timeout')) { 93 | options.timeout = 10.0; 94 | } else if (parseFloat(options.timeout) === options.timeout) { 95 | options.timeout = options.timeout / 1000; 96 | } 97 | 98 | // map options to protobuf config 99 | var driverConfig = new DeviceDriver.DriverConfig; 100 | // 2 seconds between updates. 101 | driverConfig.delayBetweenUpdates = 0.05; 102 | // Stop sending updates 6 seconds after pings. 103 | // driverConfig.timeoutAfterLastPing = options.timeout; 104 | 105 | var camConfig = new EyeDriver.CameraConfig; 106 | camConfig.cameraId = 0; 107 | camConfig.width = 640; 108 | camConfig.height = 480; 109 | 110 | driverConfig.malosEyeConfig = new EyeDriver.MalosEyeConfig; 111 | driverConfig.malosEyeConfig.cameraConfig = camConfig; 112 | 113 | debug('config>', driverConfig); 114 | cb(DeviceDriver.DriverConfig.encode(driverConfig).finish()); 115 | }, 116 | ping: function () { 117 | if (_.has(Matrix.components, 'detection')) { 118 | Matrix.components.detection.ping(); 119 | } else { 120 | console.log('Detection available, not activated.'); 121 | } 122 | }, 123 | error: function (err) { 124 | console.error('Face', err); 125 | }, 126 | 127 | config: function (options) { 128 | 129 | debug('config detection>', options); 130 | 131 | var config = new DeviceDriver.DriverConfig; 132 | config.malosEyeConfig = new EyeDriver.MalosEyeConfig; 133 | 134 | // send config 135 | //config.malosEyeConfig.objectToDetect.push(EyeDriver.EnumMalosEyeDetectionType[options.enumName]); 136 | config.malosEyeConfig.objectToDetect.push(EyeDriver.EnumMalosEyeDetectionType.FACE_DESCRIPTOR); 137 | config.malosEyeConfig.objectToDetect.push(EyeDriver.EnumMalosEyeDetectionType.FACE_DEMOGRAPHICS); 138 | 139 | if (_.has(Matrix.components, 'detection')) { 140 | Matrix.components.detection.config(DeviceDriver.DriverConfig.encode(config).finish()); 141 | } else { 142 | console.log('Detection Component not ready for Config'); 143 | } 144 | 145 | } 146 | }; 147 | -------------------------------------------------------------------------------- /lib/device/drivers/gesture.js: -------------------------------------------------------------------------------- 1 | var protoBuilder, VisionDriver, DeviceDriver, MalosEye; 2 | 3 | var debug = debugLog('gesture') 4 | 5 | module.exports = { 6 | commands: ['palm', 'thumb-up', 'fist', 'pinch'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | VisionDriver = Matrix.service.protobuf.vision; 10 | DeviceDriver = Matrix.service.protobuf.malos.driver; 11 | MalosEye = Matrix.service.protobuf.malos.maloseye; 12 | //Enums 13 | }, 14 | // not technically async, but needs to be this way to support recog being under service 15 | read: function (buffer) { 16 | 17 | debug('-read>', VisionDriver.VisionResult.decode(buffer)); 18 | var detect = VisionDriver.VisionResult.decode(buffer); 19 | 20 | // unlike other sensors, this one is a collection 21 | return _.map(detect.rectDetection, function (d) { 22 | return { 23 | location: d.location, 24 | tag: d.tag 25 | } 26 | }) 27 | }, 28 | 29 | 30 | // Prepare is done after init. To Prepare the connection by adding a configuration 31 | prepare: function (options, cb) { 32 | 33 | if (!_.has(options, 'enumName')) { 34 | return console.error('gesture>prepare has no enum name to specify algo') 35 | } 36 | 37 | if (!_.has(options, 'refresh')) { 38 | options.refresh = 1.0; 39 | } else if (parseFloat(options.refresh) === options.refresh) { 40 | options.refresh = options.refresh / 1000 41 | } 42 | if (!_.has(options, 'timeout')) { 43 | options.timeout = 10.0; 44 | } else if (parseFloat(options.timeout) === options.timeout) { 45 | options.timeout = options.timeout / 1000 46 | } 47 | 48 | var config = new DeviceDriver.DriverConfig; 49 | // Generic configuration. 50 | // Almost 0 delay between updates. 200ms. 51 | config.delayBetweenUpdates = 0.25; 52 | // Driver specific configuration. 53 | config.malosEyeConfig = new MalosEye.MalosEyeConfig; 54 | 55 | 56 | var camConfig = new MalosEye.CameraConfig; 57 | camConfig.cameraId = 0; 58 | camConfig.width = 640; 59 | camConfig.height = 480; 60 | 61 | config.malosEyeConfig = new MalosEye.MalosEyeConfig; 62 | config.malosEyeConfig.cameraConfig = camConfig; 63 | 64 | 65 | debug('gesture video setup for ', options.enumName) 66 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 67 | }, 68 | 69 | // pings are executed by heartbeats 70 | ping: function () { 71 | if (_.has(Matrix.components, 'gesture')) { 72 | Matrix.components.gesture.ping(); 73 | } else { 74 | console.error('No Gesture Component Available for Ping') 75 | console.error('Components:', Matrix.components); 76 | } 77 | }, 78 | error: function (err) { 79 | console.error('Face', err); 80 | }, 81 | 82 | // gesture as enum PALM 83 | // TODO: support options, handle arrays 84 | config: function (options) { 85 | 86 | debug('configure options>', options); 87 | 88 | var config = new DeviceDriver.DriverConfig; 89 | config.malosEyeConfig = new MalosEye.MalosEyeConfig; 90 | 91 | // send config = options.enumName = HAND_FIST, HAND_PALM, etc 92 | config.malosEyeConfig.objectToDetect.push(MalosEye.EnumMalosEyeDetectionType[options.enumName]); 93 | 94 | if (_.has(Matrix.components, 'gesture')) { 95 | Matrix.components.gesture.config(DeviceDriver.DriverConfig.encode(config).finish()); 96 | } else { 97 | console.error('No Gesture Component Available for Config') 98 | console.error('Components:', _.keys(Matrix.components)); 99 | } 100 | 101 | } 102 | } -------------------------------------------------------------------------------- /lib/device/drivers/gpio.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, IODriver; 2 | 3 | var debug = debugLog('gpio') 4 | var pinAmount = 16; 5 | 6 | module.exports = { 7 | commands: ['gpio'], 8 | // init runs automatically, wait for app to request component creation 9 | init: function () { 10 | DeviceDriver = Matrix.service.protobuf.malos.driver; 11 | IODriver = Matrix.service.protobuf.malos.io; 12 | }, 13 | read: function (buffer) { 14 | var buffer = new IODriver.GpioParams.decode(buffer); 15 | return { 16 | pin: buffer.pin, //set_pin 17 | value: buffer.value, //set_value 18 | mode: buffer.mode, //set_mode(matrixMalosBuilder.GpioParams.EnumMode.OUTPUT) 19 | values: buffer.values 20 | } 21 | }, 22 | send: function (options) { 23 | 24 | }, 25 | /** 26 | * prepare gpio protobufs 27 | * @param [] options.pin pin to setup for writing 28 | * @param [] options.value value to write 29 | * @param {Function} cb [description] 30 | * @return {[type]} [description] 31 | */ 32 | 33 | prepare: function (options, cb) { 34 | debug('prepare=>', options); 35 | var pin = parseInt(options.pin); 36 | var value = parseInt(options.value); 37 | var config = new DeviceDriver.DriverConfig; 38 | 39 | if (options.servo === true && pin < pinAmount) { 40 | var servoHandler = new IODriver.ServoParams; 41 | servoHandler.pin = pin; 42 | servoHandler.angle = value; 43 | config.servo = servoHandler; 44 | return cb(DeviceDriver.DriverConfig.encode(config).finish()); 45 | } 46 | 47 | var gpioHandler = new IODriver.GpioParams; 48 | gpioHandler.mode = IODriver.GpioParams.EnumMode['OUTPUT']; 49 | 50 | if (!_.isUndefined(pin)) { 51 | if (_.isArray(pin)) { //#Overload pin = pins array //TODO This will eventually be supported by the protos 52 | debug('SETTING ALL PINS'); 53 | gpioHandler.pins = true; //TODO Still not supported by the protos 54 | gpioHandler.values = pin; //TODO Still not supported by the protos 55 | config.gpio = gpioHandler; 56 | return cb(DeviceDriver.DriverConfig.encode(config).finish()); 57 | } else if (_.isInteger(pin) && pin >= 0 && pin < pinAmount && _.isInteger(value)) { 58 | debug('SETTING PIN #', pin); 59 | gpioHandler.pin = pin; 60 | gpioHandler.value = value; 61 | config.gpio = gpioHandler; 62 | return cb(DeviceDriver.DriverConfig.encode(config).finish()); 63 | } else { 64 | //Incorrect format 65 | debug('Incorrect format!'); 66 | cb(new Error('Incorrectly formatted params')); 67 | } 68 | } else { 69 | debug('Need to provide params!'); 70 | return cb(new Error('Need to provide params')); 71 | } 72 | }, 73 | ping: function () { 74 | if (_.has(Matrix.components, 'gpio')) { 75 | Matrix.components.gpio.ping(); 76 | } else { 77 | console.error('GPIO available, not activated.'); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /lib/device/drivers/gyroscope.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, matrixMalosBuilder; 2 | 3 | var debug = debugLog('gyroscope'); 4 | 5 | module.exports = { 6 | commands: ['gyroscope'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver; 10 | // Parse matrix_malos package (namespace). 11 | SenseDriver = Matrix.service.protobuf.malos.sense; 12 | }, 13 | read: function (buffer) { 14 | var g = SenseDriver.Imu.decode(buffer); 15 | return { 16 | // orientation values sent with gyro 17 | yaw: g.yaw, 18 | pitch: g.pitch, 19 | roll: g.roll, 20 | x: g.gyroX, 21 | y: g.gyroY, 22 | z: g.gyroZ 23 | }; 24 | }, 25 | prepare: function (options, cb) { 26 | if (_.isFunction(options)) { 27 | cb = options; 28 | options = {}; 29 | } 30 | if (_.isUndefined(options)) { 31 | options = {}; 32 | } 33 | 34 | if (!_.has(options, 'refresh')) { 35 | options.refresh = 1.0; 36 | } else if (parseFloat(options.refresh) === options.refresh) { 37 | options.refresh = options.refresh / 1000; 38 | } 39 | if (!_.has(options, 'timeout')) { 40 | options.timeout = 15.0; 41 | } else if (parseFloat(options.timeout) === options.timeout) { 42 | options.timeout = options.timeout / 1000; 43 | } 44 | 45 | // map options to protobuf config 46 | var config = new DeviceDriver.DriverConfig; 47 | // 2 seconds between updates. 48 | config.delayBetweenUpdates = options.refresh; 49 | // Stop sending updates 6 seconds after pings. 50 | config.timeoutAfterLastPing = options.timeout; 51 | debug('gyro start'); 52 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 53 | }, 54 | ping: function () { 55 | if (_.has(Matrix.components, 'gyroscope')) { 56 | Matrix.components.gyroscope.ping(); 57 | } else { 58 | console.log('Gyroscope available, not activated.'); 59 | } 60 | } 61 | }; -------------------------------------------------------------------------------- /lib/device/drivers/humidity.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('humidity') 4 | 5 | module.exports = { 6 | commands: ['humidity'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver 10 | // Parse matrix_malos package (namespace). 11 | SenseDriver = Matrix.service.protobuf.malos.sense; 12 | }, 13 | read: function (buffer) { 14 | return { value: SenseDriver.Humidity.decode(buffer).humidity } 15 | }, 16 | prepare: function (options, cb) { 17 | if (_.isFunction(options)) { 18 | cb = options; 19 | options = {}; 20 | } 21 | if (_.isUndefined(options)) { 22 | options = {}; 23 | } 24 | 25 | if (!_.has(options, 'refresh')) { 26 | options.refresh = 1.0; 27 | } else if (parseFloat(options.refresh) === options.refresh) { 28 | options.refresh = options.refresh / 1000 29 | } 30 | if (!_.has(options, 'timeout')) { 31 | options.timeout = 15.0; 32 | } else if (parseFloat(options.timeout) === options.timeout) { 33 | options.timeout = options.timeout / 1000 34 | } 35 | 36 | // map options to protobuf config 37 | var config = new DeviceDriver.DriverConfig 38 | // 2 seconds between updates. 39 | config.delayBetweenUpdates = options.refresh; 40 | // Stop sending updates 6 seconds after pings. 41 | config.timeoutAfterLastPing = options.timeout; 42 | debug('humidity start> :', config) 43 | 44 | // we don't really need anything specific to humidity here 45 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 46 | }, 47 | ping: function () { 48 | if (_.has(Matrix.components, 'humidity')) { 49 | Matrix.components.humidity.ping(); 50 | } else { 51 | console.error('Temperature available, not activated.'); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /lib/device/drivers/ir.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('ir'); 4 | 5 | module.exports = { 6 | init: function () { 7 | DeviceDriver = Matrix.service.protobuf.malos.driver; 8 | // Parse matrix_malos package (namespace). 9 | 10 | SenseDriver = Matrix.service.protobuf.malos.sense; 11 | }, 12 | read: function (buffer) { 13 | return { 14 | value: SenseDriver.LircParams.decode(buffer).pressure 15 | }; 16 | }, 17 | send: function (message) { 18 | // doesn't work yet 19 | var msg = new SenseDriver.LircParams(); 20 | msg.proto.device = ''; 21 | msg.proto.command = ''; 22 | // Matrix.components.ir.send(SenseDriver.LircParams.encode(msg).finish()); 23 | return console.warn('IR.Send is not working yet') 24 | }, 25 | prepare: function (options, cb) { 26 | if (_.isFunction(options)) { 27 | cb = options; 28 | options = {}; 29 | } 30 | if (_.isUndefined(options)) { 31 | options = {}; 32 | } 33 | 34 | if (!_.has(options, 'refresh')) { 35 | options.refresh = 1.0; 36 | } else if (parseFloat(options.refresh) === options.refresh) { 37 | options.refresh = options.refresh / 1000; 38 | } 39 | if (!_.has(options, 'timeout')) { 40 | options.timeout = 15.0; 41 | } else if (parseFloat(options.timeout) === options.timeout) { 42 | options.timeout = options.timeout / 1000; 43 | } 44 | 45 | // map options to protobuf config 46 | var config = new DeviceDriver.DriverConfig; 47 | // 2 seconds between updates. 48 | config.delayBetweenUpdates = options.refresh; 49 | // Stop sending updates 6 seconds after pings. 50 | config.timeoutAfterLastPing = options.timeout; 51 | debug('ir start'); 52 | 53 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 54 | }, 55 | ping: function () { 56 | if (_.has(Matrix.components, 'ir')) { 57 | Matrix.components.ir.ping(); 58 | } else { 59 | console.log('IR available, not activated.'); 60 | } 61 | } 62 | }; -------------------------------------------------------------------------------- /lib/device/drivers/magnetometer.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('magnetometer'); 4 | 5 | module.exports = { 6 | commands: ['magnetometer'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver; 10 | // Parse matrix_malos package (namespace). 11 | SenseDriver = Matrix.service.protobuf.malos.sense; 12 | }, 13 | read: function (buffer) { 14 | var m = SenseDriver.Imu.decode(buffer); 15 | return { x: m.magX, y: m.magY, z: m.magZ }; 16 | }, 17 | prepare: function (options, cb) { 18 | if (_.isFunction(options)) { 19 | cb = options; 20 | options = {}; 21 | } 22 | if (_.isUndefined(options)) { 23 | options = {}; 24 | } 25 | 26 | if (!_.has(options, 'refresh')) { 27 | options.refresh = 1.0; 28 | } else if (parseFloat(options.refresh) === options.refresh) { 29 | options.refresh = options.refresh / 1000; 30 | } 31 | if (!_.has(options, 'timeout')) { 32 | options.timeout = 15.0; 33 | } else if (parseFloat(options.timeout) === options.timeout) { 34 | options.timeout = options.timeout / 1000; 35 | } 36 | 37 | // map options to protobuf config 38 | var config = new DeviceDriver.DriverConfig; 39 | // 2 seconds between updates. 40 | config.delayBetweenUpdates = options.refresh; 41 | // Stop sending updates 6 seconds after pings. 42 | config.timeoutAfterLastPing = options.timeout; 43 | debug('gyro start'); 44 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 45 | }, 46 | ping: function () { 47 | if (_.has(Matrix.components, 'magnetometer')) { 48 | Matrix.components.magnetometer.ping(); 49 | } else { 50 | console.log('Magnetometer available, not activated.'); 51 | } 52 | } 53 | }; -------------------------------------------------------------------------------- /lib/device/drivers/mic.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, IODriver; 2 | 3 | // not using malos for data 4 | var micInstance, micKill, micStream; 5 | 6 | var debug = debugLog('mic'); 7 | var mic = require('mic'); 8 | 9 | module.exports = { 10 | init: function () { 11 | DeviceDriver = Matrix.service.protobuf.malos.driver; 12 | IODriver = Matrix.service.protobuf.malos.io; 13 | }, 14 | read: function (spl) { 15 | debug('-read>', spl); 16 | 17 | return spl; 18 | }, 19 | 20 | stop: function () { 21 | micInstance.stop(); 22 | }, 23 | 24 | prepare: function (options, cb) { 25 | 26 | //default to beamformed mic 27 | var micNum = (options.hasOwnProperty('number')) ? options.number : 8; 28 | 29 | if (!_.isNumber(micNum) || micNum < 0 || micNum > 8) { 30 | return cb(new Error('Bad Microphone Number:', micNum)); 31 | } 32 | 33 | micInstance = mic({ 34 | 'bitwidth': 16, 35 | 'device': 'mic_channel' + micNum, 36 | 'rate': '16000', 37 | 'channels': '1' 38 | }); 39 | 40 | micStream = micInstance.getAudioStream(); 41 | 42 | var headerSent = false; 43 | var ds = []; 44 | micStream.on('data', function (d) { 45 | 46 | if (!headerSent) { 47 | debug('id', d.slice(0, 4).toString()); 48 | debug('format', d.slice(8, 12).toString()); 49 | // debug('channels', d.readInt8(22)); 50 | debug('rate', d.readInt16BE(24)); 51 | debug('bits', d.readInt8(34)); 52 | headerSent = true; 53 | return; 54 | } 55 | 56 | ds = []; 57 | var total = 0; 58 | 59 | for (var i = 0; i < d.byteLength / 2; i++) { 60 | ds.push(d.readInt16LE(i * 2)); 61 | } 62 | // Root Mean Square for Energy 63 | var total = ds.reduce((a, b) => { return a + b; }); 64 | var value = Math.abs(Math.round(total / ds.length / 128)); 65 | // console.log(value, _.repeat('.', value)) 66 | 67 | Matrix.events.emit('sensor-emit', { type: 'mic', value: value }); 68 | 69 | }); 70 | 71 | 72 | micInstance.start(); 73 | 74 | var config = new DeviceDriver.DriverConfig; 75 | 76 | if (!_.has(options, 'gain')) { 77 | options.gain = 0; 78 | } 79 | 80 | var micConfig = new IODriver.MicArrayParams; 81 | 82 | micConfig.gain = options.gain; 83 | // setup gain for all microphones 84 | micConfig.set_gain(8); 85 | 86 | // setup a sound source perpendicular to the MATRIX Creator 87 | micConfig.set_azimutal_angle(0); 88 | micConfig.set_polar_angle(0); 89 | micConfig.set_radial_distance_mm(1000); 90 | micConfig.set_sound_speed_mmseg(340.3 * 1000); 91 | 92 | config.set_micarray(micConfig); 93 | 94 | 95 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 96 | }, 97 | 98 | ping: function () { 99 | 100 | if (_.has(Matrix.components, 'mic')) { 101 | 102 | Matrix.components.mic.ping(); 103 | 104 | // mic isn't managed by malos, so we need to stop it here 105 | clearInterval(micKill); 106 | // if the above isn't called every 10 seconds, the process stops 107 | micKill = setTimeout(function () { 108 | micInstance.stop(); 109 | }, 10000); 110 | 111 | } else { 112 | console.error('No Microphone Component Available for Ping'); 113 | console.error('Components:', Matrix.components); 114 | } 115 | }, 116 | error: function (err) { 117 | console.error('Mic', err); 118 | } 119 | }; -------------------------------------------------------------------------------- /lib/device/drivers/pressure.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('pressure'); 4 | 5 | module.exports = { 6 | commands: ['pressure'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver; 10 | // Parse matrix_malos package (namespace). 11 | SenseDriver = Matrix.service.protobuf.malos.sense; 12 | }, 13 | read: function (buffer) { 14 | return { 15 | value: new SenseDriver.Pressure.decode(buffer).pressure 16 | }; 17 | }, 18 | prepare: function (options, cb) { 19 | if (_.isFunction(options)) { 20 | cb = options; 21 | options = {}; 22 | } 23 | if (_.isUndefined(options)) { 24 | options = {}; 25 | } 26 | 27 | if (!_.has(options, 'refresh')) { 28 | options.refresh = 1.0; 29 | } else if (parseFloat(options.refresh) === options.refresh) { 30 | options.refresh = options.refresh / 1000; 31 | } 32 | if (!_.has(options, 'timeout')) { 33 | options.timeout = 15.0; 34 | } else if (parseFloat(options.timeout) === options.timeout) { 35 | options.timeout = options.timeout / 1000; 36 | } 37 | 38 | // map options to protobuf config 39 | var config = new DeviceDriver.DriverConfig; 40 | // 2 seconds between updates. 41 | config.delayBetweenUpdates = options.refresh; 42 | // Stop sending updates 6 seconds after pings. 43 | config.timeoutAfterLastPing = options.timeout; 44 | debug('pressure start'); 45 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 46 | }, 47 | ping: function () { 48 | if (_.has(Matrix.components, 'pressure')) { 49 | Matrix.components.pressure.ping(); 50 | } else { 51 | console.log('Temperature available, not activated.'); 52 | } 53 | } 54 | }; -------------------------------------------------------------------------------- /lib/device/drivers/temperature.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('temperature'); 4 | 5 | module.exports = { 6 | commands: ['temperature'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver; 10 | // Parse matrix_malos package (namespace). 11 | SenseDriver = Matrix.service.protobuf.malos.sense; 12 | }, 13 | read: function (buffer) { 14 | return { value: SenseDriver.Humidity.decode(buffer).temperature }; 15 | }, 16 | prepare: function (options, cb) { 17 | if (_.isFunction(options)) { 18 | cb = options; 19 | options = {}; 20 | } 21 | if (_.isUndefined(options)) { 22 | options = {}; 23 | } 24 | 25 | if (!_.has(options, 'refresh')) { 26 | options.refresh = 1.0; 27 | } else if (parseFloat(options.refresh) === options.refresh) { 28 | options.refresh = options.refresh / 1000; 29 | } 30 | if (!_.has(options, 'timeout')) { 31 | options.timeout = 3.0; 32 | } else if (parseFloat(options.timeout) === options.timeout) { 33 | options.timeout = options.timeout / 1000; 34 | } 35 | 36 | // map options to protobuf config 37 | var config = new DeviceDriver.DriverConfig; 38 | // 2 seconds between updates. 39 | config.delayBetweenUpdates = options.refresh; 40 | // Stop sending updates 6 seconds after pings. 41 | config.timeoutAfterLastPing = options.timeout; 42 | debug('temperature start'); 43 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 44 | }, 45 | ping: function () { 46 | if (_.has(Matrix.components, 'temperature')) { 47 | Matrix.components.temperature.ping(); 48 | } else { 49 | console.log('Temperature available, not activated.'); 50 | } 51 | } 52 | }; -------------------------------------------------------------------------------- /lib/device/drivers/uv.js: -------------------------------------------------------------------------------- 1 | var DeviceDriver, SenseDriver; 2 | 3 | var debug = debugLog('uv'); 4 | 5 | module.exports = { 6 | commands: ['uv'], 7 | // init runs automatically, wait for app to request component creation 8 | init: function () { 9 | DeviceDriver = Matrix.service.protobuf.malos.driver; 10 | // Parse matrix_malos package (namespace). 11 | SenseDriver = Matrix.service.protobuf.malos.sense; 12 | }, 13 | read: function (buffer) { 14 | return { 15 | value: SenseDriver.UV.decode(buffer).uvIndex, 16 | risk: SenseDriver.UV.decode(buffer).omsRisk 17 | }; 18 | }, 19 | prepare: function (options, cb) { 20 | if (_.isFunction(options)) { 21 | cb = options; 22 | options = {}; 23 | } 24 | if (_.isUndefined(options)) { 25 | options = {}; 26 | } 27 | 28 | if (!_.has(options, 'refresh')) { 29 | options.refresh = 1.0; 30 | } else if (parseFloat(options.refresh) === options.refresh) { 31 | options.refresh = options.refresh / 1000; 32 | } 33 | if (!_.has(options, 'timeout')) { 34 | options.timeout = 15.0; 35 | } else if (parseFloat(options.timeout) === options.timeout) { 36 | options.timeout = options.timeout / 1000; 37 | } 38 | 39 | // map options to protobuf config 40 | var config = new DeviceDriver.DriverConfig; 41 | // 2 seconds between updates. 42 | config.delayBetweenUpdates = options.refresh; 43 | // Stop sending updates 6 seconds after pings. 44 | config.timeoutAfterLastPing = options.timeout; 45 | debug('uv start'); 46 | cb(DeviceDriver.DriverConfig.encode(config).finish()); 47 | }, 48 | ping: function () { 49 | if (_.has(Matrix.components, 'uv')) { 50 | Matrix.components.uv.ping(); 51 | } else { 52 | console.log('UV available, not activated.'); 53 | } 54 | } 55 | }; -------------------------------------------------------------------------------- /lib/device/drivers/voice.js: -------------------------------------------------------------------------------- 1 | /* // Wake Word 2 | string wake_word = 1; 3 | 4 | // Mic channel 5 | enum MicChannel { 6 | channel0 = 0; 7 | channel1 = 1; 8 | channel2 = 2; 9 | channel3 = 3; 10 | channel4 = 4; 11 | channel5 = 5; 12 | channel6 = 6; 13 | channel7 = 7; 14 | channel8 = 8; 15 | } 16 | 17 | MicChannel channel = 2; 18 | 19 | // lenguaje model path from lmtool or similar alternative: 20 | // http://www.speech.cs.cmu.edu/tools/lmtool-new.html 21 | string lm_path = 3; 22 | 23 | // dictionary path from lmtool 24 | string dic_path = 4; 25 | 26 | // enable pocketsphinx verbose mode 27 | bool enable_verbose = 5; 28 | 29 | // stop recognition service 30 | bool stop_recognition = 6; 31 | */ 32 | 33 | let IODriver, DeviceDriver; 34 | 35 | const debug = debugLog('voice'); 36 | 37 | module.exports = { 38 | init: () => { 39 | IODriver = Matrix.service.protobuf.malos.io; 40 | DeviceDriver = Matrix.service.protobuf.malos.driver; 41 | }, 42 | 43 | // translate from wakeWord to speech 44 | read: (buffer) => { 45 | var d = IODriver.WakeWordParams.decode(buffer); 46 | debug('read.decode> ', d); 47 | return { 48 | speech: (_.isUndefined(d.speech)) ? d.wakeWord : d.speech 49 | } 50 | }, 51 | /** 52 | * @param options.channel - which microphone 53 | * @param options.wakeword - set wakeword, defaults to MATRIX 54 | */ 55 | prepare: (options, cb) => { 56 | debug('prepare', options); 57 | // FIXME: bad structure. easier to patch here then in component 58 | var vc = options.options; 59 | if (!_.has(vc, 'wakeword') || 60 | !_.isString(vc.wakeword) || vc.wakeword.length === 0 61 | ) { 62 | console.log(!_.has(vc, 'wakeword'), 63 | !_.isString(vc.wakeword), vc.wakeword.length) 64 | return console.error('No Wakeword provided.') 65 | } 66 | 67 | let config = IODriver.WakeWordParams.create({ 68 | wakeWord: vc.wakeword.toUpperCase(), 69 | lmPath: "/home/pi/assets/9854.lm", 70 | dicPath: "/home/pi/assets/9854.dic", 71 | // TODO: change the channel 72 | channel: IODriver.WakeWordParams.MicChannel.channel8, 73 | enableVerbose: false 74 | }); 75 | 76 | 77 | // if (_.has(vc, 'channel')) { 78 | // if ( 79 | // _.isInteger(vc.channel) && 80 | // vc.channel <= 8 && 81 | // vc.channel >= 0 82 | // ) { 83 | // let channel = IODriver.WakeWordParams.MicChannel['channel' + vc.channel]; 84 | // config.channel = channel; 85 | // } else { 86 | // return console.error('Invalid Channel ( 0-8 )', vc.channel); 87 | // } 88 | // } else { 89 | // config.channel = IODriver.WakeWordParams.MicChannel.channel0; 90 | // } 91 | // config.enableVerbose = false; 92 | debug('prepare >', config); 93 | var DeviceConfig = new DeviceDriver.DriverConfig; 94 | DeviceConfig.wakeword = config; 95 | cb(DeviceDriver.DriverConfig.encode(DeviceConfig).finish()); 96 | }, 97 | stop: () => { 98 | let wakeword_config = new IODriver.WakeWordParams; 99 | wakeword_config.stopRecognition = true; 100 | Matrix.components.wakeword.config(IODriver.WakeWordParams.encode(wakeword_config).finish()); 101 | }, 102 | error: (err) => { 103 | console.error(err) 104 | }, 105 | config: (options) => { 106 | debug('Voice doesn\'t need a config step'); 107 | } 108 | } -------------------------------------------------------------------------------- /lib/device/gpio.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('gpio'); 2 | 3 | function dec2bin(dec) { 4 | return (dec >>> 0).toString(2); 5 | } 6 | 7 | var pinAmount = 17; 8 | 9 | module.exports = { 10 | open: function (options) { 11 | //If gpio is already open, ignore 12 | if (Matrix.components.hasOwnProperty('gpio')) { 13 | return; 14 | } 15 | 16 | // fetches the zero mq connections in a keyed object { config, update, ping... } 17 | var mqs = Matrix.service.zeromq.registerComponent(Matrix.device.drivers['gpio']); 18 | 19 | // put connections in options for component 20 | _.merge(options, mqs); 21 | 22 | var component = new Matrix.service.component(options); 23 | 24 | component.read(function (buffer) { 25 | var binary = dec2bin(buffer.values); 26 | var pinArray = binary.split("").reverse(); 27 | 28 | //Fill out the missing pins in the array 29 | /*for (var index = 0; index < pinAmount; pinAmount++){ 30 | if (index >= pinArray.length) { 31 | pinArray[index] = 0; 32 | } 33 | }*/ 34 | 35 | Matrix.events.emit('gpio-emit', { type: 'read', values: pinArray }); 36 | }); 37 | 38 | //component.write(); 39 | 40 | }, 41 | close: function (pin) { 42 | 43 | }, 44 | write: function (options) { 45 | 46 | //setup the component if needed 47 | if (!Matrix.components.hasOwnProperty('gpio') && options.servo !== true) { 48 | // fetches the zero mq connections in a keyed object { config, update, ping... } 49 | 50 | //gpio 51 | var mqs = Matrix.service.zeromq.registerComponent(Matrix.device.drivers['gpio']); 52 | 53 | // put connections in options for component 54 | _.merge(options, mqs); 55 | 56 | var component = new Matrix.service.component(options); 57 | options = _.pick(options, ['pin', 'value']); 58 | 59 | } else if (!Matrix.components.hasOwnProperty('servo') && options.servo === true) { 60 | // fakes a servo component using gpio driver, but a different port 61 | 62 | var gpioBase = _.clone(Matrix.device.drivers['gpio']); 63 | gpioBase.name = 'gpio'; 64 | // uses new port 65 | var mqs = Matrix.service.zeromq.registerComponent(gpioBase); 66 | 67 | // put connections in options for component 68 | _.merge(options, mqs); 69 | 70 | // makes Matrix.component.servo with gpio driver 71 | var component = new Matrix.service.component(options, Matrix.device.drivers['gpio']); 72 | 73 | // remove 0mq from options 74 | options = _.pick(options, ['pin', 'servo', 'value']); 75 | } 76 | // component ready 77 | 78 | if (options.servo === true && Matrix.components.hasOwnProperty('servo') && options.hasOwnProperty('pin') && options.hasOwnProperty('value')) { 79 | Matrix.components.servo.send(options); 80 | } else if (Matrix.components.hasOwnProperty('gpio') && options.hasOwnProperty('pin') && options.hasOwnProperty('value')) { 81 | Matrix.components.gpio.send(options); 82 | } else { 83 | console.error('Invalid Conditions for GPIO Write', options); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/device/heartbeat.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var hbInterval; 4 | var shbInterval; 5 | var shbTimeout; 6 | var Heartbeat; 7 | 8 | 9 | var debug = debugLog('heartbeat'); 10 | 11 | 12 | // hack 13 | var firstBeat = true; 14 | var h = 0; 15 | 16 | // Heartbeat signals the MALOS to do things and return data. 17 | module.exports = { 18 | init: function () { 19 | this.start(); 20 | 21 | }, 22 | start: function () { 23 | var self = this; 24 | hbInterval = setInterval(function () { 25 | self.beat(); 26 | }, 5000); 27 | }, 28 | beat: function () { 29 | var services = []; 30 | var sensors = []; 31 | var integrations = []; 32 | 33 | _.each(Matrix.activeApplications, function (app) { 34 | 35 | debug('activeApp->', app.name, app.config.sensors || '', app.config.services || ''); 36 | // debug('activeApp->', app ) 37 | // debug('activeApp->', app.name ) 38 | 39 | // disable policy for now until deploy is good 40 | 41 | _.each(app.config.sensors, function (s) { 42 | //verify policy 43 | if (app.policy.sensors[s] === true) { 44 | sensors.push(s.toUpperCase()); 45 | } else if (process.env.POLICY_CHECK === true) { 46 | debug(app.name, 'policy denies', s); 47 | } else { 48 | sensors.push(s.toUpperCase()); 49 | } 50 | }); 51 | 52 | // => ( { engine, type } , serviceName ) 53 | _.each(app.config.services, function (d, k) { 54 | 55 | 56 | if (!_.isUndefined(d.type)) { 57 | 58 | var sName = Matrix.device.service.getEnumName(d.engine, d.type); 59 | // check type for policy 60 | if (app.policy.services[d.type] === true) { 61 | services.push(sName); 62 | } else if (process.env.POLICY_CHECK === true) { 63 | debug('policy denied', app.name, k, d.type); 64 | } else { 65 | services.push(sName); 66 | } 67 | } else if (d.engine === 'voice') { 68 | services.push('voice'); 69 | } else { 70 | console.warn(k, ' service type is not defined'); 71 | } 72 | }); 73 | 74 | _.each(app.config.integrations, function (i) { 75 | //verify policy 76 | if (app.policy.integrations[i] === true) { 77 | integrations.push(i.toUpperCase()); 78 | } else if (process.env.POLICY_CHECK === true) { 79 | debug(app.name, 'policy denies', i); 80 | } else { 81 | integrations.push(i.toUpperCase()); 82 | } 83 | }); 84 | }); 85 | 86 | var d = {}; 87 | 88 | if (services.length > 0) { 89 | d.services = _.uniq(services); 90 | } 91 | 92 | if (sensors.length > 0) { 93 | d.sensors = _.uniq(sensors); 94 | } 95 | 96 | if (integrations.length > 0) { 97 | d.integrations = _.uniq(integrations); 98 | } 99 | 100 | firstBeat = false; 101 | 102 | // in case you need a fake heartbeat. you monster. 💚 103 | // d = { 104 | // services: { 105 | // detection: 'FACE', 106 | // zones: [{ x: 12.0, y: 12.0, width: 10.0, height: 10.0 }] 107 | // }, 108 | // sensors: ['TEMPERATURE'] 109 | // } 110 | // 111 | // Matrix.events.emit('heart-beat', d); 112 | // var beat = new Heartbeat(d); 113 | 114 | // console.log(beat); 115 | // 116 | // Matrix.service.zeromq.heartbeat(beat.encode().toBuffer()); 117 | 118 | //FIXME: Heartbeat needs to manually restart services right now. Waiting on MALOS 119 | // Remove when done. Also change HB to 1000. 120 | // TODO: Do another one of these for services 121 | 122 | // show every 10 seconds 123 | if (h++ % 10 === 0) { 124 | console.log('hb:', 'sen>', Matrix.activeSensors, 'svc>', Matrix.activeServices); 125 | } 126 | _.each(d.sensors, function (s) { 127 | if (!_.has(Matrix.device.drivers, s.toLowerCase())) { 128 | console.warn(s, 'sensor is not available as a component'); 129 | } else if (Matrix.activeSensors.indexOf(s.toLowerCase()) === -1) { 130 | console.warn(s, 'sensor is requested by the app, but not active'); 131 | } else { 132 | Matrix.device.drivers[s.toLowerCase()].ping(); 133 | } 134 | }); 135 | 136 | _.each(d.integrations, function (i) { 137 | if (_.has(Matrix.device.drivers, i.toLowerCase())) { 138 | Matrix.device.drivers[i.toLowerCase()].ping(); 139 | } else { 140 | console.warn(i, 'integration is not available as a component'); 141 | } 142 | }); 143 | 144 | // not dynamic at all 👊 145 | if (services.length > 0) { 146 | // console.log(d.services); 147 | if (Matrix.device.service.isDetection(d.services[0])) { 148 | // Matrix.device.drivers.detection.config(d.services[0]) 149 | Matrix.device.drivers.detection.ping(); 150 | } else if (Matrix.device.service.isGesture(d.services[0])) { 151 | // Matrix.device.drivers.gesture.config(d.services[0]) 152 | Matrix.device.drivers.gesture.ping(); 153 | } else if (Matrix.device.service.isRecognition(d.services[0])) { 154 | Matrix.device.drivers.recognition.ping(); 155 | } 156 | } 157 | }, 158 | stop: function () { 159 | clearInterval(hbInterval); 160 | }, 161 | 162 | // for reverse heartbeat from server, socket maintenance 163 | serverCheck: function () { 164 | shbTimeout = Matrix.config.serverHeartbeat * Matrix.config.maxSkipBeats 165 | debug('reset heartbeat killswitch to ', shbTimeout) 166 | 167 | // reset every time, or reconnect 168 | clearTimeout(shbInterval); 169 | shbInterval = setInterval(function () { 170 | console.error('[!!!]'.red, 'heartbeat not recieved within ', shbTimeout); 171 | console.error('Reconnecting to MXSS'); 172 | // close triggers reconnect 173 | Matrix.service.stream.close(); 174 | // default to 3 mins if something is off 175 | }, shbTimeout || 3 * 1000 * 60); 176 | } 177 | }; -------------------------------------------------------------------------------- /lib/device/index.js: -------------------------------------------------------------------------------- 1 | /* Everything having to do with the computer */ 2 | 3 | var debug = debugLog('device') 4 | var f = {}; 5 | 6 | // only do files that end in .js 7 | var files = require('fs').readdirSync(__dirname).filter(function (f) { return (f.indexOf('.js') === f.length - 3) }); 8 | 9 | var drivers = require('fs').readdirSync(__dirname + '/drivers').filter(function (f) { return (f.indexOf('.js') === f.length - 3) }); 10 | 11 | 12 | 13 | //remove self 14 | files.splice(files.indexOf(require('path').basename(__filename)), 1); 15 | 16 | files.forEach(function (file) { 17 | debug('Loading... device > ' + file.slice(0, -3)) 18 | f[file.slice(0, -3)] = require('./' + file); 19 | }); 20 | 21 | f.drivers = {}; 22 | 23 | var cmds = []; 24 | drivers.forEach(function (file) { 25 | var fileName = file.slice(0, -3); 26 | debug('Loading... device > driver > ' + fileName) 27 | f.drivers[fileName] = require('./drivers/' + file); 28 | f.drivers[fileName].name = fileName; 29 | if (f.drivers[fileName].hasOwnProperty('commands')) { 30 | cmds = cmds.concat(f.drivers[fileName].commands); 31 | } 32 | 33 | }); 34 | debug('Init Cmds Registered:>'.blue, cmds.length, cmds.join(' ').blue); 35 | 36 | // TODO improve command to driver mapping 37 | 38 | // Matrix.applicationEnvironment.validInitCommands = cmds; 39 | 40 | f.init = function () { 41 | 42 | for (var i in f) { 43 | if (f[i].init) { 44 | f[i].init(); 45 | debug('device init:'.blue, i); 46 | } 47 | } 48 | 49 | f['bluetooth'] = require('./bluetooth'); //Add bluetooth folder 50 | 51 | for (i in f.drivers) { 52 | if (f.drivers[i].init) { 53 | f.drivers[i].init(); 54 | debug('driver init:'.blue, i); 55 | } 56 | } 57 | } 58 | 59 | module.exports = f; 60 | -------------------------------------------------------------------------------- /lib/device/maintain.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function upgradeDependencies(cb) { 4 | 5 | var updated = false; 6 | var helper = require('matrix-app-config-helper'); 7 | var eventFilter = require('matrix-eventfilter'); 8 | var piwifi = require('pi-wifi'); 9 | 10 | //Get recent version 11 | async.parallel({ 12 | helperVersion: function (cb) { 13 | if (_.has(helper, 'checkVersion')) helper.checkVersion(function (err, version) { 14 | console.log('DEP helper: ', err, version); 15 | cb(err, version.updated); 16 | }); 17 | else cb(undefined, helper.current); 18 | }, 19 | apiVersion: function (cb) { 20 | if (_.has(Matrix.api, 'checkVersion')) Matrix.api.checkVersion(function (err, version) { 21 | console.log('DEP api: ', err, version); 22 | cb(err, version.updated); 23 | }); 24 | else cb(undefined, Matrix.api.current); 25 | }, 26 | firebaseVersion: function (cb) { 27 | if (_.has(Matrix.service.firebase, 'checkVersion')) Matrix.service.firebase.checkVersion(function (err, version) { 28 | console.log('DEP firebase: ', err, version); 29 | cb(err, version.updated); 30 | }); 31 | else cb(undefined, Matrix.service.firebase.current); 32 | }, 33 | eventVersion: function (cb) { 34 | if (_.has(eventFilter, 'checkVersion')) eventFilter.checkVersion(function (err, version) { 35 | console.log('DEP event: ', err, version); 36 | cb(err, version.updated); 37 | }); 38 | else cb(undefined, eventFilter.current); 39 | }, 40 | piwifiVersion: function (cb) { 41 | if (_.has(piwifi, 'checkVersion')) piwifi.checkVersion(function (err, version) { 42 | console.log('DEP pi: ', err, version); 43 | cb(err, version.updated); 44 | }); 45 | else cb(undefined, piwifi.current); 46 | } 47 | 48 | }, 49 | function versionResults(err, results) { 50 | var olds = _.filter(results, function (o) { return o === false; }); 51 | if (olds.length > 0) { 52 | console.log('Upgrading Dependencies....'.yellow); 53 | exec('npm upgrade matrix-node-sdk matrix-app-config-helper matrix-firebase matrix-eventfilter pi-wifi', function (error, stdout, stderr) { 54 | if (error) { 55 | console.error('Error upgrading dependencies: '.red + error); 56 | err = error; 57 | } else { 58 | checks.update = true; 59 | updated = true; 60 | console.log('Dependencies upgrade Done!'.green, 'MATRIX OS restart required.'); 61 | } 62 | cb(err); 63 | }); 64 | } else { 65 | console.log('Dependencies up to date.'); 66 | cb(err, updated); 67 | } 68 | }); 69 | 70 | } 71 | 72 | function upgradeMOS(cb) { 73 | fs.readFile(__dirname + '/package.json', function (err, info) { 74 | if (err) { 75 | console.error('Unable to read package.json file'.red, err.message); 76 | return cb(err, false); 77 | } 78 | 79 | try { 80 | info = JSON.parse(info); 81 | } catch (error) { 82 | err = error; 83 | } 84 | if (err) { 85 | console.error('Unable to parse package.json file'.red, err.message); 86 | return cb(err, false); 87 | } 88 | var currentVersion = info.version; 89 | 90 | //Check the MOS 91 | function processMOSVersion(remoteVersion, cb) { 92 | var err; 93 | var updated = false; 94 | if (currentVersion === remoteVersion) { 95 | debug('Latest Version Installed. ' + currentVersion.grey); 96 | checks.update = true; 97 | cb(err, updated); 98 | } else { 99 | //TODO Start Update LED motion 100 | updated = true; 101 | checks.update = true; 102 | console.log('MATRIX OS Upgrade Ready. ' + remoteVersion + ' now available.\n', 'Upgrading MATRIX OS....'.yellow); 103 | exec('git submodule update --init', function (error, stdout, stderr) { 104 | err = error; 105 | if (!err) { 106 | console.log('Modules updated... '.green); 107 | exec('git fetch && git pull', function (error, stdout, stderr) { 108 | err = error; 109 | if (!err) { 110 | console.log('Main code updated... '.green); 111 | console.log('Upgrade Complete: MATRIX OS restart required'.green); 112 | } else { //Code update failed 113 | debug('Error updating main code:\n', err.message); 114 | console.error('Unable to update MATRIX OS main code'.yellow); 115 | console.error('Please make sure you haven\'t modified any files ('.yellow + 'git status'.gray + '), check your connection and try again'.yellow); 116 | console.error('Alternatively, you can run MATRIX OS without the upgrade check in the meantime \''.yellow + 'NO_UPGRADE=true node index.js'.gray + '\''.yellow); 117 | } 118 | cb(err, updated); 119 | }); 120 | } else { //Git submodules update failed 121 | debug('Error updating modules:\n', err.message); 122 | console.error('Unable to update MATRIX OS submodules'.yellow); 123 | console.error('Try \''.yellow + 'git submodule deinit -f ; git submodule update --init'.gray + '\' to fix your modules'.yellow); 124 | console.error('Alternatively, you can run MATRIX OS without the upgrade check in the meantime \''.yellow + 'NO_UPGRADE=true node index.js'.gray + '\''.yellow); 125 | cb(err, updated); 126 | } 127 | 128 | }); 129 | } 130 | } 131 | 132 | //Send the actual request 133 | require('request').get(mosRepoURL, function (err, resp, body) { 134 | if (err) return console.error(err); 135 | 136 | try { 137 | Matrix.latestVersion = JSON.parse(body).version; 138 | } catch (error) { 139 | console.error('Unable to parse MATRIX OS version info:', error.message); 140 | } 141 | 142 | }); 143 | 144 | }); 145 | } 146 | 147 | -------------------------------------------------------------------------------- /lib/device/malos.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | info: function (cb) { 3 | var DriverInfo = Matrix.service.protobuf.malos.driver; 4 | Matrix.service.zeromq.deviceInfo(function (response) { 5 | var re = DriverInfo.MalosDriverInfo.decode(response); 6 | cb(re); 7 | }); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/device/port.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('port'); 2 | 3 | module.exports = { 4 | discovery: function() { 5 | // map ports by 10s 6 | }, 7 | defaults: { 8 | accelerometer: 20013, 9 | altitude: 20025, 10 | camera: 9040, 11 | detect_info: 22012, 12 | detection: 22013, 13 | gesture: 22013, 14 | gpio: 20049, 15 | gyroscope: 20013, 16 | heartbeat: 9050, 17 | humidity: 20017, 18 | info: 20012, 19 | ir: 20041, 20 | led: 20021, 21 | magnetometer: 20013, 22 | mic: 20037, 23 | microphone: 9010, 24 | pressure: 20025, 25 | recognition: 22013, 26 | servo: 20045, 27 | speaker: 9020, 28 | temperature: 20017, 29 | uv: 20029, 30 | zigbee: 40001, 31 | voice: 60001, 32 | }, 33 | get: function(subdevice) { 34 | if ( Matrix.device.drivers[subdevice].hasOwnProperty('port')){ 35 | // check driver for port definition 36 | var p = new Port(Matrix.device.drivers[subdevice].port); 37 | return p; 38 | } else if (Matrix.device.port.defaults.hasOwnProperty(subdevice)) { 39 | // check above for port number 40 | p = new Port(Matrix.device.port.defaults[subdevice]); 41 | return p; 42 | } else { 43 | console.error('No Port Defined for ', subdevice); 44 | } 45 | } 46 | }; 47 | 48 | function Port(num) { 49 | var o = { 50 | // input & config have same function 51 | input: num, 52 | config: num, 53 | send: num, 54 | error: num + 2, 55 | ping: num + 1, 56 | out: num + 3, 57 | update: num + 3, 58 | read: num + 3 59 | }; 60 | 61 | return o; 62 | } -------------------------------------------------------------------------------- /lib/device/sensor.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var debug = debugLog('device-sensor') 4 | 5 | 6 | function stopSensor( sensor ){ 7 | if ( _.isString(sensor)){ 8 | warn('stopSensor is not implemented yet from string.') 9 | } else if (_.isObject(sensor)){ 10 | // processes 11 | Matrix.device.process.kill(sensor.id); 12 | } 13 | } 14 | 15 | var sensorTypeList = [ 16 | 'temperature', 17 | 'humidity', 18 | 'gyroscope', 19 | 'accelerometer', 20 | 'magnetometer', 21 | 'nfc', 22 | 'pressure', 23 | 'uv', 24 | 'altitude', 25 | 'ir', 26 | 'face', 27 | 'mic' 28 | ] 29 | 30 | 31 | 32 | 33 | module.exports = { 34 | install: function(model){ 35 | console.warn('Sensor Install is not working now.') 36 | debug('[SENSOR INSTALL]>', model) 37 | }, 38 | start: function(options){ 39 | 40 | debug('device.sensor.start>', options); 41 | 42 | // TODO: Need a consistent internal information for sensors up and down 43 | // Check for dupes 44 | 45 | if (sensorTypeList.indexOf(options.name) === -1 ){ 46 | return console.error('No Matching Sensor Found'.red, options.name.yellow) 47 | } 48 | 49 | // we want apps to use multiple sensors TODO: remove, use heartbeat 50 | if ( Matrix.activeSensors.indexOf(options.name) !== -1 ){ 51 | return console.error('Duplicate Sensor Initialization'.red, options.yellow) 52 | } 53 | 54 | Matrix.activeSensors.push(options.name); 55 | 56 | // fetches the zero mq connections in a keyed object { config, update, ping... } 57 | var mqs = Matrix.service.zeromq.registerComponent(Matrix.device.drivers[options.name]); 58 | 59 | // put connections in options for component 60 | _.merge(options, mqs); 61 | 62 | var component = new Matrix.service.component(options); 63 | 64 | component.send(options.options, function(){ 65 | // after sensor is configured, handle data events 66 | if ( component.sensor === true ){ 67 | component.read( function(data){ 68 | 69 | debug('.read>', data); 70 | 71 | // for re-routing to apps on the other side of emit 72 | data.type = options.name; 73 | 74 | // Forward back to the rest of the Application 75 | Matrix.events.emit('sensor-emit', data); 76 | 77 | }) 78 | } 79 | }); 80 | }, 81 | killAllSensors: function(cb){ 82 | _.each(Matrix.activeSensors, function (p) { 83 | // Zeromq.removeSubscriber(p 84 | }) 85 | 86 | if (_.isFunction(cb)){ 87 | cb(null); 88 | } 89 | }, 90 | stop: function(name){ 91 | // Zeromq.removeSubscriber(name); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/device/system.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reboot: reboot 3 | } 4 | 5 | 6 | 7 | /* 8 | @method Reboot 9 | @description Reboots with the provided description message 10 | @param {String} description 11 | @param {Function} [callback] 12 | */ 13 | function reboot(description, cb) { 14 | if ( _.isUndefined(cb)){ 15 | cb = function(err){ error(err); } 16 | } 17 | 18 | log("Boot -- Rebooting " + description); 19 | var proc = require('child_process').exec("reboot", 20 | function(err, out, err){ 21 | if (err) cb(err); 22 | }); 23 | 24 | proc.on('exit', function(code, signal){ 25 | log('Reboot Initiated: ', code); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /lib/device/wifi.js: -------------------------------------------------------------------------------- 1 | var piwifi = require('pi-wifi'); 2 | module.exports = { 3 | connect: function (ssid, password, cb) { 4 | var result = false; 5 | piwifi.connect(ssid, password, function (err) { 6 | if (!err) { 7 | setTimeout(function () { 8 | piwifi.check(ssid, function (err, status) { 9 | if (!err) result = status.connected; 10 | cb(err, result, status); 11 | }); 12 | }, 1000); 13 | } else { 14 | cb(err, result); 15 | } 16 | }); 17 | }, 18 | connectOpen: function (ssid, cb) { 19 | var result = false; 20 | piwifi.connectOpen(ssid, function (err) { 21 | if (!err) { 22 | setTimeout(function () { 23 | piwifi.check(ssid, function (err, status) { 24 | if (!err) result = status.connected; 25 | cb(err, result, status); 26 | }); 27 | }, 1000); 28 | } else { 29 | cb(err, result); 30 | } 31 | }); 32 | }, 33 | scan: piwifi.scan, 34 | status: piwifi.status 35 | } 36 | -------------------------------------------------------------------------------- /lib/event/device.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('device events') 2 | 3 | module.exports = { 4 | init: function(){ 5 | Matrix.events.on('gpio-emit', gpioEmitListener); 6 | Matrix.events.on('gpio-open', Matrix.device.gpio.open); 7 | Matrix.events.on('gpio-write', Matrix.device.gpio.write); 8 | Matrix.events.on('zigbee-light', Matrix.device.drivers.zigbee.lightHandler ); 9 | Matrix.events.on('zigbee-net', Matrix.device.drivers.zigbee.netHandler ); 10 | } 11 | } 12 | 13 | function gpioEmitListener( data ){ 14 | debug('(gpio-emit)->', data); 15 | 16 | var targetApplications = _.filter( Matrix.activeApplications, function (a){ 17 | if ( _.has( a, 'integrations' ) && !_.isUndefined(a.integrations) ){ 18 | return ( a.integrations.indexOf('gpio') > -1 ); 19 | } 20 | }) 21 | 22 | if ( targetApplications.length === 0 ){ 23 | debug('GPIO Event with No Application Listener', data); 24 | } 25 | 26 | _.each(targetApplications, function (p) { 27 | p.process.send({ 28 | eventType: 'gpio-emit', 29 | payload: data 30 | }) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /lib/event/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Matrix.event is to support all the event handlers, 3 | */ 4 | 5 | //* The server we're running 6 | // 7 | var debug = debugLog('matrix'); 8 | var f = {}; 9 | 10 | // only do files that end in .js 11 | var files = require('fs').readdirSync(__dirname).filter(function (f) { return (f.indexOf('.js') === f.length - 3) }); 12 | 13 | //remove self 14 | files.splice(files.indexOf(require('path').basename(__filename)), 1); 15 | 16 | files.forEach(function (file) { 17 | debug('Loading... event > ' + file.slice(0, -3)) 18 | f[file.slice(0, -3)] = require('./' + file); 19 | }); 20 | 21 | // expose this to fire off the event listeners after everything is loaded 22 | f.init = function () { 23 | for (var i in f) { 24 | if (f[i].init) { 25 | f[i].init(); 26 | debug('event listener:'.blue, i); 27 | } 28 | } 29 | } 30 | 31 | module.exports = f; 32 | -------------------------------------------------------------------------------- /lib/event/sensors.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('sensor'); 2 | 3 | // var io = require('socket.io')(sensorServer); 4 | // var ioClient = require('socket.io-client'); 5 | // var sensors = require('adsensors'); 6 | 7 | // TODO: Remove all reference to sensor sockets. 8 | var activeSensorSockets = []; 9 | 10 | module.exports = { 11 | sensors: activeSensorSockets, 12 | init: function() { 13 | Matrix.events.on('sensor-emit', sensorEventListener); 14 | Matrix.events.on('sensor-init', sensorInitListener); 15 | Matrix.events.on('sensor-close', sensorCloseListener); 16 | Matrix.events.on('sensor-install', Matrix.device.sensor.install); 17 | } 18 | } 19 | 20 | // Open a socker server starting at 8000 21 | // Handles Socket output to events and sockets 22 | function sensorInitListener(sensorInitObj) { 23 | 24 | Matrix.device.sensor.start(sensorInitObj); 25 | 26 | } 27 | 28 | 29 | // send sensor data to listening processes 30 | function sensorEventListener(msg) { 31 | debug('[M]ev(sensor-emit)->'.green, msg); 32 | 33 | // find app listeners 34 | var targetApplications = _.filter(Matrix.activeApplications, function(app) { 35 | debug( app.sensors, '=?'.blue, msg.type ); 36 | 37 | if ( _.has( app, 'sensors' ) && !_.isUndefined(app.sensors) ){ 38 | return ( app.sensors.indexOf(msg.type) > -1 ); 39 | } 40 | }) 41 | 42 | if ( targetApplications.length === 0 ){ 43 | debug('Sensor Event with No Application Listener', msg, Matrix.activeApplications); 44 | } 45 | 46 | var payload = _.omit(msg, 'type'); 47 | 48 | _.each(targetApplications, function(proc) { 49 | debug('Sending', msg.type, 'to', proc.name, payload); 50 | // sends to child process 51 | proc.process.send({ 52 | eventType: 'sensor-emit', 53 | sensor: msg.type, 54 | payload: payload 55 | }); 56 | }); 57 | } 58 | 59 | // Untested 60 | function sensorCloseListener(sensorName) { 61 | var sensor = _.filter(activeSensorSockets, function(s) { 62 | return (s.name === sensorName); 63 | }); 64 | if (_.isEmpty(sensor)) { 65 | warn('Sensor:', sensorName, 'not open') 66 | } else if (sensor.length > 1) { 67 | error('Multiple Versions of Same Sensor Detected. Error Condition.') 68 | } else { 69 | sensor[0].socket.disconnect();; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/event/server.js: -------------------------------------------------------------------------------- 1 | // Events that are sent from the streaming server. 2 | // TODO: Find a better name for this, it handles all CLI events 3 | var debug = debugLog('app'); 4 | 5 | module.exports = { 6 | init: function () { 7 | // service/stream emits these 8 | Matrix.events.on('cli-message', cliMessageHandler); 9 | Matrix.events.on('trigger', triggerHandler); 10 | Matrix.events.on('trigger-group', triggerOut); 11 | Matrix.events.on('auth-ok', Matrix.service.stream.register); 12 | Matrix.events.on('auth-fail', Matrix.service.stream.authError); 13 | Matrix.events.on('register-ok', Matrix.service.stream.registrationSuccessful); 14 | Matrix.events.on('device-heartbeat', Matrix.device.heartbeat.serverCheck); 15 | // not used yet 16 | // Matrix.events.on('user-authentication', userAuthHandler ); 17 | } 18 | } 19 | 20 | function triggerOut(msg) { 21 | Matrix.service.stream.send('trigger-group', msg); 22 | } 23 | 24 | function triggerHandler(msg) { 25 | debug('==== SERVER-MESSAGE (TRIGGER) ==== '.red, msg) 26 | if (_.isString(msg)) { 27 | try { 28 | msg = JSON.parse(msg); 29 | } catch (err) { 30 | error('Server Message : JSON Error', err); 31 | return; 32 | } 33 | } 34 | 35 | var eventName = msg.type; 36 | var payload = msg.payload; 37 | 38 | Matrix.event.app.triggerHandler(payload); 39 | 40 | } 41 | 42 | // handle commands from server / CLI 43 | function cliMessageHandler(msg) { 44 | if (_.isString(msg)) { 45 | try { 46 | msg = JSON.parse(msg); 47 | } catch (err) { 48 | error('Server Message : JSON Error', err); 49 | return; 50 | } 51 | } 52 | 53 | var eventName; 54 | if (_.has(msg.payload, 'type')) { 55 | eventName = msg.payload.type; 56 | } else if (_.has(msg, 'type')) { 57 | eventName = msg.type; 58 | } 59 | 60 | if (_.has(msg, 'payload.payload')) { 61 | msg = msg.payload; 62 | } 63 | 64 | var payload = msg.payload; 65 | 66 | debug('=(CLI>'.red, eventName, payload); 67 | switch (eventName) { 68 | case 'device-reboot': 69 | Matrix.device.system.reboot('CLI', debug); 70 | break; 71 | case 'app-install': 72 | case 'app-update': 73 | Matrix.service.manager.install(payload.url, payload.name, payload.version, function (err) { 74 | console.error(err); 75 | }); 76 | break; 77 | 78 | case 'sensor-install': 79 | // just a string 80 | Matrix.device.sensor.install(payload); 81 | break; 82 | case 'app-uninstall': 83 | Matrix.service.manager.uninstall(payload.name); 84 | break; 85 | case 'app-start': 86 | Matrix.service.manager.start(payload.name); 87 | break; 88 | case 'app-restart': 89 | Matrix.service.manager.restart(payload.name); 90 | break; 91 | case 'app-stop': 92 | Matrix.service.manager.stop(payload.name); 93 | break; 94 | // sent via cli or dashboard 95 | case 'app-config-key-value': 96 | Matrix.service.application.updateConfigKey(payload, function (err) { 97 | debug(err); 98 | }); 99 | break; 100 | case 'device-reregister': 101 | Matrix.service.stream.register(); 102 | break; 103 | case 'app-log': 104 | var log = Matrix.service.manager.getLogs(); 105 | // var follow = payload.follow; 106 | // send log back to streaming server 107 | debug('app-log Out'.yellow, log) 108 | Matrix.service.stream.streamLog(log); 109 | break; 110 | case 'shutdown': 111 | break; 112 | case 'trigger': //This should probably be redirected to triggerHandler 113 | //TODO: Standardize this 114 | if (_.has(payload, 'data')) { 115 | // from CLI 116 | if (payload.data === 'amazing-matrix-ping') { 117 | return Matrix.device.drivers.led.ping(); 118 | } 119 | Matrix.event.app.triggerHandler({ type: 'trigger', eventName: payload.data }); 120 | } else if (_.has(payload, 'value')) { 121 | // sent from input 122 | Matrix.event.app.triggerHandler({ type: 'trigger', eventName: payload.eventName, value: payload.value }); 123 | } else { 124 | Matrix.event.app.triggerHandler({ type: 'trigger', eventName: payload.eventName }); 125 | } 126 | 127 | 128 | break; 129 | default: 130 | 131 | } 132 | 133 | 134 | } -------------------------------------------------------------------------------- /lib/event/service.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('event>service'); 2 | 3 | module.exports = { 4 | init: function() { 5 | Matrix.events.on('service-init', Matrix.device.service.start); 6 | Matrix.events.on('service-emit', serviceEmitHandler); 7 | Matrix.events.on('service-cmd', Matrix.device.service.cmdHandler); 8 | } 9 | }; 10 | 11 | // maps service messages to applications 12 | /** 13 | * 14 | * @param {} msg 15 | * @param {} msg.engine - what engine it uses 16 | * @param {} msg.type - what engine subset it uses 17 | * @param {} msg.enumName - was used for heartbeat, probably not necessary 18 | * @param {} msg.serviceType - for routing on the application layer 19 | */ 20 | 21 | function serviceEmitHandler(msg) { 22 | debug('Begin Service Emit>', msg); 23 | 24 | // use configured services to list apps which detect this object 25 | var targetApplications = _.filter(Matrix.activeApplications, function(app) { 26 | var svs = _.values(app.services); 27 | svs = _.filter(svs, function(s) { 28 | return (s.type === msg.type && s.engine === msg.engine); 29 | }); 30 | return !_.isEmpty(svs); 31 | }); 32 | 33 | debug('Target Applications', _.map(targetApplications, 'name')); 34 | 35 | _.each(targetApplications, function(proc) { 36 | // sends to child process 37 | debug('[service-emit]>' + '(%s)'.blue, proc.name, msg); 38 | var sendObj = { 39 | // routes to lib/service.js 40 | eventType: 'service-emit', 41 | engine: msg.engine, 42 | // routes within lib/service.js 43 | payload: msg.payload 44 | }; 45 | 46 | if (_.has(msg, 'serviceType')) { 47 | sendObj.serviceType = msg.serviceType; 48 | } 49 | if (_.has(msg, 'type')) { 50 | sendObj.type = msg.type; 51 | } 52 | 53 | proc.process.send(sendObj); 54 | }); 55 | } -------------------------------------------------------------------------------- /lib/event/token.js: -------------------------------------------------------------------------------- 1 | // todo: handle expired tokens 2 | 3 | module.exports = { 4 | init : function(){ 5 | Matrix.events.on('token-refresh', function(state){ 6 | log('Token Refresh:'.green); 7 | if ( _.isUndefined( state.user.token ) ){ 8 | console.error('User Token Not Set for (token-refresh)'.red); 9 | } 10 | Matrix.service.token.set({ 11 | deviceToken: state.device.token, 12 | deviceSecret: state.device.secret, 13 | token: state.user.token, 14 | userId: state.user.id 15 | }); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/event/util.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('events') 2 | 3 | module.exports = { 4 | init: function(){ 5 | // Matrix.events.on('no-free-space'); 6 | // Matrix.events.on('space-released'); 7 | Matrix.events.on('device-reboot', function(){ 8 | debug('device-reboot'); 9 | Matrix.device.system.reboot(function(err){ 10 | if (err) console.error(err); 11 | }) 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var version = require(__dirname + '/../package.json').version; 2 | 3 | var f = { 4 | is: '=<[^\\/^]>=', banner: function () { 5 | log(' _ _ ____ ___ ____ _ _ _ '.red); 6 | log(' |\\/| |__| | |__/ | \\/ '.green, '[o__o]'.grey); 7 | log(' | | | | | | \\ | _/\\_ '.blue, 'v' + version + '-' + Matrix.currentHash); 8 | } 9 | }; 10 | 11 | try { 12 | f.currentHash = execSync('git rev-parse --short HEAD').toString().trim(); 13 | } catch (err) { 14 | f.currentHash = 'nohash'; 15 | } 16 | 17 | var files = require('fs').readdirSync(__dirname); 18 | 19 | //remove self 20 | files.splice(files.indexOf(require('path').basename(__filename)), 1); 21 | files.forEach(function (file) { 22 | log('Loading... '.grey, file); 23 | f[file] = require('./' + file); 24 | }); 25 | 26 | module.exports = f; -------------------------------------------------------------------------------- /lib/service/auth.js: -------------------------------------------------------------------------------- 1 | // authenticate is the first step towards getting a token for deviceToken 2 | module.exports = { 3 | authenticate: function(cb) { 4 | debug('secret->'.green, Matrix.deviceSecret) 5 | Matrix.api.device.getToken({ 6 | apiServer: Matrix.apiServer, 7 | deviceId: Matrix.deviceId, 8 | deviceSecret: Matrix.deviceSecret 9 | }, function(err, token) { 10 | if (err) return cb(err); 11 | debug('auth state => '.green, token); 12 | // Matrix.events.emit('token-refresh', state); 13 | cb(null, token); 14 | }); 15 | }, 16 | isSet: function () { 17 | return (!_.isEmpty(Matrix.deviceId) && !_.isEmpty(Matrix.deviceSecret)); 18 | }, 19 | set: function (id, secret, callback) { 20 | debug('Setting up device with id: ', id, ' and secret: ', secret); 21 | Matrix.db.device.insert({ 22 | id: id, 23 | secret: secret, 24 | env: Matrix.env 25 | }, function(err) { 26 | if (!err) { 27 | Matrix.deviceId = id; 28 | Matrix.deviceSecret = secret; 29 | } 30 | callback(err); 31 | }); 32 | } 33 | } -------------------------------------------------------------------------------- /lib/service/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component is a factory function for mapping ZeroMQ sockets to a standard interface. 3 | * This is used by drivers to interface with the hardware. The challenge here is 4 | * utilizing protobufs and 0mq in a standardized way where the drivers can use a 5 | * common format to expose different functionality to address the differences between 6 | * hardware components and mapping those to software requirements. 7 | * 8 | * @example Face detection creates a collection of recognition objects, detection.prepare 9 | * converts that to a single object format which is more easily parsed by humans. 10 | * 11 | * @param options { object } - 12 | * @param options.name - component name 13 | * @param options.send - 0MQ push socket for send 14 | * @param options.ping - 0MQ push socket for ping 15 | * @param options.read - 0MQ sub socket for read 16 | * @param options.error - 0MQ sub socket for error 17 | * @param options.config - 0MQ push socket for config, takes raw protobufs 18 | * 19 | * @var driver - references to the driver file at device/drivers 20 | * 21 | * @method send ( config, [cb] ) - protobuf and send a config object to 0MQ 22 | * @method print ( configProto ) - same as send, sending a raw protobuf buffer 23 | * @method ping () - sends a keepalive 24 | * @method read(cb) - start a listener waiting for a 0MQ message to Forward 25 | * @method read(cb) - start a listener waiting for a 0MQ error message 26 | * @method config(cb) - sends raw config protobuf to 0MQ 27 | * 28 | * @exports Matrix.components[options.name] 29 | * 30 | */ 31 | 32 | // Pass second option for component driver override. ie. servo / gpio 33 | module.exports = function (options, driver) { 34 | var self = this; 35 | 36 | //TODO: Make an env switch for this so we can dev new device drivers easily. 37 | self.debug = debugLog('Component]::' + options.name); 38 | self.name = options.name; 39 | 40 | // stores references to device/driver methods 41 | self.sockets = { 42 | send: options.send, 43 | read: options.read, 44 | ping: options.ping, 45 | error: options.error, 46 | config: options.config 47 | }; 48 | 49 | if (_.isUndefined(options.read)) { 50 | self.sensor = false; 51 | } else { 52 | self.sensor = true; 53 | } 54 | 55 | if (_.isUndefined(driver)) { 56 | self.driver = Matrix.device.drivers[options.name]; 57 | } else { 58 | self.driver = driver; 59 | } 60 | 61 | // send = send config to component. expects object. 62 | self.send = function (config, cb) { 63 | 64 | self.debug(']<-config', _.omit(config, 'send', 'config', 'error', 'ping')); 65 | 66 | // the driver turns it into a protobuf 67 | self.driver.prepare(config, function (configProto) { 68 | // proto -> zeromq 69 | self.debug(']<-configP', configProto); 70 | self.sockets.send.send(configProto); 71 | // continue, initialize sensor handler if necessary 72 | if (_.isFunction(cb)) { 73 | cb(configProto); 74 | } 75 | }); 76 | }; 77 | 78 | //for leds + loader right now, write protos direct to socket 79 | self.print = function (configProto) { 80 | self.debug(']<-print', configProto); 81 | self.sockets.send.send(configProto); 82 | }; 83 | 84 | // sends a keepalive on the channel 85 | self.ping = function () { 86 | self.debug(']<-ping'); 87 | self.sockets.ping.send(''); 88 | }; 89 | 90 | // attach a listener to the socket, pass data to driver read method 91 | // NOTE: read will only be called on a component if a read function is defined in the driver 92 | self.read = function (cb) { 93 | self.debug('] read init'); 94 | self.sockets.read.subscribe(''); 95 | self.sockets.read.on('message', function (data) { 96 | data = self.driver.read(data); 97 | self.debug(']->', data); 98 | cb(data); 99 | }); 100 | }; 101 | 102 | // For async read processing - used with grpc services 103 | self.readAsync = function (cb) { 104 | self.debug('] read async init'); 105 | self.sockets.read.subscribe(''); 106 | self.sockets.read.on('message', function (data) { 107 | self.driver.read(data, cb); 108 | }); 109 | }; 110 | 111 | // similiar to read above, except on error port 112 | self.error = function (cb) { 113 | self.debug('error init'); 114 | self.sockets.error.subscribe(''); 115 | self.sockets.error.on('message', function (data) { 116 | self.debug('->err'.red, data); 117 | if (_.has(self.driver, 'error')) { 118 | cb(self.driver.error(data)); 119 | } else { 120 | cb(data); 121 | } 122 | }); 123 | }; 124 | 125 | // expects proto, unlike send 126 | // used for video services 127 | self.config = function (config, cb) { 128 | self.debug('config'); 129 | self.sockets.config.send(config); 130 | }; 131 | 132 | // expose this component to the global space 133 | Matrix.components[self.name] = self; 134 | return self; 135 | }; -------------------------------------------------------------------------------- /lib/service/cypher.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('cypher'); 2 | var crypto = require('crypto'), 3 | settings = { 4 | algorithm: 'aes-256-ctr', 5 | prefix: '_$CM' 6 | } 7 | 8 | const ivLength = 16; 9 | const keyLength = 32; 10 | 11 | module.exports = { 12 | encrypt: function (text, prefix) { 13 | if (_.isUndefined(text) || text === '') return ''; 14 | if (_.isUndefined(prefix)) prefix = settings.prefix; 15 | var key = Matrix.deviceSecret.substring(0, keyLength); 16 | //This works as long as the deviceId.length < ivLength (12 < 16) 17 | var iv = _.repeat(Matrix.deviceId, (ivLength / Matrix.deviceId.length) + 1).substring(0, ivLength); 18 | 19 | debug('encryption pass: ' + key); 20 | debug('In', text) 21 | var cipher = crypto.createCipheriv(settings.algorithm, key, iv); 22 | var crypted = cipher.update(text, 'utf8', 'hex'); 23 | crypted += cipher.final('hex'); 24 | return prefix+crypted; 25 | }, 26 | decrypt: function (text, prefix) { 27 | if (_.isUndefined(prefix)) prefix = settings.prefix; 28 | var key = Matrix.deviceSecret.substring(0, keyLength); 29 | //This works as long as the deviceId.length < ivLength (12 < 16) 30 | var iv = _.repeat(Matrix.deviceId, (ivLength / Matrix.deviceId.length) + 1).substring(0, ivLength); 31 | var result = text; 32 | debug('(CYPHER) decryption pass: ', key); 33 | if (!text || (prefix !== '' && ( (text.length < prefix.length) || (text.substring(0, prefix.length) != prefix)))) { 34 | debug('(CYPHER) Prefix ' + prefix + ' wasn\'t found, skipping decryption'); 35 | } else { 36 | text = text.substring(prefix.length); 37 | var decipher = crypto.createDecipheriv(settings.algorithm, key, iv); 38 | var dec = decipher.update(text,'hex','utf8'); 39 | dec += decipher.final('utf8'); 40 | result = dec; 41 | } 42 | return result; 43 | }, 44 | settings: settings 45 | } 46 | -------------------------------------------------------------------------------- /lib/service/firebase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Load matrix-firebase module into Matrix.service.firebase; 3 | */ 4 | 5 | var firebase = require('matrix-firebase'); 6 | 7 | module.exports = firebase; 8 | -------------------------------------------------------------------------------- /lib/service/grpc.js: -------------------------------------------------------------------------------- 1 | // We use GRPC to communicate with the MALOS and external services 2 | 3 | var grpc = require('grpc'); 4 | var protos = require('matrix-protos').proto; 5 | 6 | var client = grpc.loadObject(protos, { protobufjsVersion: 6 }).matrix_io; 7 | 8 | // TODO: Parse versions for newest. Provide for stable vs building. 9 | 10 | module.exports = { 11 | client: client, 12 | recognition: client.recognition.v1.RecognitionService, 13 | vision: client.vision.v1.VisionService 14 | }; 15 | -------------------------------------------------------------------------------- /lib/service/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * index loads all files in service and makes their exports available on Matrix.service 3 | * Also calls init() if it exists on the service 4 | */ 5 | var f = {}; 6 | 7 | var debug = debugLog('matrix'); 8 | 9 | // only do files that end in .js 10 | var files = require('fs').readdirSync(__dirname).filter(function (f) { return (f.indexOf('.js') === f.length - 3) }); 11 | 12 | //remove self 13 | files.splice(files.indexOf(require('path').basename(__filename)), 1); 14 | 15 | _.remove(files, function (f) { 16 | // remove invisibles and vim swaps and firebase module 17 | return (f[0] === '.' || f.indexOf('.swp') > -1) 18 | }) 19 | 20 | files.forEach(function (file) { 21 | debug('Loading... service > ' + file.slice(0, -3)); 22 | f[file.slice(0, -3)] = require('./' + file); 23 | }); 24 | 25 | f.init = function () { 26 | for (var i in f) { 27 | // skips firebase init for until after tokens are available 28 | if (f[i].init && i !== 'firebase') { 29 | f[i].init(); 30 | debug('service init:'.blue, i); 31 | } 32 | } 33 | } 34 | 35 | module.exports = f; 36 | -------------------------------------------------------------------------------- /lib/service/lifecycle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | updateLastBootTime: function(){ 3 | Matrix.db.service.update({lastBoot : { $exists: true }}, { lastBoot: Date.now() }, {upsert: true }); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/service/protobuf.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * protobuf.js 4 | * 5 | * Protocol buffers are how MATRIX OS reads and writes to zero mq. They are data structures 6 | * which transform into binary payloads for high efficiency. Currently, MOS uses protobufs 7 | * to communicate with MALOS and Vision Services. In the future, we may use protobufs for 8 | * app data and mxss communication 9 | * 10 | * Note the patch to resolve enums into strings. You're welcome. 11 | * 12 | * @exports malos.driver - hardware sensors, incl zigbee for now 13 | * @exports vision.vision - base class for vision service engine: detection, demographic 14 | * @exports vision.vision_service - methods for integration with vision 15 | * @exports vision.recognition - base class for service 16 | * @exports vision.recognition_service - methods for integration with recognition 17 | */ 18 | 19 | var debug = debugLog('proto') 20 | 21 | // var protobuf = require('protobufjs'); 22 | const Protos = require('matrix-protos').matrix_io; 23 | const grpc = require('grpc'); 24 | 25 | let pExport = {}; 26 | for (let p in Protos) { 27 | 28 | // for keeping track of latest version when iterating 29 | let latestV = 0; 30 | for (let pv in Protos[p]) { 31 | // version integer 32 | let vInt = pv.replace(/\D/g, ''); 33 | 34 | if (vInt > latestV) { 35 | latestV = vInt; 36 | } 37 | 38 | // automatically load newest protos available 39 | if (vInt === latestV) { 40 | 41 | pExport[p] = Protos[p]['v' + latestV]; 42 | 43 | debug('Protobuf ::', p, 'v' + latestV) 44 | 45 | } 46 | } 47 | } 48 | 49 | // grpc is based on proto objects 50 | module.exports = pExport; 51 | 52 | // const fs = require('fs') 53 | // const protosPath = __dirname + '/../../proto'; 54 | // // monkey patch protobuf to add enum strings 55 | // var decode = protobuf.Reflect.Message.Field.prototype.decode; 56 | // protobuf.Reflect.Message.Field.prototype.decode = function () { 57 | // var value = decode.apply(this, arguments); 58 | // if (protobuf.TYPES["enum"] === this.type) { 59 | // var values = this.resolvedType.children; 60 | // for (var i = 0; i < values.length; i++) { 61 | // if (values[i].id == value) { 62 | // return values[i].name; 63 | // } 64 | // } 65 | // // add nested enums 66 | // } else if (protobuf.TYPES["message"] === this.type) { 67 | // _.each(this.resolvedType.children, function (c) { 68 | // var parent = c.name; 69 | // if (protobuf.TYPES["enum"] === c.type) { 70 | // var values = c.resolvedType.children; 71 | // for (var i = 0; i < values.length; i++) { 72 | // if (values[i].id == value[parent]) { 73 | // value[parent] = values[i].name; 74 | // } 75 | // } 76 | // } 77 | // }) 78 | // } 79 | // return value; 80 | // } 81 | 82 | -------------------------------------------------------------------------------- /lib/service/timers.js: -------------------------------------------------------------------------------- 1 | module.exports.init = function () { 2 | 3 | setInterval(function writeMaintenance() { 4 | if (Matrix.sendCache.length > 0) { 5 | let data = _.clone(Matrix.sendCache); 6 | debug(' write interval >>>>'); 7 | Matrix.sendCache = []; 8 | 9 | // appemit data is the only one cached 10 | Matrix.service.stream.send('app-emit', data); 11 | } 12 | }, Matrix.config.writeInterval); 13 | 14 | setInterval(function writeLogs() { 15 | //transform so we can see the hours 16 | let t = 0; 17 | let d = Matrix.dailyDPCount; 18 | 19 | log('=== hourly writes ==='); 20 | for (let i = 0; i < 24; i++) { 21 | log(i, d[i]); 22 | t += d[i]; 23 | } 24 | 25 | log('day total: ', t); 26 | }, Matrix.config.writeLogInterval); 27 | 28 | } -------------------------------------------------------------------------------- /lib/service/token.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('token'); 2 | var jwt = require('jsonwebtoken') 3 | 4 | module.exports = { 5 | get: function(cb) { 6 | // check local db 7 | Matrix.db.service.findOne({ 8 | token: { 9 | $exists: true 10 | } 11 | }, function(err, token){ 12 | if (err) return cb(err); 13 | if (_.isNull(token)) { 14 | log('no token. at all.'.red, null) 15 | Matrix.service.auth.authenticate(function(err, auth){ 16 | if(err) return cb(err); 17 | auth.token = Matrix.service.cypher.decrypt(auth.token); 18 | Matrix.service.token.set(auth, cb); 19 | }); 20 | } else { 21 | debug("[DB]->(token) : ", token); 22 | token.token = Matrix.service.cypher.decrypt(token.token); 23 | 24 | 25 | _.extend(Matrix, token); 26 | debug("[DB]->(token)(decrypt): ", token.token); 27 | cb(null, token.token); 28 | } 29 | }); 30 | }, 31 | set: function(token, cb) { 32 | debug('(token)', token) 33 | 34 | // Set Matrix.deviceSecret Matrix.userId 35 | // Matrix.token Matrix.deviceToken 36 | _.extend(Matrix, token); 37 | 38 | Matrix.db.service.remove({ 39 | token: { 40 | $exists: true 41 | } 42 | }, { 43 | multi: true 44 | }, function(err, count) { 45 | if (err) return cb(err); 46 | debug("(token)->[DB] ", token); 47 | var encryptedToken = Matrix.service.cypher.encrypt(token.token); 48 | debug("(token)->[DB](encrypt) ", encryptedToken); 49 | var decryptedToken = Matrix.service.cypher.decrypt(encryptedToken); 50 | debug("(token)->[DB](decrypt) ", decryptedToken); 51 | 52 | Matrix.db.service.insert({ 53 | deviceToken: token.deviceToken, 54 | deviceSecret: token.deviceSecret, 55 | token: encryptedToken, 56 | userId: token.userId 57 | }, function(err, token){ 58 | if ( _.isFunction(cb)){ 59 | cb(null, decryptedToken) 60 | } 61 | }); 62 | }); 63 | }, 64 | clear: function(cb){ 65 | Matrix.db.service.remove({ 66 | token: { 67 | $exists: true 68 | } 69 | }, { 70 | multi: true 71 | }, cb); 72 | }, 73 | populate: function (cb) { // Fetches device token from service and saves to local DB 74 | Matrix.service.auth.authenticate(function (err, token) { 75 | if (err) return cb(err); 76 | 77 | debug('PopulateToken - OK>'.green, token); 78 | 79 | var decoded = jwt.decode(token); 80 | debug('PopulateToken - decoded>'.yellow, decoded); 81 | 82 | if (!_.has(decoded, 'claims') || !_.has(decoded.claims, 'deviceToken') || 83 | decoded.claims.deviceToken !== true) { 84 | return cb('Bad device token'); 85 | } 86 | 87 | if (decoded.claims.device.id !== Matrix.deviceId) { 88 | return cb('Device Token Device Id does not match deviceId'); 89 | } 90 | 91 | Matrix.deviceToken = token; 92 | Matrix.deviceRecordId = decoded.claims.device.key; 93 | Matrix.userId = decoded.uid; 94 | 95 | debug('processDeviceToken - Matrix.userId>'.green, Matrix.userId); 96 | debug('processDeviceToken - Matrix.deviceRecordId>'.green, Matrix.deviceRecordId); 97 | cb(); 98 | }); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/service/track.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For managing CV Tracking 3 | */ 4 | var debug = debugLog('track') 5 | 6 | // Set of ids returned from tracker 7 | var ids = new Set(); 8 | 9 | // ids saved by key 10 | var info = {}; 11 | module.exports = { 12 | add: function(id){ 13 | debug('+'.blue, id); 14 | ids.add(id.toString()); 15 | info[id] = { descriptors: [] }; 16 | }, 17 | has: function(id){ 18 | // debug('?'.blue, id, ids); 19 | return ids.has(id.toString()) 20 | }, 21 | remove: function(id){ 22 | debug('-'.blue, id); 23 | ids.delete(id.toString()); 24 | debug( ids ); 25 | }, 26 | reset: function(){ 27 | debug('x reset'.red, ids); 28 | ids.clear(); 29 | }, 30 | addDescriptor: function(tId, descriptors){ 31 | debug('+[]>', tId); 32 | if ( info[tId].descriptors.length < 10 ){ 33 | info[tId].descriptors.push(descriptors); 34 | } 35 | }, 36 | getDescriptors: function(tId){ 37 | return info[tId].descriptors; 38 | }, 39 | clearDescriptors: function(tId){ 40 | info[tId].descriptors = []; 41 | }, 42 | dwellLookup: function(id){ 43 | return info[id.toString()].dwell; 44 | }, 45 | sessionLookup: function(id){ 46 | return info[id.toString()].session 47 | }, 48 | getIds: function(){ 49 | return Array.from(ids); 50 | }, 51 | getInfo: function(){ 52 | var idCollection = []; 53 | ids.forEach(function (id) { 54 | idCollection.push({ 55 | id: id.toString(), 56 | session: info[id].session, 57 | dwell: info[id].dwell 58 | }) 59 | }) 60 | return idCollection; 61 | }, 62 | } 63 | -------------------------------------------------------------------------------- /lib/service/tracking.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For managing CV Tracking 3 | */ 4 | var debug = debugLog('track') 5 | 6 | // Set of ids returned from tracker 7 | var ids = new Set(); 8 | 9 | // ids saved by key 10 | var info = {}; 11 | module.exports = { 12 | add: function(id){ 13 | ids.add(id); 14 | }, 15 | has: function(id){ 16 | return ids.has(id) 17 | }, 18 | remove: function(id){ 19 | ids.delete(id); 20 | }, 21 | reset: function(){ 22 | ids.clear(); 23 | }, 24 | dwellLookup: function(id){ 25 | return info[id].dwell; 26 | }, 27 | sessionLookup: function(id){ 28 | return info[id].session 29 | }, 30 | ids: function(){ 31 | return Array.from(ids); 32 | }, 33 | info: function(){ 34 | var idCollection = []; 35 | ids.forEach(function (id) { 36 | idCollection.push({ 37 | id: id, 38 | session: info[id].session, 39 | dwell: info[id].dwell 40 | }) 41 | }) 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /lib/service/ves.js: -------------------------------------------------------------------------------- 1 | var debug = debugLog('ves'); 2 | 3 | module.exports = { 4 | spawn: function( name, options ){ 5 | debug('VES>'.grey) 6 | Matrix.service.firebase.ves.init({ 7 | userId: Matrix.userId, 8 | username: Matrix.username, 9 | deviceId: Matrix.deviceId, 10 | deviceRecordId: Matrix.deviceRecordId, 11 | deviceSecret: Matrix.deviceSecret, 12 | streamingServer: Matrix.streamingServer, 13 | token: Matrix.token, 14 | schema: options.schema 15 | }, function(err, conf){ 16 | if(err) console.error(err); 17 | debug('\n>VES'.grey, conf) 18 | // TODO:recieves server ready configuration from VES 19 | // TODO:update config with options 20 | // TODO:turn on camera and send video to VES server 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/service/zeromq.js: -------------------------------------------------------------------------------- 1 | /* jshint -W058 */ 2 | // For the protos 3 | 4 | 5 | var zmq = require('zeromq') 6 | var debug = debugLog('zeromq'); 7 | 8 | 9 | var heartbeatSocket = zmq.socket('push'); 10 | var heartbeatListener = zmq.socket('pull'); 11 | 12 | module.exports = { 13 | heartbeatMonitor : function(cb){ 14 | heartbeatListener.on('message', cb); 15 | }, 16 | heartbeat: function(msg){ 17 | heartbeatSocket.send(msg); 18 | }, 19 | 20 | deviceInfo: function(cb){ 21 | var request = zmq.socket('req'); 22 | request.on('message', function(resp){ 23 | cb(resp); 24 | }); 25 | 26 | request.connect('tcp://127.0.0.1:' + Matrix.device.port.defaults.info) 27 | request.send(''); 28 | }, 29 | 30 | // Returns zmq connections 31 | registerComponent: function(driver){ 32 | if ( !_.has(driver, 'name') ){ 33 | console.error('Cannot register component without name', driver); 34 | return false; 35 | } 36 | 37 | debug('[zmq][registerComponent>', driver.name) 38 | // use method / feature detection 39 | var config = zmq.socket('push'); 40 | var error = zmq.socket('sub'); 41 | var update = zmq.socket('sub'); 42 | var ping = zmq.socket('push'); 43 | var o = {}; 44 | 45 | o.name = driver.name; 46 | 47 | if ( _.has(driver, 'send') || _.has(driver, 'prepare') || _.has(driver, 'config') ){ 48 | o.send = config.connect('tcp://127.0.0.1:' + Matrix.device.port.get(driver.name).input); 49 | // mirror, this is for detection services which need to reconfig regularly 50 | o.config = o.send; 51 | } 52 | 53 | if ( _.has(driver, 'error')){ 54 | o.error = error.connect('tcp://127.0.0.1:' + Matrix.device.port.get(driver.name).error); 55 | } 56 | 57 | if ( _.has(driver, 'read')){ 58 | o.read = update.connect('tcp://127.0.0.1:' + Matrix.device.port.get(driver.name).read); 59 | } 60 | 61 | if (_.has(driver, 'ping')){ 62 | o.ping = ping.connect('tcp://127.0.0.1:' + Matrix.device.port.get(driver.name).ping); 63 | } 64 | 65 | if (_.has(driver, 'config')){ 66 | o.config = config.connect('tcp://127.0.0.1:' + Matrix.device.port.get(driver.name).config); 67 | } 68 | 69 | return o; 70 | }, 71 | 72 | monitor: function(){ 73 | // Create a socket 74 | // socket = zmq.socket('req'); 75 | // 76 | // 77 | // // Register to monitoring events 78 | // socket.on('connect', function(fd, ep) {console.log('connect, endpoint:', ep);}); 79 | // socket.on('connect_delay', function(fd, ep) {console.log('connect_delay, endpoint:', ep);}); 80 | // socket.on('connect_retry', function(fd, ep) {console.log('connect_retry, endpoint:', ep);}); 81 | // socket.on('listen', function(fd, ep) {console.log('listen, endpoint:', ep);}); 82 | // socket.on('bind_error', function(fd, ep) {console.log('bind_error, endpoint:', ep);}); 83 | // socket.on('accept', function(fd, ep) {console.log('accept, endpoint:', ep);}); 84 | // socket.on('accept_error', function(fd, ep) {console.log('accept_error, endpoint:', ep);}); 85 | // socket.on('close', function(fd, ep) {console.log('close, endpoint:', ep);}); 86 | // socket.on('close_error', function(fd, ep) {console.log('close_error, endpoint:', ep);}); 87 | // socket.on('disconnect', function(fd, ep) {console.log('disconnect, endpoint:', ep);}); 88 | // 89 | // // Handle monitor error 90 | // socket.on('monitor_error', function(err) { 91 | // console.log('Error in monitoring: %s, will restart monitoring in 5 seconds', err); 92 | // setTimeout(function() { socket.monitor(500, 0); }, 5000); 93 | // }); 94 | // 95 | // // Call monitor, check for events every 500ms and get all available events. 96 | // console.log('Start monitoring...'); 97 | // socket.monitor(500, 0); 98 | // socket.connect('tcp://127.0.0.1:9991'); 99 | // 100 | // setTimeout(function() { 101 | // console.log('Stop the monitoring...'); 102 | // socket.unmonitor(); 103 | // }, 20000); 104 | }, 105 | } 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matrix-os", 3 | "version": "0.17.1", 4 | "description": "Portal to device layer for AdMobilize Matrix devices. Includes global component, npm install -g matrix-cli", 5 | "main": "app.js", 6 | "repository": "http://github.com/matrix-io/matrix-os", 7 | "keywords": "matrix,creator,iot,zmq,protobuf,zigbee,zwave,nfc,gyroscope,accellerometer,thread,bluetooth,ir,microphone,robots,smart homes,smart business,security,sensors,maker,firebase", 8 | "homepage": "http://creator.matrix.one", 9 | "bugs": "https://github.com/matrix-io/matrix-os/issues/new", 10 | "scripts": { 11 | "debug": "DEBUG=*,-engine*,-needle*,-*led,-gatt*,-bleno*,-Component*,-bt-characteristic*,-hci* NODE_ENV=dev node index.js", 12 | "debugger": "DEBUG=*,-engine*,-needle,-*led,-gatt,-bleno,-Component,-bt-characteristic,-hci NODE_ENV=dev node --debug index.js", 13 | "debug-test": "DEBUG=*,-engine*,-Component*,-gatt,-bleno,-bt-characteristic,-hci node test/_runner.js", 14 | "debug-watch": "NODE_ENV=dev nodemon index.js -i apps", 15 | "local-debug": "DEBUG=*,-engine*,-Component* NODE_ENV=local node index.js", 16 | "local": "NODE_ENV=local node index.js", 17 | "start": "nodemon --watch lib --watch config", 18 | "test": "node test/_runner.js", 19 | "pr": "open https://bitbucket.org/admobilize/admatrix/pull-request/new", 20 | "deploy-clear": "sudo rm -r db node_modules/matrix-firebase node_modules/matrix-app-config-helper node_modules/matrix-node-sdk node_modules/matrix-eventfilter", 21 | "deploy-copy": "cd node_modules; cp -r ../../matrix-firebase ./matrix-firebase; cp -r ../../matrix-app-config-helper ./matrix-app-config-helper; cp -r ../../matrix-node-sdk ./matrix-node-sdk; cp -r ../../matrix-eventfilter ./matrix-eventfilter; cd ..;", 22 | "deploy-image": "find apps -name '*.matrix' ! -name 'monitor.matrix' -type d -exec rm -r {} +; docker build --no-cache -t matrix/matrix-os .;docker push admobilize/matrix-os", 23 | "local-setup": "cd node_modules; ln -s ../../pi-wifi ./pi-wifi; ln -s ../../matrix-firebase ./matrix-firebase; ln -s ../../matrix-app-config-helper ./matrix-app-config-helper; ln -s ../../matrix-node-sdk ./matrix-node-sdk; ln -s ../../matrix-eventfilter ./matrix-eventfilter; ln -s ../../matrix-protos ./matrix-protos; cd ..", 24 | "sync": "rsync -e ssh --progress -u package.json index.js pi@m:mos/;rsync -ru -e ssh --progress lib proto config apps test pi@m:mos", 25 | "sync-local": "rsync -e ssh --progress -u package.json index.js pi@l:matrix-os/;rsync -ru -e ssh --progress lib proto config apps test pi@l:matrix-os", 26 | "sync-m1": "rsync -e ssh --progress -u package.json index.js pi@m1:matrix-os/;rsync -ru -e ssh --progress lib proto config apps test pi@m1:matrix-os", 27 | "device-diagnostics": "DEBUG=*,-engine*,-gatt,-bleno,-bt-characteristic,-hci START_APP=monitor NODE_ENV=dev node index.js", 28 | "screensaver": "START_APP=life NODE_ENV=dev node index.js", 29 | "clock": "START_APP=clock NODE_ENV=dev node index.js", 30 | "apphost-debug": "docker run -v `pwd`/apps:/apps -it matrix-apphost bash", 31 | "apphost-build": "docker build -t matrix-apphost -f Dockerfile-apphost-arm .", 32 | "upgrade": "npm update matrix-node-sdk matrix-app-config-helper matrix-firebase matrix-eventfilter pi-wifi", 33 | "watch": "nodemon --watch lib --watch config --exec npm run sync", 34 | "watch-apps": "nodemon --watch apps config --exec npm run sync", 35 | "watch-test": "nodemon --watch lib test --exec npm test" 36 | }, 37 | "author": "Sean Canton ", 38 | "contributors": [ 39 | "Sean Canton " 40 | ], 41 | "license": "ISC", 42 | "dependencies": { 43 | "assert-plus": "^1.0.0", 44 | "async": "^1.5.2", 45 | "chai": "^3.5.0", 46 | "colors": "^1.1.2", 47 | "debug": "^2.2.0", 48 | "engine.io-client": "^1.6.8", 49 | "fs-extra": "^0.30.0", 50 | "grpc": "^1.8.4", 51 | "js-yaml": "^3.6.1", 52 | "jsonwebtoken": "^5.7.0", 53 | "lodash": "^4.13.1", 54 | "matrix-app-config-helper": "https://github.com/matrix-io/matrix-app-config-helper/tarball/master", 55 | "matrix-eventfilter": "https://github.com/matrix-io/matrix-eventfilter/tarball/master", 56 | "matrix-firebase": "https://github.com/matrix-io/matrix-firebase/tarball/master", 57 | "matrix-node-sdk": "https://github.com/matrix-io/matrix-node-sdk/tarball/master", 58 | "matrix-protos": "^0.0.17", 59 | "mic": "^2.1.1", 60 | "mocha": "^2.4.5", 61 | "nedb": "https://github.com/matrix-io/nedb/tarball/master", 62 | "network": "^0.3.2", 63 | "optional": "^0.1.4", 64 | "pi-wifi": "^1.1.2", 65 | "protobufjs": "^5.0.1", 66 | "request": "^2.72.0", 67 | "rimraf": "^2.5.2", 68 | "should": "^7.0.2", 69 | "tinycolor2": "^1.3.0", 70 | "tree-kill": "^1.1.0", 71 | "unzip2": "^0.2.5", 72 | "zeromq": "^4.6.0" 73 | }, 74 | "engines": { 75 | "node": ">=0.12.7 <=6.5" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/_old/api.test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-extra'); 2 | var Matrix = require('../app').Matrix; 3 | var should = require('should'); 4 | 5 | 6 | 7 | describe('Matrix<->API', function() { 8 | this.timeout(15000) 9 | it('can recieve a token'); 10 | it('can regenerate a token'); 11 | it('can send data points to API'); 12 | it('can check for updates'); 13 | it('can start a heartbeat'); 14 | }); 15 | -------------------------------------------------------------------------------- /test/_old/app-lifecycle.test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-extra'); 2 | var Matrix = require('../app').Matrix; 3 | var should = require('should'); 4 | 5 | 6 | 7 | describe('Matrix<->App', function() { 8 | var pid, child; 9 | this.timeout(15000) 10 | before(function() { 11 | fs.copySync('./test/test.matrix', './apps/test.matrix'); 12 | }) 13 | // after(function(){ 14 | // //teardown 15 | // fs.removeSync('./apps/test.matrix'); 16 | // }); 17 | describe('Matrix.service.manager[]', function() { 18 | 19 | it('[start] an app', function(done) { 20 | Matrix.service.manager.start('test', function(err, proc) { 21 | proc.pid.should.be.ok(); 22 | pid = proc.pid; 23 | child = child; 24 | done(); 25 | }); 26 | }); 27 | it('[listen] should bind an event', function(done){ 28 | Matrix.service.manager.listen('test', done); 29 | Matrix.events.emit('app-test'); 30 | }); 31 | 32 | //described in test.matrix 33 | describe('test app should', function(){ 34 | it('emits a message'); 35 | it('emits a sensor-init') 36 | it('emits a sensor-emit'); 37 | 38 | }); 39 | 40 | it('[stop] an app', function(done) { 41 | Matrix.service.manager.stop(pid, function() { 42 | done(); 43 | }); 44 | }) 45 | it('[install] will install an app') 46 | it('[uninstall] will uninstall an app') 47 | it('[update] will update an app') 48 | it('[update] will update an app') 49 | }); 50 | 51 | 52 | 53 | 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /test/_old/bluetooth.test.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var expect = require('chai').expect; 3 | 4 | describe('Bluetooth', function() { 5 | 6 | it('Should have a bluetooth device', function(done) { 7 | exec("hcitool dev", function(error, stdout, stderr, command) { 8 | var result = stdout.trim().replace("Devices:",""); 9 | expect(result.length).to.be.above(0); 10 | done(); 11 | }); 12 | }); 13 | 14 | it('Should bluetooth interface be up', function(done) { 15 | exec("sudo hciconfig hci0", function(error, stdout, stderr, command) { 16 | if(stdout.indexOf("UP") > -1){ 17 | done(); 18 | }else{ 19 | done(new Error('bluetooth interface is down')); 20 | } 21 | }); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /test/_old/cdma.test.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var expect = require('chai').expect; 3 | 4 | describe('CDMA', function() { 5 | 6 | it('Should have the CDMA Service running', function(done){ 7 | // exec('ps -fea | grep gpsd | grep -v grep', function(error, stdout, stderr, command) { 8 | // expect(stdout.indexOf('gpsd')).to.be.above(-1); 9 | done(); 10 | // }); 11 | }); 12 | 13 | 14 | it('Should have acquired signal', function(done){ 15 | done(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/_old/configuration.test.js: -------------------------------------------------------------------------------- 1 | describe('config', function(){ 2 | it('should change config based on server'); 3 | }) 4 | -------------------------------------------------------------------------------- /test/_old/dns.test.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var expect = require('chai').expect; 3 | 4 | describe('DNS', function() { 5 | 6 | it('Should be able to resolve a URL', function(done) { 7 | require('dns').resolve('www.google.com',done); 8 | }); 9 | 10 | /* 11 | it('Should be able to ping backup DNS', function(done) { 12 | require('dns').resolve('www.google.com',done); 13 | }); 14 | */ 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/_old/sensor.test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-extra'); 2 | var Matrix = require('../app').Matrix; 3 | var should = require('should'); 4 | 5 | 6 | 7 | describe('Matrix<->Sensors', function() { 8 | this.timeout(15000) 9 | before( function(){ 10 | //'can make a fake sensor for testing' 11 | }); 12 | 13 | it('can initialize a sensor'); 14 | it('cannot initialize a sensor twice'); 15 | it('can read a sensor'); 16 | it('can turn off a sensor'); 17 | it('can hook up a sensor to an app and read the filter'); 18 | it('can close a sensor'); 19 | }); 20 | -------------------------------------------------------------------------------- /test/_old/socket.test.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | var client = net.connect(80, '104.197.117.149', 3 | // var client = net.connect(3000, 'localhost', 4 | function() { //'connect' listener 5 | console.log('connected to server!'); 6 | client.write('world!\r\n'); 7 | }); 8 | client.on('data', function(data) { 9 | console.log(data.toString()); 10 | client.end(); 11 | }); 12 | client.on('end', function() { 13 | console.log('disconnected from server'); 14 | }); 15 | client.on('error', function(err){ 16 | if (err) console.error(err) 17 | }) 18 | -------------------------------------------------------------------------------- /test/_old/wifi.test.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var expect = require('chai').expect; 3 | //var WifiManager = require('pi-wifi'); 4 | 5 | describe('Wifi', function() { 6 | 7 | it('Should have the Wifi Service running', function(done) { 8 | exec("ps -fea | grep wpa_supplicant | grep -v grep", function(error, stdout, stderr, command) { 9 | expect(stdout.indexOf('wpa_supplicant')).to.be.above(-1); 10 | done(); 11 | }); 12 | }); 13 | 14 | it.skip('Should be connected to a wifi network', function(done) { 15 | /*var wifiManager = new WifiManager(); 16 | wifiManager.status(function(status){ 17 | if(status.ssid && status.ip_address){ 18 | done(); 19 | }else{ 20 | done(new Error("Not connected to a wifi network")); 21 | } 22 | });*/ 23 | }); 24 | 25 | 26 | /* //Useless when testing from a wifi network 27 | 28 | 29 | it('Should be able to connect to a network', function(done) { 30 | var wifiManager = new WifiManager(); 31 | this.timeout(60000); 32 | wifiManager.disconnect(); 33 | wifiManager.connect("0",function(){ 34 | wifiManager.status(function(status){ 35 | expect(status.ssid).to.be.exist(); 36 | expect(status.ssid).to.be.ok(); 37 | if(status.ssid && status.ip_address){ 38 | done(); 39 | }else{ 40 | done(new Error("Unable to connect to a known network")); 41 | } 42 | }) 43 | }); 44 | }); 45 | */ 46 | }); 47 | -------------------------------------------------------------------------------- /test/_runner.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'); 3 | _ = require('lodash'); 4 | should = require('should'); 5 | assert = require('chai').assert; 6 | 7 | var Mocha = require('mocha'); 8 | var mocha = new Mocha(); 9 | 10 | 11 | log = console.log; 12 | 13 | // Instantiate a Mocha instance. 14 | 15 | process.env.NODE_ENV = 'dev'; 16 | process.env.DEBUG = '*'; 17 | Matrix = require('../index.js').Matrix; 18 | 19 | 20 | testAppAPI = function(test, cb) { 21 | faketrix = require('child_process').fork('./apps/test.matrix/index.js', { 22 | env: { TEST_MODE: true }, 23 | // silent: true, stdio: 'ignore' 24 | }); 25 | faketrix.send({ test: test }); 26 | faketrix.on('message', function(msg) { 27 | faketrix.kill(); 28 | cb(msg); 29 | }) 30 | } 31 | 32 | setTimeout(function() { 33 | require('child_process').execSync('cp -r ./test/fixtures/test.matrix/ ./apps/test.matrix/') 34 | Matrix.events.on('matrix-ready', function() { 35 | var testDir = __dirname; 36 | 37 | log('ready') 38 | 39 | // Add each .js file to the mocha instance 40 | fs.readdirSync(testDir).filter(function(file) { 41 | // Only keep the .js files 42 | return file.substr(-7) === 'test.js'; 43 | 44 | }).forEach(function(file) { 45 | console.log('Test Loading', file); 46 | mocha.addFile( 47 | path.join(testDir, file) 48 | ); 49 | }); 50 | 51 | // Run the tests. 52 | mocha.run(function(failures) { 53 | process.on('exit', function() { 54 | process.exit(failures); 55 | }); 56 | Matrix.haltTheMatrix(function() { 57 | console.log("Woot Tests Done!~".rainbow) 58 | // require('child_process').execSync('rm -r ./apps/test.matrix') 59 | }); 60 | }); 61 | }) 62 | }, 500) 63 | 64 | 65 | //Define a fn function that exposes the run function 66 | fn = { 67 | run: function (cmd, options, done) { 68 | if (!_.isFunction(done)) { 69 | throw new Error('Run needs a done()'); 70 | } 71 | var args = cmd.split(' '); 72 | var isM = cmd.split(' ').shift(); 73 | if (isM === 'matrix') { 74 | // matrix included, remove 75 | args.shift(); 76 | } 77 | 78 | if (_.isString(options.checks)) { 79 | options.checks = [options.checks] 80 | } 81 | // console.log(args) 82 | var proc = require('child_process').spawn('matrix', args); 83 | 84 | var responseCount = 0; //options.responses.length; 85 | var checkCount = 0; //options.checks.length; 86 | 87 | var respondPrompts = _.map(options.responses, _.first); 88 | // return first for regex map 89 | // => /name|password/ 90 | var respondRegex = new RegExp(_.map(options.responses, _.first).join('|')); 91 | 92 | var targetChecks = (options.hasOwnProperty('checks')) ? options.checks.length : 0; 93 | var targetResps = (options.hasOwnProperty('responses')) ? options.responses.length : 0; 94 | 95 | // global to match multis 96 | var checkRegex = new RegExp(options.checks.join('|'), 'g'); 97 | 98 | // console.log(respondRegex, checkRegex) 99 | // 100 | 101 | var output = []; 102 | var finished = false; 103 | 104 | var handleOutput = function (out) { 105 | out = out.toString(); 106 | output.push(out.split('\n')) 107 | if (process.env.hasOwnProperty('DEBUG')) { 108 | console.log(out); 109 | } 110 | // called for each line of out 111 | var respMatch = out.match(respondRegex); 112 | // console.log(responseCount, '<', targetResps); 113 | // console.log(respMatch, out, '[]=>', respondRegex, targetResps) 114 | if (responseCount < targetResps && options.hasOwnProperty('responses') && !_.isNull(respMatch)) { 115 | var index = respondPrompts.indexOf(respMatch[0]); 116 | console.log(respMatch[0], index, options.responses[index][1]) 117 | proc.stdin.write(options.responses[index][1]); 118 | responseCount += 1; 119 | } 120 | 121 | if (options.hasOwnProperty('checks') && !_.isNull(out.match(checkRegex))) { 122 | checkCount += out.match(checkRegex).length; 123 | } 124 | 125 | // console.log(responseCount, checkCount) 126 | if (!finished && responseCount >= targetResps && checkCount >= targetChecks) { 127 | finished = true; 128 | 129 | if (options.hasOwnProperty('postCheck')) { 130 | // make sure command has time to finish 131 | setTimeout(function () { 132 | // console.log('>>>Function POSTCHECK') 133 | options.postCheck(done, output); 134 | }, 100) 135 | } else { 136 | done(); 137 | } 138 | } 139 | } 140 | // TODO: Debug uses stderr 141 | proc.stdout.on('data', handleOutput); 142 | 143 | // forward errors 144 | proc.stderr.on('data', handleOutput); 145 | 146 | proc.on('close', function (code) { 147 | console.log('finished'.green, cmd, code) 148 | }) 149 | } 150 | } -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | describe('Matrix Applications', function () { 4 | before(function () { 5 | require('child_process').execSync('cp -r ' + __dirname + '/fixtures/test.matrix/ ' + __dirname + '/../apps/test.matrix/'); 6 | }); 7 | 8 | describe('Lifecycle', function () { 9 | // it('should be able to install an app') 10 | it('should be able to update an app'); 11 | describe('start app', function () { 12 | 13 | it('should start an app by name', function (done) { 14 | Matrix.service.manager.start('test', done); 15 | }); 16 | describe('activeApplications management', function () { 17 | var appRecord; 18 | before(function () { 19 | appRecord = _.find(Matrix.activeApplications, { name: 'test' }); 20 | }); 21 | it('should save a reference to the name', function (done) { 22 | appRecord.name.should.equal('test'); 23 | done(); 24 | }); 25 | it('should save a reference to the process', function (done) { 26 | appRecord.should.have.property('process'); 27 | done(); 28 | }); 29 | it('should save a reference to the configuration', function (done) { 30 | appRecord.should.have.property('config'); 31 | appRecord.config.name.should.equal('test'); 32 | done(); 33 | }); 34 | it.skip('should save a reference to the sensors', function (done) { 35 | appRecord.should.have.property('sensors'); 36 | done(); 37 | }); 38 | it.skip('should save a reference to the services', function (done) { 39 | appRecord.should.have.property('services'); 40 | done(); 41 | }); 42 | it('should save a reference to the pid', function (done) { 43 | appRecord.should.have.property('pid'); 44 | done(); 45 | }); 46 | }); 47 | 48 | 49 | describe('event management', function () { 50 | var appRecord; 51 | before(function () { 52 | appRecord = _.find(Matrix.activeApplications, { name: 'test' }); 53 | }); 54 | 55 | it('should have listeners attached to process', function (done) { 56 | appRecord.process.should.have.property('handlers'); 57 | done(); 58 | }); 59 | 60 | 61 | it('should have listeners on the global event emitter for crosstalk', function (done) { 62 | if (Matrix.events.hasOwnProperty('eventNames')) { 63 | // Node 6+ 64 | Matrix.events.eventNames().should.matchAny(/app-message|app-test-message/); 65 | } else { 66 | // Node <5 67 | Matrix.events.listeners('app-message').length.should.equal(1); 68 | Matrix.events.listeners('app-test-message').length.should.equal(1); 69 | } 70 | done(); 71 | }); 72 | 73 | it.skip('should rate limit app.send() events', function (done) { 74 | testAppApi('rate-test'); 75 | this.timeout(6000); 76 | setInterval(function () { 77 | console.log(Matrix.rateCache); 78 | }, 1000); 79 | setTimeout(done, 5000); 80 | }); 81 | }); 82 | 83 | describe('stop an app and manage applications', function () { 84 | 85 | before(function (done) { 86 | Matrix.service.manager.stop('test', done); 87 | }); 88 | it('stopped apps should not be in activeApplications', function () { 89 | var appRecord = _.find(Matrix.activeApplications, { name: 'test' }); 90 | assert(typeof appRecord, 'undefined'); 91 | }); 92 | 93 | }); 94 | 95 | describe('able to synchronize application state', function () { 96 | // this does not have a firebase record 97 | before(function (done) { 98 | Matrix.service.manager.start('test', (err, app) => { 99 | if (err) return done(err); 100 | var pid = app.pid; 101 | require('child_process').execSync('kill -9 ' + pid); 102 | done(); 103 | }); 104 | }); 105 | before(function (done) { 106 | Matrix.service.manager.syncAppActivity(done); 107 | }); 108 | it('should restart applications which are active in firebase', function () { 109 | assert.equal(Matrix.activeApplications.length, 1); 110 | }); 111 | after(function (done) { 112 | Matrix.service.manager.stop('test', done); 113 | }); 114 | }); 115 | 116 | // this does strange things in travis, it seems to be unable to find the test file 117 | // removing for now 118 | describe.skip('crash management', function () { 119 | var appRecord; 120 | this.timeout(5000); 121 | before(function (done) { 122 | Matrix.service.manager.start('test', done); 123 | }); 124 | it('should remove an app from activeApplications on crash', function (done) { 125 | appRecord = _.find(Matrix.activeApplications, { name: 'test' }); 126 | appRecord.process.send({ test: 'crash' }); 127 | setTimeout(function () { 128 | appRecord = _.find(Matrix.activeApplications, { name: 'test' }); 129 | console.log(Matrix.activeApplications); 130 | assert(typeof appRecord, 'undefined'); 131 | done(); 132 | }, 500); 133 | }); 134 | }); 135 | 136 | 137 | }); 138 | 139 | 140 | }); 141 | after(function () { 142 | require('child_process').execSync('rm -r ' + __dirname + '/../apps/test.matrix'); 143 | }); 144 | }); -------------------------------------------------------------------------------- /test/auth.test.js: -------------------------------------------------------------------------------- 1 | describe('Authentication', function(){ 2 | it('should be able to register with the streaming server'); 3 | it('should be able to refresh a device token'); 4 | }) 5 | -------------------------------------------------------------------------------- /test/base.test.js: -------------------------------------------------------------------------------- 1 | describe('Matrix Base', function() { 2 | describe('Environment Variables', function() { 3 | it('should have MATRIX_API_SERVER', function(done) { 4 | _.isUndefined(Matrix.apiServer).should.be.false; 5 | done(); 6 | }); 7 | it('should have MATRIX_STREAMING_SERVER', function(done) { 8 | _.isUndefined(Matrix.streamingServer).should.be.false; 9 | done(); 10 | }); 11 | it('should have MATRIX_DEVICE_ID', function(done) { 12 | _.isUndefined(Matrix.deviceId).should.be.false; 13 | done(); 14 | }); 15 | it('should have MATRIX_DEVICE_NAME', function(done) { 16 | _.isUndefined(Matrix.deviceName).should.be.false; 17 | done(); 18 | }); 19 | }); 20 | 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /test/bin.test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-extra'); 2 | var binPath = './bin/'; 3 | var exec = require('child_process').exec; 4 | var DataStore = require('nedb'); 5 | var path = require('path'); 6 | var root = path.join(__dirname,'../'); 7 | var deviceDbPath = root + 'db/device.db' 8 | var deviceDB = new DataStore({ 9 | filename: deviceDbPath, 10 | autoload: true 11 | }); 12 | 13 | 14 | describe('Bin', function () { 15 | describe('Device config', function () { 16 | var deviceFilePath = './db/device.db'; 17 | var deviceBackupFilePath = './test/test.matrix'; 18 | 19 | before(function () { 20 | //Check Backup Files 21 | var currentFileStat, err; 22 | 23 | try { 24 | currentFileStat = fs.statSync(deviceFilePath); 25 | } catch (error) { 26 | err = error; 27 | } 28 | if (!err && currentFileStat) { 29 | fs.copySync(deviceFilePath, deviceBackupFilePath); 30 | } 31 | }); 32 | 33 | after(function () { 34 | var bkFileStat, err; 35 | //Restore Backup and remove File 36 | try { 37 | bkFileStat = fs.statSync(deviceBackupFilePath); 38 | } catch (error) { 39 | err = error; 40 | } 41 | if (!err && bkFileStat) { 42 | fs.copySync(deviceBackupFilePath, deviceFilePath); 43 | fs.unlinkSync(deviceBackupFilePath); 44 | } 45 | }); 46 | 47 | describe('Reset device', function () { 48 | it('should reset the configuration for all environments', function (done) { 49 | exec('node ' + binPath + 'reset.js', function (error, stdout, stderr) { 50 | if (!error) { 51 | deviceDB.loadDatabase(function (err) { 52 | var query = { env: { $exists: true } }; 53 | deviceDB.find(query, function (err, result) { 54 | if (err) { 55 | done(err); 56 | } 57 | //check if there is any data left for any envs 58 | if (result.length === 0) { 59 | done(); 60 | } else { 61 | done(new Error('Reset failed')); 62 | } 63 | }); 64 | }); 65 | }else{ 66 | done(error); 67 | } 68 | }); 69 | }); 70 | }); 71 | 72 | describe('Set device', function () { 73 | var env = 'dev'; 74 | var id = 'ThisIsMyTestId'; 75 | var secret = 'DeviceSecretAtItsBest'; 76 | 77 | describe('Correct command', function () { 78 | it('should be able to set the device configuration for a specific environment', function (done) { 79 | exec( 'node ' + binPath + 'set.js ' + env + ' ' + id + ' ' + secret, function (error, stdout, stderr) { 80 | if (!error) { 81 | deviceDB.loadDatabase(function (err) { 82 | var query = {'env': env}; 83 | deviceDB.find(query, function (err, result) { 84 | if (err) { 85 | done(err); 86 | } 87 | //Use NeDB find to check if there's data for that environment 88 | if (result.length != 0) { 89 | done(); 90 | } else { 91 | done(new Error('Set device failed')); 92 | } 93 | }); 94 | }); 95 | }else{ 96 | done(error); 97 | } 98 | }); 99 | }); 100 | 101 | it('should reset the configuration for a specific environment', function (done) { 102 | exec( 'node ' + binPath + 'reset.js ' + env, function (error, stdout, stderr) { 103 | if (!error) { 104 | var query = {'env': env}; 105 | deviceDB.loadDatabase(function (err) { 106 | deviceDB.find(query, function (err, result) { 107 | if (err) { 108 | done(err); 109 | } 110 | //check if there is any data left for env 111 | if (result.length === 0) { 112 | done(); 113 | } else { 114 | done(new Error('Reset the configuration for a specific environment failed')); 115 | } 116 | }); 117 | }); 118 | }else{ 119 | done(error); 120 | } 121 | }); 122 | }); 123 | }); 124 | 125 | describe('Wrong parameters', function () { 126 | it('should not set device configuration if env isn\'t dev|rc|production', function (done) { 127 | exec('node ' + binPath + 'set.js ' + 'aMadeUpEnv' + ' ' + id + ' ' + secret, function (error, stdout, stderr) { 128 | if (!error) { 129 | done(new Error('Wrong parameters validation failed')); 130 | }else{ 131 | done(); 132 | } 133 | }); 134 | }); 135 | 136 | it('should not set device configuration if missing env parameter', function (done) { 137 | exec('node ' + binPath + 'set.js ' + id + ' ' + secret, function (error, stdout, stderr) { 138 | if (!error) { 139 | done(new Error('Wrong parameters validation failed')); 140 | }else{ 141 | done(); 142 | } 143 | }); 144 | }); 145 | 146 | it('should not set device configuration if missing device id', function (done) { 147 | exec('node ' + binPath + 'set.js ' + env + ' ' + secret, function (error, stdout, stderr) { 148 | if (!error) { 149 | done(new Error('Wrong parameters validation failed')); 150 | }else{ 151 | done(); 152 | } 153 | }); 154 | }); 155 | 156 | it('should not set device configuration if missing device secret', function (done) { 157 | exec( 'node ' + binPath + 'set.js ' + env + ' ' + id, function (error, stdout, stderr) { 158 | if (!error) { 159 | done(new Error('Wrong parameters validation failed')); 160 | }else{ 161 | done(); 162 | } 163 | }); 164 | }); 165 | 166 | it('should not set device configuration if missing multiple parameters', function (done) { 167 | exec('node ' + binPath + 'set.js', function (error, stdout, stderr) { 168 | if (!error) { 169 | done(new Error('Wrong parameters validation failed')); 170 | }else{ 171 | done(); 172 | } 173 | }); 174 | }); 175 | }); 176 | }); 177 | }); 178 | }); -------------------------------------------------------------------------------- /test/component.test.js: -------------------------------------------------------------------------------- 1 | var zeromq = require('zeromq'); 2 | var protobuf = require('protobufjs'); 3 | 4 | //TODO this whole tests needs to use Driver.DriverInfo.name from matrix-protos 5 | describe('component', function () { 6 | var component, TestProto, malosSend, malosRead, malosPing, malosError, TestProto; 7 | 8 | before(function () { 9 | 10 | var TestBuilder = protobuf.loadProtoFile('./test/fixtures/test.proto'); 11 | TestProto = TestBuilder.build('matrix_test'); 12 | //fake device 13 | Matrix.device.port.defaults.test = 11370; 14 | 15 | // fake driver 16 | Matrix.device.drivers.test = { 17 | name: 'test', 18 | init: function () { }, 19 | read: function (buffer) { 20 | return new TestProto.Test.decode(buffer) 21 | }, 22 | prepare: function (option, cb) { 23 | var t = new TestProto.Test; 24 | _.extend(t, option) 25 | cb(TestProto.Test.encode(t).finish()); 26 | }, 27 | ping: function () { 28 | Matrix.components.test.ping(); 29 | }, 30 | config: function (config) { 31 | Matrix.components.test.config(config) 32 | }, 33 | error: function (err) { 34 | return err.toString(); 35 | } 36 | } 37 | 38 | // makes component available 39 | var mqs = Matrix.service.zeromq.registerComponent(Matrix.device.drivers.test); 40 | 41 | component = new Matrix.service.component(mqs); 42 | 43 | //fake malos 44 | malosSend = zeromq.socket('pull'); 45 | malosError = zeromq.socket('pub'); 46 | malosRead = zeromq.socket('pub'); 47 | malosPing = zeromq.socket('pull'); 48 | 49 | malosSend.bindSync('tcp://127.0.0.1:' + Matrix.device.port.get('test').send); 50 | malosPing.bindSync('tcp://127.0.0.1:' + Matrix.device.port.get('test').ping); 51 | 52 | }) 53 | 54 | describe('Component', function () { 55 | it('should register component on Matrix.components', function () { 56 | Matrix.components.should.have.property('test') 57 | }); 58 | 59 | it('should identify as a sensor', function () { 60 | Matrix.components.test.sensor.should.equal(true); 61 | }) 62 | 63 | describe('Component methods', function () { 64 | it('should have a send method', function () { 65 | Matrix.components.test.should.have.property('send') 66 | }) 67 | it('should have a read method', function () { 68 | Matrix.components.test.should.have.property('read') 69 | }) 70 | it('should have a ping method', function () { 71 | Matrix.components.test.should.have.property('ping') 72 | }) 73 | it('should have a error method', function () { 74 | Matrix.components.test.should.have.property('error') 75 | }) 76 | it('should have a config method', function () { 77 | Matrix.components.test.should.have.property('config') 78 | }) 79 | }) 80 | 81 | describe('functional', function () { 82 | 83 | it('should implement ping', function (done) { 84 | var d = _.once(done); 85 | malosPing.on('message', function (msg) { 86 | d(); 87 | }) 88 | Matrix.components.test.ping(); 89 | }); 90 | 91 | it.skip('should implement send', function (done) { 92 | var d = _.once(done); 93 | malosSend.on('message', function (msg) { 94 | var decode = new TestProto.Test.decode(msg); 95 | decode.should.property('test', true); 96 | d(); 97 | }) 98 | 99 | Matrix.components.test.send({ test: true }); 100 | }); 101 | 102 | it.skip('should implement print', function (done) { 103 | malosSend.connect('tcp://127.0.0.1:' + Matrix.device.port.get('test').input); 104 | var d = _.once(done); 105 | 106 | malosSend.on('message', function (msg) { 107 | var decode = new TestProto.Test.decode(msg); 108 | decode.should.property('test', true); 109 | d(); 110 | }) 111 | 112 | //print takes raw protobufs 113 | var t = new TestProto.Test; 114 | t.test = true; 115 | Matrix.components.test.print(TestProto.Test.encode(t).finish()); 116 | }) 117 | 118 | it.skip('should implement read', function (done) { 119 | Matrix.components.test.read(function (msg) { 120 | msg.should.property('test', true); 121 | done(); 122 | }); 123 | 124 | malosRead.bindSync('tcp://127.0.0.1:' + Matrix.device.port.get('test').read); 125 | //print takes raw protobufs 126 | var t = new TestProto.Test; 127 | t.test = true; 128 | setTimeout(function () { 129 | malosRead.send(TestProto.Test.encode(t).finish()); 130 | }, 500) 131 | }) 132 | 133 | // TODO: Finish this, not sure why not working 134 | it('should implement error', function (done) { 135 | Matrix.components.test.error(function (msg) { 136 | msg.should.equal('test'); 137 | done(); 138 | }); 139 | 140 | 141 | malosError.bindSync('tcp://127.0.0.1:' + Matrix.device.port.get('test').error); 142 | // var malosSub = zeromq.socket('sub') 143 | // malosSub.connect('tcp://127.0.0.1:' + Matrix.device.port.get('test').error); 144 | // malosSub.subscribe(''); 145 | // malosSub.on('message', function(d){ 146 | // console.log('TEST DATA', d.toString()); 147 | // }) 148 | setTimeout(function () { 149 | malosError.send('test'); 150 | }, 500) 151 | 152 | }) 153 | }); 154 | 155 | }) 156 | }) 157 | -------------------------------------------------------------------------------- /test/config.test.js: -------------------------------------------------------------------------------- 1 | // describe('Configuration via Firebase', function(){ 2 | // 3 | // var app; 4 | // 5 | // before(function(){ 6 | // require('child_process').execSync('cp -r '+ __dirname + '/fixtures/test.matrix/ '+ __dirname +'/../apps/test.matrix/'); 7 | // app = new Matrix.service.application.Application('test'); 8 | // }) 9 | // 10 | // after(function(){ 11 | // require('child_process').execSync('rm -r '+__dirname+'/../apps/test.matrix'); 12 | // }) 13 | // 14 | // //app.getConfigFromFile(cb) 15 | // it('can populate from file', function(done){ 16 | // app.getConfigFromFile(); 17 | // app.config.test.should.equal(true); 18 | // done(); 19 | // }) 20 | // it('should save an application configuration to firebase', function(done){ 21 | // app.syncConfig(app.config); 22 | // app.config = {}; 23 | // done(); 24 | // }); 25 | // 26 | // it('can populate from firebase', function(done){ 27 | // app.getConfig(function(err, resp){ 28 | // app.config.test.should.equal(true); 29 | // done(); 30 | // }); 31 | // }) 32 | // it('should provide a configuration to an application'); 33 | // it('should update a configuration based on local config.yaml'); 34 | // it('should emit a `config-update` when config is updated'); 35 | // it('should restart an application when the active configuration is changed'); 36 | // // it('should assign an application to a device in the configuration'); 37 | // }) 38 | -------------------------------------------------------------------------------- /test/encrypt.test.js: -------------------------------------------------------------------------------- 1 | describe.skip('Encryption', function() { 2 | var testMessage = "Hello World"; 3 | var currentDeviceId = Matrix.deviceId; 4 | var cypher = Matrix.service.cypher; 5 | var encryptedTestMessage; 6 | 7 | it('should be able to encrypt a string', function(done) { 8 | var result = cypher.encrypt(testMessage); 9 | encryptedTestMessage = result; 10 | if (result !== testMessage) { 11 | done(); 12 | } else { 13 | done("Message is not encrypted"); 14 | } 15 | }); 16 | it('should be able to decrypt a string', function(done) { 17 | var result = cypher.decrypt(encryptedTestMessage); 18 | if (result === testMessage) { 19 | done(); 20 | } else { 21 | done("Unable to decrypt sample message"); 22 | } 23 | }); 24 | it('should add a prefix to encrypted strings', function(done) { 25 | var result = cypher.encrypt(testMessage); 26 | if (result.substring(0, cypher.settings.prefix.length) === cypher.settings.prefix) { 27 | done(); 28 | } else { 29 | done("Encrypted message doesn't contain the prefix in it"); 30 | } 31 | }); 32 | it('should not decrypt unencrypted messages', function(done) { 33 | var result = cypher.decrypt(testMessage); 34 | console.log(result, testMessage) 35 | if (result === testMessage) { 36 | done(); 37 | } else { 38 | done("Decrypted message doesn't match the original message"); 39 | } 40 | }); 41 | after(function() { 42 | Matrix.deviceId = currentDeviceId; 43 | }); 44 | }); -------------------------------------------------------------------------------- /test/event.test.js: -------------------------------------------------------------------------------- 1 | describe('Event System', function(){ 2 | this.timeout(15000); 3 | it('should attach app event listeners', function(done){ 4 | Matrix.events.listeners('app-emit').should.have.lengthOf(1); 5 | Matrix.events.listeners('app-log').should.have.lengthOf(1); 6 | done(); 7 | }); 8 | it('should attach sensor event listeners', function(done){ 9 | Matrix.events.listeners('sensor-emit').should.have.lengthOf(1); 10 | Matrix.events.listeners('sensor-init').should.have.lengthOf(1); 11 | Matrix.events.listeners('sensor-close').should.have.lengthOf(1); 12 | done(); 13 | }); 14 | it('should attach server event listeners', function(done){ 15 | Matrix.events.listeners('cli-message').should.have.lengthOf(1); 16 | done(); 17 | }); 18 | it('should attach token event listeners', function(done){ 19 | Matrix.events.listeners('token-refresh').should.have.lengthOf(1); 20 | done(); 21 | }); 22 | it('should attach util event listeners', function(done){ 23 | Matrix.events.listeners('device-reboot').should.have.lengthOf(1); 24 | done(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/test.matrix/app.js: -------------------------------------------------------------------------------- 1 | // // typed send 2 | // matrix.type('testType').send({ foo: 1 }) 3 | // 4 | // // default typed send 5 | // matrix.send({foo:2}) 6 | // 7 | // //send global message 8 | // matrix.emit('foo') 9 | // 10 | // //send app specific message 11 | // matrix.emit('otherapp', 'otherfoo') 12 | // 13 | // //send namespaced message 14 | // matrix.emit('otherapp','nameofevent', 'namedfoo') 15 | 16 | 17 | matrix = require('../matrix.js'); 18 | 19 | var c = JSON.parse(require('fs').readFileSync(__dirname + '/config.json')); 20 | // matrix.startApp('test', c); 21 | 22 | process.on('message', function (msg) { 23 | if (!msg.hasOwnProperty('test')) { 24 | return console.error('no test passed to faketrix'); 25 | } 26 | switch (msg.test) { 27 | case 'led': 28 | matrix.led('red').render(); 29 | break; 30 | case 'face': 31 | matrix.service('face').start(); 32 | break; 33 | case 'recog-start': 34 | matrix.service('recognition').start(); 35 | break; 36 | case 'recog-train': 37 | matrix.service('recognition').train('test'); 38 | break; 39 | case 'recog-untrain': 40 | matrix.service('recognition').untrain('test'); 41 | break; 42 | case 'recog-stop': 43 | matrix.service('recognition').stop(); 44 | break; 45 | case 'demographics': 46 | matrix.service('demographics').start(); 47 | break; 48 | case 'vehicle': 49 | matrix.service('vehicle').start(); 50 | break; 51 | case 'palm': 52 | matrix.service('palm').start(); 53 | break; 54 | case 'pinch': 55 | matrix.service('pinch').start(); 56 | break; 57 | case 'fist': 58 | matrix.service('fist').start(); 59 | break; 60 | case 'thumb-up': 61 | matrix.service('thumb-up').start(); 62 | break; 63 | case 'temperature': 64 | matrix.sensor('temperature'); 65 | break; 66 | case 'altitude': 67 | matrix.sensor('altitude'); 68 | break; 69 | case 'humidity': 70 | matrix.sensor('humidity'); 71 | break; 72 | case 'gyroscope': 73 | matrix.sensor('gyroscope'); 74 | break; 75 | case 'accellerometer': 76 | matrix.sensor('accellerometer'); 77 | break; 78 | case 'nfc': 79 | matrix.sensor('nfc'); 80 | break; 81 | case 'pressure': 82 | matrix.sensor('pressure'); 83 | break; 84 | case 'uv': 85 | matrix.sensor('uv'); 86 | break; 87 | case 'ir': 88 | matrix.sensor('ir'); 89 | break; 90 | case 'send': 91 | matrix.type('test').send({ foo: 1, bar: 1.12 }); 92 | break; 93 | case 'sendzerofloat': 94 | matrix.type('test').send({ foo: 1, bar: 0.0 }); 95 | break; 96 | case 'talk': 97 | // //send global message 98 | matrix.emit('foo'); 99 | // 100 | // //send app specific message 101 | matrix.emit('otherapp', 'otherfoo'); 102 | // 103 | // //send namespaced message 104 | matrix.emit('otherapp', 'nameofevent', 'namedfoo'); 105 | break; 106 | case 'crash': 107 | throw new Error('this is not a real error'); 108 | case 'rate-test': 109 | matrix.type('test').send({ foo: 1 }); 110 | matrix.type('test').send({ foo: 1 }); 111 | matrix.type('test').send({ foo: 1 }); 112 | matrix.type('test').send({ foo: 1 }); 113 | matrix.type('test').send({ foo: 1 }); 114 | break; 115 | case 'bulk-send': 116 | matrix.type('test').send([{ foo: 1, foo: 1, foo: 1, foo: 1, foo: 1 }]) 117 | break; 118 | default: 119 | break; 120 | } 121 | }); -------------------------------------------------------------------------------- /test/fixtures/test.matrix/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "dataTypes": { 4 | "test": { 5 | "foo": "integer", 6 | "bar": "float" 7 | } 8 | }, 9 | "description": "none", 10 | "configVersion": 2, 11 | "keywords": "test", 12 | "sensors": [ 13 | "temperature" 14 | ], 15 | "integrations": [ 16 | "gpio" 17 | ], 18 | "events": [ 19 | "global" 20 | ], 21 | "services": { 22 | "faceSvc": { 23 | "engine": "detection", 24 | "type": "face" 25 | }, 26 | "recogSvc": { 27 | "engine": "recognition", 28 | "type": "face" 29 | }, 30 | "vehicleSvc": { 31 | "engine": "recognition", 32 | "type": "vehicle" 33 | }, 34 | "demoSvc": { 35 | "engine": "recognition", 36 | "type": "demographics" 37 | }, 38 | "palmSvc": { 39 | "engine": "detection", 40 | "type": "palm" 41 | }, 42 | "thumbSvc": { 43 | "engine": "detection", 44 | "type": "thumb-up" 45 | }, 46 | "pinchSvc": { 47 | "engine": "detection", 48 | "type": "pinch" 49 | }, 50 | "fistSvc": { 51 | "engine": "detection", 52 | "type": "fist" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/fixtures/test.matrix/config.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | dataTypes: 3 | test: 4 | foo: integer 5 | bar: float 6 | description: none 7 | configVersion: 2 8 | keywords: test 9 | 10 | sensors: 11 | - temperature 12 | 13 | integrations: 14 | - gpio 15 | 16 | events: 17 | - global 18 | 19 | services: 20 | faceSvc: 21 | engine: detection 22 | type: face 23 | recogSvc: 24 | engine: recognition 25 | type: face 26 | vehicleSvc: 27 | engine: recognition 28 | type: vehicle 29 | demoSvc: 30 | engine: recognition 31 | type: demographics 32 | palmSvc: 33 | engine: detection 34 | type: palm 35 | thumbSvc: 36 | engine: detection 37 | type: thumb-up 38 | pinchSvc: 39 | engine: detection 40 | type: pinch 41 | fistSvc: 42 | engine: detection 43 | type: fist -------------------------------------------------------------------------------- /test/fixtures/test.matrix/index.js: -------------------------------------------------------------------------------- 1 | var appName = require('path').basename(__dirname).split('.')[0]; 2 | 3 | matrix = require('../matrix.js') 4 | 5 | matrix.startApp(appName); 6 | 7 | require('./app.js'); 8 | -------------------------------------------------------------------------------- /test/fixtures/test.matrix/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matrix-test-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package matrix_test; 4 | 5 | message Test { 6 | bool test = 1; 7 | } 8 | -------------------------------------------------------------------------------- /test/stdtestimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matrix-io/matrix-os/004e8e4d0a9e70983b66bfb482c18fe52612384e/test/stdtestimage.jpg --------------------------------------------------------------------------------