diff --git a/config/runtime.exs b/config/runtime.exs index 6a7c95c..83628dc 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -77,6 +77,13 @@ config :geo_therminator, api_device_temp_set_reg_index: 3, api_device_reg_set_client_id: get_env("API_DEVICE_REG_SET_CLIENT_ID"), api_refresh: 10_000, + b2c_client_id: get_env("B2C_CLIENT_ID", "09ea4903-9e95-45fe-ae1f-e3b7d32fa385"), + b2c_redirect_url: get_env("B2C_REDIRECT_URL", "https://online-genesis.thermia.se/login"), + b2c_auth_url: + get_env( + "B2C_AUTH_URL", + "https://thermialogin.b2clogin.com/thermialogin.onmicrosoft.com/b2c_1a_signuporsigninonline" + ), # Database directory for settings db_dir: diff --git a/lib/geo_therminator/pump_api/auth/tokens.ex b/lib/geo_therminator/pump_api/auth/tokens.ex index af3f0d4..0d5da0a 100644 --- a/lib/geo_therminator/pump_api/auth/tokens.ex +++ b/lib/geo_therminator/pump_api/auth/tokens.ex @@ -8,11 +8,5 @@ defmodule GeoTherminator.PumpAPI.Auth.Tokens do :refresh_token_expiry ]) - @type t :: - record(:record, - access_token: String.t(), - access_token_expiry: DateTime.t(), - refresh_token: String.t(), - refresh_token_expiry: DateTime.t() - ) + @type t :: :pump_api@auth@tokens.tokens() end diff --git a/lib/geo_therminator/pump_api/auth/user.ex b/lib/geo_therminator/pump_api/auth/user.ex index a09d577..8886958 100644 --- a/lib/geo_therminator/pump_api/auth/user.ex +++ b/lib/geo_therminator/pump_api/auth/user.ex @@ -3,5 +3,5 @@ defmodule GeoTherminator.PumpAPI.Auth.User do Record.defrecord(:record, :user, [:tokens]) - @type t :: record(:record, tokens: GeoTherminator.PumpAPI.Auth.Tokens.t()) + @type t :: :pump_api@auth@user.user() end diff --git a/lib/geo_therminator/pump_api/device/api.ex b/lib/geo_therminator/pump_api/device/api.ex deleted file mode 100644 index 9436fb2..0000000 --- a/lib/geo_therminator/pump_api/device/api.ex +++ /dev/null @@ -1,154 +0,0 @@ -defmodule GeoTherminator.PumpAPI.Device.API do - require GeoTherminator.PumpAPI.Auth.InstallationInfo - - alias GeoTherminator.PumpAPI.HTTP - alias GeoTherminator.PumpAPI.Device - alias GeoTherminator.PumpAPI.Auth - - @spec device_info(Auth.User.t(), Auth.InstallationInfo.t()) :: Device.t() - def device_info(user, installation) do - url = - Application.get_env(:geo_therminator, :api_device_url) - |> String.replace("{id}", to_string(Auth.InstallationInfo.record(installation, :id))) - - req = HTTP.authed_req(user, :get, url) - - {:ok, response} = Finch.request(req, HTTP) - json = Jason.decode!(response.body) - - last_online = NaiveDateTime.from_iso8601!(json["lastOnline"]) - created_when = NaiveDateTime.from_iso8601!(json["createdWhen"]) - - %Device{ - id: json["id"], - device_id: json["deviceId"], - is_online: json["isOnline"], - last_online: last_online, - created_when: created_when, - mac_address: json["macAddress"], - name: json["name"], - model: json["model"], - retailer_access: json["retailerAccess"] - } - end - - @spec status(Auth.User.t(), Device.t()) :: Device.Status.t() - def status(user, device) do - url = - Application.get_env(:geo_therminator, :api_device_status_url) - |> String.replace("{id}", to_string(device.id)) - - req = HTTP.authed_req(user, :get, url) - - {:ok, response} = Finch.request(req, HTTP) - json = Jason.decode!(response.body) - - %Device.Status{ - heating_effect: json["heatingEffect"], - is_heating_effect_set_by_user: json["isHeatingEffectSetByUser"] - } - end - - @spec register_info(Auth.User.t(), Device.t()) :: Device.RegisterCollection.t() - def register_info(user, device) do - url = - Application.get_env(:geo_therminator, :api_device_register_url) - |> String.replace("{id}", to_string(device.id)) - - req = HTTP.authed_req(user, :get, url) - - {:ok, response} = Finch.request(req, HTTP) - json = Jason.decode!(response.body) - - registers = - Enum.map(json, fn item -> - {:ok, timestamp, _} = DateTime.from_iso8601(item["timeStamp"]) - - %Device.Register{ - register_name: item["registerName"], - register_value: item["registerValue"], - timestamp: timestamp - } - end) - - %Device.RegisterCollection{ - outdoor_temp: find_register(registers, "REG_OUTDOOR_TEMPERATURE"), - supply_out: find_register(registers, "REG_SUPPLY_LINE"), - supply_in: find_register(registers, "REG_OPER_DATA_RETURN"), - desired_supply: find_register(registers, "REG_DESIRED_SYS_SUPPLY_LINE_TEMP"), - brine_out: find_register(registers, "REG_BRINE_OUT"), - brine_in: find_register(registers, "REG_BRINE_IN"), - hot_water_temp: find_register(registers, "REG_HOT_WATER_TEMPERATURE") - } - end - - @spec opstat(Auth.User.t(), Device.t()) :: Device.OpStat.t() - def opstat(user, device) do - url = - Application.get_env(:geo_therminator, :api_device_opstat_url) - |> String.replace("{id}", to_string(device.id)) - - req = HTTP.authed_req(user, :get, url) - - {:ok, response} = Finch.request(req, HTTP) - json = Jason.decode!(response.body) - - registers = - Enum.map(json, fn item -> - {:ok, timestamp, _} = DateTime.from_iso8601(item["timeStamp"]) - - %Device.Register{ - register_name: item["registerName"], - register_value: item["registerValue"], - timestamp: timestamp - } - end) - - priority_register = find_register(registers, "REG_OPERATIONAL_STATUS_PRIO1") - - priority_register_fallback = - find_register(registers, "REG_OPERATIONAL_STATUS_PRIORITY_BITMASK") - - {mapping, register} = - if not is_nil(priority_register) do - {Application.get_env(:geo_therminator, :api_opstat_mapping), priority_register} - else - {Application.get_env(:geo_therminator, :api_opstat_bitmask_mapping), - priority_register_fallback} - end - - %Device.OpStat{ - priority: Map.get(mapping, register.register_value, :unknown) - } - end - - @spec set_temp(Auth.User.t(), Device.t(), integer()) :: :ok | {:error, String.t()} - def set_temp(user, device, temp) do - url = - Application.get_env(:geo_therminator, :api_device_reg_set_url) - |> String.replace("{id}", to_string(device.id)) - - register_index = Application.get_env(:geo_therminator, :api_device_temp_set_reg_index) - client_id = Application.get_env(:geo_therminator, :api_device_reg_set_client_id) - - req = - HTTP.authed_req(user, :post, url, [], %{ - registerIndex: register_index, - registerValue: temp, - clientUuid: client_id - }) - - IO.inspect(req) - {:ok, response} = Finch.request(req, HTTP) - - if response.status == 200 do - :ok - else - {:error, "Error #{response.status}: " <> response.body} - end - end - - defp find_register(registers, name) do - Enum.find(registers, &(&1.register_name == name)) - end -end diff --git a/lib/geo_therminator/pump_api/device/device.ex b/lib/geo_therminator/pump_api/device/device.ex index 9310a89..8dd8c1c 100644 --- a/lib/geo_therminator/pump_api/device/device.ex +++ b/lib/geo_therminator/pump_api/device/device.ex @@ -1,61 +1,50 @@ defmodule GeoTherminator.PumpAPI.Device do - import GeoTherminator.TypedStruct + require Record - deftypedstruct(%{ - id: integer(), - device_id: integer(), - is_online: boolean(), - last_online: NaiveDateTime.t(), - created_when: NaiveDateTime.t(), - mac_address: String.t(), - name: String.t(), - model: String.t(), - retailer_access: integer() - }) + Record.defrecord(:record, :device, [ + :id, + :device_id, + :is_online, + :last_online, + :created_when, + :mac_address, + :name, + :model, + :retailer_access + ]) + + @type t :: :pump_api@device.device() defmodule Status do - deftypedstruct(%{ - heating_effect: integer(), - is_heating_effect_set_by_user: boolean() - }) + Record.defrecord(:record, :status, [:heating_effect, :is_heating_effect_set_by_user]) + + @type t :: :pump_api@device.status() end defmodule Register do - deftypedstruct(%{ - register_name: String.t(), - register_value: integer(), - timestamp: DateTime.t() - }) + Record.defrecord(:record, :register, [:name, :value, :timestamp]) + + @type t :: :pump_api@device.register() end defmodule RegisterCollection do - deftypedstruct(%{ - outdoor_temp: Register.t(), - supply_out: Register.t(), - supply_in: Register.t(), - desired_supply: Register.t(), - brine_out: Register.t(), - brine_in: Register.t(), - hot_water_temp: Register.t() - }) + Record.defrecord(:record, :register_collection, [ + :outdoor_temp, + :supply_out, + :supply_in, + :desired_supply, + :brine_out, + :brine_in, + :hot_water_temp + ]) + + @type t :: :pump_api@device.register_collection() end defmodule OpStat do - deftypedstruct(%{ - priority: - :hand_operated - | :hot_water - | :heating - | :active_cooling - | :pool - | :anti_legionella - | :passive_cooling - | :standby - | :idle - | :off - | :defrost - | :unknown - }) + Record.defrecord(:record, :op_stat, [:priority]) + + @type t :: :pump_api@device.op_stat() end @spec get_device_process(GenServer.name(), GeoTherminator.PumpAPI.Auth.InstallationInfo.t()) :: diff --git a/lib/geo_therminator/pump_api/device/pub_sub.ex b/lib/geo_therminator/pump_api/device/pub_sub.ex index 56a8d63..f788efa 100644 --- a/lib/geo_therminator/pump_api/device/pub_sub.ex +++ b/lib/geo_therminator/pump_api/device/pub_sub.ex @@ -1,8 +1,10 @@ defmodule GeoTherminator.PumpAPI.Device.PubSub do require GeoTherminator.PumpAPI.Auth.InstallationInfo + require GeoTherminator.PumpAPI.Device alias Phoenix.PubSub alias GeoTherminator.PumpAPI.Auth.InstallationInfo + alias GeoTherminator.PumpAPI.Device @installation_topic "installation:" @@ -20,7 +22,7 @@ defmodule GeoTherminator.PumpAPI.Device.PubSub do :ok = PubSub.broadcast!( __MODULE__, - @installation_topic <> to_string(device.id), + @installation_topic <> to_string(Device.record(device, :id)), {:device, device} ) end @@ -33,7 +35,7 @@ defmodule GeoTherminator.PumpAPI.Device.PubSub do :ok = PubSub.broadcast!( __MODULE__, - @installation_topic <> to_string(device.id), + @installation_topic <> to_string(Device.record(device, :id)), {:status, status} ) end @@ -46,7 +48,7 @@ defmodule GeoTherminator.PumpAPI.Device.PubSub do :ok = PubSub.broadcast!( __MODULE__, - @installation_topic <> to_string(device.id), + @installation_topic <> to_string(Device.record(device, :id)), {:registers, registers} ) end @@ -59,7 +61,7 @@ defmodule GeoTherminator.PumpAPI.Device.PubSub do :ok = PubSub.broadcast!( __MODULE__, - @installation_topic <> to_string(device.id), + @installation_topic <> to_string(Device.record(device, :id)), {:opstat, opstat} ) end diff --git a/lib/geo_therminator/pump_api/device/server.ex b/lib/geo_therminator/pump_api/device/server.ex index c0466a4..bd98fb7 100644 --- a/lib/geo_therminator/pump_api/device/server.ex +++ b/lib/geo_therminator/pump_api/device/server.ex @@ -40,8 +40,7 @@ defmodule GeoTherminator.PumpAPI.Device.Server do @impl true def handle_continue({:init, installation}, state) do user = AuthServer.get_auth(state.auth_server) - device = Device.API.device_info(user, installation) - + {:ok, device} = :pump_api@device@api.device_info(user, installation) :ok = Device.PubSub.broadcast_device(device) state = @@ -75,7 +74,7 @@ defmodule GeoTherminator.PumpAPI.Device.Server do user = AuthServer.get_auth(state.auth_server) Logger.debug("Begin set temp to #{temp}") - resp = Device.API.set_temp(user, state.device, temp) + resp = :pump_api@device@api.set_temp(user, state.device, temp) Logger.debug("Set temp result: #{inspect(resp)}") {:reply, resp, state} end @@ -119,11 +118,15 @@ defmodule GeoTherminator.PumpAPI.Device.Server do [status, registers, opstat] = Task.async_stream( - [&Device.API.status/2, &Device.API.register_info/2, &Device.API.opstat/2], + [ + &:pump_api@device@api.status/2, + &:pump_api@device@api.register_info/2, + &:pump_api@device@api.opstat/2 + ], & &1.(user, state.device), timeout: Application.fetch_env!(:geo_therminator, :api_timeout) ) - |> Enum.map(fn {:ok, val} -> val end) + |> Enum.map(fn {:ok, {:ok, val}} -> val end) Device.PubSub.broadcast_status(state.device, status) Device.PubSub.broadcast_registers(state.device, registers) diff --git a/lib/geo_therminator_web/live/components/main_view.ex b/lib/geo_therminator_web/live/components/main_view.ex index 474d246..4547638 100644 --- a/lib/geo_therminator_web/live/components/main_view.ex +++ b/lib/geo_therminator_web/live/components/main_view.ex @@ -1,19 +1,51 @@ defmodule GeoTherminatorWeb.Components.MainView do + require GeoTherminator.PumpAPI.Device + require GeoTherminator.PumpAPI.Device.Status + require GeoTherminator.PumpAPI.Device.RegisterCollection + require GeoTherminator.PumpAPI.Device.OpStat + require GeoTherminator.PumpAPI.Device.Register + use GeoTherminatorWeb, :live_component + alias GeoTherminator.PumpAPI.Device + @impl true def update(assigns, socket) do {:ok, assign(socket, - set_temp: assigns.status.heating_effect, - set_temp_active: assigns.status.is_heating_effect_set_by_user, - hot_water_temp: assigns.registers.hot_water_temp.register_value, - brine_out: assigns.registers.brine_out.register_value, - brine_in: assigns.registers.brine_in.register_value, - supply_out: assigns.registers.supply_out.register_value, - supply_in: assigns.registers.supply_in.register_value, - outdoor_temp: assigns.registers.outdoor_temp.register_value, - priority: assigns.opstat.priority + set_temp: Device.Status.record(assigns.status, :heating_effect), + set_temp_active: Device.Status.record(assigns.status, :is_heating_effect_set_by_user), + hot_water_temp: + Device.Register.record( + Device.RegisterCollection.record(assigns.registers, :hot_water_temp), + :value + ), + brine_out: + Device.Register.record( + Device.RegisterCollection.record(assigns.registers, :brine_out), + :value + ), + brine_in: + Device.Register.record( + Device.RegisterCollection.record(assigns.registers, :brine_in), + :value + ), + supply_out: + Device.Register.record( + Device.RegisterCollection.record(assigns.registers, :supply_out), + :value + ), + supply_in: + Device.Register.record( + Device.RegisterCollection.record(assigns.registers, :supply_in), + :value + ), + outdoor_temp: + Device.Register.record( + Device.RegisterCollection.record(assigns.registers, :outdoor_temp), + :value + ), + priority: Device.OpStat.record(assigns.opstat, :priority) )} end end diff --git a/lib/geo_therminator_web/live/main/device_list.ex b/lib/geo_therminator_web/live/main/device_list.ex index ee20248..2b1919c 100644 --- a/lib/geo_therminator_web/live/main/device_list.ex +++ b/lib/geo_therminator_web/live/main/device_list.ex @@ -1,5 +1,7 @@ defmodule GeoTherminatorWeb.MainLive.DeviceList do + require GeoTherminator.PumpAPI.Auth.InstallationInfo use GeoTherminatorWeb, :live_view + alias GeoTherminator.PumpAPI.Auth.InstallationInfo @impl Phoenix.LiveView def mount(_params, _session, socket) do diff --git a/lib/geo_therminator_web/live/main/device_list.html.heex b/lib/geo_therminator_web/live/main/device_list.html.heex index 51cba2a..22ab26a 100644 --- a/lib/geo_therminator_web/live/main/device_list.html.heex +++ b/lib/geo_therminator_web/live/main/device_list.html.heex @@ -8,8 +8,14 @@