Skip to content
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

Publish extra active_route fields to mqtt output #3789

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 112 additions & 44 deletions lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do

alias TeslaMate.Mqtt.Publisher
alias TeslaMate.Vehicles.Vehicle.Summary
alias TeslaMate.Locations.GeoFence
alias TeslaMate.Vehicles

defstruct [:car_id, :last_summary, :deps, :namespace]
defstruct [:car_id, :last_values, :deps, :namespace]
alias __MODULE__, as: State

def child_spec(arg) do
Expand Down Expand Up @@ -38,28 +37,33 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
{:ok, %State{car_id: car_id, namespace: namespace, deps: deps}}
end

@impl true
def handle_info(summary, %State{last_summary: summary} = state) do
{:noreply, state}
end

@always_published ~w(charge_energy_added charger_actual_current charger_phases
charger_power charger_voltage scheduled_charging_start_time
time_to_full_charge shift_state geofence trim_badging)a

@impl true
def handle_info(%Summary{} = summary, state) do
summary
|> Map.from_struct()
|> Map.drop([:car])
values =
%{}
|> add_simple_values(summary)
|> add_car_latitude_longitude(summary)
|> add_geofence(summary)
|> add_active_route(summary)

publish_values(values, state)
{:noreply, %State{state | last_values: values}}
end

defp publish_values(values, %State{last_values: values}) do
nil
end

defp publish_values(values, state) do
values
|> Stream.reject(&match?({_key, :unknown}, &1))
|> Stream.filter(fn {key, value} ->
(key in @always_published or value != nil) and
(state.last_summary == nil or Map.get(state.last_summary, key) != value)
end)
|> Stream.map(fn
{key = :geofence, %GeoFence{name: name}} -> {key, name}
{key = :geofence, nil} -> {key, Application.get_env(:teslamate, :default_geofence)}
{key, val} -> {key, val}
(state.last_values == nil or Map.get(state.last_values, key) != value)
end)
|> Task.async_stream(&publish(&1, state),
max_concurrency: 10,
Expand All @@ -73,38 +77,102 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
_ok ->
nil
end)
end

@simple_values ~w(
display_name state since healthy latitude longitude heading battery_level charging_state usable_battery_level
ideal_battery_range_km est_battery_range_km rated_battery_range_km charge_energy_added
speed outside_temp inside_temp is_climate_on is_preconditioning locked sentry_mode
plugged_in scheduled_charging_start_time charge_limit_soc charger_power windows_open doors_open
odometer shift_state charge_port_door_open time_to_full_charge charger_phases
charger_actual_current charger_voltage version update_available update_version is_user_present
model trim_badging exterior_color wheel_type spoiler_type trunk_open frunk_open elevation power
charge_current_request charge_current_request_max tpms_pressure_fl tpms_pressure_fr tpms_pressure_rl tpms_pressure_rr
tpms_soft_warning_fl tpms_soft_warning_fr tpms_soft_warning_rl tpms_soft_warning_rr climate_keeper_mode
)a

defp add_simple_values(map, %Summary{} = summary) do
Map.merge(map, Map.take(summary, @simple_values))
end

if state.last_summary == nil or
state.last_summary.latitude != summary.latitude or
state.last_summary.longitude != summary.longitude do
lat_lng =
case {summary.latitude, summary.longitude} do
{nil, _} -> nil
{_, nil} -> nil
{%Decimal{} = lat, %Decimal{} = lon} -> {Decimal.to_float(lat), Decimal.to_float(lon)}
{lat, lon} -> {lat, lon}
end

case lat_lng do
nil ->
nil

{lat, lon} ->
location =
%{
latitude: lat,
longitude: lon
}
|> Jason.encode!()

case publish({"location", location}, state) do
:ok -> nil
{:error, reason} -> Logger.warning("Failed to publish location: #{inspect(reason)}")
end
defp add_car_latitude_longitude(map, %Summary{} = summary) do
lat_lng =
case {summary.latitude, summary.longitude} do
{nil, _} -> nil
{_, nil} -> nil
{%Decimal{} = lat, %Decimal{} = lon} -> {Decimal.to_float(lat), Decimal.to_float(lon)}
{lat, lon} -> {lat, lon}
end

case lat_lng do
nil ->
map

{lat, lon} ->
location =
%{
latitude: lat,
longitude: lon
}
|> Jason.encode!()

Map.put(map, :location, location)
end
end

defp add_geofence(map, %Summary{} = summary) do
case summary.geofence do
nil ->
Map.put(map, :geofence, Application.get_env(:teslamate, :default_geofence))

geofence ->
Map.put(map, :geofence, geofence.name)
end
end

defp add_active_route(map, %Summary{active_route_destination: nil}) do
error =
%{
error: "No active route available"
}
|> Jason.encode!()

Map.merge(
map,
%{
active_route_destination: "nil",
active_route_latitude: "nil",
active_route_longitude: "nil",
active_route: error
}
)
end

{:noreply, %State{state | last_summary: summary}}
defp add_active_route(map, %Summary{} = summary) do
location =
%{
latitude: summary.active_route_latitude,
longitude: summary.active_route_longitude
}

active_route =
%{
destination: summary.active_route_destination,
energy_at_arrival: summary.active_route_energy_at_arrival,
miles_to_arrival: summary.active_route_miles_to_arrival,
minutes_to_arrival: summary.active_route_minutes_to_arrival,
traffic_minutes_delay: summary.active_route_traffic_minutes_delay,
location: location,
error: nil
}
|> Jason.encode!()

Map.merge(map, %{
active_route_destination: summary.active_route_destination,
active_route_latitude: summary.active_route_latitude,
active_route_longitude: summary.active_route_longitude,
active_route: active_route
})
end

defp publish({key, value}, %State{car_id: car_id, namespace: namespace, deps: deps}) do
Expand Down
11 changes: 10 additions & 1 deletion lib/teslamate/vehicles/vehicle/summary.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ defmodule TeslaMate.Vehicles.Vehicle.Summary do
model trim_badging exterior_color wheel_type spoiler_type trunk_open frunk_open elevation power
charge_current_request charge_current_request_max tpms_pressure_fl tpms_pressure_fr tpms_pressure_rl tpms_pressure_rr
tpms_soft_warning_fl tpms_soft_warning_fr tpms_soft_warning_rl tpms_soft_warning_rr climate_keeper_mode
active_route_destination active_route_latitude active_route_longitude
active_route_destination active_route_latitude active_route_longitude active_route_energy_at_arrival
active_route_miles_to_arrival active_route_minutes_to_arrival active_route_traffic_minutes_delay
)a

def into(nil, %{state: :start, healthy?: healthy?, car: car}) do
Expand Down Expand Up @@ -79,6 +80,14 @@ defmodule TeslaMate.Vehicles.Vehicle.Summary do
active_route_destination: get_in_struct(vehicle, [:drive_state, :active_route_destination]),
active_route_latitude: get_in_struct(vehicle, [:drive_state, :active_route_latitude]),
active_route_longitude: get_in_struct(vehicle, [:drive_state, :active_route_longitude]),
active_route_energy_at_arrival:
get_in_struct(vehicle, [:drive_state, :active_route_energy_at_arrival]),
active_route_miles_to_arrival:
get_in_struct(vehicle, [:drive_state, :active_route_miles_to_arrival]),
active_route_minutes_to_arrival:
get_in_struct(vehicle, [:drive_state, :active_route_minutes_to_arrival]),
active_route_traffic_minutes_delay:
get_in_struct(vehicle, [:drive_state, :active_route_traffic_minutes_delay]),
latitude: get_in_struct(vehicle, [:drive_state, :latitude]),
longitude: get_in_struct(vehicle, [:drive_state, :longitude]),
power: get_in_struct(vehicle, [:drive_state, :power]),
Expand Down
57 changes: 57 additions & 0 deletions test/teslamate/mqtt/pubsub/vehicle_subscriber_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriberTest do
"longitude" => 41.129182
}

# Published as nil
for key <- [
:active_route_destination,
:active_route_longitude,
:active_route_latitude
] do
topic = "teslamate/cars/0/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, "nil", [retain: true, qos: 1]}}
end

# Published as nil
for key <- [
:active_route
] do
topic = "teslamate/cars/0/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, data, [retain: true, qos: 1]}}
assert Jason.decode!(data) == %{"error" => "No active route available"}
end

refute_receive _
end

Expand Down Expand Up @@ -155,6 +174,25 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriberTest do
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/trim_badging", "", [retain: true, qos: 1]}}

# Published as nil
for key <- [
:active_route_destination,
:active_route_longitude,
:active_route_latitude
] do
topic = "teslamate/cars/0/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, "nil", [retain: true, qos: 1]}}
end

# Published as nil
for key <- [
:active_route
] do
topic = "teslamate/cars/0/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, data, [retain: true, qos: 1]}}
assert Jason.decode!(data) == %{"error" => "No active route available"}
end

refute_receive _
end

Expand Down Expand Up @@ -219,6 +257,25 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriberTest do
assert_receive {MqttPublisherMock, {:publish, ^topic, "", [retain: true, qos: 1]}}
end

# Published as nil
for key <- [
:active_route_destination,
:active_route_longitude,
:active_route_latitude
] do
topic = "teslamate/account_0/cars/0/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, "nil", [retain: true, qos: 1]}}
end

# Published as nil
for key <- [
:active_route
] do
topic = "teslamate/account_0/cars/0/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, data, [retain: true, qos: 1]}}
assert Jason.decode!(data) == %{"error" => "No active route available"}
end

refute_receive _
end
end
19 changes: 19 additions & 0 deletions test/teslamate/vehicles/vehicle_sync_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,25 @@ defmodule TeslaMate.Vehicles.VehicleSyncTest do
"longitude" => 41.128817
}

# Published as nil
for key <- [
:active_route_destination,
:active_route_longitude,
:active_route_latitude
] do
topic = "teslamate/cars/#{car.id}/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, "nil", [retain: true, qos: 1]}}
end

# Published as nil
for key <- [
:active_route
] do
topic = "teslamate/cars/#{car.id}/#{key}"
assert_receive {MqttPublisherMock, {:publish, ^topic, data, [retain: true, qos: 1]}}
assert Jason.decode!(data) == %{"error" => "No active route available"}
end

refute_receive _
end
end
Expand Down
Loading
Loading