Skip to content
gtfierro edited this page Sep 18, 2014 · 8 revisions

Note: this feature is currently only supported on the unitoftime branch

##How Scheduling Works in sMAP

The sMAP scheduler service takes advantage of sMAP's publish-subscribe functionality to "advertise" a schedule that other sMAP sources can choose to listen to. Schedules are defined as a set of values to be published at a certain time, e.g. "set the heating setpoint to 60 at 8:00 AM." These declarations are advertised on the advent of the epoch, and are not reasserted by the scheduler service. This allows for other service and user overrides.

The scheduler service attaches metadata tags to the values it publishes. These tags are drawn from a known ontology, so sMAP sources can execute a metadata query on those tags to discover the scheduler; this removes the need for every sMAP source to know exactly where the scheduler is. If a sMAP source wishes to be scheduled, it subscribes to a stream of setpoints. When the scheduler notes that there is an epoch change, it publishes the new setpoints, and the sMAP archiver pushes those new setpoints to any sMAP source that is listening.

##Defining a Schedule

Schedules are defined as a simple JSON object:

{
    "master_schedule": {
        "fri": "weekday",
        "mon": "weekday",
        "sat": "weekend",
        "sun": "weekend",
        "thu": "weekday",
        "tue": "weekday",
        "wed": "weekday"
    },
    "schedules": [
        {
            "name": "weekday",
            "periods": [
                {
                    "name": "morning",
                    "points": [
                        {
                            "path": "temp_heat",
                            "value": 72
                        },
                        {
                            "path": "temp_cool",
                            "value": 83
                        }
                    ],
                    "start": "7:30"
                },
                {
                    "name": "afternoon",
                    "points": [
                        {
                            "path": "temp_heat",
                            "value": 70
                        },
                        {
                            "path": "temp_cool",
                            "value": 80
                        }
                    ],
                    "start": "13:30"
                },
                {
                    "name": "evening",
                    "points": [
                        {
                            "path": "temp_heat",
                            "value": 50
                        },
                        {
                            "path": "temp_cool",
                            "value": 90
                        }
                    ],
                    "start": "18:30"
                }
            ]
        },
        {
            "name": "weekend",
            "periods": [
                {
                    "name": "morning",
                    "points": [
                        {
                            "path": "temp_heat",
                            "value": 65
                        },
                        {
                            "path": "temp_cool",
                            "value": 85
                        }
                    ],
                    "start": "09:30"
                },
                {
                    "name": "afternoon",
                    "points": [
                        {
                            "path": "temp_heat",
                            "value": 70
                        },
                        {
                            "path": "temp_cool",
                            "value": 80
                        }
                    ],
                    "start": "17:30"
                },
                {
                    "name": "evening",
                    "points": [
                        {
                            "path": "temp_heat",
                            "value": 50
                        },
                        {
                            "path": "temp_cool",
                            "value": 90
                        }
                    ],
                    "start": "21:00"
                }
            ]
        }
    ]
}

this JSON object can be defined in an external file, as a web resource, or in a document store such as MongoDB.

The scheduler service parses this document to derive what values should be advertised at what times. The nested "path" refers to a URI endpoint defined in the scheduler service; the scheduled values are published on these endpoints.

# inside schedule.json
"points" : [
        {
          "path" : "temp_heat",
          "value" : 50
        },
...
# inside 'setup' method of scheduler.py
self.add_timeseries('/temp_heat', 'F', data_type='long')

We attach metadata to this endpoint in order to allow sMAP sources to discover it via a metadata query:

# in scheduler.ini
[/scheduler/temp_heat]
Metadata/Description = Master Heating setpoint
Metadata/System = HVAC
Metadata/Type = Setpoint

Metadata/Type and Metadata/System follow the same ontology as OpenBAS.

##Subscribing to a Scheduler

Subscriptions to a scheduler are done within the instantiation of an actuator, but the subscription information is typically passed to the actuator through the sMAP driver. This is because the sMAP driver setup() method receives all configuration settings from the .ini file upon initialization of the sMAP source. We do not want to hard code the subscription queries for the scheduler, so we put them in the .ini file:

# in thermostat.ini

[/thermostats/thermostat0]
type = smap.drivers.thermostats.virtualthermostat.VirtualThermostat
Metadata/Floor = 4
temp_heat = Metadata/System = 'HVAC' and Metadata/Description = 'Master Heating setpoint'
temp_cool = Metadata/System = 'HVAC' and Metadata/Description = 'Master Cooling setpoint'

The temp_heat and temp_cool items are not metadata, but are strings passed directly to the internal opts variable that is accessed in the setup() method of the sMAP driver. (We use opts.get('temp_heat') in order to access the subscription string).

When initializing an actuator, we pass the subscription string in using the subscribe keyword.

# in thermostat.py driver
# normal creation of timeseries endpoint
temp_heat = self.add_timeseries('/temp_heat', 'F', data_type='long') 
# create actuator for timeseries
temp_heat.add_actuator(SetpointActuator(path='temp_heat', _range=(45, 95), subscribe=opts.get('temp-heat')))

When instantiated, the actuator for the '/temp_heat' aspect of the thermostat will subscribe to whatever streams meet the metadata query "Metadata/System = 'HVAC' and Metadata/Description = 'Master Heating setpoint'". When a value is published to one of those streams (typically from the scheduler service), the actuator will call its own set_state method with the published value.

##Example

Scheduler

Scheduler config

Thermostat config

Thermostat driver

Clone this wiki locally