def test_rounding(): e = datetime(2018, 12, 13, 2, 0, 0) assert time_utils.ceil_dt_hour(datetime.now()) == e assert time_utils.ceil_dt_hour(e) == e e = time_utils.dt_now_utc().replace(2018, 12, 13, 1, 20, 0) assert time_utils.ceil_dt(time_utils.dt_now_utc(), timedelta(minutes=20)) == e assert time_utils.ceil_dt(e, timedelta(minutes=20)) == e e = datetime(2018, 12, 13, 1, 0, 0) assert time_utils.floor_dt_hour(datetime.now()) == e assert time_utils.floor_dt_hour(e) == e e = time_utils.dt_now_utc().replace(2018, 12, 13, 1, 2, 0) assert time_utils.floor_dt(time_utils.dt_now_utc(), timedelta(minutes=1)) == e assert time_utils.floor_dt(e, timedelta(minutes=1)) == e e = datetime(2018, 12, 13, 1, 0, 0) assert time_utils.round_nearest(datetime.now(), timedelta(hours=1)) == e assert time_utils.round_nearest(e, timedelta(hours=1)) == e assert time_utils.round_nearest(e + timedelta(minutes=30), timedelta(hours=1)) == e assert time_utils.round_nearest( e + timedelta(minutes=31), timedelta(hours=1)) == e + timedelta(hours=1)
def test_state_storage(): central_heating_log.clear_old_entries(time_utils.dt_now_local()) rsp = central_heating_log.query_last_available_state(None) assert rsp.success assert len( rsp.log_entries ) <= 1 # Can also be 1 if we have a current state stored in the dbase rsp = central_heating_log.query_last_available_state( time_utils.dt_now_local()) assert rsp.success assert len(rsp.log_entries) == 0 def _check(exp_flag, exp_task): flag, task = central_heating_log.load_state() assert flag == exp_flag assert task == exp_task central_heating_log.save_state(False, None) _check(False, None) rsp = central_heating_log.query_last_available_state(None) assert rsp.success assert len(rsp.log_entries) == 1 central_heating_log.save_state(True, None) _check(True, None) for _tp in [RequestType.MANUAL, RequestType.SCHEDULED]: for duration in [None, timedelta(seconds=7320)]: for temperature in [None, 17.3, 55.01]: for hysteresis in [None, 0.5, 1]: t = HeatingTask(request_type=_tp, user='******', duration=duration, temperature_target=temperature, temperature_hysteresis=hysteresis) #, # started_at=started_at, ends_at=ends_at) central_heating_log.save_state(False, t) _check(False, t) for start in [time_utils.dt_now_utc(), time_utils.dt_now_local()]: for duration in [timedelta(minutes=25), timedelta(hours=2, minutes=3)]: t = HeatingTask(RequestType.SCHEDULED, 'PyTest', timedelta(minutes=15), None, None) t.start() # To automatically set the end time central_heating_log.save_state(True, t) _check(True, t) assert central_heating_log.query(None, None).success assert central_heating_log.query(None, time_utils.dt_now_local()).success dfrom = time_utils.dt_now_local() - datetime.timedelta(days=3) assert central_heating_log.query(dfrom, None).success assert central_heating_log.query(dfrom, time_utils.dt_now_local()).success # Clean up table for other tests. central_heating_log.clear_old_entries(None)
def test_time_utils(): # Test splitting seconds into days, hours, ... data = { 0: (0, 0, 0, 0), 1: (0, 0, 0, 1), 60: (0, 0, 1, 0), 70: (0, 0, 1, 10), 1800: (0, 0, 30, 0), 3600: (0, 1, 0, 0), 9004: (0, 2, 30, 4), 86399: (0, 23, 59, 59), 86400: (1, 0, 0, 0), 86401: (1, 0, 0, 1), 86460: (1, 0, 1, 0), 86461: (1, 0, 1, 1), 90000: (1, 1, 0, 0), 90001: (1, 1, 0, 1), 90060: (1, 1, 1, 0), 90067: (1, 1, 1, 7) } for insec in data: d, h, m, s = time_utils.days_hours_minutes_seconds_from_sec(insec) assert d == data[insec][0] assert h == data[insec][1] assert m == data[insec][2] assert s == data[insec][3] assert time_utils.t_fromstr(None) is None assert time_utils.t_fromstr('5:30') == time(hour=5, minute=30) assert time_utils.t_fromstr('12:01:30') == time(hour=12, minute=1, second=30) with pytest.raises(ValueError): time_utils.t_fromstr('a') assert time_utils.time_as_local(None) is None assert time_utils.assume_local(None) is None dt_now = time_utils.dt_now_utc() assert time_utils.assume_local(dt_now) != dt_now assert time_utils.assume_local(dt_now).tzinfo == tz.tzlocal() assert time_utils.assume_local(dt_now.timetz()) != dt_now.timetz() res = time_utils.assume_local(dt_now.timetz()) assert res.hour == dt_now.hour assert res.minute == dt_now.minute assert res.second == dt_now.second tmr = time_utils.tomorrow_local() now = time_utils.dt_now_local().date() assert (tmr.day == now.day + 1) or (tmr.day == 1 and tmr.month == now.month + 1)
def test_conversions(): assert time_utils.dt_as_local(None) is None assert time_utils.dt_as_utc(None) is None now_utc = time_utils.dt_now_utc() now_local = time_utils.dt_now_local() assert time_utils.dt_as_utc(now_local) == time_utils.dt_now_utc() assert time_utils.t_now_utc() == now_utc.timetz() assert time_utils.t_now_local() == time_utils.time_as_local( now_utc.timetz()) # time has no timezone, thus, time_as_local() should assume it is in UTC and convert correctly assert time_utils.t_now_local() == time_utils.time_as_local(now_utc.time()) t_local = time_utils.t_now_local() assert time_utils.local_time_as_utc(t_local.hour, t_local.minute, t_local.second) == now_utc.timetz() assert time_utils.t_now_local() == time_utils.time_as_local( time_utils.t_now_local()) # Format as local time: assert time_utils.format_time( t_local) == f"{t_local.hour:02d}:{t_local.minute:02d}" t_utc = time_utils.t_now_utc() assert time_utils.format_time( t_utc) == f"{t_local.hour:02d}:{t_local.minute:02d}"
def test_heating_task_start(): for delta in [ MIN_HEATING_DURATION, MAX_HEATING_DURATION, timedelta(minutes=59, seconds=59), timedelta(hours=1, minutes=30), timedelta(hours=5, seconds=3) ]: t = _mk_valid_manual() assert t.residual_heating_time is None t.duration = delta assert t.residual_heating_time is None assert t.started_at is None t.start() now = time_utils.dt_now_utc() end_time = now + delta assert t.started_at == now assert t.ends_at == end_time assert t.residual_heating_time == delta
def test_heating_controller(): # Skip these tests - we may have an active task in the database upon # creation of the heating singleton (when the module is loaded). Then, # these tests would fail. # # assert not hc.is_heater_on # # assert not hc.is_heating_task_active def _validate_dev_state(expected): for state in central_heater.power_socket_states: assert state.is_powered_on == expected _validate_dev_state(False) # Pause the controller (scheduled tasks should be ignored) assert not central_heater.is_paused assert central_heater.toggle_pause('pytester') assert central_heater.is_paused assert central_heater.start_controller() # We shouldn't be able to start the controller twice assert not central_heater.start_controller() assert not central_heater.start_heating(None).success # Start a scheduled task ts = HeatingTask(RequestType.SCHEDULED, 'another pytester') rsp = central_heater.start_heating(ts) assert not rsp.success # This task is invalid ts.duration = MIN_HEATING_DURATION assert not central_heater.start_heating(ts).success # We're still paused assert not central_heater.toggle_pause('another pytester') assert central_heater.start_heating(ts).success # Now it should work # Wait a bit to let the heater start _sleep() assert central_heater.is_heater_on assert central_heater.is_heating_task_active _validate_dev_state(True) assert central_heater.residual_heating_time is not None assert central_heater.residual_heating_time < MIN_HEATING_DURATION # Stop the task again assert central_heater.stop_heating('pytester').success _sleep() assert not central_heater.is_heater_on assert not central_heater.is_heating_task_active _validate_dev_state(False) # Pause the controller (manual tasks should overrule this) assert central_heater.toggle_pause('pytester') assert central_heater.is_paused # Start a manual task without feedback controller tm = HeatingTask(RequestType.MANUAL, 'Overruler') assert not tm.needs_feedback_controller assert central_heater.start_heating(tm).success assert not central_heater.is_paused # System must be "unpaused" now _sleep() assert central_heater.is_heater_on assert central_heater.is_heating_task_active assert central_heater.residual_heating_time is None _validate_dev_state(True) assert not central_heater.start_heating( ts).success # A scheduled task cannot replace an active manual task assert central_heater.stop_heating('pytester').success _sleep() assert not central_heater.is_heater_on assert not central_heater.is_heating_task_active _validate_dev_state(False) # Start a manual task with feedback controller assert not central_heater.is_paused tm = HeatingTask(RequestType.MANUAL, 'Standard user', temperature_target=30) assert tm.needs_feedback_controller assert central_heater.start_heating(tm).success assert not central_heater.is_paused _sleep() assert central_heater.is_heater_on assert central_heater.is_heating_task_active _validate_dev_state(True) assert central_heater.stop_heating('pytester').success _sleep() assert not central_heater.is_heater_on assert not central_heater.is_heating_task_active _validate_dev_state(False) # Test if a task times out properly with freeze_time(time_utils.dt_now_utc()) as frozen_datetime: tm = HeatingTask(RequestType.MANUAL, 'Time Out User', duration=MIN_HEATING_DURATION) assert central_heater.start_heating(tm).success frozen_datetime.tick(delta=timedelta(seconds=1) + MIN_HEATING_DURATION) central_heater._force_loop_iteration() _sleep() assert not central_heater.is_heating_task_active central_heater.shutdown()