Registering a Photons action

All the tasks available to the Photons lifx program are actions that are registered in one of the Photons modules. An action is a function that takes in objects that represent the arguments provided on the command line.

For example, the power_toggle action is defined as:

from photons_app.actions import an_action

from photons_control.transform import PowerToggle


@an_action(needs_target=True, special_reference=True)
async def power_toggle(collector, target, reference, artifact, **kwargs):
    """
    Toggle the power of devices.

    ``target:power_toggle match:group_label=kitchen -- '{"duration": 2}'``

    It takes in a ``duration`` field that is the seconds of the duration.
    This defaults to 1 second.
    """
    extra = collector.photons_app.extra_as_json
    msg = PowerToggle(**extra)
    await target.send(msg, reference)

This definition is detailed as follows:

  • an_action takes in a few arguments

    • needs_reference=True means Photons throws an error if the <reference> argument is not specified

    • special_reference=True means the reference variable provided must be is a Special Reference

    • needs_target=True means a <target> must be specified.

  • The collector is the entry point to all functionality registered by the Photons modules.

  • The target is the object that sends messages. The artifact (which is not used above) is the last argument given on the command line.

  • The JSON options string provided on the command line is available as the collector.photons_app.extra_as_json attribute.

  • All arguments to an action must be provided as keyword arguments. If you do not specify a keyword, the unused arguments will be consumed by the **kwargs argument.

Create a script that registers and runs an action:

from photons_app.actions import an_action

from photons_messages import DeviceMessages


@an_action(needs_target=True, special_reference=True)
async def display_label(collector, target, reference, **kwargs):
    async for pkt in target.send(DeviceMessages.GetLabel(), reference):
        print(f"{pkt.serial}: {pkt.label}")

if __name__ == "__main__":
    __import__("photons_core").run('lan:display_label {@:1:}')

The action has the same command-line options available as the lifx utility, including the references:

$ python my_script.py match:group_name=kitchen --silent

photons_core.run

The run function takes either a formatted string of environment variables and sys.argv values or a list of manually specified arguments.

For example, this:

__import__("photons_core").run("{TRANSPORT_TARGET|lan:env}:{@:1} {@:2:}")

Is the same as this:

import sys
import os

target = os.environ.get("TRANSPORT_TARGET", "lan")
__import__("photons_core").run([f"{transport}:{sys.argv[1]}"] + sys.argv[2:])

An environment variable is mandatory if a default is not provided:

__import__("photons_core").run("{TRANSPORT_TARGET:env}:{@:1} {@:2:}")

By default this will start the core modules, which is likely all that’ll ever be needed. Run can be given default_activate=[] to make Photons not load any modules. If there are other Photons modules in the environment, they can be loaded with default_activate=["other_module"]. Not specifying a default_activate is equivalent to default_activate=["core"]. Finally if it’s desirable to load all Photons modules found in the environment, then the special __all__ module can be used.

The an_action decorator

class photons_app.actions.an_action(target=None, special_reference=False, needs_reference=False, needs_target=False, label='Project')

Records a task in the available_actions dictionary

It takes in:

target

The type of the target that applies to this action. For example lan or http. This is so that you can have a different task with the same name registered for different targets

needs_reference

Specifies that a reference needs to be specified on the commandline

special_reference

Allow us to provide more detailed reference to devices.

Empty string or ‘_’ resolves to all serials found on the network

comma seperated list of serials is split by comma.

Otherwise, we use <resolver>:<options> to resolve our reference to serials

needs_target

Specifies that it needs the target type specified on the commandline

label

A string used by the help tasks to sort the actions into groups.