-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AS-3125 Map current set of Tesla fields #94
Merged
Merged
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
dc8d4af
Tesla mapping file
elffjs 7232450
Comments on units
elffjs 47fb648
More comments on CurrentPower
elffjs 0ea7811
Conversion functions
elffjs 5f88339
A little test. Too little
elffjs b469868
More tests
elffjs 624d0c6
Fix power field, more tests
elffjs 3f43ff4
Everything tested
elffjs 5646adb
Add generation task
elffjs 8be0d9a
Test the CloudEvent wrapper conversion instead
elffjs 501f538
Energy added
elffjs a0587da
Some more comments
elffjs 7072479
Official VSS commit
elffjs 77310b7
Add energy added test, clean up constants
elffjs 5314b10
Merge branch 'main' into as-3125-first-set-of-tesla-mappings
elffjs 3bf1192
Package comment
elffjs d4fea77
Gen
elffjs f8bec32
Use header struct instead
elffjs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Code generated by github.com/DIMO-Network/model-garage DO NOT EDIT. | ||
package tesla | ||
|
||
var errNotFound = errors.New("field not found") | ||
|
||
// SignalsFromV1Data creates a slice of vss.Signal from the given v1 status JSON data. | ||
// On error, partial results may be returned. | ||
func SignalsFromTesla(baseSignal vss.Signal, jsonData []byte) ([]vss.Signal, []error) { | ||
var retSignals []vss.Signal | ||
{{ $first := true -}} | ||
{{- $root := . }} | ||
{{- range $idx, $sig := .Signals }} | ||
{{ if eq (len $sig.Conversions) 0 }} {{ continue }} {{ end -}} | ||
{{ if $first -}} | ||
var val any | ||
var ts time.Time | ||
var err error | ||
var errs []error | ||
{{ $first = false }} {{ end }} | ||
|
||
val, ts, err = {{ $sig.GOName }}FromTesla(jsonData) | ||
if err != nil { | ||
if !errors.Is(err, errNotFound) { | ||
errs = append(errs, fmt.Errorf("failed to convert '{{ $sig.GOName }}': %w", err)) | ||
} | ||
} else { | ||
sig := vss.Signal{ | ||
Name: "{{ $sig.JSONName }}", | ||
TokenID: baseSignal.TokenID, | ||
Timestamp: ts, | ||
Source: baseSignal.Source, | ||
} | ||
sig.SetValue(val) | ||
retSignals = append(retSignals, sig) | ||
} | ||
{{- end }} | ||
return retSignals, errs | ||
} | ||
|
||
var zeroTime time.Time | ||
|
||
{{- range $i, $sig := .Signals }} | ||
// {{ $sig.GOName }}FromTesla converts the given JSON data to a {{ $sig.GOType }}. | ||
func {{ $sig.GOName }}FromTesla(jsonData []byte) (ret {{ $sig.GOType }}, ts time.Time, err error) { | ||
var errs error | ||
var result gjson.Result | ||
|
||
{{- range $j, $conv := .Conversions }} | ||
result = gjson.GetBytes(jsonData, "data.{{ $conv.OriginalName }}") | ||
if result.Exists() && result.Value() != nil { | ||
val, ok := result.Value().({{ $conv.OriginalType }}) | ||
if ok { | ||
retVal, err := To{{ $sig.GOName }}{{ $j }}(jsonData, val) | ||
if err == nil { | ||
endpoint, _, _ := strings.Cut("{{ $conv.OriginalName }}", ".") | ||
result := gjson.GetBytes(jsonData, "data." + endpoint + ".timestamp") | ||
|
||
if result.Exists() && result.Value() != nil { | ||
if unix, ok := result.Value().(float64); ok { | ||
ts := time.Unix(int64(unix), 0) | ||
|
||
return retVal, ts, nil | ||
} | ||
} | ||
|
||
errs = errors.Join(errs, fmt.Errorf("couldn't find a timestamp for 'data.{{ $conv.OriginalName }}'")) | ||
} | ||
errs = errors.Join(errs, fmt.Errorf("failed to convert 'data.{{ $conv.OriginalName }}': %w", err)) | ||
} else { | ||
errs = errors.Join(errs, fmt.Errorf("%w, field 'data.{{ $conv.OriginalName }}' is not of type '{{ $conv.OriginalType }}' got '%v' of type '%T'", convert.InvalidTypeError(), result.Value(), result.Value())) | ||
} | ||
} | ||
{{- end }} | ||
|
||
if errs == nil { | ||
return ret, zeroTime, fmt.Errorf("%w '{{ $sig.GOName }}'", errNotFound) | ||
} | ||
|
||
return ret, zeroTime, errs | ||
} | ||
{{- end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# This file defines mappings from Tesla /vehicle_data responses to VSS. See | ||
# https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-endpoints#vehicle-data | ||
|
||
- vspecName: Vehicle.Chassis.Axle.Row1.Wheel.Left.Tire.Pressure | ||
conversions: | ||
- originalName: "vehicle_state.tpms_pressure_fl" # In bars | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Chassis.Axle.Row1.Wheel.Right.Tire.Pressure | ||
conversions: | ||
- originalName: "vehicle_state.tpms_pressure_fr" # In bars | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Chassis.Axle.Row2.Wheel.Left.Tire.Pressure | ||
conversions: | ||
- originalName: "vehicle_state.tpms_pressure_rl" # In bars | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Chassis.Axle.Row2.Wheel.Right.Tire.Pressure | ||
conversions: | ||
- originalName: "vehicle_state.tpms_pressure_rr" # In bars | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.CurrentLocation.Latitude | ||
conversions: | ||
- originalName: drive_state.latitude | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_ALL_TIME_LOCATION | ||
|
||
- vspecName: Vehicle.CurrentLocation.Longitude | ||
conversions: | ||
- originalName: drive_state.longitude | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_ALL_TIME_LOCATION | ||
|
||
- vspecName: Vehicle.Exterior.AirTemperature | ||
conversions: | ||
- originalName: "climate_state.outside_temp" # I believe this is in Celsius. | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.Range | ||
conversions: | ||
- originalName: "charge_state.battery_range" # In miles | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.TractionBattery.Charging.AddedEnergy | ||
conversions: | ||
- originalName: "charge_state.charge_energy_added" # In kilowatt-hours. | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.TractionBattery.Charging.ChargeLimit | ||
conversions: | ||
- originalName: "charge_state.charge_limit_soc" # In percent | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.TractionBattery.Charging.IsCharging | ||
conversions: | ||
- originalName: "charge_state.charging_state" # Observed values: "Disconnected", "NoPower", "Starting", "Charging", "Complete", "Stopped" | ||
originalType: "string" | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.TractionBattery.CurrentPower | ||
conversions: | ||
- originalName: "drive_state.power" # I believe this is in kilowatts. Need to check that this is just charge_state.charger_power but better. | ||
# It's negative when charging, positive when expending energy driving. Note that because of regenerative braking | ||
# this may be negative even while driving. | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.TractionBattery.StateOfCharge.Current | ||
conversions: | ||
- originalName: "charge_state.battery_level" # In percent | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Powertrain.Transmission.TravelledDistance | ||
conversions: | ||
- originalName: "vehicle_state.odometer" # In miles. | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA | ||
|
||
- vspecName: Vehicle.Speed | ||
conversions: | ||
- originalName: drive_state.speed # In miles per hour. | ||
originalType: float64 | ||
requiredPrivileges: | ||
- VEHICLE_NON_LOCATION_DATA |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Package status converts Tesla CloudEvents to ClickHouse-ready slices of signals. | ||
package status | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/DIMO-Network/model-garage/pkg/cloudevent" | ||
"github.com/DIMO-Network/model-garage/pkg/convert" | ||
"github.com/DIMO-Network/model-garage/pkg/tesla" | ||
"github.com/DIMO-Network/model-garage/pkg/vss" | ||
) | ||
|
||
func Decode(msgBytes []byte) ([]vss.Signal, error) { | ||
// Only interested in the top-level CloudEvent fields. | ||
var ce cloudevent.CloudEvent[struct{}] | ||
|
||
if err := json.Unmarshal(msgBytes, &ce); err != nil { | ||
return nil, fmt.Errorf("failed to unmarshal payload: %w", err) | ||
} | ||
|
||
did, err := cloudevent.DecodeNFTDID(ce.Subject) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to decode subject DID: %w", err) | ||
} | ||
|
||
tokenID := did.TokenID | ||
source := ce.Source | ||
|
||
baseSignal := vss.Signal{ | ||
TokenID: tokenID, | ||
Source: source, | ||
} | ||
|
||
sigs, errs := tesla.SignalsFromTesla(baseSignal, msgBytes) | ||
if len(errs) != 0 { | ||
return nil, convert.ConversionError{ | ||
TokenID: tokenID, | ||
Source: source, | ||
DecodedSignals: sigs, | ||
Errors: errs, | ||
} | ||
} | ||
|
||
return sigs, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package status | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/DIMO-Network/model-garage/pkg/vss" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
var baseDoc = []byte(` | ||
{ | ||
"subject": "did:nft:137:0xbA5738a18d83D41847dfFbDC6101d37C69c9B0cF_37", | ||
"source": "0x983110309620D911731Ac0932219af06091b6744", | ||
"data": { | ||
"charge_state": { | ||
"battery_level": 23, | ||
"battery_range": 341, | ||
"charge_energy_added": 42, | ||
"charge_limit_soc": 80, | ||
"charging_state": "Charging", | ||
"timestamp": 1730728800 | ||
}, | ||
"climate_state": { | ||
"outside_temp": 19, | ||
"timestamp": 1730728802 | ||
}, | ||
"drive_state": { | ||
"latitude": 38.89, | ||
"longitude": 77.03, | ||
"power": -7, | ||
"speed": 25, | ||
"timestamp": 1730738800 | ||
}, | ||
"vehicle_state": { | ||
"odometer": 5633, | ||
"tpms_pressure_fl": 3.12, | ||
"tpms_pressure_fr": 3.09, | ||
"tpms_pressure_rl": 2.98, | ||
"tpms_pressure_rr": 2.99, | ||
"timestamp": 1730728805 | ||
} | ||
} } | ||
`) | ||
|
||
const teslaConnection = "0x983110309620D911731Ac0932219af06091b6744" | ||
|
||
var expSignals = []vss.Signal{ | ||
{TokenID: 37, Timestamp: time.Unix(1730728805, 0), Name: "chassisAxleRow1WheelLeftTirePressure", ValueNumber: 312, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728805, 0), Name: "chassisAxleRow1WheelRightTirePressure", ValueNumber: 309, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728805, 0), Name: "chassisAxleRow2WheelLeftTirePressure", ValueNumber: 298, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728805, 0), Name: "chassisAxleRow2WheelRightTirePressure", ValueNumber: 299, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730738800, 0), Name: "currentLocationLatitude", ValueNumber: 38.89, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730738800, 0), Name: "currentLocationLongitude", ValueNumber: 77.03, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728802, 0), Name: "exteriorAirTemperature", ValueNumber: 19, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728800, 0), Name: "powertrainRange", ValueNumber: 548.7863040000001, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728800, 0), Name: "powertrainTractionBatteryChargingAddedEnergy", ValueNumber: 42, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728800, 0), Name: "powertrainTractionBatteryChargingChargeLimit", ValueNumber: 80, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728800, 0), Name: "powertrainTractionBatteryChargingIsCharging", ValueNumber: 1, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730738800, 0), Name: "powertrainTractionBatteryCurrentPower", ValueNumber: 7000, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728800, 0), Name: "powertrainTractionBatteryStateOfChargeCurrent", ValueNumber: 23, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730728805, 0), Name: "powertrainTransmissionTravelledDistance", ValueNumber: 9065.434752000001, Source: teslaConnection}, | ||
{TokenID: 37, Timestamp: time.Unix(1730738800, 0), Name: "speed", ValueNumber: 40.2336, Source: teslaConnection}, | ||
} | ||
|
||
func TestSignalsFromTesla(t *testing.T) { | ||
computedSignals, err := Decode(baseDoc) | ||
require.Empty(t, err, "Expected no errors.") | ||
assert.ElementsMatch(t, computedSignals, expSignals) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Let's do that.