Packages

Watch notification bridge for Raxol. Pushes glanceable summaries and accessibility announcements to Apple Watch (APNS) and Wear OS (FCM). Tap actions route back as events to the TEA app.

Current section

Files

Jump to
raxol_watch lib raxol watch device_registry.ex
Raw

lib/raxol/watch/device_registry.ex

defmodule Raxol.Watch.DeviceRegistry do
@moduledoc """
ETS-backed registry of watch devices for push notification targeting.
Stores device tokens with their platform (`:apns` or `:fcm`) and
per-device preferences (muted, high-priority-only, badge count).
Writes go through the GenServer for serialization. Reads hit ETS
directly for performance (`:protected` table with `read_concurrency`).
"""
use GenServer
@table __MODULE__
defstruct []
def start_link(_opts \\ []) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@doc """
Registers a device for push notifications.
## Options
* `:muted` - suppress all pushes (default: false)
* `:high_priority_only` - only push high-priority alerts (default: false)
"""
@spec register(String.t(), :apns | :fcm, keyword()) :: :ok
def register(device_token, platform, opts \\ []) when platform in [:apns, :fcm] do
GenServer.call(__MODULE__, {:register, device_token, platform, opts})
end
@doc "Unregisters a device."
@spec unregister(String.t()) :: :ok
def unregister(device_token) do
GenServer.call(__MODULE__, {:unregister, device_token})
end
@doc "Lists all registered devices."
@spec list_devices() :: [{String.t(), :apns | :fcm, map()}]
def list_devices do
:ets.tab2list(@table)
end
@doc "Lists devices filtered by platform."
@spec list_devices(:apns | :fcm) :: [{String.t(), :apns | :fcm, map()}]
def list_devices(platform) when platform in [:apns, :fcm] do
:ets.match_object(@table, {:_, platform, :_})
end
@doc "Returns the number of registered devices."
@spec device_count() :: non_neg_integer()
def device_count do
:ets.info(@table, :size)
end
# -- GenServer --
@impl true
def init(_opts) do
case :ets.whereis(@table) do
:undefined ->
:ets.new(@table, [:named_table, :set, :protected, read_concurrency: true])
_ref ->
:ok
end
{:ok, %__MODULE__{}}
end
@impl true
def handle_call({:register, device_token, platform, opts}, _from, state) do
prefs = %{
muted: Keyword.get(opts, :muted, false),
high_priority_only: Keyword.get(opts, :high_priority_only, false)
}
:ets.insert(@table, {device_token, platform, prefs})
{:reply, :ok, state}
end
def handle_call({:unregister, device_token}, _from, state) do
:ets.delete(@table, device_token)
{:reply, :ok, state}
end
end