class riceCookerApp(driver.SmapDriver):
    def setup(self, opts):
        self.rate = float(opts.get('rate', 1))
        self.add_timeseries('/temp', 'unit', data_type='double')
        self.set_metadata(
            '/temp', {
                'Metadata/Description': 'Application for rice cooker',
            })

        self.archiverurl = opts.get('archiverurl',
                                    'http://shell.storm.pm:8079')
        self.subscription = opts.get(
            'subscription', 'Metadata/SourceName = "Rice Cooker Sensor"')
        self.r = RepublishClient(self.archiverurl,
                                 self.cb,
                                 restrict=self.subscription)

    def cb(self, points):
        print points
        #curr_time = int(time.time())
        #self.add('/temp', curr_time)

    def start(self):
        self.r.connect()
        util.periodicSequentialCall(self.dummyData).start(1)

    def dummyData(self):
        print "hello"
        curr_time = int(time.time())
        self.add("/temp", curr_time, float(2))

    def stop(self):
        print "Quit"
        self.stopping = True
Esempio n. 2
0
class Room(driver.SmapDriver):
    def setup(self, opts):
        # source of streaming data
        self.archiver_url = opts.pop('archiver_url', 'http://localhost:8079')
        # streaming data uuid
        self.oatuuid = opts.get('oatuuid')
        # subscribe to datastream
        restriction = "uuid = '{0}'".format(self.oatuuid)
        self.oatclient = RepublishClient(self.archiver_url,
                                         self.oatcb,
                                         restrict=restriction)

        self.cooluuid = opts.get('cooluuid')
        restriction = "uuid = '{0}'".format(self.cooluuid)
        self.coolclient = RepublishClient(self.archiver_url,
                                          self.coolcb,
                                          restrict=restriction)

        # rate between recalculating room model
        self.rate = float(opts.get('rate', 1))
        # thermal resistance of room
        self.therm_resistance = float(opts.get('therm_resistance', .1))
        # initial temperature of room
        self.temp = float(opts.get('starttemp', 75))
        # epsilon for room (for cooling)
        self.e = float(opts.get('epsilon', .1))

        # state vars
        self.oat_val = 0
        self.cool = 0

        self.add_timeseries('/currenttemp', 'F', data_type='double')

    def start(self):
        self.oatclient.connect()
        self.coolclient.connect()
        periodicSequentialCall(self.read).start(self.rate)

    def read(self):
        dt = (self.oat_val - self.temp)
        print self.temp, self.rate, self.therm_resistance, self.cool, self.e
        self.temp = self.temp + self.rate * self.therm_resistance * dt - self.cool * self.e * dt
        self.add('/currenttemp', self.temp)

    def oatcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.oat_val = mostrecent[1]

    def coolcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.cool = mostrecent[1]
        print 'am i cooling', self.cool
Esempio n. 3
0
class Room(driver.SmapDriver):
    def setup(self, opts):
        # source of streaming data
        self.archiver_url = opts.pop('archiver_url','http://localhost:8079')
        # streaming data uuid
        self.oatuuid = opts.get('oatuuid')
        # subscribe to datastream
        restriction = "uuid = '{0}'".format(self.oatuuid)
        self.oatclient = RepublishClient(self.archiver_url, self.oatcb, restrict=restriction)

        self.cooluuid = opts.get('cooluuid')
        restriction = "uuid = '{0}'".format(self.cooluuid)
        self.coolclient = RepublishClient(self.archiver_url, self.coolcb, restrict=restriction)

        # rate between recalculating room model
        self.rate = float(opts.get('rate',1))
        # thermal resistance of room
        self.therm_resistance = float(opts.get('therm_resistance', .1))
        # initial temperature of room
        self.temp = float(opts.get('starttemp', 75))
        # epsilon for room (for cooling)
        self.e = float(opts.get('epsilon',.1))

        # state vars
        self.oat_val = 0
        self.cool = 0

        self.add_timeseries('/currenttemp','F',data_type='double')

    def start(self):
        self.oatclient.connect()
        self.coolclient.connect()
        periodicSequentialCall(self.read).start(self.rate)

    def read(self):
        dt = (self.oat_val - self.temp)
        print self.temp, self.rate, self.therm_resistance, self.cool,self.e
        self.temp = self.temp + self.rate * self.therm_resistance * dt - self.cool*self.e*dt
        self.add('/currenttemp', self.temp)

    def oatcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.oat_val = mostrecent[1]

    def coolcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.cool = mostrecent[1]
        print 'am i cooling', self.cool
Esempio n. 4
0
class Controller(driver.SmapDriver):
    def setup(self, opts):
        # source of streaming data
        self.archiver_url = opts.pop('archiver_url', 'http://localhost:8079')
        # streaming data uuid
        self.roomuuid = opts.get('roomuuid')
        # subscribe to datastream
        restriction = "uuid = '{0}'".format(self.roomuuid)
        self.roomclient = RepublishClient(self.archiver_url,
                                          self.controlcb,
                                          restrict=restriction)
        # setpoint
        self.sp = float(opts.get('setpoint', 72))
        # deadband
        self.db = float(opts.get('deadband', 1))
        # period between calling controller
        self.rate = float(opts.get('rate', 1))
        self.cool = 0  # off

        self.add_timeseries('/cool', 'On/Off', data_type='long')

        # initialize current temperature to setpoint
        self.cur_temp = self.sp

        self.cool_controller = cool_controller

    def start(self):
        self.roomclient.connect()
        periodicSequentialCall(self.read).start(self.rate)

    def read(self):
        # calculate and send control decision
        #print self.cool_controller(self.cur_temp, self.sp, self.db)
        #self.add('/cool', self.cool_controller(self.cur_temp, self.sp, self.db))
        if self.cool:  #
            self.cool = self.cur_temp > self.sp - self.db
        else:
            self.cool = self.cur_temp > self.sp + self.db
        self.add('/cool', int(self.cool))

    def controlcb(self, _, data):
        # parse streaming data
        mostrecent = data[-1][-1]
        self.cur_temp = mostrecent[1]
Esempio n. 5
0
class Controller(driver.SmapDriver):
    def setup(self, opts):
        self.archiver_url = opts.pop('archiver_url', 'http://localhost:8079')
        self.sp = float(opts.get('setpoint', 78))
        self.db = float(opts.get('deadband', 1))
        self.rate = float(opts.get('rate', 1))

        # Initialize controller state
        self.cur_temp = self.sp
        self.state = 0  # initial cool is off

        # subscribe to zone air temperature
        self.zonepath = opts.pop('zonepath', '/room/airtemp')
        self.siteid = opts.pop('zonesiteid', '')
        restriction = "Path = '{0}'".format(self.zonepath)
        if self.siteid:
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(
                self.siteid)
        self.roomclient = RepublishClient(self.archiver_url,
                                          self.controlcb,
                                          restrict=restriction)

        # create timeseries for contoller actions
        self.add_timeseries('/cool', 'On/Off', data_type='long')

    def start(self):
        self.roomclient.connect()
        periodicSequentialCall(self.read).start(self.rate)

    # Periodically schedule controller event
    #  - update control state
    #  - build timeseries of actions
    def read(self):
        if (self.cur_temp > self.sp + self.db): self.state = 1  # start cool
        if (self.cur_temp < self.sp - self.db): self.state = 0  # stop cool
        self.add('/cool', self.state)  # publish the state change

    # Handle temperature reporting event
    #  record most recent zone temperature for next contol event
    def controlcb(self, _, data):
        mostrecent = data[-1][-1]
        self.cur_temp = mostrecent[1]
class Controller(driver.SmapDriver):
    def setup(self, opts):
        # source of streaming data
        self.archiver_url = opts.pop('archiver_url','http://localhost:8079')
        # streaming data uuid
        self.roomuuid = opts.get('roomuuid')
        # subscribe to datastream
        restriction = "uuid = '{0}'".format(self.roomuuid)
        self.roomclient = RepublishClient(self.archiver_url, self.controlcb, restrict=restriction)
        # setpoint
        self.sp = float(opts.get('setpoint',72))
        # deadband
        self.db = float(opts.get('deadband',1))
        # period between calling controller
        self.rate = float(opts.get('rate',1))
        self.cool = 0 # off

        self.add_timeseries('/cool', 'On/Off',data_type='long')

        # initialize current temperature to setpoint
        self.cur_temp = self.sp

        self.cool_controller = cool_controller

    def start(self):
        self.roomclient.connect()
        periodicSequentialCall(self.read).start(self.rate)

    def read(self):
        # calculate and send control decision
        #print self.cool_controller(self.cur_temp, self.sp, self.db)
        #self.add('/cool', self.cool_controller(self.cur_temp, self.sp, self.db))
        if self.cool: #
            self.cool = self.cur_temp > self.sp - self.db
        else:
            self.cool = self.cur_temp > self.sp + self.db
        self.add('/cool',int(self.cool))

    def controlcb(self, _, data):
        # parse streaming data
        mostrecent = data[-1][-1]
        self.cur_temp = mostrecent[1]
class Controller(driver.SmapDriver):
    def setup(self, opts):
        self.archiver_url = opts.pop('archiver_url','http://localhost:8079')
        self.sp = float(opts.get('setpoint',78))
        self.db = float(opts.get('deadband',1))
        self.rate = float(opts.get('rate',1))

        # Initialize controller state
        self.cur_temp = self.sp
        self.state = 0          # initial cool is off

        # subscribe to zone air temperature
        self.zonepath = opts.pop('zonepath','/room/airtemp')
        self.siteid = opts.pop('zonesiteid','')
        restriction = "Path = '{0}'".format(self.zonepath)
        if self.siteid :
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(self.siteid)
        self.roomclient = RepublishClient(self.archiver_url, self.controlcb, restrict=restriction)

        # create timeseries for contoller actions
        self.add_timeseries('/cool', 'On/Off', data_type='long')

    def start(self):
        self.roomclient.connect()
        periodicSequentialCall(self.read).start(self.rate)

    # Periodically schedule controller event
    #  - update control state
    #  - build timeseries of actions
    def read(self):
        if (self.cur_temp > self.sp + self.db) : self.state = 1 # start cool
        if (self.cur_temp < self.sp - self.db) : self.state = 0 # stop cool
        self.add('/cool', self.state) # publish the state change

    # Handle temperature reporting event
    #  record most recent zone temperature for next contol event
    def controlcb(self, _, data):
        mostrecent = data[-1][-1]
        self.cur_temp = mostrecent[1]
class VirtualThermostat(driver.SmapDriver):
    def setup(self, opts):
        self.state = {
            'temp': 70,
            'humidity': 50,
            'hvac_state': 1,
            'temp_heat': 70,
            'temp_cool': 75,
            'hold': 0,
            'override': 0,
            'hvac_mode': 1,
            'fan_mode': 1,
        }

        self.readperiod = float(opts.get('ReadPeriod', 3))
        self.add_timeseries('/temp', 'F', data_type='long')
        self.add_timeseries('/humidity', '%RH', data_type='long')
        self.add_timeseries('/hvac_state', 'Mode', data_type='long')
        temp_heat = self.add_timeseries('/temp_heat', 'F', data_type='long')
        temp_cool = self.add_timeseries('/temp_cool', 'F', data_type='long')
        hold = self.add_timeseries('/hold', 'On/Off', data_type='long')
        override = self.add_timeseries('/override', 'On/Off', data_type='long')
        hvac_mode = self.add_timeseries('/hvac_mode', 'Mode', data_type='long')
        fan_mode = self.add_timeseries('/fan_mode', 'Mode', data_type='long')

        temp_heat.add_actuator(
            SetpointActuator(tstat=self, path='temp_heat', _range=(45, 95)))
        temp_cool.add_actuator(
            SetpointActuator(tstat=self, path='temp_cool', _range=(45, 95)))
        hold.add_actuator(OnOffActuator(tstat=self, path='hold'))
        override.add_actuator(OnOffActuator(tstat=self, path='override'))
        hvac_mode.add_actuator(
            ModeActuator(tstat=self, path='hvac_mode', states=[0, 1, 2, 3]))
        fan_mode.add_actuator(OnOffActuator(tstat=self, path='fan_mode'))

        self.archiver_url = opts.pop('archiver_url', 'http://localhost:8079')
        self.heatSPpath = opts.pop('heatSPpath', '/scheduler/heatSetpoint')
        self.coolSPpath = opts.pop('coolSPpath', '/scheduler/coolSetpoint')
        # add mode
        self.siteid = opts.pop('siteid', '')
        restriction = "Path = '{0}'".format(self.heatSPpath)
        if self.siteid:
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(
                self.siteid)
        self.heatSPclient = RepublishClient(self.archiver_url,
                                            self.heatSPcb,
                                            restrict=restriction)
        restriction = "Path = '{0}'".format(self.coolSPpath)
        if self.siteid:
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(
                self.siteid)
        self.coolSPclient = RepublishClient(self.archiver_url,
                                            self.coolSPcb,
                                            restrict=restriction)

        metadata_type = [('/temp', 'Sensor'), ('/humidity', 'Sensor'),
                         ('/temp_heat', 'Reading'), ('/temp_heat_act', 'SP'),
                         ('/temp_cool', 'Reading'), ('/temp_cool_act', 'SP'),
                         ('/hold', 'Reading'), ('/hold_act', 'Command'),
                         ('/override', 'Reading'),
                         ('/override_act', 'Command'),
                         ('/hvac_mode', 'Reading'),
                         ('/hvac_mode_act', 'Command')]
        for ts, tstype in metadata_type:
            self.set_metadata(ts, {'Metadata/Type': tstype})

    def start(self):
        self.heatSPclient.connect(
        )  # activate subscription scheduler setpoints
        self.coolSPclient.connect()
        periodicSequentialCall(self.read).start(self.readperiod)

    def read(self):
        for k, v in self.state.iteritems():
            self.add('/' + k, v)

    # Event handler for publication to heatSP stream
    def heatSPcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.heatSP = mostrecent[1]
        print "Set heating setpoint", self.heatSP
        self.state['temp_heat'] = self.heatSP

    def coolSPcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.coolSP = mostrecent[1]
        print "Set cooling setpoint", self.coolSP
        self.state['temp_cool'] = self.coolSP
class ZoneController(driver.SmapDriver):
    def setup(self, opts):
        self.rate = float(opts.get('rate',10))
        # Current state of the points
        self.heatSP=int(opts.get('defaultHeatSetpoint',68))
        self.coolSP=int(opts.get('defaultCoolSetpoint',76))

        self.therm_temp = 70

        self.trim = int(opts.get('trim',0)) # dummy zoneCtrl action

        # create timeseries for zone controller actions
        heatSetPoint = self.add_timeseries('/heatSetpoint', 'F', data_type='double')
        coolSetPoint = self.add_timeseries('/coolSetpoint', 'F', data_type='double')
        # add actuators to them
        heatSetPoint.add_actuator(setpointActuator(controller=self, range=(40,90)))
        coolSetPoint.add_actuator(setpointActuator(controller=self, range=(40,90)))

        # get master set point stream paths
        self.archiver_url = opts.get('archiver_url','http://localhost:8079')
        self.heatSPwhere = opts.get('heatSPwhere', '')
        self.coolSPwhere = opts.get('coolSPwhere', '')
        self.thermwhere = opts.get('thermwhere', '')
        self.tempwhere = opts.get('tempwhere', '')

        print "ZoneController: heat sp where = ", self.heatSPwhere
        print "ZoneController: cool sp where = ", self.coolSPwhere
        print "ZoneController: thermostat where = ", self.thermwhere
        print "ZoneController: temp sensor where = ", self.tempwhere

        self.client = SmapClient(self.archiver_url)

        self.heatSPclient = RepublishClient(self.archiver_url, self.heatSPcb, restrict=self.heatSPwhere)
        self.coolSPclient = RepublishClient(self.archiver_url, self.coolSPcb, restrict=self.coolSPwhere)
        #self.tempclient = RepublishClient(self.archiver_url, self.tempcb, restrict=self.tempwhere)
        self.thermclient = RepublishClient(self.archiver_url, self.thermcb, restrict=self.thermwhere)


    def start(self):
        print "zone controller start: ", self.rate
        self.heatSPclient.connect() # activate subscription scheduler setpoints
        self.coolSPclient.connect() 
        #self.tempclient.connect() 
        self.thermclient.connect() 
        periodicSequentialCall(self.read).start(self.rate)

    def read(self):
        all_readings = self.client.latest(self.tempwhere)
        for p in all_readings:
            print '-'*20
            md = self.client.tags('uuid = "'+p['uuid']+'"')[0]
            print 'Room:', md['Metadata/Room']
            print 'Reading:', p['Readings'][0][1]
            ts = dtutil.ts2dt(p['Readings'][0][0]/1000)
            print 'Time:', dtutil.strftime_tz(ts, tzstr='America/Los_Angeles')
        avg_room_temp = sum([x['Readings'][0][1] for x in all_readings]) / float(len(all_readings))

        # get difference between avg room temperature and thermostat temperature
        new_diff = self.therm_temp - avg_room_temp

        # periodically update output streams.  Here a bogus adjustment
        self.add('/heatSetpoint', self.heatSP + new_diff)
        self.add('/coolSetpoint', self.coolSP + new_diff)
        print "zone controller publish: ", self.heatSP, self.coolSP

    # Event handler for publication to heatSP stream
    def heatSPcb(self, _, data):
        # list of arrays of [time, val]
        print "ZoneController heatSPcb: ", data
        mostrecent = data[-1][-1] 
        self.heatSP = mostrecent[1]

    def coolSPcb(self, _, data):
        # list of arrays of [time, val]
        print "ZoneController coolSPcb: ", data
        mostrecent = data[-1][-1] 
        self.coolSP = mostrecent[1]

    def tempcb(self, _, data):
        # list of arrays of [time, val]
        print "ZoneController tempcb: ", data


    def thermcb(self, _, data):
        # list of arrays of [time, val]
        print "ZoneController thermcb: ", data
        self.therm_temp = data[-1][-1][1]
Esempio n. 10
0
class Room(driver.SmapDriver):
    def badclient(self, resp):
        print "Error connecting: ", resp

    def setup(self, opts):
        self.archiver_url = opts.pop('archiver_url', 'http://localhost:8079')
        self.oatpath = opts.pop('OATpath', '/OAT/temperature')
        self.coolpath = opts.pop('coolpath', '/control/cool')
        self.siteid = opts.pop('siteid', '')

        # Subscription to OAT stream, registering a callback
        restriction = "Path = '{0}'".format(self.oatpath)
        if self.siteid:
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(
                self.siteid)
        self.oatclient = RepublishClient(self.archiver_url,
                                         self.oatcb,
                                         restrict=restriction,
                                         connect_error=self.badclient)

        # subscribe to the cool control stream
        restriction = "Path = '{0}'".format(self.coolpath)
        if self.siteid:
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(
                self.siteid)

        self.coolclient = RepublishClient(self.archiver_url,
                                          self.coolcb,
                                          restrict=restriction,
                                          connect_error=self.badclient)

        # initalize parameters for the room model
        self.rate = float(opts.get('rate', 1))  # model update rate
        self.therm_resistance = float(opts.get(
            'therm_resistance', .1))  # thermal resistance factor
        self.e = float(opts.get('epsilon', .1))  # cooling factor

        # initial state of the room
        self.temp = float(opts.get('starttemp', 75))  # initial room
        self.oat_val = self.temp
        self.cool = 0

        # Create the timeseries for the pseudo air temp sensor
        self.add_timeseries('/airtemp', 'F', data_type='double')

    # start the driver
    def start(self):
        self.oatclient.connect()  # activate subscription to OAT stream'
        self.coolclient.connect()  # activate subscription to cool stream
        periodicSequentialCall(self.read).start(
            self.rate)  # schedule model periodically

    # Model simple physics of a room with heating across a thermally
    # resistant barrier and forced cooling
    def read(self):
        dt = (self.oat_val - self.temp)
        #        self.temp = self.oat_val + 2.3 # little test during debugging
        self.temp = self.temp + self.rate * self.therm_resistance * dt - self.cool * self.e * dt
        self.add('/airtemp', self.temp)  # add new value to the airtemp stream

    # Event handler for publication to OAT stream
    def oatcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1]
        self.oat_val = mostrecent[1]

    # Event handler for publication to cool stream
    def coolcb(self, _, data):
        mostrecent = data[-1][-1]
        self.cool = mostrecent[1]
class VirtualThermostat(driver.SmapDriver):
    def setup(self, opts):
        self.state = {'temp': 70,
                      'humidity': 50,
                      'hvac_state': 1,
                      'temp_heat': 70,
                      'temp_cool': 75,
                      'hold': 0,
                      'override': 0,
                      'hvac_mode': 1,
                      'fan_mode': 1,
                      }

        self.readperiod = float(opts.get('ReadPeriod',3))
        self.add_timeseries('/temp', 'F', data_type='long') 
        self.add_timeseries('/humidity', '%RH', data_type='long') 
        self.add_timeseries('/hvac_state', 'Mode', data_type='long') 
        temp_heat = self.add_timeseries('/temp_heat', 'F', data_type='long') 
        temp_cool = self.add_timeseries('/temp_cool', 'F', data_type='long') 
        hold = self.add_timeseries('/hold', 'On/Off', data_type='long') 
        override = self.add_timeseries('/override', 'On/Off', data_type='long') 
        hvac_mode = self.add_timeseries('/hvac_mode', 'Mode', data_type='long') 
        fan_mode = self.add_timeseries('/fan_mode', 'Mode', data_type='long') 

        temp_heat.add_actuator(SetpointActuator(tstat=self, path='temp_heat', _range=(45, 95)))
        temp_cool.add_actuator(SetpointActuator(tstat=self, path='temp_cool', _range=(45, 95)))
        hold.add_actuator(OnOffActuator(tstat=self, path='hold'))
        override.add_actuator(OnOffActuator(tstat=self, path='override'))
        hvac_mode.add_actuator(ModeActuator(tstat=self, path='hvac_mode', states=[0,1,2,3]))
        fan_mode.add_actuator(OnOffActuator(tstat=self, path='fan_mode'))

        self.archiver_url = opts.pop('archiver_url','http://localhost:8079')
        self.heatSPpath = opts.pop('heatSPpath', '/scheduler/heatSetpoint')
        self.coolSPpath = opts.pop('coolSPpath', '/scheduler/coolSetpoint')
# add mode
        self.siteid = opts.pop('siteid','')
        restriction = "Path = '{0}'".format(self.heatSPpath)
        if self.siteid :
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(self.siteid)
        self.heatSPclient = RepublishClient(self.archiver_url, self.heatSPcb, restrict=restriction)
        restriction = "Path = '{0}'".format(self.coolSPpath)
        if self.siteid :
            restriction = restriction + " and Metadata/Site/id = '{0}'".format(self.siteid)
        self.coolSPclient = RepublishClient(self.archiver_url, self.coolSPcb, restrict=restriction)


        metadata_type = [
                ('/temp','Sensor'),
                ('/humidity','Sensor'),
                ('/temp_heat','Reading'),
                ('/temp_heat_act','SP'),
                ('/temp_cool','Reading'),
                ('/temp_cool_act','SP'),
                ('/hold','Reading'),
                ('/hold_act','Command'),
                ('/override','Reading'),
                ('/override_act','Command'),
                ('/hvac_mode','Reading'),
                ('/hvac_mode_act','Command')
            ]
        for ts, tstype in metadata_type:
            self.set_metadata(ts,{'Metadata/Type':tstype})

    def start(self):
        self.heatSPclient.connect() # activate subscription scheduler setpoints
        self.coolSPclient.connect() 
        periodicSequentialCall(self.read).start(self.readperiod)

    def read(self):
        for k,v in self.state.iteritems():
            self.add('/'+k, v)

    # Event handler for publication to heatSP stream
    def heatSPcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1] 
        self.heatSP = mostrecent[1]
        print "Set heating setpoint", self.heatSP
        self.state['temp_heat'] = self.heatSP

    def coolSPcb(self, _, data):
        # list of arrays of [time, val]
        mostrecent = data[-1][-1] 
        self.coolSP = mostrecent[1]
        print "Set cooling setpoint", self.coolSP
        self.state['temp_cool'] = self.coolSP