mqtt_gateways.cbus package

Module contents

Interface to C-BUS via the PCI module from Clipsal-Schneider.

Submodules

mqtt_gateways.cbus.cbus2mqtt module

Starter module for the C-Bus gateway.

mqtt_gateways.cbus.cbus_interface module

This module defines the cbusInterface class.

mqtt_gateways.cbus.cbus_interface._checksum(hexstring)

Returns True if hexstring is a valid hexadecimal string and if the checksum is correct as specified by the C-Bus documentation, otherwise returns False.

mqtt_gateways.cbus.cbus_interface._PATTERN_MonitoredSAL = '05([0-9A-F]{2})3800([0-9A-F]+)'

RegEx patterns to match incoming messages from C-Bus; see docs for terminology.

mqtt_gateways.cbus.cbus_interface._PATTERN_CALReply = '86[0-9A-F]{4}00([0-9A-F]{2})0738([0-9A-F]{2})([0-9A-F]+)'

RegEx patterns to match incoming messages from C-Bus; see docs for terminology.

mqtt_gateways.cbus.cbus_interface._PATTERN_LevelRequest = '\\05FF00730738'

Level request pattern (it is just a string here) - it needs to be followed by the Block Start and then x0D.

mqtt_gateways.cbus.cbus_interface._CBUSMSGMAXLEN = 16

C-Bus actual maximum message length.

mqtt_gateways.cbus.cbus_interface._STATUS_REQUEST_FREQ = 60

Number of seconds between each Status Request - arbitrary.

mqtt_gateways.cbus.cbus_interface._BLOCK_START = ['00', '20', '40', '60', '80', 'A0', 'C0', 'E0']

The 8 different possibilities for _BLOCK_START.

mqtt_gateways.cbus.cbus_interface._MAXARGNUMBER = 2

Maximum number of arguments accepted in a command. Needed for error management.

mqtt_gateways.cbus.cbus_interface._INTERNAL2CBUS = 0

Index for the dictionaries.

mqtt_gateways.cbus.cbus_interface._CBUS2INTERNAL = 1

Index for the dictionaries.

class mqtt_gateways.cbus.cbus_interface.cbusInterface(params, msglist_in, msglist_out)

Bases: mqtt_gateways.cbus.cbus_serial.cbusSerial

Represents the higher layer of communication with the C-Bus interface.

Parameters:
  • params (dictionary) – the parameters from the configuration file under the [INTERFACE] section.
  • msglists (list) – pair of lists of internalMsg objects; the first list is for the inbound messages, the second one for the outbound messages.
  • fullpath (string) – full absolute path of the application.
loop()

The compulsory method called periodically by the gateway.

The loop deals with inbound messages as expected, but also triggers a periodic status request per block as per C-Bus documentation. This is an extra feature to double check that no state change has been missed, but also to get the correct status at startup.

_execute_command(imsg)

Executes the command represented by the internal message.

The internal message might represent a single light to operate on or a whole set of lights (for a whole location for example), in which case there should be many commands to send. The algorithm concatenates the tokens depending on the number of arguments required by the action to create single commands (made of an action byte, an address byte and eventually a level byte), then concatenates those commands so that the maximum amount of messages are sent to the interface. According to the documentation, the maximum length of the code that can be sent to the serial interface is 21 bytes, excluding the leading x5C and the trailing x0D. This means 18 bytes for commands (excluding the ‘053800’ at the beginning). Therefore the length of concatenated commands is tested in order to avoid sending messages that are too long.

Parameters:

imsg (internalMsg object) – the message/command to execute

Raises:
  • cbusConnectionError – in case of a problem writing to the serial interface.
  • ValueError – when the message can not be converted in a C-Bus action.
_status_request()

Launch status request periodically.

Only one block is requested at a time. Depending on the frequency set, it might take some time to go through all the blocks. If all 8 blocks are used, the whole cycle takes 8 * _STATUS_REQUEST_FREQ to complete. As the initialisation code actually removes the blocks that do not contain any active lights, the actual cycle might be shorter.

_read_bus()

Reads one line from the CBus serial interface and converts it in a command list.

The line read is a string made of characters that are mostly hexadecimal with some special characters as well. It should always have at the end a <cr> and <lf>. The algorithm checks first for special situations (code too short, code ends with correct characters, code is an error or represent a restart request). Then it checks if it is a ‘Monitored SAL’ message, which means that an input unit (keypad or button) has sent a command to operate lights. Then it checks if the code is a ‘CAL Reply’ message, which means it is a reply to a status request and contains the status of one or more lights.

Raises:
  • cbusConnectionError – in case of problems reading the serial interface.
  • cbusInitError – in case of problems initialising the serial interface.

mqtt_gateways.cbus.cbus_serial module

This module defines the cbusSerial class representing the low-level communication layer with the C-Bus PCI serial interface.

mqtt_gateways.cbus.cbus_serial._s2p(hexs)

Helper to log properly the commands sent to C-BUS. It catches non printable characters and replaces them with hex string in the form ‘xFF’. Note: s2p = string to printable.

Parameters:hexs (string) – hexadecimal string.
Returns:the argument with non printable characters replaced by their hex value.
Return type:string
mqtt_gateways.cbus.cbus_serial._REQUESTS = [['Interface Options 1 Settings Live', '@2A3001', '8230301E', '~@A3300030', '3230009E'], ['Interface Options 1 Power Up Settings', '@1A4101', '8241300D', '@A3410030', '3241008D'], ['Interface Options 2', '@1A3E01', '823E0040', '@A33E0000', '323E0090'], ['Interface Options 3', '@1A4201', '82420F2D', '@A342000F', '3242008C']]

Hard coded data needed to request and set specific parameters of the C-Bus serial interface, as well as the values of these parameters that are needed for this interface to operate properly. More specifically:

  • Options 1: SMART mode ON and MONITOR ON, all else OFF;
  • Options 2: all OFF;
  • Options 3: PCN, LOCAL_SAL, PUN and EXSTAT ON.

The format is: [Parameter Name for reference, Request code, Desired Answer, Set Command code, Acknowledge code]

mqtt_gateways.cbus.cbus_serial._PAUSE = 0.1

in seconds, to leave time to the PCI to process the commands; completely arbitrary, might be useless.

mqtt_gateways.cbus.cbus_serial._THROTTLELAG = 600

in seconds, lag to throttle communication error with the PCI.

exception mqtt_gateways.cbus.cbus_serial.cbusError

Bases: exceptions.Exception

Local Exception.

exception mqtt_gateways.cbus.cbus_serial.cbusInitError

Bases: exceptions.Exception

Initialisation of the Serial Interface Error.

exception mqtt_gateways.cbus.cbus_serial.cbusConnectionError(msg=None)

Bases: mqtt_gateways.utils.throttled_exception.ThrottledException

Connection Error for the Serial Interface.

class mqtt_gateways.cbus.cbus_serial.cbusSerial(port)

Bases: serial.serialposix.Serial

Represents the low-level communication layer with the C-Bus PCI serial interface.

It is an extension of the pySerial.Serial class. It allows to open the port specifically to communicate with the C-Bus interface, as well as initiate its parameters. The methods defined override the serial library ones; they mostly catch the exceptions and log the errors if any.

Any code using these methods must make sure that values returned are tested and that exceptions are being caught. In those cases probably the port is not working and need to be restarted or the application has to stop. This allows to write non-blocking code that deals with the bad connection only at higher levels, for example in the main loop, rather than at each write or read command.

The constructor tries to open the port but might not succeed. Therefore the class instance is always created but the port might be open or not. Use the serial library methods open() to later open the port in case of failure, and is_open() to test if it is opened already.

Parameters:
  • port (string) – port name, passed as is to the Serial library
  • full_path (string) – used to ‘hook’ to the root logger; should contain the application name
Raises:

cbusInitError – if the serial interface can not be opened.

readline()

Reads a full line from the serial interface. Overrides parent class method.

Raises:cbusConnectionError – in case of a SerialException from the interface
write(code)

Writes the code to the interface. Overrides parent class method.

Parameters:code (string) – characters or bytes to write to the serial interface
Returns:number of characters or bytes written to the interface
Return type:int
Raises:cbusConnectionError – if there are any problems during the writing process
init_pci_options()

Initialises the parameters of the PCI interface.

This code relies on the _REQUESTS list of parameters. Going through every item of the list, the code asks for the status of a parameter ‘block’, checks the reply against what is expected, requests an update with the correct values in case they aren’t, and checks the reply to the update to make sure it has been accepted.

Raises:cbusInitError – if something fatal happens during this process

mqtt_gateways.cbus.cbus_data module

This module contains the data necessary to decode or encode C-Bus messages.

mqtt_gateways.cbus.cbus_data.LIGHTS = {'Bathroom_Spots': ['0D', 'Bathroom'], 'Bedroom_Bedside': ['0A', 'Bedroom'], 'Bedroom_Socket': ['0B', 'Bedroom'], 'Bedroom_Spots': ['0C', 'Bedroom'], 'DiningRoom_Pendant': ['04', 'DiningRoom'], 'DiningRoom_Socket': ['05', 'DiningRoom'], 'Kitchen_Spots': ['01', 'Kitchen'], 'Kitchen_UnderUnit': ['02', 'Kitchen'], 'LivingRoom_Spots': ['06', 'LivingRoom'], 'LivingRoom_Wall': ['07', 'LivingRoom'], 'Office_Socket': ['08', 'Office'], 'Office_Spots': ['09', 'Office'], 'TVRoom_Spots': ['03', 'TVRoom']}

Dictionary of lights names with their C-Bus codes and their location.

The dictionary is in the form 'internal name of device/light':['C-Bus address', 'location name']. This dictionary allows to build at once the devices dictionary as well as the location one. Obviously the lights names must be unique, as well as the corresponding C-Bus codes. This setup ensures every item (light name, location name, light C-Bus code) appears only once and there are no risks of duplicates.

mqtt_gateways.cbus.cbus_data.ACTIONS = [['LIGHT_ON', ['79']], ['LIGHT_OFF', ['01']], ['LIGHT_LVL', ['02', '%%']], ['RAMP_0S_LVL', ['02', '%%']], ['RAMP_4S_LVL', ['0A', '%%']], ['RAMP_8S_LVL', ['12', '%%']], ['RAMP_12S_LVL', ['1A', '%%']], ['RAMP_20S_LVL', ['22', '%%']], ['RAMP_30S_LVL', ['2A', '%%']], ['RAMP_40S_LVL', ['32', '%%']], ['RAMP_60S_LVL', ['3A', '%%']], ['RAMP_90S_LVL', ['42', '%%']], ['RAMP_2M_LVL', ['4A', '%%']], ['RAMP_3M_LVL', ['52', '%%']], ['RAMP_5M_LVL', ['5A', '%%']], ['RAMP_7M_LVL', ['62', '%%']], ['RAMP_10M_LVL', ['6A', '%%']], ['RAMP_15M_LVL', ['72', '%%']], ['RAMP_17M_LVL', ['7A', '%%']], ['TERMINATERAMP', ['09']], ['LIGHT_LOW', ['02', '55']], ['LIGHT_MEDIUM', ['02', 'AA']]]

List of local actions and their C-Bus hex code equivalent.

Only short commands are used here (as defined in C-Bus documentation) as long commands seem to be only needed for labels. The 3 last bits of short commands indicate the number of arguments required. In practice, only the ON and OFF commands (and TERMINATERAMP, rarely used) require one argument only (the Address) while all the others (the RAMP to LEVEL ones) require two arguments (the Address and the Level to reach). No other commands are allowed.

The list is made of pairs where the first element is the internal name of the action and the second element is another list made of the C-Bus codes representing this action. This list of C-Bus codes has one or two elements. The first one represents the actual action code in C-Bus (ON, OFF, RAMP, …) and the second one represents the LEVEL to reach in the RAMP case. In the case of standard actions, the argument is left variable and has to be communicated as an argument of the action; in this case the convention is to represent it with a %%.

The code allows to create different ways to execute the same action, e.g. switching a light to a medium level can be achieved with the action LIGHT_LVL and a parameter AA or with the custom made action LIGHT_MEDIUM. Add your own actions at the end of the list.

This data is represented in a list because the order here matters to build the reverse dictionary. The reverse dictionary will only contain the standard actions, which means that in the translation from C-Bus to MQTT, the custom-made actions are not taken into account.

mqtt_gateways.cbus.cbus_data.FUNCTIONS = {'Lighting': '38'}

Dictionary of Functions or Applications as defined in C-Bus.

The dictionary is in the form 'internal name':'C-Bus code'. This dictionary should not be modified but can be appended with additional Applications from C-Bus.

mqtt_gateways.cbus.cbus_data.LEVELS = {'55': 'F', '56': 'E', '59': 'D', '5A': 'C', '65': 'B', '66': 'A', '69': '9', '6A': '8', '95': '7', '96': '6', '99': '5', '9A': '4', 'A5': '3', 'A6': '2', 'A9': '1', 'AA': '0'}

Dictionary for the correspondence of levels definition in C-Bus status replies.

From C-Bus documentation. Do not change.