Ejemplo n.º 1
0
    def test_output_actions(self):
        floor = Floor.create(number=5)
        room = Room.create(number=10, floor=floor)
        Output.create(number=2, room=room)
        Output.create(number=3)

        with mock.patch.object(self.master_controller,
                               'set_all_lights') as call:
            self.controller.set_all_lights(action='OFF')
            call.assert_called_once_with(action='OFF')
        with mock.patch.object(self.master_controller,
                               'set_all_lights') as call:
            self.controller.set_all_lights(action='ON')
            call.assert_called_once_with(action='ON')
        with mock.patch.object(self.master_controller,
                               'set_all_lights') as call:
            self.controller.set_all_lights(action='OFF', floor_id=1)
            call.assert_called_once_with(action='OFF',
                                         floor_id=1,
                                         output_ids=[])
        with mock.patch.object(self.master_controller,
                               'set_all_lights') as call:
            self.controller.set_all_lights(action='OFF', floor_id=5)
            call.assert_called_once_with(action='OFF',
                                         floor_id=5,
                                         output_ids=[2])
        with mock.patch.object(self.master_controller,
                               'set_all_lights') as call:
            self.controller.set_all_lights(action='ON', floor_id=5)
            call.assert_called_once_with(action='ON',
                                         floor_id=5,
                                         output_ids=[2])
Ejemplo n.º 2
0
    def _save_pump_groups(
        mode, pump_groups
    ):  # type: (str, List[Tuple[PumpGroupDTO, List[str]]]) -> None
        for pump_group_dto, fields in pump_groups:
            if 'pump_output_id' in fields and 'valve_output_ids' in fields:
                valve_output_ids = pump_group_dto.valve_output_ids
                pump = Pump.get(id=pump_group_dto.id)  # type: Pump
                pump.output = Output.get(number=pump_group_dto.pump_output_id)

                links = {
                    pump_to_valve.valve.output.number: pump_to_valve
                    for pump_to_valve in PumpToValve.select(
                        PumpToValve, Pump, Valve, Output).join_from(
                            PumpToValve, Valve).join_from(
                                PumpToValve, Pump).join_from(Valve, Output).
                    join_from(Valve, ValveToThermostat).where((
                        ValveToThermostat.mode == mode) & (Pump.id == pump.id))
                }
                for output_id in list(links.keys()):
                    if output_id not in valve_output_ids:
                        pump_to_valve = links.pop(
                            output_id)  # type: PumpToValve
                        pump_to_valve.delete_instance()
                    else:
                        valve_output_ids.remove(output_id)
                for output_id in valve_output_ids:
                    output = Output.get(number=output_id)
                    valve = Valve.get_or_none(output=output)
                    if valve is None:
                        valve = Valve(name=output.name, output=output)
                        valve.save()
                    PumpToValve.create(pump=pump, valve=valve)
Ejemplo n.º 3
0
 def test_get_output_status(self):
     select_mock = mock.Mock()
     select_mock.join_from.return_value = [
         Output(id=0, number=2),
         Output(id=1, number=40, room=Room(id=2, number=3))
     ]
     with mock.patch.object(Output, 'select', return_value=select_mock), \
          mock.patch.object(self.master_controller, 'load_output',
                            side_effect=lambda output_id: OutputDTO(id=output_id)), \
          mock.patch.object(self.master_controller, 'load_output_status',
                            return_value=[OutputStateDTO(id=2, status=False),
                                          OutputStateDTO(id=40, status=True)]):
         self.controller._sync_state()
         status = self.controller.get_output_status(40)
         assert status == OutputStateDTO(id=40, status=True)
Ejemplo n.º 4
0
 def test_get_output_statuses(self):
     select_mock = mock.Mock()
     select_mock.join_from.return_value = [
         Output(id=0, number=2),
         Output(id=1, number=40, module_type='D', room=Room(id=2, number=3))
     ]
     with mock.patch.object(Output, 'select', return_value=select_mock), \
          mock.patch.object(self.master_controller, 'load_output',
                            side_effect=lambda output_id: OutputDTO(id=output_id)), \
          mock.patch.object(self.master_controller, 'load_output_status',
                            return_value=[OutputStateDTO(id=2, status=False, dimmer=0),
                                          OutputStateDTO(id=40, status=True, dimmer=50)]):
         self.controller._sync_state()
         status = self.controller.get_output_statuses()
         assert len(status) == 2
         assert OutputStateDTO(id=2, status=False) in status
         assert OutputStateDTO(id=40, status=True, dimmer=50) in status
Ejemplo n.º 5
0
 def load_output(self, output_id):  # type: (int) -> OutputDTO
     output = Output.select(Room) \
                    .join_from(Output, Room, join_type=JOIN.LEFT_OUTER) \
                    .where(Output.number == output_id) \
                    .get()  # type: Output  # TODO: Load dict
     output_dto = self._master_controller.load_output(output_id=output_id)
     output_dto.room = output.room.number if output.room is not None else None
     return output_dto
Ejemplo n.º 6
0
 def test_load_outputs(self):
     select_mock = mock.Mock()
     select_mock.join_from.return_value = [
         Output(id=1, number=42, room=Room(id=2, number=3))
     ]
     with mock.patch.object(Output, 'select', return_value=select_mock), \
          mock.patch.object(self.master_controller, 'load_output',
                            return_value=OutputDTO(id=42)) as load:
         outputs = self.controller.load_outputs()
         assert OutputDTO(id=42, room=3) in outputs
         load.assert_called_with(output_id=42)
Ejemplo n.º 7
0
 def load_outputs(self):  # type: () -> List[OutputDTO]
     output_dtos = []
     for output in list(
             Output.select(Output, Room).join_from(
                 Output, Room,
                 join_type=JOIN.LEFT_OUTER)):  # TODO: Load dicts
         output_dto = self._master_controller.load_output(
             output_id=output.number)
         output_dto.room = output.room.number if output.room is not None else None
         output_dtos.append(output_dto)
     self._cache.update_outputs(output_dtos)
     return output_dtos
Ejemplo n.º 8
0
    def save_thermostat_group(self, thermostat_group):
        # type: (Tuple[ThermostatGroupDTO, List[str]]) -> None
        thermostat_group_dto, fields = thermostat_group

        # Update thermostat group configuration
        orm_object = ThermostatGroup.get(number=0)  # type: ThermostatGroup
        if 'outside_sensor_id' in fields:
            orm_object.sensor = Sensor.get(
                number=thermostat_group_dto.outside_sensor_id)
        if 'threshold_temperature' in fields:
            orm_object.threshold_temperature = thermostat_group_dto.threshold_temperature  # type: ignore
        orm_object.save()

        # Link configuration outputs to global thermostat config
        for mode in ['cooling', 'heating']:
            links = {
                link.index: link
                for link in OutputToThermostatGroup.select().where(
                    (OutputToThermostatGroup.thermostat_group == orm_object)
                    & (OutputToThermostatGroup.mode == mode))
            }
            for i in range(4):
                field = 'switch_to_{0}_{1}'.format(mode, i)
                if field not in fields:
                    continue

                link = links.get(i)
                data = getattr(thermostat_group_dto, field)
                if data is None:
                    if link is not None:
                        link.delete_instance()
                else:
                    output_number, value = data
                    output = Output.get(number=output_number)
                    if link is None:
                        OutputToThermostatGroup.create(
                            output=output,
                            thermostat_group=orm_object,
                            mode=mode,
                            index=i,
                            value=value)
                    else:
                        link.output = output
                        link.value = value
                        link.save()

        if 'pump_delay' in fields:
            # Set valve delay for all valves in this group
            for thermostat in orm_object.thermostats:
                for valve in thermostat.valves:
                    valve.delay = thermostat_group_dto.pump_delay  # type: ignore
                    valve.save()
Ejemplo n.º 9
0
 def test_load_output(self):
     where_mock = mock.Mock()
     where_mock.get.return_value = Output(id=1,
                                          number=42,
                                          room=Room(id=2, number=3))
     join_from_mock = mock.Mock()
     join_from_mock.where.return_value = where_mock
     select_mock = mock.Mock()
     select_mock.join_from.return_value = join_from_mock
     with mock.patch.object(Output, 'select', return_value=select_mock), \
          mock.patch.object(self.master_controller, 'load_output',
                            return_value=OutputDTO(id=42)) as load:
         output = self.controller.load_output(42)
         assert output == OutputDTO(id=42, room=3)
         load.assert_called_with(output_id=42)
Ejemplo n.º 10
0
    def test_open_valves(self):
        Valve.create(number=1,
                     name='valve 1',
                     delay=30,
                     output=Output.create(number=1))
        Valve.create(number=2,
                     name='valve 2',
                     delay=30,
                     output=Output.create(number=2))
        Valve.create(number=3,
                     name='valve 3',
                     delay=30,
                     output=Output.create(number=3))

        SetUpTestInjections(output_controller=mock.Mock(OutputController))
        controller = PumpValveController()
        controller.refresh_from_db()

        self.assertIn(1, controller._valve_drivers)
        valve_driver_1 = controller.get_valve_driver(1)
        self.assertIn(2, controller._valve_drivers)
        valve_driver_2 = controller.get_valve_driver(2)
        self.assertIn(3, controller._valve_drivers)
        valve_driver_3 = controller.get_valve_driver(3)

        for percentage, mode, results in [(100, 'equal', [100, 100]),
                                          (50, 'equal', [50, 50]),
                                          (0, 'equal', [0, 0]),
                                          (100, 'cascade', [100, 100]),
                                          (75, 'cascade', [100, 50]),
                                          (50, 'cascade', [100, 0]),
                                          (0, 'cascade', [0, 0])]:
            controller.set_valves(percentage, [1, 2], mode)
            self.assertEqual(results[0], valve_driver_1._desired_percentage)
            self.assertEqual(results[1], valve_driver_2._desired_percentage)
            self.assertEqual(0, valve_driver_3._desired_percentage)
Ejemplo n.º 11
0
 def _get_thermostat_pid(self):
     thermostat = Thermostat.create(number=1,
                                    name='thermostat 1',
                                    sensor=Sensor.create(number=10),
                                    pid_heating_p=200,
                                    pid_heating_i=100,
                                    pid_heating_d=50,
                                    pid_cooling_p=200,
                                    pid_cooling_i=100,
                                    pid_cooling_d=50,
                                    automatic=True,
                                    room=None,
                                    start=0,
                                    valve_config='equal',
                                    thermostat_group=self._thermostat_group)
     ValveToThermostat.create(thermostat=thermostat,
                              valve=Valve.create(
                                  number=1,
                                  name='valve 1',
                                  output=Output.create(number=1)),
                              mode=ThermostatGroup.Modes.HEATING,
                              priority=0)
     ValveToThermostat.create(thermostat=thermostat,
                              valve=Valve.create(
                                  number=2,
                                  name='valve 2',
                                  output=Output.create(number=2)),
                              mode=ThermostatGroup.Modes.COOLING,
                              priority=0)
     Preset.create(type=Preset.Types.SCHEDULE,
                   heating_setpoint=20.0,
                   cooling_setpoint=25.0,
                   active=True,
                   thermostat=thermostat)
     return ThermostatPid(thermostat=thermostat,
                          pump_valve_controller=self._pump_valve_controller)
Ejemplo n.º 12
0
 def save_outputs(self, outputs):  # type: (List[OutputDTO]) -> None
     outputs_to_save = []
     for output_dto in outputs:
         output = Output.get_or_none(number=output_dto.id)  # type: Output
         if output is None:
             logger.info('Ignored saving non-existing Output {0}'.format(
                 output_dto.id))
         if 'room' in output_dto.loaded_fields:
             if output_dto.room is None:
                 output.room = None
             elif 0 <= output_dto.room <= 100:
                 output.room, _ = Room.get_or_create(number=output_dto.room)
             output.save()
         outputs_to_save.append(output_dto)
     self._master_controller.save_outputs(outputs_to_save)
Ejemplo n.º 13
0
    def test_orm_sync(self):
        events = []

        def handle_event(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.CONFIG,
                                             handle_event)

        output_dto = OutputDTO(id=42)
        with mock.patch.object(self.master_controller,
                               'load_outputs',
                               return_value=[output_dto]):
            self.controller.run_sync_orm()
            self.pubsub._publish_all_events()
            assert Output.select().where(
                Output.number == output_dto.id).count() == 1
            assert GatewayEvent(GatewayEvent.Types.CONFIG_CHANGE,
                                {'type': 'output'}) in events
            assert len(events) == 1
Ejemplo n.º 14
0
    def test_pump_driver(self):
        output = Output.create(number=0)
        pump = Pump.create(number=1,
                           name='pump',
                           output=output)

        SetUpTestInjections(output_controller=mock.Mock(OutputController))
        driver = PumpDriver(pump)
        self.assertIsNone(driver.state)
        self.assertFalse(driver.error)
        self.assertEqual(pump.id, driver.id)

        driver.turn_on()
        self.assertTrue(driver.state)
        self.assertFalse(driver.error)

        driver.turn_off()
        self.assertFalse(driver.state)
        self.assertFalse(driver.error)

        driver._output_controller.set_output_status.side_effect = RuntimeError()
        with self.assertRaises(RuntimeError):
            driver.turn_on()
        self.assertFalse(driver.state)
        self.assertTrue(driver.error)

        driver._output_controller.set_output_status.side_effect = None
        driver.turn_on()
        self.assertTrue(driver.state)
        self.assertFalse(driver.error)

        driver._output_controller.set_output_status.side_effect = RuntimeError()
        with self.assertRaises(RuntimeError):
            driver.turn_off()
        self.assertTrue(driver.state)
        self.assertTrue(driver.error)

        driver._output_controller.set_output_status.side_effect = None
        driver.turn_off()
        self.assertFalse(driver.state)
        self.assertFalse(driver.error)
Ejemplo n.º 15
0
    def set_all_lights(
        self,
        action,
        floor_id=None
    ):  # type: (Literal['ON', 'OFF', 'TOGGLE'], Optional[int]) -> None
        # TODO: Also include other sources (e.g. plugins) once implemented
        if floor_id is None:
            self._master_controller.set_all_lights(action=action)
            return

        # TODO: Filter on output type "light" once available
        query = Output.select(Output.number) \
                      .join_from(Output, Room, join_type=JOIN.INNER) \
                      .join_from(Room, Floor, join_type=JOIN.INNER) \
                      .where(Floor.number == floor_id)
        output_ids = [output['number'] for output in query.dicts()]

        # It is unknown whether `floor` is known to the Master implementation. So pass both the floor_id
        # and the list of Output ids to the MasterController
        self._master_controller.set_all_lights(action=action,
                                               floor_id=floor_id,
                                               output_ids=output_ids)
Ejemplo n.º 16
0
    def test_valve_driver(self):
        valve_output_1 = Output.create(number=2)
        valve_1 = Valve.create(number=1,
                               name='valve 1',
                               delay=30,
                               output=valve_output_1)

        SetUpTestInjections(output_controller=mock.Mock(OutputController))
        driver_1 = ValveDriver(valve_1)

        self.assertEqual(valve_1.id, driver_1.id)
        self.assertEqual(0, driver_1.percentage)
        self.assertEqual(0, driver_1._desired_percentage)
        self.assertFalse(driver_1.is_open)
        self.assertFalse(driver_1.in_transition)

        driver_1.set(50)
        self.assertEqual(50, driver_1._desired_percentage)
        driver_1.close()
        self.assertEqual(0, driver_1._desired_percentage)
        driver_1.open()
        self.assertEqual(100, driver_1._desired_percentage)
        self.assertTrue(driver_1.will_open)
        driver_1.steer_output()
        driver_1._output_controller.set_output_status.assert_called_once()
        self.assertFalse(driver_1.will_open)
        self.assertEqual(100, driver_1.percentage)
        self.assertFalse(driver_1.is_open)
        self.assertTrue(driver_1.in_transition)

        time.sleep(20)
        self.assertFalse(driver_1.is_open)
        self.assertTrue(driver_1.in_transition)

        time.sleep(15)
        self.assertTrue(driver_1.is_open)
        self.assertFalse(driver_1.in_transition)
Ejemplo n.º 17
0
    def test_thermostat_control(self):
        thermostat = Thermostat.create(number=1,
                                       name='thermostat 1',
                                       sensor=Sensor.create(number=10),
                                       pid_heating_p=200,
                                       pid_heating_i=100,
                                       pid_heating_d=50,
                                       pid_cooling_p=200,
                                       pid_cooling_i=100,
                                       pid_cooling_d=50,
                                       automatic=True,
                                       room=None,
                                       start=0,
                                       valve_config='equal',
                                       thermostat_group=self._thermostat_group)
        Output.create(number=1)
        Output.create(number=2)
        Output.create(number=3)
        valve_output = Output.create(number=4)
        valve = Valve.create(number=1,
                             name='valve 1',
                             output=valve_output)
        ValveToThermostat.create(thermostat=thermostat,
                                 valve=valve,
                                 mode=ThermostatGroup.Modes.HEATING,
                                 priority=0)
        self._thermostat_controller.refresh_config_from_db()

        expected = ThermostatGroupStatusDTO(id=0,
                                            on=True,
                                            setpoint=0,
                                            cooling=False,
                                            automatic=True,
                                            statusses=[ThermostatStatusDTO(id=1,
                                                                           name='thermostat 1',
                                                                           automatic=True,
                                                                           setpoint=0,
                                                                           sensor_id=10,
                                                                           actual_temperature=10.0,
                                                                           setpoint_temperature=14.0,
                                                                           outside_temperature=10.0,
                                                                           output_0_level=0,
                                                                           output_1_level=0,
                                                                           mode=0,
                                                                           airco=0)])
        self.assertEqual(expected, self._thermostat_controller.get_thermostat_status())

        self._thermostat_controller.set_current_setpoint(thermostat_number=1, heating_temperature=15.0)
        expected.statusses[0].setpoint_temperature = 15.0
        self.assertEqual(expected, self._thermostat_controller.get_thermostat_status())

        self._thermostat_controller.set_per_thermostat_mode(thermostat_number=1,
                                                            automatic=True,
                                                            setpoint=16.0)
        expected.statusses[0].setpoint_temperature = 16.0
        self.assertEqual(expected, self._thermostat_controller.get_thermostat_status())

        preset = self._thermostat_controller.get_current_preset(thermostat_number=1)
        self.assertTrue(preset.active)
        self.assertEqual(30.0, preset.cooling_setpoint)
        self.assertEqual(16.0, preset.heating_setpoint)
        self.assertEqual(Preset.Types.SCHEDULE, preset.type)

        self._thermostat_controller.set_current_preset(thermostat_number=1, preset_type=Preset.Types.PARTY)
        expected.statusses[0].setpoint_temperature = 14.0
        expected.statusses[0].setpoint = expected.setpoint = 5  # PARTY = legacy `5` setpoint
        expected.statusses[0].automatic = expected.automatic = False
        self.assertEqual(expected, self._thermostat_controller.get_thermostat_status())

        self._thermostat_controller.set_thermostat_mode(thermostat_on=True, cooling_mode=True, cooling_on=True, automatic=False, setpoint=4)
        expected.statusses[0].setpoint_temperature = 30.0
        expected.statusses[0].setpoint = expected.setpoint = 4  # VACATION = legacy `4` setpoint
        expected.cooling = True
        self.assertEqual(expected, self._thermostat_controller.get_thermostat_status())

        self._thermostat_controller.set_thermostat_mode(thermostat_on=True, cooling_mode=False, cooling_on=True, automatic=True)
        expected.statusses[0].setpoint_temperature = 16.0
        expected.statusses[0].setpoint = expected.setpoint = 0  # AUTO = legacy `0/1/2` setpoint
        expected.statusses[0].automatic = expected.automatic = True
        expected.cooling = False
        self.assertEqual(expected, self._thermostat_controller.get_thermostat_status())
Ejemplo n.º 18
0
    def dto_to_orm(
            thermostat_dto, fields,
            mode):  # type: (ThermostatDTO, List[str], str) -> Thermostat
        # TODO: A mapper should not alter the database, but instead give an in-memory
        #       structure back to the caller to process
        objects = {}  # type: Dict[str, Dict[int, Any]]

        def _load_object(orm_type, number):
            if number is None:
                return None
            return objects.setdefault(orm_type.__name__, {}).setdefault(
                number, orm_type.get(number=number))

        # We don't get a start date, calculate last monday night to map the schedules
        now = int(time.time())
        day_of_week = (now / 86400 - 4) % 7  # 0: Monday, 1: Tuesday, ...
        last_monday_night = now - now % 86400 - day_of_week * 86400

        # Update/save thermostat configuration
        try:
            thermostat = Thermostat.get(number=thermostat_dto.id)
        except Thermostat.DoesNotExist:
            thermostat_group = ThermostatGroup.get(number=0)
            thermostat = Thermostat(number=thermostat_dto.id)
            thermostat.thermostat_group = thermostat_group
        for orm_field, (dto_field, mapping) in {
                'name': ('name', None),
                'sensor': ('sensor', lambda n: _load_object(Sensor, n)),
                'room': ('room', lambda n: _load_object(Room, n)),
                'pid_{0}_p'.format(mode): ('pid_p', float),
                'pid_{0}_i'.format(mode): ('pid_i', float),
                'pid_{0}_d'.format(mode): ('pid_d', float)
        }.items():
            if dto_field not in fields:
                continue
            value = getattr(thermostat_dto, dto_field)
            if value is None:
                continue
            if mapping is not None:
                value = mapping(value)
            setattr(thermostat, orm_field, value)

        thermostat.start = last_monday_night
        thermostat.save()

        # Update/save output configuration
        output_config_present = 'output0' in fields or 'output1' in fields
        if output_config_present:
            # Unlink all previously linked valve_ids, we are resetting this with the new outputs we got from the API
            deleted = ValveToThermostat \
                .delete() \
                .where(ValveToThermostat.thermostat == thermostat) \
                .where(ValveToThermostat.mode == mode) \
                .execute()
            logger.info('Unlinked {0} valve_ids from thermostat {1}'.format(
                deleted, thermostat.name))

            for field in ['output0', 'output1']:
                dto_data = getattr(thermostat_dto, field)
                if dto_data is None:
                    continue

                # 1. Get or create output, creation also saves to db
                output_number = int(dto_data)
                output, output_created = Output.get_or_create(
                    number=output_number)

                # 2. Get or create the valve and link to this output
                try:
                    valve = Valve.get(output=output)
                except DoesNotExist:
                    valve = Valve(output=output)
                valve.name = 'Valve (output {0})'.format(output_number)
                valve.save()

                # 3. Link the valve to the thermostat, set properties
                try:
                    valve_to_thermostat = ValveToThermostat.get(
                        valve=valve, thermostat=thermostat, mode=mode)
                except DoesNotExist:
                    valve_to_thermostat = ValveToThermostat(
                        valve=valve, thermostat=thermostat, mode=mode)

                # TODO: Decide if this is a cooling thermostat or heating thermostat
                valve_to_thermostat.priority = 0 if field == 'output0' else 1
                valve_to_thermostat.save()

        # Update/save scheduling configuration
        for day_index, key in [
            (0, 'auto_mon'), (1, 'auto_tue'), (2, 'auto_wed'), (3, 'auto_thu'),
            (4, 'auto_fri'), (5, 'auto_sat'), (6, 'auto_sun')
        ]:
            if key not in fields:
                continue
            dto_data = getattr(thermostat_dto, key)
            if dto_data is None:
                continue
            try:
                day_schedule = DaySchedule.get(thermostat=thermostat,
                                               index=day_index,
                                               mode=mode)
            except DoesNotExist:
                day_schedule = DaySchedule(thermostat=thermostat,
                                           index=day_index,
                                           mode=mode)
            day_schedule.schedule_data = ThermostatMapper._schedule_dto_to_orm(
                dto_data)
            day_schedule.save()

        # Presets
        for field, preset_type in [('setp3', Preset.Types.AWAY),
                                   ('setp4', Preset.Types.VACATION),
                                   ('setp5', Preset.Types.PARTY)]:
            if field not in fields:
                continue
            dto_data = getattr(thermostat_dto, field)
            if dto_data is None:
                continue
            try:
                preset = Preset.get(type=preset_type, thermostat=thermostat)
            except DoesNotExist:
                preset = Preset(type=preset_type, thermostat=thermostat)
            setattr(preset, '{0}_setpoint'.format(mode), float(dto_data))
            preset.active = False
            preset.save()

        # TODO: Map missing [permanent_manual, setp0, setp1, setp2, pid_int]
        return thermostat
Ejemplo n.º 19
0
    def test_output_sync_change(self):
        events = []

        def on_change(gateway_event):
            events.append(gateway_event)

        self.pubsub.subscribe_gateway_events(PubSub.GatewayTopics.STATE,
                                             on_change)

        outputs = {2: OutputDTO(id=2), 40: OutputDTO(id=40, module_type='D')}
        select_mock = mock.Mock()
        select_mock.join_from.return_value = [
            Output(id=0, number=2),
            Output(id=1, number=40, room=Room(id=2, number=3))
        ]
        with mock.patch.object(Output, 'select', return_value=select_mock), \
             mock.patch.object(self.master_controller, 'load_output',
                               side_effect=lambda output_id: outputs.get(output_id)), \
             mock.patch.object(self.master_controller, 'load_output_status',
                               return_value=[OutputStateDTO(id=2, status=True),
                                             OutputStateDTO(id=40, status=True)]):
            self.controller._sync_state()
            self.pubsub._publish_all_events()
            assert [
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 2,
                        'status': {
                            'on': True,
                            'locked': False
                        },
                        'location': {
                            'room_id': 255
                        }
                    }),
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 40,
                        'status': {
                            'on': True,
                            'value': 0,
                            'locked': False
                        },
                        'location': {
                            'room_id': 3
                        }
                    })
            ] == events

        select_mock = mock.Mock()
        select_mock.join_from.return_value = [
            Output(id=0, number=2),
            Output(id=1, number=40, room=Room(id=2, number=3))
        ]
        with mock.patch.object(Output, 'select', return_value=select_mock), \
             mock.patch.object(self.master_controller, 'load_output',
                               side_effect=lambda output_id: outputs.get(output_id)), \
             mock.patch.object(self.master_controller, 'load_output_status',
                               return_value=[OutputStateDTO(id=2, status=True, dimmer=0),
                                             OutputStateDTO(id=40, status=True, dimmer=50)]):
            events = []
            self.controller._sync_state()
            self.pubsub._publish_all_events()
            assert [
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 2,
                        'status': {
                            'on': True,
                            'locked': False
                        },
                        'location': {
                            'room_id': 255
                        }
                    }),
                GatewayEvent(
                    'OUTPUT_CHANGE', {
                        'id': 40,
                        'status': {
                            'on': True,
                            'value': 50,
                            'locked': False
                        },
                        'location': {
                            'room_id': 3
                        }
                    })
            ] == events
Ejemplo n.º 20
0
    def test_save_pumpgroups(self):
        thermostat = Thermostat.create(number=1,
                                       name='thermostat 1',
                                       sensor=Sensor.create(number=10),
                                       pid_heating_p=200,
                                       pid_heating_i=100,
                                       pid_heating_d=50,
                                       pid_cooling_p=200,
                                       pid_cooling_i=100,
                                       pid_cooling_d=50,
                                       automatic=True,
                                       room=None,
                                       start=0,
                                       valve_config='equal',
                                       thermostat_group=self._thermostat_group)
        valve_1_output = Output.create(number=1)
        valve_1 = Valve.create(number=1,
                               name='valve 1',
                               output=valve_1_output)
        valve_2_output = Output.create(number=2)
        valve_2 = Valve.create(number=2,
                               name='valve 2',
                               output=valve_2_output)
        valve_3_output = Output.create(number=3)
        valve_3 = Valve.create(number=3,
                               name='valve 3',
                               output=valve_3_output)
        ValveToThermostat.create(thermostat=thermostat,
                                 valve=valve_1,
                                 mode=ThermostatGroup.Modes.HEATING,
                                 priority=0)
        ValveToThermostat.create(thermostat=thermostat,
                                 valve=valve_2,
                                 mode=ThermostatGroup.Modes.COOLING,
                                 priority=0)
        ValveToThermostat.create(thermostat=thermostat,
                                 valve=valve_3,
                                 mode=ThermostatGroup.Modes.HEATING,
                                 priority=0)
        Preset.create(type=Preset.Types.SCHEDULE,
                      heating_setpoint=20.0,
                      cooling_setpoint=25.0,
                      active=True,
                      thermostat=thermostat)
        pump_output = Output.create(number=4)
        pump = Pump.create(name='pump 1',
                           output=pump_output)

        heating_pump_groups = self._thermostat_controller.load_heating_pump_groups()
        self.assertEqual([PumpGroupDTO(id=pump.id,
                                       pump_output_id=pump_output.id,
                                       valve_output_ids=[],
                                       room_id=None)], heating_pump_groups)

        PumpToValve.create(pump=pump, valve=valve_1)
        PumpToValve.create(pump=pump, valve=valve_2)

        pump_groups = self._thermostat_controller.load_heating_pump_groups()
        self.assertEqual([PumpGroupDTO(id=pump.id,
                                       pump_output_id=pump_output.id,
                                       valve_output_ids=[valve_1_output.id],
                                       room_id=None)], pump_groups)
        pump_groups = self._thermostat_controller.load_cooling_pump_groups()
        self.assertEqual([PumpGroupDTO(id=pump.id,
                                       pump_output_id=pump_output.id,
                                       valve_output_ids=[valve_2_output.id],
                                       room_id=None)], pump_groups)

        self._thermostat_controller._save_pump_groups(ThermostatGroup.Modes.HEATING,
                                                      [(PumpGroupDTO(id=pump.id,
                                                                     pump_output_id=pump_output.id,
                                                                     valve_output_ids=[valve_1_output.id, valve_3_output.id]),
                                                        ['pump_output_id', 'valve_output_ids'])])
        pump_groups = self._thermostat_controller.load_heating_pump_groups()
        self.assertEqual([PumpGroupDTO(id=pump.id,
                                       pump_output_id=pump_output.id,
                                       valve_output_ids=[valve_1_output.id, valve_3_output.id],
                                       room_id=None)], pump_groups)
        pump_groups = self._thermostat_controller.load_cooling_pump_groups()
        self.assertEqual([PumpGroupDTO(id=pump.id,
                                       pump_output_id=pump_output.id,
                                       valve_output_ids=[valve_2_output.id],
                                       room_id=None)], pump_groups)
Ejemplo n.º 21
0
    def test_transitions(self):
        pump_1 = Pump.create(number=1,
                             name='pump 1',
                             output=Output.create(number=1))
        pump_2 = Pump.create(number=2,
                             name='pump 2',
                             output=Output.create(number=2))
        valve_1 = Valve.create(number=1,
                               name='valve 1',
                               delay=30,
                               output=Output.create(number=11))
        valve_2 = Valve.create(number=2,
                               name='valve 2',
                               delay=15,
                               output=Output.create(number=12))
        valve_3 = Valve.create(number=3,
                               name='valve 3',
                               delay=15,
                               output=Output.create(number=13))
        PumpToValve.create(pump=pump_1, valve=valve_1)
        PumpToValve.create(pump=pump_1, valve=valve_2)
        PumpToValve.create(pump=pump_2, valve=valve_3)

        SetUpTestInjections(output_controller=mock.Mock(OutputController))
        controller = PumpValveController()
        controller.refresh_from_db()

        valve_driver_1 = controller.get_valve_driver(1)
        valve_driver_2 = controller.get_valve_driver(2)
        valve_driver_3 = controller.get_valve_driver(3)
        pump_driver_1 = controller._pump_drivers[1]
        pump_driver_2 = controller._pump_drivers[2]

        # Initial state, everything is off
        self.assertFalse(pump_driver_1.state)
        self.assertEqual(0, valve_driver_1.percentage)
        self.assertEqual(0, valve_driver_2.percentage)
        self.assertFalse(pump_driver_2.state)
        self.assertEqual(0, valve_driver_3.percentage)

        # Set the second valve to 50%
        # The pump should only be turned on after 15s
        valve_driver_2.set(50)
        controller.steer()
        self.assertFalse(pump_driver_1.state)
        self.assertEqual(0, valve_driver_1.percentage)
        self.assertEqual(50, valve_driver_2.percentage)
        self.assertFalse(pump_driver_2.state)
        self.assertEqual(0, valve_driver_3.percentage)

        # Pump still off after 10s
        time.sleep(10)
        controller.steer()
        self.assertFalse(pump_driver_1.state)
        self.assertEqual(0, valve_driver_1.percentage)
        self.assertEqual(50, valve_driver_2.percentage)
        self.assertFalse(pump_driver_2.state)
        self.assertEqual(0, valve_driver_3.percentage)

        # Pump is on after 10s
        time.sleep(10)
        controller.steer()
        self.assertTrue(pump_driver_1.state)
        self.assertEqual(0, valve_driver_1.percentage)
        self.assertEqual(50, valve_driver_2.percentage)
        self.assertFalse(pump_driver_2.state)
        self.assertEqual(0, valve_driver_3.percentage)

        # Other valves are also opened
        valve_driver_1.set(100)
        valve_driver_3.set(100)
        controller.steer()
        self.assertTrue(pump_driver_1.state)
        self.assertEqual(100, valve_driver_1.percentage)
        self.assertEqual(50, valve_driver_2.percentage)
        self.assertFalse(pump_driver_2.state)
        self.assertEqual(100, valve_driver_3.percentage)

        # After a time, both valves are fully open
        time.sleep(40)
        controller.steer()
        self.assertTrue(pump_driver_1.state)
        self.assertEqual(100, valve_driver_1.percentage)
        self.assertEqual(50, valve_driver_2.percentage)
        self.assertTrue(pump_driver_2.state)
        self.assertEqual(100, valve_driver_3.percentage)

        # Two valves are closed again
        # When valves are closed, the pumps are stopped immediately
        valve_driver_2.set(0)
        valve_driver_3.set(0)
        time.sleep(10)
        controller.steer()
        self.assertTrue(pump_driver_1.state)
        self.assertEqual(100, valve_driver_1.percentage)
        self.assertEqual(0, valve_driver_2.percentage)
        self.assertFalse(pump_driver_2.state)
        self.assertEqual(0, valve_driver_3.percentage)
Ejemplo n.º 22
0
    def test_thermostat_group_crud(self):
        thermostat = Thermostat.create(number=1,
                                       name='thermostat 1',
                                       sensor=Sensor.create(number=10),
                                       pid_heating_p=200,
                                       pid_heating_i=100,
                                       pid_heating_d=50,
                                       pid_cooling_p=200,
                                       pid_cooling_i=100,
                                       pid_cooling_d=50,
                                       automatic=True,
                                       room=None,
                                       start=0,
                                       valve_config='equal',
                                       thermostat_group=self._thermostat_group)
        Output.create(number=1)
        Output.create(number=2)
        Output.create(number=3)
        valve_output = Output.create(number=4)
        valve = Valve.create(number=1,
                             name='valve 1',
                             output=valve_output)
        ValveToThermostat.create(thermostat=thermostat,
                                 valve=valve,
                                 mode=ThermostatGroup.Modes.HEATING,
                                 priority=0)
        thermostat_group = ThermostatGroup.get(number=0)  # type: ThermostatGroup
        self.assertEqual(10.0, thermostat_group.threshold_temperature)
        self.assertEqual(0, OutputToThermostatGroup.select()
                                                   .where(OutputToThermostatGroup.thermostat_group == thermostat_group)
                                                   .count())
        self._thermostat_controller.save_thermostat_group((ThermostatGroupDTO(id=0,
                                                                              outside_sensor_id=1,
                                                                              pump_delay=30,
                                                                              threshold_temperature=15,
                                                                              switch_to_heating_0=(1, 0),
                                                                              switch_to_heating_1=(2, 100),
                                                                              switch_to_cooling_0=(1, 100)),
                                                           ['outside_sensor_id', 'pump_delay', 'threshold_temperature',
                                                            'switch_to_heating_0', 'switch_to_heating_1',
                                                            'switch_to_cooling_0']))
        thermostat_group = ThermostatGroup.get(number=0)
        self.assertEqual(15.0, thermostat_group.threshold_temperature)
        links = [{'index': link.index, 'value': link.value, 'mode': link.mode, 'output': link.output_id}
                 for link in (OutputToThermostatGroup.select()
                                                     .where(OutputToThermostatGroup.thermostat_group == thermostat_group))]
        self.assertEqual(3, len(links))
        self.assertIn({'index': 0, 'value': 0, 'mode': 'heating', 'output': 1}, links)
        self.assertIn({'index': 1, 'value': 100, 'mode': 'heating', 'output': 2}, links)
        self.assertIn({'index': 0, 'value': 100, 'mode': 'cooling', 'output': 1}, links)

        new_thermostat_group_dto = ThermostatGroupDTO(id=0,
                                                      outside_sensor_id=1,
                                                      pump_delay=60,
                                                      threshold_temperature=10,
                                                      switch_to_heating_0=(1, 50),
                                                      switch_to_cooling_0=(2, 0))
        self._thermostat_controller.save_thermostat_group((new_thermostat_group_dto,
                                                           ['outside_sensor_id', 'pump_delay', 'threshold_temperature',
                                                            'switch_to_heating_0', 'switch_to_heating_1', 'switch_to_cooling_0']))
        thermostat_group = ThermostatGroup.get(number=0)
        self.assertEqual(10.0, thermostat_group.threshold_temperature)
        links = [{'index': link.index, 'value': link.value, 'mode': link.mode, 'output': link.output_id}
                 for link in (OutputToThermostatGroup.select()
                                                     .where(OutputToThermostatGroup.thermostat_group == thermostat_group))]
        self.assertEqual(2, len(links))
        self.assertIn({'index': 0, 'value': 50, 'mode': 'heating', 'output': 1}, links)
        self.assertIn({'index': 0, 'value': 0, 'mode': 'cooling', 'output': 2}, links)

        self.assertEqual(new_thermostat_group_dto, self._thermostat_controller.load_thermostat_group())