Skip to content

Commit 4cafb11

Browse files
author
Frank Hunleth
committed
Support an initial LED configuration
1 parent cb9000b commit 4cafb11

2 files changed

Lines changed: 133 additions & 11 deletions

File tree

lib/delux.ex

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ defmodule Delux do
99
alias Delux.Pattern
1010
alias Delux.Program
1111

12+
require Logger
13+
1214
@default_slot :status
1315
@default_slots [:status, :notification, :user_feedback]
1416

@@ -60,13 +62,19 @@ defmodule Delux do
6062
* `:backend` - options for the backend
6163
* `:led_path` - the path to the LED directories (defaults to `"/sys/class/leds"`)
6264
* `:hz` - the Linux kernel's `HZ` setting. Delux will adjust its timing based on this setting (defaults to 1000)
65+
* `:initial` - a program or a map of indicators to programs to run on initialization. If
66+
unset, then Delux turns off all indicators on initialization.
6367
"""
6468
@type options() :: [
6569
led_path: String.t(),
6670
slots: [slot()],
6771
indicators: %{indicator_name() => indicator_config()},
6872
name: atom() | nil,
69-
backend: keyword()
73+
backend: keyword(),
74+
initial:
75+
Program.t()
76+
| {Program.t(), slot()}
77+
| {%{indicator_name() => indicator_config()}, slot()}
7078
]
7179

7280
@doc """
@@ -226,18 +234,22 @@ defmodule Delux do
226234
slots = options[:slots] || options[:priorities] || @default_slots
227235
indicator_configs = options[:indicators] || @default_indicator_config
228236
backend_config = options[:backend] || []
237+
initial = options[:initial]
229238

230-
state = %{
231-
indicator_names: Map.keys(indicator_configs),
232-
backend: open_indicators(backend_config, indicator_configs),
233-
slot_to_priority: slots |> Enum.reverse() |> Enum.with_index() |> Map.new(),
234-
active: [],
235-
brightness: 100,
236-
current: %{},
237-
refresh_time: :infinity
238-
}
239+
state =
240+
%{
241+
indicator_names: Map.keys(indicator_configs),
242+
backend: open_indicators(backend_config, indicator_configs),
243+
slot_to_priority: slots |> Enum.reverse() |> Enum.with_index() |> Map.new(),
244+
active: [],
245+
brightness: 100,
246+
current: %{},
247+
refresh_time: :infinity
248+
}
249+
|> initialize_indicators(initial)
250+
|> refresh_indicators()
239251

240-
{:ok, refresh_indicators(state)}
252+
{:ok, state}
241253
end
242254

243255
@impl GenServer
@@ -383,6 +395,36 @@ defmodule Delux do
383395
[entry | pop_entry(rest, indicator_name)]
384396
end
385397

398+
# initialize_indicators takes options that are analogous to those passed to render/3
399+
defp initialize_indicators(state, nil) do
400+
state
401+
end
402+
403+
defp initialize_indicators(state, %Program{} = program) do
404+
initialize_indicators(state, {%{@default_indicator => program}, @default_slot})
405+
end
406+
407+
defp initialize_indicators(state, {%Program{} = program, slot}) when is_atom(slot) do
408+
initialize_indicators(state, {%{@default_indicator => program}, slot})
409+
end
410+
411+
defp initialize_indicators(state, {indicators, slot})
412+
when is_map(indicators) and is_atom(slot) do
413+
case do_render(state, slot, indicators) do
414+
{:ok, new_state} ->
415+
new_state
416+
417+
error ->
418+
Logger.error("Error initializing indicators to #{inspect(indicators)}: #{inspect(error)}")
419+
state
420+
end
421+
end
422+
423+
defp initialize_indicators(state, other) do
424+
Logger.error("Don't know how to initialize indicators to #{inspect(other)}")
425+
state
426+
end
427+
386428
defp refresh_indicators(state) do
387429
current_time = System.monotonic_time(:millisecond)
388430

test/delux_test.exs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
defmodule DeluxTest do
22
use ExUnit.Case, async: true
3+
import ExUnit.CaptureLog
34

45
alias Delux.Support.FakeLEDs
56

@@ -206,6 +207,85 @@ defmodule DeluxTest do
206207
assert FakeLEDs.read_pattern(5) == "0 3600000 0 0 "
207208
end
208209

210+
@tag :tmp_dir
211+
test "starting with a simple initial configuration", %{tmp_dir: led_dir} do
212+
FakeLEDs.create_leds(led_dir, 1)
213+
214+
pid =
215+
start_supervised!(
216+
{Delux,
217+
name: nil,
218+
backend: [led_path: led_dir, hz: 0],
219+
indicators: %{default: %{green: "led0"}},
220+
initial: Delux.Effects.blink(:green, 2)}
221+
)
222+
223+
# Check that the LEDS were initialized
224+
assert info_as_binary(pid) == "green at 2 Hz"
225+
assert FakeLEDs.read_pattern(0) == "1 250 1 0 0 250 0 0 "
226+
end
227+
228+
@tag :tmp_dir
229+
test "starting with a simple initial configuration in a slot", %{tmp_dir: led_dir} do
230+
FakeLEDs.create_leds(led_dir, 1)
231+
232+
pid =
233+
start_supervised!(
234+
{Delux,
235+
name: nil,
236+
backend: [led_path: led_dir, hz: 0],
237+
indicators: %{default: %{green: "led0"}},
238+
initial: {Delux.Effects.blink(:green, 4), :notification}}
239+
)
240+
241+
# Check that the LEDS were initialized
242+
assert info_as_binary(pid) == "green at 4 Hz"
243+
assert FakeLEDs.read_pattern(0) == "1 125 1 0 0 125 0 0 "
244+
245+
# Check that clearing the slot turns it off (it's in the right slot)
246+
Delux.clear(pid, :notification)
247+
assert info_as_binary(pid) == "off"
248+
assert FakeLEDs.read_pattern(0) == "0 3600000 0 0 "
249+
end
250+
251+
@tag :tmp_dir
252+
test "starting with a full initial configuration in a slot", %{tmp_dir: led_dir} do
253+
FakeLEDs.create_leds(led_dir, 1)
254+
255+
pid =
256+
start_supervised!(
257+
{Delux,
258+
name: nil,
259+
backend: [led_path: led_dir, hz: 0],
260+
indicators: %{default: %{green: "led0"}},
261+
initial: {%{default: Delux.Effects.blink(:green, 3)}, :notification}}
262+
)
263+
264+
# Check that the LEDS were initialized
265+
assert info_as_binary(pid) == "green at 3 Hz"
266+
assert FakeLEDs.read_pattern(0) == "1 166 1 0 0 167 0 0 "
267+
268+
# Check that clearing the slot turns it off (it's in the right slot)
269+
Delux.clear(pid, :notification)
270+
assert info_as_binary(pid) == "off"
271+
assert FakeLEDs.read_pattern(0) == "0 3600000 0 0 "
272+
end
273+
274+
@tag :tmp_dir
275+
test "starting a bad initial configuration logs an error", %{tmp_dir: led_dir} do
276+
FakeLEDs.create_leds(led_dir, 1)
277+
278+
assert capture_log(fn ->
279+
start_supervised!(
280+
{Delux,
281+
name: nil,
282+
backend: [led_path: led_dir, hz: 0],
283+
indicators: %{default: %{green: "led0"}},
284+
initial: "hello"}
285+
)
286+
end) =~ "Don't know how to initialize indicators to \"hello\""
287+
end
288+
209289
@tag :tmp_dir
210290
test "render raises on unknown slots", %{tmp_dir: led_dir} do
211291
FakeLEDs.create_leds(led_dir, 1)

0 commit comments

Comments
 (0)