├── README.md ├── lifx-connector ├── README.md ├── commandCapabilityMappings.json ├── index.js ├── package-lock.json ├── package.json └── requestConfig.json └── viper-virtual-glitch-connector ├── README.md ├── discoveryResponse.json ├── index.js ├── package.json └── refreshResponse.json /README.md: -------------------------------------------------------------------------------- 1 | # st-schema-connectors 2 | Example connectors written for ST Schema 3 | -------------------------------------------------------------------------------- /lifx-connector/README.md: -------------------------------------------------------------------------------- 1 | This code is meant for AWS Lambda 2 | 1. Run `npm install --production` to install all the dependencies 3 | 2. Zip up the contents of the folder (not the root folder, but the contents). If you zip up the root folder, it won't unzip properly for AWS Lambda 4 | 3. Create a new Lambda and upload the code using the `"Upload from Zip"` option 5 | -------------------------------------------------------------------------------- /lifx-connector/commandCapabilityMappings.json: -------------------------------------------------------------------------------- 1 | { 2 | "partnerToSTCapabilityMapping": { 3 | "power": { 4 | "capability": "st.switch", 5 | "attribute": "switch" 6 | }, 7 | "brightness": { 8 | "capability": "st.switchLevel", 9 | "attribute": "level", 10 | "valueMultiplier": 100 11 | }, 12 | "connected": { 13 | "capability": "st.healthCheck", 14 | "attribute": "healthStatus", 15 | "valueMap": { 16 | "true" : "online", 17 | "false": "offline" 18 | } 19 | }, 20 | "color": { 21 | "hue": { 22 | "capability": "st.colorControl", 23 | "attribute": "hue", 24 | "valueMultiplier": 0.2778 25 | }, 26 | "saturation": { 27 | "capability": "st.colorControl", 28 | "attribute": "saturation", 29 | "valueMultiplier": 100 30 | }, 31 | "kelvin": { 32 | "capability": "st.colorTemperature", 33 | "attribute": "colorTemperature", 34 | "valueMultiplier": 1 35 | } 36 | } 37 | }, 38 | "stToPartnerCapabilityMapping": { 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lifx-connector/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const requestConfig = require("./requestConfig.json"); 4 | const commandCapabilityMappings = require("./commandCapabilityMappings.json"); 5 | const { lambda, partnerHelper } = require("st-schema"); 6 | const stPartnerHelper = new partnerHelper(requestConfig, commandCapabilityMappings); 7 | 8 | async function discoveryRequest(request, response) { 9 | const results = await stPartnerHelper.requestBuilder("discoveryRequest", request.authentication.token, null, { "debug": true }); 10 | for (let result of results) { 11 | const device = response.addDevice(result.id, result.label, _getDeviceHandler(result.product.capabilities)); 12 | device.manufacturerName(result.product.company).modelName(result.product.name).hwVersion(result.product.identifier).roomName(result.location.name).addGroup(result.group.name); 13 | } 14 | } 15 | 16 | async function commandRequest(request, response) { 17 | const deviceMap = new Map(); 18 | const lightStates = request.devices.map(({ externalDeviceId, commands }) => { 19 | const newState = { 20 | selector: externalDeviceId 21 | }; 22 | deviceMap.set(externalDeviceId, response.addDevice(externalDeviceId)); 23 | commands.forEach((command) => { 24 | switch (command.capability) { 25 | case "st.switchLevel": 26 | newState.brightness = command.arguments[0] / 100; 27 | break; 28 | case "st.switch": 29 | newState.power = command.command; 30 | break; 31 | case "st.colorTemperature": 32 | newState.color = "kelvin:" + command.arguments[0]; 33 | break; 34 | case "st.colorControl": 35 | newState.color = "hue:" + command.arguments[0].hue * 3.6 + " saturation:" + command.arguments[0].saturation / 100 36 | break; 37 | default: 38 | console.log("Unknown capability: " + command.capability); 39 | return; 40 | } 41 | }); 42 | return newState; 43 | }); 44 | let results = await stPartnerHelper.requestBuilder("commandRequest", request.authentication.token, {"states" : lightStates}, {"debug": true}) 45 | for (let result of results.results) { //lifx api does not return state back in response 46 | if (result.results[0].status !== "ok" || !deviceMap.get(result.results[0].id)) { continue; } 47 | let device = deviceMap.get(result.results[0].id); 48 | stPartnerHelper.mapSTCommandsToState(device, request.devices[0].commands) 49 | } 50 | } 51 | 52 | async function stateRefreshRequest(request, response) { 53 | let deviceMap = new Map(); 54 | request.devices.map(({ externalDeviceId }) => { 55 | deviceMap.set(externalDeviceId, response.addDevice(externalDeviceId)); 56 | }); 57 | 58 | let results = await stPartnerHelper.requestBuilder("stateRefreshRequest", request.authentication.token, null, {"debug": true}); 59 | for (let result of results) { 60 | let device = deviceMap.get(result.id); 61 | if (!device) { continue; } 62 | stPartnerHelper.mapPartnerToSTCapability(device, result) 63 | } 64 | } 65 | 66 | module.exports.handler = lambda({ 67 | discoveryRequest, 68 | commandRequest, 69 | stateRefreshRequest 70 | }); 71 | 72 | function _getDeviceHandler(lifxCapabilities) { 73 | if (lifxCapabilities.has_color) { 74 | return "c2c-rgbw-color-bulb" 75 | } else { 76 | return "c2c-color-temperature-bulb" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lifx-connector/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viper-lifx", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "6.10.0", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", 10 | "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", 11 | "requires": { 12 | "fast-deep-equal": "^2.0.1", 13 | "fast-json-stable-stringify": "^2.0.0", 14 | "json-schema-traverse": "^0.4.1", 15 | "uri-js": "^4.2.2" 16 | } 17 | }, 18 | "asn1": { 19 | "version": "0.2.4", 20 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 21 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 22 | "requires": { 23 | "safer-buffer": "~2.1.0" 24 | } 25 | }, 26 | "assert-plus": { 27 | "version": "1.0.0", 28 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 29 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 30 | }, 31 | "asynckit": { 32 | "version": "0.4.0", 33 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 34 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 35 | }, 36 | "aws-sign2": { 37 | "version": "0.7.0", 38 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 39 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 40 | }, 41 | "aws4": { 42 | "version": "1.8.0", 43 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 44 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 45 | }, 46 | "bcrypt-pbkdf": { 47 | "version": "1.0.2", 48 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 49 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 50 | "requires": { 51 | "tweetnacl": "^0.14.3" 52 | } 53 | }, 54 | "caseless": { 55 | "version": "0.12.0", 56 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 57 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 58 | }, 59 | "combined-stream": { 60 | "version": "1.0.8", 61 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 62 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 63 | "requires": { 64 | "delayed-stream": "~1.0.0" 65 | } 66 | }, 67 | "core-util-is": { 68 | "version": "1.0.2", 69 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 70 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 71 | }, 72 | "dashdash": { 73 | "version": "1.14.1", 74 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 75 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 76 | "requires": { 77 | "assert-plus": "^1.0.0" 78 | } 79 | }, 80 | "delayed-stream": { 81 | "version": "1.0.0", 82 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 83 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 84 | }, 85 | "ecc-jsbn": { 86 | "version": "0.1.2", 87 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 88 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 89 | "requires": { 90 | "jsbn": "~0.1.0", 91 | "safer-buffer": "^2.1.0" 92 | } 93 | }, 94 | "extend": { 95 | "version": "3.0.2", 96 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 97 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 98 | }, 99 | "extsprintf": { 100 | "version": "1.3.0", 101 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 102 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 103 | }, 104 | "fast-deep-equal": { 105 | "version": "2.0.1", 106 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 107 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 108 | }, 109 | "fast-json-stable-stringify": { 110 | "version": "2.0.0", 111 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 112 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 113 | }, 114 | "forever-agent": { 115 | "version": "0.6.1", 116 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 117 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 118 | }, 119 | "form-data": { 120 | "version": "2.3.3", 121 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 122 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 123 | "requires": { 124 | "asynckit": "^0.4.0", 125 | "combined-stream": "^1.0.6", 126 | "mime-types": "^2.1.12" 127 | } 128 | }, 129 | "getpass": { 130 | "version": "0.1.7", 131 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 132 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 133 | "requires": { 134 | "assert-plus": "^1.0.0" 135 | } 136 | }, 137 | "har-schema": { 138 | "version": "2.0.0", 139 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 140 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 141 | }, 142 | "har-validator": { 143 | "version": "5.1.3", 144 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 145 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 146 | "requires": { 147 | "ajv": "^6.5.5", 148 | "har-schema": "^2.0.0" 149 | } 150 | }, 151 | "http-signature": { 152 | "version": "1.2.0", 153 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 154 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 155 | "requires": { 156 | "assert-plus": "^1.0.0", 157 | "jsprim": "^1.2.2", 158 | "sshpk": "^1.7.0" 159 | } 160 | }, 161 | "is-typedarray": { 162 | "version": "1.0.0", 163 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 164 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 165 | }, 166 | "isstream": { 167 | "version": "0.1.2", 168 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 169 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 170 | }, 171 | "jsbn": { 172 | "version": "0.1.1", 173 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 174 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 175 | }, 176 | "json-schema": { 177 | "version": "0.2.3", 178 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 179 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 180 | }, 181 | "json-schema-traverse": { 182 | "version": "0.4.1", 183 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 184 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 185 | }, 186 | "json-stringify-safe": { 187 | "version": "5.0.1", 188 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 189 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 190 | }, 191 | "jsprim": { 192 | "version": "1.4.1", 193 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 194 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 195 | "requires": { 196 | "assert-plus": "1.0.0", 197 | "extsprintf": "1.3.0", 198 | "json-schema": "0.2.3", 199 | "verror": "1.10.0" 200 | } 201 | }, 202 | "lodash": { 203 | "version": "4.17.11", 204 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 205 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 206 | }, 207 | "mime-db": { 208 | "version": "1.40.0", 209 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 210 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 211 | }, 212 | "mime-types": { 213 | "version": "2.1.24", 214 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 215 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 216 | "requires": { 217 | "mime-db": "1.40.0" 218 | } 219 | }, 220 | "oauth-sign": { 221 | "version": "0.9.0", 222 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 223 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 224 | }, 225 | "performance-now": { 226 | "version": "2.1.0", 227 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 228 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 229 | }, 230 | "psl": { 231 | "version": "1.1.31", 232 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", 233 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" 234 | }, 235 | "punycode": { 236 | "version": "2.1.1", 237 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 238 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 239 | }, 240 | "qs": { 241 | "version": "6.5.2", 242 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 243 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 244 | }, 245 | "request": { 246 | "version": "2.88.0", 247 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 248 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 249 | "requires": { 250 | "aws-sign2": "~0.7.0", 251 | "aws4": "^1.8.0", 252 | "caseless": "~0.12.0", 253 | "combined-stream": "~1.0.6", 254 | "extend": "~3.0.2", 255 | "forever-agent": "~0.6.1", 256 | "form-data": "~2.3.2", 257 | "har-validator": "~5.1.0", 258 | "http-signature": "~1.2.0", 259 | "is-typedarray": "~1.0.0", 260 | "isstream": "~0.1.2", 261 | "json-stringify-safe": "~5.0.1", 262 | "mime-types": "~2.1.19", 263 | "oauth-sign": "~0.9.0", 264 | "performance-now": "^2.1.0", 265 | "qs": "~6.5.2", 266 | "safe-buffer": "^5.1.2", 267 | "tough-cookie": "~2.4.3", 268 | "tunnel-agent": "^0.6.0", 269 | "uuid": "^3.3.2" 270 | } 271 | }, 272 | "safe-buffer": { 273 | "version": "5.1.2", 274 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 275 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 276 | }, 277 | "safer-buffer": { 278 | "version": "2.1.2", 279 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 280 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 281 | }, 282 | "sshpk": { 283 | "version": "1.16.1", 284 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 285 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 286 | "requires": { 287 | "asn1": "~0.2.3", 288 | "assert-plus": "^1.0.0", 289 | "bcrypt-pbkdf": "^1.0.0", 290 | "dashdash": "^1.12.0", 291 | "ecc-jsbn": "~0.1.1", 292 | "getpass": "^0.1.1", 293 | "jsbn": "~0.1.0", 294 | "safer-buffer": "^2.0.2", 295 | "tweetnacl": "~0.14.0" 296 | } 297 | }, 298 | "st-schema": { 299 | "version": "1.0.7", 300 | "resolved": "https://registry.npmjs.org/st-schema/-/st-schema-1.0.7.tgz", 301 | "integrity": "sha512-6jE+fYLjwP20bbonxc5UYPUzY10Spmz5DlLwF4fR6tASTJTdF9n9WOaWwtkzt+/H0pXhPEyYLDWE69aeg+LPzg==", 302 | "requires": { 303 | "lodash": "4.17.11", 304 | "request": "2.88.0" 305 | } 306 | }, 307 | "tough-cookie": { 308 | "version": "2.4.3", 309 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 310 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 311 | "requires": { 312 | "psl": "^1.1.24", 313 | "punycode": "^1.4.1" 314 | }, 315 | "dependencies": { 316 | "punycode": { 317 | "version": "1.4.1", 318 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 319 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 320 | } 321 | } 322 | }, 323 | "tunnel-agent": { 324 | "version": "0.6.0", 325 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 326 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 327 | "requires": { 328 | "safe-buffer": "^5.0.1" 329 | } 330 | }, 331 | "tweetnacl": { 332 | "version": "0.14.5", 333 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 334 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 335 | }, 336 | "uri-js": { 337 | "version": "4.2.2", 338 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 339 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 340 | "requires": { 341 | "punycode": "^2.1.0" 342 | } 343 | }, 344 | "uuid": { 345 | "version": "3.3.2", 346 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 347 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 348 | }, 349 | "verror": { 350 | "version": "1.10.0", 351 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 352 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 353 | "requires": { 354 | "assert-plus": "^1.0.0", 355 | "core-util-is": "1.0.2", 356 | "extsprintf": "^1.2.0" 357 | } 358 | } 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /lifx-connector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viper-lifx", 3 | "version": "1.0.0", 4 | "description": "LifX ST schema implementation", 5 | "main": "index.js", 6 | "keywords": [ 7 | "st-schema", 8 | "lifx" 9 | ], 10 | "author": "SmartThings", 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "st-schema": "1.0.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lifx-connector/requestConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "https://api.lifx.com/v1/lights", 3 | "headers": { 4 | "Accept": "*/*" 5 | }, 6 | "discoveryRequest": { 7 | "uri": "/all", 8 | "method": "GET" 9 | }, 10 | "stateRefreshRequest": { 11 | "uri": "/all", 12 | "method": "GET" 13 | }, 14 | "commandRequest": { 15 | "uri": "/states", 16 | "method": "PUT" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /viper-virtual-glitch-connector/README.md: -------------------------------------------------------------------------------- 1 | Getting started with SmartThings: 2 | 3 | 1. Remix the project here -> https://glitch.com/~smartthings-connector. 4 | 2. Update discoveryResponse.json and refreshResponse.json with the smart device details. (optional) 5 | 3. Once the remixed app is up and running, you have a webhook url ready. 6 | 4. Register the webhook url in Samsung developer workspace and deploy it -> https://smartthings.developer.samsung.com/workspace/projects/new 7 | 5. On the SmartThings mobile app (on App Store or Google play store) you can add the smart device and control it. 8 | 9 | -------------------------------------------------------------------------------- /viper-virtual-glitch-connector/discoveryResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": { 3 | "schema": "st-schema", 4 | "version": "1.0", 5 | "interactionType": "discoveryResponse", 6 | "requestId": "abc-123-456" 7 | }, 8 | "devices": [ 9 | { 10 | "externalDeviceId": "fake-c2c-dimmer", 11 | "friendlyName": "Virtual Switch", 12 | "deviceHandlerType": "c2c-dimmer", 13 | "manufacturerInfo": { 14 | "manufacturerName": "SmartThings", 15 | "modelName": "Virtual Viper device", 16 | "hwVersion": "v1 US bulb", 17 | "swVersion": "23.123.231" 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /viper-virtual-glitch-connector/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | let express = require('express'); 3 | let bodyParser = require('body-parser'); 4 | 5 | let app = express(); 6 | app.use(bodyParser.json({type: 'application/json'})); 7 | 8 | let discoveryResponse = require("./discoveryResponse.json"); 9 | const refreshResponse = require("./refreshResponse.json"); 10 | const { partnerHelper, CommandResponse } = require("st-schema"); 11 | const stPartnerHelper = new partnerHelper({}, {}); 12 | 13 | function discoveryRequest(requestId) { 14 | discoveryResponse.headers.requestId = requestId 15 | console.log(discoveryResponse); 16 | return discoveryResponse 17 | } 18 | 19 | function commandRequest(request) { 20 | let response = new CommandResponse(request.headers.requestId) 21 | request.body.devices.map(({ externalDeviceId, deviceCookie, commands }) => { 22 | const device = response.addDevice(externalDeviceId, deviceCookie); 23 | stPartnerHelper.mapSTCommandsToState(device, commands) 24 | }); 25 | console.log("response: %j", response); 26 | return response; 27 | } 28 | 29 | function stateRefreshRequest(request) { 30 | let response = { "headers": { "schema": "st-schema", "version": "1.0", "interactionType": "stateRefreshResponse", "requestId": "abc-123-456" }, "deviceState": [] }; 31 | request.body.devices.map(({ externalDeviceId, deviceCookie }) => { 32 | console.log("externalDeviceId: ", externalDeviceId); 33 | let deviceResponse = refreshResponse[externalDeviceId]; 34 | response.deviceState.push(deviceResponse) 35 | console.log("deviceResponse: ", deviceResponse); 36 | }); 37 | 38 | console.log(response); 39 | return response; 40 | } 41 | 42 | function grantCallbackAccess(request) { 43 | console.log("grantCallbackAccess token is:", request.callbackAuthentication.code) 44 | console.log("grantCallbackAccess clientId is:", request.callbackAuthentication.clientId) 45 | return {}; 46 | } 47 | 48 | 49 | // Renders the homepage 50 | app.get('/', function (req, res) { 51 | res.writeHead(200, {'Content-Type': 'application/json'}); 52 | res.write(JSON.stringify(discoveryResponse)); 53 | res.end(); 54 | }); 55 | 56 | 57 | // [START Action] 58 | app.post('/', function (req, res) { 59 | console.log('Request received: ' + JSON.stringify(req.body)); 60 | 61 | let response 62 | const { headers, authentication, devices } = req; 63 | const { interactionType1, requestId } = headers; 64 | const interactionType = req.body.headers.interactionType; 65 | console.log("request type: ", interactionType); 66 | try { 67 | switch (interactionType) { 68 | case "discoveryRequest": 69 | response = discoveryRequest(req.headers.requestId) 70 | break; 71 | case "commandRequest": 72 | response = commandRequest(req) 73 | break; 74 | case "stateRefreshRequest": 75 | response = stateRefreshRequest(req) 76 | break; 77 | case "grantCallbackAccess": 78 | response = grantCallbackAccess(req) 79 | break; 80 | default: 81 | response = "error. not supported interactionType" + interactionType 82 | console.log(response); 83 | break; 84 | } 85 | } catch (ex) { 86 | console.log("failed with ex", ex) 87 | } 88 | 89 | res.send(response); 90 | 91 | }); 92 | 93 | 94 | if (module === require.main) { 95 | // [START server] 96 | let server = app.listen(process.env.PORT || 3000, function () { 97 | let port = server.address().port; 98 | console.log('App listening on port %s', port); 99 | }); 100 | // [END server] 101 | } 102 | 103 | module.exports = app; -------------------------------------------------------------------------------- /viper-virtual-glitch-connector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viper-virtual-glitch", 3 | "version": "1.0.0", 4 | "description": "Virtual ST schema implementation", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [ 10 | "Viper-Virtual-Glitch" 11 | ], 12 | "author": "SmartThings", 13 | "license": "Apache-2.0", 14 | "dependencies": { 15 | "body-parser": "^1.15.2", 16 | "express": "^4.13.4", 17 | "st-schema": "1.0.7" 18 | } 19 | } -------------------------------------------------------------------------------- /viper-virtual-glitch-connector/refreshResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "fake-c2c-dimmer": { 4 | "externalDeviceId": "fake-c2c-dimmer", 5 | "states": [ 6 | { 7 | "component": "main", 8 | "capability": "st.switch", 9 | "attribute": "switch", 10 | "value": "on" 11 | }, 12 | { 13 | "component": "main", 14 | "capability": "st.switchLevel", 15 | "attribute": "level", 16 | "value": 10 17 | }, 18 | { 19 | "component": "main", 20 | "capability": "st.healthCheck", 21 | "attribute": "healthStatus", 22 | "value": "online" 23 | } 24 | ] 25 | } 26 | } --------------------------------------------------------------------------------