Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
morrisonbrett committed Feb 22, 2016
0 parents commit d9e23d6
Show file tree
Hide file tree
Showing 6 changed files with 487 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.o
*.a
*.so

*.out
*.html
*.test
*.prof
*.stackdump
*.exe
129 changes: 129 additions & 0 deletions TeslaCommand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//
// Brett Morrison, Februrary 2016
//
// A program to alert if Tesla is not plugged in at a specified GeoFence
//
package main

import (
"TeslaCommand/lib/HaversinFormula"
"TeslaCommand/lib/VehicleLib"
"flag"
"fmt"
"os"
"time"
)

// Magic clientid and clientsecret available here: http://pastebin.com/fX6ejAHd
const clientid = "e4a9949fcfa04068f59abb5a658f2bac0a3428e4652315490b659d5ab3f35a9e"
const clientsecret = "c75f14bbadc8bee3a7594412c31416f8300256d7668ea7e6e7f06727bfb9d220"

var teslaLoginEmail string
var teslaLoginPassword string
var vehicleIndex int
var geoFenceLatitude float64
var geoFenceLongitude float64
var mailServer string
var mailServerPort int
var mailServerLogin string
var mailServerPassword string
var fromAddress string
var toAddress string
var radius int
var checkInterval int

func init() {
flag.StringVar(&teslaLoginEmail, "teslaLoginEmail", "", "Email for teslamotors.com account")
flag.StringVar(&teslaLoginPassword, "teslaLoginPassword", "", "Password for teslamotors.com account")
flag.IntVar(&vehicleIndex, "vehicleIndex", 0, "Index of vehicles in your account - If just 1 vehicle, use 0")
flag.Float64Var(&geoFenceLatitude, "geoFenceLatitude", 0.0, "Latitude of GeoFence Center")
flag.Float64Var(&geoFenceLongitude, "geoFenceLongitude", 0.0, "Longitude of GeoFence Center")
flag.StringVar(&mailServer, "mailServer", "", "SMTP Server hostname")
flag.IntVar(&mailServerPort, "mailServerPort", 25, "SMTP Server port number")
flag.StringVar(&mailServerLogin, "mailServerLogin", "", "SMTP Server login username")
flag.StringVar(&mailServerPassword, "mailServerPassword", "", "SMTP Server password")
flag.StringVar(&fromAddress, "fromAddress", "", "Alert send from email")
flag.StringVar(&toAddress, "toAddress", "", "Alert send to email")
flag.IntVar(&radius, "radius", 0, "Radius in meters from center geoFence - Typically use 200")
flag.IntVar(&checkInterval, "checkInterval", 5, "Time in minutes between checks for geoFence")
}

func main() {
fmt.Printf("Num Args %d\n", len(os.Args))
flag.Parse()
if len(os.Args) != 14 {
flag.Usage()
os.Exit(1)
}

var li VehicleLib.LoginInfo
err := VehicleLib.TeslaLogin(clientid, clientsecret, teslaLoginEmail, teslaLoginPassword, &li)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("token: " + li.Token)

var vir VehicleLib.VehicleInfoResponse
err = VehicleLib.ListVehicles(li.Token, &vir)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

// Need to set this flag for every time vehicle exits and enters GeoFence (so we don't send repeated alerts)
ingeofence := false

// Loop every N minutes.
fmt.Printf("Waiting to check vehicle %v location for %v minutes...\n", vir.Vehicles[vehicleIndex].DisplayName, checkInterval)
for _ = range time.Tick(time.Duration(checkInterval) * time.Minute) {
fmt.Printf("Checking vehicle %v location after waiting %v minutes.\n", vir.Vehicles[vehicleIndex].DisplayName, checkInterval)

var vlr VehicleLib.VehicleLocationResponse
err = VehicleLib.GetLocation(li.Token, vir.Vehicles[vehicleIndex].ID, &vlr)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

distance := HaversinFormula.Distance(geoFenceLatitude, geoFenceLongitude, vlr.VehicleLocation.Latitude, vlr.VehicleLocation.Longitude)

fmt.Printf("Distance: %v\n\n", distance)

// If the distance is outside the radius, that means vehicle is outside the GeoFence. Ok to get out
if distance > float64(radius) {
ingeofence = false
}

// This is to prevent the below logic, if it's already executed, no need to keep doing it
if ingeofence == true {
continue
}

// In the GeoFence. Get the vehicle charge state.
var vcsr VehicleLib.VehicleChargeStateResponse
err = VehicleLib.GetChargeState(li.Token, vir.Vehicles[vehicleIndex].ID, &vcsr)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

// Check if the vehicle is stopped.
if vlr.VehicleLocation.Speed == 0 {
//Check if the vehicle is plugged in.
if vcsr.VehicleChargeState.ChargingState == "Disconnected" {
// Disconnected, stopped, and within the radius - send alert
ingeofence = true
subject := "Tesla Command - " + vir.Vehicles[vehicleIndex].DisplayName
body := fmt.Sprintf("Vehicle is within %v meters of GeoFence with a battery level of %v percent. Please plug in.", int(distance), vcsr.VehicleChargeState.BatteryLevel)
fmt.Println(body)
err = VehicleLib.SendMail(mailServer, mailServerPort, mailServerLogin, mailServerPassword, fromAddress, toAddress, subject, body)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
fmt.Printf("Waiting to check vehicle %v location for %v minutes...\n", vir.Vehicles[vehicleIndex].DisplayName, checkInterval)
}
}
38 changes: 38 additions & 0 deletions lib/HaversinFormula/HaversinFormula.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Package HaversinFormula ...
//
// Pulled from gist: https://gist.github.com/cdipaolo/d3f8db3848278b49db68
//
package HaversinFormula

// haversin(θ) function
import "math"

func hsin(theta float64) float64 {
return math.Pow(math.Sin(theta/2), 2)
}

// Distance function returns the distance (in meters) between two points of
// a given longitude and latitude relatively accurately (using a spherical
// approximation of the Earth) through the Haversin Distance Formula for
// great arc distance on a sphere with accuracy for small distances
//
// point coordinates are supplied in degrees and converted into rad. in the func
//
// distance returned is METERS!!!!!!
// http://en.wikipedia.org/wiki/Haversine_formula
func Distance(lat1, lon1, lat2, lon2 float64) float64 {
// convert to radians
// must cast radius as float to multiply later
var la1, lo1, la2, lo2, r float64
la1 = lat1 * math.Pi / 180
lo1 = lon1 * math.Pi / 180
la2 = lat2 * math.Pi / 180
lo2 = lon2 * math.Pi / 180

r = 6378100 // Earth radius in METERS

// calculate
h := hsin(la2-la1) + math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)

return 2 * r * math.Asin(math.Sqrt(h))
}
25 changes: 25 additions & 0 deletions lib/VehicleLib/Mail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package VehicleLib

import (
"fmt"
"net/smtp"
"strconv"
)

// SendMail ...
func SendMail(mailServer string, mailServerPort int, mailServerLogin string, mailServerPassword string, fromAddress string, toAddress string, subj string, body string) error {
// Set up authentication information.
auth := smtp.PlainAuth("", mailServerLogin, mailServerPassword, mailServer)

// Connect to the server, authenticate, set the sender and recipient, and send the email in one step.
to := []string{toAddress}
msg := []byte("To: " + toAddress + "\r\nSubject: " + subj + "\r\n\r\n" + body + "\r\n")
serverPort := mailServer + ":" + strconv.Itoa(mailServerPort)
fmt.Printf("Sending mail via server %s\n", serverPort)
err := smtp.SendMail(serverPort, auth, fromAddress, to, msg)
if err != nil {
return fmt.Errorf("sendMail error: %s", err)
}

return nil
}
Loading

0 comments on commit d9e23d6

Please sign in to comment.