geo-therminator/lib/geo_therminator/pump_api/device/server.ex

140 lines
3.7 KiB
Elixir
Raw Normal View History

2021-11-07 09:01:39 +00:00
defmodule GeoTherminator.PumpAPI.Device.Server do
use GenServer
import GeoTherminator.TypedStruct
alias GeoTherminator.PumpAPI.Device
alias GeoTherminator.PumpAPI.Auth.InstallationInfo
alias GeoTherminator.PumpAPI.Auth.Server, as: AuthServer
require Logger
defmodule Options do
deftypedstruct(%{
auth_server: GenServer.name(),
installation: InstallationInfo.t()
})
end
defmodule State do
deftypedstruct(%{
auth_server: GenServer.name(),
device: {Device.t() | nil, nil},
status: {Device.Status.t() | nil, nil},
registers: {Device.RegisterCollection.t() | nil, nil},
opstat: {Device.OpStat.t() | nil, nil}
})
end
@spec start_link(Options.t()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts,
name: {:via, Registry, {Device.Registry, opts.installation.id}}
)
end
@impl true
def init(%Options{} = opts) do
{:ok, %State{auth_server: opts.auth_server}, {:continue, {:init, opts.installation}}}
end
@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.PubSub.broadcast_device(device)
state =
%State{state | device: device}
|> refresh_status()
schedule_status()
{:noreply, state}
end
@impl true
def handle_call(msg, from, state)
def handle_call(:get_device, _from, state) do
{:reply, state.device, state}
end
def handle_call(:get_status, _from, state) do
{:reply, state.status, state}
end
def handle_call(:get_registers, _from, state) do
{:reply, state.registers, state}
end
def handle_call(:get_opstat, _from, state) do
{:reply, state.opstat, state}
end
def handle_call({:set_temp, temp}, _from, state) do
user = AuthServer.get_auth(state.auth_server)
Logger.debug("Begin set temp to #{temp}")
resp = Device.API.set_temp(user, state.device, temp)
Logger.debug("Set temp result: #{inspect(resp)}")
{:reply, resp, state}
end
@impl true
def handle_info(msg, state)
def handle_info(:refresh_status, state) do
state = refresh_status(state)
schedule_status()
{:noreply, state}
end
@spec get_device(GenServer.name()) :: Device.t()
def get_device(server) do
GenServer.call(server, :get_device)
end
@spec get_status(GenServer.name()) :: Device.Status.t()
def get_status(server) do
GenServer.call(server, :get_status)
end
@spec get_registers(GenServer.name()) :: Device.RegisterCollection.t()
def get_registers(server) do
GenServer.call(server, :get_registers)
end
@spec get_opstat(GenServer.name()) :: Device.OpStat.t()
def get_opstat(server) do
GenServer.call(server, :get_opstat)
end
@spec set_temp(GenServer.name(), integer()) :: :ok | {:error, String.t()}
def set_temp(server, temp) do
GenServer.call(server, {:set_temp, temp})
end
defp refresh_status(state) do
user = AuthServer.get_auth(state.auth_server)
[status, registers, opstat] =
Task.async_stream(
[&Device.API.status/2, &Device.API.register_info/2, &Device.API.opstat/2],
2022-09-13 04:53:56 +00:00
& &1.(user, state.device),
timeout: Application.get_env(:geo_therminator, :api_timeout)
2021-11-07 09:01:39 +00:00
)
|> Enum.map(fn {:ok, val} -> val end)
Device.PubSub.broadcast_status(state.device, status)
Device.PubSub.broadcast_registers(state.device, registers)
Device.PubSub.broadcast_opstat(state.device, opstat)
%State{state | status: status, registers: registers, opstat: opstat}
end
defp schedule_status() do
Process.send_after(
self(),
:refresh_status,
Application.get_env(:geo_therminator, :api_refresh)
)
end
end