-
Notifications
You must be signed in to change notification settings - Fork 36
Scheduler Service
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