App Development Tutorial 5

Contents

Tutorial 5: Using the Clock

Thus far, we have explored the mechanisms for sensing inputs from the home, reasoning based on these inputs and actuating devices in the home.

To create context sensitive home automation apps we also need to understand the time at which particular behaviours occur within the home. It also must be possible to quickly test this time-based reasoning over the entire range of time. To this end, we've included a clock object in the CHAOS platform which allows the developer to query the current time, and step through time to test the performance of their apps over time with a variety of home inputs.

This tutorial will introduce the usage of the CHAOS clock object to enable temporal reasoning in home automation apps.

Background

The upper-right of the CHAOS Demonstrator shows the clock representing the current time. When you click one of the '+' buttons on the clock it will advance time by either an hour or a minute and an indicator will appear to confirm that we are in simulated time. You can click the 'x' on the indicator to return to normal time. These time values can be accessed via the self.clock object which is available by default to all apps.

Regular Time: Simulated Time
Clock.png ClockSimulatedTime.png

Reception of Time Change Signals

Every time the time value changes, one or more signals are emitted. Upon viewing the clock's tooltip we can see what signals are emitted and when:

ClockWithTooltipOpened.png

The minuteChanged signal is emitted once per minute, the hourChanged signal is emitted once per hour and the yearChanged signal is emitted once per year. Hence, the only time all 5 signals are emitted at the same time is when we transition from one year to the next.

All 5 of these signals contain the same attributes. Each one contains 5 integers, representing; year, month, day of month, hour and minute, in that order. As in previous lessons, any of these time change signals can be connected to methods:

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

                self.clock.minuteChanged[int,int,int,int,int].connect(self.onMinuteChanged)
                self.clock.hourChanged[int,int,int,int,int].connect(self.onHourChanged)

        ...
     
        def onMinuteChanged(self, year, month, day, hour, minute):
                self.print("The minute hand has just changed to: " + str(minute))

        def onHourChanged(self, year, month, day, hour, minute):
                self.print("The hour hand has just changed to: " + str(hour))

        ...

Evaluation of Current Time

As you can imagine, now that we can query the current time and/or trigger methods on time changes, our apps can evaluate the current time to decide what action to take.

If we wish to have a time-based trigger, we could use the code:

class ChaosApp(BaseChaosApp):
        def run(self):
                ...
                self.clock.minuteChanged[int,int,int,int,int].connect(self.onMinuteChanged)
               
        def onMinuteChanged(self, year, month, date, hour, minute):            
                # if the time is 23:15 perform our action:
                if hour == 23 and minute == 15:
                        self.print("Time to take action.")

Alternatively, if we had code that triggered on some other stimulus, like a door opening, we can access the clock object and evaluate the current time:

class ChaosApp(BaseChaosApp):
        def run(self):
                ...
                doorSensor = self.deviceList.getDevice("livingRoomDoorSensor")
                doorSensor.stateChanged[bool].connect(self.onDoorStateChanged)
               
        def onDoorStateChanged(self, newState):
                if newState:  # only if the door has opened:
                        timeTuple = self.clock.getDateTime()
                        self.print("Current time tuple = " + str(timeTuple))
                        # The returned time tuple has the contents: (year, month, dayOfMonth, hour, minute)
                        # With an index beginning at 0 we check for the current time of 23:15 using:
                        if timeTuple[3] == 23 and timeTuple[4]== 15:
                                self.print("The door has opened at the particular time, take action.")

Try this functionality for yourself. The code will work in both real and simulated time. So if you're feeling a little impatient and don't want to wait for hours for the correct time to arise, use the time '+' buttons to advance to the correct time while the apps are running.

The App

Let's solidify our understanding of clock usage by developing a simple app.

Objective

We want to write an app that encourages the user to go to bed on time by deactivating the TV at a certain time. To this end, we want to write an app that:

  1. Every time the clock advances by a minute, check the current time value
  2. If the time is 11.30pm, perform some actions to encourage the user to start getting ready to go to bed
  3. The action we will use to encourage the user to get ready to go to bed is to turn off the TV

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 5'
                self.print("Skeleton App '"+self.appName+"' Running")

                #Connect devices to other devices and functions here...
                self.clock.minuteChanged[int,int,int,int,int].connect(self.onMinuteChanged)
               
                self.tvActuator = self.deviceList.getDevice("tvSwitchActuator")

        def onMinuteChanged(self, year, month, date, hour, minute):
                # .zfill(2) zero pads a string up to 2 characters, otherwise time would
                # be 17:6 when there is only one digit in the minutes.
                self.print("Current time is "+str(hour)+":"+str(minute).zfill(2) )
               
                # if the time is 23:30 perform our action:
                if hour == 23 and minute == 30:
                        self.print("Time to take action...")
                        self.turnOffTv()
                       
        def turnOffTv(self):
                self.print("Turning Off the TV")
                self.tvActuator.turnOff()              
               
        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 + "'")

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

Be aware that testing this code will require you to hit the 23.30 mark exactly. If you advance the hour value you are likely to jump past the 23.30 time. Advance the time by one minute at a time to ensure 23.30 occurs.

Also be aware that to turn the TV back on to retest the code, you need to turn on the "tvSwitchActuator" device icon first, then press standby on the TV icon.

This code merely provides a simple example, a more complex example could check what room the user is in before taking any action. We could also use the smart TV's powerOff() method to make sure the TV is turned off, even if the user doesn't have a plug actuator connected to their TV. We will learn about this extra functionality in later tutorials. Turning off the TV in two different ways allows redundancy which enables an app to work in a wider range of homes with different device setups.

Wrap Up

This tutorial has presented how we can incorporate temporal information into an app by using the self.clock object. The next tutorial will advance onto the generation of rapid timed events by using timers in our code.

<< - <Prev - Next>