-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
147 lines (122 loc) · 3.59 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Example program: Use a Hermes connection to ping a vehicle
package main
import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"github.com/google/uuid"
"github.com/lotharbach/tesla-hermes-signaling/hermes"
log "github.com/sirupsen/logrus"
"github.com/teslamotors/vehicle-command/pkg/protocol"
"github.com/teslamotors/vehicle-command/pkg/vehicle"
)
func main() {
status := 1
debug := false
defer func() {
os.Exit(status)
}()
// Provided through command line options
var (
privateKeyFile string
vin string
ownerApiTokenFile string
)
flag.StringVar(&privateKeyFile, "key", "", "Private key `file` for authorizing commands (PEM PKCS8 NIST-P256)")
flag.StringVar(&vin, "vin", "", "Vehicle Identification Number (`VIN`) of the car")
flag.StringVar(&ownerApiTokenFile, "ownerApiToken", "", "Owner API access token in a file")
flag.BoolVar(&debug, "debug", false, "Enable debugging")
flag.Parse()
if debug {
log.SetLevel(log.DebugLevel)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if vin == "" {
log.Printf("Must specify VIN")
return
}
var err error
ownerApiToken, err := os.ReadFile(ownerApiTokenFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading token from file: %s\n", err)
return
}
var privateKey protocol.ECDHPrivateKey
if privateKeyFile != "" {
if privateKey, err = protocol.LoadPrivateKey(privateKeyFile); err != nil {
log.Printf("Failed to load private key: %s", err)
return
}
}
userToken, err := fetchHermesToken(ctx, "users/jwt/hermes", string(ownerApiToken))
if err != nil {
log.Printf("Failed to fetch user hermes tokens from owner API: %s", err)
return
}
vehicleToken, err := fetchHermesToken(ctx, "vehicles/"+vin+"/jwt/hermes", string(ownerApiToken))
if err != nil {
log.Printf("Failed to fetch vehicle hermes tokens from owner API: %s", err)
return
}
conn, err := hermes.NewConnection(vin, userToken, vehicleToken)
if err != nil {
log.Printf("Failed to connect to vehicle: %s", err)
return
}
defer conn.Close()
car, err := vehicle.NewVehicle(conn, privateKey, nil)
if err != nil {
log.Printf("Failed to connect to vehicle: %s", err)
return
}
if err := car.Connect(ctx); err != nil {
log.Printf("Failed to connect to vehicle: %s\n", err)
return
}
defer car.Disconnect()
// Most interactions with the car require an authenticated client.
// StartSession() performs a handshake with the vehicle that allows
// subsequent commands to be authenticated.
if err := car.StartSession(ctx, nil); err != nil {
log.Printf("Failed to perform handshake with vehicle: %s\n", err)
return
}
fmt.Println("Pinging car...")
if err := car.Ping(ctx); err != nil {
log.Printf("Failed to ping: %s\n", err)
return
}
fmt.Println("Success!")
status = 0
}
func fetchHermesToken(ctx context.Context, path, ownerApiToken string) (string, error) {
ownerApiURL := "https://owner-api.teslamotors.com/api/1/"
uuid := fmt.Sprintf("{\"uuid\": \"%s\" }", uuid.New())
req, _ := http.NewRequest("POST", ownerApiURL+path, bytes.NewBufferString(uuid))
req.Header.Add("Authorization", "Bearer "+strings.TrimSpace(ownerApiToken))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "TeslaHermesExperiment")
client := &http.Client{}
resp, err := client.Do(req)
var response struct {
Token string `json:"token"`
}
body, _ := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
defer resp.Body.Close()
err = json.Unmarshal(body, &response)
if err != nil {
return "", err
}
return response.Token, nil
}