├── interfaces ├── .DS_Store ├── referenceExample │ ├── .DS_Store │ └── index.js ├── arduinoUno │ ├── Vuforia-Spatial-Arduino-Library.zip │ ├── config.html │ └── index.js ├── mozillaWot │ ├── index.js │ ├── README.md │ ├── MozillaWotInterface.js │ └── config.html ├── LegoHub │ ├── README.md │ ├── style.css │ ├── config.html │ └── index.js └── philipsHue │ ├── README.md │ ├── index.js │ └── config.html ├── .gitignore ├── package.json ├── .eslintrc.js ├── index.md ├── README.md └── LICENSE /interfaces/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptcrealitylab/vuforia-spatial-basic-interfaces-addon/HEAD/interfaces/.DS_Store -------------------------------------------------------------------------------- /interfaces/referenceExample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptcrealitylab/vuforia-spatial-basic-interfaces-addon/HEAD/interfaces/referenceExample/.DS_Store -------------------------------------------------------------------------------- /interfaces/arduinoUno/Vuforia-Spatial-Arduino-Library.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptcrealitylab/vuforia-spatial-basic-interfaces-addon/HEAD/interfaces/arduinoUno/Vuforia-Spatial-Arduino-Library.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### macOS template 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | /node_modules/* 9 | 10 | ### Example user template template 11 | ### Example user template 12 | 13 | # IntelliJ project files 14 | .idea 15 | *.iml 16 | out 17 | gen 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuforiaSpatialExperiments", 3 | "version": "0.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "Valentin Heun )", 9 | "license": "MPL 2.0", 10 | "dependencies": { 11 | "serialport": "^9.0.0", 12 | "wedoboostpoweredup": "^2.1.9" 13 | }, 14 | "devDependencies": { 15 | "eslint": "^7.12.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /interfaces/mozillaWot/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | const server = require('../../../../libraries/hardwareInterfaces'); 8 | const MozillaWotInterface = require('./MozillaWotInterface.js'); 9 | 10 | let settings = server.loadHardwareInterface(__dirname); 11 | 12 | exports.enabled = settings('enabled'); 13 | exports.configurable = true; // can be turned on/off/adjusted from the web frontend 14 | exports.settings = {}; 15 | 16 | if (exports.enabled) { 17 | server.enableDeveloperUI(true); 18 | 19 | /** 20 | * runs once, adds and clears the IO points 21 | */ 22 | async function setup() { // eslint-disable-line no-inner-declarations 23 | let wotInterface = new MozillaWotInterface(); 24 | exports.settings = wotInterface.exportedSettings; 25 | 26 | try { 27 | await wotInterface.pair(); 28 | await wotInterface.discoverThings(); 29 | } catch(e) { 30 | console.error('MozillaWotInterface error', e); 31 | } 32 | } 33 | 34 | setup(); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'commonjs': true, 4 | 'es6': true, 5 | 'node': true, 6 | }, 7 | 'extends': 'eslint:recommended', 8 | 'globals': { 9 | 'Atomics': 'readonly', 10 | 'SharedArrayBuffer': 'readonly' 11 | }, 12 | 'parserOptions': { 13 | 'ecmaVersion': 2018 14 | }, 15 | 'rules': { 16 | 'indent': [ 17 | 'warn', 18 | 4 19 | ], 20 | 'linebreak-style': [ 21 | 'error', 22 | 'unix' 23 | ], 24 | 'quotes': [ 25 | 'warn', 26 | 'single' 27 | ], 28 | 'semi': [ 29 | 'warn', 30 | 'always' 31 | ], 32 | 'comma-spacing': [ 33 | 'warn', {before: false, after: true} 34 | ], 35 | 'key-spacing': 'warn', 36 | 'keyword-spacing': 'warn', 37 | 'no-trailing-spaces': 'warn', 38 | 'brace-style': ['warn', '1tbs', {allowSingleLine: true}], 39 | 'space-before-blocks': 'warn', 40 | 'space-infix-ops': 'warn', 41 | 'no-prototype-builtins': 'off', 42 | 'no-unused-vars': ['warn', {argsIgnorePattern: '^_', varsIgnorePattern: '^_'}], 43 | 'no-redeclare': 'warn', 44 | 'no-inner-declarations': 'warn', 45 | 'no-extra-semi': 'warn', 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /interfaces/LegoHub/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: doc 3 | title: Lego Hub Interface 4 | permalink: /docs/vuforia-spatial-basic-interfaces-addon/interfaces/LegoHub 5 | --- 6 | 7 | ## Lego Hub Interface 8 | 9 | To connect to your Lego Hub toy, first follow the server setup 10 | instructions in the [Vuforia Spatial Edge Server Getting Started 11 | guide](https://spatialtoolbox.vuforia.com/docs/use/connect-to-the-physical-world/startSystem). 12 | Next, clone or download the vuforia-spatial-basic-interfaces-addon into your 13 | server's `addons` directory. Restart the server so it can learn about the new 14 | add-on. 15 | 16 | Once restarted, navigate to http://localhost:8080 to configure your new Lego Hub interface. Click "Manage Hardware Interfaces" then click the switch next to 17 | "Lego Hub" to enable it. Now click the gear to watch the configuration of the 18 | interface. Press the pairing button on your Lego Hub to allow the interface to 19 | automatically configure. Your server's console may contain log messages 20 | explaining any problems encountered during the pairing process. 21 | 22 | At this point you're good to go. The interface has created a blank object for 23 | your Lego Hub that you can now give Vuforia markers to view and 24 | control them using the Spatial Toolbox. For more information on this part of 25 | the process, check out the [object creation 26 | guide](https://spatialtoolbox.vuforia.com/docs/use/connect-to-the-physical-world/create-object) 27 | -------------------------------------------------------------------------------- /interfaces/mozillaWot/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: doc 3 | title: Mozilla WoT Gateway Interface 4 | permalink: /docs/vuforia-spatial-basic-interfaces-addon/interfaces/mozillaWot 5 | --- 6 | 7 | ## Mozilla WoT Gateway Interface 8 | 9 | To connect to your local Mozilla WoT Gateway, first follow the server setup 10 | instructions in the [Vuforia Spatial Edge Server Getting Started 11 | guide](https://spatialtoolbox.vuforia.com/docs/use/connect-to-the-physical-world/startSystem). 12 | Next, clone or download the vuforia-spatial-basic-interfaces-addon into your 13 | server's `addons` directory. Restart the server so it can learn about the new 14 | add-on. 15 | 16 | Once restarted, navigate to http://localhost:8080 to configure your new 17 | interface. Click "Manage Hardware Interfaces" then click the switch next to 18 | "mozillaWot" to enable it. Now click the gear to start the configuration of the 19 | interface. Input your gateway's url and paste in a local developer token 20 | (further documentation coming soon) to kick off the pairing process. Your 21 | server's console may contain log messages explaining any problems encountered 22 | during the pairing process. 23 | 24 | At this point you're good to go. The interface has created blank objects for 25 | each Web Thing that you can now give Vuforia markers to view and 26 | control them using the Spatial Toolbox. For more information on this part of 27 | the process, check out the [object creation 28 | guide](https://spatialtoolbox.vuforia.com/docs/use/connect-to-the-physical-world/create-object) 29 | -------------------------------------------------------------------------------- /interfaces/philipsHue/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: doc 3 | title: Philips Hue Interface 4 | permalink: /docs/vuforia-spatial-basic-interfaces-addon/interfaces/philipsHue 5 | --- 6 | 7 | ## Philips Hue Interface 8 | 9 | To connect to your local Philips Hue lights, first follow the server setup 10 | instructions in the [Vuforia Spatial Edge Server Getting Started 11 | guide](https://spatialtoolbox.vuforia.com/docs/use/connect-to-the-physical-world/startSystem). 12 | Next, clone or download the vuforia-spatial-basic-interfaces-addon into your 13 | server's `addons` directory. Restart the server so it can learn about the new 14 | add-on. 15 | 16 | Once restarted, navigate to http://localhost:8080 to configure your new Philips 17 | Hue interface. Click "Manage Hardware Interfaces" then click the switch next to 18 | "philipsHue" to enable it. Now click the gear to watch the configuration of the 19 | interface. Press the pairing button on your Hue Hub to allow the interface to 20 | automatically configure. If successful, the configuration page will update with 21 | a list of your local lights. Your server's console may contain log messages 22 | explaining any problems encountered during the pairing process. 23 | 24 | At this point you're good to go. The interface has created blank objects for 25 | each Philips Hue light that you can now give Vuforia markers to view and 26 | control them using the Spatial Toolbox. For more information on this part of 27 | the process, check out the [object creation 28 | guide](https://spatialtoolbox.vuforia.com/docs/use/connect-to-the-physical-world/create-object) 29 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: doc 3 | title: Basic Interfaces Add-on 4 | permalink: /docs/vuforia-spatial-basic-interfaces-addon 5 | --- 6 | 7 | ## Basic Interfaces 8 | 9 | The [Basic Interfaces 10 | add-on](https://github.com/ptcrealitylab/vuforia-spatial-basic-interfaces-addon) 11 | hosts a set of hardware interfaces for DIY and consumer hardware. We hope that 12 | these interfaces will enable you to easily get started creating compelling 13 | experiences with the Vuforia Spatial Toolbox and Vuforia Spatial Edge Server. 14 | 15 | ## Supported hardware 16 | 17 | - [Philips Hue](/docs/vuforia-spatial-basic-interfaces-addon/interfaces/philipsHue) 18 | - Arduino: Documentation coming soon 19 | - LEGO WeDo: Documentation coming soon 20 | 21 | 22 | ## Install 23 | Once you have installed the Vuforia Spatial Edge Server clone this repo into the ```addons``` folder 24 | 25 | ```bash 26 | cd addons 27 | git clone https://github.com/ptcrealitylab/vuforia-spatial-basic-interfaces-addon.git 28 | ``` 29 | 30 | Next, enter the vuforia-spatial-basic-interfaces-addon directory and install all dependencies. 31 | 32 | ```bash 33 | cd vuforia-spatial-basic-interfaces-addon 34 | npm install 35 | ``` 36 | 37 | ## Prerequisites (for compiling the dependencies) 38 | 39 | #### OS X 40 | 41 | * Install [Xcode](https://itunes.apple.com/ca/app/xcode/id497799835?mt=12) 42 | 43 | #### Windows 44 | 45 | ```cmd 46 | npm install --global --production windows-build-tools 47 | ``` 48 | 49 | ##### Ubuntu, Debian, Raspbian 50 | 51 | ```sh 52 | sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev 53 | ``` 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vuforia-spatial-basic-interfaces-addon 2 | 3 | This repository hosts a set of hardware interfaces for DIY and consumer 4 | hardware. We hope that these interfaces will enable you to easily get started 5 | creating compelling experiences with the Vuforia Spatial Toolbox and Vuforia 6 | Spatial Edge Server. 7 | 8 | ## Read First 9 | The Vuforia Spatial Toolbox and Vuforia Spatial Edge Server make up a shared research platform for exploring spatial computing as a community. This research platform is not an out of the box production-ready enterprise solution. Please read the [MPL 2.0 license](LICENSE) before use. 10 | 11 | Join the conversations in our [discourse forum](https://forum.spatialtoolbox.vuforia.com) if you have questions, ideas want to collaborate or just say hi. 12 | 13 | ## Supported hardware 14 | 15 | - [Philips Hue](./interfaces/philipsHue/README.md) 16 | - Arduino: Documentation coming soon 17 | - LEGO WeDo / Boost: Activate the Hardware interface and push the green button on your Lego Wedo / Boost. 18 | 19 | ## Install 20 | Once you have installed the Vuforia Spatial Edge Server clone this repo into the ```addons``` folder 21 | 22 | ```bash 23 | cd addons 24 | git clone https://github.com/ptcrealitylab/vuforia-spatial-basic-interfaces-addon.git 25 | ``` 26 | 27 | Next, enter the vuforia-spatial-basic-interfaces-addon directory and install all dependencies. 28 | 29 | ```bash 30 | cd vuforia-spatial-basic-interfaces-addon 31 | npm install 32 | ``` 33 | 34 | ## Prerequisites (for compiling the dependencies) 35 | 36 | #### OS X 37 | 38 | * Install [Xcode](https://itunes.apple.com/ca/app/xcode/id497799835?mt=12) 39 | 40 | #### Windows 41 | 42 | ```cmd 43 | npm install --global --production windows-build-tools 44 | ``` 45 | 46 | ##### Ubuntu, Debian, Raspbian 47 | 48 | ```sh 49 | sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev 50 | ``` 51 | -------------------------------------------------------------------------------- /interfaces/LegoHub/style.css: -------------------------------------------------------------------------------- 1 | .switch { 2 | position: relative; 3 | display: inline-block; 4 | height: 32px; 5 | } 6 | 7 | .switch input { 8 | opacity: 0; 9 | width: 0; 10 | height: 0; 11 | } 12 | 13 | .checkmark { 14 | position: absolute; 15 | top: 0; 16 | left: 0; 17 | width: 100%; 18 | height: 32px; 19 | background-color: none; 20 | border: 2px solid rgb(242,22,121); 21 | line-height: 35px; 22 | text-align: center; 23 | color: rgb(242,22,121); 24 | } 25 | 26 | .checkmark:hover { 27 | cursor: pointer; 28 | } 29 | 30 | .switch:hover input ~ .checkmark { 31 | background-color: rgb(10,10,10); 32 | } 33 | 34 | /* When the checkbox is checked, add a blue background */ 35 | .switch input:checked ~ .checkmark { 36 | border: 2px solid rgb(41,253,47); 37 | color: rgb(41,253,47); 38 | } 39 | 40 | html { 41 | background-color: rgb(34,34,34); 42 | font-family: myriadPro, Helvetica Neue, Helvetica, Arial, sans-serif;; 43 | font-size: 12pt; 44 | -webkit-user-select: none; 45 | -moz-user-select: none; 46 | user-select: none; 47 | color: white; 48 | } 49 | 50 | #title { 51 | color: rgb(45,255,255); 52 | font-family: "Futura", Helvetica Neue, Helvetica, Arial, sans-serif; 53 | font-weight: medium; 54 | font-size: 28pt; 55 | } 56 | 57 | 58 | #contents { 59 | color: rgb(255,255,255); 60 | } 61 | 62 | .button { 63 | padding: 0px; 64 | margin: 0px; 65 | width: 104px; 66 | height: 32px; 67 | line-height: 35px; 68 | text-align: center; 69 | vertical-align: middle; 70 | border: 2px solid white; 71 | margin-top: 10px; 72 | display: inline-block; 73 | } 74 | 75 | .space { 76 | padding: 0; 77 | margin: 0; 78 | width: 8px; 79 | height: 36px; 80 | line-height: 32px; 81 | text-align: center; 82 | vertical-align: middle; 83 | margin-top: 10px; 84 | display: inline-block; 85 | color: rgb(255,255,255); 86 | background-color: rgba(255,255,255,0.15); 87 | z-index: -1; 88 | } 89 | 90 | .smallSpace { 91 | width: 1px; 92 | background-color: transparent; 93 | } 94 | 95 | .clickable:hover { 96 | opacity: 0.75; 97 | cursor: pointer; 98 | } 99 | 100 | .green { 101 | color: rgb(41,253,47); 102 | border: 2px solid rgb(41,253,47); 103 | background-color: rgba(41,253,47,0.05); 104 | } 105 | 106 | .red { 107 | color: rgb(242,22,121); 108 | border: 2px solid rgb(242,22,121); 109 | background-color: rgba(242,22,121,0.05); 110 | } 111 | 112 | .blue { 113 | color: rgb(45,255,255); 114 | border: 2px solid rgb(45,255,255); 115 | background-color: rgba(45,255,255,0.05); 116 | } 117 | 118 | .purple { 119 | color: rgb(210,113,255); 120 | border: 2px solid rgb(210,113,255); 121 | background-color: rgba(210,113,255,0.05); 122 | } 123 | 124 | .yellow { 125 | color: rgb(255,255,45); 126 | border: 2px solid rgb(255,255,45); 127 | background-color: rgba(255,255,45,0.05); 128 | } 129 | 130 | .hidden { 131 | visibility: hidden; 132 | color: rgba(255,255,255,0.0);; 133 | background-color: rgba(255,255,255,0.0); 134 | } 135 | 136 | .white { 137 | color: rgb(255,255,255); 138 | background-color: rgba(255,255,255,0.05); 139 | } 140 | 141 | .zero { 142 | margin-left: -12px; 143 | width: 0; 144 | } 145 | 146 | .half { 147 | width: 46px; 148 | } 149 | 150 | .halfPlus { 151 | width: 49px; 152 | } 153 | 154 | .one { 155 | width: 104px; 156 | } 157 | 158 | .two { 159 | width: 220px; 160 | } 161 | 162 | .twoAndHalf{ 163 | width: 278px; 164 | } 165 | 166 | .three { 167 | width: 336px; 168 | } 169 | 170 | .four { 171 | width: 452px; 172 | } 173 | 174 | .fourAndHalf { 175 | width: 514px; 176 | } 177 | 178 | .five { 179 | width: 568px; 180 | } 181 | 182 | .fiveAndHalf { 183 | width: 626px; 184 | } 185 | 186 | .textfield { 187 | cursor: text; 188 | -webkit-user-select: text; 189 | -moz-user-select: text; 190 | user-select: text; 191 | font-family: myriadPro, Helvetica Neue, Helvetica, Arial, sans-serif; 192 | font-size: 12pt; 193 | white-space: nowrap; 194 | } 195 | 196 | .setting { 197 | margin-bottom: 20px; 198 | } 199 | 200 | #introduction { 201 | margin-bottom: 42px; 202 | } 203 | 204 | a { 205 | color: rgba(45,255,255, 0.5); 206 | } 207 | 208 | .displayNone { 209 | display: none; 210 | } 211 | 212 | .inactive { 213 | opacity: 0.25; 214 | } 215 | 216 | .label { 217 | display: inline-block; 218 | line-height: 35px; 219 | height: 35px; 220 | vertical-align: middle; 221 | text-align: center; 222 | margin-top: 10px; 223 | } 224 | -------------------------------------------------------------------------------- /interfaces/referenceExample/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve 3 | * 4 | * .,,,;;,'''.. 5 | * .'','... ..',,,. 6 | * .,,,,,,',,',;;:;,. .,l, 7 | * .,',. ... ,;, :l. 8 | * ':;. .'.:do;;. .c ol;'. 9 | * ';;' ;.; ', .dkl';, .c :; .'.',::,,'''. 10 | * ',,;;;,. ; .,' .'''. .'. .d;''.''''. 11 | * .oxddl;::,,. ', .'''. .... .'. ,:;.. 12 | * .'cOX0OOkdoc. .,'. .. ..... 'lc. 13 | * .:;,,::co0XOko' ....''..'.'''''''. 14 | * .dxk0KKdc:cdOXKl............. .. ..,c.... 15 | * .',lxOOxl:'':xkl,',......'.... ,'. 16 | * .';:oo:... . 17 | * .cd, ╔═╗┌─┐┬─┐┬ ┬┌─┐┬─┐ . 18 | * .l; ╚═╗├┤ ├┬┘└┐┌┘├┤ ├┬┘ ' 19 | * 'l. ╚═╝└─┘┴└─ └┘ └─┘┴└─ '. 20 | * .o. ... 21 | * .''''','.;:''......... 22 | * .' .l 23 | * .:. l' 24 | * .:. .l. 25 | * .x: :k;,. 26 | * cxlc; cdc,,;;. 27 | * 'l :.. .c , 28 | * o. 29 | * ., 30 | * 31 | * ╦ ╦┬ ┬┌┐ ┬─┐┬┌┬┐ ╔═╗┌┐ ┬┌─┐┌─┐┌┬┐┌─┐ 32 | * ╠═╣└┬┘├┴┐├┬┘│ ││ ║ ║├┴┐ │├┤ │ │ └─┐ 33 | * ╩ ╩ ┴ └─┘┴└─┴─┴┘ ╚═╝└─┘└┘└─┘└─┘ ┴ └─┘ 34 | * 35 | * Created by Valentin on 10/22/14. 36 | * Modified by Carsten on 12/06/15. 37 | * 38 | * Copyright (c) 2015 Valentin Heun 39 | * 40 | * All ascii characters above must be included in any redistribution. 41 | * 42 | * This Source Code Form is subject to the terms of the Mozilla Public 43 | * License, v. 2.0. If a copy of the MPL was not distributed with this 44 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 45 | */ 46 | 47 | /** 48 | * Set to true to enable the hardware interface 49 | **/ 50 | var server = require('../../../../libraries/hardwareInterfaces'); 51 | var settings = server.loadHardwareInterface(__dirname); 52 | 53 | exports.enabled = false; 54 | exports.configurable = false; 55 | 56 | if (exports.enabled) { 57 | 58 | server.enableDeveloperUI(true); 59 | 60 | /* 61 | server.addPublicDataListener("stoneTest", "test2", "testNode","testData",function (data){ 62 | console.log(data); 63 | });*/ 64 | 65 | 66 | // Experiments 67 | var b = 0; 68 | 69 | setInterval(function(){ 70 | if(b === 0) { 71 | b = 1; 72 | server.addNode("stoneTest", "markerPose", "testNode", "node"); 73 | 74 | server.addReadListener("stoneTest", "markerPose", "testNode", function (data) { 75 | // console.log('HOLAAA'); 76 | }); 77 | 78 | // Call move Node after you have created it. 79 | // , , , x, y, scale, matrix 80 | server.moveNode("stoneTest", "markerPose", "testNode", 0,0,0.3,[ 81 | 1, 0, 0, 0, 82 | 0, 1, 0, 0, 83 | 0, 0, 1, 0, 84 | 0, 0, 0, 1 85 | ]); 86 | // call pushUpdatesToDevice( you want the updates to be drawn); 87 | server.pushUpdatesToDevices("stoneTest"); 88 | } else if(b === 1) { 89 | b = 2; 90 | // server.writePublicData(, , , , ); 91 | // server.writePublicData("stoneTest", "test2", "testNode", "testData","server20"); 92 | // , , , x, y, scale, matrix 93 | server.moveNode("stoneTest", "markerPose", "testNode", 0,0,0.3,[ 94 | 1, 0, 0, 0, 95 | 0, 1, 0, 0, 96 | 0, 0, 1, 0, 97 | -100, 0, 0, 1 98 | ]); 99 | 100 | server.pushUpdatesToDevices("stoneTest"); 101 | 102 | } else if(b=== 2){ 103 | b = 3; 104 | 105 | server.moveNode("stoneTest", "markerPose", "testNode", 0,0,0.3,[ 106 | 1, 0, 0, 0, 107 | 0, 1, 0, 0, 108 | 0, 0, 1, 0, 109 | 100, 0, 0, 1 110 | ]); 111 | server.pushUpdatesToDevices("stoneTest"); 112 | 113 | // server.writePublicData("stoneTest", "test2", "testNode", "testData","server30"); 114 | } else if(b===3){ 115 | server.renameNode("stoneTest", "markerPose", "testNode", "AAAAAAA"); 116 | server.pushUpdatesToDevices("stoneTest"); 117 | b = 4; 118 | }else if(b===4){ 119 | server.removeNode("stoneTest", "markerPose", "testNode"); 120 | server.pushUpdatesToDevices("stoneTest"); 121 | b = 0; 122 | } 123 | }, 2000); 124 | 125 | 126 | // server.addNode("thisDemo", "zero", "distance", "node"); 127 | // server.addNode("thisDemo", "zero", "motor", "node"); 128 | 129 | // server.addNode("frameExperiements", "graph", "value", "node"); 130 | // server.addNode("frameExperiements", "graph", "out", "node"); 131 | 132 | // server.addNode("frameExperiements", "youtube", "play", "node"); 133 | 134 | 135 | 136 | 137 | // server.addNode("thisDemo", "zero2", "distance", "node"); 138 | // server.addNode("thisDemo", "zero2", "motor", "node"); 139 | 140 | // server.addNode("thatDemo", "zero", "distance", "node"); 141 | // server.addNode("thatDemo", "zero", "motor", "node"); 142 | 143 | var count = 0; 144 | // server.write("thisDemo", "zero2", "distance", 0.5); 145 | /* setInterval(function(){ 146 | server.write("frameExperiements", "graph", "out", ((Math.random() * (0 - 100) + 100))/100, "f", "F", -20, 10); 147 | 148 | 149 | /*count++; 150 | if (count >= 100){ 151 | count = 0; 152 | }*/ 153 | /* }, 10);*/ 154 | 155 | 156 | 157 | /* 158 | server.addNode("obj45", "one", "node"); 159 | server.addNode("obj45", "two", "node"); 160 | server.addNode("obj45", "three", "node"); 161 | server.addNode("obj45", "four", "node"); 162 | */ 163 | 164 | server.addEventListener("reset", function () { 165 | 166 | }); 167 | 168 | server.addEventListener("shutdown", function () { 169 | 170 | }); 171 | 172 | /* 173 | setInterval(function () { 174 | 175 | server.advertiseConnection("obj45","one"); 176 | 177 | setTimeout(function() { 178 | server.advertiseConnection("obj47", "hans"); 179 | }, 4000); 180 | 181 | }, 8000);*/ 182 | 183 | } 184 | -------------------------------------------------------------------------------- /interfaces/mozillaWot/MozillaWotInterface.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const WebSocket = require('ws'); 3 | 4 | const server = require('../../../../libraries/hardwareInterfaces'); 5 | 6 | module.exports = class MozillaWotInterface { 7 | constructor() { 8 | this.interfaceName = 'mozillaWot'; 9 | this.settings = server.loadHardwareInterface(__dirname); 10 | this.exportedSettings = { 11 | status: { 12 | type: 'status', 13 | connection: 'SPECIFY GATEWAY URL', 14 | things: {}, 15 | }, 16 | gatewayUrl: { 17 | value: this.settings('gatewayUrl'), 18 | type: 'text', 19 | default: '', 20 | helpText: 'The url of the Mozilla WoT Gateway you want to connect to.', 21 | }, 22 | token: { 23 | value: this.settings('token'), 24 | type: 'text', 25 | default: '', 26 | helpText: 'The token to authenticate with the Gateway.', 27 | }, 28 | }; 29 | } 30 | 31 | async pair() { 32 | if (this.settings('gatewayUrl')) { 33 | this.gatewayUrl = this.settings('gatewayUrl'); 34 | } else { 35 | const gatewayUrl = await this.discoverLocalGatewayUrl(); 36 | this.exportedSettings.status.connection = 'RETRIEVE A TOKEN'; 37 | this.gatewayUrl = gatewayUrl; 38 | this.exportedSettings.gatewayUrl.value = this.gatewayUrl; 39 | await this.persistSettings(); 40 | } 41 | 42 | if (this.settings('token')) { 43 | this.token = this.settings('token'); 44 | } else { 45 | this.exportedSettings.status.connection = 'RETRIEVE A TOKEN'; 46 | await this.persistSettings(); 47 | } 48 | 49 | } 50 | 51 | async persistSettings() { 52 | return new Promise((res, rej) => { 53 | server.setHardwareInterfaceSettings( 54 | this.interfaceName, 55 | this.exportedSettings, 56 | null, 57 | function(successful, error) { 58 | if (error) { 59 | console.error('Error persisting settings', error); 60 | rej(error); 61 | return; 62 | } 63 | res(); 64 | } 65 | ); 66 | }); 67 | } 68 | 69 | async discoverLocalGatewayUrl() { 70 | // TODO support automatically discovering gateway.local 71 | } 72 | 73 | async discoverThings() { 74 | if (!this.gatewayUrl || !this.token) { 75 | console.warn('Pairing incomplete'); 76 | return; 77 | } 78 | this.exportedSettings.status.connection = 'CONNECTED'; 79 | await this.persistSettings(); 80 | 81 | const res = await fetch(`${this.gatewayUrl}/things`, {headers: this.getHeaders()}); 82 | const things = await res.json(); 83 | if (!things || !things.length) { 84 | console.warn('No things found'); 85 | return; 86 | } 87 | for (let thing of things) { 88 | let thingId = thing.id.split('/').pop(); 89 | let frameId = thingId + 'frame'; 90 | for (let propertyId in thing.properties) { 91 | server.addNode(thingId, frameId, propertyId, 'node'); 92 | } 93 | server.activate(thingId); 94 | for (let propertyId in thing.properties) { 95 | let property = thing.properties[propertyId]; 96 | let propertyLink; 97 | if (property.links && property.links.length > 0) { 98 | propertyLink = property.links[0].href; 99 | } else { 100 | propertyLink = '/' + thing.id + '/properties/' + propertyId; 101 | } 102 | server.addReadListener( 103 | thingId, frameId, propertyId, 104 | this.onRead(`${this.gatewayUrl}${propertyLink}`, propertyId, property.type === 'boolean')); 105 | } 106 | this.exportedSettings.status.things[thingId] = { 107 | id: thingId, 108 | title: thing.title, 109 | }; 110 | } 111 | await this.persistSettings(); 112 | } 113 | 114 | getHeaders() { 115 | return { 116 | Accept: 'application/json', 117 | 'Content-Type': 'application/json', 118 | Authorization: `Bearer ${this.token}`, 119 | }; 120 | } 121 | 122 | /** 123 | * Create an onRead listener 124 | * @param {string} propertyLink 125 | * @param {string} propertyId 126 | * @param {boolean} isBoolean 127 | * @return {function} 128 | */ 129 | onRead(propertyLink, propertyId, isBoolean) { 130 | return async data => { 131 | try { 132 | let body = JSON.stringify({ 133 | [propertyId]: isBoolean ? data.value > 0.5 : data.value, 134 | }); 135 | 136 | const res = await fetch(propertyLink, { 137 | headers: this.getHeaders(), 138 | method: 'PUT', 139 | body, 140 | }); 141 | await res.json(); 142 | } catch (e) { 143 | console.error('Unable to write data to WoT gateway', data, e); 144 | } 145 | }; 146 | } 147 | 148 | /** 149 | * Add WebSocket listeners for thing with id thingId 150 | * TODO: Use this to allow bidirectional communication 151 | * @param {WebSocket} ws 152 | * @param {string} thingId 153 | */ 154 | async addListeners(ws, thingId) { 155 | ws.addEventListener('message', function(event) { 156 | let msg = JSON.parse(event.data); 157 | if (msg.messageType === 'propertyStatus') { 158 | for (let propId in msg.data) { 159 | let _value = msg.data[propId]; 160 | } 161 | } 162 | }); 163 | 164 | function reopen() { 165 | let newWs = new WebSocket(ws.url); 166 | this.addListeners(newWs, thingId); 167 | } 168 | 169 | ws.addEventListener('close', function() { 170 | console.warn(`Reopening WebSocket for ${thingId} (close)`); 171 | reopen(); 172 | }); 173 | 174 | ws.addEventListener('error', function() { 175 | console.warn(`Reopening WebSocket for ${thingId} (error)`); 176 | reopen(); 177 | }); 178 | } 179 | }; 180 | -------------------------------------------------------------------------------- /interfaces/LegoHub/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lego Hub Configurator 6 | 7 | 234 | 235 | 236 | 237 | 238 |
239 | This is the hardware interface to connect to your Lego Wedo 2.0, Boost or Powered Up connected Hubs.

240 | Devices will show up as Objects in the order you started them. 241 |
242 | 243 |
244 | 245 |
246 | 247 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /interfaces/arduinoUno/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MIR Configurator 6 | 7 | 234 | 235 | 236 | 237 | 238 |
239 | This is the hardware interface to connect to your Arduino board.

240 | To use your Arduino with the Spatial Toolbox, you need to navigate within your Arduino IDE to the following menu:
241 | "Sketch > Inlude Library > Add .zip Library..."

242 | 243 | With the resulting dialog, open the following Zip File:
vuforia-spatial-basic-interfaces-addon/interfaces/arduinoUno/Vuforia-Spatial-Arduino-Library.zip 244 |
245 | 246 |
247 | 248 |
249 | 250 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /interfaces/arduinoUno/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve 3 | * 4 | * .,,,;;,'''.. 5 | * .'','... ..',,,. 6 | * .,,,,,,',,',;;:;,. .,l, 7 | * .,',. ... ,;, :l. 8 | * ':;. .'.:do;;. .c ol;'. 9 | * ';;' ;.; ', .dkl';, .c :; .'.',::,,'''. 10 | * ',,;;;,. ; .,' .'''. .'. .d;''.''''. 11 | * .oxddl;::,,. ', .'''. .... .'. ,:;.. 12 | * .'cOX0OOkdoc. .,'. .. ..... 'lc. 13 | * .:;,,::co0XOko' ....''..'.'''''''. 14 | * .dxk0KKdc:cdOXKl............. .. ..,c.... 15 | * .',lxOOxl:'':xkl,',......'.... ,'. 16 | * .';:oo:... . 17 | * .cd, ╔═╗┌─┐┬─┐┬ ┬┌─┐┬─┐ . 18 | * .l; ╚═╗├┤ ├┬┘└┐┌┘├┤ ├┬┘ ' 19 | * 'l. ╚═╝└─┘┴└─ └┘ └─┘┴└─ '. 20 | * .o. ... 21 | * .''''','.;:''......... 22 | * .' .l 23 | * .:. l' 24 | * .:. .l. 25 | * .x: :k;,. 26 | * cxlc; cdc,,;;. 27 | * 'l :.. .c , 28 | * o. 29 | * ., 30 | * 31 | * ╦ ╦┬ ┬┌┐ ┬─┐┬┌┬┐ ╔═╗┌┐ ┬┌─┐┌─┐┌┬┐┌─┐ 32 | * ╠═╣└┬┘├┴┐├┬┘│ ││ ║ ║├┴┐ │├┤ │ │ └─┐ 33 | * ╩ ╩ ┴ └─┘┴└─┴─┴┘ ╚═╝└─┘└┘└─┘└─┘ ┴ └─┘ 34 | * 35 | * Created by Valentin on 10/22/14. 36 | * 37 | * Copyright (c) 2015 Valentin Heun 38 | * 39 | * All ascii characters above must be included in any redistribution. 40 | * 41 | * This Source Code Form is subject to the terms of the Mozilla Public 42 | * License, v. 2.0. If a copy of the MPL was not distributed with this 43 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 44 | */ 45 | var server = require('../../../../libraries/hardwareInterfaces'); 46 | var settings = server.loadHardwareInterface(__dirname); 47 | 48 | exports.enabled = settings('enabled'); 49 | exports.configurable = true; // can be turned on/off/adjusted from the web frontend 50 | 51 | if (exports.enabled) { 52 | 53 | exports.settings = { 54 | 55 | }; 56 | 57 | 58 | var serialport ={} 59 | try { 60 | serialport = require("serialport"); 61 | } catch (err) { 62 | console.clear(); 63 | console.log('\x1b[33mYou\'re not done with installing the basic this addon! You need to execute the following commands:'); 64 | console.log('\x1b[0m1.\x1b[32m cd addons/vuforia-spatial-basic-addon'); 65 | console.log('\x1b[0m2.\x1b[32m npm install', '\x1b[0m'); 66 | 67 | if (process.send) { 68 | process.send('exit'); 69 | } 70 | 71 | let keepRunning = true; 72 | while (keepRunning) { 73 | // Since process.send is async, just hold the server for preventing more errors 74 | } 75 | } 76 | 77 | var _ = require('lodash'); 78 | 79 | 80 | 81 | const serialBaudRate = 115200; // baud rate for connection to arudino 82 | 83 | // change this to what ever is your Arudino Serial Port 84 | 85 | const serialSource = "/dev/cu.usbmodem141141"; // this is pointing to the arduino 86 | 87 | function ArduinoIndex() { 88 | this.objName = null; 89 | this.ioName = null; 90 | this.index = null; 91 | } 92 | 93 | var ArduinoLookup = {}; 94 | var ArduinoLookupByIndex = {}; 95 | var FullLookup = {}; 96 | var serialPortOpen = false; 97 | 98 | //initialisation of the socket connection 99 | /* var SerialP = serialport.SerialPort; // localize object constructor 100 | var serialPort = new SerialP(serialSource, { 101 | parser: serialport.parsers.readline("\n"), 102 | baudrate: serialBaudRate 103 | }, false); 104 | */ 105 | const SerialPort = require('serialport'); 106 | const Readline = SerialPort.parsers.Readline; 107 | 108 | var serialPort; 109 | 110 | var _this = this; 111 | 112 | SerialPort.list().then(function(ports) { 113 | for(var i = 0; i < ports.length; i++){ 114 | if(ports[i].manufacturer){ 115 | if(ports[i].manufacturer.includes("Arduino")) { 116 | serialPort = new SerialPort(ports[i].comName, { 117 | baudRate: 115200 118 | }); 119 | serialPort.on('error', function (err) { 120 | console.error("Serial port error", err); 121 | }); 122 | serialServer(serialPort); 123 | break; 124 | } 125 | } 126 | } 127 | }).catch(function (err) { 128 | // return err; // code doesn't come here 129 | }); 130 | 131 | function serialServer(serialPort) { 132 | 133 | const parser = serialPort.pipe(new Readline({ delimiter: '\n' })); 134 | parser.on('data', function (data){ 135 | // get a buffer of data from the serial port 136 | // console.log('serial data', data); 137 | }); 138 | console.log('serial opening'); 139 | // serialPort.open(); 140 | 141 | serialPort.on('open', function () { 142 | 143 | console.log('Serial port opened'); 144 | serialPortOpen = true; 145 | var dataSwitch = 0; 146 | var pos = null; 147 | var objID = null; 148 | var obj = null; 149 | var object = null; 150 | var arrayID = null; 151 | var valueMode = ""; 152 | var value = null; 153 | var thisName = ""; 154 | var thisPlugin = "default"; 155 | var amount = 0; 156 | //var okCounter = 0; 157 | 158 | parser.on('data', function (data) { 159 | // console.log(data.toString()); 160 | // console.log(data); 161 | switch (dataSwitch) { 162 | case 0: 163 | if (data === "f") { 164 | //if (server.getClear()) { 165 | valueMode = "f"; 166 | dataSwitch = 1; 167 | //} 168 | } 169 | else if (data === "d") { 170 | //if (server.getClear()) { 171 | valueMode = "d"; 172 | dataSwitch = 1; 173 | //} 174 | } 175 | else if (data === "p") { // positive step value 176 | //if (server.getClear()) { 177 | valueMode = "p"; 178 | dataSwitch = 1; 179 | //} 180 | } 181 | else if (data === "n") {// negative step value 182 | //if (server.getClear()) { 183 | valueMode = "n"; 184 | dataSwitch = 1; 185 | //} 186 | } 187 | else if (data === "a") { 188 | dataSwitch = 20; 189 | } 190 | else if (data === "okbird") { 191 | 192 | serialPort.write(" \n"); 193 | serialPort.write("okbird\n"); 194 | console.log("serial response ok"); 195 | dataSwitch = 0; 196 | } 197 | else if (data === "def") { 198 | console.log("serial response developer"); 199 | dataSwitch = 40; 200 | } 201 | else if (data === "c") { 202 | console.log("serial response clear"); 203 | dataSwitch = 50; 204 | 205 | } 206 | break; 207 | case 1: 208 | arrayID = parseInt(data, 10); 209 | dataSwitch = 2; 210 | break; 211 | case 2: 212 | value = parseFloat(data); 213 | 214 | if (ArduinoLookupByIndex.hasOwnProperty(arrayID)) 215 | server.write(ArduinoLookupByIndex[arrayID].objName, "arduino01", ArduinoLookupByIndex[arrayID].ioName, value, valueMode); 216 | 217 | 218 | dataSwitch = 0; 219 | break; 220 | case 20: 221 | object = data.split("\t"); 222 | dataSwitch = 21; 223 | break; 224 | case 21: 225 | arrayID = parseInt(data, 10); 226 | dataSwitch = 23; 227 | break; 228 | case 23: 229 | thisPlugin = data; 230 | obj = object[1]; 231 | pos = object[0]; 232 | 233 | console.log("Serial add Arduino Yun"); 234 | 235 | ArduinoLookup[obj + pos] = new ArduinoIndex(); 236 | ArduinoLookup[obj + pos].objName = obj; 237 | ArduinoLookup[obj + pos].ioName = pos; 238 | ArduinoLookup[obj + pos].index = arrayID; 239 | 240 | ArduinoLookupByIndex[arrayID] = new ArduinoIndex(); 241 | ArduinoLookupByIndex[arrayID].objName = obj; 242 | ArduinoLookupByIndex[arrayID].ioName = pos; 243 | ArduinoLookupByIndex[arrayID].index = arrayID; 244 | 245 | var thisObjectID = server.getObjectIdFromObjectName(obj); 246 | 247 | 248 | if (!FullLookup.hasOwnProperty(thisObjectID)) { 249 | FullLookup[thisObjectID] = {}; 250 | } 251 | server.addNode(obj, "arduino01", pos, "node"); 252 | 253 | if(thisObjectID) { 254 | FullLookup[thisObjectID][thisObjectID+pos] = arrayID; 255 | 256 | server.addReadListener(obj, "arduino01", pos, function (obj,pos,node,data) { 257 | serialSender(serialPort, obj, pos, data.value, "f"); 258 | }.bind(data,thisObjectID,thisObjectID+pos,"node")); 259 | 260 | } 261 | 262 | dataSwitch = 0; 263 | break; 264 | case 40: 265 | if (parseInt(data, 10) === 1) { 266 | // server.developerOn(); 267 | server.enableDeveloperUI(true); 268 | } 269 | dataSwitch = 0; 270 | break; 271 | case 50: 272 | amount = parseInt(data, 10); 273 | //server.clearIO("arduinoYun"); 274 | dataSwitch = 0; 275 | break; 276 | } 277 | 278 | }); 279 | 280 | // this is for when the server is started... 281 | serialPort.write(" \n"); 282 | serialPort.write("okbird\n"); 283 | 284 | }); 285 | } 286 | 287 | 288 | function serialSender(serialPort, objName, ioName, value, mode) { 289 | if (FullLookup.hasOwnProperty(objName)) { 290 | 291 | if (FullLookup[objName].hasOwnProperty(ioName)) { 292 | 293 | var index = FullLookup[objName][ioName]; 294 | 295 | // console.log("check index: ", index); 296 | var yunModes = ["f", "d", "p", "n"]; 297 | if (_.includes(yunModes, mode)) { 298 | serialPort.write(mode + "\n"); 299 | } else { 300 | serialPort.write("f\n"); 301 | } 302 | serialPort.write(index + "\n"); 303 | serialPort.write(value + "\n"); 304 | } 305 | } 306 | } 307 | 308 | server.addEventListener("reset", function () { 309 | if (serialPortOpen) { 310 | serialPort.write(" \n"); 311 | serialPort.write("okbird\n"); 312 | } 313 | }); 314 | } 315 | -------------------------------------------------------------------------------- /interfaces/LegoHub/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve 3 | * 4 | * .,,,;;,'''.. 5 | * .'','... ..',,,. 6 | * .,,,,,,',,',;;:;,. .,l, 7 | * .,',. ... ,;, :l. 8 | * ':;. .'.:do;;. .c ol;'. 9 | * ';;' ;.; ', .dkl';, .c :; .'.',::,,'''. 10 | * ',,;;;,. ; .,' .'''. .'. .d;''.''''. 11 | * .oxddl;::,,. ', .'''. .... .'. ,:;.. 12 | * .'cOX0OOkdoc. .,'. .. ..... 'lc. 13 | * .:;,,::co0XOko' ....''..'.'''''''. 14 | * .dxk0KKdc:cdOXKl............. .. ..,c.... 15 | * .',lxOOxl:'':xkl,',......'.... ,'. 16 | * .';:oo:... . 17 | * .cd, ╔═╗┌─┐┬─┐┬ ┬┌─┐┬─┐ . 18 | * .l; ╚═╗├┤ ├┬┘└┐┌┘├┤ ├┬┘ ' 19 | * 'l. ╚═╝└─┘┴└─ └┘ └─┘┴└─ '. 20 | * .o. ... 21 | * .''''','.;:''......... 22 | * .' .l 23 | * .:. l' 24 | * .:. .l. 25 | * .x: :k;,. 26 | * cxlc; cdc,,;;. 27 | * 'l :.. .c , 28 | * o. 29 | * ., 30 | * 31 | * ╦ ╦┬ ┬┌┐ ┬─┐┬┌┬┐ ╔═╗┌┐ ┬┌─┐┌─┐┌┬┐┌─┐ 32 | * ╠═╣└┬┘├┴┐├┬┘│ ││ ║ ║├┴┐ │├┤ │ │ └─┐ 33 | * ╩ ╩ ┴ └─┘┴└─┴─┴┘ ╚═╝└─┘└┘└─┘└─┘ ┴ └─┘ 34 | * 35 | * Created by Valentin on 10/22/14. 36 | * Modified by Carsten on 12/06/15. 37 | * 38 | * Copyright (c) 2015 Valentin Heun 39 | * 40 | * All ascii characters above must be included in any redistribution. 41 | * 42 | * This Source Code Form is subject to the terms of the Mozilla Public 43 | * License, v. 2.0. If a copy of the MPL was not distributed with this 44 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 45 | */ 46 | 47 | /** 48 | * Set to true to enable the hardware interface 49 | **/ 50 | var server = require('../../../../libraries/hardwareInterfaces'); 51 | var settings = server.loadHardwareInterface(__dirname); 52 | 53 | exports.enabled = settings('enabled'); 54 | exports.configurable = true; 55 | 56 | if (exports.enabled) { 57 | 58 | exports.settings = { 59 | 60 | }; 61 | var names = {}; 62 | var portIdentifier = {}; 63 | var namesLego = {}; 64 | 65 | var Hub = {}; 66 | try { 67 | Hub = require('wedoboostpoweredup'); 68 | } catch (err) { 69 | console.clear(); 70 | console.log('\x1b[33mYouYou\'re not done with installing the basic this addon! You need to execute the following commands:'); 71 | console.log('\x1b[0m1.\x1b[32m cd addons/vuforia-spatial-basic-addon'); 72 | console.log('\x1b[0m2.\x1b[32m npm install', '\x1b[0m'); 73 | 74 | if (process.send) { 75 | process.send('exit'); 76 | } 77 | 78 | let keepRunning = true; 79 | while (keepRunning) { 80 | // Since process.send is async, just hold the server for preventing more errors 81 | } 82 | } 83 | 84 | var hub = new Hub(); 85 | 86 | var HUB_NAME = ""; 87 | if (settings('hubName')) { 88 | HUB_NAME = settings('hubName'); 89 | } else { 90 | HUB_NAME = "legoHub"; 91 | } 92 | 93 | const TOOL_NAME = "IO"; 94 | 95 | server.enableDeveloperUI(true); 96 | 97 | var namecount = 1; 98 | 99 | /* hub.on('ble', function (msg) { 100 | setTimeout(function() { 101 | server.sendToUI("wedoBLE",msg); 102 | }, 500); 103 | });*/ 104 | 105 | hub.on('connected', function (uuid) { 106 | 107 | // todo If both wedos have the same name, give numbers 108 | // todo make nodes invisible on command 109 | 110 | if (hub.wedoBoostPoweredUp[uuid]) { 111 | if(hub.wedoBoostPoweredUp[uuid].deviceType === "wedo") { 112 | names[uuid] = {0x01: "port 1", 0x02: "port 2"}; 113 | portIdentifier[uuid] = {0x01: "1", 0x02: "2"}; 114 | } 115 | else { 116 | names[uuid] = {0x00: "port A", 0x01: "port B", 0x02: "port C", 0x03: "port D", 0x3A: "port E"}; 117 | portIdentifier[uuid] = {0x00: "A", 0x01: "B", 0x02: "C", 0x03: "D", 0x3A: "E"}; 118 | } 119 | 120 | if (!(uuid in namesLego)) { 121 | namesLego[uuid] = HUB_NAME+"_" + namecount; 122 | namecount++; 123 | // names[uuid].name = hub.wedoBoostPoweredUp[uuid].name; 124 | } 125 | names[uuid].name = namesLego[uuid]; 126 | hub.wedoBoostPoweredUp[uuid].name = names[uuid].name; 127 | 128 | // server.sendToUI("wedoOn",names[uuid].name); 129 | 130 | if (hub.wedoBoostPoweredUp[uuid].name) { 131 | console.log("wedo connected", hub.wedoBoostPoweredUp[uuid].name); 132 | var thisHub = hub.wedoBoostPoweredUp[uuid].name; 133 | 134 | console.log("Adding nodes for ", hub.wedoBoostPoweredUp[uuid].name); 135 | 136 | if(hub.wedoBoostPoweredUp[uuid].deviceType === "wedo") { 137 | 138 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x01], "node"); 139 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x02], "node"); 140 | server.addNode(thisHub, TOOL_NAME, "button", "node"); 141 | server.addNode(thisHub, TOOL_NAME, "light", "node"); 142 | 143 | /*server.renameNode(names[uuid].name, TOOL_NAME, names[uuid][0x01], names[uuid][0x01]); 144 | server.renameNode(names[uuid].name, "none 1", "y1"); 145 | server.renameNode(names[uuid].name, TOOL_NAME, names[uuid][0x01], names[uuid][0x01]); 146 | server.renameNode(names[uuid].name, "none 2", "y2");*/ 147 | 148 | } else { 149 | console.log('NEW NODE: ', portIdentifier[uuid][0x00]); 150 | console.log('NEW NODE: ', portIdentifier[uuid][0x01]); 151 | 152 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x00], "node"); 153 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x01], "node"); 154 | 155 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x02], "node"); 156 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x03], "node"); 157 | server.addNode(thisHub, TOOL_NAME, "port "+portIdentifier[uuid][0x3A], "node"); 158 | server.addNode(thisHub, TOOL_NAME, "button", "node"); 159 | server.addNode(thisHub, TOOL_NAME, "light", "node"); 160 | } 161 | 162 | server.activate(thisHub); 163 | 164 | for (let key in portIdentifier[uuid]){ 165 | server.addReadListener(names[uuid].name, TOOL_NAME, "port "+portIdentifier[uuid][key], function (key, portIdentifier, names, wedo, uuid, data) { 166 | // console.log(names[uuid].name,data); 167 | if (names[uuid][key] === "motor"+ " "+portIdentifier[uuid][key]) { 168 | hub.setMotor(server.map(data.value, -1, 1, -100, 100), key, uuid); 169 | } 170 | }.bind(this, key, portIdentifier, names, hub, uuid)); 171 | } 172 | 173 | 174 | server.addReadListener(names[uuid].name, TOOL_NAME, "light", function (names, hub, uuid, data) { 175 | var color = parseInt(data.value * 255); 176 | hub.setLedColor(color, color, color, uuid); 177 | }.bind(this, names, hub, uuid)); 178 | 179 | hub.on('button', function (button, uuid) { 180 | if (uuid in names) { 181 | if(hub.wedoBoostPoweredUp[uuid].deviceType === "wedo") { 182 | 183 | server.write(names[uuid].name, TOOL_NAME, "button", button, "f"); 184 | } else { 185 | if(button){ 186 | server.write(names[uuid].name, TOOL_NAME, "button", 1.0, "f"); 187 | } else { 188 | server.write(names[uuid].name, TOOL_NAME, "button", 0.0, "f"); 189 | } 190 | } 191 | } 192 | }.bind(this)); 193 | } 194 | } 195 | }.bind(this)); 196 | 197 | hub.on('disconnected', function (uuid) { 198 | // remove all listeners when disconnected 199 | 200 | if (names[uuid].name) { 201 | 202 | // server.sendToUI("wedoOff",names[uuid].name); 203 | server.deactivate(names[uuid].name); 204 | 205 | for (let key in portIdentifier[uuid]){ 206 | 207 | server.renameNode(names[uuid].name, TOOL_NAME, "port "+portIdentifier[uuid][key], " "); 208 | names[uuid][key] ="port "+portIdentifier[uuid][key]; 209 | 210 | resetNode(uuid, key); 211 | } 212 | 213 | server.removeReadListeners(names[uuid].name, TOOL_NAME); 214 | 215 | } 216 | }); 217 | 218 | hub.on('motor', function (distance, port, uuid) { 219 | if (uuid in names) { 220 | server.write(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], server.map(10 - distance, 0, 10, 0, 1), "f"); 221 | } 222 | }); 223 | 224 | hub.on('distanceSensor', function (distance, port, uuid) { 225 | if (uuid in names) { 226 | server.write(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], server.map(10 - distance, 0, 10, 0, 1), "f"); 227 | } 228 | }); 229 | 230 | hub.on('motor', function (motorRotation, port, uuid) { 231 | server.writePublicData(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], 'motorRotation', motorRotation); 232 | }); 233 | 234 | 235 | hub.on('visionSensor', function (colorLuminance, port, uuid) { 236 | server.write(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], server.map((colorLuminance.r+colorLuminance.g+colorLuminance.b)/3,0,255,0,1), "f"); 237 | server.writePublicData(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], 'colorLuminance', colorLuminance); 238 | }); 239 | 240 | 241 | hub.on('tiltSensor', function (x, y, port, uuid) { 242 | if (uuid in names) { 243 | Math.round(20.49); 244 | server.write(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], Math.round(server.map(x, -45, 45, 0, 1) * 100) / 100, "f"); 245 | server.writePublicData(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], 'tilt', {x:x, y:y}); 246 | } 247 | }); 248 | 249 | hub.on('port', function (port, connected, type, uuid) { 250 | console.log("Port, is connected and type: ", port, connected, type,); 251 | if (hub.wedoBoostPoweredUp[uuid]) { 252 | names[uuid] = names[uuid] || {}; 253 | 254 | var x = "port", y = " "; 255 | 256 | if (type === "distanceSensor") { 257 | x = "distance"; 258 | } else 259 | if (type === "motor") { 260 | x = "motor"; 261 | } else 262 | if (type === "tiltSensor") { 263 | x = "motion"; 264 | // y = "y"; 265 | } else 266 | if (type === "visionSensor") { 267 | x = "vision"; 268 | // y = "y"; 269 | } 270 | 271 | var thisHub = hub.wedoBoostPoweredUp[uuid].name; 272 | 273 | if (connected) { 274 | server.renameNode(thisHub, TOOL_NAME, "port " + portIdentifier[uuid][port], x); 275 | server.write(names[uuid].name, TOOL_NAME, "port " + portIdentifier[uuid][port], 0, "f"); 276 | names[uuid][port] = x + " " + portIdentifier[uuid][port]; 277 | } 278 | if(!connected){ 279 | server.renameNode(thisHub, TOOL_NAME, "port " + portIdentifier[uuid][port], "port " + portIdentifier[uuid][port]); 280 | names[uuid][port] = "port " + portIdentifier[uuid][port]; 281 | // server.renameNode(thisHub, "none 1", " "); 282 | // names[uuid].py1 = "none 1"; 283 | resetNode(uuid, port); 284 | 285 | } 286 | } 287 | }); 288 | 289 | function resetNode (uuid, port){ 290 | server.write(names[uuid].name, TOOL_NAME, "port "+port, 0, "f"); 291 | // server.write(names[uuid].name, "none "+port, 0, "f"); 292 | } 293 | } 294 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /interfaces/philipsHue/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Carsten on 12/06/15. 3 | * Modified by Peter Som de Cerff (PCS) on 12/21/15 4 | * 5 | * Copyright (c) 2015 Carsten Strunk 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 10 | */ 11 | 12 | /* 13 | * Philips Hue Interface 14 | * 15 | * This hardware interface can communicate with Philips Hue lights. 16 | * 17 | * TODO: Color support 18 | * TODO: Device type support 19 | * TODO: Rewrite to use fetch everywhere 20 | * TODO: Error handling 21 | */ 22 | 23 | const fetch = require('node-fetch'); 24 | const http = require('http'); 25 | 26 | const server = require('../../../../libraries/hardwareInterfaces'); 27 | 28 | let settings = server.loadHardwareInterface(__dirname); 29 | 30 | exports.enabled = settings('enabled'); 31 | exports.configurable = true; // can be turned on/off/adjusted from the web frontend 32 | exports.settings = {}; 33 | 34 | class Light { 35 | constructor(index, localBridgeIP, username) { 36 | this.id = 'Light' + index; 37 | this.host = localBridgeIP; 38 | this.url = `/api/${username}/lights/${index}`; 39 | this.port = 80; 40 | 41 | this.switch = null; 42 | this.bri = null; 43 | this.colorful = false; 44 | this.hue = null; 45 | this.sat = null; 46 | } 47 | } 48 | 49 | function sleep(ms) { 50 | return new Promise((res) => { 51 | setTimeout(res, ms); 52 | }); 53 | } 54 | 55 | async function getUsername(localBridgeIP) { 56 | let retries = 0; 57 | // Just retry forever 58 | while (true) { // eslint-disable-line 59 | retries += 1; 60 | let username = await getUsernameOnce(localBridgeIP); 61 | if (username) { 62 | return username; 63 | } 64 | if (retries < 200) { 65 | await sleep(2000); 66 | } else { 67 | await sleep(10000); 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * Perform a single pairing attempt with the bridge located at localBridgeIP 74 | * Based on my own code from the Mozilla WebThings Philips Hue Adapter 75 | * @return {string?} username or undefined if pairing failed 76 | */ 77 | async function getUsernameOnce(localBridgeIP) { 78 | const res = await fetch(`http://${localBridgeIP}/api`, { 79 | method: 'POST', 80 | body: '{"devicetype":"vst#PhilipsHueInterface"}', 81 | }); 82 | const reply = await res.json(); 83 | 84 | if (reply.length === 0) { 85 | console.warn('empty response from bridge'); 86 | return; 87 | } 88 | 89 | const msg = reply[0]; 90 | if (msg.error) { 91 | console.warn('error from bridge', msg.error); 92 | return; 93 | } 94 | 95 | return msg.success.username; 96 | } 97 | 98 | /** 99 | * Attempt to automatically retrieve the local bridge IP 100 | */ 101 | async function getLocalBridgeIP() { 102 | const res = await fetch('https://www.meethue.com/api/nupnp'); 103 | const bridges = await res.json(); 104 | 105 | if (bridges.length === 0) { 106 | console.error('Philips Hue: no bridges found'); 107 | return null; 108 | } 109 | 110 | if (bridges.length > 1) { 111 | console.warn('Philips hue found multiple local bridges', bridges); 112 | } 113 | 114 | const bridge = bridges[0]; 115 | 116 | return bridge.internalipaddress; 117 | } 118 | 119 | /** 120 | * Fetch and instantiate lights from a local bridge 121 | */ 122 | async function getLocalLights(localBridgeIP, username) { 123 | const res = await fetch(`http://${localBridgeIP}/api/${username}/lights`); 124 | const lightInfo = await res.json(); 125 | const lights = {}; 126 | for (let lightId in lightInfo) { 127 | let light = new Light(lightId, localBridgeIP, username); 128 | lights[light.id] = light; 129 | } 130 | return { 131 | lights, 132 | lightInfo, 133 | }; 134 | } 135 | 136 | /** 137 | * Communicates with the philipsHue bridge and checks the state of the light 138 | * @param {Object} light the light to check 139 | * @param {function} callback function to run when the response has arrived 140 | */ 141 | function getLightState(light, callback) { 142 | var state; 143 | 144 | var options = { 145 | host: light.host, 146 | path: light.url, 147 | port: light.port, 148 | method: 'GET', 149 | }; 150 | 151 | var callbackHttp = function (response) { 152 | var str = ''; 153 | 154 | response.on('data', function (chunk) { 155 | str += chunk; 156 | }); 157 | 158 | response.on('end', function () { 159 | //TODO add some error handling 160 | state = JSON.parse(str).state; 161 | if (!state) { 162 | console.error('Philips Hue Error', str); 163 | return; 164 | } 165 | const frameId = light.id + 'frame'; 166 | if (state.on != light.switch) { 167 | light.switch = state.on; 168 | if (state.on) { 169 | callback(light.id, frameId, 'switch', 1, 'd'); 170 | } else { 171 | callback(light.id, frameId, 'switch', 0, 'd'); 172 | } 173 | 174 | } 175 | 176 | if (state.bri != light.bri) { 177 | light.bri = state.bri; // brightness is a value between 1 and 254 178 | callback(light.id, frameId, 'brightness', (state.bri - 1) / 253, 'f'); 179 | } 180 | 181 | if (light.colorful) { 182 | if (state.hue != light.hue) { 183 | light.hue = state.hue; // hue is a value between 0 and 65535 184 | callback(light.id, frameId, 'hue', state.hue / 65535, 'f'); // map hue to [0,1] 185 | } 186 | 187 | if (state.sat != light.sat) { 188 | light.sat = state.sat; 189 | callback(light.id, frameId, 'saturation', state.sat / 254, 'f'); 190 | } 191 | } 192 | 193 | }); 194 | }; 195 | 196 | var req = http.request(options, callbackHttp); 197 | req.on('error', function (e) { 198 | console.log('GetLightState HTTP error', e.message); 199 | }); 200 | req.end(); 201 | } 202 | 203 | 204 | /** 205 | * turns the specified light on or off 206 | * @param {number} state turns the light on if > 0.5, turns it off otherwise 207 | */ 208 | function writeSwitchState(light, state) { 209 | console.log('write switch state', light, state); 210 | var options = { 211 | host: light.host, 212 | path: light.url + '/state', 213 | port: light.port, 214 | method: 'PUT', 215 | }; 216 | 217 | 218 | var req = http.request(options, function () { }); 219 | req.on('error', function (e) { 220 | console.log('writeSwitchState HTTP error', e.message); 221 | }); 222 | 223 | req.write(JSON.stringify({ 224 | on: state > 0.5 225 | })); 226 | 227 | req.end(); 228 | 229 | //TODO check for success message from the bridge 230 | } 231 | 232 | 233 | /** 234 | * Sets the brightness of the specified light 235 | * @param {number} bri is the brightness in the range [0,1] 236 | */ 237 | function writeBrightness(light, bri) { 238 | if (writeBrightness.requestInFlight) { 239 | return; 240 | } 241 | 242 | var options = { 243 | hostname: light.host, 244 | path: light.url + '/state', 245 | port: light.port, 246 | method: 'PUT', 247 | }; 248 | 249 | writeBrightness.requestInFlight = true; 250 | var req = http.request(options, function() { 251 | setTimeout(function() { 252 | writeBrightness.requestInFlight = false; 253 | }, 100); 254 | }); 255 | req.on('error', function (e) { 256 | console.log('writeBrightness HTTP error', e.message); 257 | setTimeout(function() { 258 | writeBrightness.requestInFlight = false; 259 | }, 100); 260 | }); 261 | 262 | req.write(JSON.stringify({ 263 | bri: Math.floor(bri * 253 + 1) 264 | })); 265 | 266 | req.end(); 267 | } 268 | 269 | 270 | /** 271 | * sets the saturation for the specified light 272 | * @param {number} sat is the saturation in the range [0,1] 273 | */ 274 | function writeSaturation(light, sat) { 275 | var options = { 276 | hostname: light.host, 277 | path: light.url + '/state', 278 | port: light.port, 279 | method: 'PUT', 280 | }; 281 | 282 | var req = http.request(options, function () { }); 283 | req.on('error', function (e) { 284 | console.log('writeSaturation HTTP error', e.message); 285 | }); 286 | req.write(JSON.stringify({ 287 | sat: Math.floor(sat * 254), 288 | })); 289 | req.end(); 290 | } 291 | 292 | 293 | /** 294 | * sets the hue for the specified light 295 | * @param {number} hue is the hue in the range [0,1] 296 | */ 297 | function writeHue(light, hue) { 298 | var options = { 299 | hostname: light.host, 300 | path: light.url + '/state', 301 | port: light.port, 302 | method: 'PUT', 303 | }; 304 | 305 | var req = http.request(options, function () { }); 306 | req.on('error', function (e) { 307 | console.log('writeHue HTTP error', e.message); 308 | }); 309 | req.write(JSON.stringify({ 310 | hue: Math.floor(hue * 65535), 311 | })); 312 | req.end(); 313 | } 314 | 315 | if (exports.enabled) { 316 | server.enableDeveloperUI(true); 317 | let lights; 318 | 319 | /** 320 | * runs once, adds and clears the IO points 321 | */ 322 | async function setup() { // eslint-disable-line no-inner-declarations 323 | console.log('setup philipsHue'); 324 | // Reload settings 325 | settings = server.loadHardwareInterface(__dirname); 326 | 327 | exports.settings = { 328 | status: { 329 | type: 'status', 330 | connection: 'DISCOVERING HUE BRIDGE', 331 | lights: 0, 332 | }, 333 | localBridgeIP: { 334 | value: settings('localBridgeIP'), 335 | type: 'text', 336 | default: '', 337 | helpText: 'The IP address of the local Hue Hub you want to connect to.', 338 | }, 339 | username: { 340 | value: settings('username'), 341 | type: 'text', 342 | default: '', 343 | helpText: 'The local username to authenticate with the Hue Hub', 344 | } 345 | }; 346 | 347 | let localBridgeIP = ''; 348 | let username = ''; 349 | let settingsNeedUpdate = false; 350 | if (settings('localBridgeIP')) { 351 | localBridgeIP = settings('localBridgeIP'); 352 | } else { 353 | const bridgeIP = await getLocalBridgeIP(); 354 | exports.settings.status.connection = 'PRESS THE PAIR BUTTON ON THE HUE HUB'; 355 | localBridgeIP = bridgeIP; 356 | exports.settings.localBridgeIP.value = localBridgeIP; 357 | settingsNeedUpdate = true; 358 | } 359 | 360 | if (settings('username')) { 361 | username = settings('username'); 362 | } else { 363 | username = await getUsername(localBridgeIP); 364 | exports.settings.status.connection = 'PAIRED WITH HUE HUB'; 365 | exports.settings.username.value = username; 366 | settingsNeedUpdate = true; 367 | } 368 | 369 | if (settingsNeedUpdate) { 370 | server.setHardwareInterfaceSettings('philipsHue', exports.settings, null, function(successful, error) { 371 | if (error) { 372 | console.log('error persisting settings', error); 373 | } 374 | }); 375 | } 376 | 377 | const lightResults = await getLocalLights(localBridgeIP, username); 378 | lights = lightResults.lights; 379 | const lightInfo = lightResults.lightInfo; 380 | 381 | console.log('found lights', lights); 382 | exports.settings.status.connection = 'PAIRED WITH HUE BRIDGE'; 383 | exports.settings.status.lights = lightInfo; 384 | 385 | // Set up server-side frames, nodes, and listeners for all known lights 386 | for (var lightId in lights) { 387 | console.log('add lightId', lightId); 388 | const frameId = lightId + 'frame'; 389 | server.addNode(lightId, frameId, 'switch', 'node'); 390 | server.addNode(lightId, frameId, 'brightness', 'node'); 391 | if (lights[lightId].colorful) { 392 | server.addNode(lightId, frameId, 'hue', 'node'); 393 | server.addNode(lightId, frameId, 'saturation', 'node'); 394 | } 395 | server.activate(lightId); 396 | 397 | server.addReadListener(lightId, frameId, 'switch', onRead(lightId, writeSwitchState)); 398 | server.addReadListener(lightId, frameId, 'brightness', onRead(lightId, writeBrightness)); 399 | 400 | if (lights[lightId].colorful) { 401 | server.addReadListener(lightId, frameId, 'hue', onRead(lightId, writeHue)); 402 | server.addReadListener(lightId, frameId, 'saturation', onRead(lightId, writeSaturation)); 403 | } 404 | } 405 | } 406 | 407 | /** 408 | * The main function, runs the setup and then periodically checks whether 409 | * the lights are on. 410 | */ 411 | async function philipsHueServer() { // eslint-disable-line no-inner-declarations 412 | console.log('philipsHue starting philipsHue'); 413 | await setup(); 414 | 415 | // TODO poll more often in production environment 416 | for (var key in lights) { 417 | setInterval(function (light) { 418 | getLightState(light, server.write); 419 | }, 600 + Math.random() * 200, lights[key]); 420 | } 421 | } 422 | 423 | /** 424 | * @param {string} lightId 425 | * @param {Function} writeFn 426 | * @return {Function} read listener callback that invokes writeFn 427 | */ 428 | function onRead(lightId, writeFn) { // eslint-disable-line no-inner-declarations 429 | return function(data) { 430 | writeFn(lights[lightId], data.value); 431 | }; 432 | } 433 | 434 | philipsHueServer(); 435 | } 436 | 437 | -------------------------------------------------------------------------------- /interfaces/philipsHue/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Philips Hue Configurator 6 | 192 | 193 | 194 | 215 | 216 | 224 | 225 | 230 | 231 | 232 | 233 |
234 | If your Hue Hub IP or username is missing please use this to manually 235 | configure your interface with the necessary values. 236 |
237 | 238 |
239 | 240 |
241 | 242 | 424 | 425 | 426 | 427 | -------------------------------------------------------------------------------- /interfaces/mozillaWot/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mozilla WoT Gateway Configurator 6 | 192 | 193 | 194 | 215 | 216 | 224 | 225 | 230 | 231 | 232 | 233 |
234 | If your gateway URL is missing please use this to manually configure your 235 | interface with the necessary values. To add a token, visit your gateway's 236 | Developer Settings page to issue a new local authorization. 237 |
238 | 239 |
240 | 241 |
242 | 243 | 425 | 426 | 427 | 428 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | --------------------------------------------------------------------------------