Skip to content

Instantly share code, notes, and snippets.

@ErikDeBruijn
Last active January 29, 2016 22:35
Show Gist options
  • Save ErikDeBruijn/3315e5f54281c6eaf7f8 to your computer and use it in GitHub Desktop.
Save ErikDeBruijn/3315e5f54281c6eaf7f8 to your computer and use it in GitHub Desktop.
A little script to finish charging my car just before I want to leave
#!/usr/bin/node
/*
This script does four things for Tesla Model S owners:
- it helps increase battery longevity
- saves money if your utility charges night-time power differently
- gives you a more pleasant start of your drive
- preheats for you in the morning, no need to open up the App.
How?
You tell it when it should complete its charge and what percentage.
It will calculate how long it would take and start that much
earlier.
Why the trouble?
Lithium Ion battery chemistry requires you to not keep a battery
at a high charge (e.g. 100%) for a long duration. Also, its better
for your range and your battery to start driving when it just barely
completed its charge, because it will still be warmer. This means
more regen during those first kilometers, but also: a cold battery
would still suffer from high-current charging, which is what regen
does. Actually, a charging schedule should be a standard option on
provided by Tesla. But they're busy. And I couldn't wait.
by not leaving it highly charged for long and save money
on electricity by using more of the night tariff.
WARNING: THIS CODE COMES WITH NO WARRANTY OR FITNESS FOR ANY
PURPOSE. AND IT IS NOT ENDORSED BY TESLA.
NEVER OVER-USE THE TESLA API BY RUNNING LOTS OF REQUESTS ON THEIR
SERVERS.
Installation and usage:
Requires a 24/7 running linux box, e.g. a Raspberry Pi or something.
1. Install nodejs (google if you don't know how)
In my home-build charger (3 phase 11kW, 60kWh battery) I get about
one percent of charge every two minutes. I added 10% margin and set
the variable minutesPerPercent to 2.2.
2. Change this to your liking.
Also, change the coordinates for your home, so it will know not to
mess with charging if your not at home.
3. Before use, install these:
sudo npm install -g json5 shelljs teslams
4. Add a ~/.teslams/config.json file with the following:
{
"username": "yourMyTeslaLogin@email.com",
"password": "yourPassword",
}
5. Run a "teslacmd --honk" to test and see if you can control the car
through the API with hjespers teslams node library.
6. Try running this script manually to get some confidence before
your think of relying on it. You may not be able to go on your
planned trip in time if something goes wrong.
7. Add to cron:
The recommendation is to put this in cron, one line per
day of the week if you have different times when you leave.
If you leave at the same time daily (monday-friday), this will work:
0,15,30,45 3-8 * * 1-5 /home/erik/tesla-finish-charge-at.js 7:30 85
The "7:30 85" means that at 7:30 it should be charged to 85%.
CONTRIBUTING
Contact me on github (erikdebruijn) or send me pull requests if you find a
problem or fix something.
*/
require('shelljs/global');
var JSON5 = require('json5');
if(process.argv.length < 4) {
return echo(usage());
}
// Change these with your settings
var minutesPerPercent = 2.2;
var homeCoordinates = "51.586269,5.004745";
// Don't change the following unless you know what you're doing
var chargeTargetPerc = process.argv[3];
if(chargeTargetPerc < 50 || chargeTargetPerc > 100) {
return echo("Charge percentage should always be between 50 and 100%.\n"+usage());
}
if(process.argv[2].split(':').length != 2) {
return echo("Tome to complete should be in hh:mm format.\n"+usage());
}
var chargeCompleteAtH = process.argv[2].split(':')[0];
var chargeCompleteAtM = process.argv[2].split(':')[1];
var alsoHeat = (process.argv[4] == '--heat')? true : false;
if(alsoHeat)
echo("Will heat/cool car after charging finished.");
var finishChargingAt = new Date();
finishChargingAt.setHours(parseInt(chargeCompleteAtH),parseInt(chargeCompleteAtM),0);
echo("Will attempt to finish charging at: "+finishChargingAt.toString());
if(!isAtHome())
return echo("Aborting.");
var chargeObj = getChargeInfo();
if(typeof chargeObj != "object") {
echo("No charge info found. Trying to ensure charge to 85%");
setChargeRangePerc(85);
return false;
}
var minutesToCharge = (chargeTargetPerc-chargeObj.battery_level) * minutesPerPercent;
if(minutesToCharge < 0)
return echo("Current SoC (at "+chargeObj.battery_level+"%) is already above your target of "+chargeTargetPerc+"%.");
echo(chargeObj.battery_level + "% charged.","It will take "+minutesToCharge+" minutes to charge to "+chargeTargetPerc+"%.");
var startAtUnixMillis = finishChargingAt.getTime() - 60000 * minutesToCharge;
var startChargingAt = new Date(startAtUnixMillis);
var minutesToStartCharging = (startChargingAt - +new Date)/1000/60;
console.log("It is "+minutesToStartCharging+" minutesToStartCharging.");
if(minutesToStartCharging < 59)
{
echo("Waiting for "+minutesToStartCharging+" minutes to charge.");
setTimeout(function(){
echo("Waiting finished.");
setChargeRangePerc(chargeTargetPerc);
},60*1000*minutesToStartCharging);
if(alsoHeat) {
echo("Will heat/cool car after charging finished.");
setTimeout(function(){
echo("Waiting for heating finished.");
teslaCmdToJSON("teslacmd --climate on --temp 19");
},60*1000*(minutesToStartCharging+minutesToCharge-1));
}
}
function usage(){
return "Usage: node tesla-charge.js <time_charge_should_complete> <charge_to_percent>";
}
function getCarLocation() {
return teslaCmdToJSON("teslamap --json");
}
function setChargeRangePerc(perc) {
echo("Starting to charge to "+parseInt(perc)+"%...");
var output = exec("teslacmd --charge start --range "+parseInt(perc),{silent:false}).output;
return output;
}
function getChargeInfo() {
var jsonOutput;
json = teslaCmdToJSON("teslacmd -c");
var repeatCount = 2;
while(typeof json != 'object') {
repeatCount--;
if(repeatCount < 1) break;
json = teslaCmdToJSON("sleep 5 ; teslacmd -c");
}
return json;
}
function strToLatLongObj(str) {
return {latitude: parseFloat(str.split(',')[0]),longitude: parseFloat(str.split(',')[1])};
}
function distance(latlongVector1,latlongVector2,unit) {
var radlat1 = Math.PI * latlongVector1.latitude/180
var radlat2 = Math.PI * latlongVector2.latitude/180
var radlon1 = Math.PI * latlongVector1.longitude/180
var radlon2 = Math.PI * latlongVector2.longitude/180
var theta = latlongVector1.longitude-latlongVector2.longitude
var radtheta = Math.PI * theta/180
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
return Math.acos(dist)*180/Math.PI * 60 * 1.1515 * 1.609344;
}
function teslaCmdToJSON(cmd)
{
var jsonOutput = exec(cmd,{silent:true}).output;
console.log(jsonOutput);
jsonOutput = jsonOutput.split("\n");
jsonOutput.shift();
jsonOutput = jsonOutput.join("\n");
if(jsonOutput.length < 1) {
return false;
}
try {
return JSON5.parse(jsonOutput);
}catch(e){ return false; }
}
function isAtHome() {
latLong = getCarLocation();
latLongHome = strToLatLongObj(homeCoordinates);
dist = distance(latLong,latLongHome,'K');
if(dist >= 1) {
echo("Tesla is not at home charger ("+(parseInt(dist*10)/10)+" km from home).");
return false;
}
echo("Tesla is at home. Continuing.");
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment