switch mp3 to tts, add genserver to app supervisor tree that periodically checks for newly connected devices and plays their associated tts message
This commit is contained in:
@@ -16,6 +16,7 @@ defmodule HereIAm.Application do
|
||||
{Finch, name: HereIAm.Finch},
|
||||
# Start a worker by calling: HereIAm.Worker.start_link(arg)
|
||||
# {HereIAm.Worker, arg},
|
||||
HereIAm.DeviceMonitor,
|
||||
# Start to serve requests, typically the last entry
|
||||
HereIAmWeb.Endpoint
|
||||
]
|
||||
|
71
lib/here_i_am/device_monitor.ex
Normal file
71
lib/here_i_am/device_monitor.ex
Normal file
@@ -0,0 +1,71 @@
|
||||
defmodule HereIAm.DeviceMonitor do
|
||||
use GenServer
|
||||
|
||||
alias HereIAm.Devices
|
||||
|
||||
def start_link(_opts) do
|
||||
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(state) do
|
||||
schedule_check()
|
||||
{:ok, Map.put(state, :connected_ips, %{})}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:check_devices, state) do
|
||||
connected_ips = get_connected_ips()
|
||||
|
||||
# Determine new devices and TTS
|
||||
new_devices = MapSet.difference(MapSet.new(connected_ips), MapSet.new(Map.keys(state.connected_ips)))
|
||||
Enum.each(new_devices, fn ip ->
|
||||
case Devices.get_device_by_ip(ip) do
|
||||
{:ok, device} -> play_message(device)
|
||||
{:error, _reason} -> :ok
|
||||
end
|
||||
end)
|
||||
|
||||
# Update the registry
|
||||
new_registry = Map.new(connected_ips, fn ip -> {ip, :connected} end)
|
||||
schedule_check()
|
||||
{:noreply, %{state | connected_ips: new_registry}}
|
||||
end
|
||||
|
||||
defp schedule_check() do
|
||||
Process.send_after(self(), :check_devices, 10_000) # Check every 60 seconds
|
||||
end
|
||||
|
||||
defp get_connected_ips() do
|
||||
{output, 0} = System.cmd("sudo", ["arp-scan", "-l", "--localnet"])
|
||||
parse_ips_from_arp_scan(output)
|
||||
end
|
||||
|
||||
defp parse_ips_from_arp_scan(output) do
|
||||
output
|
||||
|> String.split("\n")
|
||||
|> Enum.filter(&String.contains?(&1, "\t")) # Filter lines containing tabs (IP-MAC pairs)
|
||||
|> Enum.map(&String.split(&1, "\t"))
|
||||
|> Enum.map(&List.first(&1))
|
||||
|> Enum.filter(&valid_ip?/1)
|
||||
end
|
||||
|
||||
defp valid_ip?(ip) do
|
||||
case :inet.parse_address(to_charlist(ip)) do
|
||||
{:ok, _} -> true
|
||||
{:error, _} -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp play_message(%{tts: tts_message}) do
|
||||
cond do
|
||||
not is_nil(tts_message) and tts_message != "" -> play_tts(tts_message)
|
||||
true -> IO.puts("No audio or TTS message to play.")
|
||||
end
|
||||
end
|
||||
|
||||
defp play_tts(tts_message) do
|
||||
IO.puts("Playing TTS message: #{tts_message}")
|
||||
System.cmd("espeak", [tts_message])
|
||||
end
|
||||
end
|
@@ -101,4 +101,11 @@ defmodule HereIAm.Devices do
|
||||
def change_device(%Device{} = device, attrs \\ %{}) do
|
||||
Device.changeset(device, attrs)
|
||||
end
|
||||
|
||||
def get_device_by_ip(ip_address) do
|
||||
case Repo.get_by(Device, ip_address: ip_address) do
|
||||
nil -> {:error, :not_found}
|
||||
device -> {:ok, device}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -4,7 +4,7 @@ defmodule HereIAm.Devices.Device do
|
||||
|
||||
schema "devices" do
|
||||
field :ip_address, :string
|
||||
field :audio, :string
|
||||
field :tts, :string
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
@@ -12,7 +12,7 @@ defmodule HereIAm.Devices.Device do
|
||||
@doc false
|
||||
def changeset(device, attrs) do
|
||||
device
|
||||
|> cast(attrs, [:ip_address, :audio])
|
||||
|> validate_required([:ip_address, :audio])
|
||||
|> cast(attrs, [:ip_address, :tts])
|
||||
|> validate_required([:ip_address, :tts])
|
||||
end
|
||||
end
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="csrf-token" content={get_csrf_token()} />
|
||||
<.live_title suffix=" · Phoenix Framework">
|
||||
<%= assigns[:page_title] || "HereIAm" %>
|
||||
<%= assigns[:page_title] || "Here I Am" %>
|
||||
</.live_title>
|
||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||
|
@@ -3,10 +3,10 @@
|
||||
<div class="container mx-auto text-center py-10">
|
||||
<article class="prose mx-auto px-4">
|
||||
<p>
|
||||
This site allows you to set an MP3 to play when your device connects to the network.
|
||||
This site allows you to set a message to play when your device connects to the network.
|
||||
</p>
|
||||
<a href="/devices">
|
||||
<button class="btn btn-primary mt-5">Manage Devices</button>
|
||||
<.button class="mt-5">Manage Devices</.button>
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
@@ -20,7 +20,7 @@ defmodule HereIAmWeb.DeviceLive.FormComponent do
|
||||
phx-submit="save"
|
||||
>
|
||||
<.input field={@form[:ip_address]} type="text" label="IP Address" />
|
||||
<.input field={@form[:audio]} type="text" label="Audio" />
|
||||
<.input field={@form[:tts]} type="text" label="tts" />
|
||||
<:actions>
|
||||
<.button phx-disable-with="Saving...">Save Device</.button>
|
||||
</:actions>
|
||||
|
@@ -13,7 +13,7 @@
|
||||
row_click={fn {_id, device} -> JS.navigate(~p"/devices/#{device}") end}
|
||||
>
|
||||
<:col :let={{_id, device}} label="IP Address"><%= device.ip_address %></:col>
|
||||
<:col :let={{_id, device}} label="Audio"><%= device.audio %></:col>
|
||||
<:col :let={{_id, device}} label="TTS"><%= device.tts %></:col>
|
||||
<:action :let={{_id, device}}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/devices/#{device}"}>Show</.link>
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
<.list>
|
||||
<:item title="IP Address"><%= @device.ip_address %></:item>
|
||||
<:item title="Audio"><%= @device.audio %></:item>
|
||||
<:item title="TTS"><%= @device.tts %></:item>
|
||||
</.list>
|
||||
|
||||
<.back navigate={~p"/devices"}>Back to devices</.back>
|
||||
|
Reference in New Issue
Block a user