Registering a Photons task
All the tasks available to the Photons lifx
program are registered onto the
photons_app.tasks.task_register
object and are responsible for specifying how
we transform instructions from the commandline into objects for use in the task.
For example, the power_toggle
action is defined as:
from photons_app.tasks import task_register as task
from photons_control.transform import PowerToggle
@task
class power_toggle(task.Task):
"""
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.
"""
target = task.requires_target()
reference = task.provides_reference(special=True)
async def execute_task(self, **kwargs):
extra = self.photons_app.extra_as_json
msg = PowerToggle(**extra)
await self.target.send(msg, self.reference)
The tasks seen in the lifx
program are registered when each Photons module
is activated. A standalone script that has it’s own tasks can be written using
the photons_core.run
function:
from photons_app.tasks import task_register as task
from photons_messages import DeviceMessages
@task
class display_label(task.Task):
"""Display the label for specific devices"""
target = task.requires_target()
reference = task.provides_reference(special=True)
async def execute_task(self, **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
The fields on the class uses the data normalisation functionality provided in delfick_project.norms.
When the lifx
program creates the task it does the following:
artifact = collector.photons_app.artifact
reference = collector.photons_app.reference
target_name, task_name = collector.photons_app.task_specifier()
task_register.fill_task(
collector,
task_name,
path="CLI|",
target=target_name,
reference=reference,
artifact=artifact,
).run_loop()
So for example if the command was:
> lifx lan:attr d073d5000001 Color
# OR
> lifx --task lan:attr --reference d073d5000001 --artifact Color
Then the attr
task is created with:
target: "lan"
artifact: "Color":
reference: "d073d5000001"
And will have available in the meta
object the
Collector available.
See Photons Task Class for more information on how the Task
class works.
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.
Legacy Function based Actions
The original way that Photons defined tasks was via functions rather than classes. This can still be done using either:
from photons_app.tasks import task_register as task
@task.from_function(needs_target=True, special_reference=True)
async def power_toggle(collector, target, reference, artifact, **kwargs):
...
Or, for backwards compatible, with the original import:
from photons_app.actions import an_action
@an_action(needs_target=True, special_reference=True)
async def power_toggle(collector, target, reference, artifact, **kwargs):
...
- task_register.from_function(target=None, special_reference=False, needs_reference=False, needs_target=False, label='Project')
Registers a function as a task. The name of the task will be the name of the function. When the function is called it will be provided
collector
,target
,reference
,artifact
and any other keyword arguments that were provided when the task was invoked.The decorator has the following options for changing the values provided to the function:
- target
The
type
of the target that applies to this action. For examplelan
orhttp
. 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.
For example:
from photons_app.tasks import task_register as task @task.from_function(target="lan", special_reference=True) def my_amazing_task(collector, target, reference, **kwargs): """This task does cool things""" ...
Is equivalent to:
from photons_app.tasks import task_register as task @task class my_amazing_task(task.Task): """This task does cool things""" target = task.requires_target(target_types=["lan"]) reference = task.provides_reference(special=True) async def execute_task(self, **kwargs): ...
It is recommended you create tasks rather than functions for tasks.