├── Screen_Shot.png ├── teslams ├── icons │ └── tesla.png ├── teslams.html └── teslams.js ├── flows ├── Example_Flow_Screen Shot.png ├── Example_Flow.json └── TeslaMS Sample Logger.json ├── .gitignore ├── README.md └── package.json /Screen_Shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjespers/node-red-contrib-teslams/HEAD/Screen_Shot.png -------------------------------------------------------------------------------- /teslams/icons/tesla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjespers/node-red-contrib-teslams/HEAD/teslams/icons/tesla.png -------------------------------------------------------------------------------- /flows/Example_Flow_Screen Shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjespers/node-red-contrib-teslams/HEAD/flows/Example_Flow_Screen Shot.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | credentials.json 3 | flows*.json 4 | flows.backup 5 | *_cred* 6 | nodes/node-red-nodes/ 7 | .npm 8 | /coverage 9 | .config.json 10 | *.log 11 | -------------------------------------------------------------------------------- /flows/Example_Flow.json: -------------------------------------------------------------------------------- 1 | [{"id":"74c71a60.8b38e4","type":"debug","name":"lock result","active":true,"console":"false","complete":"false","x":632.0001525878906,"y":310.020263671875,"z":"1961a857.e69e58","wires":[]},{"id":"a0f9122a.5f06f","type":"debug","name":"flash result","active":true,"console":"false","complete":"false","x":639.5252532958984,"y":56.8181095123291,"z":"1961a857.e69e58","wires":[]},{"id":"2de8221b.d217de","type":"command","name":"flash lights","command":"flash","x":475.39394760131836,"y":66.23225975036621,"z":"1961a857.e69e58","wires":[["a0f9122a.5f06f"]]},{"id":"e9ca3752.1635c8","type":"command","name":"honk horn","command":"honk","x":472.8384094238281,"y":181.19927978515625,"z":"1961a857.e69e58","wires":[["a856e3df.57a92"]]},{"id":"a856e3df.57a92","type":"debug","name":"horn result","active":true,"console":"false","complete":"false","x":648.7778015136719,"y":167.97703552246094,"z":"1961a857.e69e58","wires":[]},{"id":"e3c505ab.1c3af8","type":"inject","name":"auth","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":91.2828369140625,"y":37.97704315185547,"z":"1961a857.e69e58","wires":[["1f9403ae.e06bfc"]]},{"id":"1f9403ae.e06bfc","type":"login","name":"","x":194.6287841796875,"y":128.2523193359375,"z":"1961a857.e69e58","wires":[["42966591.bd699c"]]},{"id":"42966591.bd699c","type":"json","name":"","x":277.2550926208496,"y":232.6794147491455,"z":"1961a857.e69e58","wires":[["e9ca3752.1635c8","2de8221b.d217de","dda43a8c.225bc8"]]},{"id":"dda43a8c.225bc8","type":"sunroof","name":"","state":"vent","x":463.0909118652344,"y":312.0909118652344,"z":"1961a857.e69e58","wires":[["74c71a60.8b38e4"]]},{"id":"5700b815.a8ff48","type":"comment","name":"remember to add your Tesla login/password to the login node","info":"","x":222.09091186523438,"y":370.99999237060547,"z":"1961a857.e69e58","wires":[]},{"id":"1d65df2b.e29a21","type":"comment","name":"add a command node configured to wake_up the car (and a delay node) to handle a sleeping car","info":"","x":334,"y":417.09092712402344,"z":"1961a857.e69e58","wires":[]}] -------------------------------------------------------------------------------- /flows/TeslaMS Sample Logger.json: -------------------------------------------------------------------------------- 1 | [{"id":"723049eb.8dcfb8","type":"command","name":"","command":"get_drive_state","x":544.5351867675781,"y":122.15148162841797,"z":"84db06cb.7b24f8","wires":[["b57c8b56.4a8378","e9e78603.161878"]]},{"id":"b57c8b56.4a8378","type":"debug","name":"drive state data","active":true,"console":"false","complete":"true","x":681.4344482421875,"y":34.09089660644531,"z":"84db06cb.7b24f8","wires":[]},{"id":"47d2aff8.b82d5","type":"delay","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":353.5453338623047,"y":126.81817626953125,"z":"84db06cb.7b24f8","wires":[["723049eb.8dcfb8","936f6d76.6c909"]]},{"id":"a72cfb51.58d308","type":"inject","name":"auth","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":97.33330535888672,"y":59,"z":"84db06cb.7b24f8","wires":[["9fb2077c.604df8"]]},{"id":"9fb2077c.604df8","type":"login","name":"","x":126.48734283447266,"y":120.2752685546875,"z":"84db06cb.7b24f8","wires":[["f0e5fa1.f0f1a08"]]},{"id":"f0e5fa1.f0f1a08","type":"json","name":"","x":161.22473907470703,"y":182.92459106445312,"z":"84db06cb.7b24f8","wires":[["47d2aff8.b82d5"]]},{"id":"e9e78603.161878","type":"csv","name":"drive state data","sep":",","hdrin":"","hdrout":false,"multi":"one","ret":"\\n","temp":"shift_state, speed, latitude, longitude, heading, gps_as_of","x":587.0910949707031,"y":200.90907287597656,"z":"84db06cb.7b24f8","wires":[["e7d91b04.1826e8","24151842.dbeae8"]]},{"id":"e7d91b04.1826e8","type":"debug","name":"in csv form","active":true,"console":"false","complete":"false","x":742.9092102050781,"y":142.4545669555664,"z":"84db06cb.7b24f8","wires":[]},{"id":"24151842.dbeae8","type":"file","name":"/tmp/tesla_data.csv","filename":"/tmp/tesla_data.csv","appendNewline":false,"overwriteFile":false,"x":714.9091033935547,"y":297.90909576416016,"z":"84db06cb.7b24f8","wires":[]},{"id":"936f6d76.6c909","type":"delay","name":"","pauseType":"delay","timeout":"14","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":354.27272033691406,"y":213.7273178100586,"z":"84db06cb.7b24f8","wires":[["47d2aff8.b82d5"]]},{"id":"3679215c.c986de","type":"comment","name":"Example Data Logger","info":"This example polls the Tesla every 15 seconds for its\ndrive state data and then logs the results in a CSV file\nor analysis and visualization.","x":336.0909118652344,"y":47.090911865234375,"z":"84db06cb.7b24f8","wires":[]}] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-red-contrib-teslams 2 | ======================== 3 | 4 | Node-Red (http://nodered.org) nodes for communicating with a Tesla Model S electric car. 5 | Based on the teslams javascript libraries (https://github.com/hjespers/teslams). 6 | 7 | 8 | 9 | These programs and documentation do not come from Tesla Motors Inc. 10 | 11 | Be careful when using these programs as they can lock and unlock your car as well as control various functions relating to the charging system, sun roof, headlights, horn, climate control, and other subsystems of the car. 12 | 13 | Be careful not to send your login and password to anyone other than Tesla or you are giving away the authentication details required to control your car. 14 | 15 | Also ensure that you don't overwhelm the Tesla servers with requests. Calling functions at very high frequency can put substantial load on the Tesla servers and might get your IP blocked by Tesla. Tesla might also reset your login for too many failed login attempts. 16 | 17 | 18 | #Install 19 | 20 | Run the following command in the root directory of your Node-RED install 21 | 22 | npm install node-red-contrib-teslams 23 | 24 | 25 | #Usage 26 | 27 | You will need a Tesla Model S. The ultimate "thing" to connect to the Internet of Things. 28 | You will need an owners account on the Telsa Motors Inc. official web site (http://www.teslamotors.com) associated with your car(s). 29 | Execute the "login" node at least once to get the appropriate security tokens for all the other nodes. 30 | All nodes besides the "login" node require the vehicle "id" to be in the input msg.payload. 31 | 32 | 33 | #Disclaimer 34 | 35 | Use these programs at your own risk. The author does not guaranteed the proper functioning of these applications. This code attempts to use the same interfaces used by the official Tesla phone apps. However, it is possible that use of this code may cause unexpected damage for which nobody but you are responsible. Use of these functions can change the settings on your car and may have negative consequences such as (but not limited to) unlocking the doors, opening the sun roof, or reducing the available charge in the battery. 36 | 37 | #Author 38 | 39 | Hans Jespersen, https://github.com/hjespers 40 | 41 | #Feedback and Support 42 | 43 | For more information, feedback, or community support see the Tesla Motors Club forum at http://www.teslamotorsclub.com/showthread.php/20325-TeslaMS-tools-for-telemetry-data-visualization or email teslams@googlegroups.com 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-teslams", 3 | "version": "0.3.0", 4 | "description": "Nodes for controlling and getting real-time telemetry from a Tesla Model S using node-red", 5 | "dependencies": { 6 | "teslams": ">=1.3.0", 7 | "json-bigint": ">= 0.1.4" 8 | }, 9 | "keywords": [ 10 | "node-red", 11 | "tesla", 12 | "teslams" 13 | ], 14 | "node-red": { 15 | "nodes": { 16 | "teslams": "teslams/teslams.js" 17 | } 18 | }, 19 | "main": "teslams/teslams.js", 20 | "scripts": { 21 | "test": "echo \"Error: no test specified\" && exit 1" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/hjespers/node-red-contrib-teslams.git" 26 | }, 27 | "author": { 28 | "name": "Hans Jespersen" 29 | }, 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/hjespers/node-red-contrib-teslams/issues" 33 | }, 34 | "homepage": "https://github.com/hjespers/node-red-contrib-teslams", 35 | "readme": "node-red-contrib-teslams\n========================\n\nNode-Red (http://nodered.org) nodes for communicating with a Tesla Model S electric car. \nBased on the teslams javascript libraries (https://github.com/hjespers/teslams).\n\n\n\nThese programs and documentation do not come from Tesla Motors Inc.\n\nBe careful when using these programs as they can lock and unlock your car as well as control various functions relating to the charging system, sun roof, headlights, horn, climate control, and other subsystems of the car.\n\nBe careful not to send your login and password to anyone other than Tesla or you are giving away the authentication details required to control your car.\n\nAlso ensure that you don't overwhelm the Tesla servers with requests. Calling functions at very high frequency can put substantial load on the Tesla servers and might get your IP blocked by Tesla. Tesla might also reset your login for too many failed login attempts.\n\n\n#Install\n\nRun the following command in the root directory of your Node-RED install\n\n npm install node-red-contrib-teslams\n\n\n#Usage\n\nYou will need a Tesla Model S. The ultimate \"thing\" to connect to the Internet of Things.\nYou will need an owners account on the Telsa Motors Inc. official web site (http://www.teslamotors.com) associated with your car(s).\nExecute the \"login\" node at least once to get the appropriate security tokens for all the other nodes.\nAll nodes besides the \"login\" node require the vehicle \"id\" to be in the input msg.payload. \n\n\n#Disclaimer\n\nUse these programs at your own risk. The author does not guaranteed the proper functioning of these applications. This code attempts to use the same interfaces used by the official Tesla phone apps. However, it is possible that use of this code may cause unexpected damage for which nobody but you are responsible. Use of these functions can change the settings on your car and may have negative consequences such as (but not limited to) unlocking the doors, opening the sun roof, or reducing the available charge in the battery.\n\n# Author\n\nHans Jespersen, https://github.com/hjespers\n\n#Feedback and Support\n\nFor more information, feedback, or community support see the Tesla Motors Club forum at http://www.teslamotorsclub.com/showthread.php/20325-TeslaMS-tools-for-telemetry-data-visualization or email teslams@googlegroups.com\n", 36 | "readmeFilename": "README.md", 37 | "_id": "node-red-contrib-teslams@0.2.0", 38 | "_from": "node-red-contrib-teslams@" 39 | } 40 | -------------------------------------------------------------------------------- /teslams/teslams.html: -------------------------------------------------------------------------------- 1 | <- 2 | By Hans Jespersen 3 | -> 4 | 5 | 24 | 25 | 28 | 29 | 53 | 54 | 75 | 76 | 79 | 80 | 100 | 101 | 115 | 116 | 119 | 120 | 140 | 141 | 157 | 158 | 161 | 162 | 182 | 183 | 197 | 198 | 201 | 202 | 222 | 223 | <- TODO make range input with a slider bar input instead of number -> 224 | 225 | 237 | 238 | 245 | 246 | 266 | 267 | 281 | 282 | 285 | 286 | 306 | 307 | 321 | 322 | 325 | 326 | 347 | 348 | -------------------------------------------------------------------------------- /teslams/teslams.js: -------------------------------------------------------------------------------- 1 | // By Hans Jespersen 2 | var util = require('util'); 3 | var JSONbig = require('json-bigint'); 4 | 5 | module.exports = function(RED) { 6 | "use strict"; 7 | var teslams = require('teslams'); 8 | 9 | var carNum = 0; 10 | 11 | function TeslaLogin(n) { 12 | 13 | RED.nodes.createNode(this,n); 14 | var node = this; 15 | var tesla_email = this.credentials.user; 16 | var tesla_password = this.credentials.password; 17 | var tesla_token = this.credentials.token; 18 | 19 | var logindata = ""; 20 | if(tesla_token != "") 21 | { 22 | logindata = { email: tesla_email, token: tesla_token }; 23 | } 24 | else 25 | { 26 | logindata = { email: tesla_email, password: tesla_password }; 27 | } 28 | 29 | this.on("input", function(msg) { 30 | var outmsg = { 31 | topic: msg.topic 32 | }; 33 | carNum = parseInt(msg.payload); 34 | try{ 35 | teslams.all( logindata, function ( error, response, body ) { 36 | var data, vehicle, e; 37 | //check we got a valid JSON response from Tesla 38 | //util.inspect( 'response is: \n' + response ); 39 | try { 40 | data = JSONbig.parse(body); 41 | 42 | //check we got an array of vehicles and get the first one 43 | if (!util.isArray(data.response)) { 44 | util.inspect( data.response ); 45 | e = new Error('expecting an array from Tesla Motors cloud service'); 46 | util.log('[teslams] ' + e); 47 | outmsg.payload = e; 48 | node.send(outmsg); 49 | } else { 50 | vehicle = data.response[0]; 51 | //check the vehicle has a valid id 52 | if (vehicle === undefined || vehicle.id === undefined) { 53 | e = new Error('expecting vehicle ID from Tesla Motors cloud service'); 54 | util.log('[teslams] ' + e ); 55 | outmsg.payload = e; 56 | node.send(outmsg); 57 | } else { 58 | vehicle.id = vehicle.id.toString(); 59 | vehicle.vehicle_id = vehicle.vehicle_id.toString(); 60 | outmsg.payload = JSON.stringify(new Array(vehicle) ); 61 | node.send(outmsg); 62 | } 63 | } 64 | } catch(err) { 65 | console.log('[teslams] Telsa login error: ' + err); 66 | if(response != undefined) 67 | console.log('[teslams] Response: ' + response); 68 | outmsg.payload = err; 69 | node.send(outmsg); 70 | return; //some error will crash node-red if we don't exit here 71 | } 72 | //check we got an array of vehicles and get the first one 73 | if (!util.isArray(data.response)) { 74 | util.inspect( data.response ); 75 | e = new Error('expecting an array from Tesla Motors cloud service'); 76 | util.log('[teslams] ' + e); 77 | outmsg.payload = e; 78 | node.send(outmsg); 79 | } else { 80 | vehicle = data.response[carNum]; 81 | //check the vehicle has a valid id 82 | if (vehicle === undefined || vehicle.id === undefined) { 83 | e = new Error('expecting vehicle ID from Tesla Motors cloud service'); 84 | util.log('[teslams] ' + e ); 85 | outmsg.payload = e; 86 | node.send(outmsg); 87 | } else { 88 | vehicle.id = vehicle.id.toString(); 89 | vehicle.vehicle_id = vehicle.vehicle_id.toString(); 90 | outmsg.payload = JSON.stringify(new Array(vehicle) ); 91 | node.send(outmsg); 92 | } 93 | } 94 | }); 95 | } catch (e) { 96 | util.log('[teslams] Telsa login error: ' + e); 97 | outmsg.payload = e; 98 | node.send(outmsg); 99 | } 100 | }); 101 | } 102 | RED.nodes.registerType("login",TeslaLogin,{ 103 | credentials: { 104 | user: {type:"text"}, 105 | password: {type: "password"}, 106 | token: {type: "text"} 107 | } 108 | }); 109 | 110 | function TeslaCmd(n) { 111 | RED.nodes.createNode(this,n); 112 | var node = this; 113 | var commandType = n.command; 114 | 115 | this.on("input", function(msg) { 116 | var outmsg = { 117 | topic: msg.topic 118 | }; 119 | var vid = null; 120 | //console.log( 'tesla command node called with msg: ' + util.inspect(msg) ); 121 | //console.log( 'tesla command type: ' + commandType ); 122 | //console.log( 'this looks like: ' + util.inspect(this) ); 123 | //console.log( 'n looks like: ' + util.inspect(n) ); 124 | 125 | var vehicles = msg.payload; 126 | //console.log( 'tesla command node called with vehicles: ' + vehicles ); 127 | if ( vehicles instanceof Array) { 128 | var vehicle = vehicles[0]; //first car in the vehicles array 129 | //console.log( 'tesla command node called with vehicle: ' + vehicle ); 130 | if(vehicle.id) { 131 | vid = vehicle.id; // the three digit vehicle "id" 132 | } 133 | } else { 134 | console.log( 'tesla command node got incorrect vehicle array on input: ' + msg.payload); 135 | outmsg.payload = msg.payload; 136 | node.send(outmsg); 137 | } 138 | //console.log( 'tesla command node called with vid: ' + vid ); 139 | 140 | if (vid) { 141 | switch(commandType) { 142 | case "flash": 143 | teslams.flash( vid, function (resp) { 144 | outmsg.payload = resp; 145 | node.send(outmsg); 146 | }); 147 | break; 148 | case "honk": 149 | teslams.honk( vid, function (resp) { 150 | outmsg.payload = resp; 151 | node.send(outmsg); 152 | }); 153 | break; 154 | case "get_drive_state": 155 | teslams.get_drive_state( vid, function (resp) { 156 | outmsg.payload = resp; 157 | node.send(outmsg); 158 | }); 159 | break; 160 | case "get_vehicle_state": 161 | teslams.get_vehicle_state( vid, function (resp) { 162 | outmsg.payload = resp; 163 | node.send(outmsg); 164 | }); 165 | break; 166 | case "get_climate_state": 167 | teslams.get_climate_state( vid, function (resp) { 168 | outmsg.payload = resp; 169 | node.send(outmsg); 170 | }); 171 | break; 172 | case "get_charge_state": 173 | teslams.get_charge_state( vid, function ( resp ) { 174 | outmsg.payload = resp; 175 | node.send(outmsg); 176 | }); 177 | break; 178 | case "get_gui_settings": 179 | teslams.get_gui_settings( vid, function ( resp ) { 180 | outmsg.payload = resp; 181 | node.send(outmsg); 182 | }); 183 | break; 184 | case "open_charge_port": 185 | teslams.open_charge_port( vid, function ( resp ) { 186 | outmsg.payload = resp; 187 | node.send(outmsg); 188 | }); 189 | break; 190 | case "wake_up": 191 | teslams.wake_up( vid, function ( resp ) { 192 | outmsg.payload = resp; 193 | node.send(outmsg); 194 | }); 195 | break; 196 | default: 197 | console.log('undefined commandType'); 198 | } 199 | } else { 200 | console.log('undefined vid, skipping calls to tesla'); 201 | } 202 | }); 203 | } 204 | RED.nodes.registerType("command",TeslaCmd); 205 | 206 | function TeslaLock(n) { 207 | RED.nodes.createNode(this,n); 208 | var node = this; 209 | var state = n.state; 210 | 211 | this.on("input", function(msg) { 212 | var outmsg = { 213 | topic: msg.topic 214 | }; 215 | var vid = null; 216 | //console.log( 'tesla lock node called with msg: ' + util.inspect(msg) ); 217 | //console.log( 'tesla lock type: ' + commandType ); 218 | //console.log( 'this looks like: ' + util.inspect(this) ); 219 | //console.log( 'n looks like: ' + util.inspect(n) ); 220 | 221 | var vehicles = msg.payload; 222 | //console.log( 'tesla command node called with vehicles: ' + vehicles ); 223 | if ( vehicles instanceof Array) { 224 | var vehicle = vehicles[0]; //first car in the vehicles array 225 | //console.log( 'tesla command node called with vehicle: ' + vehicle ); 226 | if(vehicle.id) { 227 | vid = vehicle.id; // the three digit vehicle "id" 228 | } 229 | } else { 230 | console.log( 'tesla command node called with unexpected input: ' + msg.payload); 231 | outmsg.payload = msg.payload; 232 | node.send(outmsg); 233 | } 234 | //console.log( 'tesla command node called with vid: ' + vid ); 235 | 236 | if (vid) { 237 | switch(state) { 238 | case "lock": 239 | teslams.door_lock( {id: vid, lock: "lock" }, function (resp) { 240 | outmsg.payload = resp; 241 | node.send(outmsg); 242 | }); 243 | break; 244 | case "unlock": 245 | teslams.door_lock( {id: vid, lock: "unlock"}, function (resp) { 246 | outmsg.payload = resp; 247 | node.send(outmsg); 248 | }); 249 | break; 250 | default: 251 | console.log('undefined lock state'); 252 | } 253 | } else { 254 | console.log('undefined vid, skipping calls to tesla'); 255 | } 256 | }); 257 | } 258 | RED.nodes.registerType("lock",TeslaLock); 259 | 260 | function TeslaSunroof(n) { 261 | RED.nodes.createNode(this,n); 262 | var node = this; 263 | var state = n.state; 264 | 265 | this.on("input", function(msg) { 266 | var outmsg = { 267 | topic: msg.topic 268 | }; 269 | var vid = null; 270 | var err = null; 271 | 272 | var vehicles = msg.payload; 273 | if ( vehicles instanceof Array) { 274 | var vehicle = vehicles[0]; //first car in the vehicles array 275 | if(vehicle.id) { 276 | vid = vehicle.id; // the three digit vehicle "id" 277 | } 278 | } else { 279 | console.log( 'tesla sunroof node called with unexpected input: ' + msg.payload); 280 | outmsg.payload = msg.payload; 281 | node.send(outmsg); 282 | } 283 | 284 | if (vid) { 285 | if (state == "open" || state == "close" || state == "comfort" || state == "vent") { 286 | teslams.sun_roof( {id: vid, roof: state }, function (resp) { 287 | outmsg.payload = resp; 288 | node.send(outmsg); 289 | }); 290 | } else { 291 | err = new Error("Invalid sun roof state. Specify 'open', 'close', 'comfort' or 'vent'"); 292 | outmsg.payload = err; 293 | node.send(outmsg); 294 | } 295 | } else { 296 | console.log('undefined vid, skipping calls to tesla'); 297 | err = new Error("undefined vehicle id, input must be properly formated tesla vehicle array with 'id' field of car set"); 298 | outmsg.payload = err; 299 | node.send(outmsg); 300 | } 301 | }); 302 | } 303 | RED.nodes.registerType("sunroof",TeslaSunroof); 304 | 305 | function TeslaCharger(n) { 306 | RED.nodes.createNode(this,n); 307 | var node = this; 308 | var state = n.state; 309 | 310 | this.on("input", function(msg) { 311 | var outmsg = { 312 | topic: msg.topic 313 | }; 314 | var vid = null; 315 | var err = null; 316 | 317 | var vehicles = msg.payload; 318 | if ( vehicles instanceof Array) { 319 | var vehicle = vehicles[0]; //first car in the vehicles array 320 | if(vehicle.id) { 321 | vid = vehicle.id; // the three digit vehicle "id" 322 | } 323 | } else { 324 | console.log( 'tesla charger node called with unexpected input: ' + msg.payload); 325 | outmsg.payload = msg.payload; 326 | node.send(outmsg); 327 | } 328 | 329 | if (vid) { 330 | if (state == "start" || state == "stop" ) { 331 | teslams.charge_state( { id: vid, charge: state }, function (resp) { 332 | outmsg.payload = resp; 333 | node.send(outmsg); 334 | }); 335 | } else { 336 | err = new Error("Invalid charge state. Use 'start' or 'stop'"); 337 | outmsg.payload = err; 338 | node.send(outmsg); 339 | } 340 | } else { 341 | console.log('undefined vid, skipping calls to tesla'); 342 | err = new Error("undefined vehicle id, input must be properly formated tesla vehicle array with 'id' field of car set"); 343 | outmsg.payload = err; 344 | node.send(outmsg); 345 | } 346 | }); 347 | } 348 | RED.nodes.registerType("charger",TeslaCharger); 349 | 350 | function TeslaAutoConditioning(n) { 351 | RED.nodes.createNode(this,n); 352 | var node = this; 353 | var state = n.state; 354 | 355 | this.on("input", function(msg) { 356 | var outmsg = { 357 | topic: msg.topic 358 | }; 359 | var vid = null; 360 | var err = null; 361 | 362 | var vehicles = msg.payload; 363 | if ( vehicles instanceof Array) { 364 | var vehicle = vehicles[0]; //first car in the vehicles array 365 | if(vehicle.id) { 366 | vid = vehicle.id; // the three digit vehicle "id" 367 | } 368 | } else { 369 | console.log( 'tesla auto conditioning node called with unexpected input: ' + msg.payload); 370 | outmsg.payload = msg.payload; 371 | node.send(outmsg); 372 | } 373 | 374 | if (vid) { 375 | if (state == "start" || state == "stop" ) { 376 | teslams.auto_conditioning( { id: vid, climate: state}, function (resp) { 377 | outmsg.payload = resp; 378 | node.send(outmsg); 379 | }); 380 | } else { 381 | err = new Error("Invalid auto conditioning state. Use 'start' or 'stop'"); 382 | outmsg.payload = err; 383 | node.send(outmsg); 384 | } 385 | } else { 386 | console.log('undefined vid, skipping calls to tesla'); 387 | err = new Error("undefined vehicle id, input must be properly formated tesla vehicle array with 'id' field of car set"); 388 | outmsg.payload = err; 389 | node.send(outmsg); 390 | } 391 | }); 392 | } 393 | RED.nodes.registerType("climate",TeslaAutoConditioning); 394 | 395 | function TeslaChargeRange(n) { 396 | RED.nodes.createNode(this,n); 397 | var node = this; 398 | var range = n.range; 399 | 400 | this.on("input", function(msg) { 401 | var outmsg = { 402 | topic: msg.topic 403 | }; 404 | var vid = null; 405 | var err = null; 406 | 407 | var vehicles = msg.payload; 408 | if ( vehicles instanceof Array) { 409 | var vehicle = vehicles[0]; //first car in the vehicles array 410 | if(vehicle.id) { 411 | vid = vehicle.id; // the three digit vehicle "id" 412 | } 413 | } else { 414 | console.log( 'tesla charge range node called with unexpected input: ' + msg.payload); 415 | outmsg.payload = msg.payload; 416 | node.send(outmsg); 417 | } 418 | 419 | if (msg.range >= 50 && msg.range <= 100) range = msg.range; 420 | if (vid) { 421 | if ( range >= 50 && range <= 100 ) { 422 | teslams.charge_range( { id: vid, range: 'set', percent: range }, function (resp) { 423 | outmsg.range = range; 424 | outmsg.payload = resp; 425 | node.send(outmsg); 426 | }); 427 | } else { 428 | err = new Error("Invalid charge range. Use a number from between 50-100 percent"); 429 | outmsg.payload = err; 430 | node.send(outmsg); 431 | } 432 | } else { 433 | console.log('undefined vid, skipping calls to tesla'); 434 | err = new Error("undefined vehicle id, input must be properly formated tesla vehicle array with 'id' field of car set"); 435 | outmsg.payload = err; 436 | node.send(outmsg); 437 | } 438 | }); 439 | } 440 | RED.nodes.registerType("charge-range",TeslaChargeRange); 441 | 442 | function TeslaSetTemperature(n) { 443 | RED.nodes.createNode(this,n); 444 | var node = this; 445 | var temp = n.temp; 446 | 447 | this.on("input", function(msg) { 448 | var outmsg = { 449 | topic: msg.topic 450 | }; 451 | var vid = null; 452 | var err = null; 453 | 454 | var vehicles = msg.payload; 455 | if ( vehicles instanceof Array) { 456 | var vehicle = vehicles[0]; //first car in the vehicles array 457 | if(vehicle.id) { 458 | vid = vehicle.id; // the three digit vehicle "id" 459 | } 460 | } else { 461 | console.log( 'tesla temp range node called with unexpected input: ' + msg.payload); 462 | outmsg.payload = msg.payload; 463 | node.send(outmsg); 464 | } 465 | 466 | if (vid) { 467 | if ( temp >= 17 && temp <= 32 ) { 468 | console.log('setting temp to ' + temp); 469 | teslams.set_temperature( { id: vid, dtemp: temp}, function (resp) { 470 | outmsg.payload = resp; 471 | node.send(outmsg); 472 | }); 473 | } else { 474 | err = new Error("Invalid temp value. Valid range is 17C - 32C"); 475 | outmsg.payload = err; 476 | node.send(outmsg); 477 | } 478 | } else { 479 | console.log('undefined vid, skipping calls to tesla'); 480 | err = new Error("Invalid temp value. Valid range is 17C - 32C" ); 481 | outmsg.payload = err; 482 | node.send(outmsg); 483 | } 484 | }); 485 | } 486 | RED.nodes.registerType("set-temp",TeslaSetTemperature); 487 | 488 | }; 489 | 490 | --------------------------------------------------------------------------------