Fledex (fledex v0.6.0)

View Source

This module should provide some simple macros that allow to define the led strip and to update it. The code you would write (in livebook) would look something like the following:

  use Fledex
  led_strip :strip_name, Kino do
    animation :john do
      config = [
        num_leds: 50,
        reversed: true
      ]

      leds(50)
        |> rainbow(config)
    end
  end

Summary

Functions

By use-ing this module, the Fledex macros are made available.

This introduces a new animation that will be played over and over again until it is changed.

A component is a pre-defined animation that reacts to some input. We might have a thermometer component that defines the display of a thermometer

This introduces a new coordinator.

Add an effect to an animation

A job is a cron job that will trigger in regular intervals (depending on the pattern specified). You can run any function and the most likely event you will trigger is to publish an event to the triggers (see the weather example livebook)

The static macro is equal to the animation macro, but it will not receive any triggers.

Returns the version of the Fledex library. It can be important to know the version in order to adjust some code depending on the version

Functions

__using__(opts)

(macro)
@spec __using__(keyword()) :: Macro.t()

By use-ing this module, the Fledex macros are made available.

This macro does also import Fledex.Leds, Fledex.Utils.PubSub, Fledex.Color.Names, and Crontab.CronExpression. Therefore the functions from those modules are directly available without namespace, the drivers part of the FledexDriver.Impl namespace are all aliased.

Note: this could lead to a conflict with other libraries (like the Kino-driver with the Kino-library). In that case just use the fully qualified module name and prefix it even with Elixir., i.e. Elixir.Kino if you want to use the Kino-library.

Take a look at the various livebook examples for more details on how to use the Fledex library and macros.

When calling use Fledex you can specify a couple of options:

  • :dont_start: This will prevent from the Fledex.Supervisor.AnimationSystem from being started. Note:: this will not stop the AnimationSystem if it was already started by someone else.
  • :supervisor: specifies how we want to supervise it. The options are: :none: We will start the Fledex.Supervisor.AnimationSystem but without hanging in a supervision tree (the default) :app: We add the Fledex.Supervisor.AnimationSystem to the application supervisr. You need to ensure that you have started the fledex application (done automatically if you run iex -S mix from the fledex project) :kino: The Fledex.Supervisor.AnimationSystem will be added to the Kino session supervisor. The AnimationSystem will terminate when the livebook session terminates. {:dynamic, name}: The Fledex.Supervisor.AnimationSystem will be added as a child process to the DynamicSupervisor with the given name.
  • :log_level: specifies the log level if none is already specified. This is important if Fledex is not started as application

animation(name, options \\ nil, list)

(macro)

This introduces a new animation that will be played over and over again until it is changed.

Therefore we give it a name to know whether it changes. The do ... end block needs to define a function. This function receives a trigger as argument, but you have two possbilities to implement it.

  • Either you pattern match on the triggers, e.g. something like the following:
    led_strip :strip, Kino do
    animation :name do
      %{strip: counter} ->
        do_something_with_the_counter(counter)
      triggers ->
        # During init it can happen that the strip trigger is not available yet
        do_something_during init_phase(triggers)
    end
    end
  • Or, if you don't require the trigger, you can specify it without a trigger, e.g.
    led_strip :strip, Kino do
    animation :name do
      do_something_without_a_trigger()
    end
    end

component(name, module, opts)

@spec component(atom(), module(), keyword()) :: Fledex.Animation.Manager.config_t()

A component is a pre-defined animation that reacts to some input. We might have a thermometer component that defines the display of a thermometer:

  • input: single value
  • display is a range (positive, 0, negative)
  • ...

A component does not have a do ... end block, since it defines it's own animation(s), and it's only controlled through some parameters that can be passed as options like:

  • the value,
  • the display colors,
  • the range of our scale

Thus, our component would look like the following:

  alias Fledex.Component.Thermometer
  component :thermo, Thermometer,
    range: -20..40,
    trigger: :temperature,
    negative: :blue,
    null: :may_green,
    positive: :red

It is up to each component to define their own set of mandatory and optional parameters.

coordinator(name, options \\ [], list)

(macro)

This introduces a new coordinator.

A coordinator is a component that receives events from the different animations and effects and can react to them (e.g. enabling or disabling animations and effects).

Each coordinator is identified by a name and implements a state machine in its do ... end block. Probably the best way to do this is through pattern matching. On the broadcastet state, the context (information on who emitted it) and some coordinator state.

effect(module, options \\ [], list)

(macro)

Add an effect to an animation

This macro allows to add an effect to an animation (or even a component (TODO: figure out whether an effect on a static component makes any sense, it would mean that the static component suddenly would need to be animated)

You simply warp the animation inside a effect block. It's possible to have severeal nested effects. In that case they will all be executed in sequence.

Example:

use Fledex
alias Fledex.Effect.Wanish
led_strip :john, Kino do
  effect Wanish, trigger_name: :john do
    animation :test do
      _triggers ->
        leds(1) |> light(:red) |> repeat(50)
    end
  end
end

job(name, pattern, options \\ [], list)

(macro)

A job is a cron job that will trigger in regular intervals (depending on the pattern specified). You can run any function and the most likely event you will trigger is to publish an event to the triggers (see the weather example livebook):

  broadcast_trigger(%{temperature: -15.2})

Each job consists of:

  • name- a unique name
  • pattern- a cron pattern (as specified in this cheatsheet). Note: Crontab.CronExpression gets imported and therefore the sigil can directly be used, i.e. ~e[* * * * * * * *]e
  • options- a keyword list with some options. The following options exist:
    • :run_once- a boolean that indicates whether the job should be run once at creation time. This can be important, because you might otherwise have to wait for an extended time before the function will be executed.
    • :timezone- The timezone the cron pattern applies to. If nothing is specified :utc is assumed
    • :overlap- This indicates whether jobs should overlap or not. An overlap can happen when running the job takes more time than the interval between job runs. For safety reason the default is false.
  • :do - a block of code that should be executed. You can specify directly your code here. It will be wrapped into an anonymous function.

Example:

use Fledex
led_strip :nested_components2, Kino do
  job :clock, ~e[@secondly]e do
    date_time = DateTime.utc_now()

    broadcast_trigger(%{
      clock_hour: date_time.hour,
      clock_minute: date_time.minute,
      clock_second: date_time.second
    })
  end
end

led_strip(strip_name, drivers, strip_options \\ [], list)

(macro)

This introduces a new led_strip.

The drivers can be spcified in 3 different ways:

  • just a driver module (like Spi). In this case the default settings will be used
  • a driver module with it's configuration (like {Spi, [dev: "spidev0.1"]})
  • or a set of drivers (always with their configuration), like: [{Spi, []}, {Spi, [dev: "spidev0.1"}]

A set of default drivers exist for conenience that can be used like Spi, Null, ... (see Fledex.LedStrip for details).

A special driver :config exists that will simply return the converted dsl to the corresponding configuration. This can be very convenient for

  • running tests
  • implementing components consisting of several animations. Take a look at the Fledex.Component.Clock as an example.

The strip_options configures any non-driver specific settings of the strip (like how often the strip should be repainted, how different animations should be merged, ...).

static(name, options \\ nil, list)

(macro)

The static macro is equal to the animation macro, but it will not receive any triggers.

Therefore, there will not be any repainting and the def_func will not receive any parameter. It will only be painted once at definition time.

version()

(since 0.5)

Returns the version of the Fledex library. It can be important to know the version in order to adjust some code depending on the version