defmodule LambentExWeb.BrightnessLive.Index do
use LambentExWeb, :live_view
alias LambentEx.Brightness
alias LambentEx.Utils.Brightness, as: B
@pubsub_name LambentEx.PubSub
@impl true
def mount(_params, _session, socket) do
Phoenix.PubSub.subscribe(@pubsub_name, "brightness")
Phoenix.PubSub.subscribe(@pubsub_name, "scan-82667777")
snap = Brightness.snapshot()
{:ok,
socket
|> assign(:global, snap.global)
|> assign(:places, snap.places)
|> assign(:devices, %{})
|> assign(:nonew, true)}
end
@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_link, "Brightness")
|> assign(:page_title, "Brightness Clamps")
|> assign(:source, nil)
end
@impl true
def handle_info({:brightness_pub, %{global: global, places: places}}, socket) do
{:noreply, socket |> assign(:global, global) |> assign(:places, places)}
end
@impl true
def handle_info({:devices_pub, devices}, socket) do
{:noreply, socket |> assign(:devices, socket.assigns.devices |> Map.merge(devices))}
end
@impl true
def handle_info(_msg, socket), do: {:noreply, socket}
@impl true
def handle_event("brighter", %{"place" => place}, socket) do
Brightness.brighter(place)
{:noreply, socket}
end
@impl true
def handle_event("brighter", _params, socket) do
Brightness.brighter()
{:noreply, socket}
end
@impl true
def handle_event("dimmer", %{"place" => place}, socket) do
Brightness.dimmer(place)
{:noreply, socket}
end
@impl true
def handle_event("dimmer", _params, socket) do
Brightness.dimmer()
{:noreply, socket}
end
# the slider runs over ladder positions, not raw 0..255, so it can only ever
# land on one of the named brightness steps
@impl true
def handle_event("set", %{"place" => place, "rung" => rung}, socket) do
Brightness.set_place(place, rung |> to_val() |> B.at())
{:noreply, socket}
end
@impl true
def handle_event("set", %{"rung" => rung}, socket) do
Brightness.set_global(rung |> to_val() |> B.at())
{:noreply, socket}
end
@impl true
def handle_event("rate", %{"place" => place, "rate" => rate}, socket) do
Brightness.set_place_rate(place, to_val(rate))
{:noreply, socket}
end
@impl true
def handle_event("rate", %{"rate" => rate}, socket) do
Brightness.set_global_rate(to_val(rate))
{:noreply, socket}
end
@impl true
def handle_event("clear", %{"place" => place}, socket) do
Brightness.clear_place(place)
{:noreply, socket}
end
defp to_val(val) do
case Integer.parse(val) do
{v, _rest} -> v
:error -> 0
end
end
# the named fade durations, quickest first
defp fades do
B.fades() |> Enum.sort_by(fn {rate, _name} -> rate end)
end
# components
attr :walk, :map, required: true
attr :place, :string, default: nil
@doc """
The target on top, driven, and the walk's actual position underneath it,
disabled -- the lower slider chases the upper one at the clamp's fade rate.
Both run over ladder positions rather than raw brightness: the target snaps
to a rung, and the rungs get even spacing they do not have in 0..255. The
lower slider uses a hundred sub-steps per rung so a walk between two of them
moves smoothly and still lines up with the handle above it.
"""
def clamp_sliders(assigns) do
~H"""
<form phx-change="set">
<input :if={@place} type="hidden" name="place" value={@place} />
<input
type="range"
name="rung"
min="0"
max={B.rungs() - 1}
step="1"
value={B.index(@walk.tgt)}
phx-debounce="100"
class="range range-sm"
/>
</form>
<input
type="range"
min="0"
max={(B.rungs() - 1) * 100}
value={(B.fraction(@walk.curr) * 100) |> round}
disabled
class="range range-xs opacity-60"
/>
<div class="text-sm opacity-60">
<%= @walk.curr |> pct %>%
<%= if @walk.curr != @walk.tgt do %>
<i class="fa-solid fa-fw fa-caret-right"></i>
<% end %>
<span class="badge badge-sm"><%= B.name(@walk.tgt) || @walk.tgt %></span>
</div>
"""
end
# functions
# every place a device claims, plus any that carry a clamp but have no device
# answering right now
defp places(devices, clamped) do
devices
|> Map.values()
|> Enum.map(fn d -> d["place"] end)
|> Enum.concat(Map.keys(clamped))
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
|> Enum.sort()
end
defp walk(clamped, place) do
Map.get(clamped, place, %{tgt: B.full(), curr: B.full(), rate: B.default_fade()})
end
defp devices_in(devices, place) do
devices |> Map.values() |> Enum.count(fn d -> d["place"] == place end)
end
defp pct(val), do: B.pct(val)
end
neiam
/lambent_ex
Lambent Aether 5
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
4.9 KiB · text
History
9c0fc16