mirror of
https://github.com/tobiasehlert/teslamateapi.git
synced 2026-02-27 09:54:18 +08:00
Refactor MQTT so it connects once to broker and caches messages
This commit is contained in:
+255
-288
@@ -2,10 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
@@ -13,8 +14,8 @@ import (
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// defining a lot of vars that are used my the MQTT MessageHandler
|
||||
var (
|
||||
// statusInfo holds the status info for a car
|
||||
type statusInfo struct {
|
||||
MQTTDataDisplayName string
|
||||
MQTTDataState string
|
||||
MQTTDataStateSince string
|
||||
@@ -61,127 +62,25 @@ var (
|
||||
MQTTDataChargerVoltage int
|
||||
MQTTDataScheduledChargingStartTime string
|
||||
MQTTDataTimeToFullCharge float64
|
||||
)
|
||||
|
||||
// define a function for the default message handler
|
||||
var f mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
|
||||
|
||||
// extracting the last part of topic
|
||||
_, MqttTopic := path.Split(msg.Topic())
|
||||
|
||||
// running if-else statements to collect data and put into overall vars..
|
||||
if MqttTopic == "display_name" {
|
||||
MQTTDataDisplayName = string(msg.Payload())
|
||||
} else if MqttTopic == "state" {
|
||||
MQTTDataState = string(msg.Payload())
|
||||
} else if MqttTopic == "since" {
|
||||
MQTTDataStateSince = string(msg.Payload())
|
||||
} else if MqttTopic == "healthy" {
|
||||
MQTTDataHealthy = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "version" {
|
||||
MQTTDataVersion = string(msg.Payload())
|
||||
} else if MqttTopic == "update_available" {
|
||||
MQTTDataUpdateAvailable = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "update_version" {
|
||||
MQTTDataUpdateVersion = string(msg.Payload())
|
||||
} else if MqttTopic == "model" {
|
||||
MQTTDataModel = string(msg.Payload())
|
||||
} else if MqttTopic == "trim_badging" {
|
||||
MQTTDataTrimBadging = string(msg.Payload())
|
||||
} else if MqttTopic == "exterior_color" {
|
||||
MQTTDataExteriorColor = string(msg.Payload())
|
||||
} else if MqttTopic == "wheel_type" {
|
||||
MQTTDataWheelType = string(msg.Payload())
|
||||
} else if MqttTopic == "spoiler_type" {
|
||||
MQTTDataSpoilerType = string(msg.Payload())
|
||||
} else if MqttTopic == "geofence" {
|
||||
MQTTDataGeofence = string(msg.Payload())
|
||||
} else if MqttTopic == "latitude" {
|
||||
MQTTDataLatitude = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "longitude" {
|
||||
MQTTDataLongitude = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "shift_state" {
|
||||
MQTTDataShiftState = string(msg.Payload())
|
||||
} else if MqttTopic == "speed" {
|
||||
MQTTDataSpeed = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "heading" {
|
||||
MQTTDataHeading = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "elevation" {
|
||||
MQTTDataElevation = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "locked" {
|
||||
MQTTDataLocked = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "sentry_mode" {
|
||||
MQTTDataSentryMode = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "windows_open" {
|
||||
MQTTDataWindowsOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "doors_open" {
|
||||
MQTTDataDoorsOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "trunk_open" {
|
||||
MQTTDataTrunkOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "frunk_open" {
|
||||
MQTTDataFrunkOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "is_user_present" {
|
||||
MQTTDataIsUserPresent = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "is_climate_on" {
|
||||
MQTTDataIsClimateOn = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "inside_temp" {
|
||||
MQTTDataInsideTemp = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "outside_temp" {
|
||||
MQTTDataOutsideTemp = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "is_preconditioning" {
|
||||
MQTTDataIsPreconditioning = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "odometer" {
|
||||
MQTTDataOdometer = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "est_battery_range_km" {
|
||||
MQTTDataEstBatteryRange = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "rated_battery_range_km" {
|
||||
MQTTDataRatedBatteryRange = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "ideal_battery_range_km" {
|
||||
MQTTDataIdealBatteryRange = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "battery_level" {
|
||||
MQTTDataBatteryLevel = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "usable_battery_level" {
|
||||
MQTTDataUsableBatteryLevel = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "plugged_in" {
|
||||
MQTTDataPluggedIn = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "charge_energy_added" {
|
||||
MQTTDataChargeEnergyAdded = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "charge_limit_soc" {
|
||||
MQTTDataChargeLimitSoc = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "charge_port_door_open" {
|
||||
MQTTDataChargePortDoorOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_actual_current" {
|
||||
MQTTDataChargerActualCurrent = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_phases" {
|
||||
MQTTDataChargerPhases = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_power" {
|
||||
MQTTDataChargerPower = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_voltage" {
|
||||
MQTTDataChargerVoltage = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "scheduled_charging_start_time" {
|
||||
MQTTDataScheduledChargingStartTime = string(msg.Payload())
|
||||
} else if MqttTopic == "time_to_full_charge" {
|
||||
MQTTDataTimeToFullCharge = convertStringToFloat(string(msg.Payload()))
|
||||
} else {
|
||||
log.Printf("[warning] mqtt.MessageHandler issue.. extraction of data for %s not implemented!", MqttTopic)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TeslaMateAPICarsStatusV1 func
|
||||
func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
type statusCache struct {
|
||||
mqttDisabled bool
|
||||
|
||||
topicScan string // scan parameter (expect it to generate car ID then relevant parameter)
|
||||
|
||||
cache map[int]*statusInfo
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func startMQTT() (*statusCache, error) {
|
||||
var s statusCache
|
||||
// getting mqtt flag
|
||||
mqttdisabled := getEnvAsBool("DISABLE_MQTT", false)
|
||||
if mqttdisabled {
|
||||
log.Println("[notice] TeslaMateAPICarsStatusV1 DISABLE_MQTT is set to true.. can not return status for car without mqtt!")
|
||||
c.JSON(http.StatusNotImplemented, gin.H{"error": "mqtt disabled.. status not accessible!"})
|
||||
return
|
||||
s.mqttDisabled = getEnvAsBool("DISABLE_MQTT", false)
|
||||
if s.mqttDisabled {
|
||||
return nil, errors.New("[notice] TeslaMateAPICarsStatusV1 DISABLE_MQTT is set to true.. can not return status for car without mqtt!")
|
||||
}
|
||||
|
||||
// getting CarID param from URL
|
||||
CarID := convertStringToInteger(c.Param("CarID"))
|
||||
|
||||
// default values that get might get overwritten..
|
||||
MQTTPort := 0
|
||||
MQTTUserstring := ""
|
||||
@@ -199,7 +98,6 @@ func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
MQTTUser := getEnv("MQTT_USERNAME", "")
|
||||
MQTTPass := getEnv("MQTT_PASSWORD", "")
|
||||
// MQTTInvCert := getEnvAsBool("MQTT_TLS_ACCEPT_INVALID_CERTS", false)
|
||||
MQTTNameSpace := getEnv("MQTT_NAMESPACE", "")
|
||||
|
||||
// creating mqttURL to connect with
|
||||
// mqtt[s]://[username][:password]@host.domain[:port]
|
||||
@@ -215,11 +113,6 @@ func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
|
||||
mqttURL := fmt.Sprintf("%s://%s%s:%d", MQTTProtocol, MQTTUserstring, MQTTHost, MQTTPort)
|
||||
|
||||
// adding MQTTNameSpace info
|
||||
if len(MQTTNameSpace) > 0 {
|
||||
MQTTNameSpace = ("/" + MQTTNameSpace)
|
||||
}
|
||||
|
||||
/*
|
||||
// if some more logging is needed.. which we skip for now
|
||||
mqtt.DEBUG = log.New(os.Stdout, "", 0)
|
||||
@@ -229,16 +122,20 @@ func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
// create options for the MQTT client connection
|
||||
opts := mqtt.NewClientOptions().AddBroker(mqttURL)
|
||||
// setting generic MQTT settings in opts
|
||||
opts.SetKeepAlive(2 * time.Second) // setting keepalive for client
|
||||
opts.SetDefaultPublishHandler(f) // using f mqtt.MessageHandler function
|
||||
opts.SetPingTimeout(1 * time.Second) // setting pingtimeout for client
|
||||
opts.SetClientID("teslamateapi") // setting mqtt client id for TeslaMateApi
|
||||
opts.SetCleanSession(true) // removal of all subscriptions on disconnect
|
||||
opts.SetKeepAlive(2 * time.Second) // setting keepalive for client
|
||||
opts.SetDefaultPublishHandler(s.newMessage) // using f mqtt.MessageHandler function
|
||||
opts.SetPingTimeout(1 * time.Second) // setting pingtimeout for client
|
||||
opts.SetClientID("teslamateapi") // setting mqtt client id for TeslaMateApi
|
||||
opts.SetCleanSession(true) // removal of all subscriptions on disconnect
|
||||
opts.SetOrderMatters(false) // don't care about order (removes need for callbacks to return immediately)
|
||||
opts.SetAutoReconnect(true) // if connection drops automatically re-establish it
|
||||
opts.AutoReconnect = true
|
||||
|
||||
// creating MQTT connection with options
|
||||
m := mqtt.NewClient(opts)
|
||||
if token := m.Connect(); token.Wait() && token.Error() != nil {
|
||||
log.Panic(token.Error())
|
||||
return nil, fmt.Errorf("failed to connect to MQTT: %w", token.Error())
|
||||
// Note : May want to use opts.ConnectRetry which will keep trying the connection
|
||||
}
|
||||
|
||||
// showing mqtt successfully connected
|
||||
@@ -246,6 +143,163 @@ func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
log.Println("TeslaMateAPICarsStatus successfully connected to mqtt.")
|
||||
}
|
||||
|
||||
// adding MQTTNameSpace info
|
||||
MQTTNameSpace := getEnv("MQTT_NAMESPACE", "")
|
||||
if len(MQTTNameSpace) > 0 {
|
||||
MQTTNameSpace = ("/" + MQTTNameSpace)
|
||||
}
|
||||
|
||||
// Subscribe - we will accept info on any car...
|
||||
topic := fmt.Sprintf("teslamate%s/cars/#", MQTTNameSpace)
|
||||
if token := m.Subscribe(topic, 0, s.newMessage); token.Wait() && token.Error() != nil {
|
||||
log.Panic(token.Error()) // Note : May want to use opts.ConnectRetry which will keep trying the connection
|
||||
}
|
||||
s.topicScan = fmt.Sprintf("teslamate%s/cars/%%d/%%s", MQTTNameSpace)
|
||||
|
||||
// Thats all - newMessage will be called when something new arrives
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// newMessage - called by mqtt package when new message received
|
||||
func (s *statusCache) newMessage(c mqtt.Client, msg mqtt.Message) {
|
||||
// topic is in the format teslamateMQTT_NAMESPACE/cars/carID/display_name
|
||||
var carID int
|
||||
var MqttTopic string
|
||||
_, err := fmt.Sscanf(msg.Topic(), s.topicScan, &carID, &MqttTopic)
|
||||
if err != nil {
|
||||
log.Printf("Unexpected topic format (%s) - ignoring message: %v", msg.Topic(), err)
|
||||
return
|
||||
}
|
||||
|
||||
// extracting the last part of topic
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
stat := s.cache[carID]
|
||||
if stat == nil {
|
||||
stat = &statusInfo{}
|
||||
s.cache[carID] = stat
|
||||
}
|
||||
|
||||
// running if-else statements to collect data and put into overall vars..
|
||||
if MqttTopic == "display_name" {
|
||||
stat.MQTTDataDisplayName = string(msg.Payload())
|
||||
} else if MqttTopic == "state" {
|
||||
stat.MQTTDataState = string(msg.Payload())
|
||||
} else if MqttTopic == "since" {
|
||||
stat.MQTTDataStateSince = string(msg.Payload())
|
||||
} else if MqttTopic == "healthy" {
|
||||
stat.MQTTDataHealthy = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "version" {
|
||||
stat.MQTTDataVersion = string(msg.Payload())
|
||||
} else if MqttTopic == "update_available" {
|
||||
stat.MQTTDataUpdateAvailable = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "update_version" {
|
||||
stat.MQTTDataUpdateVersion = string(msg.Payload())
|
||||
} else if MqttTopic == "model" {
|
||||
stat.MQTTDataModel = string(msg.Payload())
|
||||
} else if MqttTopic == "trim_badging" {
|
||||
stat.MQTTDataTrimBadging = string(msg.Payload())
|
||||
} else if MqttTopic == "exterior_color" {
|
||||
stat.MQTTDataExteriorColor = string(msg.Payload())
|
||||
} else if MqttTopic == "wheel_type" {
|
||||
stat.MQTTDataWheelType = string(msg.Payload())
|
||||
} else if MqttTopic == "spoiler_type" {
|
||||
stat.MQTTDataSpoilerType = string(msg.Payload())
|
||||
} else if MqttTopic == "geofence" {
|
||||
stat.MQTTDataGeofence = string(msg.Payload())
|
||||
} else if MqttTopic == "latitude" {
|
||||
stat.MQTTDataLatitude = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "longitude" {
|
||||
stat.MQTTDataLongitude = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "shift_state" {
|
||||
stat.MQTTDataShiftState = string(msg.Payload())
|
||||
} else if MqttTopic == "speed" {
|
||||
stat.MQTTDataSpeed = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "heading" {
|
||||
stat.MQTTDataHeading = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "elevation" {
|
||||
stat.MQTTDataElevation = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "locked" {
|
||||
stat.MQTTDataLocked = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "sentry_mode" {
|
||||
stat.MQTTDataSentryMode = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "windows_open" {
|
||||
stat.MQTTDataWindowsOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "doors_open" {
|
||||
stat.MQTTDataDoorsOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "trunk_open" {
|
||||
stat.MQTTDataTrunkOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "frunk_open" {
|
||||
stat.MQTTDataFrunkOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "is_user_present" {
|
||||
stat.MQTTDataIsUserPresent = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "is_climate_on" {
|
||||
stat.MQTTDataIsClimateOn = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "inside_temp" {
|
||||
stat.MQTTDataInsideTemp = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "outside_temp" {
|
||||
stat.MQTTDataOutsideTemp = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "is_preconditioning" {
|
||||
stat.MQTTDataIsPreconditioning = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "odometer" {
|
||||
stat.MQTTDataOdometer = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "est_battery_range_km" {
|
||||
stat.MQTTDataEstBatteryRange = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "rated_battery_range_km" {
|
||||
stat.MQTTDataRatedBatteryRange = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "ideal_battery_range_km" {
|
||||
stat.MQTTDataIdealBatteryRange = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "battery_level" {
|
||||
stat.MQTTDataBatteryLevel = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "usable_battery_level" {
|
||||
stat.MQTTDataUsableBatteryLevel = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "plugged_in" {
|
||||
stat.MQTTDataPluggedIn = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "charge_energy_added" {
|
||||
stat.MQTTDataChargeEnergyAdded = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "charge_limit_soc" {
|
||||
stat.MQTTDataChargeLimitSoc = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "charge_port_door_open" {
|
||||
stat.MQTTDataChargePortDoorOpen = convertStringToBool(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_actual_current" {
|
||||
stat.MQTTDataChargerActualCurrent = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_phases" {
|
||||
stat.MQTTDataChargerPhases = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_power" {
|
||||
stat.MQTTDataChargerPower = convertStringToFloat(string(msg.Payload()))
|
||||
} else if MqttTopic == "charger_voltage" {
|
||||
stat.MQTTDataChargerVoltage = convertStringToInteger(string(msg.Payload()))
|
||||
} else if MqttTopic == "scheduled_charging_start_time" {
|
||||
stat.MQTTDataScheduledChargingStartTime = string(msg.Payload())
|
||||
} else if MqttTopic == "time_to_full_charge" {
|
||||
stat.MQTTDataTimeToFullCharge = convertStringToFloat(string(msg.Payload()))
|
||||
} else {
|
||||
log.Printf("[warning] mqtt.MessageHandler issue.. extraction of data for %s not implemented!", MqttTopic)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TeslaMateAPICarsStatusV1 func
|
||||
func (s *statusCache) TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
if s.mqttDisabled {
|
||||
log.Println("[notice] TeslaMateAPICarsStatusV1 DISABLE_MQTT is set to true.. can not return status for car without mqtt!")
|
||||
c.JSON(http.StatusNotImplemented, gin.H{"error": "mqtt disabled.. status not accessible!"})
|
||||
return
|
||||
}
|
||||
|
||||
// getting CarID param from URL
|
||||
carID := convertStringToInteger(c.Param("CarID"))
|
||||
|
||||
// Now see what data we have on the car
|
||||
s.mu.Lock()
|
||||
stat := s.cache[carID]
|
||||
s.mu.Unlock()
|
||||
|
||||
if stat == nil {
|
||||
c.JSON(http.StatusNoContent, gin.H{"error": "no info on this car ID"})
|
||||
return
|
||||
}
|
||||
|
||||
// creating structs for /cars
|
||||
// BatteryDetails struct - child of MQTTInformation
|
||||
type BatteryDetails struct {
|
||||
@@ -359,7 +413,7 @@ func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
var UnitsLength, UnitsTemperature string
|
||||
var ValidResponse bool // default is false
|
||||
|
||||
// getting data from database
|
||||
// getting data from database (assume that carID is unique!)
|
||||
query := `
|
||||
SELECT
|
||||
id,
|
||||
@@ -369,171 +423,84 @@ func TeslaMateAPICarsStatusV1(c *gin.Context) {
|
||||
FROM cars
|
||||
WHERE id=$1
|
||||
LIMIT 1;`
|
||||
rows, err := db.Query(query, CarID)
|
||||
err := db.QueryRow(query, carID).Scan(&CarData.CarID,
|
||||
&CarData.CarName,
|
||||
&UnitsLength,
|
||||
&UnitsTemperature)
|
||||
|
||||
// checking for errors in query
|
||||
// checking for errors in query (this will include no rows found)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
// setting data from MQTT into data fields to return
|
||||
MQTTInformationData.DisplayName = stat.MQTTDataDisplayName
|
||||
MQTTInformationData.State = stat.MQTTDataState
|
||||
MQTTInformationData.StateSince = stat.MQTTDataStateSince
|
||||
MQTTInformationData.CarStatus.Healthy = stat.MQTTDataHealthy
|
||||
MQTTInformationData.CarVersions.Version = stat.MQTTDataVersion
|
||||
MQTTInformationData.CarVersions.UpdateAvailable = stat.MQTTDataUpdateAvailable
|
||||
MQTTInformationData.CarVersions.UpdateVersion = stat.MQTTDataUpdateVersion
|
||||
MQTTInformationData.CarDetails.Model = stat.MQTTDataModel
|
||||
MQTTInformationData.CarDetails.TrimBadging = stat.MQTTDataTrimBadging
|
||||
MQTTInformationData.CarExterior.ExteriorColor = stat.MQTTDataExteriorColor
|
||||
MQTTInformationData.CarExterior.WheelType = stat.MQTTDataWheelType
|
||||
MQTTInformationData.CarExterior.SpoilerType = stat.MQTTDataSpoilerType
|
||||
MQTTInformationData.CarGeodata.Geofence = stat.MQTTDataGeofence
|
||||
MQTTInformationData.CarGeodata.Latitude = stat.MQTTDataLatitude
|
||||
MQTTInformationData.CarGeodata.Longitude = stat.MQTTDataLongitude
|
||||
MQTTInformationData.DrivingDetails.ShiftState = stat.MQTTDataShiftState
|
||||
MQTTInformationData.DrivingDetails.Speed = stat.MQTTDataSpeed
|
||||
MQTTInformationData.DrivingDetails.Heading = stat.MQTTDataHeading
|
||||
MQTTInformationData.DrivingDetails.Elevation = stat.MQTTDataElevation
|
||||
MQTTInformationData.CarStatus.Locked = stat.MQTTDataLocked
|
||||
MQTTInformationData.CarStatus.SentryMode = stat.MQTTDataSentryMode
|
||||
MQTTInformationData.CarStatus.WindowsOpen = stat.MQTTDataWindowsOpen
|
||||
MQTTInformationData.CarStatus.DoorsOpen = stat.MQTTDataDoorsOpen
|
||||
MQTTInformationData.CarStatus.TrunkOpen = stat.MQTTDataTrunkOpen
|
||||
MQTTInformationData.CarStatus.FrunkOpen = stat.MQTTDataFrunkOpen
|
||||
MQTTInformationData.CarStatus.IsUserPresent = stat.MQTTDataIsUserPresent
|
||||
MQTTInformationData.ClimateDetails.IsClimateOn = stat.MQTTDataIsClimateOn
|
||||
MQTTInformationData.ClimateDetails.InsideTemp = stat.MQTTDataInsideTemp
|
||||
MQTTInformationData.ClimateDetails.OutsideTemp = stat.MQTTDataOutsideTemp
|
||||
MQTTInformationData.ClimateDetails.IsPreconditioning = stat.MQTTDataIsPreconditioning
|
||||
MQTTInformationData.Odometer = stat.MQTTDataOdometer
|
||||
MQTTInformationData.BatteryDetails.EstBatteryRange = stat.MQTTDataEstBatteryRange
|
||||
MQTTInformationData.BatteryDetails.RatedBatteryRange = stat.MQTTDataRatedBatteryRange
|
||||
MQTTInformationData.BatteryDetails.IdealBatteryRange = stat.MQTTDataIdealBatteryRange
|
||||
MQTTInformationData.BatteryDetails.BatteryLevel = stat.MQTTDataBatteryLevel
|
||||
MQTTInformationData.BatteryDetails.UsableBatteryLevel = stat.MQTTDataUsableBatteryLevel
|
||||
MQTTInformationData.ChargingDetails.PluggedIn = stat.MQTTDataPluggedIn
|
||||
MQTTInformationData.ChargingDetails.ChargeEnergyAdded = stat.MQTTDataChargeEnergyAdded
|
||||
MQTTInformationData.ChargingDetails.ChargeLimitSoc = stat.MQTTDataChargeLimitSoc
|
||||
MQTTInformationData.ChargingDetails.ChargePortDoorOpen = stat.MQTTDataChargePortDoorOpen
|
||||
MQTTInformationData.ChargingDetails.ChargerActualCurrent = stat.MQTTDataChargerActualCurrent
|
||||
MQTTInformationData.ChargingDetails.ChargerPhases = stat.MQTTDataChargerPhases
|
||||
MQTTInformationData.ChargingDetails.ChargerPower = stat.MQTTDataChargerPower
|
||||
MQTTInformationData.ChargingDetails.ChargerVoltage = stat.MQTTDataChargerVoltage
|
||||
MQTTInformationData.ChargingDetails.ScheduledChargingStartTime = stat.MQTTDataScheduledChargingStartTime
|
||||
MQTTInformationData.ChargingDetails.TimeToFullCharge = stat.MQTTDataTimeToFullCharge
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// scanning row and putting values into the car
|
||||
err = rows.Scan(
|
||||
&CarData.CarID,
|
||||
&CarData.CarName,
|
||||
&UnitsLength,
|
||||
&UnitsTemperature,
|
||||
)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if CarID != 0 && CarID == CarData.CarID || CarID == 0 {
|
||||
|
||||
// creating list of all topics to subscribe on..
|
||||
MQTTAllTopics := map[string]byte{
|
||||
fmt.Sprintf("teslamate%s/cars/%d/display_name", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/state", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/since", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/healthy", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/version", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/update_available", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/update_version", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/model", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/trim_badging", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/exterior_color", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/wheel_type", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/spoiler_type", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/geofence", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/latitude", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/longitude", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/shift_state", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/speed", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/heading", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/elevation", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/locked", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/sentry_mode", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/windows_open", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/doors_open", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/trunk_open", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/frunk_open", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/is_user_present", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/is_climate_on", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/inside_temp", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/outside_temp", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/is_preconditioning", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/odometer", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/est_battery_range_km", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/rated_battery_range_km", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/ideal_battery_range_km", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/battery_level", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/usable_battery_level", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/plugged_in", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charge_energy_added", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charge_limit_soc", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charge_port_door_open", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charger_actual_current", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charger_phases", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charger_power", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/charger_voltage", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/scheduled_charging_start_time", MQTTNameSpace, CarID): 0,
|
||||
fmt.Sprintf("teslamate%s/cars/%d/time_to_full_charge", MQTTNameSpace, CarID): 0,
|
||||
}
|
||||
|
||||
// run SubscribeMultiple on MQTTAllTopics map[string]byte
|
||||
m.SubscribeMultiple(MQTTAllTopics, nil)
|
||||
|
||||
// getting sleep tim before disconnecting
|
||||
MQTTSleepTime := getEnvAsInt("MQTT_SLEEPTIME", 100)
|
||||
// adding some short sleep before disconnecting (based on MQTTSleepTimer)
|
||||
time.Sleep(time.Duration(MQTTSleepTime) * time.Millisecond)
|
||||
|
||||
// disconnecting from MQTT
|
||||
m.Disconnect(250)
|
||||
|
||||
// setting data from MQTT into data fields to return
|
||||
MQTTInformationData.DisplayName = MQTTDataDisplayName
|
||||
MQTTInformationData.State = MQTTDataState
|
||||
MQTTInformationData.StateSince = MQTTDataStateSince
|
||||
MQTTInformationData.CarStatus.Healthy = MQTTDataHealthy
|
||||
MQTTInformationData.CarVersions.Version = MQTTDataVersion
|
||||
MQTTInformationData.CarVersions.UpdateAvailable = MQTTDataUpdateAvailable
|
||||
MQTTInformationData.CarVersions.UpdateVersion = MQTTDataUpdateVersion
|
||||
MQTTInformationData.CarDetails.Model = MQTTDataModel
|
||||
MQTTInformationData.CarDetails.TrimBadging = MQTTDataTrimBadging
|
||||
MQTTInformationData.CarExterior.ExteriorColor = MQTTDataExteriorColor
|
||||
MQTTInformationData.CarExterior.WheelType = MQTTDataWheelType
|
||||
MQTTInformationData.CarExterior.SpoilerType = MQTTDataSpoilerType
|
||||
MQTTInformationData.CarGeodata.Geofence = MQTTDataGeofence
|
||||
MQTTInformationData.CarGeodata.Latitude = MQTTDataLatitude
|
||||
MQTTInformationData.CarGeodata.Longitude = MQTTDataLongitude
|
||||
MQTTInformationData.DrivingDetails.ShiftState = MQTTDataShiftState
|
||||
MQTTInformationData.DrivingDetails.Speed = MQTTDataSpeed
|
||||
MQTTInformationData.DrivingDetails.Heading = MQTTDataHeading
|
||||
MQTTInformationData.DrivingDetails.Elevation = MQTTDataElevation
|
||||
MQTTInformationData.CarStatus.Locked = MQTTDataLocked
|
||||
MQTTInformationData.CarStatus.SentryMode = MQTTDataSentryMode
|
||||
MQTTInformationData.CarStatus.WindowsOpen = MQTTDataWindowsOpen
|
||||
MQTTInformationData.CarStatus.DoorsOpen = MQTTDataDoorsOpen
|
||||
MQTTInformationData.CarStatus.TrunkOpen = MQTTDataTrunkOpen
|
||||
MQTTInformationData.CarStatus.FrunkOpen = MQTTDataFrunkOpen
|
||||
MQTTInformationData.CarStatus.IsUserPresent = MQTTDataIsUserPresent
|
||||
MQTTInformationData.ClimateDetails.IsClimateOn = MQTTDataIsClimateOn
|
||||
MQTTInformationData.ClimateDetails.InsideTemp = MQTTDataInsideTemp
|
||||
MQTTInformationData.ClimateDetails.OutsideTemp = MQTTDataOutsideTemp
|
||||
MQTTInformationData.ClimateDetails.IsPreconditioning = MQTTDataIsPreconditioning
|
||||
MQTTInformationData.Odometer = MQTTDataOdometer
|
||||
MQTTInformationData.BatteryDetails.EstBatteryRange = MQTTDataEstBatteryRange
|
||||
MQTTInformationData.BatteryDetails.RatedBatteryRange = MQTTDataRatedBatteryRange
|
||||
MQTTInformationData.BatteryDetails.IdealBatteryRange = MQTTDataIdealBatteryRange
|
||||
MQTTInformationData.BatteryDetails.BatteryLevel = MQTTDataBatteryLevel
|
||||
MQTTInformationData.BatteryDetails.UsableBatteryLevel = MQTTDataUsableBatteryLevel
|
||||
MQTTInformationData.ChargingDetails.PluggedIn = MQTTDataPluggedIn
|
||||
MQTTInformationData.ChargingDetails.ChargeEnergyAdded = MQTTDataChargeEnergyAdded
|
||||
MQTTInformationData.ChargingDetails.ChargeLimitSoc = MQTTDataChargeLimitSoc
|
||||
MQTTInformationData.ChargingDetails.ChargePortDoorOpen = MQTTDataChargePortDoorOpen
|
||||
MQTTInformationData.ChargingDetails.ChargerActualCurrent = MQTTDataChargerActualCurrent
|
||||
MQTTInformationData.ChargingDetails.ChargerPhases = MQTTDataChargerPhases
|
||||
MQTTInformationData.ChargingDetails.ChargerPower = MQTTDataChargerPower
|
||||
MQTTInformationData.ChargingDetails.ChargerVoltage = MQTTDataChargerVoltage
|
||||
MQTTInformationData.ChargingDetails.ScheduledChargingStartTime = MQTTDataScheduledChargingStartTime
|
||||
MQTTInformationData.ChargingDetails.TimeToFullCharge = MQTTDataTimeToFullCharge
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
// drive.OdometerDetails.OdometerStart = kilometersToMiles(drive.OdometerDetails.OdometerStart)
|
||||
MQTTInformationData.Odometer = kilometersToMiles(MQTTInformationData.Odometer)
|
||||
MQTTInformationData.BatteryDetails.EstBatteryRange = kilometersToMiles(MQTTInformationData.BatteryDetails.EstBatteryRange)
|
||||
MQTTInformationData.BatteryDetails.RatedBatteryRange = kilometersToMiles(MQTTInformationData.BatteryDetails.RatedBatteryRange)
|
||||
MQTTInformationData.BatteryDetails.IdealBatteryRange = kilometersToMiles(MQTTInformationData.BatteryDetails.IdealBatteryRange)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
MQTTInformationData.ClimateDetails.InsideTemp = celsiusToFahrenheit(MQTTInformationData.ClimateDetails.InsideTemp)
|
||||
MQTTInformationData.ClimateDetails.OutsideTemp = celsiusToFahrenheit(MQTTInformationData.ClimateDetails.OutsideTemp)
|
||||
}
|
||||
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
MQTTInformationData.StateSince = getTimeInTimeZone(MQTTInformationData.StateSince)
|
||||
MQTTInformationData.ChargingDetails.ScheduledChargingStartTime = getTimeInTimeZone(MQTTInformationData.ChargingDetails.ScheduledChargingStartTime)
|
||||
|
||||
// setting response as valid
|
||||
ValidResponse = true
|
||||
}
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
// drive.OdometerDetails.OdometerStart = kilometersToMiles(drive.OdometerDetails.OdometerStart)
|
||||
MQTTInformationData.Odometer = kilometersToMiles(MQTTInformationData.Odometer)
|
||||
MQTTInformationData.BatteryDetails.EstBatteryRange = kilometersToMiles(MQTTInformationData.BatteryDetails.EstBatteryRange)
|
||||
MQTTInformationData.BatteryDetails.RatedBatteryRange = kilometersToMiles(MQTTInformationData.BatteryDetails.RatedBatteryRange)
|
||||
MQTTInformationData.BatteryDetails.IdealBatteryRange = kilometersToMiles(MQTTInformationData.BatteryDetails.IdealBatteryRange)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
MQTTInformationData.ClimateDetails.InsideTemp = celsiusToFahrenheit(MQTTInformationData.ClimateDetails.InsideTemp)
|
||||
MQTTInformationData.ClimateDetails.OutsideTemp = celsiusToFahrenheit(MQTTInformationData.ClimateDetails.OutsideTemp)
|
||||
}
|
||||
|
||||
// checking for errors in the rows result
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
MQTTInformationData.StateSince = getTimeInTimeZone(MQTTInformationData.StateSince)
|
||||
MQTTInformationData.ChargingDetails.ScheduledChargingStartTime = getTimeInTimeZone(MQTTInformationData.ChargingDetails.ScheduledChargingStartTime)
|
||||
|
||||
// setting response as valid
|
||||
ValidResponse = true
|
||||
|
||||
//
|
||||
// build the data-blob
|
||||
|
||||
+7
-1
@@ -38,6 +38,12 @@ func main() {
|
||||
initDBconnection()
|
||||
defer db.Close()
|
||||
|
||||
// Connect to the MQTT broker
|
||||
statusCache, err := startMQTT()
|
||||
if err != nil {
|
||||
log.Fatalf("MQTT connection failed: %s", err)
|
||||
}
|
||||
|
||||
// kicking off Gin in value r
|
||||
r := gin.Default()
|
||||
|
||||
@@ -68,7 +74,7 @@ func main() {
|
||||
v1.GET("/cars/:CarID/charges/:ChargeID", TeslaMateAPICarsChargesDetailsV1)
|
||||
v1.GET("/cars/:CarID/drives", TeslaMateAPICarsDrivesV1)
|
||||
v1.GET("/cars/:CarID/drives/:DriveID", TeslaMateAPICarsDrivesDetailsV1)
|
||||
v1.GET("/cars/:CarID/status", TeslaMateAPICarsStatusV1)
|
||||
v1.GET("/cars/:CarID/status", statusCache.TeslaMateAPICarsStatusV1)
|
||||
v1.GET("/cars/:CarID/updates", TeslaMateAPICarsUpdatesV1)
|
||||
v1.GET("/globalsettings", TeslaMateAPIGlobalsettingsV1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user