Project: HomeAutomation

Example: Switching on a room light

This is a copy from the HomeAutomation project documentation PDF.

To get an overview of the different components in the HomeAutomation system and their functionality, this section will follow a user action through the HA stack with reasonable detail: A user presses an in-wall switch to turn on the light in a room.

This explanation will neccessarily include more technical details than are discussed on these web pages, for example message identifiers exchanged on the bus, and hardware component details. If required, these can be looked up in the PDF documentation..

A digital input controller frequently checks its connected inputs for state changes. As long as the user presses the pushbutton light switch, this causes one of the controller's input lines to read a high level, which is a change from the default low level. Therefore, the controller sends an INDIGPULSE message on the CAN bus, informing the HACS about that change in state.

This message is read from the bus by the CAN/USB interface attached to the HACS, and converted into a serial line message. This in turn is read by ha_iod on the HACS, and dispatched to its TCP clients.

One receiving TCP client is ha_control's iodclt thread, which is now responsible for generating an event message from this datagram, and placing it on the queues of other ha_control threads that have registered for notification about such events. For compatibility with the user-centric configuration file, the CAN-bus identifier (bus number, DID and channel number) is converted to the room.number identifier when creating the event notice. As layer of abstraction on the input side, the INDIGPULS CAN notice is translated to a switch_toggled event.

At least the actor thread is then notified about this event.

The actor thread has a table of possible events and the associated actions as configured through the actions.xml configuration and associated configuration files, in particular macros and group configurations. As a single light is to be switched, macros and groups do not apply for this example, though, so the action to cause is just the immediate switching of a single device. This device is identified by its device identificator (room.number type), and the matching Device instance (in this case, a relayoutputclass Device instance) is looked up.

The actor thread has been configured by the user to create an output_toggle action when a switch_toggled event is received for the respective light switch device, so it calls the Device.action_trigger() method with the action='output_toggle' argument.

The Device.action_trigger() method creates from this an action command as a CAN datagram (actually, a list of CAN datagrams containing only a single datagram). The Device class knows its own address in both room.number and bus+DID+channel forms. The created datagram is addressed to the CAN address of the digital output controller to which the actor for the lamp is connected, the MTID is set as OUTDIG, and data[0] to the channel number. data[1] is set to 3, the toggle command. The completed datagram is then handed to the iodclt thread for transmission down the stream.

The iodclt thread sends the datagram through its TCP connection to ha_iod, which in turn reformats it and sends it through the USB-serial line to the CAN/USB controller. This device creates an actual CAN message from it, identifies the bus number to use, places it in the respective CAN controller's transmission buffer, and initiates the transmission. As soon as the bus is free to be used (or the HACS' CAN interface wins the bus arbitration process), the datagram will be transmitted to the actor bus device.

On the actor bus device, the message is received and evaluated, resulting in the identification of the I/O port for which a newly set state is desired. First, the current state of the port is measured through the actor's feedback line. Because the toggle command was received, this state is inverted to get the actually desired new state. The respective I/O port control signal is asserted to switch the actor, and the feedback line is monitored until the desired state has been entered, or a timeout has been reached. Then, the I/O port control signal is deactivated again, and a final measurement is done on the feedback line. If all components work, this measurement gives the desired new port state, and the respective room is illuminated now.

If the I/O port is found to be in the desired state, the bus device sends a SYSM_LO_C, CI=S_IOSTATE CAN message for the respective channel number and including the new state value to signal either on or off. If the final feedback line measurement did not confirm the desired state, a SYSM_LO_C, CI=S_OFAIL message is sent instead, and the I/O port is marked as failed in non-volatile memory in the bus device. If a timeout has occurred while driving the actor, but the final measurement confirms that the new state has been activated on the actor, a SYSM_LO_C, CI=S_ODEGRAD message is sent, and the I/O port is not registered as failed. In that case, operator interaction would still be expected to mitigate the imminent risk of the output device terminally failing.

The SYSM_LO_C message sent by the bus device is received by ha_control as detailed above, and handed to the Device instance which commanded the change in state by calling Device.can_msg_handler() with the respective event message as parameter. There, the internal state of the bus device is updated accordingly (Device.currentstate is set to reflect the new state of the actor).

If an error indicator (SYSM_LO_C, CI=S_OFAIL or CI=S_ODEGRAD) was received instead, appropriate error reporting would be initiated by ha_control on request of the Device instance.


Last page update: 2015-06-29