Merge pull request #60 from tobiasehlert/feature-10-logging-commands

Feature 10 logging commands
This commit is contained in:
Tobias Lindberg
2021-05-19 19:36:06 +02:00
committed by GitHub
4 changed files with 134 additions and 1 deletions
+7 -1
View File
@@ -100,6 +100,9 @@ Basically the same environment variables for the database, mqqt and timezone nee
**Optional** environment variables
- **TESLAMATE_SSL** boolean *(default: false)*
- **TESLAMATE_HOST** string *(default: teslamate)*
- **TESLAMATE_PORT** string *(default: 4000)*
- **API_TOKEN** string *(default: )*
- **DATABASE_PORT** integer *(default: 5432)*
- **DATABASE_TIMEOUT** integer *(default: 60000)*
@@ -117,6 +120,7 @@ Basically the same environment variables for the database, mqqt and timezone nee
- **ENABLE_COMMANDS** boolean *(default: false)*
- **COMMANDS_ALL** boolean *(default: false)*
- **COMMANDS_ALLOWLIST** string *(default: allow_list.json)*
- **COMMANDS_LOGGING** boolean *(deafault: false)*
- **COMMANDS_WAKE** boolean *(default: false)*
- **COMMANDS_ALERT** boolean *(default: false)*
- **COMMANDS_REMOTESTART** boolean *(default: false)*
@@ -150,6 +154,8 @@ More detailed documentation of every endpoint will come..
- POST `/api/v1/cars/:CarID/command/:Command`
- GET `/api/v1/cars/:CarID/drives`
- GET `/api/v1/cars/:CarID/drives/:DriveID`
- PUT `/api/v1/cars/:CarID/logging/:Command`
- GET `/api/v1/cars/:CarID/logging`
- GET `/api/v1/cars/:CarID/status`
- GET `/api/v1/cars/:CarID/updates`
- POST `/api/v1/cars/:CarID/wake_up`
@@ -158,7 +164,7 @@ More detailed documentation of every endpoint will come..
### 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.
If you want to use command or logging endpoints such as `/api/v1/cars/:CarID/command/:Command`, `/api/v1/cars/:CarID/wake_up`, or `/api/v1/cars/:CarID/logging/:Command` 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.
+7
View File
@@ -16,6 +16,13 @@ func initCommandAllowList() {
// allow all commands available below
allowAll := getEnvAsBool("COMMANDS_ALL", false)
// https://github.com/adriankumpf/teslamate/discussions/1433
if getEnvAsBool("COMMANDS_LOGGING", false) || allowAll {
allowList = append(allowList,
"/logging/resume",
"/logging/suspend")
}
// https://tesla-api.timdorr.com/vehicle/commands/wake
if getEnvAsBool("COMMANDS_WAKE", false) || allowAll {
allowList = append(allowList, "/wake_up")
+116
View File
@@ -0,0 +1,116 @@
package main
import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
"strings"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)
// TeslaMateAPICarsLoggingV1 func
func TeslaMateAPICarsLoggingV1(c *gin.Context) {
// creating required vars
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] TeslaMateAPICarsLoggingV1 ENABLE_COMMANDS is not true.. returning 403 forbidden.")
c.JSON(http.StatusForbidden, gin.H{"error": "You are not allowed to access logging 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] TeslaMateAPICarsLoggingV1 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] TeslaMateAPICarsLoggingV1 error in first ioutil.ReadAll", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal"})
return
}
// getting :Command
command := ("/logging/" + c.Param("Command"))
log.Println("[debug] TeslaMateAPICarsLoggingV1 command received:", command)
if !checkArrayContainsString(allowList, command) {
log.Print("[warning] TeslaMateAPICarsLoggingV1 command: " + command + " not allowed")
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
client := &http.Client{}
putURL := ""
if getEnvAsBool("TESLAMATE_SSL", false) {
putURL = "https://"
} else {
putURL = "http://"
}
putURL = putURL + getEnv("TESLAMATE_HOST", "teslamate") + ":" + getEnv("TESLAMATE_PORT", "4000") + "/api/car/" + ParamCarID + command
req, _ := http.NewRequest(http.MethodPut, putURL, strings.NewReader(string(reqBody)))
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] TeslaMateAPICarsLoggingV1 error in http request to http://teslamate:", 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] TeslaMateAPICarsLoggingV1 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] TeslaMateAPICarsLoggingV1 " + c.Request.RequestURI + " returned data:")
js, _ := json.Marshal(jsonData)
log.Printf("[debug] %s\n", js)
}
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent {
log.Println("[info] TeslaMateAPICarsLoggingV1 " + c.Request.RequestURI + " executed successful.")
} else {
log.Println("[error] TeslaMateAPICarsLoggingV1 " + c.Request.RequestURI + " error in execution!")
}
c.JSON(resp.StatusCode, jsonData)
}
+4
View File
@@ -100,6 +100,10 @@ func main() {
v1.GET("/cars/:CarID/drives", TeslaMateAPICarsDrivesV1)
v1.GET("/cars/:CarID/drives/:DriveID", TeslaMateAPICarsDrivesDetailsV1)
// v1 /api/v1/cars/:CarID/logging endpoints
v1.GET("/cars/:CarID/logging", TeslaMateAPICarsLoggingV1)
v1.PUT("/cars/:CarID/logging/:Command", TeslaMateAPICarsLoggingV1)
// v1 /api/v1/cars/:CarID/status endpoints
v1.GET("/cars/:CarID/status", statusCache.TeslaMateAPICarsStatusV1)