Files
localiserd/lib/localiser/domain/sensors.ex
T
2026-04-16 15:46:00 +02:00

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