From f9b3e3fa5d94a21e2cfc5912a1afb65cf779491f Mon Sep 17 00:00:00 2001 From: Adrian Kumpf Date: Wed, 23 Oct 2019 21:19:02 +0200 Subject: [PATCH] Display more icons in the UI --- CHANGELOG.md | 2 + docs/integrations/mqtt.md | 13 +++--- .../mqtt/pubsub/vehicle_subscriber.ex | 16 +++---- lib/teslamate/vehicles/vehicle/summary.ex | 44 +++++-------------- .../templates/car/summary.html.leex | 12 ++++- .../mqtt/pubsub/vehicle_subscriber_test.exs | 6 ++- .../controllers/car_controller_test.exs | 35 ++++++++++++--- 7 files changed, 72 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54dbcf51..fa649f5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ If you have feedback or can even verify/compare the calculated value with a real - `teslamate/cars/$car_id/update_available`: Indicates if a car software update is available - `teslamate/cars/$car_id/is_climate_on`: Indicates if the climate control is on +- `teslamate/cars/$car_id/is_preconditioning`: Indicates if the vehicle is being preconditioned +- `teslamate/cars/$car_id/is_user_present`: Indicates if a user is present in the vehicle ### Enhancements diff --git a/docs/integrations/mqtt.md b/docs/integrations/mqtt.md index afcfa557..0c353cc6 100644 --- a/docs/integrations/mqtt.md +++ b/docs/integrations/mqtt.md @@ -12,28 +12,31 @@ Vehicle data will be published to the following topics (`$car_id` usually starts | ------------------------------------------------------ | -------------------- | ------------------------------------------------------------ | | `teslamate/cars/$car_id/display_name` | Blue Thunder | Vehicle Name | | `teslamate/cars/$car_id/state` | asleep | Status of the vehicle (e.g. `onlilne`, `asleep`, `charging`) | +| `teslamate/cars/$car_id/since` | 2019-02-29T23:00:07Z | Date of the last status change | | `teslamate/cars/$car_id/healthy` | true | Health status of the logger for that vehicle | | `teslamate/cars/$car_id/version` | 2019.32.12.2 58f3b76 | Software Version | | `teslamate/cars/$car_id/update_available` | false | Indicates if a software update is available | -| | | | +| ------------------------------------------------------ | -------------------- | ------------------------------------------------------------ | | `teslamate/cars/$car_id/latitude` | 35.278131 | Last reported car latitude | | `teslamate/cars/$car_id/longitude` | 29.744801 | Last reported car longitude | | `teslamate/cars/$car_id/shift_state` | D | Current/Last Shift State (D/N/R/P) | | `teslamate/cars/$car_id/speed` | 12 | Current Speed in km/h | -| | | | +| ------------------------------------------------------ | -------------------- | ------------------------------------------------------------ | | `teslamate/cars/$car_id/locked` | true | Indicates if the car is locked | | `teslamate/cars/$car_id/sentry_mode` | false | Indicates if Sentry Mode is active | | `teslamate/cars/$car_id/windows_open` | false | Indicates if any of the windows are open | -| | | | +| `teslamate/cars/$car_id/is_user_present` | false | Indicates if a user is present in the vehicle | +| ------------------------------------------------------ | -------------------- | ------------------------------------------------------------ | | `teslamate/cars/$car_id/is_climate_on` | true | Indicates if the climate control is on | | `teslamate/cars/$car_id/inside_temp` | 20.8 | Inside Temperature in °C | | `teslamate/cars/$car_id/outside_temp` | 18.4 | Temperature in °C | -| | | | +| `teslamate/cars/$car_id/is_preconditioning` | false | Indicates if the vehicle is being preconditioned | +| ------------------------------------------------------ | -------------------- | ------------------------------------------------------------ | | `teslamate/cars/$car_id/odometer` | 1653 | Car odometer in km | | `teslamate/cars/$car_id/est_battery_range_km` | 372.5 | Estimated Range in km | | `teslamate/cars/$car_id/rated_battery_range_km` | 401.63 | Rated Range in km | | `teslamate/cars/$car_id/ideal_battery_range_km` | 335.79 | Ideal Range in km | -| | | | +| ------------------------------------------------------ | -------------------- | ------------------------------------------------------------ | | `teslamate/cars/$car_id/battery_level` | 88 | Battery Level Percentage | | `teslamate/cars/$car_id/plugged_in` | true | If car is currently plugged into a charger | | `teslamate/cars/$car_id/charge_energy_added` | 5.06 | Last added energy in kW | diff --git a/lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex b/lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex index 522a2c8d..6e7a93f5 100644 --- a/lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex +++ b/lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex @@ -40,20 +40,16 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do {:noreply, state} end + @blacklist [:car] + @always_published ~w(charge_energy_added charger_actual_current charger_phases + charger_power charger_voltage scheduled_charging_start_time + time_to_full_charge)a + def handle_info(summary, state) do summary |> Map.from_struct() |> Stream.filter(fn {key, value} -> - not is_nil(value) or - key in [ - :charge_energy_added, - :charger_actual_current, - :charger_phases, - :charger_power, - :charger_voltage, - :scheduled_charging_start_time, - :time_to_full_charge - ] + not (key in @blacklist) and (not is_nil(value) or key in @always_published) end) |> Task.async_stream(&publish(&1, state), max_concurrency: 10, diff --git a/lib/teslamate/vehicles/vehicle/summary.ex b/lib/teslamate/vehicles/vehicle/summary.ex index 506db9b7..aca5d011 100644 --- a/lib/teslamate/vehicles/vehicle/summary.ex +++ b/lib/teslamate/vehicles/vehicle/summary.ex @@ -4,40 +4,14 @@ defmodule TeslaMate.Vehicles.Vehicle.Summary do alias TeslaApi.Vehicle.State.{Drive, Charge, VehicleState} alias TeslaApi.Vehicle - defstruct [ - :car, - :display_name, - :state, - :since, - :healthy, - :latitude, - :longitude, - :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, - :locked, - :sentry_mode, - :plugged_in, - :scheduled_charging_start_time, - :charge_limit_soc, - :charger_power, - :windows_open, - :odometer, - :shift_state, - :charge_port_door_open, - :time_to_full_charge, - :charger_phases, - :charger_actual_current, - :charger_voltage, - :version, - :update_available - ] + defstruct ~w( + car display_name state since healthy latitude longitude 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 + odometer shift_state charge_port_door_open time_to_full_charge charger_phases + charger_actual_current charger_voltage version update_available is_user_present + )a def into(nil, %{state: :start, healthy?: healthy?, car: car}) do %__MODULE__{state: :unavailable, healthy: healthy?, car: car} @@ -90,6 +64,7 @@ defmodule TeslaMate.Vehicles.Vehicle.Summary do # Climate State is_climate_on: get_in_struct(vehicle, [:climate_state, :is_climate_on]), + is_preconditioning: get_in_struct(vehicle, [:climate_state, :is_preconditioning]), outside_temp: get_in_struct(vehicle, [:climate_state, :outside_temp]), inside_temp: get_in_struct(vehicle, [:climate_state, :inside_temp]), @@ -98,6 +73,7 @@ defmodule TeslaMate.Vehicles.Vehicle.Summary do locked: get_in_struct(vehicle, [:vehicle_state, :locked]), sentry_mode: get_in_struct(vehicle, [:vehicle_state, :sentry_mode]), windows_open: window_open(vehicle), + is_user_present: get_in_struct(vehicle, [:vehicle_state, :is_user_present]), version: get_in_struct(vehicle, [:vehicle_state, :car_version]), update_available: update_available(vehicle) } diff --git a/lib/teslamate_web/templates/car/summary.html.leex b/lib/teslamate_web/templates/car/summary.html.leex index 419f137f..f1954fa6 100644 --- a/lib/teslamate_web/templates/car/summary.html.leex +++ b/lib/teslamate_web/templates/car/summary.html.leex @@ -25,6 +25,16 @@ <% end %>
+ <%= if @summary.is_preconditioning do %> + + + + <% end %> + <%= if @summary.state != :driving and @summary.is_user_present do %> + + + + <% end %> <%= if @summary.plugged_in do %> @@ -203,7 +213,7 @@ not is_nil(@error) -> link @error, to: "#", class: "button is-danger is-small is-outlined is-fullwidth", disabled: true - @summary.state == :online and !@summary.sentry_mode and @summary.locked-> + @summary.state == :online and !@summary.sentry_mode and !@summary.is_user_present and !@summary.is_preconditioning and @summary.locked-> link gettext("try to sleep"), to: "#", phx_click: "suspend_logging", class: "button is-info is-small is-outlined is-fullwidth" <> (if @loading, do: " is-loading", else: "") diff --git a/test/teslamate/mqtt/pubsub/vehicle_subscriber_test.exs b/test/teslamate/mqtt/pubsub/vehicle_subscriber_test.exs index ce5917a8..2c739a5b 100644 --- a/test/teslamate/mqtt/pubsub/vehicle_subscriber_test.exs +++ b/test/teslamate/mqtt/pubsub/vehicle_subscriber_test.exs @@ -28,6 +28,7 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriberTest do assert_receive {VehiclesMock, {:subscribe, 0}} summary = %Summary{ + healthy: true, display_name: "Foo", odometer: 42_000, windows_open: true, @@ -43,7 +44,10 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriberTest do sentry_mode: false, plugged_in: false, version: "2019.42", - update_available: false + update_available: false, + is_preconditioning: true, + is_user_present: false, + is_climate_on: true } send(pid, summary) diff --git a/test/teslamate_web/controllers/car_controller_test.exs b/test/teslamate_web/controllers/car_controller_test.exs index a019318a..0549cf92 100644 --- a/test/teslamate_web/controllers/car_controller_test.exs +++ b/test/teslamate_web/controllers/car_controller_test.exs @@ -27,9 +27,31 @@ defmodule TeslaMateWeb.CarControllerTest do assert redirected_to(conn, 302) == Routes.live_path(conn, TeslaMateWeb.SignInLive.Index) end - # @tag :signed_in - # test "lists all cares", %{conn: conn} do - # end + @tag :signed_in + test "lists all active vehicles", %{conn: conn} do + {:ok, _pid} = + start_supervised( + {ApiMock, name: :api_vehicle, events: [{:ok, online_event()}], pid: self()} + ) + + {:ok, _pid} = + start_supervised( + {TeslaMate.Vehicles, + vehicle: VehicleMock, + vehicles: [ + %TeslaApi.Vehicle{display_name: "f0o", id: 4241, vehicle_id: 11111, vin: "1221"}, + %TeslaApi.Vehicle{display_name: "fo0", id: 1242, vehicle_id: 22222, vin: "2112"} + ]} + ) + + conn = get(conn, Routes.car_path(conn, :index)) + html = response(conn, 200) + + assert [ + {"div", [{"class", "car card"}], _}, + {"div", [{"class", "car card"}], _} + ] = Floki.find(html, ".car") + end @tag :signed_in test "renders last knwon vehicle stats", %{conn: conn} do @@ -82,7 +104,7 @@ defmodule TeslaMateWeb.CarControllerTest do battery_range: 175, battery_level: 69 }, - climate_state: %{is_preconditioning: false, outside_temp: 24, inside_temp: 23.2}, + climate_state: %{is_preconditioning: true, outside_temp: 24, inside_temp: 23.2}, vehicle_state: %{ software_update: %{status: "available"}, locked: true, @@ -90,7 +112,8 @@ defmodule TeslaMateWeb.CarControllerTest do fd_window: 1, fp_window: 0, rd_window: 0, - rp_window: 0 + rp_window: 0, + is_user_present: true }, vehicle_config: %{car_type: "models2", trim_badging: "p90d"} )} @@ -110,6 +133,8 @@ defmodule TeslaMateWeb.CarControllerTest do assert table_row(html, "Range (est.)", "289.68 km") assert table_row(html, "State of Charge", "69%") assert icon(html, "Locked", "lock") + assert icon(html, "User present", "account") + assert icon(html, "Preconditioning", "air-conditioner") assert icon(html, "Sentry Mode", "shield-check") assert icon(html, "Windows open", "window-open") assert icon(html, "Software Update available", "gift-outline")