def test_serialize(self):
        # expected json data for valid serialization
        validJson = '''{"start": "12:00:00", "end": "15:00:00", "mode": "MANUAL", "position": 52}'''
        timeBlock = ScheduleTimeBlock(datetime.time(12,
                                                    0), datetime.time(15, 0),
                                      BlindMode.MANUAL, 52)

        assert (ScheduleTimeBlock.toJson(timeBlock) == validJson)
    def test_invalidPosition(self):
        startTime = datetime.time(11, 11)
        endTime = datetime.time(17, 55)
        mode = BlindMode.MANUAL
        position = 7

        timeBlock = ScheduleTimeBlock(startTime, endTime, mode, position)
        timeBlock._position = -101  # not a valid position

        with pytest.raises(InvalidTimeBlockException):
            timeBlock.validate()
    def test_validFromDict(self):
        timeBlockDict = {
            "start": "08:00:00",
            "end": "22:11:00",
            "mode": "MANUAL",
            "position": 52
        }

        expectedBlock = ScheduleTimeBlock(datetime.time(8, 0),
                                          datetime.time(22, 11),
                                          BlindMode.MANUAL, 52)

        convertedBlock = ScheduleTimeBlock.fromDict(timeBlockDict)

        assert (convertedBlock == expectedBlock)
    def toTimeBlock( self, currentTimeProvider=None ): 
        if currentTimeProvider is None:
            currentTime = datetime.datetime.utcnow().time()
        else:
            currentTime = currentTimeProvider()
        startTime = datetime.time( currentTime.hour, currentTime.minute )

        if ( self._duration == 0 ):
            endTime = ScheduleTimeBlock.END_OF_DAY
        else:
            # convert duration in minutes to hours + minutes
            hourOffset = int( ( currentTime.minute + self._duration ) / 60 ) 
            newHour = currentTime.hour + hourOffset
            newMinutes = ( currentTime.minute + self._duration ) % 60

            if ( newHour > 23 ):
                endTime = ScheduleTimeBlock.END_OF_DAY
            else:
                endTime = datetime.time( newHour, newMinutes )
        
        # check edge case of start and end at 23:59, which will be only case of true 0 duration
        # this will cause errors in the time block. In this case, there is 0 duration and thus no 
        # real time block, so we return None for consistency
        if ( startTime == endTime ):
            return None

        return ScheduleTimeBlock( startTime, endTime, self._mode, self._position )
    def test_validDeserialize(self):
        validJson = '''{"start": "12:00:00", "end": "15:00:00", "mode": "MANUAL", "position": 52}'''
        timeBlock = ScheduleTimeBlock.fromJson(validJson)

        assert (timeBlock._start == datetime.time(12, 0))
        assert (timeBlock._end == datetime.time(15, 0))
        assert (timeBlock._mode == BlindMode.MANUAL)
        assert (timeBlock._position == 52)
    def test_update(self):
        startTime = datetime.time(3, 41)
        endTime = datetime.time(12, 41)
        mode = BlindMode.MANUAL
        position = 7
        timeBlock = ScheduleTimeBlock(startTime, endTime, mode, position)

        newStart = datetime.time(15, 41)
        newEnd = datetime.time(18, 9)
        newMode = BlindMode.DARK

        timeBlock.update(newStart, newEnd, newMode)

        assert (timeBlock._start == newStart)
        assert (timeBlock._end == newEnd)
        assert (timeBlock._mode == newMode)
        assert (timeBlock._position == None)
    def check_state_and_update( self ):
        current_datetime = datetime.datetime.now( self._blindsSchedule._timezone )
        current_time = current_datetime.time()
        print( f"Checking and updating at time: {current_datetime}" )

        # check active command. apply or clear the command
        if self._activeCommandTimeBlock is not None:
            check_time_result = self._activeCommandTimeBlock.checkTime( current_time )

            # case 1: current time is before the command duration
            # ignore and do nothing, this means the command does not have to be dealt with yet

            # case 2: current time is within the command duration
            if check_time_result == 0:
                print( "DEBUG: Found an applicable command.", ScheduleTimeBlock.toJson( self._activeCommandTimeBlock ) )
                self.do_blinds_update( self._activeCommandTimeBlock._mode, self._activeCommandTimeBlock._position )
                return

            # case 3: current time is after the command duration
            elif check_time_result == 1:
                # clear the current command, it is no longer valid
                self._activeCommandTimeBlock = None
                
        # At this point, there is no need to deal with manual commands. Check the schedule for a time block
        current_weekday_index = current_datetime.weekday()
        current_weekday_name = BlindsSchedule.DAYS_OF_WEEK[ current_weekday_index ]

        day_sched = self._blindsSchedule._schedule.get( current_weekday_name )

        active_schedule_block = None
        for block in day_sched:
            if block.checkTime( current_time ) == 0:
                active_schedule_block = block
                break

        # found a time block correspoding to current time
        if active_schedule_block is not None: 
            print( "DEBUG: Found an applicable scheduled block.", ScheduleTimeBlock.toJson( active_schedule_block ) )
            self.do_blinds_update( active_schedule_block._mode, active_schedule_block._position )
            return 

        # At this point, no time block was found, so we go to the default behaviour
        print( "DEBUG: Using defaults. Mode=", self._blindsSchedule._default_mode.name, " Pos=", self._blindsSchedule._default_pos )
        self.do_blinds_update( self._blindsSchedule._default_mode, self._blindsSchedule._default_pos )
        return 
    def test_toDict(self):
        expectedDict = {
            "start": datetime.time(8, 0),
            "end": datetime.time(19, 54),
            "mode": "MANUAL",
            "position": 52
        }

        timeBlock = ScheduleTimeBlock(expectedDict["start"],
                                      expectedDict["end"],
                                      BlindMode[expectedDict["mode"]],
                                      expectedDict["position"])
        timeBlockDict = ScheduleTimeBlock.toDict(timeBlock)

        assert (timeBlockDict["start"] == str(expectedDict["start"]))
        assert (timeBlockDict["end"] == str(expectedDict["end"]))
        assert (timeBlockDict["mode"] == expectedDict["mode"])
        assert (timeBlockDict["position"] == expectedDict["position"])
    def test_constructor(self):
        startTime = datetime.time(3, 41)
        endTime = datetime.time(12, 41)
        mode = BlindMode.MANUAL
        position = 7
        timeBlock = ScheduleTimeBlock(startTime, endTime, mode, position)

        assert (timeBlock._start == startTime)
        assert (timeBlock._end == endTime)
        assert (timeBlock._mode == mode)
        assert (timeBlock._position == position)
示例#10
0
    def test_sortedTimeBlockList(self):
        # empty list case, verify that it runs with no errors and returns the empty list
        sortedList = BlindsSchedule.sortedTimeBlockList([])
        assert (not sortedList
                )  # this checks for result being an empty sequence

        timeBlockList = [
            ScheduleTimeBlock(datetime.time(12, 00), datetime.time(15, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(4, 3), datetime.time(6, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(22, 44), datetime.time(23, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(10, 22), datetime.time(12, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(00, 32), datetime.time(1, 00),
                              BlindMode.LIGHT, None)
        ]

        sortedList = BlindsSchedule.sortedTimeBlockList(timeBlockList)

        previousBlock = sortedList[0]
        for block in sortedList[1:]:
            assert (previousBlock._start <= block._start)
示例#11
0
    def test_hasConflict(self):
        # lists must be sorted by start time
        # list without conflict, should return False
        timeBlockList = [
            ScheduleTimeBlock(datetime.time(4, 3), datetime.time(6, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(12, 00), datetime.time(15, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(22, 44), datetime.time(23, 00),
                              BlindMode.LIGHT, None),
        ]

        assert (BlindsSchedule.hasConflict(timeBlockList) == False)

        # list with conflict, should return True
        timeBlockList = [
            ScheduleTimeBlock(datetime.time(4, 3), datetime.time(13, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(12, 00), datetime.time(15, 00),
                              BlindMode.LIGHT, None),
            ScheduleTimeBlock(datetime.time(22, 44), datetime.time(23, 00),
                              BlindMode.LIGHT, None),
        ]

        assert (BlindsSchedule.hasConflict(timeBlockList) == True)
示例#12
0
    def test_serialization(self):
        default_mode = BlindMode.LIGHT
        default_pos = None
        tz = timezone("Etc/GMT-6")
        sched = {
            BlindsSchedule.SUNDAY: [
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(5, 00), datetime.time(7, 00),
                                  BlindMode.MANUAL, 15)
            ],
            BlindsSchedule.MONDAY: [
                ScheduleTimeBlock(datetime.time(12, 00), datetime.time(15, 00),
                                  BlindMode.MANUAL, -33),
                ScheduleTimeBlock(datetime.time(4, 3), datetime.time(6, 00),
                                  BlindMode.LIGHT, None)
            ],
            BlindsSchedule.TUESDAY: [
                ScheduleTimeBlock(datetime.time(10, 22), datetime.time(12, 00),
                                  BlindMode.DARK, None)
            ],
            BlindsSchedule.WEDNESDAY: [],
            BlindsSchedule.THURSDAY: [],
            BlindsSchedule.FRIDAY: [
                ScheduleTimeBlock(datetime.time(22, 44), datetime.time(23, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.ECO, None),
                ScheduleTimeBlock(datetime.time(00, 32), datetime.time(1, 00),
                                  BlindMode.LIGHT, None)
            ],
            BlindsSchedule.SATURDAY: []
        }

        blindsSchedule = BlindsSchedule(default_mode,
                                        default_pos,
                                        sched,
                                        timezone=tz)

        # use the sortKeys flag to allow deterministic output for comparison
        scheduleJson = BlindsSchedule.toJson(blindsSchedule, sortKeys=True)

        # open schedule1.json to compare
        with open(os.path.join(testFilePath, "schedule1.json"), "r") as file:
            expectedJson = file.read()

            assert (scheduleJson == expectedJson)
示例#13
0
    def test_constructor(self):
        default_mode = BlindMode.LIGHT
        default_pos = -45
        tz = timezone("Etc/GMT-6")
        sched = {
            BlindsSchedule.SUNDAY: [
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(5, 00), datetime.time(7, 00),
                                  BlindMode.MANUAL, 15)
            ],
            BlindsSchedule.MONDAY: [
                ScheduleTimeBlock(datetime.time(12, 00), datetime.time(15, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(4, 3), datetime.time(6, 00),
                                  BlindMode.LIGHT, None)
            ],
            BlindsSchedule.TUESDAY: [
                ScheduleTimeBlock(datetime.time(10, 22), datetime.time(12, 00),
                                  BlindMode.DARK, None)
            ],
            BlindsSchedule.WEDNESDAY: [],
            BlindsSchedule.THURSDAY: [],
            BlindsSchedule.FRIDAY: [
                ScheduleTimeBlock(datetime.time(22, 44), datetime.time(23, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.ECO, None),
                ScheduleTimeBlock(datetime.time(00, 32), datetime.time(1, 00),
                                  BlindMode.LIGHT, None)
            ],
            BlindsSchedule.SATURDAY: []
        }

        blindsSchedule = BlindsSchedule(default_mode,
                                        default_pos,
                                        sched,
                                        timezone=tz)
        assert (blindsSchedule._default_mode == default_mode)
        assert (blindsSchedule._default_pos == default_pos)
        assert (blindsSchedule._timezone.zone == tz.zone)

        for day in BlindsSchedule.DAYS_OF_WEEK:
            assert (len(blindsSchedule._schedule[day]) == len(sched[day]))
            for block in blindsSchedule._schedule[day]:
                assert (block in sched[day])
示例#14
0
    def test_validDeserialization(self):
        default_mode = BlindMode.LIGHT
        default_pos = None
        tz = timezone("Etc/GMT+6")
        sched = {
            BlindsSchedule.SUNDAY: [
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(5, 00), datetime.time(7, 00),
                                  BlindMode.MANUAL, 15)
            ],
            BlindsSchedule.MONDAY: [
                ScheduleTimeBlock(datetime.time(12, 00), datetime.time(15, 00),
                                  BlindMode.MANUAL, -33),
                ScheduleTimeBlock(datetime.time(4, 3), datetime.time(6, 00),
                                  BlindMode.LIGHT, None)
            ],
            BlindsSchedule.TUESDAY: [
                ScheduleTimeBlock(datetime.time(10, 22), datetime.time(12, 00),
                                  BlindMode.DARK, None)
            ],
            BlindsSchedule.WEDNESDAY: [],
            BlindsSchedule.THURSDAY: [],
            BlindsSchedule.FRIDAY: [
                ScheduleTimeBlock(datetime.time(22, 44), datetime.time(23, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.ECO, None),
                ScheduleTimeBlock(datetime.time(00, 32), datetime.time(1, 00),
                                  BlindMode.LIGHT, None)
            ],
            BlindsSchedule.SATURDAY: []
        }

        expectedSched = BlindsSchedule(default_mode, default_pos, sched)

        with open(os.path.join(testFilePath, "schedule1.json"), "r") as file:
            scheduleJson = file.read()

            parsedSched = BlindsSchedule.fromJson(scheduleJson)

            assert (parsedSched._default_mode == expectedSched._default_mode)
            assert (parsedSched._default_pos == expectedSched._default_pos)
            assert (parsedSched._timezone.zone == tz.zone)
            assert (parsedSched._schedule == expectedSched._schedule)
示例#15
0
    def test_postSchedule(self, blindsSystem):
        test_sched = BlindsSchedule(
            BlindMode.DARK,
            schedule={
                BlindsSchedule.SUNDAY: [
                    ScheduleTimeBlock(datetime.time(23, 38),
                                      datetime.time(23, 59), BlindMode.LIGHT,
                                      None),
                    ScheduleTimeBlock(datetime.time(5,
                                                    00), datetime.time(7, 00),
                                      BlindMode.MANUAL, 15)
                ],
                BlindsSchedule.MONDAY: [
                    ScheduleTimeBlock(datetime.time(12, 00),
                                      datetime.time(15, 00), BlindMode.LIGHT,
                                      None),
                    ScheduleTimeBlock(datetime.time(4,
                                                    3), datetime.time(6, 00),
                                      BlindMode.LIGHT, None)
                ],
                BlindsSchedule.TUESDAY: [
                    ScheduleTimeBlock(datetime.time(10, 22),
                                      datetime.time(12, 00), BlindMode.DARK,
                                      None)
                ],
                BlindsSchedule.WEDNESDAY: [],
                BlindsSchedule.THURSDAY: [],
                BlindsSchedule.FRIDAY: [
                    ScheduleTimeBlock(datetime.time(22, 44),
                                      datetime.time(23, 00), BlindMode.LIGHT,
                                      None),
                    ScheduleTimeBlock(datetime.time(23, 38),
                                      datetime.time(23, 59), BlindMode.ECO,
                                      None),
                    ScheduleTimeBlock(datetime.time(00, 32),
                                      datetime.time(1, 00), BlindMode.LIGHT,
                                      None)
                ],
                BlindsSchedule.SATURDAY: []
            })

        assert (blindsSystem.postSchedule(
            BlindsSchedule.toDict(test_sched))[1] == RESP_CODES["ACCEPTED"])
    def postBlindsCommand( self, command, forceUpdate=False ):
        print( "processing request for POST command")
        try:
            blindsCommand = BlindsCommand.fromDict( command )

            # update active command, use custome time provider to insert a timezone
            self._activeCommandTimeBlock = blindsCommand.toTimeBlock( 
                    currentTimeProvider=datetime.datetime.now( self._blindsSchedule._timezone ).time )

            # return the resulting time block from the command
            data = ScheduleTimeBlock.toDict( self._activeCommandTimeBlock ) or {}

            if forceUpdate:
                # Update current state based on the command
                self.check_state_and_update()

            return data, RESP_CODES[ "ACCEPTED" ]   

        except Exception as err:
            return ( str(err), RESP_CODES[ "BAD_REQUEST" ] )
示例#17
0
    def test_scheduleConflictChecks(self):
        default_mode = BlindMode.LIGHT
        default_pos = -45

        blindsSchedule = BlindsSchedule(default_mode, default_pos)

        # start without conflict
        sched = {
            BlindsSchedule.SUNDAY: [],
            BlindsSchedule.MONDAY: [],
            BlindsSchedule.TUESDAY: [],
            BlindsSchedule.WEDNESDAY: [],
            BlindsSchedule.THURSDAY: [],
            BlindsSchedule.FRIDAY: [
                ScheduleTimeBlock(datetime.time(00, 32), datetime.time(6, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(16, 44), datetime.time(23, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.ECO, None)
            ],
            BlindsSchedule.SATURDAY: []
        }

        blindsSchedule._schedule = sched
        assert (blindsSchedule.checkHasNoTimeConflicts() == True)

        # add time conflict
        sched = {
            BlindsSchedule.SUNDAY: [],
            BlindsSchedule.MONDAY: [],
            BlindsSchedule.TUESDAY: [],
            BlindsSchedule.WEDNESDAY: [],
            BlindsSchedule.THURSDAY: [],
            BlindsSchedule.FRIDAY: [
                ScheduleTimeBlock(datetime.time(00, 32), datetime.time(20, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(16, 44), datetime.time(23, 00),
                                  BlindMode.LIGHT, None),
                ScheduleTimeBlock(datetime.time(23, 38), datetime.time(23, 59),
                                  BlindMode.ECO, None)
            ],
            BlindsSchedule.SATURDAY: []
        }

        blindsSchedule._schedule = sched
        with pytest.raises(BlindSchedulingException):
            blindsSchedule.checkHasNoTimeConflicts()
 def test_invalidDeserialize(self):
     invalidJson = '''{"start": "12:00:00", "end": "15:00:00", "mode": "MANUAL"}'''
     with pytest.raises(InvalidTimeBlockException):
         timeBlock = ScheduleTimeBlock.fromJson(invalidJson)
    def test_invalidFromDict(self):
        timeBlockDict = {"start": "08:00:00", "mode": "MANUAL", "position": 52}

        with pytest.raises(InvalidTimeBlockException):
            convertedBlock = ScheduleTimeBlock.fromDict(timeBlockDict)