def generate(self, lower_t, upper_t) -> None: """Randomly generates home :param lower_t: lower temperature gradient (used for determining temperature color of home) :type lower_t: int :param upper_t: higher temperature gradient (used for determining temperature color of home) :type upper_t: int :return: Nothing """ # convert sizes to m self.length = ft2m(self.length) self.width = ft2m(self.width) self.height = ft2m(self.height) self._lower_temp_grad = lower_t self._upper_temp_grad = upper_t # select wall material properties wall_type = random.randint(1, 3) if wall_type == 1: self.walls = material.LowEfficiency() elif wall_type == 2: self.walls = material.MedEfficiency() elif wall_type == 3: self.walls = material.HighEfficiency() else: self.walls = material.BrickWall() self.sharedInfo[0] = self.outside_temp.value self.sharedInfo[1] = 101325 # internal pressure, PA self.temp_history.append(self.sharedInfo[0]) sizes = [self.length, self.width, self.height] self.thermostat = Thermostat(sizes, self.sharedInfo, self.world_clock, self.logger)
class Extender: def __init__(self, thermostatName, target): logger.info("Initializing extender") self.sensor = Sensor() self.thermostat = Thermostat(thermostatName) self.reporter = Reporter() self.target = target def startPoller(self, interval): logger.info("Starting poller") while True: try: self.poll() except Exception as e: logger.error("Could not complete run: {}".format(e.message)) logger.info("Sleeping for {} seconds".format(interval)) sleep(interval) def poll(self): temperature = self.sensor.getCurrentTemp() thermostatTarget = self.thermostat.thermostat.target thermostatTemp = self.thermostat.thermostat.temperature self.reporter.send('thermostat_target', thermostatTarget) self.reporter.send('thermostat_temp', thermostatTemp) self.reporter.send('sensor_temp', temperature) if temperature == -1: logger.info("Got a bad reading from the sensor, try again later") pass elif temperature >= self.target: logger.info("Temperature was hot enough") self.thermostat.heatOff() else: logger.info("Temperature was too cold") self.thermostat.heatOn()
def test_dont_keep_heater_on_forever(self): # use the real heater controller, not the test heater self.heater = HeaterCycleProtection(self.clock) self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) self.thermostat.set_mode('on') # advance to just before time limit self.clock.advance(minutes=59) self.thermostat.iterate() self.assertTrue(self.heater.is_on()) # advance past limit, heater should go off self.clock.advance(minutes=1) self.thermostat.iterate() self.assertFalse(self.heater.is_on())
def main(): home_thermostat = Thermostat(update_server) thermostat_server = create_server(home_thermostat.update_set_temperature) try: thermostat_thread = Thread(target=home_thermostat.run) #server_thread.daemon = True thermostat_thread.start() thermostat_server.run(host='0.0.0.0') except Exception as e: print('An Exception Occured!') print(e.message) finally: home_thermostat.shutdown()
def open_thermostat(config): ip = config.get( 'secret', { "ip": "192.168.0.100"}).get( 'ip', '192.168.0.100') tempVariableId = int( config.get( 'global', { "tempvariable": "28"}).get( 'tempvariable', '28')) setpointDayVariableId = config.get( 'global', { "setpointdayvariable": "29"}).get( 'setpointdayvariable', '29') setpointNightVariableId = config.get( 'global', { "setpointnightvariable": "29"}).get( 'setpointnightvariable', '30') modeVariableId = config.get( 'global', { "modevariable": "29"}).get( 'modevariable', '31') stateVariableId = config.get( 'global', { "statevariable": "13"}).get( 'statevariable', '13') thermostatScenarioId = config.get( 'global', { "thermostatscenario": "17"}).get( 'thermostatscenario', '17') thermostatProbeId = config.get( 'global', { "thermostatprobeid": "13"}).get( 'thermostatprobeid', '13') thermostat = Thermostat( ip, int(tempVariableId), int(setpointDayVariableId), int(setpointNightVariableId), int(modeVariableId), int(stateVariableId), int(thermostatScenarioId), int(thermostatProbeId)) logger.debug(" Address ip zibase:{}".format(ip)) logger.debug(" Indoor Temperature:{}".format( thermostat.tempStr(thermostat.getTemp() / 10.0))) logger.debug(" Thermostat Mode:{}".format(thermostat.getModeString())) logger.debug(" Thermostat State:{}".format(thermostat.getStateString())) logger.debug(" Thermostat runMode:{}".format( thermostat.getRunModeString())) logger.debug(" setpoint Day: {}°C".format( thermostat.getSetpointDay() / 10.0)) logger.debug(" setpoint Night:{}°C".format( thermostat.getSetpointNight() / 10.0)) return thermostat
def PATCH(self, ID): thermostat = Thermostat.find(int(ID)) if thermostat is None: raise web.notfound() # parse and validate patch input data try: fields = json.loads(web.data()) except ValueError: raise BadRequest("invalid JSON") if not isinstance(fields, dict): raise BadRequest("invalid JSON") # validate all keys are correct before mutating for field in fields: if not isinstance(field, basestring): raise BadRequest("invalid JSON") if not hasattr(thermostat, field) or field in ["temperature", "ID"]: raise UnprocessableEntity("invalid field {}".format(field)) try: for field in fields: setattr(thermostat, field, fields[field]) except TypeError: raise UnprocessableEntity("invalid type for field {}".format(field)) except ValueError: raise UnprocessableEntity("invalid value for field {}".format(field)) return ""
def add_thermostat(self, location_id, location_name, thermostat, update): t_name = location_name + ' - ' + thermostat['userDefinedDeviceName'] t_device_id = thermostat['deviceID'] t_addr = thermostat['macID'].lower() use_celsius = thermostat['units'].lower() != 'fahrenheit' LOGGER.debug('Adding thermostat with id {0} and name {1} and addr {2}'.format(t_device_id, t_name, t_addr)) self.addNode(Thermostat(self, t_addr, t_addr, t_name, self._api, location_id, t_device_id, use_celsius), update) if 'groups' not in thermostat: return for group in thermostat['groups']: group_id = group['id'] sensors = self._api.get_sensors(location_id, t_device_id, group_id) for sensor in sensors.rooms: if len(sensor.accessories) == 0: continue # TODO: Do we ever have to care about multiple accessory blocks? sensor_type = sensor.accessories[0].accessory_attribute.type sensor_name = sensor.name sensor_addr = t_addr + str(group_id) + str(sensor.id) if sensor_type == 'IndoorAirSensor' or sensor_type == 'Thermostat': LOGGER.debug('Adding IndoorAirSensor with name {0} and addr {1} for thermostat {2}'.format(sensor_name, sensor_addr, t_addr)) self.addNode(IndoorAirSensor(self, t_addr, sensor_addr, sensor_name, self._api, location_id, t_device_id, group_id, sensor.id, use_celsius))
def setUp(self): # create instances self.heater = TestHeater() self.thermometer = TestThermometer() self.clock = TestClock() self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) # setup testing environment # in the beginning, temperature is above threshold, heater is off, both exceptions are on self.clock.set_clock(0) self.thermometer.temperature = 68 self.heater._is_turned_on = False self.heater.exception_if_turned_on = True self.heater.exception_if_turned_off = True # start thermostat in target mode with target=68 (thresholds of 67/69) self.thermostat.set_target_temperature(68) self.thermostat.set_mode('target')
def GET(self, ID): params = web.input(fields=None) try: thermostat = Thermostat.find(int(ID)) if thermostat is None: raise web.notfound() return json.dumps(thermostat, default=lambda o: o.json(params.fields), indent=4) except KeyError as e: raise UnprocessableEntity("invalid field {}".format(e))
def test_dont_keep_heater_on_forever(self): # use the real heater controller, not the test heater self.heater = HeaterCycleProtection(self.clock) self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) self.thermostat.set_mode('on') # advance to just before time limit self.clock.advance(minutes=59) self.thermostat.iterate() self.assertTrue(self.heater.is_on()) # advance past limit, heater should go off self.clock.advance(minutes=1) self.thermostat.iterate() self.assertFalse(self.heater.is_on())
def test_should_heat(self): mock_temp_sensor_reader = MockTempSensorReader("") mock_temp_sensor_reader.set_temperature(10.0) thermostat = Thermostat(mock_temp_sensor_reader) thermostat.set_target_temperature(20.0) self.assertTrue(thermostat.should_heat()) mock_temp_sensor_reader.set_temperature(25.0) self.assertFalse(thermostat.should_heat())
def setUp(self): # create instances self.heater = TestHeater() self.thermometer = TestThermometer() self.clock = TestClock() self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) # setup testing environment # in the beginning, temperature is above threshold, heater is off, both exceptions are on self.clock.set_clock(0) self.thermometer.temperature = 68 self.heater._is_turned_on = False self.heater.exception_if_turned_on = True self.heater.exception_if_turned_off = True # start thermostat in target mode with target=68 (thresholds of 67/69) self.thermostat.set_target_temperature(68) self.thermostat.set_mode('target')
class TestThermostat(unittest.TestCase): def setUp(self): self.thermostat = Thermostat("Upstairs") def test_heatOn(self): self.thermostat.heatOn() sleep(10) self.assertEqual(self.thermostat.getThermostatByName("Upstairs").hvac_state, 'heating') def test_heatOff(self): self.thermostat.heatOff() sleep(10) self.assertEqual(self.thermostat.getThermostatByName("Upstairs").hvac_state, 'off')
def initFromDataFile(self): data = self.fileService.getComponents(self.dataFileName) if "components" not in data: raise BadHomeDataFileFormatError( "Home data JSON file missing key \"{0}\"".format("components")) for comp in data["components"]: if comp["name"] in self.data: raise BadHomeDataFileFormatError( "Duplicate component name \"{0}\" found in file".format( comp["name"])) if comp["type"] == "Thermostat": self.data[comp["name"]] = Thermostat(comp["name"], comp["temperature"]) elif comp["type"] == "Light": self.data[comp["name"]] = Light(comp["name"], comp["status"]) else: logging.warning( "InMemoryRepository.initFromDataFile - Component with name \"{0}\" not recognized, will not be included in initial data" .format(comp["name"]))
def _load_settings(settings, guess_mode): with open(settings) as f: raw = safe_load(f) parsed = {} for thermo in raw["thermostats"]: name = thermo["name"] addr = thermo["address"] secret = bytes.fromhex(thermo["secret"]) set_point = thermo["set_point"] offset = float(thermo["offset"]) remote_topic = thermo.get("remote") parsed[addr] = Thermostat(name=name, addr=addr, secret=secret, set_points=set_point, offset=offset, remote_topic=remote_topic, guess_mode=guess_mode) return parsed
from config import Config from LCD_1602 import LCD_1602 from ds1820 import DS1820 from thermostat import Thermostat from thermometre import Thermometre import time ECO = 1 CONF = 2 if __name__ == '__main__': config = Config() consigne = Thermostat(config) GPIO.setwarnings = False thermometre = Thermometre() lcd = LCD_1602(config.pin['rs'], config.pin['e'], config.pin['db'], config.pin['GPIO']) sondes = DS1820("/sys/bus/w1/devices/", ("28-011561577dff", "28-0115615a35ff")) channel = 23 GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP) try: GPIO.add_event_detect(channel, GPIO.BOTH, callback=thermometre.change_mode, bouncetime=75) while True: now = time.localtime() thermometre.read_temp(sondes) thermometre.save_temp(now, 1) (mode, temp_consigne) = consigne.get_tranche_infos(config.conf, now) status = consigne.get_status(thermometre.get_temp(1), temp_consigne) lcd.clear()
def GET(self): params = web.input(fields=None) try: return json.dumps(Thermostat.all(), default=lambda o: o.json(params.fields), indent=4) except KeyError as e: raise UnprocessableEntity("invalid field {}".format(e))
from PySide2 import QtWidgets from thermostat import Room, Sensor, Actuator, Thermostat, OutsideTemperature from thermostat_gui import MyWidget from test_framework import TestCase, TestRunner import threading import time import sys outside_temperature = 20. target_temperature = 22. room = Room(outside_temperature) sensor = Sensor(room) actuator = Actuator(room) thermostat = Thermostat(sensor, actuator, target_temperature) outside_temperature_mod = OutsideTemperature(room, outside_temperature) tc0 = TestCase(name="Heat if current temperature is below target", preconditions=[ lambda: actuator.state == "OFF", lambda: room.temperature < thermostat.target - thermostat.slack ], invariants=[ lambda: actuator.state == "HEATING" ], invariants_at_least_once=True, postconditions=[ lambda: actuator.state == "OFF", lambda: room.temperature >= thermostat.target - thermostat.slack, lambda: room.temperature <= thermostat.target + thermostat.slack ])
def format_date(d): return "%d/%d" % (d[1], d[2]) def format_datetime_short(t): return time.strftime("%Y%m%dT%H%M%S", time.localtime(t)) logging.basicConfig(level="INFO") app = Flask(__name__) app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 thermostat = Thermostat(pref_file="prefs/thermostat.json", schedule_file="prefs/schedule.json", runhistory_file="prefs/usage.csv", activitydata_file="prefs/activity.csv", tempdata_file="prefs/stats.csv") dayNames = [ "Every Day", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ] timeCommandRE = re.compile( "(deleteTime|ignoreTime)([0-7])([0-2][0-9])([0-5][0-9])") def simpleStats(): (last_temp, last_humi) = thermostat.getLastReading() if last_temp != None: last_temp = round(last_temp, 2)
def test_read_temperature(self): self.home.repository.get = MagicMock( return_value=Thermostat("Thermostat", temp=68)) self.assertEqual(self.home.getTemperature(), 68)
"{0}.csv".format(datetime.datetime.now().strftime("%Y%m%d%H%M%S")) BASE_DIR = '/sys/bus/w1/devices/' DEVICE_FOLDER = glob.glob(BASE_DIR + '28*')[0] DEVICE_FILE = DEVICE_FOLDER + '/w1_slave' RELAY_PIN = 17 os.system('modprobe w1-gpio') os.system('modprobe w1-therm') heating_relay = HeatingRelay(RELAY_PIN) def signal_handler(signal, frame): heating_relay.cleanup() sys.exit(0) signal.signal(signal.SIGINT, signal_handler) thermostat = Thermostat(TempSensorReader(DEVICE_FILE)) thermostat.set_target_temperature(thermostat.read_current_temperature() + TARGET_TEMPERATURE_CHANGE) thermo_logger = ThermoLogger() thermo_logger.set_csv_logger(CSVLogger(LOG_FILENAME)) thermo_logger.set_thermostat(thermostat) thermo_logger.set_heating_relay(heating_relay) thermo_logger.run()
def __init__(self, name, addr): Thermostat.__init__(self, name, addr, 0x0fff)
def __init__(self, thermostatName, target): logger.info("Initializing extender") self.sensor = Sensor() self.thermostat = Thermostat(thermostatName) self.reporter = Reporter() self.target = target
class Building(ABC): """Abstract Class for Building Types""" @abstractmethod def __init__(self, n_id_, i, amb_t, world_clock_, logger_=None) -> None: """Constructor for homes :param n_id_: ID of neighborhood containing home :type n_id_: int :param i: ID of home :type i: int :param amb_t: Ambient temperature :type amb_t: multiprocessing variable :param world_clock_: World Clock :type world_clock_: multiprocessing variable :param logger_: log file to write log messages to :type: logger_: log object :return: Nothing """ self._length = -1 self._width = -1 self._height = -1 self._num_floors = -1 self._n_id = n_id_ self._h_id = i self._lower_temp_grad = None self._upper_temp_grad = None self.outside_temp = amb_t self.world_clock = world_clock_ self.logger = logger_ self.thermostat = None self.walls = None self.battery = None self.pv = None self.devices = dict() self.temp_history = list() self.sharedInfo = mp.Array('d', range(4)) @property def n_id(self): """Get neighborhood ID""" return self._n_id @n_id.setter def n_id(self, value): """Set neighborhood ID""" self._n_id = value @property def h_id(self): """Get house ID""" return self._h_id @h_id.setter def h_id(self, value): """Set house ID""" self._h_id = value @property def length(self): """Get house length""" return self._length @length.setter def length(self, value): """Set house length""" self._length = value @property def width(self): """Get house width""" return self._width @width.setter def width(self, value): """Set house width""" self._width = value @property def height(self): """Get house height""" return self._height @height.setter def height(self, value): """Set house height""" self._height = value @property def num_floors(self): """Get number of floors in house""" return self._num_floors @num_floors.setter def num_floors(self, value): """Set number of floors in house""" self._num_floors = value @abstractmethod def generate(self, lower_t, upper_t) -> None: """Randomly generates home :param lower_t: lower temperature gradient (used for determining temperature color of home) :type lower_t: int :param upper_t: higher temperature gradient (used for determining temperature color of home) :type upper_t: int :return: Nothing """ # convert sizes to m self.length = ft2m(self.length) self.width = ft2m(self.width) self.height = ft2m(self.height) self._lower_temp_grad = lower_t self._upper_temp_grad = upper_t # select wall material properties wall_type = random.randint(1, 3) if wall_type == 1: self.walls = material.LowEfficiency() elif wall_type == 2: self.walls = material.MedEfficiency() elif wall_type == 3: self.walls = material.HighEfficiency() else: self.walls = material.BrickWall() self.sharedInfo[0] = self.outside_temp.value self.sharedInfo[1] = 101325 # internal pressure, PA self.temp_history.append(self.sharedInfo[0]) sizes = [self.length, self.width, self.height] self.thermostat = Thermostat(sizes, self.sharedInfo, self.world_clock, self.logger) @abstractmethod def step(self) -> None: """Proceeds the house a single step. Calculates next temperature and energy consumption of all devices. :return: Nothing """ self.battery.charge(self.pv.produce()) # approach ambient temperature if HVAC is off if not self.thermostat.running(): self.approach_amb() else: # determine if the HVAC should be turned off if self.world_clock.value > self.thermostat.get_end_time(): self.thermostat.fan_off() self.approach_amb() else: self.thermostat.step(self.thermostat.calc_temp_delta()) if self.logger is not None: self.logger.debug("\t\tInner Temperature: {:.3f}, end_time: {}".format(self.sharedInfo[0], self.thermostat.get_end_time())) self.consume_energy() self.temp_history.append(self.sharedInfo[0]) def consume_energy(self) -> int: """Calculates consumption of all devices and systems within a house :return: Energy consumption """ hvac_consumption = 0 device_consumption = 0 if self.thermostat.running(): hvac_consumption = self.thermostat.get_power() for key, device in self.devices.items(): if device.check_run_time(self.world_clock.value) is True: device.turn_off(self.world_clock.value) device_consumption += device.consumption total_consumption = device_consumption + hvac_consumption # if the total consumption is greater than the current charge # held by the house's battery, then fully discharge the battery and # return the amount of energy needed from the grid. Else, return 0 if total_consumption > self.battery.current_charge(): energy_needed = total_consumption - self.battery.current_charge() self.battery.discharge(total_consumption - energy_needed) return energy_needed else: self.battery.discharge(total_consumption) return 0 # TO-DO: Calculate heat loss through roof conduction def approach_amb(self) -> None: """Approach the ambient temperature. If the HVAC fan is off, approach the ambient world temperature at a rate dependent on the wall materials. :return: Nothing """ if self.logger is not None: self.logger.debug("\t\tApproaching Ambient Temperature ({:.3f}) (off since {})".format( self.outside_temp.value, self.thermostat.get_end_time())) air_specific_r = 287.058 # J/(kg * K) based on mean molar mass for dry air (28.96 g/mol) air_heat_cap = 0.718 # J/(kg * K) based on c_v value for dry air @ 300 K air_density = self.sharedInfo[1] / (air_specific_r * (self.sharedInfo[0] + 273)) # kg/m^3 w_area = 2 * ((self.length * self.height) + (self.width * self.height)) r_volume = self.length * self.width * self.height # constants sun_temp = 5800 # K sun_radius = 6.96 * (10 ** 5) # km o = (5.6703 * (10 ** -8)) # W/(m^2 * K^-4) distance_sun2earth = 1.496 * (10 ** 8) # km # calculate radiation from sun to outside of wall solar_rad = o * (4 * math.pi * sun_radius ** 2) * (sun_temp ** 4) # W solar_i = solar_rad / (4 * math.pi * (distance_sun2earth ** 2)) # W/m^2 # calculate amount of heat conducted through a single uniform wall (no layers) w_conducted_heat = (w_area * (self.outside_temp.value - self.sharedInfo[0])) / self.walls.R # calculate amount that internal temperature raises by after adding the # heat conducted through the walls into the room temp_change = w_conducted_heat / (air_density * r_volume * air_heat_cap) self.sharedInfo[0] += temp_change def color_gradient(self, step_num=None) -> str: """Determine color of house depending on temperature :param step_num: Step to go to :type step_num: int :return: String containing hex color code of the house """ if step_num is not None: internal_temp = self.get_int_temp(step_num) else: internal_temp = self.get_int_temp() r = 255 g = 255 b = 255 t_c = int(round((c2f(internal_temp) - self._lower_temp_grad))) diff = int(math.ceil((self._upper_temp_grad - self._lower_temp_grad) / 4)) if t_c >= (diff * 4): g = 0 b = 0 elif t_c >= (diff * 3): g = int(round(g * (1 - ((t_c % diff) * (1 / diff))))) b = 0 elif t_c >= (diff * 2): r = int(round(r * ((t_c % diff) * (1 / diff)))) b = 0 elif t_c >= diff: r = 0 b = int(round(b * (1 - ((t_c % diff) * (1 / diff))))) elif t_c >= 0: r = 0 g = int(g * round((t_c % diff) * (1 / diff), 2)) else: r = 0 g = 0 color_code = '%02x%02x%02x' % (r, g, b) hex_int = int(color_code, 16) if t_c < 0: hex_int -= abs(t_c) * 4 elif t_c > (diff * 4): t_c -= (diff * 4) hex_int -= t_c * (131072 * 2) return str.format('#{:06x}', hex_int) def get_int_temp(self, step_num=None) -> float: """Returns internal temperature of a house at a specified time :param step_num: Step to retrieve temperature from. Default is None, which means the current step :type step_num: int :return: temperature at step """ if step_num is not None: return self.temp_history[step_num] else: return self.temp_history[self.world_clock.value] def get_target_temp(self) -> int: """Returns the current target temperature of the house""" return self.thermostat.get_target_temp() def get_wall_type(self) -> str: """Returns the type of the materials making up the walls""" return self.walls.type
def setUp(self): self.thermostat = Thermostat("Upstairs")
#!/usr/bin/python from threading import Thread import thermostat import time from furnace import Furnace from thermostat import Thermostat from temperature_reader import TemperatureReader print("Starting Runner.py") f = Furnace() furnaceThread = Thread(target = f.run, args = ()) furnaceThread.daemon = True furnaceThread.start() r = TemperatureReader() readerThread = Thread(target = r.run, args = ()) readerThread.daemon = True readerThread.start() t = Thermostat() thermostatThread = Thread(target = t.run, args = (f,)) thermostatThread.daemon = True thermostatThread.start() while True: time.sleep(1)
def main(t, g): while True: griddy_data = g.query() current_price = griddy_data["now"]["price_ckwh"] price_display = g.format_price(current_price) if g.price_is_high(current_price): t.set_spike_active(True) log.info("Spike active. Current price: {}".format(price_display)) else: t.set_spike_active(False) t.run() time.sleep(POLL_FREQUENCY) if __name__ == "__main__": log.info("Thermostat monitor started.") t = Thermostat(config["thermostat"]["address"]) g = griddy.Griddy(config["griddy"]["meter_id"], config["griddy"]["member_id"], config["griddy"]["settlement_point"]) m = Process(target=main, args=(t, g)) m.start() m.join()
def setTemperature(self, newValue): self.repository.update(Thermostat("Thermostat", temp=newValue)) logging.info("Set temperature to \"{0}\"".format(newValue))
def test_get_thermostat(self): thermo = Thermostat("Thermostat", temp=68) self.home.repository.get = MagicMock(return_value=thermo) self.assertEqual(self.home.getThermostat(), thermo)
#!/usr/bin/python from thermostat import Thermostat ''' Start and maintain the thermostat ''' # execute # thermostat = Thermostat() thermostat.engage_thermostat()
def initBaseComponents(self): self.repository.add(Thermostat("Thermostat", temp=68)) self.repository.add(Light("Living Room Light")) logging.info("Initialized 1 thermostat and 1 light in memory")
class TestThermostat(TestCase): @classmethod def setUpClass(cls): random.seed(0) def setUp(self): # create instances self.heater = TestHeater() self.thermometer = TestThermometer() self.clock = TestClock() self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) # setup testing environment # in the beginning, temperature is above threshold, heater is off, both exceptions are on self.clock.set_clock(0) self.thermometer.temperature = 68 self.heater._is_turned_on = False self.heater.exception_if_turned_on = True self.heater.exception_if_turned_off = True # start thermostat in target mode with target=68 (thresholds of 67/69) self.thermostat.set_target_temperature(68) self.thermostat.set_mode('target') def test_read_heater_status(self): # turn on self.heater.exception_if_turned_on = False self.heater.set_to_on(True) self.assertTrue(self.thermostat.get_heater_is_on()) # turn off self.heater.exception_if_turned_off = False self.heater.set_to_on(False) self.assertFalse(self.thermostat.get_heater_is_on()) def test_turn_on_heater_manually(self): # turn on self.heater.exception_if_turned_on = False self.thermostat.set_mode("on") self.assertTrue(self.heater.is_on()) def test_turn_off_heater_manually(self): # turn off heater self.heater.exception_if_turned_off = False self.thermostat.set_mode("off") self.assertFalse(self.heater.is_on()) def test_read_thermometer(self): for temp in [0, -5, 98.6]: self.thermometer.temperature = temp self.assertEquals(self.thermostat.get_room_temperature(), temp) def test_set_point(self): for temp in [40, 68, 82]: self.thermostat.set_target_temperature(temp) self.assertEquals(self.thermostat.get_target_temperature(), temp) self.assertLess(self.thermostat.threshold_low, temp) self.assertGreater(self.thermostat.threshold_high, temp) def test_set_point_too_low(self): temp = 39 # min allowable is 40 with self.assertRaises(ThermostatException): self.thermostat.set_target_temperature(temp) def test_set_point_too_high(self): temp = 83 # max allowable is 82 with self.assertRaises(ThermostatException): self.thermostat.set_target_temperature(temp) def test_turn_heater_on_when_too_cold(self): # after 5 minutes below at or below .threshold_low(), turns on heater # advance to some arbitrary time, go below threshold self.clock.advance_random() self.thermometer.temperature = 66 self.thermostat.iterate() # advance less than the required time, heater still off self.clock.advance(minutes=4.9) self.thermostat.iterate() # advance to the required time, heater should kick on self.clock.advance(minutes=0.1) self.heater.exception_if_turned_on = False self.thermostat.iterate() self.assertTrue(self.heater.is_on()) def helper_get_cold_to_trigger_heater(self): # possible unintended consequences if not run as first line in test # advance to some arbitrary time, it is now too cold self.clock.advance_random() self.thermometer.temperature = 66 self.thermostat.iterate() # stay cold long enough for heater to kick on self.clock.advance(minutes=5) self.heater.exception_if_turned_on = False self.thermostat.iterate() self.assertTrue(self.heater.is_on()) def test_turn_heater_on_when_too_cold_helper(self): self.helper_get_cold_to_trigger_heater() def test_dont_heat_up_if_not_cold_long_enough(self): # threshold duration is 5 minutes. heater should not turn on if cold for 4, warm for 1, cold for 4 # advance to some arbitrary time, go below threshold self.clock.advance_random() self.thermometer.temperature = 66 self.thermostat.iterate() # advance less than the required time, heater still off self.clock.advance(minutes=4) self.thermostat.iterate() # advance 1 minute, but temperature is above threshold now self.clock.advance(minutes=1) self.thermometer.temperature = 68 self.thermostat.iterate() # advance 1 minute, temperature back below threshold self.clock.advance(minutes=1) self.thermometer.temperature = 66 self.thermostat.iterate() # advance less than the required time, heater still off self.clock.advance(minutes=4) self.thermostat.iterate() def test_keep_heater_on_until_warm_enough(self): self.helper_get_cold_to_trigger_heater() # advance arbitrary amount of time, heater is still on # this arbitrary time must be less than the maximum-on-duration (20 minutes) self.clock.advance(minutes=7.2) self.thermostat.iterate() # come above threshold, heater will run until above for 5 minutes # total arbitrary time must be less than the maximum-on-duration (20 minutes) self.clock.advance(minutes=4.8) self.thermometer.temperature = 70 self.thermostat.iterate() # advance 5 minutes self.clock.advance(minutes=5) self.heater.exception_if_turned_off = False self.thermostat.iterate() self.assertFalse(self.heater.is_on()) # def test_dont_cycle_off_heater_too_quickly(self): # self.helper_get_cold_to_trigger_heater() # # advance 1 minute, already warm enough, but don't cycle off heater yet # self.clock.advance(minutes=1) # self.thermometer.temperature = 70 # self.thermostat.iterate() # # advance to just under cycle minimum time on, heater should still be on # self.clock.advance(minutes=3.9) # self.thermostat.iterate() # # advance beyond cycle minimum time on, heater should turn off # self.clock.advance(minutes=0.1) # self.heater.exception_if_turned_off = False # self.thermostat.iterate() # self.assertFalse(self.heater.is_on()) # # def test_dont_cycle_on_heater_too_quickly(self): # # the beginning of this test needs to trigger the heater and let things warm up # self.helper_get_cold_to_trigger_heater() # # advance and warm up # self.clock.advance(minutes=10) # self.thermometer.temperature = 70 # self.heater.exception_if_turned_off = False # self.thermostat.iterate() # self.assertFalse(self.heater.is_on()) # # the heater is off now, but throw exception if thermostat tries to turn if off again # self.heater.exception_if_turned_off = True # # now quickly get cold again, but don't turn on heater yet # self.clock.advance(minutes=1) # self.thermometer.temperature = 66 # self.thermostat.iterate() # # advance to just inside cycle limit, still not on yet # self.clock.advance(minutes=3.9) # self.thermostat.iterate() # # advance to just outside of cycle limit, heater should kick on # self.clock.advance(minutes=0.1) # self.heater.exception_if_turned_on = False # self.thermostat.iterate() # self.assertTrue(self.heater.is_on()) def test_dont_keep_heater_on_forever(self): # use the real heater controller, not the test heater self.heater = HeaterCycleProtection(self.clock) self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) self.thermostat.set_mode('on') # advance to just before time limit self.clock.advance(minutes=59) self.thermostat.iterate() self.assertTrue(self.heater.is_on()) # advance past limit, heater should go off self.clock.advance(minutes=1) self.thermostat.iterate() self.assertFalse(self.heater.is_on())
class TestThermostat(TestCase): @classmethod def setUpClass(cls): random.seed(0) def setUp(self): # create instances self.heater = TestHeater() self.thermometer = TestThermometer() self.clock = TestClock() self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) # setup testing environment # in the beginning, temperature is above threshold, heater is off, both exceptions are on self.clock.set_clock(0) self.thermometer.temperature = 68 self.heater._is_turned_on = False self.heater.exception_if_turned_on = True self.heater.exception_if_turned_off = True # start thermostat in target mode with target=68 (thresholds of 67/69) self.thermostat.set_target_temperature(68) self.thermostat.set_mode('target') def test_read_heater_status(self): # turn on self.heater.exception_if_turned_on = False self.heater.set_to_on(True) self.assertTrue(self.thermostat.get_heater_is_on()) # turn off self.heater.exception_if_turned_off = False self.heater.set_to_on(False) self.assertFalse(self.thermostat.get_heater_is_on()) def test_turn_on_heater_manually(self): # turn on self.heater.exception_if_turned_on = False self.thermostat.set_mode("on") self.assertTrue(self.heater.is_on()) def test_turn_off_heater_manually(self): # turn off heater self.heater.exception_if_turned_off = False self.thermostat.set_mode("off") self.assertFalse(self.heater.is_on()) def test_read_thermometer(self): for temp in [0, -5, 98.6]: self.thermometer.temperature = temp self.assertEquals(self.thermostat.get_room_temperature(), temp) def test_set_point(self): for temp in [40, 68, 82]: self.thermostat.set_target_temperature(temp) self.assertEquals(self.thermostat.get_target_temperature(), temp) self.assertLess(self.thermostat.threshold_low, temp) self.assertGreater(self.thermostat.threshold_high, temp) def test_set_point_too_low(self): temp = 39 # min allowable is 40 with self.assertRaises(ThermostatException): self.thermostat.set_target_temperature(temp) def test_set_point_too_high(self): temp = 83 # max allowable is 82 with self.assertRaises(ThermostatException): self.thermostat.set_target_temperature(temp) def test_turn_heater_on_when_too_cold(self): # after 5 minutes below at or below .threshold_low(), turns on heater # advance to some arbitrary time, go below threshold self.clock.advance_random() self.thermometer.temperature = 66 self.thermostat.iterate() # advance less than the required time, heater still off self.clock.advance(minutes=4.9) self.thermostat.iterate() # advance to the required time, heater should kick on self.clock.advance(minutes=0.1) self.heater.exception_if_turned_on = False self.thermostat.iterate() self.assertTrue(self.heater.is_on()) def helper_get_cold_to_trigger_heater(self): # possible unintended consequences if not run as first line in test # advance to some arbitrary time, it is now too cold self.clock.advance_random() self.thermometer.temperature = 66 self.thermostat.iterate() # stay cold long enough for heater to kick on self.clock.advance(minutes=5) self.heater.exception_if_turned_on = False self.thermostat.iterate() self.assertTrue(self.heater.is_on()) def test_turn_heater_on_when_too_cold_helper(self): self.helper_get_cold_to_trigger_heater() def test_dont_heat_up_if_not_cold_long_enough(self): # threshold duration is 5 minutes. heater should not turn on if cold for 4, warm for 1, cold for 4 # advance to some arbitrary time, go below threshold self.clock.advance_random() self.thermometer.temperature = 66 self.thermostat.iterate() # advance less than the required time, heater still off self.clock.advance(minutes=4) self.thermostat.iterate() # advance 1 minute, but temperature is above threshold now self.clock.advance(minutes=1) self.thermometer.temperature = 68 self.thermostat.iterate() # advance 1 minute, temperature back below threshold self.clock.advance(minutes=1) self.thermometer.temperature = 66 self.thermostat.iterate() # advance less than the required time, heater still off self.clock.advance(minutes=4) self.thermostat.iterate() def test_keep_heater_on_until_warm_enough(self): self.helper_get_cold_to_trigger_heater() # advance arbitrary amount of time, heater is still on # this arbitrary time must be less than the maximum-on-duration (20 minutes) self.clock.advance(minutes=7.2) self.thermostat.iterate() # come above threshold, heater will run until above for 5 minutes # total arbitrary time must be less than the maximum-on-duration (20 minutes) self.clock.advance(minutes=4.8) self.thermometer.temperature = 70 self.thermostat.iterate() # advance 5 minutes self.clock.advance(minutes=5) self.heater.exception_if_turned_off = False self.thermostat.iterate() self.assertFalse(self.heater.is_on()) # def test_dont_cycle_off_heater_too_quickly(self): # self.helper_get_cold_to_trigger_heater() # # advance 1 minute, already warm enough, but don't cycle off heater yet # self.clock.advance(minutes=1) # self.thermometer.temperature = 70 # self.thermostat.iterate() # # advance to just under cycle minimum time on, heater should still be on # self.clock.advance(minutes=3.9) # self.thermostat.iterate() # # advance beyond cycle minimum time on, heater should turn off # self.clock.advance(minutes=0.1) # self.heater.exception_if_turned_off = False # self.thermostat.iterate() # self.assertFalse(self.heater.is_on()) # # def test_dont_cycle_on_heater_too_quickly(self): # # the beginning of this test needs to trigger the heater and let things warm up # self.helper_get_cold_to_trigger_heater() # # advance and warm up # self.clock.advance(minutes=10) # self.thermometer.temperature = 70 # self.heater.exception_if_turned_off = False # self.thermostat.iterate() # self.assertFalse(self.heater.is_on()) # # the heater is off now, but throw exception if thermostat tries to turn if off again # self.heater.exception_if_turned_off = True # # now quickly get cold again, but don't turn on heater yet # self.clock.advance(minutes=1) # self.thermometer.temperature = 66 # self.thermostat.iterate() # # advance to just inside cycle limit, still not on yet # self.clock.advance(minutes=3.9) # self.thermostat.iterate() # # advance to just outside of cycle limit, heater should kick on # self.clock.advance(minutes=0.1) # self.heater.exception_if_turned_on = False # self.thermostat.iterate() # self.assertTrue(self.heater.is_on()) def test_dont_keep_heater_on_forever(self): # use the real heater controller, not the test heater self.heater = HeaterCycleProtection(self.clock) self.thermostat = Thermostat(self.heater, self.thermometer, self.clock) self.thermostat.set_mode('on') # advance to just before time limit self.clock.advance(minutes=59) self.thermostat.iterate() self.assertTrue(self.heater.is_on()) # advance past limit, heater should go off self.clock.advance(minutes=1) self.thermostat.iterate() self.assertFalse(self.heater.is_on())
ip = None thermostat = None if config and config.get('secret', None) is not None: if config.get('secret').get('ip_zibase', None) is not None: ip = config.get('secret').get('ip_zibase') if ip == "": ip = None print("Address ip zibase:{}").format(ip) for x, y in config.items(): print(x, y) if ip is not None: try: thermostat = Thermostat(ip) thermostat.setSetpointDay(207) thermostat.setSetpointNight(195) thermostat.addSetpointDay(1) thermostat.addSetpointNight(5) thermostat.setMode(0) thermostat.update() thermostat.read() except Exception as e: zibase = None print('Error Thermostat {}'.format(e)) h.subscribe_intents(intent_received).start()
def __init__(self, name, addr): Thermostat.__init__(self, name, addr, 0x0fff)
iconDay = Icon(path, '/icons/sun-2-32') iconNight = Icon(path, '/icons/moon-2-32') iconOff = Icon(path, '/icons/off') iconFlame = Icon(path, '/icons/flame32') zibaseId = None tokenId = None try: config = SnipsConfigParser.read_configuration_file(configPath) #print configPath, config zibaseId = config.get('secret').get('zibaseid') tokenId = config.get('secret').get('tokenid') except: config = None thermostat = Thermostat(config) display = 3 # Set the Default display here # update interval in seconds if display == 2 or display == 3: updateRate = 60 else: updateRate = 60 * 5 lastupdate = 0 # define a variable to control the main loop running = True try: