Public Access
Add HA dropdowns, off image, flip fixes and refresh effects
Home Assistant: remove the Clock switch (its entity is deleted on connect), add an IP sensor and Mode / Off image dropdowns whose options follow the saved bitmaps. Remove the built-in "Nintendo border" bitmap. Off image: a bitmap (or blank) left on the dots when the display switches off. Nano: pause after each flip pulse and repeat pulses for changed dots, both stored in EEPROM (G/N commands). Refresh effects (E command): instant, sparkle, wipe, rain, from the middle out, diagonal, spiral, flip all then reveal, push and random, optionally only for small changes. Effects run in steps from loop() so serial input keeps flowing. Web UI: flip settings, refresh effect and test patterns under Settings; the System tab shows why the ESP32 last restarted. Settings records from versions 1 and 2 are migrated. Firmware 2.3.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4a5f6a8b5d
commit
1d3b084de1
42 files changed
+1631
-230
No files matched your search
@@ -63,8 +63,7 @@ public:
|
||||
snprintf(s, sizeof(s), "nowifi %02d:%02d", hour, minute);
|
||||
shown.push_back(s);
|
||||
}
|
||||
void logo() override { shown.push_back("logo"); }
|
||||
void blank() override { shown.push_back("blank"); }
|
||||
void off() override { shown.push_back("off"); }
|
||||
|
||||
std::string last() const { return shown.empty() ? "" : shown.back(); }
|
||||
};
|
||||
@@ -363,7 +362,7 @@ void test_night_clears_then_powers_off_after_delay()
|
||||
{
|
||||
run(23, 30);
|
||||
run(23, 31, POWER_OFF_DELAY - 200);
|
||||
TEST_ASSERT_EQUAL_STRING("blank", frames.last().c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
run(23, 31, 300);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
@@ -382,7 +381,7 @@ void test_standby_keeps_the_last_time_and_powers_off()
|
||||
run(22, 0);
|
||||
run(22, 1, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 22:00 evening", frames.last().c_str()); // no blank
|
||||
TEST_ASSERT_EQUAL_STRING("clock 22:00 evening", frames.last().c_str()); // no off screen
|
||||
}
|
||||
|
||||
void test_quarter_hour_powers_on_again_and_redraws()
|
||||
@@ -411,7 +410,19 @@ void test_power_switch_off_clears_and_powers_off_quickly()
|
||||
run(22, 14, 1000); // 30 s countdown already running
|
||||
controller->setPowerEnabled(false);
|
||||
run(22, 14, CLEAR_BEFORE_OFF_TIME + 200);
|
||||
TEST_ASSERT_EQUAL_STRING("blank", frames.last().c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
}
|
||||
|
||||
void test_power_switch_off_waits_for_the_refresh_effect()
|
||||
{
|
||||
controller->setEffectTime(3000);
|
||||
run(12, 0);
|
||||
controller->setPowerEnabled(false);
|
||||
run(12, 0, CLEAR_BEFORE_OFF_TIME + 2000);
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
TEST_ASSERT_TRUE(power.is_on); // the effect is still flipping dots
|
||||
run(12, 0, 1400);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
}
|
||||
|
||||
@@ -433,33 +444,6 @@ void test_power_switch_on_again_shows_clock()
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:01", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_clock_switch_off_shows_logo_then_powers_off()
|
||||
{
|
||||
run(12, 0);
|
||||
controller->setClockEnabled(false);
|
||||
run(12, 0, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_EQUAL_STRING("logo", frames.last().c_str());
|
||||
TEST_ASSERT_EQUAL(1, (int)std::count(frames.shown.begin(), frames.shown.end(), std::string("logo")));
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
}
|
||||
|
||||
void test_clock_switch_off_never_powers_on()
|
||||
{
|
||||
controller->setClockEnabled(false);
|
||||
run(12, 0, 60000);
|
||||
TEST_ASSERT_EQUAL(0, power.on_count);
|
||||
}
|
||||
|
||||
void test_clock_switch_on_again_shows_clock()
|
||||
{
|
||||
run(12, 0);
|
||||
controller->setClockEnabled(false);
|
||||
run(12, 0);
|
||||
controller->setClockEnabled(true);
|
||||
run(12, 0);
|
||||
TEST_ASSERT_EQUAL_STRING("clock 12:00", frames.last().c_str());
|
||||
}
|
||||
|
||||
void test_redraws_after_power_was_switched_off_elsewhere()
|
||||
{
|
||||
// e.g. after flashing the Nano: the Nano restarts without a frame
|
||||
@@ -518,7 +502,7 @@ void test_night_without_wifi_still_powers_off()
|
||||
wifi = false;
|
||||
run(23, 31, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
TEST_ASSERT_EQUAL_STRING("blank", frames.last().c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -545,6 +529,64 @@ void test_settings_times_must_be_in_order()
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
}
|
||||
|
||||
void test_settings_drive_ranges()
|
||||
{
|
||||
Settings s;
|
||||
TEST_ASSERT_NULL(validateSettings(s));
|
||||
s.drive.flip_gap = 5001;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s = Settings();
|
||||
s.drive.pulses = 0;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s.drive.pulses = 4;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s = Settings();
|
||||
s.drive.effect = DRIVE_EFFECT_COUNT;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s = Settings();
|
||||
s.drive.effect_time = 99;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
s.drive.effect_time = 10001;
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
}
|
||||
|
||||
void test_effect_time_is_zero_when_instant()
|
||||
{
|
||||
DriveSettings d;
|
||||
TEST_ASSERT_EQUAL(0, (int)effectTime(d)); // default: instant
|
||||
d.effect = 1;
|
||||
d.effect_time = 2500;
|
||||
TEST_ASSERT_EQUAL(2500, (int)effectTime(d));
|
||||
d.effect = DRIVE_EFFECT_PUSH; // can take much longer
|
||||
TEST_ASSERT_EQUAL(2500 + (int)DRIVE_PUSH_EXTRA_TIME, (int)effectTime(d));
|
||||
d.effect = DRIVE_EFFECT_RANDOM; // can be a push
|
||||
TEST_ASSERT_EQUAL(2500 + (int)DRIVE_PUSH_EXTRA_TIME, (int)effectTime(d));
|
||||
}
|
||||
|
||||
void test_settings_off_page_blank_or_bitmap_name()
|
||||
{
|
||||
Settings s;
|
||||
TEST_ASSERT_EQUAL_STRING("", s.off_page); // default: blank
|
||||
TEST_ASSERT_NULL(validateSettings(s));
|
||||
strcpy(s.off_page, "Nintendo");
|
||||
TEST_ASSERT_NULL(validateSettings(s));
|
||||
strcpy(s.off_page, "../x");
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
memset(s.off_page, 'a', sizeof(s.off_page)); // not terminated
|
||||
TEST_ASSERT_NOT_NULL(validateSettings(s));
|
||||
}
|
||||
|
||||
void test_changed_off_image_is_redrawn_while_the_display_is_on()
|
||||
{
|
||||
run(23, 30);
|
||||
run(23, 31, 1000);
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
controller->pageChanged();
|
||||
run(23, 31, 1000);
|
||||
TEST_ASSERT_EQUAL(2, (int)std::count(frames.shown.begin(), frames.shown.end(), std::string("off")));
|
||||
TEST_ASSERT_TRUE(power.is_on);
|
||||
}
|
||||
|
||||
void test_settings_flip_time_and_port_ranges()
|
||||
{
|
||||
Settings s;
|
||||
@@ -719,7 +761,7 @@ void test_bitmap_page_follows_the_schedule()
|
||||
controller->setPage(Page::Bitmap);
|
||||
run(23, 30);
|
||||
run(23, 31, POWER_OFF_DELAY + 200);
|
||||
TEST_ASSERT_EQUAL_STRING("blank", frames.last().c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("off", frames.last().c_str());
|
||||
TEST_ASSERT_FALSE(power.is_on);
|
||||
}
|
||||
|
||||
@@ -835,11 +877,9 @@ int main()
|
||||
RUN_TEST(test_quarter_hour_powers_on_again_and_redraws);
|
||||
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);
|
||||
RUN_TEST(test_power_switch_off_keeps_display_off_during_the_day);
|
||||
RUN_TEST(test_power_switch_on_again_shows_clock);
|
||||
RUN_TEST(test_clock_switch_off_shows_logo_then_powers_off);
|
||||
RUN_TEST(test_clock_switch_off_never_powers_on);
|
||||
RUN_TEST(test_clock_switch_on_again_shows_clock);
|
||||
RUN_TEST(test_redraws_after_power_was_switched_off_elsewhere);
|
||||
RUN_TEST(test_no_wifi_is_shown_after_a_minute_without_wifi);
|
||||
RUN_TEST(test_no_wifi_screen_keeps_the_time_running);
|
||||
@@ -849,6 +889,10 @@ int main()
|
||||
|
||||
RUN_TEST(test_default_settings_are_valid);
|
||||
RUN_TEST(test_settings_times_must_be_in_order);
|
||||
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);
|
||||
RUN_TEST(test_changed_off_image_is_redrawn_while_the_display_is_on);
|
||||
RUN_TEST(test_settings_flip_time_and_port_ranges);
|
||||
RUN_TEST(test_settings_page_must_be_clock_or_bitmap_name);
|
||||
RUN_TEST(test_time_parse_and_format);
|
||||
|
||||
Reference in new issue
Block a user