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'
ROLL_UP and ROLL_DOWN are exemplary member names to be used
in the Python code.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'
PREVENT_UP and PREVENT_DOWN are examplary member names to be
used in the Python code.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'
POSITION, IS_RON and WIDTH are exemplary member names to
be used in the Python code.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.failedif 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:
The super constructor
__init__()must be called._performAction()must be implemented if there is at least one custom action the control can perform._getProperty()and_getPropertiesListsEn()must be implemented if there is at least one custom property the control can provide.
- __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(). Seefind()for the discovery algorithm.None if the control does not need an environment configuration.
SeeBashHelperfor the supported configuration syntax.A possibly existingMODE_DIR_NAME_ENVKEYenvironment value is used for discovering the mode directory. This is only relevant ifmodeTypeis 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_SETandMODE_UNSETactions always related to the active mode. The pending mode is skipped. Only relevant ifmodeTypeis 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-setreturnsset,setting,set,mode, Nonepower-offorpoweroffreturnspower,powering,powered, None,offfade-outorfadeoutreturnsfade,fading,faded, None,outlight-fade-outorlight-fadeoutreturnsfade,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.
modeorlight. 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:
- _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:
alreadymaps toALREADY_OK_SEVERITYorALREADY_NOT_OK_SEVERITY, depending on the value ofisOkNodopreventedmaps toWARNINGdisabledmaps toVERBOSEfailedmaps toERRORall other feedback identifiers map to
SUCCESS
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 ofErrorMessage.- 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
failedonly needs to be changed if a more specific feedback identifier is required for the error.
- Returns:
The created exception.
- Return type:
- 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']
… wherePOSITIONis the member name (str) of the property as listed in the property Enum.…Position.UPis the current value of the property as returned bygetProperty().…screen positionis the human-readable name (str) of the property.… andupis 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-activebecomesmode 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,
yesornoare produced. If it is None,n/ais 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:
- 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:
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.
ErrorMessage – MODE_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:
- 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:
The capitalized human-readable name of the property. If the property value matches
TRUE_VALUE_PATTERNorFALSE_VALUE_PATTERN, a question mark is appended. For instance,is-powered-onbecomesis powered on?. If the property value has any other type, a colon is appended.The corresponding human-readable value.
- Parameters:
- Returns:
The human-readable property listing.
- Return type:
- 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:MODE_PENDING: CallsgetPendingMode().MODE_ACTIVE: CallsgetActiveMode().
- 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:
- 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 IDrequest29476, and other calls use the IDrequest39549, 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:MODE_SET: Sets the pending mode by callingsetPendingMode()unlessisSkipPendingModeis True. In that case the active mode is set. Expects a sequence ofmodeandtimeout_sinargument.MODE_UNSET: Unsets the pending mode by callingunsetPendingMode()unlessisSkipPendingModeis True. In that case the active mode is unset. Expects None inargument.MODE_ACTIVATE: Activates the pending mode by callingactivatePendingMode(). Expects None inargument.
- 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_ENVKEYenvironment 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,
0in the case of the MODE_UNSET action.
- Return type:
- 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, theMODE_TIMEOUT_DEFAULT_ENVKEYenvironment value is used if it exists. If not, the value ofTIMEOUT_DEFAULT_Sis used. Passing0is equivalent to callingunsetPendingMode().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_ENVKEYenvironment 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.
0in the case of the MODE_UNSET action.
- Return type:
- 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.
- 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:
ControlEnumStandard 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
actionTypewill 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:
ControlEnumStandard modes supported by the Mode methods of
Control.Any mode Enum passed into
modeTypewill dynamically inherit the methods of this class.- NONE = 'none'
- class ctlbase.control.StandardProperty(value)
Bases:
ControlEnumStandard 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
propertyTypewill 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,VERBOSEorDEBUG.
- 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
WARNINGorERROR.
- 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.