Public Access
Slow mode replaces the late-evening quarter hours
Slow mode (Home Assistant switch "FlipDot Display Slow mode", dropdown
"FlipDot Display Slow mode interval", web UI Settings) makes the display
change only every 1, 2, 5, 10, 15, 20, 30 or 60 minutes, on the clock, and
powers it off in between: less noise. Nothing else is drawn meanwhile, also
no page changes. It updates at once after boot, the night, the Power switch
and live mode, and when it is switched off. Rules can override it ("show at
once in slow mode"): while their page is shown the display works normally.
Settings layout 7 and schedule layout 4 (older records are converted; they
are stored again once the firmware is marked good, so a rollback still
finds the old ones). Pages in older formats are rewritten in the current
one, in time-boxed steps. /api/status reports old_data_left.
Version 2.15.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a904615c51
commit
968492ca35
39 files changed
+726
-126
No files matched your search
+206
-32
@@ -135,18 +135,27 @@ void test_schedule_clock_from_8_to_22()
|
||||
TEST_ASSERT_TRUE(scheduleFor(21, 59, defaults.schedule) == ScheduleMode::Clock);
|
||||
}
|
||||
|
||||
void test_schedule_quarter_hours_until_23_30()
|
||||
void test_schedule_clock_until_23_30()
|
||||
{
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 0, defaults.schedule) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 1, defaults.schedule) == ScheduleMode::Standby);
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 14, defaults.schedule) == ScheduleMode::Standby);
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 15, defaults.schedule) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 45, defaults.schedule) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(23, 0, defaults.schedule) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(23, 29, defaults.schedule) == ScheduleMode::Standby);
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 1, defaults.schedule) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(23, 29, defaults.schedule) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(23, 30, defaults.schedule) == ScheduleMode::Clock);
|
||||
}
|
||||
|
||||
void test_slow_mode_update_minutes()
|
||||
{
|
||||
TEST_ASSERT_TRUE(slowModeUpdate(22, 0, 15));
|
||||
TEST_ASSERT_FALSE(slowModeUpdate(22, 1, 15));
|
||||
TEST_ASSERT_FALSE(slowModeUpdate(22, 14, 15));
|
||||
TEST_ASSERT_TRUE(slowModeUpdate(22, 15, 15));
|
||||
TEST_ASSERT_TRUE(slowModeUpdate(22, 45, 15));
|
||||
TEST_ASSERT_TRUE(slowModeUpdate(12, 7, 1));
|
||||
TEST_ASSERT_TRUE(slowModeUpdate(12, 40, 20));
|
||||
TEST_ASSERT_FALSE(slowModeUpdate(12, 30, 20));
|
||||
TEST_ASSERT_TRUE(slowModeUpdate(13, 0, 60));
|
||||
TEST_ASSERT_FALSE(slowModeUpdate(13, 30, 60));
|
||||
}
|
||||
|
||||
void test_schedule_night_from_23_31()
|
||||
{
|
||||
TEST_ASSERT_TRUE(scheduleFor(23, 31, defaults.schedule) == ScheduleMode::Night);
|
||||
@@ -387,36 +396,165 @@ void test_night_does_not_power_on()
|
||||
TEST_ASSERT_EQUAL(0, (int)frames.shown.size());
|
||||
}
|
||||
|
||||
void test_standby_keeps_the_last_time_and_powers_off()
|
||||
static void slowMode(bool enabled, uint8_t minutes = 15)
|
||||
{
|
||||
SlowModeSettings slow;
|
||||
slow.enabled = enabled;
|
||||
slow.minutes = minutes;
|
||||
controller->setSettings(defaults.schedule, defaults.clock, slow);
|
||||
}
|
||||
|
||||
void test_slow_mode_keeps_the_last_time_and_powers_off()
|
||||
{
|
||||
slowMode(true);
|
||||
run(22, 0);
|
||||
run(22, 1, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 22:00", frames.last().c_str()); // no off screen
|
||||
TEST_ASSERT_EQUAL(1, (int)frames.shown.size());
|
||||
}
|
||||
|
||||
void test_quarter_hour_powers_on_again_and_redraws()
|
||||
void test_slow_mode_powers_on_again_and_redraws()
|
||||
{
|
||||
slowMode(true);
|
||||
run(22, 0);
|
||||
run(22, 1, POWER_OFF_DELAY + 200);
|
||||
run(22, 14);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
run(22, 15);
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 22:15", frames.last().c_str());
|
||||
TEST_ASSERT_TRUE(frames.last_redraw);
|
||||
run(22, 15, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL(2, (int)frames.shown.size());
|
||||
}
|
||||
|
||||
void test_slow_mode_other_intervals()
|
||||
{
|
||||
slowMode(true, 5);
|
||||
run(12, 0);
|
||||
run(12, 4, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_EQUAL(1, (int)frames.shown.size());
|
||||
run(12, 5);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:05", frames.last().c_str());
|
||||
slowMode(false);
|
||||
slowMode(true, 1); // switched on again: keeps 12:05 until the next minute
|
||||
run(12, 5, 1000);
|
||||
TEST_ASSERT_EQUAL(2, (int)frames.shown.size());
|
||||
run(12, 6);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:06", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_slow_mode_switched_on_keeps_the_image()
|
||||
{
|
||||
run(12, 3);
|
||||
slowMode(true);
|
||||
run(12, 4, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:03", frames.last().c_str());
|
||||
TEST_ASSERT_EQUAL(1, (int)frames.shown.size());
|
||||
run(12, 15);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:15", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_slow_mode_after_boot_updates_at_once()
|
||||
{
|
||||
slowMode(true); // stored setting, before the first update
|
||||
run(12, 7);
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:07", frames.last().c_str());
|
||||
run(12, 8, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL(1, (int)frames.shown.size());
|
||||
}
|
||||
|
||||
void test_slow_mode_page_changes_wait()
|
||||
{
|
||||
slowMode(true);
|
||||
run(12, 0, POWER_OFF_DELAY + 200);
|
||||
controller->setPage(Page::Bitmap);
|
||||
run(12, 5, 1000);
|
||||
controller->pageChanged();
|
||||
run(12, 6, 1000);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL(1, (int)frames.shown.size());
|
||||
run(12, 15);
|
||||
TEST_ASSERT_EQUAL_STRING("page", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_slow_mode_freezes_moving_pages()
|
||||
{
|
||||
frames.wait = 1000; // a scrolling page
|
||||
slowMode(true);
|
||||
run(12, 0, 20000);
|
||||
TEST_ASSERT_EQUAL(1, (int)frames.shown.size());
|
||||
}
|
||||
|
||||
void test_slow_mode_switched_off_updates_at_once()
|
||||
{
|
||||
slowMode(true);
|
||||
run(12, 0, POWER_OFF_DELAY + 200);
|
||||
run(12, 7);
|
||||
slowMode(false);
|
||||
run(12, 7);
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:07", frames.last().c_str());
|
||||
run(12, 8, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_TRUE(power.is_on); // normal again
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:08", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_slow_mode_power_switch_on_updates_at_once()
|
||||
{
|
||||
slowMode(true);
|
||||
run(12, 0);
|
||||
controller->setPowerEnabled(false);
|
||||
run(12, 1, CLEAR_BEFORE_OFF_TIME + 200);
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
controller->setPowerEnabled(true);
|
||||
run(12, 2);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:02", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_slow_mode_override_by_a_rule()
|
||||
{
|
||||
slowMode(true);
|
||||
run(12, 0, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_TRUE(controller->slowModeActive());
|
||||
controller->setSlowModeOverride(true);
|
||||
controller->setPage(Page::Bitmap); // the rule's page
|
||||
run(12, 7);
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("page", frames.last().c_str());
|
||||
TEST_ASSERT_FALSE(controller->slowModeActive());
|
||||
run(12, 8, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_TRUE(power.is_on); // works normally while the rule shows its page
|
||||
|
||||
controller->setSlowModeOverride(false);
|
||||
controller->setPage(Page::Dynamic);
|
||||
run(12, 9);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:09", frames.last().c_str()); // back at once
|
||||
run(12, 10, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on); // then slow again
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:09", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_clock_cancels_pending_power_off()
|
||||
{
|
||||
slowMode(true);
|
||||
run(22, 0);
|
||||
run(22, 14, 10000); // power off countdown running
|
||||
run(22, 1, 10000); // power off countdown running
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
run(22, 15, POWER_OFF_DELAY);
|
||||
slowMode(false);
|
||||
run(22, 1, POWER_OFF_DELAY);
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
TEST_ASSERT_EQUAL(0, power.off_count);
|
||||
}
|
||||
|
||||
void test_power_switch_off_clears_and_powers_off_quickly()
|
||||
{
|
||||
slowMode(true);
|
||||
run(12, 0);
|
||||
run(22, 14, 1000); // 30 s countdown already running
|
||||
controller->setPowerEnabled(false);
|
||||
@@ -528,10 +666,7 @@ void test_default_settings_are_valid()
|
||||
void test_settings_times_must_be_in_order()
|
||||
{
|
||||
Settings s;
|
||||
s.schedule.late_start = 7 * 60; // before day_start
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s = Settings();
|
||||
s.schedule.night_start = 21 * 60; // before late_start
|
||||
s.schedule.night_start = 7 * 60; // before day_start
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s = Settings();
|
||||
s.schedule.night_start = MINUTES_PER_DAY; // never night: fine
|
||||
@@ -540,6 +675,22 @@ void test_settings_times_must_be_in_order()
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
}
|
||||
|
||||
void test_settings_slow_mode_interval()
|
||||
{
|
||||
Settings s;
|
||||
for (uint8_t m : SLOW_MODE_MINUTES)
|
||||
{
|
||||
s.slow_mode.minutes = m;
|
||||
TEST_ASSERT_NULL(validateSettings(s));
|
||||
}
|
||||
s.slow_mode.minutes = 7;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s.slow_mode.minutes = 0;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
TEST_ASSERT_FALSE(Settings().slow_mode.enabled);
|
||||
TEST_ASSERT_EQUAL(15, Settings().slow_mode.minutes);
|
||||
}
|
||||
|
||||
void test_settings_drive_ranges()
|
||||
{
|
||||
Settings s;
|
||||
@@ -713,23 +864,13 @@ void test_schedule_custom_times()
|
||||
{
|
||||
ScheduleSettings s;
|
||||
s.day_start = 6 * 60 + 30;
|
||||
s.late_start = 20 * 60;
|
||||
s.night_start = 21 * 60;
|
||||
TEST_ASSERT_TRUE(scheduleFor(6, 29, s) == ScheduleMode::Night);
|
||||
TEST_ASSERT_TRUE(scheduleFor(6, 30, s) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(20, 1, s) == ScheduleMode::Standby);
|
||||
TEST_ASSERT_TRUE(scheduleFor(20, 30, s) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(20, 1, s) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(21, 0, s) == ScheduleMode::Night);
|
||||
}
|
||||
|
||||
void test_schedule_without_quarter_hours_stays_on_until_night()
|
||||
{
|
||||
ScheduleSettings s;
|
||||
s.quarter_hours = false;
|
||||
TEST_ASSERT_TRUE(scheduleFor(22, 7, s) == ScheduleMode::Clock);
|
||||
TEST_ASSERT_TRUE(scheduleFor(23, 31, s) == ScheduleMode::Night);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pages and live mode
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -783,7 +924,7 @@ void test_settings_change_redraws()
|
||||
run(18, 0);
|
||||
ClockSettings c;
|
||||
c.no_wifi_screen = false;
|
||||
controller->setSettings(defaults.schedule, c);
|
||||
controller->setSettings(defaults.schedule, c, defaults.slow_mode);
|
||||
run(18, 0);
|
||||
TEST_ASSERT_EQUAL(2, (int)frames.shown.size());
|
||||
}
|
||||
@@ -1847,6 +1988,29 @@ void test_selector_take_over_rules()
|
||||
TEST_ASSERT_TRUE(c.instant); // a rule's page leaves at once
|
||||
}
|
||||
|
||||
void test_selector_rule_over_slow_mode()
|
||||
{
|
||||
PageSchedule s = twoBlocks();
|
||||
s.rule_count = 2;
|
||||
s.rules[0] = rule("Deurbel", TriggerMode::WhileTrue);
|
||||
s.rules[0].over_slow_mode = true;
|
||||
s.rules[1] = rule("3D printer", TriggerMode::WhileTrue);
|
||||
PageSelector sel;
|
||||
sel.setSchedule(&s);
|
||||
TEST_ASSERT_FALSE(sel.update(8 * 60, 0).over_slow_mode);
|
||||
sel.setRuleState(1, true, 0);
|
||||
PageChoice c = sel.update(8 * 60, 0);
|
||||
TEST_ASSERT_EQUAL_STRING("3D printer", c.page);
|
||||
TEST_ASSERT_FALSE(c.over_slow_mode);
|
||||
sel.setRuleState(0, true, 0);
|
||||
c = sel.update(8 * 60, 0);
|
||||
TEST_ASSERT_EQUAL_STRING("Deurbel", c.page);
|
||||
TEST_ASSERT_TRUE(c.over_slow_mode);
|
||||
sel.setRuleState(0, false, 0);
|
||||
TEST_ASSERT_FALSE(sel.update(8 * 60, 0).over_slow_mode);
|
||||
TEST_ASSERT_FALSE(TriggerRule().over_slow_mode);
|
||||
}
|
||||
|
||||
void test_selector_rule_can_leave_a_manual_pick_alone()
|
||||
{
|
||||
PageSchedule s = twoBlocks(); // 07:00 Weer + Datum, 17:00 Energie
|
||||
@@ -2400,7 +2564,8 @@ int main()
|
||||
|
||||
RUN_TEST(test_schedule_night_until_8);
|
||||
RUN_TEST(test_schedule_clock_from_8_to_22);
|
||||
RUN_TEST(test_schedule_quarter_hours_until_23_30);
|
||||
RUN_TEST(test_schedule_clock_until_23_30);
|
||||
RUN_TEST(test_slow_mode_update_minutes);
|
||||
RUN_TEST(test_schedule_night_from_23_31);
|
||||
|
||||
RUN_TEST(test_timer_expires_once_after_delay);
|
||||
@@ -2432,8 +2597,16 @@ int main()
|
||||
RUN_TEST(test_clock_is_only_sent_when_the_minute_changes);
|
||||
RUN_TEST(test_night_clears_then_powers_off_after_delay);
|
||||
RUN_TEST(test_night_does_not_power_on);
|
||||
RUN_TEST(test_standby_keeps_the_last_time_and_powers_off);
|
||||
RUN_TEST(test_quarter_hour_powers_on_again_and_redraws);
|
||||
RUN_TEST(test_slow_mode_keeps_the_last_time_and_powers_off);
|
||||
RUN_TEST(test_slow_mode_powers_on_again_and_redraws);
|
||||
RUN_TEST(test_slow_mode_other_intervals);
|
||||
RUN_TEST(test_slow_mode_switched_on_keeps_the_image);
|
||||
RUN_TEST(test_slow_mode_after_boot_updates_at_once);
|
||||
RUN_TEST(test_slow_mode_page_changes_wait);
|
||||
RUN_TEST(test_slow_mode_freezes_moving_pages);
|
||||
RUN_TEST(test_slow_mode_switched_off_updates_at_once);
|
||||
RUN_TEST(test_slow_mode_power_switch_on_updates_at_once);
|
||||
RUN_TEST(test_slow_mode_override_by_a_rule);
|
||||
RUN_TEST(test_clock_cancels_pending_power_off);
|
||||
RUN_TEST(test_power_switch_off_clears_and_powers_off_quickly);
|
||||
RUN_TEST(test_power_switch_off_waits_for_the_refresh_effect);
|
||||
@@ -2448,6 +2621,7 @@ int main()
|
||||
|
||||
RUN_TEST(test_default_settings_are_valid);
|
||||
RUN_TEST(test_settings_times_must_be_in_order);
|
||||
RUN_TEST(test_settings_slow_mode_interval);
|
||||
RUN_TEST(test_settings_drive_ranges);
|
||||
RUN_TEST(test_effect_time_is_zero_when_instant);
|
||||
RUN_TEST(test_settings_off_page_blank_or_bitmap_name);
|
||||
@@ -2461,7 +2635,6 @@ int main()
|
||||
RUN_TEST(test_bitmap_names);
|
||||
RUN_TEST(test_schedule_disabled_is_always_on);
|
||||
RUN_TEST(test_schedule_custom_times);
|
||||
RUN_TEST(test_schedule_without_quarter_hours_stays_on_until_night);
|
||||
RUN_TEST(test_bitmap_page_is_shown_once);
|
||||
RUN_TEST(test_bitmap_page_redrawn_when_changed);
|
||||
RUN_TEST(test_switching_back_to_the_clock_page);
|
||||
@@ -2514,6 +2687,7 @@ int main()
|
||||
RUN_TEST(test_selector_one_block_starts_again_each_day);
|
||||
RUN_TEST(test_selector_take_over_rules);
|
||||
RUN_TEST(test_selector_rules_only_in_their_blocks);
|
||||
RUN_TEST(test_selector_rule_over_slow_mode);
|
||||
RUN_TEST(test_selector_rule_can_leave_a_manual_pick_alone);
|
||||
RUN_TEST(test_selector_add_to_rotation);
|
||||
RUN_TEST(test_ha_auto_unit_and_absolute);
|
||||
|
||||
Reference in new issue
Block a user