mirror of
https://github.com/tobiasehlert/teslamateapi.git
synced 2026-02-27 09:54:18 +08:00
adding endpoints for charges and drives
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// TeslaMateAPICarsCharges func
|
||||
func TeslaMateAPICarsCharges(CarID int, ResultPage int, ResultShow int) (string, bool) {
|
||||
|
||||
// creating structs for /cars/<CarID>/charges
|
||||
// Car struct - child of Data
|
||||
type Car struct {
|
||||
CarID int `json:"car_id"` // smallint
|
||||
CarName string `json:"car_name"` // text
|
||||
}
|
||||
// BatteryDetails struct - child of Charges
|
||||
type BatteryDetails struct {
|
||||
StartBatteryLevel int `json:"start_battery_level"` // int
|
||||
EndBatteryLevel int `json:"end_battery_level"` // int
|
||||
}
|
||||
// PreferredRange struct - child of Charges
|
||||
type PreferredRange struct {
|
||||
StartRange float64 `json:"start_range"` // float64
|
||||
EndRange float64 `json:"end_range"` // float64
|
||||
}
|
||||
// Charges struct - child of Data
|
||||
type Charges struct {
|
||||
ChargeID int `json:"charge_id"` // int
|
||||
StartDate string `json:"start_date"` // string
|
||||
EndDate string `json:"end_date"` // string
|
||||
Address string `json:"address"` // string
|
||||
ChargeEnergyAdded float64 `json:"charge_energy_added"` // float64
|
||||
ChargeEnergyUsed float64 `json:"charge_energy_used"` // float64
|
||||
Cost float64 `json:"cost"` // float64
|
||||
DurationMin int `json:"duration_min"` // int
|
||||
DurationStr string `json:"duration_str"` // string
|
||||
BatteryDetails BatteryDetails `json:"battery_details"` // BatteryDetails
|
||||
RangeIdeal PreferredRange `json:"range_ideal"` // PreferredRange
|
||||
RangeRated PreferredRange `json:"range_rated"` // PreferredRange
|
||||
OutsideTempAvg float64 `json:"outside_temp_avg"` // float64
|
||||
}
|
||||
// TeslaMateUnits struct - child of Data
|
||||
type TeslaMateUnits struct {
|
||||
UnitsLength string `json:"unit_of_length"` // string
|
||||
UnitsTemperature string `json:"unit_of_temperature"` // string
|
||||
}
|
||||
// Data struct - child of JSONData
|
||||
type Data struct {
|
||||
Car Car `json:"car"`
|
||||
Charges []Charges `json:"charges"`
|
||||
TeslaMateUnits TeslaMateUnits `json:"units"`
|
||||
}
|
||||
// JSONData struct - main
|
||||
type JSONData struct {
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
// creating required vars
|
||||
var ChargesData []Charges
|
||||
var UnitsLength, UnitsTemperature, CarName string
|
||||
var ValidResponse bool // default is false
|
||||
|
||||
// calculate offset based on page (page 0 is not possible, since first page is minimum 1)
|
||||
if ResultPage > 0 {
|
||||
ResultPage--
|
||||
} else {
|
||||
ResultPage = 0
|
||||
}
|
||||
ResultPage = (ResultPage * ResultShow)
|
||||
|
||||
// getting data from database
|
||||
query := `
|
||||
SELECT
|
||||
charging_processes.id AS charge_id,
|
||||
start_date,
|
||||
end_date,
|
||||
COALESCE(geofence.name, CONCAT_WS(', ', COALESCE(address.name, nullif(CONCAT_WS(' ', address.road, address.house_number), '')), address.city)) AS address,
|
||||
COALESCE(charge_energy_added, 0) AS charge_energy_added,
|
||||
COALESCE(charge_energy_used, 0) AS charge_energy_used,
|
||||
COALESCE(cost, 0) AS cost,
|
||||
start_ideal_range_km AS start_ideal_range,
|
||||
end_ideal_range_km AS end_ideal_range,
|
||||
start_rated_range_km AS start_rated_range,
|
||||
end_rated_range_km AS end_rated_range,
|
||||
start_battery_level,
|
||||
end_battery_level,
|
||||
duration_min,
|
||||
TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,
|
||||
outside_temp_avg,
|
||||
(SELECT unit_of_length FROM settings LIMIT 1) as unit_of_length,
|
||||
(SELECT unit_of_temperature FROM settings LIMIT 1) as unit_of_temperature,
|
||||
cars.name
|
||||
FROM charging_processes
|
||||
LEFT JOIN cars ON car_id = cars.id
|
||||
LEFT JOIN addresses address ON address_id = address.id
|
||||
LEFT JOIN positions position ON position_id = position.id
|
||||
LEFT JOIN geofences geofence ON geofence_id = geofence.id
|
||||
WHERE charging_processes.car_id=$1
|
||||
ORDER BY start_date DESC
|
||||
LIMIT $2 OFFSET $3;`
|
||||
rows, err := db.Query(query, CarID, ResultShow, ResultPage)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// creating charge object based on struct
|
||||
charge := Charges{}
|
||||
|
||||
// scanning row and putting values into the charge
|
||||
err = rows.Scan(
|
||||
&charge.ChargeID,
|
||||
&charge.StartDate,
|
||||
&charge.EndDate,
|
||||
&charge.Address,
|
||||
&charge.ChargeEnergyAdded,
|
||||
&charge.ChargeEnergyUsed,
|
||||
&charge.Cost,
|
||||
&charge.RangeIdeal.StartRange,
|
||||
&charge.RangeIdeal.EndRange,
|
||||
&charge.RangeRated.StartRange,
|
||||
&charge.RangeRated.EndRange,
|
||||
&charge.BatteryDetails.StartBatteryLevel,
|
||||
&charge.BatteryDetails.EndBatteryLevel,
|
||||
&charge.DurationMin,
|
||||
&charge.DurationStr,
|
||||
&charge.OutsideTempAvg,
|
||||
&UnitsLength,
|
||||
&UnitsTemperature,
|
||||
&CarName,
|
||||
)
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
charge.RangeIdeal.StartRange = kilometersToMiles(charge.RangeIdeal.StartRange)
|
||||
charge.RangeIdeal.EndRange = kilometersToMiles(charge.RangeIdeal.EndRange)
|
||||
charge.RangeRated.StartRange = kilometersToMiles(charge.RangeRated.StartRange)
|
||||
charge.RangeRated.EndRange = kilometersToMiles(charge.RangeRated.EndRange)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
charge.OutsideTempAvg = celsiusToFahrenheit(charge.OutsideTempAvg)
|
||||
}
|
||||
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
charge.StartDate = getTimeInTimeZone(charge.StartDate)
|
||||
charge.EndDate = getTimeInTimeZone(charge.EndDate)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// appending charge to ChargesData
|
||||
ChargesData = append(ChargesData, charge)
|
||||
ValidResponse = true
|
||||
}
|
||||
|
||||
// checking for errors in the rows result
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//
|
||||
// build the data-blob
|
||||
jsonData := JSONData{
|
||||
Data{
|
||||
Car: Car{
|
||||
CarID: CarID,
|
||||
CarName: CarName,
|
||||
},
|
||||
Charges: ChargesData,
|
||||
TeslaMateUnits: TeslaMateUnits{
|
||||
UnitsLength: UnitsLength,
|
||||
UnitsTemperature: UnitsTemperature,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// print readable output to log
|
||||
log.Printf("data for /cars/%d/charges created:", CarID)
|
||||
|
||||
js, _ := json.Marshal(jsonData)
|
||||
fmt.Printf("%s\n", js)
|
||||
return string(js), ValidResponse
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// TeslaMateAPICarsChargesDetails func
|
||||
func TeslaMateAPICarsChargesDetails(CarID int, ChargeID int) (string, bool) {
|
||||
|
||||
// creating structs for /cars/<CarID>/charges/<ChargeID>
|
||||
// Car struct - child of Data
|
||||
type Car struct {
|
||||
CarID int `json:"car_id"` // smallint
|
||||
CarName string `json:"car_name"` // text
|
||||
}
|
||||
// BatteryDetails struct - child of Charges
|
||||
type BatteryDetails struct {
|
||||
StartBatteryLevel int `json:"start_battery_level"` // int
|
||||
EndBatteryLevel int `json:"end_battery_level"` // int
|
||||
}
|
||||
// PreferredRange struct - child of Charges
|
||||
type PreferredRange struct {
|
||||
StartRange float64 `json:"start_range"` // float64
|
||||
EndRange float64 `json:"end_range"` // float64
|
||||
}
|
||||
// ChargerDetails struct - child of ChargeDetails
|
||||
type ChargerDetails struct {
|
||||
ChargerActualCurrent int `json:"charger_actual_current"` // int
|
||||
ChargerPhases int `json:"charger_phases"` // int
|
||||
ChargerPilotCurrent int `json:"charger_pilot_current"` // int
|
||||
ChargerPower int `json:"charger_power"` // int
|
||||
ChargerVoltage int `json:"charger_voltage"` // int
|
||||
}
|
||||
// FastChargerInfo struct - child of ChargeDetails
|
||||
type FastChargerInfo struct {
|
||||
FastChargerPresent bool `json:"fast_charger_present"` // bool
|
||||
FastChargerBrand string `json:"fast_charger_brand"` // string
|
||||
FastChargerType string `json:"fast_charger_type"` // string
|
||||
|
||||
}
|
||||
// BatteryInfo struct - child of ChargeDetails
|
||||
type BatteryInfo struct {
|
||||
IdealBatteryRange float64 `json:"ideal_battery_range"` // float64
|
||||
RatedBatteryRange float64 `json:"rated_battery_range"` // float64
|
||||
BatteryHeater bool `json:"battery_heater"` // bool
|
||||
BatteryHeaterOn bool `json:"battery_heater_on"` // bool
|
||||
BatteryHeaterNoPower bool `json:"battery_heater_no_power"` // bool
|
||||
}
|
||||
// ChargeDetails struct - child of Charge
|
||||
type ChargeDetails struct {
|
||||
DetailID int `json:"detail_id"` // integer
|
||||
Date string `json:"date"` // string
|
||||
BatteryLevel int `json:"battery_level"` // int
|
||||
UsableBatteryLevel int `json:"usable_battery_level"` // int
|
||||
ChargeEnergyAdded float64 `json:"charge_energy_added"` // float64
|
||||
NotEnoughPowerToHeat bool `json:"not_enough_power_to_heat"` // bool
|
||||
ChargerDetails ChargerDetails `json:"charger_details"` // struct
|
||||
BatteryInfo BatteryInfo `json:"battery_info"` // struct
|
||||
ConnChargeCable string `json:"conn_charge_cable"` // string
|
||||
FastChargerInfo FastChargerInfo `json:"fast_charger_info"` // struct
|
||||
OutsideTemp float64 `json:"outside_temp"` // float64
|
||||
}
|
||||
// Charge struct - child of Data
|
||||
type Charge struct {
|
||||
ChargeID int `json:"charge_id"` // int
|
||||
StartDate string `json:"start_date"` // string
|
||||
EndDate string `json:"end_date"` // string
|
||||
Address string `json:"address"` // string
|
||||
ChargeEnergyAdded float64 `json:"charge_energy_added"` // float64
|
||||
ChargeEnergyUsed float64 `json:"charge_energy_used"` // float64
|
||||
Cost float64 `json:"cost"` // float64
|
||||
DurationMin int `json:"duration_min"` // int
|
||||
DurationStr string `json:"duration_str"` // string
|
||||
BatteryDetails BatteryDetails `json:"battery_details"` // BatteryDetails
|
||||
RangeIdeal PreferredRange `json:"range_ideal"` // PreferredRange
|
||||
RangeRated PreferredRange `json:"range_rated"` // PreferredRange
|
||||
OutsideTempAvg float64 `json:"outside_temp_avg"` // float64
|
||||
ChargeDetails []ChargeDetails `json:"charge_details"` // struct
|
||||
}
|
||||
// TeslaMateUnits struct - child of Data
|
||||
type TeslaMateUnits struct {
|
||||
UnitsLength string `json:"unit_of_length"` // string
|
||||
UnitsTemperature string `json:"unit_of_temperature"` // string
|
||||
}
|
||||
// Data struct - child of JSONData
|
||||
type Data struct {
|
||||
Car Car `json:"car"`
|
||||
Charge Charge `json:"charge"`
|
||||
TeslaMateUnits TeslaMateUnits `json:"units"`
|
||||
}
|
||||
// JSONData struct - main
|
||||
type JSONData struct {
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
// creating required vars
|
||||
var ChargeData Charge
|
||||
var ChargeDetailsData []ChargeDetails
|
||||
var UnitsLength, UnitsTemperature, CarName string
|
||||
var ValidResponse bool // default is false
|
||||
|
||||
// getting data from database
|
||||
query := `
|
||||
SELECT
|
||||
charging_processes.id AS charge_id,
|
||||
start_date,
|
||||
end_date,
|
||||
COALESCE(geofence.name, CONCAT_WS(', ', COALESCE(address.name, nullif(CONCAT_WS(' ', address.road, address.house_number), '')), address.city)) AS address,
|
||||
COALESCE(charging_processes.charge_energy_added, 0) AS charge_energy_added,
|
||||
COALESCE(charge_energy_used, 0) AS charge_energy_used,
|
||||
COALESCE(cost, 0) AS cost,
|
||||
start_ideal_range_km AS start_ideal_range,
|
||||
end_ideal_range_km AS end_ideal_range,
|
||||
start_rated_range_km AS start_rated_range,
|
||||
end_rated_range_km AS end_rated_range,
|
||||
start_battery_level,
|
||||
end_battery_level,
|
||||
duration_min,
|
||||
TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,
|
||||
outside_temp_avg,
|
||||
(SELECT unit_of_length FROM settings LIMIT 1) as unit_of_length,
|
||||
(SELECT unit_of_temperature FROM settings LIMIT 1) as unit_of_temperature,
|
||||
cars.name
|
||||
FROM charging_processes
|
||||
LEFT JOIN cars ON car_id = cars.id
|
||||
LEFT JOIN addresses address ON address_id = address.id
|
||||
LEFT JOIN positions position ON position_id = position.id
|
||||
LEFT JOIN geofences geofence ON geofence_id = geofence.id
|
||||
LEFT JOIN charges ON charging_processes.id = charges.id
|
||||
WHERE charging_processes.car_id=$1 AND charging_processes.id=$2
|
||||
ORDER BY start_date DESC;`
|
||||
rows, err := db.Query(query, CarID, ChargeID)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// creating charge object based on struct
|
||||
charge := Charge{}
|
||||
|
||||
// scanning row and putting values into the charge
|
||||
err = rows.Scan(
|
||||
&charge.ChargeID,
|
||||
&charge.StartDate,
|
||||
&charge.EndDate,
|
||||
&charge.Address,
|
||||
&charge.ChargeEnergyAdded,
|
||||
&charge.ChargeEnergyUsed,
|
||||
&charge.Cost,
|
||||
&charge.RangeIdeal.StartRange,
|
||||
&charge.RangeIdeal.EndRange,
|
||||
&charge.RangeRated.StartRange,
|
||||
&charge.RangeRated.EndRange,
|
||||
&charge.BatteryDetails.StartBatteryLevel,
|
||||
&charge.BatteryDetails.EndBatteryLevel,
|
||||
&charge.DurationMin,
|
||||
&charge.DurationStr,
|
||||
&charge.OutsideTempAvg,
|
||||
&UnitsLength,
|
||||
&UnitsTemperature,
|
||||
&CarName,
|
||||
)
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
charge.RangeIdeal.StartRange = kilometersToMiles(charge.RangeIdeal.StartRange)
|
||||
charge.RangeIdeal.EndRange = kilometersToMiles(charge.RangeIdeal.EndRange)
|
||||
charge.RangeRated.StartRange = kilometersToMiles(charge.RangeRated.StartRange)
|
||||
charge.RangeRated.EndRange = kilometersToMiles(charge.RangeRated.EndRange)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
charge.OutsideTempAvg = celsiusToFahrenheit(charge.OutsideTempAvg)
|
||||
}
|
||||
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
charge.StartDate = getTimeInTimeZone(charge.StartDate)
|
||||
charge.EndDate = getTimeInTimeZone(charge.EndDate)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// appending charge to ChargeData
|
||||
ChargeData = charge
|
||||
ValidResponse = true
|
||||
|
||||
// getting detailed charge data from database
|
||||
query = `
|
||||
SELECT
|
||||
id AS detail_id,
|
||||
date,
|
||||
battery_level,
|
||||
usable_battery_level,
|
||||
charge_energy_added,
|
||||
not_enough_power_to_heat,
|
||||
COALESCE(charger_actual_current, 0) as charger_actual_current,
|
||||
COALESCE(charger_phases, 0) AS charger_phases,
|
||||
COALESCE(charger_pilot_current, 0) as charger_pilot_current,
|
||||
COALESCE(charger_power, 0) as charger_power,
|
||||
COALESCE(charger_voltage, 0) as charger_voltage,
|
||||
ideal_battery_range_km AS ideal_battery_range,
|
||||
rated_battery_range_km AS rated_battery_range,
|
||||
battery_heater,
|
||||
battery_heater_on,
|
||||
battery_heater_no_power,
|
||||
conn_charge_cable,
|
||||
fast_charger_present,
|
||||
fast_charger_brand,
|
||||
fast_charger_type,
|
||||
outside_temp
|
||||
FROM charges
|
||||
WHERE charging_process_id=$1
|
||||
ORDER BY id ASC;`
|
||||
rows, err = db.Query(query, ChargeID)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// creating chargedetails object based on struct
|
||||
chargedetails := ChargeDetails{}
|
||||
|
||||
// scanning row and putting values into the drive
|
||||
err = rows.Scan(
|
||||
&chargedetails.DetailID,
|
||||
&chargedetails.Date,
|
||||
&chargedetails.BatteryLevel,
|
||||
&chargedetails.UsableBatteryLevel,
|
||||
&chargedetails.ChargeEnergyAdded,
|
||||
&chargedetails.NotEnoughPowerToHeat,
|
||||
&chargedetails.ChargerDetails.ChargerActualCurrent,
|
||||
&chargedetails.ChargerDetails.ChargerPhases,
|
||||
&chargedetails.ChargerDetails.ChargerPilotCurrent,
|
||||
&chargedetails.ChargerDetails.ChargerPower,
|
||||
&chargedetails.ChargerDetails.ChargerVoltage,
|
||||
&chargedetails.BatteryInfo.IdealBatteryRange,
|
||||
&chargedetails.BatteryInfo.RatedBatteryRange,
|
||||
&chargedetails.BatteryInfo.BatteryHeater,
|
||||
&chargedetails.BatteryInfo.BatteryHeaterOn,
|
||||
&chargedetails.BatteryInfo.BatteryHeaterNoPower,
|
||||
&chargedetails.ConnChargeCable,
|
||||
&chargedetails.FastChargerInfo.FastChargerPresent,
|
||||
&chargedetails.FastChargerInfo.FastChargerBrand,
|
||||
&chargedetails.FastChargerInfo.FastChargerType,
|
||||
&chargedetails.OutsideTemp,
|
||||
)
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
chargedetails.BatteryInfo.IdealBatteryRange = kilometersToMiles(chargedetails.BatteryInfo.IdealBatteryRange)
|
||||
chargedetails.BatteryInfo.RatedBatteryRange = kilometersToMiles(chargedetails.BatteryInfo.RatedBatteryRange)
|
||||
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
chargedetails.OutsideTemp = celsiusToFahrenheit(chargedetails.OutsideTemp)
|
||||
}
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
chargedetails.Date = getTimeInTimeZone(chargedetails.Date)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// appending drive to ChargeData
|
||||
ChargeDetailsData = append(ChargeDetailsData, chargedetails)
|
||||
ChargeData.ChargeDetails = ChargeDetailsData
|
||||
}
|
||||
}
|
||||
|
||||
// checking for errors in the rows result
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//
|
||||
// build the data-blob
|
||||
jsonData := JSONData{
|
||||
Data{
|
||||
Car: Car{
|
||||
CarID: CarID,
|
||||
CarName: CarName,
|
||||
},
|
||||
Charge: ChargeData,
|
||||
TeslaMateUnits: TeslaMateUnits{
|
||||
UnitsLength: UnitsLength,
|
||||
UnitsTemperature: UnitsTemperature,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// print readable output to log
|
||||
log.Printf("data for /cars/%d/charges/%d created:", CarID, ChargeID)
|
||||
|
||||
js, _ := json.Marshal(jsonData)
|
||||
fmt.Printf("%s\n", js)
|
||||
return string(js), ValidResponse
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// TeslaMateAPICarsDrives func
|
||||
func TeslaMateAPICarsDrives(CarID int, ResultPage int, ResultShow int) (string, bool) {
|
||||
|
||||
// creating structs for /cars/<CarID>/drives
|
||||
// Car struct - child of Data
|
||||
type Car struct {
|
||||
CarID int `json:"car_id"` // smallint
|
||||
CarName string `json:"car_name"` // text
|
||||
}
|
||||
// OdometerDetails struct - child of Drives
|
||||
type OdometerDetails struct {
|
||||
OdometerStart float64 `json:"odometer_start"` // float64
|
||||
OdometerEnd float64 `json:"odometer_end"` // float64
|
||||
OdometerDistance float64 `json:"odometer_distance"` // float64
|
||||
}
|
||||
// BatteryDetails struct - child of Drives
|
||||
type BatteryDetails struct {
|
||||
StartUsableBatteryLevel int `json:"start_usable_battery_level"` // int
|
||||
StartBatteryLevel int `json:"start_battery_level"` // int
|
||||
EndUsableBatteryLevel int `json:"end_usable_battery_level"` // int
|
||||
EndBatteryLevel int `json:"end_battery_level"` // int
|
||||
ReducedRange bool `json:"reduced_range"` // bool
|
||||
IsSufficientlyPrecise bool `json:"is_sufficiently_precise"` // bool
|
||||
}
|
||||
// PreferredRange struct - child of Drives
|
||||
type PreferredRange struct {
|
||||
StartRange float64 `json:"start_range"` // float64
|
||||
EndRange float64 `json:"end_range"` // float64
|
||||
RangeDiff float64 `json:"range_diff"` // float64
|
||||
}
|
||||
// Drives struct - child of Data
|
||||
type Drives struct {
|
||||
DriveID int `json:"drive_id"` // int
|
||||
StartDate string `json:"start_date"` // string
|
||||
EndDate string `json:"end_date"` // string
|
||||
StartAddress string `json:"start_address"` // string
|
||||
EndAddress string `json:"end_address"` // string
|
||||
OdometerDetails OdometerDetails `json:"odometer_details"` // OdometerDetails
|
||||
DurationMin int `json:"duration_min"` // int
|
||||
DurationStr string `json:"duration_str"` // string
|
||||
SpeedMax int `json:"speed_max"` // int
|
||||
SpeedAvg float64 `json:"speed_avg"` // float64
|
||||
PowerMax int `json:"power_max"` // int
|
||||
PowerMin int `json:"power_min"` // int
|
||||
BatteryDetails BatteryDetails `json:"battery_details"` // BatteryDetails
|
||||
RangeIdeal PreferredRange `json:"range_ideal"` // PreferredRange
|
||||
RangeRated PreferredRange `json:"range_rated"` // PreferredRange
|
||||
OutsideTempAvg float64 `json:"outside_temp_avg"` // float64
|
||||
InsideTempAvg float64 `json:"inside_temp_avg"` // float64
|
||||
}
|
||||
// TeslaMateUnits struct - child of Data
|
||||
type TeslaMateUnits struct {
|
||||
UnitsLength string `json:"unit_of_length"` // string
|
||||
UnitsTemperature string `json:"unit_of_temperature"` // string
|
||||
}
|
||||
// Data struct - child of JSONData
|
||||
type Data struct {
|
||||
Car Car `json:"car"`
|
||||
Drives []Drives `json:"drives"`
|
||||
TeslaMateUnits TeslaMateUnits `json:"units"`
|
||||
}
|
||||
// JSONData struct - main
|
||||
type JSONData struct {
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
// creating required vars
|
||||
var DrivesData []Drives
|
||||
var UnitsLength, UnitsTemperature, CarName string
|
||||
var ValidResponse bool // default is false
|
||||
|
||||
// calculate offset based on page (page 0 is not possible, since first page is minimum 1)
|
||||
if ResultPage > 0 {
|
||||
ResultPage--
|
||||
} else {
|
||||
ResultPage = 0
|
||||
}
|
||||
ResultPage = (ResultPage * ResultShow)
|
||||
|
||||
// getting data from database
|
||||
query := `
|
||||
SELECT
|
||||
drives.id AS drive_id,
|
||||
start_date,
|
||||
end_date,
|
||||
COALESCE(start_geofence.name, CONCAT_WS(', ', COALESCE(start_address.name, nullif(CONCAT_WS(' ', start_address.road, start_address.house_number), '')), start_address.city)) AS start_address,
|
||||
COALESCE(end_geofence.name, CONCAT_WS(', ', COALESCE(end_address.name, nullif(CONCAT_WS(' ', end_address.road, end_address.house_number), '')), end_address.city)) AS end_address,
|
||||
start_km,
|
||||
end_km,
|
||||
distance,
|
||||
duration_min,
|
||||
TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,
|
||||
speed_max,
|
||||
COALESCE(distance / NULLIF(duration_min, 0) * 60, 0) AS speed_avg,
|
||||
power_max,
|
||||
power_min,
|
||||
COALESCE(start_position.usable_battery_level, start_position.battery_level) as start_usable_battery_level,
|
||||
start_position.battery_level as start_battery_level,
|
||||
COALESCE(end_position.usable_battery_level, end_position.battery_level) as end_usable_battery_level,
|
||||
end_position.battery_level as end_battery_level,
|
||||
case when ( start_position.battery_level != start_position.usable_battery_level OR end_position.battery_level != end_position.usable_battery_level ) = true then true else false end as reduced_range,
|
||||
duration_min > 1 AND distance > 1 AND ( start_position.usable_battery_level IS NULL OR end_position.usable_battery_level IS NULL OR ( end_position.battery_level - end_position.usable_battery_level ) = 0 ) as is_sufficiently_precise,
|
||||
start_ideal_range_km,
|
||||
end_ideal_range_km,
|
||||
COALESCE( NULLIF ( GREATEST ( start_ideal_range_km - end_ideal_range_km, 0 ), 0 ),0 ) as range_diff_ideal_km,
|
||||
start_rated_range_km,
|
||||
end_rated_range_km,
|
||||
COALESCE( NULLIF ( GREATEST ( start_rated_range_km - end_rated_range_km, 0 ), 0 ),0 ) as range_diff_rated_km,
|
||||
outside_temp_avg,
|
||||
inside_temp_avg,
|
||||
(SELECT unit_of_length FROM settings LIMIT 1) as unit_of_length,
|
||||
(SELECT unit_of_temperature FROM settings LIMIT 1) as unit_of_temperature,
|
||||
cars.name
|
||||
FROM drives
|
||||
LEFT JOIN cars ON car_id = cars.id
|
||||
LEFT JOIN addresses start_address ON start_address_id = start_address.id
|
||||
LEFT JOIN addresses end_address ON end_address_id = end_address.id
|
||||
LEFT JOIN positions start_position ON start_position_id = start_position.id
|
||||
LEFT JOIN positions end_position ON end_position_id = end_position.id
|
||||
LEFT JOIN geofences start_geofence ON start_geofence_id = start_geofence.id
|
||||
LEFT JOIN geofences end_geofence ON end_geofence_id = end_geofence.id
|
||||
WHERE drives.car_id=$1 AND end_date IS NOT NULL
|
||||
ORDER BY start_date DESC
|
||||
LIMIT $2 OFFSET $3;`
|
||||
rows, err := db.Query(query, CarID, ResultShow, ResultPage)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// creating drive object based on struct
|
||||
drive := Drives{}
|
||||
|
||||
// scanning row and putting values into the drive
|
||||
err = rows.Scan(
|
||||
&drive.DriveID,
|
||||
&drive.StartDate,
|
||||
&drive.EndDate,
|
||||
&drive.StartAddress,
|
||||
&drive.EndAddress,
|
||||
&drive.OdometerDetails.OdometerStart,
|
||||
&drive.OdometerDetails.OdometerEnd,
|
||||
&drive.OdometerDetails.OdometerDistance,
|
||||
&drive.DurationMin,
|
||||
&drive.DurationStr,
|
||||
&drive.SpeedMax,
|
||||
&drive.SpeedAvg,
|
||||
&drive.PowerMax,
|
||||
&drive.PowerMin,
|
||||
&drive.BatteryDetails.StartUsableBatteryLevel,
|
||||
&drive.BatteryDetails.StartBatteryLevel,
|
||||
&drive.BatteryDetails.EndUsableBatteryLevel,
|
||||
&drive.BatteryDetails.EndBatteryLevel,
|
||||
&drive.BatteryDetails.ReducedRange,
|
||||
&drive.BatteryDetails.IsSufficientlyPrecise,
|
||||
&drive.RangeIdeal.StartRange,
|
||||
&drive.RangeIdeal.EndRange,
|
||||
&drive.RangeIdeal.RangeDiff,
|
||||
&drive.RangeRated.StartRange,
|
||||
&drive.RangeRated.EndRange,
|
||||
&drive.RangeRated.RangeDiff,
|
||||
&drive.OutsideTempAvg,
|
||||
&drive.InsideTempAvg,
|
||||
&UnitsLength,
|
||||
&UnitsTemperature,
|
||||
&CarName,
|
||||
)
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
drive.OdometerDetails.OdometerStart = kilometersToMiles(drive.OdometerDetails.OdometerStart)
|
||||
drive.OdometerDetails.OdometerEnd = kilometersToMiles(drive.OdometerDetails.OdometerEnd)
|
||||
drive.OdometerDetails.OdometerDistance = kilometersToMiles(drive.OdometerDetails.OdometerDistance)
|
||||
drive.RangeIdeal.StartRange = kilometersToMiles(drive.RangeIdeal.StartRange)
|
||||
drive.RangeIdeal.EndRange = kilometersToMiles(drive.RangeIdeal.EndRange)
|
||||
drive.RangeIdeal.RangeDiff = kilometersToMiles(drive.RangeIdeal.RangeDiff)
|
||||
drive.RangeRated.StartRange = kilometersToMiles(drive.RangeRated.StartRange)
|
||||
drive.RangeRated.EndRange = kilometersToMiles(drive.RangeRated.EndRange)
|
||||
drive.RangeRated.RangeDiff = kilometersToMiles(drive.RangeRated.RangeDiff)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
drive.OutsideTempAvg = celsiusToFahrenheit(drive.OutsideTempAvg)
|
||||
drive.InsideTempAvg = celsiusToFahrenheit(drive.InsideTempAvg)
|
||||
}
|
||||
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
drive.StartDate = getTimeInTimeZone(drive.StartDate)
|
||||
drive.EndDate = getTimeInTimeZone(drive.EndDate)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// appending drive to DrivesData
|
||||
DrivesData = append(DrivesData, drive)
|
||||
ValidResponse = true
|
||||
}
|
||||
|
||||
// checking for errors in the rows result
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//
|
||||
// build the data-blob
|
||||
jsonData := JSONData{
|
||||
Data{
|
||||
Car: Car{
|
||||
CarID: CarID,
|
||||
CarName: CarName,
|
||||
},
|
||||
Drives: DrivesData,
|
||||
TeslaMateUnits: TeslaMateUnits{
|
||||
UnitsLength: UnitsLength,
|
||||
UnitsTemperature: UnitsTemperature,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// print readable output to log
|
||||
log.Printf("data for /cars/%d/drives created:", CarID)
|
||||
|
||||
js, _ := json.Marshal(jsonData)
|
||||
fmt.Printf("%s\n", js)
|
||||
return string(js), ValidResponse
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// TeslaMateAPICarsDrivesDetails func
|
||||
func TeslaMateAPICarsDrivesDetails(CarID int, DriveID int) (string, bool) {
|
||||
|
||||
// creating structs for /cars/<CarID>/drives/<DriveID>
|
||||
// Car struct - child of Data
|
||||
type Car struct {
|
||||
CarID int `json:"car_id"` // smallint
|
||||
CarName string `json:"car_name"` // text
|
||||
}
|
||||
// OdometerDetails struct - child of Drives
|
||||
type OdometerDetails struct {
|
||||
OdometerStart float64 `json:"odometer_start"` // float64
|
||||
OdometerEnd float64 `json:"odometer_end"` // float64
|
||||
OdometerDistance float64 `json:"odometer_distance"` // float64
|
||||
}
|
||||
// BatteryDetails struct - child of Drives
|
||||
type BatteryDetails struct {
|
||||
StartUsableBatteryLevel int `json:"start_usable_battery_level"` // int
|
||||
StartBatteryLevel int `json:"start_battery_level"` // int
|
||||
EndUsableBatteryLevel int `json:"end_usable_battery_level"` // int
|
||||
EndBatteryLevel int `json:"end_battery_level"` // int
|
||||
ReducedRange bool `json:"reduced_range"` // bool
|
||||
IsSufficientlyPrecise bool `json:"is_sufficiently_precise"` // bool
|
||||
}
|
||||
// PreferredRange struct - child of Drives
|
||||
type PreferredRange struct {
|
||||
StartRange float64 `json:"start_range"` // float64
|
||||
EndRange float64 `json:"end_range"` // float64
|
||||
RangeDiff float64 `json:"range_diff"` // float64
|
||||
}
|
||||
// ClimateInfo struct - child of DriveDetails
|
||||
type ClimateInfo struct {
|
||||
InsideTemp NullFloat64 `json:"inside_temp"` // numeric(4,1)
|
||||
OutsideTemp NullFloat64 `json:"outside_temp"` // numeric(4,1)
|
||||
IsClimateOn NullBool `json:"is_climate_on"` // boolean
|
||||
FanStatus NullInt64 `json:"fan_status"` // integer
|
||||
DriverTempSetting NullFloat64 `json:"driver_temp_setting"` // numeric(4,1)
|
||||
PassengerTempSetting NullFloat64 `json:"passenger_temp_setting"` // numeric(4,1)
|
||||
IsRearDefrosterOn NullBool `json:"is_rear_defroster_on"` // boolean
|
||||
IsFrontDefrosterOn NullBool `json:"is_front_defroster_on"` // boolean
|
||||
}
|
||||
// BatteryInfo struct - child of DriveDetails
|
||||
type BatteryInfo struct {
|
||||
EstBatteryRange NullFloat64 `json:"est_battery_range"` // numeric(6,2)
|
||||
IdealBatteryRange NullFloat64 `json:"ideal_battery_range"` // numeric(6,2)
|
||||
RatedBatteryRange NullFloat64 `json:"rated_battery_range"` // numeric(6,2)
|
||||
BatteryHeater NullBool `json:"battery_heater"` // boolean
|
||||
BatteryHeaterOn NullBool `json:"battery_heater_on"` // boolean
|
||||
BatteryHeaterNoPower NullBool `json:"battery_heater_no_power"` // boolean
|
||||
}
|
||||
// DriveDetails struct - child of Drive
|
||||
type DriveDetails struct {
|
||||
DetailID int `json:"detail_id"` // integer
|
||||
Date string `json:"date"` // timestamp without time zone
|
||||
Latitude float64 `json:"latitude"` // numeric(8,6)
|
||||
Longitude float64 `json:"longitude"` // numeric(9,6)
|
||||
Speed int `json:"speed"` // smallint
|
||||
Power int `json:"power"` // smallint
|
||||
Odometer float64 `json:"odometer"` // double precision
|
||||
BatteryLevel int `json:"battery_level"` // smallint
|
||||
UsableBatteryLevel NullInt64 `json:"usable_battery_level"` // smallint
|
||||
Elevation NullInt64 `json:"elevation"` // smallint
|
||||
ClimateInfo ClimateInfo `json:"climate_info"` // struct
|
||||
BatteryInfo BatteryInfo `json:"battery_info"` // struct
|
||||
}
|
||||
// Drive struct - child of Data
|
||||
type Drive struct {
|
||||
DriveID int `json:"drive_id"` // int
|
||||
StartDate string `json:"start_date"` // string
|
||||
EndDate string `json:"end_date"` // string
|
||||
StartAddress string `json:"start_address"` // string
|
||||
EndAddress string `json:"end_address"` // string
|
||||
OdometerDetails OdometerDetails `json:"odometer_details"` // OdometerDetails
|
||||
DurationMin int `json:"duration_min"` // int
|
||||
DurationStr string `json:"duration_str"` // string
|
||||
SpeedMax int `json:"speed_max"` // int
|
||||
SpeedAvg float64 `json:"speed_avg"` // float64
|
||||
PowerMax int `json:"power_max"` // int
|
||||
PowerMin int `json:"power_min"` // int
|
||||
BatteryDetails BatteryDetails `json:"battery_details"` // BatteryDetails
|
||||
RangeIdeal PreferredRange `json:"range_ideal"` // PreferredRange
|
||||
RangeRated PreferredRange `json:"range_rated"` // PreferredRange
|
||||
OutsideTempAvg float64 `json:"outside_temp_avg"` // float64
|
||||
InsideTempAvg float64 `json:"inside_temp_avg"` // float64
|
||||
DriveDetails []DriveDetails `json:"drive_details"` // struct
|
||||
}
|
||||
// TeslaMateUnits struct - child of Data
|
||||
type TeslaMateUnits struct {
|
||||
UnitsLength string `json:"unit_of_length"` // string
|
||||
UnitsTemperature string `json:"unit_of_temperature"` // string
|
||||
}
|
||||
// Data struct - child of JSONData
|
||||
type Data struct {
|
||||
Car Car `json:"car"`
|
||||
Drive Drive `json:"drive"`
|
||||
TeslaMateUnits TeslaMateUnits `json:"units"`
|
||||
}
|
||||
// JSONData struct - main
|
||||
type JSONData struct {
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
// creating required vars
|
||||
var DriveData Drive
|
||||
var DriveDetailsData []DriveDetails
|
||||
var UnitsLength, UnitsTemperature, CarName string
|
||||
var ValidResponse bool // default is false
|
||||
|
||||
// getting data from database
|
||||
query := `
|
||||
SELECT
|
||||
drives.id AS drive_id,
|
||||
start_date,
|
||||
end_date,
|
||||
COALESCE(start_geofence.name, CONCAT_WS(', ', COALESCE(start_address.name, nullif(CONCAT_WS(' ', start_address.road, start_address.house_number), '')), start_address.city)) AS start_address,
|
||||
COALESCE(end_geofence.name, CONCAT_WS(', ', COALESCE(end_address.name, nullif(CONCAT_WS(' ', end_address.road, end_address.house_number), '')), end_address.city)) AS end_address,
|
||||
start_km,
|
||||
end_km,
|
||||
distance,
|
||||
duration_min,
|
||||
TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,
|
||||
speed_max,
|
||||
COALESCE(distance / NULLIF(duration_min, 0) * 60, 0) AS speed_avg,
|
||||
power_max,
|
||||
power_min,
|
||||
COALESCE(start_position.usable_battery_level, start_position.battery_level) as start_usable_battery_level,
|
||||
start_position.battery_level as start_battery_level,
|
||||
COALESCE(end_position.usable_battery_level, end_position.battery_level) as end_usable_battery_level,
|
||||
end_position.battery_level as end_battery_level,
|
||||
case when ( start_position.battery_level != start_position.usable_battery_level OR end_position.battery_level != end_position.usable_battery_level ) = true then true else false end as reduced_range,
|
||||
duration_min > 1 AND distance > 1 AND ( start_position.usable_battery_level IS NULL OR end_position.usable_battery_level IS NULL OR ( end_position.battery_level - end_position.usable_battery_level ) = 0 ) as is_sufficiently_precise,
|
||||
start_ideal_range_km,
|
||||
end_ideal_range_km,
|
||||
COALESCE( NULLIF ( GREATEST ( start_ideal_range_km - end_ideal_range_km, 0 ), 0 ),0 ) as range_diff_ideal_km,
|
||||
start_rated_range_km,
|
||||
end_rated_range_km,
|
||||
COALESCE( NULLIF ( GREATEST ( start_rated_range_km - end_rated_range_km, 0 ), 0 ),0 ) as range_diff_rated_km,
|
||||
outside_temp_avg,
|
||||
inside_temp_avg,
|
||||
(SELECT unit_of_length FROM settings LIMIT 1) as unit_of_length,
|
||||
(SELECT unit_of_temperature FROM settings LIMIT 1) as unit_of_temperature,
|
||||
cars.name
|
||||
FROM drives
|
||||
LEFT JOIN cars ON car_id = cars.id
|
||||
LEFT JOIN addresses start_address ON start_address_id = start_address.id
|
||||
LEFT JOIN addresses end_address ON end_address_id = end_address.id
|
||||
LEFT JOIN positions start_position ON start_position_id = start_position.id
|
||||
LEFT JOIN positions end_position ON end_position_id = end_position.id
|
||||
LEFT JOIN geofences start_geofence ON start_geofence_id = start_geofence.id
|
||||
LEFT JOIN geofences end_geofence ON end_geofence_id = end_geofence.id
|
||||
WHERE drives.car_id=$1 AND end_date IS NOT NULL AND drives.id = $2;`
|
||||
rows, err := db.Query(query, CarID, DriveID)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// creating drive object based on struct
|
||||
drive := Drive{}
|
||||
|
||||
// scanning row and putting values into the drive
|
||||
err = rows.Scan(
|
||||
&drive.DriveID,
|
||||
&drive.StartDate,
|
||||
&drive.EndDate,
|
||||
&drive.StartAddress,
|
||||
&drive.EndAddress,
|
||||
&drive.OdometerDetails.OdometerStart,
|
||||
&drive.OdometerDetails.OdometerEnd,
|
||||
&drive.OdometerDetails.OdometerDistance,
|
||||
&drive.DurationMin,
|
||||
&drive.DurationStr,
|
||||
&drive.SpeedMax,
|
||||
&drive.SpeedAvg,
|
||||
&drive.PowerMax,
|
||||
&drive.PowerMin,
|
||||
&drive.BatteryDetails.StartUsableBatteryLevel,
|
||||
&drive.BatteryDetails.StartBatteryLevel,
|
||||
&drive.BatteryDetails.EndUsableBatteryLevel,
|
||||
&drive.BatteryDetails.EndBatteryLevel,
|
||||
&drive.BatteryDetails.ReducedRange,
|
||||
&drive.BatteryDetails.IsSufficientlyPrecise,
|
||||
&drive.RangeIdeal.StartRange,
|
||||
&drive.RangeIdeal.EndRange,
|
||||
&drive.RangeIdeal.RangeDiff,
|
||||
&drive.RangeRated.StartRange,
|
||||
&drive.RangeRated.EndRange,
|
||||
&drive.RangeRated.RangeDiff,
|
||||
&drive.OutsideTempAvg,
|
||||
&drive.InsideTempAvg,
|
||||
&UnitsLength,
|
||||
&UnitsTemperature,
|
||||
&CarName,
|
||||
)
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
drive.OdometerDetails.OdometerStart = kilometersToMiles(drive.OdometerDetails.OdometerStart)
|
||||
drive.OdometerDetails.OdometerEnd = kilometersToMiles(drive.OdometerDetails.OdometerEnd)
|
||||
drive.OdometerDetails.OdometerDistance = kilometersToMiles(drive.OdometerDetails.OdometerDistance)
|
||||
drive.SpeedMax = int(kilometersToMiles(float64(drive.SpeedMax)))
|
||||
drive.SpeedAvg = kilometersToMiles(drive.SpeedAvg)
|
||||
drive.RangeIdeal.StartRange = kilometersToMiles(drive.RangeIdeal.StartRange)
|
||||
drive.RangeIdeal.EndRange = kilometersToMiles(drive.RangeIdeal.EndRange)
|
||||
drive.RangeIdeal.RangeDiff = kilometersToMiles(drive.RangeIdeal.RangeDiff)
|
||||
drive.RangeRated.StartRange = kilometersToMiles(drive.RangeRated.StartRange)
|
||||
drive.RangeRated.EndRange = kilometersToMiles(drive.RangeRated.EndRange)
|
||||
drive.RangeRated.RangeDiff = kilometersToMiles(drive.RangeRated.RangeDiff)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
drive.OutsideTempAvg = celsiusToFahrenheit(drive.OutsideTempAvg)
|
||||
drive.InsideTempAvg = celsiusToFahrenheit(drive.InsideTempAvg)
|
||||
}
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
drive.StartDate = getTimeInTimeZone(drive.StartDate)
|
||||
drive.EndDate = getTimeInTimeZone(drive.EndDate)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// appending drive to DriveData
|
||||
DriveData = drive
|
||||
ValidResponse = true
|
||||
|
||||
// getting detailed drive data from database
|
||||
query = `
|
||||
SELECT
|
||||
id AS detail_id,
|
||||
date,
|
||||
latitude,
|
||||
longitude,
|
||||
COALESCE(speed, 0) AS speed,
|
||||
power,
|
||||
odometer,
|
||||
battery_level,
|
||||
usable_battery_level,
|
||||
elevation,
|
||||
inside_temp,
|
||||
outside_temp,
|
||||
is_climate_on,
|
||||
fan_status,
|
||||
driver_temp_setting,
|
||||
passenger_temp_setting,
|
||||
is_rear_defroster_on,
|
||||
is_front_defroster_on,
|
||||
est_battery_range_km,
|
||||
ideal_battery_range_km,
|
||||
rated_battery_range_km,
|
||||
battery_heater,
|
||||
battery_heater_on,
|
||||
battery_heater_no_power
|
||||
FROM positions
|
||||
WHERE drive_id = $1
|
||||
ORDER BY id ASC;`
|
||||
rows, err = db.Query(query, DriveID)
|
||||
|
||||
// checking for errors in query
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// defer closing rows
|
||||
defer rows.Close()
|
||||
|
||||
// looping through all results
|
||||
for rows.Next() {
|
||||
|
||||
// creating drivedetails object based on struct
|
||||
drivedetails := DriveDetails{}
|
||||
|
||||
// scanning row and putting values into the drive
|
||||
err = rows.Scan(
|
||||
&drivedetails.DetailID,
|
||||
&drivedetails.Date,
|
||||
&drivedetails.Latitude,
|
||||
&drivedetails.Longitude,
|
||||
&drivedetails.Speed,
|
||||
&drivedetails.Power,
|
||||
&drivedetails.Odometer,
|
||||
&drivedetails.BatteryLevel,
|
||||
&drivedetails.UsableBatteryLevel,
|
||||
&drivedetails.Elevation,
|
||||
&drivedetails.ClimateInfo.InsideTemp,
|
||||
&drivedetails.ClimateInfo.OutsideTemp,
|
||||
&drivedetails.ClimateInfo.IsClimateOn,
|
||||
&drivedetails.ClimateInfo.FanStatus,
|
||||
&drivedetails.ClimateInfo.DriverTempSetting,
|
||||
&drivedetails.ClimateInfo.PassengerTempSetting,
|
||||
&drivedetails.ClimateInfo.IsRearDefrosterOn,
|
||||
&drivedetails.ClimateInfo.IsFrontDefrosterOn,
|
||||
&drivedetails.BatteryInfo.EstBatteryRange,
|
||||
&drivedetails.BatteryInfo.IdealBatteryRange,
|
||||
&drivedetails.BatteryInfo.RatedBatteryRange,
|
||||
&drivedetails.BatteryInfo.BatteryHeater,
|
||||
&drivedetails.BatteryInfo.BatteryHeaterOn,
|
||||
&drivedetails.BatteryInfo.BatteryHeaterNoPower,
|
||||
)
|
||||
|
||||
// converting values based of settings UnitsLength
|
||||
if UnitsLength == "mi" {
|
||||
drivedetails.Odometer = kilometersToMiles(drivedetails.Odometer)
|
||||
drivedetails.Speed = int(kilometersToMiles(float64(drivedetails.Speed)))
|
||||
drivedetails.BatteryInfo.EstBatteryRange = kilometersToMilesNilSupport(drivedetails.BatteryInfo.EstBatteryRange)
|
||||
drivedetails.BatteryInfo.IdealBatteryRange = kilometersToMilesNilSupport(drivedetails.BatteryInfo.IdealBatteryRange)
|
||||
drivedetails.BatteryInfo.RatedBatteryRange = kilometersToMilesNilSupport(drivedetails.BatteryInfo.RatedBatteryRange)
|
||||
}
|
||||
// converting values based of settings UnitsTemperature
|
||||
if UnitsTemperature == "F" {
|
||||
drivedetails.ClimateInfo.InsideTemp = celsiusToFahrenheitNilSupport(drivedetails.ClimateInfo.InsideTemp)
|
||||
drivedetails.ClimateInfo.OutsideTemp = celsiusToFahrenheitNilSupport(drivedetails.ClimateInfo.OutsideTemp)
|
||||
drivedetails.ClimateInfo.DriverTempSetting = celsiusToFahrenheitNilSupport(drivedetails.ClimateInfo.DriverTempSetting)
|
||||
drivedetails.ClimateInfo.PassengerTempSetting = celsiusToFahrenheitNilSupport(drivedetails.ClimateInfo.PassengerTempSetting)
|
||||
}
|
||||
// adjusting to timezone differences from UTC to be userspecific
|
||||
drivedetails.Date = getTimeInTimeZone(drivedetails.Date)
|
||||
|
||||
// checking for errors after scanning
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// appending drive to DriveData
|
||||
DriveDetailsData = append(DriveDetailsData, drivedetails)
|
||||
DriveData.DriveDetails = DriveDetailsData
|
||||
}
|
||||
|
||||
// checking for errors in the rows result
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// checking for errors in the rows result
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//
|
||||
// build the data-blob
|
||||
jsonData := JSONData{
|
||||
Data{
|
||||
Car: Car{
|
||||
CarID: CarID,
|
||||
CarName: CarName,
|
||||
},
|
||||
Drive: DriveData,
|
||||
TeslaMateUnits: TeslaMateUnits{
|
||||
UnitsLength: UnitsLength,
|
||||
UnitsTemperature: UnitsTemperature,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// print readable output to log
|
||||
log.Printf("data for /cars/%d/drives/%d created:", CarID, DriveID)
|
||||
|
||||
js, _ := json.Marshal(jsonData)
|
||||
fmt.Printf("%s\n", js)
|
||||
return string(js), ValidResponse
|
||||
}
|
||||
@@ -56,6 +56,74 @@ func main() {
|
||||
}
|
||||
})
|
||||
|
||||
// /cars/<CarID>/charges endpoint
|
||||
r.GET("/cars/:CarID/charges", func(c *gin.Context) {
|
||||
// getting CarID param from URL
|
||||
CarID := convertStringToInteger(c.Param("CarID"))
|
||||
// query options to modify query when collecting data
|
||||
ResultPage := convertStringToInteger(c.DefaultQuery("page", "1"))
|
||||
ResultShow := convertStringToInteger(c.DefaultQuery("show", "100"))
|
||||
// TeslaMateAPICarsCharges to get data
|
||||
result, ValidResponse := TeslaMateAPICarsCharges(CarID, ResultPage, ResultShow)
|
||||
|
||||
c.Header("Content-Type", "application/json")
|
||||
if ValidResponse {
|
||||
c.String(http.StatusOK, result)
|
||||
} else {
|
||||
c.String(http.StatusNotFound, result)
|
||||
}
|
||||
})
|
||||
|
||||
// /cars/<CarID>/charges/<ChargeID> endpoint
|
||||
r.GET("/cars/:CarID/charges/:ChargeID", func(c *gin.Context) {
|
||||
// getting CarID and ChargeID param from URL
|
||||
CarID := convertStringToInteger(c.Param("CarID"))
|
||||
ChargeID := convertStringToInteger(c.Param("ChargeID"))
|
||||
// TeslaMateAPICarsChargesDetails to get data
|
||||
result, ValidResponse := TeslaMateAPICarsChargesDetails(CarID, ChargeID)
|
||||
|
||||
c.Header("Content-Type", "application/json")
|
||||
if ValidResponse {
|
||||
c.String(http.StatusOK, result)
|
||||
} else {
|
||||
c.String(http.StatusNotFound, result)
|
||||
}
|
||||
})
|
||||
|
||||
// /cars/<CarID>/drives endpoint
|
||||
r.GET("/cars/:CarID/drives", func(c *gin.Context) {
|
||||
// getting CarID param from URL
|
||||
CarID := convertStringToInteger(c.Param("CarID"))
|
||||
// query options to modify query when collecting data
|
||||
ResultPage := convertStringToInteger(c.DefaultQuery("page", "1"))
|
||||
ResultShow := convertStringToInteger(c.DefaultQuery("show", "100"))
|
||||
// TeslaMateAPICarsDrives to get data
|
||||
result, ValidResponse := TeslaMateAPICarsDrives(CarID, ResultPage, ResultShow)
|
||||
|
||||
c.Header("Content-Type", "application/json")
|
||||
if ValidResponse {
|
||||
c.String(http.StatusOK, result)
|
||||
} else {
|
||||
c.String(http.StatusNotFound, result)
|
||||
}
|
||||
})
|
||||
|
||||
// /cars/<CarID>/drives/<DriveID> endpoint
|
||||
r.GET("/cars/:CarID/drives/:DriveID", func(c *gin.Context) {
|
||||
// getting CarID and DriveID param from URL
|
||||
CarID := convertStringToInteger(c.Param("CarID"))
|
||||
DriveID := convertStringToInteger(c.Param("DriveID"))
|
||||
// TeslaMateAPICarsDrivesDetails to get data
|
||||
result, ValidResponse := TeslaMateAPICarsDrivesDetails(CarID, DriveID)
|
||||
|
||||
c.Header("Content-Type", "application/json")
|
||||
if ValidResponse {
|
||||
c.String(http.StatusOK, result)
|
||||
} else {
|
||||
c.String(http.StatusNotFound, result)
|
||||
}
|
||||
})
|
||||
|
||||
// /cars/<CarID>/status endpoint
|
||||
r.GET("/cars/:CarID/status", func(c *gin.Context) {
|
||||
// getting mqtt flag
|
||||
|
||||
Reference in New Issue
Block a user