def _schedule_orm_to_dto( schedule_orm ): # type: (DaySchedule) -> Optional[ThermostatScheduleDTO] schedule = schedule_orm.schedule_data amount_of_entries = len(schedule) if amount_of_entries == 0: logger.warning('Mapping an empty temperature day schedule.') return None if amount_of_entries < 5: logger.warning( 'Not enough data to map day schedule. Returning best effort data.' ) return ThermostatScheduleDTO(temp_night=16.0, start_day_1='07:00', end_day_1='09:00', temp_day_1=20, start_day_2='16:00', end_day_2='22:00', temp_day_2=20) # Parsing day/night, assuming following (classic) schedule: # ______ ______ # | | | | # _____| |_____| |_____ # ^ ^ ^ ^ ^ # So to parse a classic format out of it, at least 5 of the markers are required index = 0 kwargs = {} # type: Dict[str, Any] for timestamp in sorted(schedule.keys(), key=lambda t: int(t)): temperature = schedule[timestamp] timestamp_int = int(timestamp) if index == 0: kwargs['temp_night'] = temperature elif index == 1: kwargs['temp_day_1'] = temperature kwargs['start_day_1'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) elif index == 2: kwargs['end_day_1'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) elif index == 3: kwargs['temp_day_2'] = temperature kwargs['start_day_2'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) elif index == 4: kwargs['end_day_2'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) index += 1 return ThermostatScheduleDTO(**kwargs)
def orm_to_dto(orm_object): # type: (EepromModel) -> ThermostatDTO data = orm_object.serialize() kwargs = { 'name': data['name'], 'permanent_manual': data['permanent_manual'] } for i in range(6): field = 'setp{0}'.format(i) kwargs[field] = data[field] for field in [ 'sensor', 'output0', 'output1', 'pid_p', 'pid_i', 'pid_d', 'pid_int', 'room' ]: kwargs[field] = Toolbox.nonify(data[field], ThermostatMapper.BYTE_MAX) for day in ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']: field = 'auto_{0}'.format(day) kwargs[field] = ThermostatScheduleDTO(temp_night=data[field][0], start_day_1=data[field][1], end_day_1=data[field][2], temp_day_1=data[field][3], start_day_2=data[field][4], end_day_2=data[field][5], temp_day_2=data[field][6]) return ThermostatDTO(id=data['id'], **kwargs)
def test_orm_to_dto_mapping(self): controller = GatewayThermostatMappingTests._create_controller() group, _ = ThermostatGroup.get_or_create( number=0, name='Default', on=True, mode=ThermostatGroup.Modes.HEATING) thermostat = Thermostat( number=10, start=0, # 0 is on a thursday name='thermostat', thermostat_group=group) thermostat.save() for i in range(7): day_schedule = DaySchedule(index=i, content='{}', mode='heating') day_schedule.thermostat = thermostat day_schedule.save() heating_thermostats = controller.load_heating_thermostats() self.assertEqual(1, len(heating_thermostats)) dto = heating_thermostats[0] # type: ThermostatDTO self.assertEqual( ThermostatDTO(id=10, name='thermostat', setp3=14.0, setp4=14.0, setp5=14.0, sensor=None, pid_p=120.0, pid_i=0.0, pid_d=0.0, room=None, permanent_manual=True), dto) day_schedule = thermostat.heating_schedules()[0] # type: DaySchedule day_schedule.schedule_data = { 0: 5.0, 120: 5.5, # 120 and 1200 are selected because 120 < 1200, 1200: 6.0, # but str(120) > str(1200) 3600: 6.5, 7500: 7.0 } day_schedule.save() heating_thermostats = controller.load_heating_thermostats() self.assertEqual(1, len(heating_thermostats)) dto = heating_thermostats[0] # type: ThermostatDTO self.assertEqual( ThermostatScheduleDTO(temp_night=5.0, temp_day_1=5.5, temp_day_2=6.5, start_day_1='00:02', end_day_1='00:20', start_day_2='01:00', end_day_2='02:05'), dto.auto_thu)
def _schedule_orm_to_dto( schedule_orm, mode, log_warnings=True ): # type: (DaySchedule, Literal['cooling', 'heating'], bool) -> Optional[ThermostatScheduleDTO] schedule = schedule_orm.schedule_data amount_of_entries = len(schedule) if amount_of_entries == 0: if log_warnings: logger.warning('Mapping an empty temperature day schedule.') schedule = DaySchedule.DEFAULT_SCHEDULE[mode] elif amount_of_entries < 5: if log_warnings: logger.warning( 'Not enough data to map day schedule. Returning best effort data.' ) schedule = DaySchedule.DEFAULT_SCHEDULE[mode] # Parsing day/night, assuming following (classic) schedule: # ______ ______ # | | | | # _____| |_____| |_____ # ^ ^ ^ ^ ^ # So to parse a classic format out of it, at least 5 of the markers are required index = 0 kwargs = {} # type: Dict[str, Any] for timestamp in sorted(schedule.keys(), key=lambda t: int(t)): temperature = schedule[timestamp] timestamp_int = int(timestamp) if index == 0: kwargs['temp_night'] = temperature elif index == 1: kwargs['temp_day_1'] = temperature kwargs['start_day_1'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) elif index == 2: kwargs['end_day_1'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) elif index == 3: kwargs['temp_day_2'] = temperature kwargs['start_day_2'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) elif index == 4: kwargs['end_day_2'] = '{0:02d}:{1:02d}'.format( timestamp_int // 3600, (timestamp_int % 3600) // 60) index += 1 return ThermostatScheduleDTO(**kwargs)
def deserialize( api_data): # type: (Dict) -> Tuple[ThermostatDTO, List[str]] loaded_fields = ['id'] thermostat_dto = ThermostatDTO(api_data['id']) loaded_fields += SerializerToolbox.deserialize( dto=thermostat_dto, # Referenced api_data=api_data, mapping={ 'name': ('name', None), 'permanent_manual': ('permanent_manual', None), 'setp0': ('setp0', None), 'setp1': ('setp1', None), 'setp2': ('setp2', None), 'setp3': ('setp3', None), 'setp4': ('setp4', None), 'setp5': ('setp5', None), 'room': ('room', ThermostatSerializer.BYTE_MAX), 'sensor': ('sensor', ThermostatSerializer.BYTE_MAX), 'output0': ('output0', ThermostatSerializer.BYTE_MAX), 'output1': ('output1', ThermostatSerializer.BYTE_MAX), 'pid_p': ('pid_p', ThermostatSerializer.BYTE_MAX), 'pid_i': ('pid_i', ThermostatSerializer.BYTE_MAX), 'pid_d': ('pid_d', ThermostatSerializer.BYTE_MAX), 'pid_int': ('pid_int', ThermostatSerializer.BYTE_MAX) }) for day in ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']: field = 'auto_{0}'.format(day) if field not in api_data: continue loaded_fields.append(field) field_dto = ThermostatScheduleDTO(temp_night=api_data[field][0], start_day_1=api_data[field][1], end_day_1=api_data[field][2], temp_day_1=api_data[field][3], start_day_2=api_data[field][4], end_day_2=api_data[field][5], temp_day_2=api_data[field][6]) setattr(thermostat_dto, field, field_dto) return thermostat_dto, loaded_fields
def test_save(self): temperatures = {} def _get_temperature(sensor_id): return temperatures[sensor_id] controller = GatewayThermostatMappingTests._create_controller( get_sensor_temperature_status=_get_temperature) room = Room(number=5) room.save() thermostat_group = ThermostatGroup(number=0, name='global') thermostat_group.save() thermostat = Thermostat( number=10, start=0, # 0 is on a thursday name='thermostat', thermostat_group=thermostat_group) thermostat.save() heating_thermostats = controller.load_heating_thermostats() self.assertEqual(1, len(heating_thermostats)) dto = heating_thermostats[0] # type: ThermostatDTO default_schedule_dto = ThermostatScheduleDTO(temp_day_1=20.0, start_day_1='07:00', end_day_1='09:00', temp_day_2=21.0, start_day_2='17:00', end_day_2='22:00', temp_night=16.0) Sensor.create(number=15) dto.room = 5 dto.sensor = 15 dto.output0 = 5 dto.name = 'changed' dto.auto_thu = ThermostatScheduleDTO(temp_night=10, temp_day_1=15, temp_day_2=30, start_day_1='08:00', end_day_1='10:30', start_day_2='16:00', end_day_2='18:45') temperatures[15] = 5.0 controller.save_heating_thermostats([dto]) heating_thermostats = controller.load_heating_thermostats() self.assertEqual(1, len(heating_thermostats)) dto = heating_thermostats[0] # type: ThermostatDTO self.assertEqual( ThermostatDTO(id=10, name='changed', setp3=16.0, setp4=15.0, setp5=22.0, sensor=15, pid_p=120.0, pid_i=0.0, pid_d=0.0, room=5, output0=5, permanent_manual=True, auto_mon=default_schedule_dto, auto_tue=default_schedule_dto, auto_wed=default_schedule_dto, auto_thu=ThermostatScheduleDTO(temp_night=10.0, temp_day_1=15.0, temp_day_2=30.0, start_day_1='08:00', end_day_1='10:30', start_day_2='16:00', end_day_2='18:45'), auto_fri=default_schedule_dto, auto_sat=default_schedule_dto, auto_sun=default_schedule_dto), dto)
def serialize( thermostat_dto, fields): # type: (ThermostatDTO, Optional[List[str]]) -> Dict data = { 'id': thermostat_dto.id, 'name': thermostat_dto.name, 'room': Toolbox.denonify(thermostat_dto.room, ThermostatSerializer.BYTE_MAX), 'setp0': thermostat_dto.setp0, 'setp1': thermostat_dto.setp1, 'setp2': thermostat_dto.setp2, 'setp3': thermostat_dto.setp3, 'setp4': thermostat_dto.setp4, 'setp5': thermostat_dto.setp5, 'sensor': Toolbox.denonify(thermostat_dto.sensor, ThermostatSerializer.BYTE_MAX), 'output0': Toolbox.denonify(thermostat_dto.output0, ThermostatSerializer.BYTE_MAX), 'output1': Toolbox.denonify(thermostat_dto.output1, ThermostatSerializer.BYTE_MAX), 'pid_p': Toolbox.denonify(thermostat_dto.pid_p, ThermostatSerializer.BYTE_MAX), 'pid_i': Toolbox.denonify(thermostat_dto.pid_i, ThermostatSerializer.BYTE_MAX), 'pid_d': Toolbox.denonify(thermostat_dto.pid_d, ThermostatSerializer.BYTE_MAX), 'pid_int': Toolbox.denonify(thermostat_dto.pid_int, ThermostatSerializer.BYTE_MAX), 'permanent_manual': thermostat_dto.permanent_manual } for day in ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']: field = 'auto_{0}'.format(day) dto_data = getattr(thermostat_dto, field) # type: ThermostatScheduleDTO if dto_data is None: # TODO: Remove once UI can handle "no schedule" dto_data = ThermostatScheduleDTO(temp_night=16, temp_day_1=20, temp_day_2=20, start_day_1="07:00", end_day_1="09:00", start_day_2="16:00", end_day_2="22:00") data[field] = [ dto_data.temp_night, dto_data.start_day_1, dto_data.end_day_1, dto_data.temp_day_1, dto_data.start_day_2, dto_data.end_day_2, dto_data.temp_day_2 ] return SerializerToolbox.filter_fields(data, fields)
def test_save(self): temperatures = {} def _get_temperature(sensor_id): return temperatures[sensor_id] controller = GatewayThermostatMappingTests._create_controller( get_sensor_temperature_status=_get_temperature) thermostat_group = ThermostatGroup(number=0, name='global') thermostat_group.save() thermostat = Thermostat( number=10, start=0, # 0 is on a thursday name='thermostat', thermostat_group=thermostat_group) thermostat.save() for i in range(7): day_schedule = DaySchedule(index=i, content='{}', mode='heating') day_schedule.thermostat = thermostat day_schedule.save() heating_thermostats = controller.load_heating_thermostats() self.assertEqual(1, len(heating_thermostats)) dto = heating_thermostats[0] # type: ThermostatDTO Sensor.create(number=15) dto.room = 5 # This field won't be saved dto.sensor = 15 dto.output0 = 5 dto.name = 'changed' dto.auto_thu = ThermostatScheduleDTO(temp_night=10, temp_day_1=15, temp_day_2=30, start_day_1='08:00', end_day_1='10:30', start_day_2='16:00', end_day_2='18:45') temperatures[15] = 5.0 controller.save_heating_thermostats([ (dto, ['sensor', 'output0', 'name', 'auto_thu']) ]) heating_thermostats = controller.load_heating_thermostats() self.assertEqual(1, len(heating_thermostats)) dto = heating_thermostats[0] # type: ThermostatDTO self.assertEqual( ThermostatDTO( id=10, name='changed', setp3=14.0, setp4=14.0, setp5=14.0, sensor=15, pid_p=120.0, pid_i=0.0, pid_d=0.0, room=None, # Unchanged output0=5, permanent_manual=True, auto_thu=ThermostatScheduleDTO(temp_night=10.0, temp_day_1=15.0, temp_day_2=30.0, start_day_1='08:00', end_day_1='10:30', start_day_2='16:00', end_day_2='18:45')), dto)