mirror of
https://github.com/tobiasehlert/teslamateapi.git
synced 2026-02-27 09:54:18 +08:00
Merge branch 'main' into bug-8
This commit is contained in:
+14
-1
@@ -5,6 +5,17 @@
|
||||
### Changed
|
||||
- adding randomized string to mqtt client (#15 by @LelandSindt)
|
||||
|
||||
## [1.4.0] - 2021-03-25
|
||||
|
||||
### Added
|
||||
- added feature commands to proxy POST commands to Tesla owner API (#22)
|
||||
- support for authentication on command endpoints
|
||||
|
||||
## [1.3.1] - 2021-03-23
|
||||
|
||||
### Fixed
|
||||
- fixing sql error when BatteryHeaterNoPower is null (#19 by @LelandSindt)
|
||||
|
||||
## [1.3.0] - 2021-03-17
|
||||
|
||||
### Added
|
||||
@@ -88,7 +99,9 @@
|
||||
|
||||
## [1.0.0] - 2021-02-15
|
||||
|
||||
[Unreleased]: https://github.com/tobiasehlert/teslamateapi/compare/v1.3.0...HEAD
|
||||
[Unreleased]: https://github.com/tobiasehlert/teslamateapi/compare/v1.4.0...HEAD
|
||||
[1.3.1]: https://github.com/tobiasehlert/teslamateapi/compare/v1.3.1...v1.4.0
|
||||
[1.3.1]: https://github.com/tobiasehlert/teslamateapi/compare/v1.3.0...v1.3.1
|
||||
[1.3.0]: https://github.com/tobiasehlert/teslamateapi/compare/v1.2.3...v1.3.0
|
||||
[1.2.3]: https://github.com/tobiasehlert/teslamateapi/compare/v1.2.2...v1.2.3
|
||||
[1.2.2]: https://github.com/tobiasehlert/teslamateapi/compare/v1.2.1...v1.2.2
|
||||
|
||||
@@ -13,6 +13,7 @@ TeslaMateApi is a RESTful API to get data collected by self-hosted data logger *
|
||||
- Written in **[Golang](https://golang.org/)**
|
||||
- Data is collected from TeslaMate **Postgres** database and local **MQTT** Broker
|
||||
- Endpoints return data in JSON format
|
||||
- Send commands to your Tesla through the TeslaMateApi
|
||||
|
||||
### Table of Contents
|
||||
|
||||
@@ -20,6 +21,9 @@ TeslaMateApi is a RESTful API to get data collected by self-hosted data logger *
|
||||
- [Docker-compose](#docker-compose)
|
||||
- [Environment variables](#environment-variables)
|
||||
- [API documentation](#api-documentation)
|
||||
- [Available endpoints](#available-endpoints)
|
||||
- [Authentication](#authentication)
|
||||
- [Commands](#commands)
|
||||
- [Security information](#security-information)
|
||||
- [Credits](#credits)
|
||||
|
||||
@@ -96,6 +100,7 @@ Basically the same environment variables for the database, mqqt and timezone nee
|
||||
|
||||
**Optional** environment variables
|
||||
|
||||
- **API_TOKEN** string *(default: )*
|
||||
- **DATABASE_PORT** integer *(default: 5432)*
|
||||
- **DATABASE_TIMEOUT** integer *(default: 60000)*
|
||||
- **DATABASE_SSL** boolean *(default: true)*
|
||||
@@ -107,23 +112,84 @@ Basically the same environment variables for the database, mqqt and timezone nee
|
||||
- **MQTT_PASSWORD** string *(default: )*
|
||||
- **MQTT_NAMESPACE** string *(default: )*
|
||||
|
||||
**Commands** environment variables
|
||||
|
||||
- **ENABLE_COMMANDS** boolean *(default: false)*
|
||||
- **COMMANDS_ALL** boolean *(default: false)*
|
||||
- **COMMANDS_ALLOWLIST** string *(default: allow_list.json)*
|
||||
- **COMMANDS_WAKE** boolean *(default: false)*
|
||||
- **COMMANDS_ALERT** boolean *(default: false)*
|
||||
- **COMMANDS_REMOTESTART** boolean *(default: false)*
|
||||
- **COMMANDS_HOMELINK** boolean *(default: false)*
|
||||
- **COMMANDS_SPEEDLIMIT** boolean *(default: false)*
|
||||
- **COMMANDS_VALET** boolean *(default: false)*
|
||||
- **COMMANDS_SENTRYMODE** boolean *(default: false)*
|
||||
- **COMMANDS_DOORS** boolean *(default: false)*
|
||||
- **COMMANDS_TRUNK** boolean *(default: false)*
|
||||
- **COMMANDS_WINDOWS** boolean *(default: false)*
|
||||
- **COMMANDS_SUNROOF** boolean *(default: false)*
|
||||
- **COMMANDS_CHARGING** boolean *(default: false)*
|
||||
- **COMMANDS_CLIMATE** boolean *(default: false)*
|
||||
- **COMMANDS_MEDIA** boolean *(default: false)*
|
||||
- **COMMANDS_SHARING** boolean *(default: false)*
|
||||
- **COMMANDS_SOFTWAREUPDATE** boolean *(default: false)*
|
||||
|
||||
## API documentation
|
||||
|
||||
More detailed documentation of every endpoint will come..
|
||||
|
||||
### Available endpoints
|
||||
|
||||
- GET `/api`
|
||||
- GET `/api/v1`
|
||||
- GET `/api/v1/cars`
|
||||
- GET `/api/v1/cars/:CarID`
|
||||
- GET `/api/v1/cars/:CarID/charges`
|
||||
- GET `/api/v1/cars/:CarID/charges/:ChargeID`
|
||||
- GET `/api/v1/cars/:CarID/command`
|
||||
- POST `/api/v1/cars/:CarID/command/:Command`
|
||||
- GET `/api/v1/cars/:CarID/drives`
|
||||
- GET `/api/v1/cars/:CarID/drives/:DriveID`
|
||||
- GET `/api/v1/cars/:CarID/status`
|
||||
- GET `/api/v1/cars/:CarID/updates`
|
||||
- POST `/api/v1/cars/:CarID/wake_up`
|
||||
- GET `/api/v1/globalsettings`
|
||||
- GET `/api/ping`
|
||||
|
||||
### Authentication
|
||||
|
||||
If you want to use command endpoints such as `/api/v1/cars/:CarID/command/:Command` and `/api/v1/cars/:CarID/wake_up`, you need to add authentication to your request.
|
||||
|
||||
You need to specify a token yourself (called **API_TOKEN**) in the environment variables file, to set it. The token has the requirement to be a minimum of 32 characters long.
|
||||
|
||||
There are two options available for authentication to be done.
|
||||
|
||||
1. Adding extra header `Authorization: Bearer <token>` to your request. (recommended option)
|
||||
|
||||
2. Adding URI parameter `?token=<token>` to the endpoint you try to reach. (not a good option)
|
||||
|
||||
\* *Note: If you use the second option and your logs get compromised, your token will be leaked.*
|
||||
|
||||
### Commands
|
||||
|
||||
Commands are not enabled by default.
|
||||
|
||||
You need to enable them in your environment variables (with `ENABLE_COMMANDS=true`) and you need to specify which commands you want to use as well.
|
||||
|
||||
There are 3 ways of using Commands:
|
||||
|
||||
1. Specific groups of commands can be enabled for example `COMMANDS_ALERT=true` will enable the [alert](https://tesla-api.timdorr.com/vehicle/commands/alerts) commands group.
|
||||
|
||||
2. If you need a granular set of commands enabled `COMMANDS_ALLOWLIST=/path/to/allow_list.json` can be used to specify a [JSON formatted list of commands](./example/allow_list.json) to enable.
|
||||
|
||||
3. The most coarse option `COMMANDS_ALL=true` will enable all commands (specific groups and allow_list will be ignored).
|
||||
|
||||
\* *Note: if `COMMANDS_ALL` or any specific group of commands has been enabled `COMMANDS_ALLOWLIST` is ignored.*
|
||||
|
||||
A list of possible commands can be found under [environment variables](#environment-variables).
|
||||
|
||||
Regarding what fields you need to provide in the commands, we will referr to the [timdorr/tesla-api](https://tesla-api.timdorr.com/vehicle/commands) documentation.
|
||||
|
||||
## Security information
|
||||
|
||||
There is **no** possibility to get access to your Tesla account tokens by this API and we'll keep it this way!
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
["/wake_up","/command/flash_lights"]
|
||||
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// initAuthToken func
|
||||
func initAuthToken() {
|
||||
// get token from environment variable API_TOKEN
|
||||
envToken = getEnv("API_TOKEN", "")
|
||||
if envToken == "" {
|
||||
log.Println("[warning] initAuthToken - environment variable API_TOKEN not set or is empty.")
|
||||
} else if len(envToken) < 32 {
|
||||
log.Println("[warning] initAuthToken - environment variable API_TOKEN too short.. should be 32 or longer.")
|
||||
} else {
|
||||
log.Println("[info] initAuthToken - environment variable API_TOKEN is set and good.")
|
||||
}
|
||||
}
|
||||
|
||||
// validateAuthToken func
|
||||
func validateAuthToken(c *gin.Context) (bool, string) {
|
||||
|
||||
// trying with http header - Authorization: Bearer <token>
|
||||
reqHeaderToken := c.Request.Header.Get("Authorization")
|
||||
|
||||
// if length of reqHeaderToken is more than zero
|
||||
if len(reqHeaderToken) > 0 {
|
||||
|
||||
// removing Bearer part from header to get token out of header
|
||||
splitToken := strings.Split(reqHeaderToken, "Bearer")
|
||||
|
||||
if len(splitToken) != 2 {
|
||||
// bearer token is not proper formatted.. returning bad request
|
||||
log.Println("[info] validateAuthToken - header authorization bearer token is not proper formatted.. returning 401")
|
||||
return false, "header authorization bearer token is not proper formatted"
|
||||
|
||||
} else if strings.TrimSpace(splitToken[1]) == "" {
|
||||
// bearer token is empty string.. we'll return unauthorized
|
||||
log.Println("[info] validateAuthToken - header authorization bearer token is empty.. returning 401")
|
||||
return false, "header authorization bearer token is empty"
|
||||
|
||||
} else if checkAuthToken(strings.TrimSpace(splitToken[1])) {
|
||||
// the bearer token is valid!
|
||||
log.Println("[debug] validateAuthToken - header authorization bearer token valid.")
|
||||
return true, ""
|
||||
|
||||
}
|
||||
// the check did fail.. bearer token is invalid
|
||||
log.Println("[info] validateAuthToken - header authorization bearer token invalid.. returning 401")
|
||||
return false, "header authorization bearer token invalid"
|
||||
}
|
||||
|
||||
// trying with http parameter - ?token=<token>
|
||||
tokenParamsValue := c.DefaultQuery("token", "")
|
||||
|
||||
// if validTokenParams is longer than zero
|
||||
if len(tokenParamsValue) > 0 {
|
||||
|
||||
// checking if token is valid (since it's over zero length)
|
||||
if checkAuthToken(tokenParamsValue) {
|
||||
// the token is valid!
|
||||
log.Println("[debug] validateAuthToken - param token valid.")
|
||||
return true, ""
|
||||
|
||||
}
|
||||
// the token is invalid.
|
||||
log.Println("[info] validateAuthToken - param token invalid.. returning 401")
|
||||
return false, "param token invalid"
|
||||
}
|
||||
|
||||
// unauthozie all calls!
|
||||
return false, "failed validation"
|
||||
}
|
||||
|
||||
// checkAuthToken func
|
||||
func checkAuthToken(token string) bool {
|
||||
// checking if it's valid or not
|
||||
if token == envToken {
|
||||
// check that envToken was longer than zero
|
||||
if len(envToken) == 0 {
|
||||
log.Println("[warning] checkAuthToken - returning false (API_TOKEN is not set or empty)")
|
||||
return false
|
||||
}
|
||||
log.Println("[info] checkAuthToken - returning true")
|
||||
return true
|
||||
}
|
||||
|
||||
// failing check what so ever..
|
||||
log.Println("[info] checkAuthToken - returning false (other reason)")
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// initCommandAllowList func
|
||||
func initCommandAllowList() {
|
||||
|
||||
// allow all commands available below
|
||||
allowAll := getEnvAsBool("COMMANDS_ALL", false)
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/wake
|
||||
if getEnvAsBool("COMMANDS_WAKE", false) || allowAll {
|
||||
allowList = append(allowList, "/wake_up")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/alerts
|
||||
if getEnvAsBool("COMMANDS_ALERT", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/honk_horn",
|
||||
"/command/flash_lights")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/remotestart
|
||||
if getEnvAsBool("COMMANDS_REMOTESTART", false) || allowAll {
|
||||
allowList = append(allowList, "/command/remote_start_drive")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/homelink
|
||||
if getEnvAsBool("COMMANDS_HOMELINK", false) || allowAll {
|
||||
allowList = append(allowList, "/command/trigger_homelink")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/speedlimit
|
||||
if getEnvAsBool("COMMANDS_SPEEDLIMIT", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/speed_limit_set_limit",
|
||||
"/command/speed_limit_activate",
|
||||
"/command/speed_limit_deactivate",
|
||||
"/command/speed_limit_clear_pin")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/valet
|
||||
if getEnvAsBool("COMMANDS_VALET", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/set_valet_mode",
|
||||
"/command/reset_valet_pin")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/sentrymode
|
||||
if getEnvAsBool("COMMANDS_SENTRYMODE", false) || allowAll {
|
||||
allowList = append(allowList, "/command/set_sentry_mode")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/doors
|
||||
if getEnvAsBool("COMMANDS_DOORS", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/door_unlock",
|
||||
"/command/door_lock")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/trunk
|
||||
if getEnvAsBool("COMMANDS_TRUNK", false) || allowAll {
|
||||
allowList = append(allowList, "/command/actuate_trunk")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/windows
|
||||
if getEnvAsBool("COMMANDS_WINDOWS", false) || allowAll {
|
||||
allowList = append(allowList, "/command/window_control")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/sunroof
|
||||
if getEnvAsBool("COMMANDS_SUNROOF", false) || allowAll {
|
||||
allowList = append(allowList, "/command/sun_roof_control")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/charging
|
||||
if getEnvAsBool("COMMANDS_CHARGING", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/charge_port_door_open",
|
||||
"/command/charge_port_door_close",
|
||||
"/command/charge_start",
|
||||
"/command/charge_stop",
|
||||
"/command/charge_standard",
|
||||
"/command/charge_max_range",
|
||||
"/command/set_charge_limit")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/climate
|
||||
if getEnvAsBool("COMMANDS_CLIMATE", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/auto_conditioning_start",
|
||||
"/command/auto_conditioning_stop",
|
||||
"/command/set_temps",
|
||||
"/command/set_preconditioning_max",
|
||||
"/command/remote_seat_heater_request",
|
||||
"/command/remote_steering_wheel_heater_request")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/media
|
||||
if getEnvAsBool("COMMANDS_MEDIA", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/media_toggle_playback",
|
||||
"/command/media_next_track",
|
||||
"/command/media_prev_track",
|
||||
"/command/media_next_fav",
|
||||
"/command/media_prev_fav",
|
||||
"/command/media_volume_up",
|
||||
"/command/media_volume_down")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/sharing
|
||||
if getEnvAsBool("COMMANDS_SHARING", false) || allowAll {
|
||||
allowList = append(allowList, "/command/share")
|
||||
}
|
||||
|
||||
// https://tesla-api.timdorr.com/vehicle/commands/softwareupdate
|
||||
if getEnvAsBool("COMMANDS_SOFTWAREUPDATE", false) || allowAll {
|
||||
allowList = append(allowList,
|
||||
"/command/schedule_software_update",
|
||||
"/command/cancel_software_update")
|
||||
}
|
||||
|
||||
// if allowList is empty, read COMMANDS_ALLOWLIST and append to allowList
|
||||
commandAllowListLocation := getEnv("COMMANDS_ALLOWLIST", "allow_list.json")
|
||||
if len(allowList) == 0 {
|
||||
var allowListFile []string
|
||||
commandAllowListFile, err := os.Open(commandAllowListLocation)
|
||||
defer commandAllowListFile.Close()
|
||||
if err != nil {
|
||||
log.Println("[error] getAllowList error with COMMANDS_ALLOWLIST: " + commandAllowListLocation + " not found and will be ignored")
|
||||
} else {
|
||||
byteValue, err := ioutil.ReadAll(commandAllowListFile)
|
||||
if err != nil {
|
||||
log.Println("[error] getAllowList error while reading COMMANDS_ALLOWLIST: " + commandAllowListLocation + " it will be ignored")
|
||||
} else {
|
||||
err = json.Unmarshal(byteValue, &allowListFile)
|
||||
if err != nil {
|
||||
log.Println("[error] getAllowList error while parsing JSON.. COMMANDS_ALLOWLIST: " + commandAllowListLocation + " it will be ignored")
|
||||
} else {
|
||||
allowList = append(allowList, allowListFile...)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Print("[info] getAllowList COMMANDS from environment variables set, " + commandAllowListLocation + " will be ignored.")
|
||||
}
|
||||
|
||||
if gin.IsDebugging() {
|
||||
log.Println("[info] initCommandAllowList - generated following list of allowed commands: " + strings.Join(allowList, ", "))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// TeslaMateAPICarsCommandV1 func
|
||||
func TeslaMateAPICarsCommandV1(c *gin.Context) {
|
||||
|
||||
// creating required vars
|
||||
var TeslaAccessToken, TeslaVehicleID string
|
||||
var jsonData map[string]interface{}
|
||||
var err error
|
||||
|
||||
// check if commands are enabled.. if not we need to abort
|
||||
if getEnvAsBool("ENABLE_COMMANDS", false) == false {
|
||||
log.Println("[warning] TeslaMateAPICarsCommandV1 ENABLE_COMMANDS is not true.. returning 403 forbidden.")
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "You are not allowed to access commands"})
|
||||
return
|
||||
}
|
||||
|
||||
// if request method is GET return list of commands
|
||||
if c.Request.Method == http.MethodGet {
|
||||
c.JSON(http.StatusOK, gin.H{"enabled_commands": allowList})
|
||||
return
|
||||
}
|
||||
|
||||
// authentication for the endpoint
|
||||
validToken, errorMessage := validateAuthToken(c)
|
||||
if !validToken {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": errorMessage})
|
||||
return
|
||||
}
|
||||
|
||||
// getting CarID param from URL
|
||||
ParamCarID := c.Param("CarID")
|
||||
var CarID int
|
||||
if ParamCarID != "" {
|
||||
CarID = convertStringToInteger(ParamCarID)
|
||||
}
|
||||
|
||||
// validating that CarID is not zero
|
||||
if CarID == 0 {
|
||||
log.Println("[error] TeslaMateAPICarsCommandV1 CarID is invalid (zero)!")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "CarID invalid"})
|
||||
return
|
||||
}
|
||||
|
||||
// getting request body to pass to Tesla
|
||||
reqBody, err := ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
log.Println("[error] TeslaMateAPICarsCommandV1 error in first ioutil.ReadAll", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal"})
|
||||
return
|
||||
}
|
||||
|
||||
// getting :Command
|
||||
command := ("/command/" + c.Param("Command"))
|
||||
// if command is /command/ or /command/wake_up, set to /wake_up only
|
||||
if command == "/command/" || command == "/command/wake_up" {
|
||||
command = "/wake_up"
|
||||
}
|
||||
log.Println("[debug] TeslaMateAPICarsCommandV1 command received:", command)
|
||||
|
||||
if !checkArrayContainsString(allowList, command) {
|
||||
log.Print("[warning] TeslaMateAPICarsCommandV1 command: " + command + " not allowed")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
// get TeslaVehicleID and TeslaAccessToken
|
||||
query := `
|
||||
SELECT
|
||||
eid as TeslaVehicleID,
|
||||
(SELECT access FROM tokens LIMIT 1) as TeslaAccessToken
|
||||
FROM cars
|
||||
WHERE id = $1
|
||||
LIMIT 1;`
|
||||
rows, err := db.Query(query, CarID)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results (even if it's only one..)
|
||||
for rows.Next() {
|
||||
// scanning row and putting values into the drive
|
||||
err = rows.Scan(
|
||||
&TeslaVehicleID,
|
||||
&TeslaAccessToken,
|
||||
)
|
||||
}
|
||||
|
||||
// checking for errors in query when doing scan action
|
||||
if err != nil {
|
||||
log.Println("[error] TeslaMateAPICarsCommandV1 error in sql query:", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal"})
|
||||
return
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest(http.MethodPost, "https://owner-api.teslamotors.com/api/1/vehicles/"+TeslaVehicleID+command, strings.NewReader(string(reqBody)))
|
||||
req.Header.Set("Authorization", "Bearer "+TeslaAccessToken)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "TeslaMateApi/"+apiVersion+" (+https://github.com/tobiasehlert/teslamateapi)")
|
||||
resp, err := client.Do(req)
|
||||
|
||||
// check response error
|
||||
if err != nil {
|
||||
log.Println("[error] TeslaMateAPICarsCommandV1 error in http request to https://owner-api.teslamotors.com:", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal"})
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println("[error] TeslaMateAPICarsCommandV1 error in second ioutil.ReadAll:", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal"})
|
||||
return
|
||||
}
|
||||
json.Unmarshal([]byte(respBody), &jsonData)
|
||||
|
||||
// print to log about request
|
||||
if gin.IsDebugging() {
|
||||
log.Println("[debug] TeslaMateAPICarsCommandV1 " + c.Request.RequestURI + " returned data:")
|
||||
js, _ := json.Marshal(jsonData)
|
||||
log.Printf("[debug] %s\n", js)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
log.Println("[info] TeslaMateAPICarsCommandV1 " + c.Request.RequestURI + " executed successful.")
|
||||
} else {
|
||||
log.Println("[error] TeslaMateAPICarsCommandV1 " + c.Request.RequestURI + " error in execution!")
|
||||
}
|
||||
c.JSON(resp.StatusCode, jsonData)
|
||||
}
|
||||
+45
-1
@@ -14,9 +14,19 @@ import (
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// setting TeslaMateApi version number
|
||||
// TODO: get the value from git-tag later..
|
||||
var apiVersion = "1.4.0"
|
||||
|
||||
// defining db var
|
||||
var db *sql.DB
|
||||
|
||||
// defining envToken that contains API_TOKEN value
|
||||
var envToken string
|
||||
|
||||
// list of allowed commands
|
||||
var allowList []string
|
||||
|
||||
// main function
|
||||
func main() {
|
||||
|
||||
@@ -38,6 +48,11 @@ func main() {
|
||||
initDBconnection()
|
||||
defer db.Close()
|
||||
|
||||
// run initAuthToken to validate environment vars
|
||||
initAuthToken()
|
||||
// initialize allowList stored for /command section
|
||||
initCommandAllowList()
|
||||
|
||||
// Connect to the MQTT broker
|
||||
statusCache, err := startMQTT()
|
||||
if err != nil {
|
||||
@@ -68,14 +83,33 @@ func main() {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "TeslaMateApi v1 runnnig..", "path": "/api/v1"})
|
||||
})
|
||||
|
||||
// v1 /api/v1/cars endpoints
|
||||
v1.GET("/cars", TeslaMateAPICarsV1)
|
||||
v1.GET("/cars/:CarID", TeslaMateAPICarsV1)
|
||||
|
||||
// v1 /api/v1/cars/:CarID/charges endpoints
|
||||
v1.GET("/cars/:CarID/charges", TeslaMateAPICarsChargesV1)
|
||||
v1.GET("/cars/:CarID/charges/:ChargeID", TeslaMateAPICarsChargesDetailsV1)
|
||||
|
||||
// v1 /api/v1/cars/:CarID/command endpoints
|
||||
v1.GET("/cars/:CarID/command", TeslaMateAPICarsCommandV1)
|
||||
v1.GET("/cars/:CarID/commands", TeslaMateAPICarsCommandV1)
|
||||
v1.POST("/cars/:CarID/command/:Command", TeslaMateAPICarsCommandV1)
|
||||
|
||||
// v1 /api/v1/cars/:CarID/drives endpoints
|
||||
v1.GET("/cars/:CarID/drives", TeslaMateAPICarsDrivesV1)
|
||||
v1.GET("/cars/:CarID/drives/:DriveID", TeslaMateAPICarsDrivesDetailsV1)
|
||||
|
||||
// v1 /api/v1/cars/:CarID/status endpoints
|
||||
v1.GET("/cars/:CarID/status", statusCache.TeslaMateAPICarsStatusV1)
|
||||
|
||||
// v1 /api/v1/cars/:CarID/updates endpoints
|
||||
v1.GET("/cars/:CarID/updates", TeslaMateAPICarsUpdatesV1)
|
||||
|
||||
// v1 /api/v1/cars/:CarID/wake_up endpoints
|
||||
v1.POST("/cars/:CarID/wake_up", TeslaMateAPICarsCommandV1)
|
||||
|
||||
// v1 /api/v1/globalsettings endpoints
|
||||
v1.GET("/globalsettings", TeslaMateAPIGlobalsettingsV1)
|
||||
}
|
||||
|
||||
@@ -83,7 +117,7 @@ func main() {
|
||||
api.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "pong"}) })
|
||||
}
|
||||
|
||||
// TeslaMateApi endpoints (bofore versioning)
|
||||
// TeslaMateApi endpoints (before versioning)
|
||||
r.GET("/cars", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/api/v1"+c.Request.RequestURI) })
|
||||
r.GET("/cars/:CarID", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/api/v1"+c.Request.RequestURI) })
|
||||
r.GET("/cars/:CarID/charges", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/api/v1"+c.Request.RequestURI) })
|
||||
@@ -282,3 +316,13 @@ func fahrenheitToCelsiusNilSupport(f NullFloat64) NullFloat64 {
|
||||
f.Float64 = ((f.Float64 - 32) * 5 / 9)
|
||||
return (f)
|
||||
}
|
||||
|
||||
// checkArrayContainsString func - check if string is inside stringarray
|
||||
func checkArrayContainsString(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user