App Development Tutorial 4

Contents

Tutorial 4: Direct Device-to-Device Connections

The previous tutorial explained how to trigger a method when a device emits a signal and control another device within that method. We can also avoid the intermediate step and connect a signal directly to the input (or slot) of a device.

Background

Let's cover the basics of forming a direct device-to-device connection.

Device-to-Device Connections

As we learned already, we can connect a signal containing a known set of argument types to any custom method that takes as inputs the same number and types of arguments. Similarly, any signal that we can connect to a custom method can also be connected to a device's method, assuming the same criteria are met.

Remember the light actuator from the previous example:

LivingRoomLightActuatorTooltipOnSlots.png

If we wanted to connect the stateChanged signal to this device, we would not be able to connect it to the turnOn() method which we used in the previous example. This is because the signal arguments don't match the slot's arguments. Observe, however, that this device has a setState(bool) slot/method. This slot's set of input arguments matches the set of arguments in the setState signal. We can, therefore, connect them directly together.

This can be achieved in code by:

# Retrieving references to the devices for this example:
doorSensor = self.deviceList.getDevice("livingRoomDoorSensor")
light = self.deviceList.getDevice("livingRoomLight")

# Connecting the door sensor's stateChanged signal to a custom method in this app:
doorSensor.stateChanged[bool].connect(self.onSensorStateChanged)

# Connecting the door sensor's stateChanged signal directly to another device's slot:
doorSensor.stateChanged[bool].connect(light.setState)

This example shows a connection to a custom method, which we used in the previous example and compares it to a device-to-device connection. The only real difference is that for a custom method defined in this app we prepend the method name with self. whereas for a method/slot in another device, we prepend the method/slot with deviceReferenceVariable. .

This code will turn on the light every time the door opens and turn off the light every time the door closes. It will also call the custom method self.onSensorStateChanged(bool) when the door either opens or closes.

Disconnection

When we use device-to-device connections we also have to remember to disconnect the connections we created when the app stops. Previously, when we connected a device signal to a custom method, the signal was automatically disconnected and the functionality was disabled when the app was stopped. However, when our app connects a signal from one device directly to a slot in another device, that connection will continue to exist, even after the app which created the connection is stopped.

Hence, whenever we create a device-to-device connection in our app, we must explicitly put a corresponding disconnection in the stop() method of the app. Otherwise the connection will persist after the app has stopped, causing inexplicable behaviour for the user.

The syntax for a disconnection is identical to that for a connection, except we replace the 'connect' with 'disconnect'. For example:

class ChaosApp(BaseChaosApp):
        def run(self):
                ...

                self.doorSensor = self.deviceList.getDevice("livingRoomDoorSensor")
                self.light = self.deviceList.getDevice("livingRoomLight")
               
                self.doorSensor.stateChanged[bool].connect(self.light.setState)
               
        ...
     
        def stop(self):
                self.doorSensor.stateChanged[bool].disconnect(self.light.setState)

It is important to note here that we had to keep instance variables for the devices so that they were available for disconnection in the stop() method.

The App

To cement our understanding thus far, let's write another app.

Objective

We want to write an app that:

  1. When the main door (labelled "livingRoomDoorSensor") opens, turn on the living room light (labelled "livingRoomLight")
  2. When the main door closes, turn off the living room light
  3. When the app stops, the functionality should be stopped (i.e. make sure the signal is disconnected)

We could achieve this without using device-to-device connections, but it would require more code. Use device-to-device connections for this example to make the code as concise as possible.

Note: if your app is giving unexpected behaviour, look in the App Drawer and make sure there aren't other apps running at the same time. It is always a good idea to stop any other apps that are running when developing and testing your app.

Solution

When you want to compare with your solution, click Expand below.

Example Solution:

from __future__ import print_function
from ChaosApps import BaseChaosApp

class ChaosApp(BaseChaosApp):
        def run(self):
                self.appName='Tutorial 4'
                self.print("Skeleton App '"+self.appName+"' Running")

                #Connect devices to other devices and functions here...
                self.doorSensor = self.deviceList.getDevice("livingRoomDoorSensor")
                self.light = self.deviceList.getDevice("livingRoomLight")
               
                self.doorSensor.stateChanged[bool].connect(self.light.setState)
               
        def initAppGui(self):
                self.print("'" + self.appName + "': CHAOS has asked for the current HTML of the phone app GUI for this CHAOS App")

                htmlString = """appGuiConstructionString"""

                return htmlString

        def appGuiInteractionCallback(self,someData):
                self.print(self.appName + ": Some information has arrived for this CHAOS App from the phone GUI ")

        def stop(self):
                self.print("Disconnecting any created signals and then stopping '" + self.appName + "'")

                self.doorSensor.stateChanged[bool].disconnect(self.light.setState)

The highlighted lines are the lines we've added to the skeleton app to achieve this functionality.

Wrap Up

This tutorial has discussed how code can be simplified sometimes by connecting a device's signal directly to another device's slot. The next tutorial will explore how we can achieve different behaviour at different times of the day by using the clock object.

<< - <Prev - Next>