├── README.md └── plug-in-the-car.js /README.md: -------------------------------------------------------------------------------- 1 | # plug in the car! 2 | 3 | Checks your Hyundai/Kia EV status using BlueLink, 4 | and sends an email (via Gmail) if the battery is low 5 | and the car is not charging. 6 | 7 | ## Installation 8 | 9 | 1. `npm install bluelinky nodemailer dotenv` 10 | 11 | 2. Create an application password for your Gmail account. 12 | 13 | * Go to https://myaccount.google.com/security 14 | * Ensure that 2-step verification is enabled 15 | * Go to "app passwords" and create a new one 16 | 17 | 3. Create a file `.env` with the following: 18 | 19 | ``` 20 | GMAIL_USER=xxx@gmail.com 21 | GMAIL_PASSWORD=xxx # application password you created above 22 | EMAIL_TO='address1:address2:...' 23 | 24 | BLUELINK_USERNAME=xxx 25 | BLUELINK_PASSWORD=xxx 26 | BLUELINK_PIN=xxx 27 | BLUELINK_BRAND=hyundai # 'hyundai' or 'kia' 28 | BLUELINK_REGION=US # 'US' or 'CA' or 'EU' 29 | 30 | CAR_VIN=xxx 31 | CAR_MODEL=xxx # display name used in the email 32 | CAR_SOC_THRESHOLD=33 # alert when battery is below this percentage 33 | ``` 34 | 35 | 4. Run as `node plug-in-the-car.js`, perhaps from a cron job 36 | -------------------------------------------------------------------------------- /plug-in-the-car.js: -------------------------------------------------------------------------------- 1 | const BlueLinky = require("bluelinky"); 2 | const nodemailer = require('nodemailer'); 3 | require('dotenv').config(); 4 | 5 | // thanks https://stackoverflow.com/questions/45478293/username-and-password-not-accepted-when-using-nodemailer/45479968#45479968 6 | const transporter = nodemailer.createTransport({ 7 | service: 'gmail', 8 | auth: { 9 | user: process.env.GMAIL_USER, 10 | pass: process.env.GMAIL_PASSWORD 11 | } 12 | }); 13 | 14 | async function sendmail(soc) { 15 | const result = await transporter.sendMail({ 16 | to: process.env.EMAIL_TO.split(':'), 17 | subject: ('Plug in the ' + process.env.CAR_MODEL + '!'), 18 | text: ('The car is only at ' + soc + '% charge and not plugged in') 19 | }); 20 | 21 | console.log(JSON.stringify(result, null, 4)); 22 | } 23 | 24 | const client = new BlueLinky({ 25 | username: process.env.BLUELINK_USERNAME, 26 | password: process.env.BLUELINK_PASSWORD, 27 | pin: process.env.BLUELINK_PIN, 28 | brand: process.env.BLUELINK_BRAND, 29 | region: process.env.BLUELINK_REGION 30 | }); 31 | 32 | 33 | client.on("ready", async () => { 34 | 35 | // helpful for debugging: 36 | // console.log(await client.getVehicles()) 37 | 38 | const vehicle = client.getVehicle(process.env.CAR_VIN); 39 | const response = await vehicle.status(); 40 | var soc = response.evStatus.batteryStatus; 41 | var charging = response.evStatus.batteryCharge; 42 | 43 | if (soc < process.env.CAR_SOC_THRESHOLD && !charging) { 44 | console.log("Needs to be plugged in!") ; 45 | sendmail(soc); 46 | } else { 47 | console.log( 48 | "OK: battery at", 49 | (soc + "%"), 50 | (charging ? "and charging" : "") 51 | ); 52 | } 53 | }); 54 | --------------------------------------------------------------------------------