119 lines
3.2 KiB
Elixir
119 lines
3.2 KiB
Elixir
defmodule Localiser.Domain.Sensors do
|
|
import Ecto.Query
|
|
|
|
alias Localiser.Repo
|
|
alias Localiser.Domain.Schema.Sensor
|
|
alias Localiser.Domain.Schema.SensorCalibration
|
|
|
|
def list_sensors do
|
|
Repo.all(Sensor)
|
|
end
|
|
|
|
def list_sensors_for_room(room_id) do
|
|
Sensor
|
|
|> where([s], s.room_id == ^room_id)
|
|
|> Repo.all()
|
|
end
|
|
|
|
def list_sensors_for_floor(floor_id) do
|
|
Sensor
|
|
|> join(:inner, [s], r in assoc(s, :room))
|
|
|> where([_s, r], r.floor_id == ^floor_id)
|
|
|> Repo.all()
|
|
end
|
|
|
|
def get_sensor!(id), do: Repo.get!(Sensor, id)
|
|
|
|
def get_sensor_by_sensor_id(sensor_id) do
|
|
Repo.get_by(Sensor, sensor_id: sensor_id)
|
|
end
|
|
|
|
def create_sensor(attrs) do
|
|
%Sensor{}
|
|
|> Sensor.changeset(attrs)
|
|
|> Repo.insert()
|
|
end
|
|
|
|
def update_sensor(%Sensor{} = sensor, attrs) do
|
|
sensor
|
|
|> Sensor.changeset(attrs)
|
|
|> Repo.update()
|
|
end
|
|
|
|
def delete_sensor(%Sensor{} = sensor) do
|
|
Repo.delete(sensor)
|
|
end
|
|
|
|
def enroll_sensor(%Sensor{} = sensor, room_id) do
|
|
sensor
|
|
|> Sensor.changeset(%{room_id: room_id})
|
|
|> Repo.update()
|
|
end
|
|
|
|
def add_calibration(%Sensor{} = sensor, attrs) do
|
|
attrs = Map.put(attrs, :sensor_id, sensor.id)
|
|
|
|
%SensorCalibration{}
|
|
|> SensorCalibration.changeset(attrs)
|
|
|> Repo.insert()
|
|
end
|
|
|
|
def list_calibrations(%Sensor{} = sensor) do
|
|
SensorCalibration
|
|
|> where([c], c.sensor_id == ^sensor.id)
|
|
|> order_by([c], desc: c.calibrated_at)
|
|
|> Repo.all()
|
|
end
|
|
|
|
def latest_calibration(%Sensor{} = sensor) do
|
|
SensorCalibration
|
|
|> where([c], c.sensor_id == ^sensor.id)
|
|
|> order_by([c], desc: c.calibrated_at)
|
|
|> limit(1)
|
|
|> Repo.one()
|
|
end
|
|
|
|
# Sensor lifecycle / enrollment helpers
|
|
|
|
# Called when an ESP board self-announces on MQTT. Inserts a new unplaced sensor
|
|
# record, or bumps updated_at if the sensor_id already exists.
|
|
def upsert_announced(sensor_id) do
|
|
result =
|
|
%Sensor{}
|
|
|> Sensor.changeset(%{sensor_id: sensor_id})
|
|
|> Repo.insert(
|
|
on_conflict: [set: [updated_at: DateTime.utc_now()]],
|
|
conflict_target: :sensor_id,
|
|
returning: true
|
|
)
|
|
|
|
case result do
|
|
{:ok, sensor} ->
|
|
Phoenix.PubSub.broadcast(Localiser.PubSub, "sensors", {:sensor_announced, sensor})
|
|
{:ok, sensor}
|
|
|
|
error ->
|
|
error
|
|
end
|
|
end
|
|
|
|
# Places (or re-places) a sensor at a specific position within a room.
|
|
# Broadcasts {:sensor_enrolled, sensor, room} for Sensor.Supervisor / Sensor.Server.
|
|
def place_sensor(%Sensor{} = sensor, room_id, {x, y}) do
|
|
with {:ok, sensor} <- update_sensor(sensor, %{room_id: room_id, x: x, y: y}) do
|
|
room = Repo.preload(sensor, :room).room
|
|
Phoenix.PubSub.broadcast(Localiser.PubSub, "sensors", {:sensor_enrolled, sensor, room})
|
|
{:ok, sensor}
|
|
end
|
|
end
|
|
|
|
# Removes a sensor from the room layout without deleting the DB record.
|
|
# Broadcasts {:sensor_unenrolled, sensor_id} for Sensor.Supervisor.
|
|
def remove_from_layout(%Sensor{} = sensor) do
|
|
with {:ok, sensor} <- update_sensor(sensor, %{room_id: nil, x: nil, y: nil}) do
|
|
Phoenix.PubSub.broadcast(Localiser.PubSub, "sensors", {:sensor_unenrolled, sensor.sensor_id})
|
|
{:ok, sensor}
|
|
end
|
|
end
|
|
end
|