control module

Core concepts

The classes in this module provide a range of generic mechanisms supporting the following concepts.

control

A control encapsulates the logic for “controlling” some other hardware or software resource. It is implemented by subclassing Control. Examples of resources include:

  • Actuators in a home automation context, optionally coupled to one or more sensors.

  • Remote file systems to mount and unmount using various credentials from desktop keyrings, TPM2 hardware modules and more.

  • A mediacenter software exposing an API, e.g. Kodi.

action

A control defines one or more actions it can perform. Supported actions must be listed in a custom Enum class, called action Enum.

As an example, for controlling a movie screen in a home theater, an action Enum can be implemented as follows:

class ScreenAction(enum.Enum):
    ROLL_UP = 'roll-up'
    ROLL_DOWN = 'roll-down'
… where ROLL_UP and ROLL_DOWN are exemplary member names to be used in the Python code.
… and roll-up and roll-down are the corresponding names to be used on the command line when implementing a CLI utility.

Each action Enum member must have a single str as its value. It must include an English verb in its infinitive form and must not contain spaces. An object may be prepended with a hyphen, e.g. screen-. A preposition or similar word, called addendum, may be appended with or without a hyphen, e.g. -down. An addendum must be one of the following words: on, off, up, right, left, down, out, in, min, max.

To actually perform an action, performAction() must be called.

mode

A control can optionally support different modes. Each mode can make the control behave differently for a given action. Supported modes must be listed in a custom Enum class, called mode Enum.

As an example, a mode Enum can be implemented as follows:

class ScreenMode(enum.Enum):
    PREVENT_UP = 'prevent-up'
    PREVENT_DOWN = 'prevent-down'
… where PREVENT_UP and PREVENT_DOWN are examplary member names to be used in the Python code.
… and prevent-up and prevent-down are the corresponding names to be used on the command line when implementing a CLI utility.

Each mode Enum member must have a single str as its value.

In the normal flow, a mode is first set as the pending mode. This can be done by calling setPendingMode(). It will be unset automatically if it is not activated within a certain time span. If it is activated in time by calling activatePendingMode(), it becomes the active mode. It will persist until unset with unsetActiveMode(). Apart from that, the active mode can be set directly by calling setActiveMode().

It is possible to set one mode as active and another mode as pending at the same time.

Modes can be retrieved with getPendingMode() and getActiveMode().

property

By default, a property is read-only. It can be anything worth reporting, e.g. the state of a sensor, the mount state of a remote file system or a pending mode or the active mode of the control. If a property is meant to be read-write, values can only be changed via an action. Supported properties must be listed in a custom Enum class, called property Enum.

As an example, a property Enum can be implemented as follows:

class ScreenProperty(enum.Enum):
    POSITION = 'position'
    IS_RON = 'relay-on'
    WIDTH = 'width'
… where POSITION, IS_RON and WIDTH are exemplary member names to be used in the Python code.
… and position, relay-on and width are the corresponding names to be used on the command line when implementing a CLI utility.

Each property Enum member must have a single str as its value.

The current value of a property can be retrieved with getProperty().

feedback

A control must report feedback from a performed or attempted action, formulated as a meaningful identifier. While an identifier can be any str, common values are:

  • already, if the action has not been performed because the action’s target state was already reached.

  • prevented, if the action has not been performed due to an active mode or any other circumstance preventing the action.

  • disabled, if the action has not been performed because it not enabled implicitly or is explicitly disabled by configuration.

  • failed if an error occurred while performing the action.

  • succeeded, if the action has been performed successfully, which is the default if None is given.

Classes

class ctlbase.control.Control(loop, commandExecutor=None, envConfig=None, actionType=None, modeType=None, isSkipPendingMode=False, propertyType=None, msgBuffer=None, ns=True, jsonIndent=4)

Base class for a control.

It provides a uniform Python interface for various kinds of controls. Among the benefits, it is easy to build CLI, REST, web and other user interfaces on top. What is more, composite controls for multiple resources can be created, preserving the uniform interface.

Subclasses must comply with the following rules:

__init__(loop, commandExecutor=None, envConfig=None, actionType=None, modeType=None, isSkipPendingMode=False, propertyType=None, msgBuffer=None, ns=True, jsonIndent=4)
Parameters:
  • commandExecutor (CommandExecutor | None) – A process executor for executing commands in a subprocess. None if the control does not need to execute commands.

  • envConfig (tuple[str, dict] | str | bool | None) –

    An environment configuration in one of the following forms:

    • A tuple of an absolute file path and a dict representing the parsed configuration.

    • The file name of the Bash-style configuration file to be parsed. It may include a relative or absolute path specification. See find() for the discovery algorithm.

    • True for auto-discovering a configuration file based on the caller’s normalized module name as returned by getName(). See find() for the discovery algorithm.

    • None if the control does not need an environment configuration.

    See BashHelper for the supported configuration syntax.
    A possibly existing MODE_DIR_NAME_ENVKEY environment value is used for discovering the mode directory. This is only relevant if modeType is not None.

  • actionType (Enum) – The action Enum class.

  • modeType (Enum | None) – The mode Enum class. None if the control does not support different modes.

  • isSkipPendingMode (bool) – If True, the MODE_SET and MODE_UNSET actions always related to the active mode. The pending mode is skipped. Only relevant if modeType is not None.

  • propertyType (Enum | None) – The property Enum class. None if the control does not support any property.

  • msgBuffer (MessageBuffer | True) – The message buffer to which all occurring messages are appended. True for auto-creation.

  • ns (str | True | None) – The message namespace to use when writing messages to the message buffer. If True, the namespace is derived from the caller’s normalized module name, as returned by getName(). If auto-creating a message buffer, the namespace set as its default namespace.

  • jsonIndent (int) – The number of spaces used to indent one hierarchy level in JSON output.

_actionToGrammarEn(action, argument, feedback)

Creates a set of English word forms for a given action.

The control uses these word forms to auto-create English message texts. In order to fine-tune message texts, an implementation may override this method.

The following examples show the outcomes of the default implementation:

  • mode-set returns set, setting, set, mode, None

  • power-off or poweroff returns power, powering, powered, None, off

  • fade-out or fadeout returns fade, fading, faded, None, out

  • light-fade-out or light-fadeout returns fade, fading, faded, light, out

The arguments to an action and the feedback from an action may influence the outcomes. For simplicity, this is not considered in the examples above.

Parameters:
  • action (Enum | str) – The action for which to create the word forms. Either an action Enum value (str) or the action Enum member itself. The value must comply to the rules described under action.

  • argument (object | list | tuple | None) – The argument(s) to the action. See argument.

  • feedback (str | None) – A feedback identifier. See feedback.

Returns:

A tuple of five elements:

  • action (str) – The infinitive form of the action verb.

  • presentParticiple (str) – The present progressive form of the action verb.

  • pastParticiple (str) – The past participle of the action verb.

  • object (str | None) – The object of the action, e.g. mode or light. It may be derived from the action value and/or from the argument(s) to the action. If None, message texts are formulated without an object.

  • addendum (str | None) – An addendum as listed under action, possibly amended by an adverb related to the feedback, e.g. down already. If None, message texts are formulated without an addendum.

Return type:

tuple

_appendMessageFromActionEn(action, argument, feedback=None, isOkNodo=False, correlationId=None)

Creates a message for a given action and appends it to the message buffer.

If the control does not have a message buffer, nothing happens.

The English message text and the severity are determined by the feedback identifier. The severities map as follows:

The English message text is created using the word forms returned by _actionToGrammarEn().

Parameters:
  • action (Enum | str) – The action for which to create a message. Either an action Enum value (str) or the action Enum member itself. The value must comply to the rules described under action.

  • argument (object | list | tuple | None) – The argument(s) to the action. See argument.

  • feedback (str | None) – A feedback identifier. See feedback.

  • isOkNodo (bool | None) – If True, it is considered “OK” when the target state of the action is already reached.

  • correlationId (str | None) – The correlation ID with which to create the message. See correlationId.

_createExceptionFromActionEn(action, argument, error=None, feedback='failed')

A variant of _appendMessageFromActionEn(), creating an exception, i.e. an instance of ErrorMessage.

Parameters:
  • action (Enum | str) – The action for which to create an exception. Either an action Enum value (str) or the action Enum member itself. The value must comply to the rules described under action.

  • argument (object | list | tuple | None) – The argument(s) to the action. See argument.

  • error (Exception | None) – The original error from which to derive an English message text. If None, a generic text is created. For errors of type SynchronousError, a specific text is created. For all other types, a generic text is created.

  • feedback (str | None) – A feedback identifier. See feedback. The default of failed only needs to be changed if a more specific feedback identifier is required for the error.

Returns:

The created exception.

Return type:

ErrorMessage

async _getPropertiesListsEn()

Creates property lists for all properties and their current values in English (en).

The following is an example of a property list:

['POSITION', Position.UP, 'screen position', 'up']
… where POSITION is the member name (str) of the property as listed in the property Enum.
Position.UP is the current value of the property as returned by getProperty().
screen position is the human-readable name (str) of the property.
… and up is the current human-readable value of the property (str).

Each property list must contain these four elements in the order shown above.

The default implementation works as follows:

  • The human-readable name is created from the property Enum member’s value. Hyphens are replaced by spaces. As an example, mode-active becomes mode active.

  • As for the human-readable value, if the property value is an Enum member, its value is used. If the property value is a bool, yes or no are produced. If it is None, n/a is produced. Otherwise, the unmodified property value is used.

Returns:

An Iterable with one property list for each property.

Return type:

Iterable

abstract async _getProperty(property)

Implements the logic for retrieving the value of a custom property.

Parameters:

property (str | Enum) – The property whose value to retrieve. Either a property Enum value (str) or the property Enum member itself.

Returns:

The current property value. It may be a cached value if the custom implementation uses caching. To ensure fresh values, call _invalidateCache() first.

Return type:

object

abstract _invalidateCache()

Invalidates any cached property values.

It is up to the custom implementation whether to cache property values.

abstract async _performAction(requestedAction, argument=None, isOkNodo=False, **kwargs) tuple

Implements the logic for performing a custom action.

Parameters:

kwargs (dict) –

Any number of the following keyword arguments, depending on which the implementation wants to support:

Returns:

A tuple of three elements:

  • actualAction (Enum) – The actually performed action as an action Enum member.

  • actualArgument (object | list | tuple | None) – The actually used argument(s) as a single object or a sequence of objects.

  • feedback (str | None) – A feedback identifier. See feedback.

If None, the control does not auto-create and append any messages.

Return type:

tuple | None

async activatePendingMode()

Activates the pending mode if set.

As a result, the pending mode will be the active mode. The pending mode is cleared.

Raises:
  • RuntimeError – If the control has not been set up for handling modes.

  • ErrorMessageMODE_NOT_PENDING – If no pending mode is set.

Returns:

The activated mode.

Return type:

Enum

close()

Closes auto-created resources.

async getActiveMode()

Returns the active mode if set.

Returns:

The active mode Enum member if set, otherwise the NONE mode Enum member.

Return type:

Enum

Raises:

RuntimeError – If the control has not been set up for handling modes.

async getPendingMode()

Returns the pending mode if set.

Returns:

The pending mode Enum member if set, otherwise the NONE mode Enum member.

Return type:

Enum

Raises:

RuntimeError – If the control has not been set up for handling modes.

async getPropertiesJson(propertiesListsEn=None)

Creates a JSON-formatted list of all current property values.

Parameters:

propertiesListsEn (tuple[str]) – The property lists to be rendered in JSON format, as returned by _getPropertiesListsEn(). Only the first two elements of each property list are used.

Returns:

The properties list in JSON format.

Return type:

str

async getPropertiesTextEn(propertiesListsEn=None, firstColumnWidth=28)

Creates a human-readable listing of all current property values in English (en).

The following is an example of such a listing:

Mode pending:               none
Mode active:                up

The output contains two text columns for each property:

  1. The capitalized human-readable name of the property. If the property value matches TRUE_VALUE_PATTERN or FALSE_VALUE_PATTERN, a question mark is appended. For instance, is-powered-on becomes is powered on?. If the property value has any other type, a colon is appended.

  2. The corresponding human-readable value.

Parameters:
  • propertiesListsEn (tuple[str]) – The property lists to be rendered as plain text, as returned by _getPropertiesListsEn(). Only the last two elements of each property list are used.

  • firstColumnWidth (int) – The width of the first column in the log output, as number of characters.

Returns:

The human-readable property listing.

Return type:

str

async getProperty(property)

Retrieves the current value of a property.

Custom properties are delegated to _getProperty().

Parameters:

property (str | Enum) – The property whose value to retrieve. Either a property Enum value (str) or the property Enum member itself.

For the properties listed in StandardProperty, the default implementation works as follows:

Raises:

ValueError – If the property Enum does not have a member corresponding to property.

Returns:

The current property value. It can be of any Python type, be it a scalar type like int or str, be it a list or dict or a custom object.

Return type:

object

async performAction(action, argument=None, isOkNodo=False, isForced=None, mayPrompt=None, correlationId=None)

Performs an action.

Custom actions are delegated to _performAction().

Parameters:
  • action (str | Enum) – The action to perform. Either an action Enum value (str) or an action Enum member itself. If the implementation of _performAction() deems another action more appropriate, it must return it as actual action.

  • argument (object | list tuple) – The argument(s) required to perform the action. It may be a single object or an ordered sequence of several objects. It is up to the custom implementation of _performAction() to evaluate the arguments properly – unless a default implementation is used (see below).

  • isOkNodo (bool) – If True, a warning is suppressed if the action’s target state is already reached.

  • isForced (bool) – If True, the action is forced even if the target state is already reached.

  • mayPrompt (bool) – If True, the user may be prompted for any input, e.g. confirmations or passwords. False for non-interactive operation.

  • correlationId (str) – An ID for grouping messages in the message buffer. For instance, if some calls to performAction() use the correlation ID request29476, and other calls use the ID request39549, the message buffer can specifically retrieve the corresponding messages instead of “all” messages.

For the actions listed in StandardAction, the default implementation works as follows:

Raises:
  • ValueError – If the action is not listed in the action Enum.

  • ValueError – If one of the parameters isForced, mayPrompt or correlationId is given, but _performAction() does not declare it explicitly in its signature.

async setActiveMode(mode, group=None)

Sets the active mode.

Parameters:
  • mode (str | Enum) – The mode to set. Either a mode Enum value (str) or the mode Enum member itself. Passing the NONE mode Enum member is equivalent to calling unsetActiveMode().

  • group (str | None) – The group ownership to be set on the mode directory. Accepts a group name or numeric group ID (gid). If None, the MODE_GROUP_ENVKEY environment value is used if it exists. If not, the group ownership is left unmodified.

Raises:
  • ValueError – If the mode Enum does not have a member corresponding to mode.

  • RuntimeError – If the control has not been set up for handling modes.

Returns:

A tuple with the following elements:

  • The actual action performed, as an action Enum member, i.e. MODE_SET or MODE_UNSET.

  • The mode Enum member that was set or unset, respectively.

  • None in the case of the MODE_SET action, 0 in the case of the MODE_UNSET action.

Return type:

tuple

async setPendingMode(mode, timeout_s=-1, group=None)

Sets the pending mode.

Parameters:
  • mode (str | Enum) – The mode to set. Either a mode Enum value (str) or the mode Enum member itself. Passing the NONE mode Enum member is equivalent to calling unsetPendingMode().

  • timeout_s (int | None) – The pending mode timeout in seconds. If activatePendingMode() is not called within that time span, the pending mode will be unset automatically. If -1, the MODE_TIMEOUT_DEFAULT_ENVKEY environment value is used if it exists. If not, the value of TIMEOUT_DEFAULT_S is used. Passing 0 is equivalent to calling unsetPendingMode().

  • group (str | None) – The group ownership to be set on the mode directory. Accepts a group name or numeric group ID (gid). If None, the MODE_GROUP_ENVKEY environment value is used if it exists. If not, the group ownership is left unmodified.

Raises:
  • ValueError – If the mode Enum does not have a member corresponding to mode.

  • RuntimeError – If the control has not been set up for handling modes.

Returns:

A tuple with the following elements:

  • actualAction (Enum) – The actual action performed, as an action Enum member, i.e. MODE_SET or MODE_UNSET.

  • mode (Enum) – The mode Enum member that was set or unset, respectively.

  • timeout_s (int) – The timeout in seconds after which the mode will be unset automatically. 0 in the case of the MODE_UNSET action.

Return type:

tuple

async unsetActiveMode()

Unsets the active mode if set.

Returns:

The unset mode or None if no mode was unset.

Return type:

Enum | None

Raises:

RuntimeError – If the control has not been set up for handling modes.

async unsetPendingMode()

Unsets the pending mode if set.

Returns:

The unset mode Enum member or None if no mode was unset.

Return type:

Enum | None

Raises:

RuntimeError – If the control has not been set up for handling modes.

class ctlbase.control.ControlEnum(value)

Base class for Enum classes used by Control.

Note that a custom action, mode and property Enum does not have to extend this class, as the methods of this class are added dynamically at runtime.

__eq__(other)

Makes an Enum member comparable with simple strings, based on its value.

Parameters:

other (str | Enum) – The str value of an Enum member or an Enum member itself.

classmethod fromAny(object)

Looks for an Enum member corresponding to a given object.

Parameters:

object (str | Enum) – The str value of an Enum member or an Enum member itself.

Returns:

The corresponding Enum member.

Return type:

Enum

Raises:

ValueError – If the Enum does not have a corresponding member.

Constants and defaults

class ctlbase.control.StandardAction(value)

Bases: ControlEnum

Standard actions supported by performAction() without the need for custom implementations in _performAction().

If a custom control wants to use the default implementation of performAction() for a specific action, one of its action Enum members must have the same value as the desired standard action.

Any action Enum passed into actionType will dynamically inherit the methods of this class.

MODE_ACTIVATE = 'mode-activate'
MODE_SET = 'mode-set'
MODE_UNSET = 'mode-unset'
class ctlbase.control.StandardMode(value)

Bases: ControlEnum

Standard modes supported by the Mode methods of Control.

Any mode Enum passed into modeType will dynamically inherit the methods of this class.

__eq__(other)

Extends __eq__() by treating str, none (str) and None as equivalent.

classmethod fromAny(mode)

Extends fromAny() by treating str, none (str) and None as equivalent.

NONE = 'none'
class ctlbase.control.StandardProperty(value)

Bases: ControlEnum

Standard properties supported by getProperty() without the need for custom implementations in _getProperty().

If a custom control wants to use the default implementation of getProperty() for a specific property, one of its property Enum members must have the same value as the desired standard property.

Any property Enum passed into propertyType will dynamically inherit the methods of this class.

MODE_ACTIVE = 'mode-active'
MODE_PENDING = 'mode-pending'
ctlbase.control.ALREADY_OK_SEVERITY = Severity.NORMAL

The severity to use when the target state of an action is already reached and, under the current circumstances, is considered “OK”. The value is in effect for the entire Python process and is normally one of SUCCESS, NORMAL, VERBOSE or DEBUG.

ctlbase.control.ALREADY_NOT_OK_SEVERITY = Severity.WARNING

The severity to use when the target state of an action is already reached and, under the current circumstances, is considered “not OK”. The value is in effect for the entire Python process and is normally one of WARNING or ERROR.

ctlbase.control.MODE_DIR_NAME_ENVKEY = 'MODE_PATH'

Environment configuration key for the mode directory.

ctlbase.control.MODE_GROUP_ENVKEY = 'MODE_GROUP'

Environment configuration key for the group ownership of the mode directory.

ctlbase.control.MODE_TIMEOUT_DEFAULT_ENVKEY = 'MODE_TIMEOUT_DEFAULT_S'

Environment configuration key for the default pending mode timeout in seconds.