signal module
Base classes for translating GPIO signals into Linux kernel key events.
Supported signals have fixed or variable pulse lengths or individual edges,
with each signal received on one specific GPIO. Key events are exposed as
instances of KeyEvent.
GPIO backend must implement PigpioMinimalApi and
PigpioCallbackApi to work with this module.
Furthermore, custom protocol handlers can be created by extending the classes in
this module. See topic for an entry point into the
extensibility mechanism. Existing extensions are:
signal_buttonfor physical buttons
signal_irfor the RC6 infrared protocol
For an overview of how signals can be processed, the Configuration classes are a good starting point.
Configuration classes
The classes in this section evaluate and hold configurations for signal protocols, signal listeners and GPIO mappings. They can be easily initialized with dict instances, e.g. read from JSON files.
Auto-discovering and parsing JSON files are out of scope for
gpiosvr.signal. See the ctlbase package instead.
The following is a sample JSON representation of a signal configuration:
{
"evdevName": "pi-sig-injector",
"gpios": {
"25": "door-bell"
},
"signalProtocols": {
"door-bell": {
"basePulse_μs": 9000,
"basePulseCount": 5,
"codeStartBit": 1,
"preamble_ms": 10,
"postamble_ms": 12,
"tolerancePercentage": 30,
"debounce_μs": 600,
"signalListener": {
"mayInject": false
},
"keys": {
"0x1f": {
"keyName": "KEY_SOUND"
}
}
}
}
}
- class gpiosvr.signal.SignalConfig(signalConfig: dict, topic='signal', protocolClass_=<class 'gpiosvr.signal.SignalProtocolDescription'>, listenerConfigClass_=<class 'gpiosvr.signal.SignalListenerConfig'>)
- __init__(signalConfig: dict, topic='signal', protocolClass_=<class 'gpiosvr.signal.SignalProtocolDescription'>, listenerConfigClass_=<class 'gpiosvr.signal.SignalListenerConfig'>)
Properties describing a compound configuration for signal processing on multiple GPIOs.
They combine the configurations of signal protocols (
SignalProtocolDescription), signal listeners (SignalListenerConfig) and GPIO mappings.- Parameters:
signalConfig¶ (dict) –
Under the key
evdevName(str), the dict must contain a str for the logical name of the event device to which key events will be published. This is not the name of the event device under/dev/input/. Instead, it is a logical name that can be referenced in theinputDeviceNamepositional parameter of thegpiosvr.bin.key_monitorexecutable.Under the key
gpios(str), the dict must contain a dict mapping GPIO BCM numbers to protocol names. For instance, the GPIO number0(str) can be mapped to a protocol namemybutton. This protocol name must then be referenced in the following signal protocol descriptions.Under the key
signalProtocols(str), the dict must contain a list of signal protocol descriptions. These in turn must be dict instances to be passed tofromConfig(). To map a protocol description to a specific GPIO, anamemust be provided for each signal protocol.In a addition to a name, each signal protocol must feature a
keys(str) key with another dict mapping hex codes to Linux key names. Seegpiosvr.key.KeysConfigfor reference.Optionally, each signal protocol may each feature a
signalListener(str) key with another dict configuring the listener for that specific combination of GPIO and protocol. See__init__()for reference.topic¶ (str, optional) – For extensibility, an identifier for handling specific types of protocols. It is used as a prefix for configuration keys when parsing the configuration given in
signalConfig. For instance, if set torc, the keys expected in signalConfig arercProtocolsandrcListener. this allows for combining multiple configurations in one dict instance. Defaults tosignal.protocolClass_¶ (class object, optional) – For extensibility, the class to which parsing of the signal protocol descriptions is delegated. The class needs to implement a fromConfig method accepting a dict instance. Defaults to
SignalProtocolDescription.listenerConfigClass_¶ (class object, optional) – For extensibility, the class to which parsing of the signal listener configurations is delegated. The class needs to implement a fromConfig method accepting a dict instance. Defaults to
SignalListenerConfig.
- class gpiosvr.signal.SignalProtocolDescription(name=None, basePulse_μs=None, tolerancePercentage=None, minEdgeCount=None, basePulseCount=None, preamble_ms=None, postamble_ms=None, codeStartBit=-1, bitOrder=None, edgeEvaluationInterval_ms=None, debounce_μs=None)
- __init__(name=None, basePulse_μs=None, tolerancePercentage=None, minEdgeCount=None, basePulseCount=None, preamble_ms=None, postamble_ms=None, codeStartBit=-1, bitOrder=None, edgeEvaluationInterval_ms=None, debounce_μs=None)
Parameters describing a signal protocol, i.e. the expected pulses or edges and how to translate them to hex codes. The hex codes can be mapped to Linux key names in the scope of
SignalConfig.Before translating a signal to a hex code, it is first translated to a sequence of logical bits. The translation algorithm can be extended by custom signal listeners.
In the basic
SignalListener, the logical bit is simply alternating with the GPIO level. To set the first logical bit, use the parametercodeStartBit. The number of bits decoded from the signal, the so calledbitCount, is derived from the parametersbasePulse_μs,minEdgeCountandbasePulseCount. SeeparamsToBitCount().- Parameters:
name¶ (str, optional) – Identifying name of the protocol for mapping it to a specific GPIO. Also used in messages and asyncio task names. Defaults to
None.basePulse_μs¶ (int, optional) – For a signal with a fixed pulse length, the base pulse length in microseconds. Defaults to
0.tolerancePercentage¶ (int, optional) – For a signal with a fixed pulse length, the maximum tolerated deviation of
basePulse_μs(plus or minus). Specified as a percentage value from0to50. Defaults toTOLERANCE_PERCENTAGE_DEFAULT.minEdgeCount¶ (int, optional) – For a signal with variable pulse lengths or for individual edges, the minimum number of expected edges that marks the end of the signal. Defaults to
0.basePulseCount¶ (int, optional) – For a signal with a fixed pulse length, the number of base pulses that marks the end of the signal. For instance, for a basePulse_μs of
45, and a basePulseCount of10, a hex code will be produced after45 * 10=450microseconds. Defaults toNone.preamble_ms¶ (int, optional) – The duration of “silence” in milliseconds, that marks the start of a signal on the first edge. For instance, if set to
150, a signal will be considered “started” on the first edge after at least150milliseconds without any other edge. Defaults tobasePulse_μs * 1.5 // 1000.postamble_ms¶ (int, optional) – The duration of “silence” in milliseconds, that marks the end of the signal after the previous edge. For instance, if set to
300, the signal will be considered “ended” if no other edge has occurred for at least300milliseconds since the previous edge. The value must be larger thanpreamble_msso that no new signal is considered “started” before the current signal is considered “ended”. Defaults to(bitCount + 0.5) * basePulse_μs // 1000.codeStartBit¶ (int | str, optional) – The first logical bit in the decoded signal. It may be
0,1for a fixed logical value. It may also beGPIO_LEVEL(str) for the actual GPIO level of the first edge that marks the start of the signal. Defaults toCODE_START_BIT_DEFAULT.bitOrder¶ (
BitOrder) – The bit order to use when translating the logical bits of a signal to a hex code. For instance, if the signal is decoded to a logical bit sequence of1001101, it will be translated to hex code0x4dif the bit order isMSB_FIRST. By contrast, it will be translated to0x59, if the bit order isLSB_FIRST. Defaults toBIT_ORDER_DEFAULT.edgeEvaluationInterval_ms¶ (int, optional) – The interval in milliseconds in which queued edges are evaluated as fixed or variable-length pulses or individual edges. For bursts of pulses, the value must be high enough to completely queue all edges before evaluating them. However, if too long, the decoding into logical bits and translating them to a hex code could be perceptibly delayed. Lower values result in higher CPU load.
debounce_μs¶ (int, optional) – The time span in microseconds, within which two edges shall be ignored. Useful e.g. for hardware buttons that may produce high-frequency edges due to mechanical bouncing. May also be used to filter out high-frequency noise on a line. Defaults to
DEBOUNCE_μS_DEFAULT.
- Raises:
ValueError – if a parameter value is missing or is invalid, given the other parameter values.
- static fromConfig(name, protocolConfig: dict)
Convenience method for creating a signal protocol description from a configuration dict.
The dict entries may only contain the parameter names and values of
__init__().- Returns:
A signal protocol description representing the configuration passed in via
protocolConfig.- Return type:
- Raises:
The same exceptions as with
__init__().
- class gpiosvr.signal.SignalListenerConfig(signaListenerConfig: dict)
- __init__(signaListenerConfig: dict)
Properties describing a signal listener configuration.
- Parameters:
signaListenerConfig¶ (dict) –
Under the key
mayInject(str), the dict may contain a bool value. IfTrue, the signal listener will accept key events injected via theinjectKeyCode()method. This is security-relevant, as key codes can then be injected by malicious software without the need to be physically attached to the corresponding GPIO.Under the key
isDebugOutput(str), the dict may contain a bool value. IfTrue, debug messages will be written to the message buffer of the signal listener, if any. Defaults to None.Under the key
isTesting(str), the dict may contain a bool value. IfTrue, more verbose messages about occurring edges will be written to the message buffer of the signal listener, if any. Useful for finding out the actual pulse or edge sequences on a line. Always impliesisDebugOutput == True. Defaults to None.- Raises:
ValueError if isDebugOutput is set to
Falsewhile isTesting is set toTrue
Pulse event classes
- class gpiosvr.signal.PulseEvent(pulses_μs: list, pulseStartLevel, timestamp_μs)
Represents a completed pulse sequence.
- class gpiosvr.signal.PulsesInterrupted(pulses_μs=(), pulseStartLevel=None, timestamp_μs=None)
Bases:
PulseEvent,ExceptionRepresents an incompleted pulse sequence, i.e. some interruption to a pulse sequence.
- class gpiosvr.signal.PulseEventIterator(loop, gpioClient, gpio, protocol=None, maxBasePulseCount=100, msgBuffer=None, isDebugOutput=False, isTesting=False)
An async iterator over pulse events, based on a signal protocol.
It is the main class receiving edge callbacks and evaluating them based on a signal protocol description. The iterator yields instances of
PulseEventthat can be interpreted as signals, decoded into logical bits and translated to hex codes.The iterator lazily sets up the edge callback with its gpioClient instance once the
__anext__()method is called for the first time. Once started, the iterator runs indefinitely, untilaclose()is called.- __init__(loop, gpioClient, gpio, protocol=None, maxBasePulseCount=100, msgBuffer=None, isDebugOutput=False, isTesting=False)
- Parameters:
loop¶ (asyncio.EventLoop) – The asyncio event loop iterating over pulse evemts.
gpioClient¶ (
PigpioMinimalApi+PigpioCallbackApi) – The GPIO API for setting up the edge callback.gpio¶ (int) – The GPIO on which to set up the edge callback.
protocol¶ (
SignalProtocolDescription) – The signal protocol for determining the start and end of individual signals.maxBasePulseCount¶ (int) – The maximum number of base pulses accepted in one signal. This prevents the CPU from overloading due to noise on the line. Defaults to
MAX_BASE_PULSE_COUNT.isDebugOutput¶ – See
signaListenerConfigisTesting¶ – See
signaListenerConfig
- async onArtificialEdge()
Internal event callback. It is called when an outside requestor has triggered an artificial edge via
provokeArtificialEdge().The implementation is expected to put a corresponding
PulseEventinstance into the event queue, then returnTrueto show that the event has been handled.
- async onPulseInterruption()
Internal event callback.. It is called when the pulse sequence has been interrupted, considering the protocol.
If the implementation wishes to discard the incomplete pulse sequence, it must return
True. If the implementation wishes to append further pulses to the incomplete pulse sequence, it must returnFalse. This is the default.If the implementation whishes to notify coroutines iterating over
PulseEventinstances, it must put an instance ofPulsesInterruptedinto the event queue, then returnTrue.
- async onSequenceEnd()
Internal event callback. It is called when the pulse sequence has completed, considering the protocol.
The implementation is expected to put a corresponding
PulseEventinstance into the event queue, then returnTrueto show that the event has been handled.
- async onSingleEdge()
Internal event callback. It is called when the protocol aims at single edges and a corresponding edge has occurred.
The implementation is expected to put a corresponding
PulseEventinstance into the event queue, then returnTrueto show that the event has been handled.
- class gpiosvr.signal.SignalListener(loop, gpioClient, gpio, mayInject=False, protocol=None, maxBasePulseCount=100, msgBuffer=None, isDebugOutput=False, isTesting=False)
Main class for listening to signals and producing key events.
Pulse events are decoded to signals with logical bits, then translated to hex codes, eventually mapped and exposed as key events.
- __init__(loop, gpioClient, gpio, mayInject=False, protocol=None, maxBasePulseCount=100, msgBuffer=None, isDebugOutput=False, isTesting=False)
- Parameters:
loop¶ (asyncio.EventLoop) – The asyncio event loop of the thread waiting listening to signals.
gpioClient¶ (
PigpioMinimalApi+PigpioCallbackApi) – The GPIO API to use, connected with an appropriate GPIO backend.gpio¶ (int) – The GPIO on which to listen.
mayInject¶ – See
signaListenerConfigprotocol¶ (
SignalProtocolDescription) – The signal protocol for translating pulses to logical bits.maxBasePulseCount¶ – See
maxBasePulseCountisDebugOutput¶ – See
signaListenerConfigisTesting¶ – See
signaListenerConfig
- static fromConfig(loop, gpioClient, gpio, protocol, msgBuffer, listenerConfig)
Convenience method for creating a signal listener from a configuration.
- Parameters:
listenerConfig¶ (object) – Either a dict or an instance of
SignalListenerConfig. If the former, the dict keys must be identical to the parameter names of__init__().- Returns:
A
SignalListenerinstance representing the configuration passed in vialistenerConfig.- Return type:
- Raises:
The same exceptions as
__init__().
- createPulseEventIterator()
Returns the pulse event iterator to use in
waitForKeyEvents().May be overridden for extensibility.
- Returns:
The pulse event iterator to use. May be a cached object in case
waitForKeyEvents()is called multiple times.- Return type:
- async pulseEventToKeyEvents(pulseEventOrKeyCodes, debugOutput=None)
Translates pulse events to key events.
May be overridden for extensibility.
- Parameters:
pulseEventOrKeyCodes¶ (
PulseEvent|PulsesInterrupted| list[str] | tuple[str]) – Either a pulse event to translate or, as a short cut, a sequence of desired key codes if they are already known. Key codes must be strings starting with0x.debugOutput¶ (StringIO, optional) – A string buffer for appending debug messages. Defaults to None.
- async waitForKeyEvents()
May be used in an async for loop to wait for key events translated from received signals.
- async injectKeyCode(keyCode)
When
waitForKeyEvents()is running, injects a key code directly instead of waiting for the next authentic signal.- Parameters:
keyCode¶ (str) – The key code to inject as a string starting with
0x.- Raises:
RuntimeError if this signal listener does not allow injecting key codes. See
signaListenerConfig.
- describeProtocol()
Returns a string describing the protocol of this signal listener in a human-readable form (in English).
Useful for debugging or for log output.
- Returns:
The description.
- Return type:
str
Constants and defaults
- gpiosvr.signal.TOLERANCE_PERCENTAGE_DEFAULT = 45
- gpiosvr.signal.DEBOUNCE_μS_DEFAULT = 500
- class gpiosvr.signal.BitOrder(value)
The bit order to use when translating the logical bits of a signal to a hex code.
- LSB_FIRST = 0
The least significant bit (LSB) shall go first in the hex code.
- MSB_FIRST = 1
The most significant bit (MSB) shall go first in the hex code.
- gpiosvr.signal.BIT_ORDER_DEFAULT = BitOrder.MSB_FIRST
- gpiosvr.signal.CODE_START_BIT_DEFAULT = 'GPIO_LEVEL'
- gpiosvr.signal.MAX_BASE_PULSE_COUNT = 100
Helper classes
- class gpiosvr.signal.PulseHelper
- static paramsToBitCount(basePulse_μs, minEdgeCount, basePulseCount)
Calculates the number of logical bits that will be decoded from signals with the given parameters.
The parameters have the same meanings as with
__init__().- Returns:
The number of logical bits.
- Return type:
int
- static sequenceToBasePulseCount(pulses_μs, basePulse_μs, tolerancePercentage=0)
For a sequence of actual pulse lengths, calculates the number of contained bases pulses. Applicable only if a fixed base pulse length is given.
- Parameters:
pulses_μs¶ (list[int] | tuple[int]) – The sequence of actual pulse lengths in microseconds to be evaluated.
The parameters basePulse_μs, tolerancePercentage have the same meanings as with
__init__().- Returns:
The total number of base pulses contained in the pulse sequence.
- Return type:
int
- static sequenceToHexCodes(pulses_μs, basePulse_μs=300000, tolerancePercentage=0, bitCount=1, bitOrder=BitOrder.MSB_FIRST, pulseStartLevel=1, forceSingleCode=False, debugOutput: StringIO | None = None)
Produces a hex code for an actual pulse sequence, first converting the pulse sequence to logical bits.
- Parameters:
pulses_μs¶ (list[int] | tuple[int]) – The sequence of actual pulse lengths in microseconds to be evaluated.
pulseStartLevel¶ (int) – The actual GPIO level of the first edge in the pulse sequence. May be either
0or1.forceSingleCode¶ (bool) – If
True, returns at most one hex code. IfFalse, returns as many hex codes as are contained in the pulse sequence. For instance, if the pulse sequence is decoded to12logical bits and bitCount is5, the number of produced hex counts will bemath.ceil(12 / 5)=3. Defaults toFalse.debugOutput¶ (StringIO, optional) – A string buffer for appending debug messages. Useful for analysing unexpected hex codes. Defaults to None.
The parameters basePulse_μs, tolerancePercentage, bitCount and bitOrder have the same meanings as with
__init__().- Returns:
The produced hex code as a str starting with
0x, all lowercase.- Return type:
list[str]