From 8b8fed6a2f366b47b0aee7e126bc85f9ef9ba52b Mon Sep 17 00:00:00 2001 From: Martin Bruse Date: Wed, 28 Sep 2016 22:34:37 +0200 Subject: [PATCH 1/6] added simple homelink support --- .websocket.go.swp | Bin 0 -> 16384 bytes vehicles.go | 5 ++ websocket.go | 117 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 .websocket.go.swp create mode 100644 websocket.go diff --git a/.websocket.go.swp b/.websocket.go.swp new file mode 100644 index 0000000000000000000000000000000000000000..e9cd2588fe1413c39a7618c859b80b7a2b3eb646 GIT binary patch literal 16384 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 Date: Sun, 2 Oct 2016 11:16:24 +0200 Subject: [PATCH 2/6] More work on autopark and homelink. --- .websocket.go.swp | Bin 16384 -> 0 bytes websocket.go | 105 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 12 deletions(-) delete mode 100644 .websocket.go.swp diff --git a/.websocket.go.swp b/.websocket.go.swp deleted file mode 100644 index e9cd2588fe1413c39a7618c859b80b7a2b3eb646..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 Date: Mon, 3 Oct 2016 18:52:58 +0200 Subject: [PATCH 3/6] Made the web socket API use a callback interface instead of getters. --- websocket.go | 60 ++++++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/websocket.go b/websocket.go index 4509f55..b7e00e6 100644 --- a/websocket.go +++ b/websocket.go @@ -6,7 +6,6 @@ import ( "net/http" "net/url" "strconv" - "sync" "time" "github.com/gorilla/websocket" @@ -30,23 +29,27 @@ type heartbeatCommand struct { Timestamp int64 `json:"timestamp"` } +// WebSocketStateListener receives updates from the websocket. +type WebSocketStateListener interface { + ConnectionUp(bool) + AutoparkReady(bool) + HomelinkNearby(bool) +} + // WebSocket encapsulates a controlling websocket to a vehicle. type WebSocket struct { Vehicle *Vehicle Output <-chan map[string]interface{} - conn *websocket.Conn - - autoparkStateWG sync.WaitGroup - autoparkState string - autoparkStateInit bool - homelinkNearbyWG sync.WaitGroup - homelinkNearby bool - homelinkNearbyInit bool + conn *websocket.Conn + listener WebSocketStateListener } // Close closes the underlying connection. func (s *WebSocket) Close() error { + s.listener.ConnectionUp(false) + s.listener.AutoparkReady(false) + s.listener.HomelinkNearby(false) return s.conn.Close() } @@ -110,20 +113,8 @@ func (s *WebSocket) ActivateHomelink() error { return s.Write(cmd) } -// AutoparkState waits for the state to be loaded over the socket, then returns it. -func (s *WebSocket) AutoparkState() string { - s.autoparkStateWG.Wait() - return s.autoparkState -} - -// HomelinkNearby wait for the state to be loaded over the socket, then returns it. -func (s *WebSocket) HomelinkNearby() bool { - s.homelinkNearbyWG.Wait() - return s.homelinkNearby -} - // Returns a WebSocket connected to the vehicle. -func (v *Vehicle) WebSocket() (*WebSocket, error) { +func (v *Vehicle) WebSocket(listener WebSocketStateListener) (*WebSocket, error) { sockURL := url.URL{Scheme: "wss", Host: WebSocketServer, Path: WebSocketResource + strconv.Itoa(v.VehicleID)} data := []byte(v.client.Auth.Email + ":" + v.Tokens[0]) @@ -133,18 +124,17 @@ func (v *Vehicle) WebSocket() (*WebSocket, error) { pipe := make(chan map[string]interface{}) sock := &WebSocket{ - Vehicle: v, - Output: (<-chan map[string]interface{})(pipe), + Vehicle: v, + Output: (<-chan map[string]interface{})(pipe), + listener: listener, } - // autopark state and homelink nearby - sock.homelinkNearbyWG.Add(1) - sock.autoparkStateWG.Add(1) var err error sock.conn, _, err = websocket.DefaultDialer.Dial(sockURL.String(), headers) if err != nil { return nil, err } + sock.listener.ConnectionUp(true) go func() { for { @@ -152,6 +142,9 @@ func (v *Vehicle) WebSocket() (*WebSocket, error) { err := sock.conn.ReadJSON(&msg) log.Printf("Tesla: ReadJSON: %+v, %v", msg, err) if err != nil { + sock.listener.ConnectionUp(false) + sock.listener.AutoparkReady(false) + sock.listener.HomelinkNearby(false) close(pipe) return } @@ -177,18 +170,11 @@ func (v *Vehicle) WebSocket() (*WebSocket, error) { } }() case "autopark:status": - sock.autoparkState = msg["autopark_state"].(string) - if !sock.autoparkStateInit { - sock.autoparkStateWG.Done() - sock.autoparkStateInit = true - } + sock.listener.AutoparkReady(msg["autopark_state"] == "ready") case "homelink:status": - sock.homelinkNearby = msg["homelink_nearby"].(bool) - if !sock.homelinkNearbyInit { - sock.homelinkNearbyWG.Done() - sock.homelinkNearbyInit = true - } + sock.listener.HomelinkNearby(msg["homelink_nearby"].(bool)) + } pipe <- msg } From 68a13079a5f8064b6085927d83dddc388c8c5057 Mon Sep 17 00:00:00 2001 From: Martin Bruse Date: Tue, 4 Oct 2016 08:13:09 +0200 Subject: [PATCH 4/6] Removed redundant and broken code. --- commands.go | 57 ----------------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/commands.go b/commands.go index 5b300fd..0880134 100644 --- a/commands.go +++ b/commands.go @@ -14,46 +14,6 @@ type CommandResponse struct { } `json:"response"` } -// Required elements to POST an Autopark/Summon request -// for the vehicle -type AutoParkRequest struct { - VehicleID int `json:"vehicle_id,omitempty"` - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - Action string `json:"action,omitempty"` -} - -// Causes the vehicle to abort the Autopark request -func (v Vehicle) AutoparkAbort() error { - return v.autoPark("abort") -} - -// Causes the vehicle to pull forward -func (v Vehicle) AutoparkForward() error { - return v.autoPark("start_forward") -} - -// Causes the vehicle to go in reverse -func (v Vehicle) AutoparkReverse() error { - return v.autoPark("start_reverse") -} - -// Performs the actual auto park/summon request for the vehicle -func (v Vehicle) autoPark(action string) error { - apiUrl := BaseURL + "/vehicles/" + strconv.FormatInt(v.ID, 10) + "/command/autopark_request" - driveState, _ := v.DriveState() - autoParkRequest := &AutoParkRequest{ - VehicleID: v.VehicleID, - Lat: driveState.Latitude, - Lon: driveState.Longitude, - Action: action, - } - body, _ := json.Marshal(autoParkRequest) - - _, err := sendCommand(apiUrl, body) - return err -} - // TBD based on Github issue #7 // Toggles defrost on and off, locations values are 'front' or 'rear' // func (v Vehicle) Defrost(location string, state bool) error { @@ -69,23 +29,6 @@ func (v Vehicle) autoPark(action string) error { // return err // } -// Opens and closes the configured Homelink garage door of the vehicle -// keep in mind this is a toggle and the garage door state is unknown -// a major limitation of Homelink -func (v Vehicle) TriggerHomelink() error { - apiUrl := BaseURL + "/vehicles/" + strconv.FormatInt(v.ID, 10) + "/command/trigger_homelink" - driveState, _ := v.DriveState() - autoParkRequest := &AutoParkRequest{ - Lat: driveState.Latitude, - Lon: driveState.Longitude, - } - body, _ := json.Marshal(autoParkRequest) - - _, err := sendCommand(apiUrl, body) - return err - return nil -} - // Wakes up the vehicle when it is powered off func (v Vehicle) Wakeup() (*Vehicle, error) { apiUrl := BaseURL + "/vehicles/" + strconv.FormatInt(v.ID, 10) + "/wake_up" From a4a50eee3ce4b4621dda81d0e7922db26338fe9a Mon Sep 17 00:00:00 2001 From: Martin Bruse Date: Wed, 5 Oct 2016 08:34:41 +0200 Subject: [PATCH 5/6] Fixed typo. Accepting non-bools as IsFrontDefrosterOn (it happened to be an int, once?). --- commands.go | 2 +- states.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commands.go b/commands.go index 0880134..e1e9155 100644 --- a/commands.go +++ b/commands.go @@ -125,7 +125,7 @@ func (v Vehicle) LockDoors() error { // Sets the temprature of the vehicle, where you may set the driver // zone and the passenger zone to seperate temperatures -func (v Vehicle) SetTemprature(driver float64, passenger float64) error { +func (v Vehicle) SetTemperature(driver float64, passenger float64) error { driveTemp := strconv.FormatFloat(driver, 'f', -1, 32) passengerTemp := strconv.FormatFloat(passenger, 'f', -1, 32) apiUrl := BaseURL + "/vehicles/" + strconv.FormatInt(v.ID, 10) + "/command/set_temps?driver_temp=" + driveTemp + "&passenger_temp=" + passengerTemp diff --git a/states.go b/states.go index f8cb243..85d8787 100644 --- a/states.go +++ b/states.go @@ -57,7 +57,7 @@ type ClimateState struct { DriverTempSetting float64 `json:"driver_temp_setting"` PassengerTempSetting float64 `json:"passenger_temp_setting"` IsAutoConditioningOn bool `json:"is_auto_conditioning_on"` - IsFrontDefrosterOn bool `json:"is_front_defroster_on"` + IsFrontDefrosterOn interface{} `json:"is_front_defroster_on"` IsRearDefrosterOn bool `json:"is_rear_defroster_on"` FanStatus interface{} `json:"fan_status"` SeatHeaterLeft int `json:"seat_heater_left"` From f91743db99a1a459ad2a8ea7411546bf5ab9e7c0 Mon Sep 17 00:00:00 2001 From: Martin Bruse Date: Wed, 5 Oct 2016 08:46:30 +0200 Subject: [PATCH 6/6] Made the set_temps command a POST. --- commands.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/commands.go b/commands.go index e1e9155..ba2f612 100644 --- a/commands.go +++ b/commands.go @@ -126,10 +126,15 @@ func (v Vehicle) LockDoors() error { // Sets the temprature of the vehicle, where you may set the driver // zone and the passenger zone to seperate temperatures func (v Vehicle) SetTemperature(driver float64, passenger float64) error { - driveTemp := strconv.FormatFloat(driver, 'f', -1, 32) - passengerTemp := strconv.FormatFloat(passenger, 'f', -1, 32) - apiUrl := BaseURL + "/vehicles/" + strconv.FormatInt(v.ID, 10) + "/command/set_temps?driver_temp=" + driveTemp + "&passenger_temp=" + passengerTemp - _, err := ActiveClient.post(apiUrl, nil) + apiUrl := BaseURL + "/vehicles/" + strconv.FormatInt(v.ID, 10) + "/command/set_temps" + body, err := json.Marshal(map[string]interface{}{ + "driver_temp": driver, + "passenger_temp": passenger, + }) + if err != nil { + return err + } + _, err = ActiveClient.post(apiUrl, body) return err }