diff --git a/lib/tesla_mate/api.ex b/lib/tesla_mate/api.ex index 3b5a18dd..6c1aade0 100644 --- a/lib/tesla_mate/api.ex +++ b/lib/tesla_mate/api.ex @@ -117,12 +117,23 @@ defmodule TeslaMate.Api do end def get_vehicle_with_state(_id) do + # {:error, :unavailable} + {:ok, %Vehicle{ state: "online", - charge_state: %Vehicle.State.Charge{}, + charge_state: %Vehicle.State.Charge{ + # timestamp: DateTime.utc_now() |> DateTime.to_unix(:microsecond), + # charging_state: "Unplugged", + # charger_power: 22, + # battery_level: 16, + # charge_energy_added: 0.5, + # ideal_battery_range: 59.95 + }, drive_state: %Vehicle.State.Drive{ timestamp: DateTime.utc_now() |> DateTime.to_unix(:microsecond), + # shift_state: "N", + # speed: 50, latitude: 0.0, longitude: 0.0 }, @@ -133,9 +144,9 @@ defmodule TeslaMate.Api do def list_vehicles do m3 = %Vehicle{ - id: 0, + id: 1000, state: "online", - vehicle_id: 0, + vehicle_id: 1010, display_name: "Tesla!M3", option_codes: ["MDL3", "BT37", "DV4W"] } @@ -144,7 +155,7 @@ defmodule TeslaMate.Api do end @impl true - def init(opts) do + def init(_opts) do {:ok, nil} end end diff --git a/lib/tesla_mate/log.ex b/lib/tesla_mate/log.ex index ba73e8b7..537675d2 100644 --- a/lib/tesla_mate/log.ex +++ b/lib/tesla_mate/log.ex @@ -6,245 +6,175 @@ defmodule TeslaMate.Log do import Ecto.Query, warn: false alias TeslaMate.Repo - alias TeslaMate.Log.Position + ## Car - def insert_position(attrs) do - with {:ok, _} <- %Position{} |> Position.changeset(attrs) |> Repo.insert() do - :ok - end + alias TeslaMate.Log.Car + + def list_cars do + Repo.all(Car) end - def get_last_position_id! do - Position - |> select([p], max(p.id)) - |> Repo.one!() + def get_car!(id) do + Repo.get!(Car, id) end + def get_car_by_eid(eid) do + Repo.get_by(Car, eid: eid) + end + + def create_car(%{eid: eid, vid: vid} = attrs) do + %Car{eid: eid, vid: vid} + |> Car.changeset(attrs) + |> Repo.insert() + end + + def update_car(%Car{} = car, attrs) do + car + |> Car.changeset(attrs) + |> Repo.update() + end + + ## State + alias TeslaMate.Log.State - def start_state(state) do - case get_current_state() do - %{state: ^state} -> + def start_state(car_id, state) do + case get_current_state(car_id) do + %State{state: ^state} -> :ok - %{state: _state} -> - last_position_id = get_last_position_id!() - - attrs = %{ - state: state, - start_date: DateTime.utc_now(), - start_position_id: last_position_id - } - - with :ok <- close_state(last_position_id), - {:ok, _} <- create_state(attrs) do + %State{} = s -> + with {:ok, _} <- s |> State.changeset(%{end_date: DateTime.utc_now()}) |> Repo.update(), + {:ok, _} <- create_state(car_id, %{state: state, start_date: DateTime.utc_now()}) do :ok end nil -> - last_position_id = get_last_position_id!() - - attrs = %{ - state: state, - start_date: DateTime.utc_now(), - start_position_id: last_position_id - } - - with {:ok, _} <- create_state(attrs) do + with {:ok, _} <- create_state(car_id, %{state: state, start_date: DateTime.utc_now()}) do :ok end end end - def close_state(position_id) do - end_date = DateTime.utc_now() - - result = - State - |> where([s], is_nil(s.end_date)) - |> update(set: [end_date: ^end_date, end_position_id: ^position_id]) - |> Repo.update_all([]) - - case result do - {0, nil} -> {:erorr, :no_state_to_be_closed} - {1, nil} -> :ok - {n, nil} -> {:error, {:closed_multiple_states, n}} - end - end - - def get_current_state do + defp get_current_state(car_id) do State - |> select([:state]) - |> where([s], is_nil(s.end_date)) + |> where([s], ^car_id == s.car_id and is_nil(s.end_date)) |> Repo.one() end - defp create_state(attrs \\ %{}) do - %State{} + defp create_state(car_id, attrs) do + %State{car_id: car_id} |> State.changeset(attrs) |> Repo.insert() end - defp update_state(%State{} = state, attrs) do - state - |> State.changeset(attrs) - |> Repo.update() - end + ## Position - alias TeslaMate.Log.DriveState + alias TeslaMate.Log.Position - def start_drive_state do - last_position_id = get_last_position_id!() - attrs = %{start_date: DateTime.utc_now(), start_position_id: last_position_id} - - with {:ok, _} <- create_drive_state(attrs) do + def insert_position(car_id, attrs) do + with {:ok, _} <- + %Position{car_id: car_id, trip_id: Map.get(attrs, :trip_id)} + |> Position.changeset(attrs) + |> Repo.insert() do :ok end end - def close_drive_state do - start_position_id = - case get_last_drive_state() do - %{start_position_id: start_position_id} -> start_position_id - # Assumption there is always one position with id 1 - nil -> 1 - end + ## Trip - end_date = DateTime.utc_now() - last_position_id = get_last_position_id!() + alias TeslaMate.Log.Trip - result = - DriveState - |> where([s], is_nil(s.end_date)) - |> update(set: [end_date: ^end_date, end_position_id: ^last_position_id]) - |> Repo.update_all([]) - |> case do - {0, nil} -> {:erorr, :no_drive_state_to_be_closed} - {1, nil} -> :ok - {n, nil} -> {:error, {:closed_multiple_states, n}} - end - - with :ok <- result do - if start_position_id != 1 do - :ok = update_drive_statistics(start_position_id, last_position_id) - end - - :ok + def start_trip(car_id) do + with {:ok, %Trip{id: id}} <- + %Trip{car_id: car_id} + |> Trip.changeset(%{start_date: DateTime.utc_now()}) + |> Repo.insert() do + {:ok, id} end end - def get_last_drive_state do - DriveState - |> select([:start_position_id]) - |> where([d], is_nil(d.end_date)) - |> Repo.one() + def close_trip(trip_id) do + # TODO statistics + attrs = %{} + + with {:ok, _trip} <- + Trip + |> Repo.get!(trip_id) + |> Trip.changeset(attrs) + |> Repo.update() do + :ok + end + + # statistics = + # Position + # |> select([p], %{ + # outside_temp_avg: fragment("?::float", avg(p.outside_temp)), + # speed_max: max(p.speed), + # speed_min: min(p.speed), + # power_max: max(p.power), + # power_min: min(p.power), + # power_avg: fragment("?::float", avg(p.power)) + # }) + # |> where( + # [p], + # car_id == p.car_id and ^start_position_id <= p.id and p.id <= ^end_position_id + # ) + # |> Repo.one!() + # |> Map.to_list() + + # result = + # Trip + # |> where( + # [d], + # car_id == d.car_id and d.start_position_id == ^start_position_id and + # d.end_position_id == ^end_position_id + # ) + # |> update(set: ^statistics) + # |> Repo.update_all([]) + + # case result do + # {0, nil} -> {:erorr, :no_trips_to_be_updated} + # {1, nil} -> :ok + # end end - defp update_drive_statistics(start_position_id, end_position_id) do - statistics = + alias TeslaMate.Log.{ChargingProcess, Charge} + + def start_charging_process(car_id) do + # TODO remove when combined with other actions + last_position_id = Position - |> select([p], %{ - outside_temp_avg: fragment("?::float", avg(p.outside_temp)), - speed_max: max(p.speed), - speed_min: min(p.speed), - power_max: max(p.power), - power_min: min(p.power), - power_avg: fragment("?::float", avg(p.power)) - }) - |> where([p], ^start_position_id <= p.id and p.id <= ^end_position_id) + |> select([p], max(p.id)) + |> where(car_id: ^car_id) |> Repo.one!() - |> Map.to_list() - result = - DriveState - |> where( - [d], - d.start_position_id == ^start_position_id and d.end_position_id == ^end_position_id - ) - |> update(set: ^statistics) - |> Repo.update_all([]) - - case result do - {0, nil} -> {:erorr, :no_drive_states_to_be_updated} - {1, nil} -> :ok + with {:ok, %ChargingProcess{id: id}} <- + %ChargingProcess{car_id: car_id, position_id: last_position_id} + |> ChargingProcess.changeset(%{start_date: DateTime.utc_now()}) + |> Repo.insert() do + {:ok, id} end end - defp create_drive_state(attrs \\ %{}) do - %DriveState{} - |> DriveState.changeset(attrs) - |> Repo.insert() - end - - defp update_drive_state(%DriveState{} = drive_state, attrs) do - drive_state - |> DriveState.changeset(attrs) - |> Repo.update() - end - - alias TeslaMate.Log.{ChargingState, Charge} - - def insert_charge(attrs) do - with {:ok, _} <- %Charge{} |> Charge.changeset(attrs) |> Repo.insert() do + def insert_charge(process_id, attrs) do + with {:ok, _} <- + %Charge{charging_process_id: process_id} + |> Charge.changeset(attrs) + |> Repo.insert() do :ok end end - def get_last_charge_id! do - Charge - |> select([p], max(p.id)) - |> Repo.one!() - end + def close_charging_process(process_id) do + # TODO calculate statistics - def start_charging_state do - # TODO combine with insert_charge && insert_position - - last_position_id = get_last_position_id!() - last_charge_id = get_last_charge_id!() - - attrs = %{ - start_date: DateTime.utc_now(), - position: last_position_id, - charge_start_id: last_charge_id - } - - with {:ok, _} <- create_charging_state(attrs) do + with {:ok, _} <- + ChargingProcess + |> Repo.get!(process_id) + |> ChargingProcess.changeset(%{end_date: DateTime.utc_now()}) + |> Repo.update() do :ok end end - - def close_charging_state do - last_charge_id = get_last_charge_id!() - end_date = DateTime.utc_now() - - result = - ChargingState - |> where([c], is_nil(c.end_date)) - |> update(set: [end_date: ^end_date, charge_end_id: ^last_charge_id]) - |> Repo.update_all([]) - - case result do - {0, nil} -> {:erorr, :no_charging_state_to_be_closed} - {1, nil} -> :ok - {n, nil} -> {:error, {:closed_multiple_charging_states, n}} - end - end - - defp create_charging_state(attrs \\ %{}) do - %ChargingState{} - |> ChargingState.changeset(attrs) - |> Repo.insert() - end - - defp update_charging_state(%ChargingState{} = charging_state, attrs) do - charging_state - |> ChargingState.changeset(attrs) - |> Repo.update() - end - - defp update_charge(%Charge{} = charge, attrs) do - charge - |> Charge.changeset(attrs) - |> Repo.update() - end end diff --git a/lib/tesla_mate/log/car.ex b/lib/tesla_mate/log/car.ex new file mode 100644 index 00000000..4484cbc8 --- /dev/null +++ b/lib/tesla_mate/log/car.ex @@ -0,0 +1,28 @@ +defmodule TeslaMate.Log.Car do + use Ecto.Schema + import Ecto.Changeset + + alias TeslaMate.Log.{ChargingProcess, Position, Trip} + + schema "cars" do + field :efficiency, :float + field :eid, :integer + field :model, :string + field :vid, :integer + + has_many :charging_processes, ChargingProcess + has_many :positions, Position + has_many :trips, Trip + + timestamps() + end + + @doc false + def changeset(car, attrs) do + car + |> cast(attrs, [:model, :efficiency]) + |> validate_required([:eid, :vid, :model, :efficiency]) + |> unique_constraint(:eid) + |> unique_constraint(:vid) + end +end diff --git a/lib/tesla_mate/log/charge.ex b/lib/tesla_mate/log/charge.ex index 302bbd58..d36f2095 100644 --- a/lib/tesla_mate/log/charge.ex +++ b/lib/tesla_mate/log/charge.ex @@ -2,38 +2,44 @@ defmodule TeslaMate.Log.Charge do use Ecto.Schema import Ecto.Changeset + alias TeslaMate.Log.ChargingProcess + schema "charges" do - field :battery_level, :float + field :battery_level, :integer field :charge_energy_added, :float field :charger_actual_current, :integer field :charger_phases, :integer, default: 1 field :charger_power, :float field :charger_voltage, :integer field :date, :utc_datetime - field :ideal_battery_range, :float + field :ideal_battery_range_km, :float field :outside_temp, :float + + belongs_to :charging_process, ChargingProcess end @doc false def changeset(charge, attrs) do charge |> cast(attrs, [ + :date, :battery_level, :charge_energy_added, :charger_power, - :date, - :ideal_battery_range, + :ideal_battery_range_km, :charger_voltage, :charger_phases, :charger_actual_current, :outside_temp ]) |> validate_required([ + :date, + :charging_process_id, :battery_level, :charge_energy_added, :charger_power, - :date, - :ideal_battery_range + :ideal_battery_range_km ]) + |> foreign_key_constraint(:charging_process_id) end end diff --git a/lib/tesla_mate/log/charging_process.ex b/lib/tesla_mate/log/charging_process.ex new file mode 100644 index 00000000..2a87bad4 --- /dev/null +++ b/lib/tesla_mate/log/charging_process.ex @@ -0,0 +1,34 @@ +defmodule TeslaMate.Log.ChargingProcess do + use Ecto.Schema + import Ecto.Changeset + + alias TeslaMate.Log.{Charge, Car, Position} + + schema "charging_processes" do + field :start_date, :utc_datetime + field :end_date, :utc_datetime + + # TODO add fields + field :charge_energy_added, :float + # complete_date + + belongs_to(:car, Car) + belongs_to(:position, Position) + + has_many :charges, Charge + end + + @doc false + def changeset(charging_state, attrs) do + charging_state + |> cast(attrs, [ + :position_id, + :start_date, + :end_date, + :charge_energy_added + ]) + |> validate_required([:car_id, :start_date]) + |> foreign_key_constraint(:car_id) + |> foreign_key_constraint(:position_id) + end +end diff --git a/lib/tesla_mate/log/charging_state.ex b/lib/tesla_mate/log/charging_state.ex deleted file mode 100644 index d04800ce..00000000 --- a/lib/tesla_mate/log/charging_state.ex +++ /dev/null @@ -1,35 +0,0 @@ -defmodule TeslaMate.Log.ChargingState do - use Ecto.Schema - import Ecto.Changeset - - alias TeslaMate.Log.Charge - - schema "charging_states" do - field :start_date, :utc_datetime - field :end_date, :utc_datetime - field :unplug_date, :utc_datetime - field :charge_energy_added, :float - - belongs_to(:position, Position, foreign_key: :position_id) - belongs_to(:charge_start, Charge, foreign_key: :charge_start_id) - belongs_to(:charge_end, Charge, foreign_key: :charge_end_id) - end - - @doc false - def changeset(charging_state, attrs) do - charging_state - |> cast(attrs, [ - :start_date, - :end_date, - :unplug_date, - :charge_energy_added, - :position_id, - :charge_start_id, - :charge_end_id - ]) - |> validate_required([:start_date]) - |> foreign_key_constraint(:position_id) - |> foreign_key_constraint(:charge_start_id) - |> foreign_key_constraint(:charge_end_id) - end -end diff --git a/lib/tesla_mate/log/drive_state.ex b/lib/tesla_mate/log/drive_state.ex deleted file mode 100644 index 896d2d88..00000000 --- a/lib/tesla_mate/log/drive_state.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule TeslaMate.Log.DriveState do - use Ecto.Schema - import Ecto.Changeset - - alias TeslaMate.Log.Position - - schema "drive_states" do - field :start_date, :utc_datetime - field :end_date, :utc_datetime - - field :outside_temp_avg, :float - field :speed_max, :integer - field :speed_min, :integer - field :power_max, :float - field :power_min, :float - field :power_avg, :float - - belongs_to(:start_position, Position, foreign_key: :start_position_id) - belongs_to(:end_position, Position, foreign_key: :end_position_id) - end - - @doc false - def changeset(drive_state, attrs) do - drive_state - |> cast(attrs, [:start_date, :end_date, :start_position_id, :end_position_id]) - |> validate_required([:start_date, :start_position_id]) - |> foreign_key_constraint(:start_position_id) - |> foreign_key_constraint(:end_position_id) - end -end diff --git a/lib/tesla_mate/log/position.ex b/lib/tesla_mate/log/position.ex index eb2bc945..03be8dd5 100644 --- a/lib/tesla_mate/log/position.ex +++ b/lib/tesla_mate/log/position.ex @@ -2,17 +2,23 @@ defmodule TeslaMate.Log.Position do use Ecto.Schema import Ecto.Changeset + alias TeslaMate.Log.{Car, Trip} + schema "positions" do field :date, :utc_datetime field :latitude, :float field :longitude, :float + field :speed, :integer field :power, :integer field :odometer, :float - field :ideal_battery_range, :float + field :ideal_battery_range_km, :float field :battery_level, :float field :outside_temp, :float field :altitude, :float + + belongs_to(:car, Car) + belongs_to(:trip, Trip) end @doc false @@ -25,11 +31,13 @@ defmodule TeslaMate.Log.Position do :speed, :power, :odometer, - :ideal_battery_range, + :ideal_battery_range_km, :battery_level, :outside_temp, :altitude ]) - |> validate_required([:date, :latitude, :longitude]) + |> validate_required([:car_id, :date, :latitude, :longitude]) + |> foreign_key_constraint(:car_id) + |> foreign_key_constraint(:trip_id) end end diff --git a/lib/tesla_mate/log/state.ex b/lib/tesla_mate/log/state.ex index db325c61..38d85bf1 100644 --- a/lib/tesla_mate/log/state.ex +++ b/lib/tesla_mate/log/state.ex @@ -3,7 +3,7 @@ defmodule TeslaMate.Log.State do import Ecto.Changeset alias __MODULE__.State - alias TeslaMate.Log.Position + alias TeslaMate.Log.{Car} schema "states" do field :state, State @@ -11,16 +11,13 @@ defmodule TeslaMate.Log.State do field :start_date, :utc_datetime field :end_date, :utc_datetime - belongs_to(:start_position, Position, foreign_key: :start_position_id) - belongs_to(:end_position, Position, foreign_key: :end_position_id) + belongs_to(:car, Car) end @doc false def changeset(state, attrs) do state - |> cast(attrs, [:state, :start_date, :end_date, :start_position_id, :end_position_id]) - |> validate_required([:state, :start_date]) - |> foreign_key_constraint(:start_position_id) - |> foreign_key_constraint(:end_position_id) + |> cast(attrs, [:state, :start_date, :end_date]) + |> validate_required([:car_id, :state, :start_date]) end end diff --git a/lib/tesla_mate/log/trip.ex b/lib/tesla_mate/log/trip.ex new file mode 100644 index 00000000..06f0caa7 --- /dev/null +++ b/lib/tesla_mate/log/trip.ex @@ -0,0 +1,66 @@ +defmodule TeslaMate.Log.Trip do + use Ecto.Schema + import Ecto.Changeset + + alias TeslaMate.Log.{Position, Car} + + schema "trips" do + field :start_date, :utc_datetime + field :end_date, :utc_datetime + + field :outside_temp_avg, :float + + field :speed_max, :integer + field :speed_min, :integer + + field :power_max, :float + field :power_min, :float + field :power_avg, :float + + field :start_range_km, :float + field :end_range_km, :float + + field :start_km, :float + field :end_km, :float + field :distance, :float + field :duration_min, :integer + + # TODO Address ref + field :start_address, :string + field :end_address, :string + + field :consumption_kWh, :float + field :consumption_kWh_100km, :float + + belongs_to(:car, Car) + + has_many :positions, Position + end + + @doc false + def changeset(trip, attrs) do + trip + |> cast(attrs, [ + :start_date, + :end_date, + :outside_temp_avg, + :speed_max, + :speed_min, + :power_max, + :power_min, + :power_avg, + :start_range_km, + :end_range_km, + :start_km, + :end_km, + :distance, + :duration_min, + :start_address, + :end_address, + :consumption_kWh, + :consumption_kWh_100km + ]) + |> validate_required([:car_id, :start_date]) + |> foreign_key_constraint(:car_id) + end +end diff --git a/lib/tesla_mate/vehicles/vehicle.ex b/lib/tesla_mate/vehicles/vehicle.ex index 015d58e3..1da1c652 100644 --- a/lib/tesla_mate/vehicles/vehicle.ex +++ b/lib/tesla_mate/vehicles/vehicle.ex @@ -9,11 +9,11 @@ defmodule TeslaMate.Vehicles.Vehicle do alias TeslaApi.Vehicle.State alias TeslaApi.Vehicle - import Core.Dependency, only: [call: 3, call: 2] + import Core.Dependency, only: [call: 3] defstruct id: nil, + car_id: nil, vehicle_id: nil, - properties: nil, last_shift_state: nil, last_used: nil, sleep_between: %{from: nil, to: nil}, @@ -39,9 +39,7 @@ defmodule TeslaMate.Vehicles.Vehicle do def init(opts) do %Vehicle{} = vehicle = Keyword.fetch!(opts, :vehicle) - properties = Identification.properties(vehicle) - - Logger.info("Found Vehicle '#{vehicle.display_name}' [#{inspect(properties)}]") + Logger.info("Found Vehicle '#{vehicle.display_name}'") deps = %{ log: Keyword.get(opts, :log, Log), @@ -51,33 +49,36 @@ defmodule TeslaMate.Vehicles.Vehicle do data = %Data{ id: vehicle.id, vehicle_id: vehicle.vehicle_id, - properties: properties, last_used: DateTime.utc_now(), - sudpend_after_idle_min: Keyword.get(opts, :sudpend_after_idle_min, 15), + sudpend_after_idle_min: Keyword.get(opts, :sudpend_after_idle_min, 10), suspend_min: Keyword.get(opts, :suspend_min, 21), deps: deps } - {:ok, :start, data, {:next_event, :internal, :init}} + {:ok, :start, data, {:next_event, :internal, {:init, vehicle}}} end @impl true - def handle_event(:internal, :init, :start, data) do - # create_car if not exists + def handle_event(:internal, {:init, vehicle}, :start, data) do + {:ok, %Log.Car{id: car_id}} = + case call(data.deps.log, :get_car_by_eid, [vehicle.id]) do + nil -> + properties = Identification.properties(vehicle) - # DBHelper.getLastTrip() + call(data.deps.log, :create_car, [ + %{ + eid: vehicle.id, + vid: vehicle.vehicle_id, + model: properties.model, + efficiency: properties.efficiency + } + ]) - case call(data.deps.log, :close_charging_state) do - {:erorr, :no_charging_state_to_be_closed} -> :ok - :ok -> :ok - end + car -> + {:ok, car} + end - case call(data.deps.log, :close_drive_state) do - {:erorr, :no_drive_state_to_be_closed} -> :ok - :ok -> :ok - end - - {:keep_state_and_data, {:next_event, :internal, :fetch}} + {:keep_state, %Data{data | car_id: car_id}, {:next_event, :internal, :fetch}} end # TODO @@ -87,6 +88,7 @@ defmodule TeslaMate.Vehicles.Vehicle do # - geofecnces # - mqtt # - indices + # - cron job which "closes" drives & charging_processes def handle_event(event, :fetch, state, data) when event in [:state_timeout, :internal] do case fetch(data, expected_state: state) do @@ -118,7 +120,7 @@ defmodule TeslaMate.Vehicles.Vehicle do def handle_event(:internal, {:update, :asleep}, :start, data) do Logger.info("Start / :asleep") - :ok = call(data.deps.log, :start_state, [:asleep]) + :ok = call(data.deps.log, :start_state, [data.car_id, :asleep]) {:next_state, :asleep, data, schedule_fetch()} end @@ -126,18 +128,18 @@ defmodule TeslaMate.Vehicles.Vehicle do def handle_event(:internal, {:update, :offline}, :start, data) do Logger.info("Start / :offline") - :ok = call(data.deps.log, :start_state, [:offline]) + :ok = call(data.deps.log, :start_state, [data.car_id, :offline]) {:keep_state_and_data, schedule_fetch()} end - def handle_event(:internal, {:update, {:online, vehicle_state}}, :start, data) do + def handle_event(:internal, event, :start, data) do Logger.info("Start / :online") - :ok = insert_position(vehicle_state, data) - :ok = call(data.deps.log, :start_state, [:online]) + :ok = call(data.deps.log, :start_state, [data.car_id, :online]) - {:next_state, :online, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch()} + {:next_state, :online, %Data{data | last_used: DateTime.utc_now()}, + {:next_event, :internal, event}} end ### :online @@ -154,23 +156,21 @@ defmodule TeslaMate.Vehicles.Vehicle do when shift_state in ["D", "N", "R"] -> Logger.info("Start / :driving") - :ok = insert_position(vehicle_state, data) - :ok = call(data.deps.log, :start_drive_state) - # wh.StartStreamThread(); // for altitude + {:ok, trip_id} = call(data.deps.log, :start_trip, [data.car_id]) + :ok = insert_position(vehicle_state, data, trip_id: trip_id) - {:next_state, {:driving, shift_state}, %Data{data | last_used: DateTime.utc_now()}, + {:next_state, {:driving, trip_id}, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(5)} %{charge_state: %State.Charge{charging_state: charging_state}} when charging_state in ["Complete", "Charging"] -> Logger.info("Start / :charging") - :ok = insert_charge(vehicle_state, data) :ok = insert_position(vehicle_state, data) - :ok = call(data.deps.log, :start_charging_state) + {:ok, charging_process_id} = call(data.deps.log, :start_charging_process, [data.car_id]) - {:next_state, {:charging, charging_state}, %Data{data | last_used: DateTime.utc_now()}, - schedule_fetch(5)} + {:next_state, {:charging, charging_state, charging_process_id}, + %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(5)} _ -> suspend? = @@ -186,9 +186,6 @@ defmodule TeslaMate.Vehicles.Vehicle do {true, %State.Climate{is_preconditioning: _}} -> Logger.info("Start / :suspend") - # Insert last position before sleep - :ok = insert_position(vehicle_state, data) - {:next_state, :start, data, schedule_fetch(data.suspend_min, :minutes)} {false, _} -> @@ -199,20 +196,20 @@ defmodule TeslaMate.Vehicles.Vehicle do ### :charging - def handle_event(:internal, {:update, :offline}, {:charging, _}, _data) do + def handle_event(:internal, {:update, :offline}, {:charging, _, _}, _data) do Logger.warn("Vehicle went offline while charging") {:keep_state_and_data, schedule_fetch()} end - def handle_event(:internal, {:update, {:online, vehicle_state}}, {:charging, last}, data) do + def handle_event(:internal, {:update, {:online, vehicle_state}}, {:charging, last, pid}, data) do case {vehicle_state.charge_state.charging_state, last} do {"Charging", _} -> - :ok = insert_charge(vehicle_state, data) + :ok = insert_charge(pid, vehicle_state, data) next_check_in = round(1000 / Map.get(vehicle_state.charge_state, :charge_power, 100)) - {:next_state, {:charging, "Charging"}, %Data{data | last_used: DateTime.utc_now()}, + {:next_state, {:charging, "Charging", pid}, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(next_check_in)} {"Complete", "Complete"} -> @@ -221,9 +218,9 @@ defmodule TeslaMate.Vehicles.Vehicle do {"Complete", _} -> Logger.info("Charging complete") - :ok = insert_charge(vehicle_state, data) + :ok = insert_charge(pid, vehicle_state, data) - {:next_state, {:charging, "Complete"}, %Data{data | last_used: DateTime.utc_now()}, + {:next_state, {:charging, "Complete", pid}, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch()} {charging_state, last_charging_state} -> @@ -231,8 +228,7 @@ defmodule TeslaMate.Vehicles.Vehicle do "Charging ended (?): #{inspect(charging_state)} | Before: #{last_charging_state}" ) - :ok = insert_position(vehicle_state, data) - :ok = call(data.deps.log, :close_charging_state) + :ok = call(data.deps.log, :close_charging_process, [pid]) {:next_state, :start, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(5)} end @@ -240,28 +236,26 @@ defmodule TeslaMate.Vehicles.Vehicle do ### :driving - def handle_event(:internal, {:update, :offline}, {:driving, _}, _data) do + def handle_event(:internal, {:update, :offline}, {:driving, _trip_id}, _data) do Logger.warn("Vehicle went offline while driving") {:keep_state_and_data, schedule_fetch()} end - def handle_event(:internal, {:update, {:online, vehicle_state}}, {:driving, _}, data) do + def handle_event(:internal, {:update, {:online, vehicle_state}}, {:driving, trip_id}, data) do case vehicle_state.drive_state.shift_state do shift_state when shift_state in ["D", "R", "N"] -> - :ok = insert_position(vehicle_state, data) + :ok = insert_position(vehicle_state, data, trip_id: trip_id) - {:next_state, {:driving, shift_state}, %Data{data | last_used: DateTime.utc_now()}, - schedule_fetch(5)} + {:keep_state, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(5)} "P" -> {:keep_state, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(10)} nil -> - Logger.info("Driving ended") + Logger.info("Trip ended") - # wh.StopStreaming(); - :ok = call(data.deps.log, :close_drive_state) + :ok = call(data.deps.log, :close_trip, [trip_id]) {:next_state, :start, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(5)} end @@ -279,12 +273,11 @@ defmodule TeslaMate.Vehicles.Vehicle do {:keep_state_and_data, schedule_fetch()} end - def handle_event(:internal, {:update, {:online, vehicle_state}}, :asleep, data) do + def handle_event(:internal, {:update, {:online, _}} = event, :asleep, data) do Logger.info("Vehicle woke up") - :ok = insert_position(vehicle_state, data) - - {:next_state, :start, %Data{data | last_used: DateTime.utc_now()}, schedule_fetch(5)} + {:next_state, :start, %Data{data | last_used: DateTime.utc_now()}, + {:next_event, :internal, event}} end # Private @@ -294,7 +287,7 @@ defmodule TeslaMate.Vehicles.Vehicle do case expected_state do :online -> true {:driving, _} -> true - {:charging, _} -> true + {:charging, _, _} -> true :asleep -> false :start -> false end @@ -321,8 +314,11 @@ defmodule TeslaMate.Vehicles.Vehicle do end end - defp insert_position(%Vehicle{drive_state: %State.Drive{}} = state, data) do + defp insert_position(%Vehicle{drive_state: %State.Drive{}} = state, data, opts \\ []) do + trip_id = Keyword.get(opts, :trip_id) + attrs = %{ + trip_id: trip_id, date: DateTime.from_unix!(state.drive_state.timestamp, :microsecond), latitude: state.drive_state.latitude, longitude: state.drive_state.longitude, @@ -331,14 +327,15 @@ defmodule TeslaMate.Vehicles.Vehicle do battery_level: Map.get(state.charge_state, :battery_level), outside_temp: Map.get(state.climate_state, :outside_temp), odometer: Map.get(state.vehicle_state, :odometer), - ideal_battery_range: Map.get(state.charge_state, :ideal_battery_range), + # TODO convert + ideal_battery_range_km: Map.get(state.charge_state, :ideal_battery_range), altitude: nil } - :ok = call(data.deps.log, :insert_position, [attrs]) + :ok = call(data.deps.log, :insert_position, [data.car_id, attrs]) end - defp insert_charge(%Vehicle{charge_state: %State.Charge{}} = state, data) do + defp insert_charge(process_id, %Vehicle{charge_state: %State.Charge{}} = state, data) do attrs = %{ date: DateTime.from_unix!(state.charge_state.timestamp, :microsecond), battery_level: state.charge_state.battery_level, @@ -347,11 +344,12 @@ defmodule TeslaMate.Vehicles.Vehicle do charger_phases: state.charge_state.charger_phases, charger_power: state.charge_state.charger_power, charger_voltage: state.charge_state.charger_voltage, - ideal_battery_range: state.charge_state.ideal_battery_range, + # TODO convert + ideal_battery_range_km: state.charge_state.ideal_battery_range, outside_temp: Map.get(state.climate_state, :outside_temp) } - :ok = call(data.deps.log, :insert_charge, [attrs]) + :ok = call(data.deps.log, :insert_charge, [process_id, attrs]) end defp schedule_fetch(n \\ 10, unit \\ :seconds) diff --git a/lib/tesla_mate_web/controllers/car_controller.ex b/lib/tesla_mate_web/controllers/car_controller.ex new file mode 100644 index 00000000..635e90b7 --- /dev/null +++ b/lib/tesla_mate_web/controllers/car_controller.ex @@ -0,0 +1,26 @@ +defmodule TeslaMateWeb.CarController do + use TeslaMateWeb, :controller + + alias TeslaMate.Log + alias TeslaMate.Log.Car + + action_fallback TeslaMateWeb.FallbackController + + def index(conn, _params) do + car = Log.list_cars() + render(conn, "index.json", car: car) + end + + def show(conn, %{"id" => id}) do + car = Log.get_car!(id) + render(conn, "show.json", car: car) + end + + def update(conn, %{"id" => id, "car" => car_params}) do + car = Log.get_car!(id) + + with {:ok, %Car{} = car} <- Log.update_car(car, car_params) do + render(conn, "show.json", car: car) + end + end +end diff --git a/lib/tesla_mate_web/router.ex b/lib/tesla_mate_web/router.ex index 3d5789d7..b4bf3fed 100644 --- a/lib/tesla_mate_web/router.ex +++ b/lib/tesla_mate_web/router.ex @@ -19,8 +19,9 @@ defmodule TeslaMateWeb.Router do get "/", PageController, :index end - # Other scopes may use custom stacks. - # scope "/api", TeslaMateWeb do - # pipe_through :api - # end + scope "/api", TeslaMateWeb do + pipe_through :api + + resources "/car", CarController, only: [:index, :show, :update] + end end diff --git a/lib/tesla_mate_web/views/car_view.ex b/lib/tesla_mate_web/views/car_view.ex new file mode 100644 index 00000000..88821ff8 --- /dev/null +++ b/lib/tesla_mate_web/views/car_view.ex @@ -0,0 +1,22 @@ +defmodule TeslaMateWeb.CarView do + use TeslaMateWeb, :view + alias TeslaMateWeb.CarView + + def render("index.json", %{car: car}) do + %{data: render_many(car, CarView, "car.json")} + end + + def render("show.json", %{car: car}) do + %{data: render_one(car, CarView, "car.json")} + end + + def render("car.json", %{car: car}) do + %{ + id: car.id, + eid: car.eid, + vid: car.vid, + model: car.model, + efficiency: car.efficiency + } + end +end diff --git a/mix.exs b/mix.exs index fc4998e2..7f3aadd7 100644 --- a/mix.exs +++ b/mix.exs @@ -61,7 +61,7 @@ defmodule TeslaMate.MixProject do [ "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"], - test: ["ecto.create --quiet", "ecto.migrate", "test"] + test: ["ecto.create --quiet", "ecto.migrate", "test --no-start"] ] end end diff --git a/priv/repo/migrations/20190330150000_create_car.exs b/priv/repo/migrations/20190330150000_create_car.exs new file mode 100644 index 00000000..943cb0d2 --- /dev/null +++ b/priv/repo/migrations/20190330150000_create_car.exs @@ -0,0 +1,17 @@ +defmodule TeslaMate.Repo.Migrations.CreateCar do + use Ecto.Migration + + def change do + create table(:cars) do + add(:eid, :bigint, null: false) + add(:vid, :bigint, null: false) + add(:model, :string, null: false) + add(:efficiency, :float, null: false) + + timestamps() + end + + create(unique_index(:cars, :eid)) + create(unique_index(:cars, :vid)) + end +end diff --git a/priv/repo/migrations/20190330160000_create_trips.exs b/priv/repo/migrations/20190330160000_create_trips.exs new file mode 100644 index 00000000..08aed18c --- /dev/null +++ b/priv/repo/migrations/20190330160000_create_trips.exs @@ -0,0 +1,28 @@ +defmodule TeslaMate.Repo.Migrations.CreateTrips do + use Ecto.Migration + + def change do + create table(:trips) do + add(:start_date, :utc_datetime, null: false) + add(:end_date, :utc_datetime) + add(:outside_temp_avg, :float) + add(:speed_max, :integer) + add(:speed_min, :integer) + add(:power_max, :float) + add(:power_min, :float) + add(:power_avg, :float) + add(:start_range_km, :float) + add(:end_range_km, :float) + add(:start_km, :float) + add(:end_km, :float) + add(:distance, :float) + add(:duration_min, :integer) + add(:start_address, :string) + add(:end_address, :string) + add(:consumption_kWh, :float) + add(:consumption_kWh_100km, :float) + + add(:car_id, references(:cars), null: false) + end + end +end diff --git a/priv/repo/migrations/20190331161343_create_positions.exs b/priv/repo/migrations/20190330170000_create_positions.exs similarity index 77% rename from priv/repo/migrations/20190331161343_create_positions.exs rename to priv/repo/migrations/20190330170000_create_positions.exs index 1c541f06..33e1cab9 100644 --- a/priv/repo/migrations/20190331161343_create_positions.exs +++ b/priv/repo/migrations/20190330170000_create_positions.exs @@ -9,10 +9,13 @@ defmodule TeslaMate.Repo.Migrations.CreatePositions do add(:speed, :integer) add(:power, :integer) add(:odometer, :float) - add(:ideal_battery_range, :float) + add(:ideal_battery_range_km, :float) add(:battery_level, :float) add(:outside_temp, :float) add(:altitude, :float) + + add(:car_id, references(:cars), null: false) + add(:trip_id, references(:trips)) end end end diff --git a/priv/repo/migrations/20190331170338_create_states.exs b/priv/repo/migrations/20190330180000_create_states.exs similarity index 79% rename from priv/repo/migrations/20190331170338_create_states.exs rename to priv/repo/migrations/20190330180000_create_states.exs index 373f157e..8d5d31bb 100644 --- a/priv/repo/migrations/20190331170338_create_states.exs +++ b/priv/repo/migrations/20190330180000_create_states.exs @@ -12,8 +12,7 @@ defmodule TeslaMate.Repo.Migrations.CreateStates do add(:start_date, :utc_datetime, null: false) add(:end_date, :utc_datetime) - add(:start_position_id, references(:positions)) - add(:end_position_id, references(:positions)) + add(:car_id, references(:cars), null: false) end end diff --git a/priv/repo/migrations/20190330190000_create_charging_processes.exs b/priv/repo/migrations/20190330190000_create_charging_processes.exs new file mode 100644 index 00000000..6ae2651f --- /dev/null +++ b/priv/repo/migrations/20190330190000_create_charging_processes.exs @@ -0,0 +1,15 @@ +defmodule TeslaMate.Repo.Migrations.CreateChargingProcesses do + use Ecto.Migration + + def change do + create table(:charging_processes) do + add(:start_date, :utc_datetime, nul: false) + add(:end_date, :utc_datetime) + + add(:charge_energy_added, :float) + + add(:car_id, references(:cars), null: false) + add(:position_id, references(:positions)) + end + end +end diff --git a/priv/repo/migrations/20190331184331_create_charges.exs b/priv/repo/migrations/20190330200000_create_charges.exs similarity index 70% rename from priv/repo/migrations/20190331184331_create_charges.exs rename to priv/repo/migrations/20190330200000_create_charges.exs index 4fa7b3b4..a1d6b57e 100644 --- a/priv/repo/migrations/20190331184331_create_charges.exs +++ b/priv/repo/migrations/20190330200000_create_charges.exs @@ -4,14 +4,16 @@ defmodule TeslaMate.Repo.Migrations.CreateCharges do def change do create table(:charges) do add(:date, :utc_datetime, null: false) - add(:ideal_battery_range, :float, null: false) - add(:battery_level, :float, null: false) + add(:ideal_battery_range_km, :float, null: false) + add(:battery_level, :integer, null: false) add(:charge_energy_added, :float, null: false) add(:charger_power, :float, null: false) add(:charger_voltage, :integer) add(:charger_phases, :integer) add(:charger_actual_current, :integer) add(:outside_temp, :float) + + add(:charging_process_id, references(:charging_processes), null: false) end end end diff --git a/priv/repo/migrations/20190331182720_create_drive_states.exs b/priv/repo/migrations/20190331182720_create_drive_states.exs deleted file mode 100644 index 694fb822..00000000 --- a/priv/repo/migrations/20190331182720_create_drive_states.exs +++ /dev/null @@ -1,20 +0,0 @@ -defmodule TeslaMate.Repo.Migrations.CreateDriveStates do - use Ecto.Migration - - def change do - create table(:drive_states) do - add(:start_date, :utc_datetime, null: false) - add(:end_date, :utc_datetime) - - add(:outside_temp_avg, :float) - add(:speed_max, :integer) - add(:speed_min, :integer) - add(:power_max, :float) - add(:power_min, :float) - add(:power_avg, :float) - - add(:start_position_id, references(:positions), null: false) - add(:end_position_id, references(:positions)) - end - end -end diff --git a/priv/repo/migrations/20190331185127_create_charging_states.exs b/priv/repo/migrations/20190331185127_create_charging_states.exs deleted file mode 100644 index c9b80599..00000000 --- a/priv/repo/migrations/20190331185127_create_charging_states.exs +++ /dev/null @@ -1,17 +0,0 @@ -defmodule TeslaMate.Repo.Migrations.CreateChargingStates do - use Ecto.Migration - - def change do - create table(:charging_states) do - add(:start_date, :utc_datetime, nul: false) - add(:end_date, :utc_datetime) - - add(:unplug_date, :utc_datetime) - add(:charge_energy_added, :float) - - add(:position_id, references(:positions)) - add(:charge_start_id, references(:charges)) - add(:charge_end_id, references(:charges)) - end - end -end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index c6890a03..effe3e7c 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -33,6 +33,8 @@ defmodule TeslaMateWeb.ConnCase do Ecto.Adapters.SQL.Sandbox.mode(TeslaMate.Repo, {:shared, self()}) end + {:ok, _} = TeslaMateWeb.Endpoint.start_link() + {:ok, conn: Phoenix.ConnTest.build_conn()} end end diff --git a/test/support/mocks/log.ex b/test/support/mocks/log.ex index a0d07f3e..ba5e27ce 100644 --- a/test/support/mocks/log.ex +++ b/test/support/mocks/log.ex @@ -4,22 +4,37 @@ defmodule LogMock do defstruct [:pid] alias __MODULE__, as: State + alias TeslaMate.Log.Car + # API def start_link(opts) do GenServer.start_link(__MODULE__, opts, name: Keyword.fetch!(opts, :name)) end - def start_state(name, state), do: GenServer.call(name, {:start_state, state}) + def get_car_by_eid(name, eid), do: GenServer.call(name, {:get_car_by_eid, eid}) + def create_car(name, car), do: GenServer.call(name, {:create_car, car}) - def start_drive_state(name), do: GenServer.call(name, :start_drive_state) - def close_drive_state(name), do: GenServer.call(name, :close_drive_state) + def start_state(name, car_id, state), do: GenServer.call(name, {:start_state, car_id, state}) - def start_charging_state(name), do: GenServer.call(name, :start_charging_state) - def close_charging_state(name), do: GenServer.call(name, :close_charging_state) + def start_trip(name, car_id), do: GenServer.call(name, {:start_trip, car_id}) + def close_trip(name, trip_id), do: GenServer.call(name, {:close_trip, trip_id}) - def insert_position(name, attrs), do: GenServer.call(name, {:insert_position, attrs}) - def insert_charge(name, attrs), do: GenServer.call(name, {:insert_charge, attrs}) + def start_charging_process(name, car_id) do + GenServer.call(name, {:start_charging_process, car_id}) + end + + def close_charging_process(name, process_id) do + GenServer.call(name, {:close_charging_process, process_id}) + end + + def insert_position(name, car_id, attrs) do + GenServer.call(name, {:insert_position, car_id, attrs}) + end + + def insert_charge(name, car_id, attrs) do + GenServer.call(name, {:insert_charge, car_id, attrs}) + end # Callbacks @@ -29,6 +44,24 @@ defmodule LogMock do end @impl true + def handle_call({:get_car_by_eid, _eid}, _from, state) do + {:reply, nil, state} + end + + def handle_call({:create_car, car}, _from, state) do + {:reply, {:ok, struct(Car, Map.put(car, :id, 999))}, state} + end + + def handle_call({:start_charging_process, _car_id} = action, _from, %State{pid: pid} = state) do + send(pid, action) + {:reply, {:ok, 99}, state} + end + + def handle_call({:start_trip, _car_id} = action, _from, %State{pid: pid} = state) do + send(pid, action) + {:reply, {:ok, 111}, state} + end + def handle_call(action, _from, %State{pid: pid} = state) do send(pid, action) {:reply, :ok, state} diff --git a/test/support/vehicle_case.ex b/test/support/vehicle_case.ex index 8d516792..08f5b0be 100644 --- a/test/support/vehicle_case.ex +++ b/test/support/vehicle_case.ex @@ -24,9 +24,6 @@ defmodule TeslaMate.VehicleCase do opts} ) - assert_receive :close_charging_state - assert_receive :close_drive_state - :ok end diff --git a/test/tesla_mate/log/log_test.exs b/test/tesla_mate/log/log_test.exs index 3f988d54..7029fbfe 100644 --- a/test/tesla_mate/log/log_test.exs +++ b/test/tesla_mate/log/log_test.exs @@ -1,7 +1,7 @@ defmodule TeslaMate.LogTest do use TeslaMate.DataCase - # alias TeslaMate.Log + alias TeslaMate.Log # describe "positions" do # alias TeslaMate.Log.Position @@ -129,73 +129,73 @@ defmodule TeslaMate.LogTest do # end # end - # describe "drive_states" do - # alias TeslaMate.Log.DriveState + # describe "trips" do + # alias TeslaMate.Log.Trip # @valid_attrs %{end_date: "2010-04-17T14:00:00Z", end_position_id: 42, start_date: "2010-04-17T14:00:00Z", start_position_id: 42} # @update_attrs %{end_date: "2011-05-18T15:01:01Z", end_position_id: 43, start_date: "2011-05-18T15:01:01Z", start_position_id: 43} # @invalid_attrs %{end_date: nil, end_position_id: nil, start_date: nil, start_position_id: nil} - # def drive_state_fixture(attrs \\ %{}) do - # {:ok, drive_state} = + # def trip_fixture(attrs \\ %{}) do + # {:ok, trip} = # attrs # |> Enum.into(@valid_attrs) - # |> Log.create_drive_state() + # |> Log.create_trip() - # drive_state + # trip # end - # test "list_drive_states/0 returns all drive_states" do - # drive_state = drive_state_fixture() - # assert Log.list_drive_states() == [drive_state] + # test "list_trips/0 returns all trips" do + # trip = trip_fixture() + # assert Log.list_trips() == [trip] # end - # test "get_drive_state!/1 returns the drive_state with given id" do - # drive_state = drive_state_fixture() - # assert Log.get_drive_state!(drive_state.id) == drive_state + # test "get_trip!/1 returns the trip with given id" do + # trip = trip_fixture() + # assert Log.get_trip!(trip.id) == trip # end - # test "create_drive_state/1 with valid data creates a drive_state" do - # assert {:ok, %DriveState{} = drive_state} = Log.create_drive_state(@valid_attrs) - # assert drive_state.end_date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") - # assert drive_state.end_position_id == 42 - # assert drive_state.start_date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") - # assert drive_state.start_position_id == 42 + # test "create_trip/1 with valid data creates a trip" do + # assert {:ok, %Trip{} = trip} = Log.create_trip(@valid_attrs) + # assert trip.end_date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") + # assert trip.end_position_id == 42 + # assert trip.start_date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") + # assert trip.start_position_id == 42 # end - # test "create_drive_state/1 with invalid data returns error changeset" do - # assert {:error, %Ecto.Changeset{}} = Log.create_drive_state(@invalid_attrs) + # test "create_trip/1 with invalid data returns error changeset" do + # assert {:error, %Ecto.Changeset{}} = Log.create_trip(@invalid_attrs) # end - # test "update_drive_state/2 with valid data updates the drive_state" do - # drive_state = drive_state_fixture() - # assert {:ok, %DriveState{} = drive_state} = Log.update_drive_state(drive_state, @update_attrs) - # assert drive_state.end_date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") - # assert drive_state.end_position_id == 43 - # assert drive_state.start_date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") - # assert drive_state.start_position_id == 43 + # test "update_trip/2 with valid data updates the trip" do + # trip = trip_fixture() + # assert {:ok, %Trip{} = trip} = Log.update_trip(trip, @update_attrs) + # assert trip.end_date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") + # assert trip.end_position_id == 43 + # assert trip.start_date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") + # assert trip.start_position_id == 43 # end - # test "update_drive_state/2 with invalid data returns error changeset" do - # drive_state = drive_state_fixture() - # assert {:error, %Ecto.Changeset{}} = Log.update_drive_state(drive_state, @invalid_attrs) - # assert drive_state == Log.get_drive_state!(drive_state.id) + # test "update_trip/2 with invalid data returns error changeset" do + # trip = trip_fixture() + # assert {:error, %Ecto.Changeset{}} = Log.update_trip(trip, @invalid_attrs) + # assert trip == Log.get_trip!(trip.id) # end - # test "delete_drive_state/1 deletes the drive_state" do - # drive_state = drive_state_fixture() - # assert {:ok, %DriveState{}} = Log.delete_drive_state(drive_state) - # assert_raise Ecto.NoResultsError, fn -> Log.get_drive_state!(drive_state.id) end + # test "delete_trip/1 deletes the trip" do + # trip = trip_fixture() + # assert {:ok, %Trip{}} = Log.delete_trip(trip) + # assert_raise Ecto.NoResultsError, fn -> Log.get_trip!(trip.id) end # end - # test "change_drive_state/1 returns a drive_state changeset" do - # drive_state = drive_state_fixture() - # assert %Ecto.Changeset{} = Log.change_drive_state(drive_state) + # test "change_trip/1 returns a trip changeset" do + # trip = trip_fixture() + # assert %Ecto.Changeset{} = Log.change_trip(trip) # end # end # describe "charging_states" do - # alias TeslaMate.Log.ChargingState + # alias TeslaMate.Log.ChargingProcess # @valid_attrs %{end_date: "2010-04-17T14:00:00Z", start_date: "2010-04-17T14:00:00Z"} # @update_attrs %{end_date: "2011-05-18T15:01:01Z", start_date: "2011-05-18T15:01:01Z"} @@ -221,7 +221,7 @@ defmodule TeslaMate.LogTest do # end # test "create_charging_state/1 with valid data creates a charging_state" do - # assert {:ok, %ChargingState{} = charging_state} = Log.create_charging_state(@valid_attrs) + # assert {:ok, %ChargingProcess{} = charging_state} = Log.create_charging_state(@valid_attrs) # assert charging_state.end_date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") # assert charging_state.start_date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") # end @@ -232,7 +232,7 @@ defmodule TeslaMate.LogTest do # test "update_charging_state/2 with valid data updates the charging_state" do # charging_state = charging_state_fixture() - # assert {:ok, %ChargingState{} = charging_state} = Log.update_charging_state(charging_state, @update_attrs) + # assert {:ok, %ChargingProcess{} = charging_state} = Log.update_charging_state(charging_state, @update_attrs) # assert charging_state.end_date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") # assert charging_state.start_date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") # end @@ -245,7 +245,7 @@ defmodule TeslaMate.LogTest do # test "delete_charging_state/1 deletes the charging_state" do # charging_state = charging_state_fixture() - # assert {:ok, %ChargingState{}} = Log.delete_charging_state(charging_state) + # assert {:ok, %ChargingProcess{}} = Log.delete_charging_state(charging_state) # assert_raise Ecto.NoResultsError, fn -> Log.get_charging_state!(charging_state.id) end # end @@ -258,9 +258,9 @@ defmodule TeslaMate.LogTest do # describe "charges" do # alias TeslaMate.Log.Charge - # @valid_attrs %{battery_level: 120.5, charge_energy_added: 120.5, charger_actual_current: 42, charger_phases: 42, charger_power: 120.5, charger_voltage: 42, date: "2010-04-17T14:00:00Z", ideal_battery_range: 120.5, outside_temp: 120.5} - # @update_attrs %{battery_level: 456.7, charge_energy_added: 456.7, charger_actual_current: 43, charger_phases: 43, charger_power: 456.7, charger_voltage: 43, date: "2011-05-18T15:01:01Z", ideal_battery_range: 456.7, outside_temp: 456.7} - # @invalid_attrs %{battery_level: nil, charge_energy_added: nil, charger_actual_current: nil, charger_phases: nil, charger_power: nil, charger_voltage: nil, date: nil, ideal_battery_range: nil, outside_temp: nil} + # @valid_attrs %{battery_level: 120.5, charge_energy_added: 120.5, charger_actual_current: 42, charger_phases: 42, charger_power: 120.5, charger_voltage: 42, date: "2010-04-17T14:00:00Z", ideal_battery_range_km: 120.5, outside_temp: 120.5} + # @update_attrs %{battery_level: 456.7, charge_energy_added: 456.7, charger_actual_current: 43, charger_phases: 43, charger_power: 456.7, charger_voltage: 43, date: "2011-05-18T15:01:01Z", ideal_battery_range_km: 456.7, outside_temp: 456.7} + # @invalid_attrs %{battery_level: nil, charge_energy_added: nil, charger_actual_current: nil, charger_phases: nil, charger_power: nil, charger_voltage: nil, date: nil, ideal_battery_range_km: nil, outside_temp: nil} # def charge_fixture(attrs \\ %{}) do # {:ok, charge} = @@ -290,7 +290,7 @@ defmodule TeslaMate.LogTest do # assert charge.charger_power == 120.5 # assert charge.charger_voltage == 42 # assert charge.date == DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC") - # assert charge.ideal_battery_range == 120.5 + # assert charge.ideal_battery_range_km == 120.5 # assert charge.outside_temp == 120.5 # end @@ -308,7 +308,7 @@ defmodule TeslaMate.LogTest do # assert charge.charger_power == 456.7 # assert charge.charger_voltage == 43 # assert charge.date == DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC") - # assert charge.ideal_battery_range == 456.7 + # assert charge.ideal_battery_range_km == 456.7 # assert charge.outside_temp == 456.7 # end @@ -329,4 +329,65 @@ defmodule TeslaMate.LogTest do # assert %Ecto.Changeset{} = Log.change_charge(charge) # end # end + + describe "car" do + alias TeslaMate.Log.Car + + @valid_attrs %{efficiency: 120.5, eid: 42, model: "some model", vid: 42} + @update_attrs %{ + efficiency: 456.7, + model: "some updated model", + eid: 43, + vid: 43 + } + @invalid_attrs %{efficiency: nil, eid: nil, model: nil, vid: nil} + + def car_fixture(attrs \\ %{}) do + {:ok, car} = + attrs + |> Enum.into(@valid_attrs) + |> Log.create_car() + + car + end + + test "list_cars/0 returns all car" do + car = car_fixture() + assert Log.list_cars() == [car] + end + + test "get_car!/1 returns the car with given id" do + car = car_fixture() + assert Log.get_car!(car.id) == car + end + + test "create_car/1 with valid data creates a car" do + assert {:ok, %Car{} = car} = Log.create_car(@valid_attrs) + assert car.efficiency == 120.5 + assert car.eid == 42 + assert car.model == "some model" + assert car.vid == 42 + end + + test "create_car/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Log.create_car(@invalid_attrs) + end + + test "update_car/2 with valid data updates the car" do + car = car_fixture() + assert {:ok, %Car{} = car} = Log.update_car(car, @update_attrs) + + assert car.efficiency == 456.7 + assert car.model == "some updated model" + + assert car.eid == 42 + assert car.vid == 42 + end + + test "update_car/2 with invalid data returns error changeset" do + car = car_fixture() + assert {:error, %Ecto.Changeset{}} = Log.update_car(car, @invalid_attrs) + assert car == Log.get_car!(car.id) + end + end end diff --git a/test/tesla_mate/vehicles/vehicle/charging_test.exs b/test/tesla_mate/vehicles/vehicle/charging_test.exs index 27edd4dc..49ac0fed 100644 --- a/test/tesla_mate/vehicles/vehicle/charging_test.exs +++ b/test/tesla_mate/vehicles/vehicle/charging_test.exs @@ -12,9 +12,10 @@ defmodule TeslaMate.Vehicles.Vehicle.ChargingTest do ) end + @tag :only test "logs a full charging cycle", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, @@ -30,20 +31,16 @@ defmodule TeslaMate.Vehicles.Vehicle.ChargingTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} - assert_receive :start_charging_state - assert_receive {:insert_position, %{date: _, latitude: 0.0, longitude: 0.0}} - assert_receive {:insert_charge, %{date: _, charger_power: nil}} - assert_receive {:insert_charge, %{date: _, charger_power: 125}} - assert_receive {:insert_charge, %{date: _, charger_power: 120}} - assert_receive {:insert_charge, %{date: _, charger_power: 0}} - assert_receive :close_charging_state + assert_receive {:insert_position, 999, %{date: _, latitude: 0.0, longitude: 0.0}} + assert_receive {:start_charging_process, 999} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 125}} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 120}} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 0}} + assert_receive {:close_charging_process, 99} - assert_receive {:insert_position, %{date: _, latitude: 0.0, longitude: 0.0, speed: nil}} - assert_receive {:insert_position, %{date: _, latitude: 0.2, longitude: 0.2, speed: nil}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} refute_receive _ end @@ -51,7 +48,7 @@ defmodule TeslaMate.Vehicles.Vehicle.ChargingTest do @tag :capture_log test "handles a connection loss when charging", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, @@ -71,27 +68,23 @@ defmodule TeslaMate.Vehicles.Vehicle.ChargingTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} - assert_receive :start_charging_state - assert_receive {:insert_position, %{date: _, latitude: 0.0, longitude: 0.0}} - assert_receive {:insert_charge, %{date: _, charger_power: nil}} - assert_receive {:insert_charge, %{date: _, charger_power: 125}} - assert_receive {:insert_charge, %{date: _, charger_power: 120}} - assert_receive {:insert_charge, %{date: _, charger_power: 0}} - assert_receive :close_charging_state + assert_receive {:insert_position, 999, %{date: _, latitude: 0.0, longitude: 0.0, speed: nil}} + assert_receive {:start_charging_process, 999} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 125}} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 120}} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 0}} + assert_receive {:close_charging_process, 99} - assert_receive {:insert_position, %{date: _, latitude: 0.0, longitude: 0.0, speed: nil}} - assert_receive {:insert_position, %{date: _, latitude: 0.2, longitude: 0.2, speed: nil}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} refute_receive _ end test "Transitions directly into charging state", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, @@ -100,13 +93,13 @@ defmodule TeslaMate.Vehicles.Vehicle.ChargingTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} - assert_receive :start_charging_state - assert_receive {:insert_position, %{date: _, latitude: 0.0, longitude: 0.0}} - assert_receive {:insert_charge, %{date: _, charger_power: 22}} - assert_receive {:insert_charge, %{date: _, charger_power: 22}} + assert_receive {:insert_position, 999, %{date: _, latitude: 0.0, longitude: 0.0}} + assert_receive {:start_charging_process, 999} + + assert_receive {:insert_charge, 99, %{date: _, charger_power: 22}} + assert_receive {:insert_charge, 99, %{date: _, charger_power: 22}} # ... refute_received _ diff --git a/test/tesla_mate/vehicles/vehicle/driving_test.exs b/test/tesla_mate/vehicles/vehicle/driving_test.exs index bad471a5..cf6565fd 100644 --- a/test/tesla_mate/vehicles/vehicle/driving_test.exs +++ b/test/tesla_mate/vehicles/vehicle/driving_test.exs @@ -1,44 +1,42 @@ defmodule TeslaMate.Vehicles.Vehicle.DrivingTest do use TeslaMate.VehicleCase, async: true - defp drive_event(ts, shift_state, speed) do + defp drive_event(ts, shift_state, speed_mph) do vehicle_full( drive_state: %{ timestamp: ts, latitude: 0.1, longitude: 0.1, shift_state: shift_state, - speed: speed + speed: speed_mph } ) end test "logs a full drive", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, {:ok, vehicle_full(drive_state: %{timestamp: now_ts, latitude: 0.0, longitude: 0.0})}, - {:ok, drive_event(now_ts + 1, "D", 50)}, - {:ok, drive_event(now_ts + 2, "N", 0)}, + {:ok, drive_event(now_ts + 1, "D", 60)}, + {:ok, drive_event(now_ts + 2, "N", 30)}, {:ok, drive_event(now_ts + 3, "R", -5)}, {:ok, vehicle_full(drive_state: %{timestamp: now_ts, latitude: 0.2, longitude: 0.2})} ] :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} - assert_receive :start_drive_state - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 50}} - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 0}} - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: -5}} - assert_receive :close_drive_state + assert_receive {:start_trip, 999} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 97.0, trip_id: 111}} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 48.0, trip_id: 111}} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: -8.0, trip_id: 111}} + assert_receive {:close_trip, 111} - assert_receive {:insert_position, %{date: _, latitude: 0.2, longitude: 0.2, speed: nil}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} refute_receive _ end @@ -46,7 +44,7 @@ defmodule TeslaMate.Vehicles.Vehicle.DrivingTest do @tag :capture_log test "handles a connection loss when driving", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, @@ -63,24 +61,22 @@ defmodule TeslaMate.Vehicles.Vehicle.DrivingTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} - assert_receive :start_drive_state - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 50}} - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 55}} - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 40}} - assert_receive :close_drive_state + assert_receive {:start_trip, 999} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 80.0, trip_id: 111}} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 89.0, trip_id: 111}} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 64.0, trip_id: 111}} + assert_receive {:close_trip, 111} - assert_receive {:insert_position, %{date: _, latitude: 0.2, longitude: 0.2, speed: nil}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} refute_receive _ end test "Transitions directly into driving state", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, @@ -89,14 +85,29 @@ defmodule TeslaMate.Vehicles.Vehicle.DrivingTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, %{date: ^now, latitude: 0.1, longitude: 0.1}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} - assert_receive :start_drive_state - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 0}} - assert_receive {:insert_position, %{date: _, latitude: 0.1, longitude: 0.1, speed: 0}} + assert_receive {:start_trip, 999} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 0.0, trip_id: 111}} + assert_receive {:insert_position, 999, %{longitude: 0.1, speed: 0.0, trip_id: 111}} # ... refute_received _ end + + test "shift state P does not trigger driving state", %{test: name} do + now = DateTime.utc_now() + now_ts = DateTime.to_unix(now, :millisecond) + + events = [ + {:ok, %TeslaApi.Vehicle{state: "online"}}, + {:ok, drive_event(now_ts, "P", 0)} + ] + + :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) + + assert_receive {:start_state, 999, :online} + + refute_receive _ + end end diff --git a/test/tesla_mate/vehicles/vehicle/suspend_test.exs b/test/tesla_mate/vehicles/vehicle/suspend_test.exs index 90215585..c9c52401 100644 --- a/test/tesla_mate/vehicles/vehicle/suspend_test.exs +++ b/test/tesla_mate/vehicles/vehicle/suspend_test.exs @@ -3,7 +3,7 @@ defmodule TeslaMate.Vehicles.Vehicle.SuspendTest do test "suspends after idling", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) suspendable = vehicle_full( @@ -27,13 +27,9 @@ defmodule TeslaMate.Vehicles.Vehicle.SuspendTest do suspend_min: suspend_ms ) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} - - assert_receive {:insert_position, %{date: _, latitude: 0.0, longitude: 0.0}} + assert_receive {:start_state, 999, :online} refute_receive _, sudpend_after_idle_ms + suspend_ms - 20 - - assert_receive {:start_state, :asleep} + assert_receive {:start_state, 999, :asleep} refute_receive _ end @@ -41,7 +37,7 @@ defmodule TeslaMate.Vehicles.Vehicle.SuspendTest do @tag :capture_log test "does not suspend if preconditioning", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) not_supendable = vehicle_full( @@ -63,8 +59,7 @@ defmodule TeslaMate.Vehicles.Vehicle.SuspendTest do suspend_min: suspend_ms ) - assert_receive {:insert_position, %{date: ^now, latitude: 0.0, longitude: 0.0}} - assert_receive {:start_state, :online} + assert_receive {:start_state, 999, :online} # Stays online refute_receive _, round(suspend_ms * 1.5) diff --git a/test/tesla_mate/vehicles/vehicle_test.exs b/test/tesla_mate/vehicles/vehicle_test.exs index ea6c1299..bb215462 100644 --- a/test/tesla_mate/vehicles/vehicle_test.exs +++ b/test/tesla_mate/vehicles/vehicle_test.exs @@ -3,19 +3,20 @@ defmodule TeslaMate.Vehicles.VehicleTest do describe "start" do @tag :capture_log - test "handles unkown state", %{test: name} do + test "handles unkown and faulty states", %{test: name} do events = [ - {:ok, %TeslaApi.Vehicle{state: "unknown"}} + {:ok, %TeslaApi.Vehicle{state: "unknown"}}, + {:error, %TeslaApi.Error{message: "boom"}} ] - :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) + :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0, vehicle_id: 1001}, events) refute_receive _ end test "handles online state", %{test: name} do now = DateTime.utc_now() - now_ts = DateTime.to_unix(now, :microsecond) + now_ts = DateTime.to_unix(now, :millisecond) events = [ {:ok, %TeslaApi.Vehicle{state: "online"}}, @@ -24,21 +25,21 @@ defmodule TeslaMate.Vehicles.VehicleTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:insert_position, - %{ - altitude: nil, - battery_level: nil, - date: ^now, - ideal_battery_range: nil, - latitude: 0.0, - longitude: 0.0, - odometer: nil, - outside_temp: nil, - power: nil, - speed: nil - }} + assert_receive {:start_state, 999, :online} - assert_receive {:start_state, :online} + # assert_receive {:insert_position, + # %{ + # altitude: nil, + # battery_level: nil, + # date: ^now, + # ideal_battery_range_km: nil, + # latitude: 0.0, + # longitude: 0.0, + # odometer: nil, + # outside_temp: nil, + # power: nil, + # speed: nil + # }} refute_receive _ end @@ -50,13 +51,9 @@ defmodule TeslaMate.Vehicles.VehicleTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:start_state, :offline} - assert_receive {:start_state, :offline} - assert_receive {:start_state, :offline} - assert_receive {:start_state, :offline} - # ... + assert_receive {:start_state, 999, :offline} - refute_received _ + refute_receive _ end test "handles asleep state", %{test: name} do @@ -66,7 +63,7 @@ defmodule TeslaMate.Vehicles.VehicleTest do :ok = start_vehicle(name, %TeslaApi.Vehicle{id: 0}, events) - assert_receive {:start_state, :asleep} + assert_receive {:start_state, 999, :asleep} refute_receive _ end diff --git a/test/tesla_mate_web/controllers/car_controller_test.exs b/test/tesla_mate_web/controllers/car_controller_test.exs new file mode 100644 index 00000000..a869ba75 --- /dev/null +++ b/test/tesla_mate_web/controllers/car_controller_test.exs @@ -0,0 +1,65 @@ +defmodule TeslaMateWeb.CarControllerTest do + use TeslaMateWeb.ConnCase + + alias TeslaMate.Log + alias TeslaMate.Log.Car + + @create_attrs %{ + efficiency: 120.5, + eid: 42, + model: "some model", + vid: 42 + } + @update_attrs %{ + efficiency: 456.7, + eid: 43, + model: "some updated model", + vid: 43 + } + @invalid_attrs %{efficiency: nil, eid: nil, model: nil, vid: nil} + + def fixture(:car) do + {:ok, car} = Log.create_car(@create_attrs) + car + end + + setup %{conn: conn} do + {:ok, conn: put_req_header(conn, "accept", "application/json")} + end + + describe "index" do + test "lists all car", %{conn: conn} do + conn = get(conn, Routes.car_path(conn, :index)) + assert json_response(conn, 200)["data"] == [] + end + end + + describe "update car" do + setup [:create_car] + + test "renders car when data is valid", %{conn: conn, car: %Car{id: id} = car} do + conn = put(conn, Routes.car_path(conn, :update, car), car: @update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["data"] + + conn = get(conn, Routes.car_path(conn, :show, id)) + + assert %{ + "id" => id, + "efficiency" => 456.7, + "model" => "some updated model", + "eid" => 42, + "vid" => 42 + } = json_response(conn, 200)["data"] + end + + test "renders errors when data is invalid", %{conn: conn, car: car} do + conn = put(conn, Routes.car_path(conn, :update, car), car: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + defp create_car(_) do + car = fixture(:car) + {:ok, car: car} + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 64623c84..844d07e9 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,3 +1,13 @@ +Application.load(:tesla_mate) + +for app <- Application.spec(:tesla_mate, :applications) do + {:ok, _} = Application.ensure_all_started(app) +end + +# :ok = Application.ensure_all_started(TeslaMate.Repo) + +TeslaMate.Repo.start_link() + ExUnit.start() # Ecto.Adapters.SQL.Sandbox.mode(TeslaMate.Repo, :manual)