From d05f4c39ec9a5006c30374fd369ae4a049cd40b9 Mon Sep 17 00:00:00 2001 From: Alexander Rosenberg Date: Fri, 5 Apr 2024 04:16:42 -0700 Subject: [PATCH] Add date set/view and power screen --- Makefile | 18 +++-- src/main.c | 8 ++ src/ui/datesel.c | 84 +++++++++++---------- src/ui/datesel.h | 6 ++ src/ui/powerscreen.c | 75 +++++++++++++++++++ src/ui/powerscreen.h | 31 ++++++++ src/ui/setdatescreen.c | 157 ++++++++++++++++++++++++++++++++++++++++ src/ui/setdatescreen.h | 35 +++++++++ src/ui/statrange.c | 24 +----- src/ui/timesel.c | 18 +++++ src/ui/timesel.h | 5 ++ src/ui/viewdatescreen.c | 45 ++++++++++++ src/ui/viewdatescreen.h | 25 +++++++ src/util.c | 2 +- src/util.h | 2 +- 15 files changed, 468 insertions(+), 67 deletions(-) create mode 100644 src/ui/powerscreen.c create mode 100644 src/ui/powerscreen.h create mode 100644 src/ui/setdatescreen.c create mode 100644 src/ui/setdatescreen.h create mode 100644 src/ui/viewdatescreen.c create mode 100644 src/ui/viewdatescreen.h diff --git a/Makefile b/Makefile index 263ef01..44a5cbb 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,8 @@ LDFLAGS=-lgpio -lfigpar -lpthread ${SQLITE3_LDFLAGS} SRCS=src/main.c src/util.c src/lcd.c src/ths.c src/button.c src/ui/screen.c\ src/ui/datesel.c src/ui/statsby.c src/ui/datapoints.c src/ui/timesel.c\ src/ui/statrange.c src/config.c src/ui/blankscreen.c src/drive.c\ - src/ui/exportscreen.c + src/ui/exportscreen.c src/ui/setdatescreen.c src/ui/viewdatescreen.c\ + src/ui/powerscreen.c PROG=rpi4b-temp-humidity all: config.conf bin/${PROG} @@ -22,20 +23,24 @@ bin/main.o bin/config.o: config.mk bin/main.o bin/util.o bin/lcd.o bin/ths.o bin/button.o: src/util.h bin/ui/screen.o bin/ui/statsby.o bin/ui/datesel.o: src/util.h bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/util.h +bin/ui/viewdatescreen.o bin/ui/setdatescreen.o bin/ui/powerscreen.o: src/util.h bin/main.o bin/lcd.o bin/screen.o bin/ui/datesel.o: src/lcd.h bin/ui/statsby.o bin/ui/timesel.o bin/ui/statrange.o: src/lcd.h -bin/ui/datapoints.o: src/lcd.h +bin/ui/datapoints.o bin/ui/viewdatescreen.o: src/lcd.h +bin/ui/setdatescreen.o bin/ui/powerscreen.o: src/lcd.h bin/main.o bin/ui/screen.o bin/ui/statsby.o: src/ui/screen.h bin/ui/datesel.o bin/ui/datapoints.o bin/ui/timesel.o: src/ui/screen.h -bin/ui/statrange.o: src/ui/screen.h +bin/ui/statrange.o bin/ui/setdatescreen.o: src/ui/screen.h +bin/ui/viewdatescreen.o bin/ui/powerscreen.o: src/ui/screen.h bin/main.o bin/ui/datesel.o bin/ui/statsby.o: src/ui/datesel.h bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/ui/datesel.h +bin/ui/setdatescreen.o: src/ui/datesel.h bin/main.o bin/ui/timesel.o bin/ui/datapoints.o: src/ui/timesel.h -bin/ui/statrange.o: src/ui/timesel.h +bin/ui/statrange.o bin/ui/setdatescreen.o: src/ui/timesel.h bin/main.o bin/ths.o: src/ths.h bin/main.o bin/config.o: src/config.h @@ -44,7 +49,10 @@ bin/main.o bin/ui/statsby.o: src/ui/statsby.h bin/main.o bin/ui/datapoints.o: src/ui/datapoints.h bin/main.o bin/ui/blankscreen.o: src/ui/blankscreen.h bin/main.o bin/ui/statrange.o: src/ui/statrange.h -vin/main.o bin/ui/exportscreen.o: src/ui/exportscreen.h +bin/main.o bin/ui/exportscreen.o: src/ui/exportscreen.h +bin/main.o bin/ui/setdatescreen.o: src/ui/setdatescreen.h +bin/main.o bin/ui/viewdatescreen.o: src/ui/viewdatescreen.h +bin/main.o bin/ui/powerscreen.o: src/ui/powerscreen.h bin/main.o bin/drive.o bin/ui/exportscreen.o: src/drive.h .if "${DEFAULT_TEMP_UNIT}" != "F" && "${DEFAULT_TEMP_UNIT}" != "C" &&\ diff --git a/src/main.c b/src/main.c index 54a8063..74fd4dd 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,9 @@ #include "ui/statrange.h" #include "ui/blankscreen.h" #include "ui/exportscreen.h" +#include "ui/viewdatescreen.h" +#include "ui/setdatescreen.h" +#include "ui/powerscreen.h" #include #include @@ -118,6 +121,9 @@ int main(int argc, char *const *argv) { if (GLOBAL_OPTS.bl_pin >= 0) { screen_manager_add(screen_manager, blank_screen_new()); } + screen_manager_add(screen_manager, (Screen *) view_date_screen_new()); + screen_manager_add(screen_manager, (Screen *) set_date_screen_new()); + screen_manager_add(screen_manager, (Screen *) power_screen_new()); while (RUNNING) { lock_stat_globals(); uint32_t temp = LAST_TEMP; @@ -127,6 +133,8 @@ int main(int argc, char *const *argv) { // 10ms is probably faster than anyone will press a button usleep(10 * 1000); } + // this is mostly about LCD and DB cleanup, not freeing memory, this leaks a + // lot when caused from something other than the main screen screen_manager_delete(screen_manager); lcd_close(lcd); if (pthread_join(bg_update, NULL) != 0) { diff --git a/src/ui/datesel.c b/src/ui/datesel.c index 8d02e6d..4ae0073 100644 --- a/src/ui/datesel.c +++ b/src/ui/datesel.c @@ -13,8 +13,7 @@ #include void date_sel_init(DateSelection *ds, UtilPeriod limit_period) { - memset(&ds->start_time, 0, sizeof(UtilDate)); // min date - memset(&ds->end_time, 0xff, sizeof(UtilDate)); // max date + ds->clamp_time = true; date_sel_set_period(ds, limit_period); date_sel_reset(ds); } @@ -42,36 +41,38 @@ void date_sel_set_period(DateSelection *ds, UtilPeriod limit_period) { } static void date_sel_clamp_time(DateSelection *ds) { - if (ds->year > ds->end_time.local_year) { - ds->year = ds->end_time.local_year; - } - if (ds->year == ds->end_time.local_year && - ds->month > ds->end_time.local_month) { - ds->month = ds->end_time.local_month; - if (ds->stage == DATE_SEL_DAY) { - // last days of previous month - ds->day = days_in_month(ds->month, ds->year); + if (ds->clamp_time) { + if (ds->year > ds->end_time.local_year) { + ds->year = ds->end_time.local_year; } - } - if (ds->year == ds->end_time.local_year && - ds->month == ds->end_time.local_month && - ds->day > ds->end_time.local_day) { - ds->day = ds->end_time.local_day; - } - if (ds->year < ds->start_time.local_year) { - ds->year = ds->start_time.local_year; - } - if (ds->year == ds->start_time.local_year && - ds->month < ds->start_time.local_month) { - ds->month = ds->start_time.local_month; - if (ds->stage == DATE_SEL_DAY) { - ds->day = 1; // first day of the next month + if (ds->year == ds->end_time.local_year && + ds->month > ds->end_time.local_month) { + ds->month = ds->end_time.local_month; + if (ds->stage == DATE_SEL_DAY) { + // last days of previous month + ds->day = days_in_month(ds->month, ds->year); + } + } + if (ds->year == ds->end_time.local_year && + ds->month == ds->end_time.local_month && + ds->day > ds->end_time.local_day) { + ds->day = ds->end_time.local_day; + } + if (ds->year < ds->start_time.local_year) { + ds->year = ds->start_time.local_year; + } + if (ds->year == ds->start_time.local_year && + ds->month < ds->start_time.local_month) { + ds->month = ds->start_time.local_month; + if (ds->stage == DATE_SEL_DAY) { + ds->day = 1; // first day of the next month + } + } + if (ds->year == ds->start_time.local_year && + ds->month == ds->start_time.local_month && + ds->day < ds->start_time.local_day) { + ds->day = ds->start_time.local_day; } - } - if (ds->year == ds->start_time.local_year && - ds->month == ds->start_time.local_month && - ds->day < ds->start_time.local_day) { - ds->day = ds->start_time.local_day; } } @@ -96,7 +97,6 @@ static void date_sel_cleanup(DateSelection *ds) { ++ds->month; date_sel_cleanup(ds); } - date_sel_clamp_time(ds); } static void date_sel_add_units(DateSelection *ds, int n) { @@ -117,15 +117,17 @@ static void date_sel_add_units(DateSelection *ds, int n) { DateSelectionState date_sel_dispatch(DateSelection *ds, SensorState *state, const char *label) { - UtilDate start, end; - if (!get_database_limits(state->db, ds->limit_period, &start, &end)) { - warnx("failed to query database limits"); - lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, - LCD_DISPLAY_ON); - return DATE_SEL_ERROR; + if (ds->clamp_time) { + UtilDate start, end; + if (!get_database_limits(state->db, ds->limit_period, &start, &end)) { + warnx("failed to query database limits"); + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_ON); + return DATE_SEL_ERROR; + } + ds->start_time = start; + ds->end_time = end; } - ds->start_time = start; - ds->end_time = end; lcd_clear(state->lcd); lcd_move_to(state->lcd, 0, 0); lcd_write_string(state->lcd, label); @@ -199,3 +201,7 @@ DateSelectionState date_sel_dispatch(DateSelection *ds, lcd_move_to(state->lcd, 1, cursor_pos); return DATE_SEL_CONTINUE; } + +void date_sel_set_clamp_time(DateSelection *ds, bool clamp_time) { + ds->clamp_time = clamp_time; +} diff --git a/src/ui/datesel.h b/src/ui/datesel.h index ef90269..427855a 100644 --- a/src/ui/datesel.h +++ b/src/ui/datesel.h @@ -28,6 +28,7 @@ typedef struct { UtilPeriod limit_period; UtilDate start_time; UtilDate end_time; + bool clamp_time; } DateSelection; typedef enum { @@ -61,4 +62,9 @@ DateSelectionState date_sel_dispatch(DateSelection *ds, SensorState *state, const char *label); +/* + * Set weather or not DS clamps time according to CLAMP_TIME. + */ +void date_sel_set_clamp_time(DateSelection *ds, bool clamp_time); + #endif diff --git a/src/ui/powerscreen.c b/src/ui/powerscreen.c new file mode 100644 index 0000000..deb2fd7 --- /dev/null +++ b/src/ui/powerscreen.c @@ -0,0 +1,75 @@ +/* + * powerscreen.h - Screen for rebooting or halting + * Copyright (C) 2024 Alexander Rosenberg + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See the LICENSE file for more information. + */ +#include "powerscreen.h" + +#include +#include +#include + +static bool power_screen_dispatch(PowerScreen *screen, + SensorState *state) { + if (state->back_down) { + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_ON); + screen->choice = POWER_SCREEN_REBOOT; + return true; + } + if (state->sel_down) { + switch (screen->choice) { + case POWER_SCREEN_REBOOT: + LOG_VERBOSE("Rebooting..."); + reboot(RB_AUTOBOOT); + abort(); + // how did we even get here??? + break; + case POWER_SCREEN_POWEROFF: + LOG_VERBOSE("Powering off..."); + reboot(RB_POWEROFF); + abort(); + // how did we even get here??? + break; + default: + warnx("invalid power screen choice"); + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_ON); + return true; + } + } + screen->choice = abs((screen->choice + state->up_down - state->down_down) % + POWER_SCREEN_NCHOICE); + if (state->force_draw || state->up_down || state->down_down) { + lcd_clear(state->lcd); + lcd_move_to(state->lcd, 0, 0); + lcd_write_string(state->lcd, "Type:"); + lcd_move_to(state->lcd, 1, 0); + lcd_write_char(state->lcd, '>'); + switch (screen->choice) { + case POWER_SCREEN_REBOOT: + lcd_write_string(state->lcd, "REBOOT"); + break; + case POWER_SCREEN_POWEROFF: + lcd_write_string(state->lcd, "POWEROFF"); + break; + } + lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON, + LCD_DISPLAY_ON); + lcd_move_to(state->lcd, 1, 0); + } + return false; +} + +PowerScreen *power_screen_new(void) { + PowerScreen *s = malloc_checked(sizeof(PowerScreen)); + screen_init(&s->parent, "Power options", + (ScreenDispatchFunc) power_screen_dispatch, + (ScreenCleanupFunc) free); + s->choice = POWER_SCREEN_REBOOT; + return s; +} diff --git a/src/ui/powerscreen.h b/src/ui/powerscreen.h new file mode 100644 index 0000000..4bfcc72 --- /dev/null +++ b/src/ui/powerscreen.h @@ -0,0 +1,31 @@ +/* + * powerscreen.h - Screen for rebooting or halting + * Copyright (C) 2024 Alexander Rosenberg + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See the LICENSE file for more information. + */ +#ifndef INCLUDED_POWERSCREEN_H +#define INCLUDED_POWERSCREEN_H + +#include "screen.h" + +typedef struct { + Screen parent; + enum { + POWER_SCREEN_REBOOT = 0, + POWER_SCREEN_POWEROFF, + POWER_SCREEN_NCHOICE, + }; + int choice; +} PowerScreen; + +/* + * Create a new power screen that can be used to reboot or halt the device. + */ +PowerScreen *power_screen_new(void); + +#endif + diff --git a/src/ui/setdatescreen.c b/src/ui/setdatescreen.c new file mode 100644 index 0000000..df5d7b6 --- /dev/null +++ b/src/ui/setdatescreen.c @@ -0,0 +1,157 @@ +/* + * setdatescreen.c - Screen for setting the date and time + * Copyright (C) 2024 Alexander Rosenberg + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See the LICENSE file for more information. + */ +#include "setdatescreen.h" + +#include +#include +#include + +static void set_date_reset(SetDateScreen *screen, + SensorState *state) { + date_sel_reset(&screen->ds); + time_sel_reset(&screen->ts); + screen->need_redraw = true; + screen->stage = SET_DATE_SELECT_DATE; + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_ON); +} + +static bool set_date_screen_select_date(SetDateScreen *screen, + SensorState *state) { + if (state->sel_down || state->back_down || state->up_down || + state->down_down) { + screen->need_redraw = true; + } + if (screen->need_redraw) { + screen->need_redraw = false; + DateSelectionState dss = date_sel_dispatch(&screen->ds, state, "New Date:"); + switch (dss) { + case DATE_SEL_CONTINUE: + return false; // ignore + case DATE_SEL_BACK: + return true; + case DATE_SEL_DONE: + screen->stage = SET_DATE_SELECT_TIME; + break; + case DATE_SEL_ERROR: + screen->stage = SET_DATE_FAIL; + break; + } + screen->need_redraw = true; + } + return false; +} + +static bool set_date_and_time(DateSelection *ds, TimeSelection *ts) { + int64_t utc_time = timedate_sel_to_uts_timestamp(ds, ts); + struct timeval new_time = { + .tv_sec = utc_time, + .tv_usec = 0, // can't set, user not fast enough (lol) + }; + if (settimeofday(&new_time, NULL) < 0) { + warn("settimeofday(2) failed"); + return false; + } + LOG_VERBOSE("Set system date and time to %04d/%02d/%02d %02d:%02d:%02d\n", + ds->year, ds->month, ds->day, ts->hour, ts->minute, ts->second); + return true; +} + +static void set_date_screen_select_time(SetDateScreen *screen, + SensorState *state) { + if (state->sel_down || state->back_down || state->up_down || + state->down_down) { + screen->need_redraw = true; + } + if (screen->need_redraw) { + screen->need_redraw = false; + TimeSelState tss = time_sel_dispatch(&screen->ts, state, "New Time"); + switch (tss) { + case TIME_SEL_CONTINUE: + return; // ignore + case TIME_SEL_BACK: + --screen->stage; + break; + case TIME_SEL_DONE: + if (set_date_and_time(&screen->ds, &screen->ts)) { + screen->stage = SET_DATE_SUCCESS; + } else { + screen->stage = SET_DATE_FAIL; + } + break; + case TIME_SEL_ERROR: + screen->stage = SET_DATE_FAIL; + break; + } + screen->need_redraw = true; + } +} + +static bool set_date_screen_message(SetDateScreen *screen, + SensorState *state, + const char *msg) { + if (state->up_down || state->down_down || state->sel_down || + state->back_down) { + return true; + } + if (screen->need_redraw) { + screen->need_redraw = false; + lcd_clear(state->lcd); + lcd_move_to(state->lcd, 0, 0); + lcd_write_string(state->lcd, msg); + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_ON); + } + return false; +} + +static bool set_date_screen_dispatch(SetDateScreen *screen, + SensorState *state) { + if (state->force_draw) { + screen->need_redraw = true; + } + switch (screen->stage) { + case SET_DATE_SELECT_DATE: + if (set_date_screen_select_date(screen, state)) { + set_date_reset(screen, state); + return true; + } + break; + case SET_DATE_SELECT_TIME: + set_date_screen_select_time(screen, state); + break; + case SET_DATE_FAIL: + if (set_date_screen_message(screen, state, "Fail!")) { + set_date_reset(screen, state); + return true; + } + break; + case SET_DATE_SUCCESS: + if (set_date_screen_message(screen, state, "Success!")) { + set_date_reset(screen, state); + return true; + } + break; + } + return false; +} + +SetDateScreen *set_date_screen_new() { + SetDateScreen *s = malloc_checked(sizeof(SetDateScreen)); + screen_init(&s->parent, "Set date/time", + (ScreenDispatchFunc) set_date_screen_dispatch, + (ScreenCleanupFunc) free); + date_sel_init(&s->ds, PERIOD_DAY); + date_sel_set_clamp_time(&s->ds, false); + time_sel_init(&s->ts, NULL, true); + s->need_redraw = true; + s->stage = SET_DATE_SELECT_DATE; + return s; +} diff --git a/src/ui/setdatescreen.h b/src/ui/setdatescreen.h new file mode 100644 index 0000000..3fa70d1 --- /dev/null +++ b/src/ui/setdatescreen.h @@ -0,0 +1,35 @@ +/* + * setdatescreen.h - Screen for setting the date and time + * Copyright (C) 2024 Alexander Rosenberg + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See the LICENSE file for more information. + */ +#ifndef INCLUDED_SETDATESCREEN_H +#define INCLUDED_SETDATESCREEN_H + +#include "screen.h" +#include "timesel.h" + +typedef struct { + Screen parent; + enum { + SET_DATE_SELECT_DATE, + SET_DATE_SELECT_TIME, + SET_DATE_FAIL, + SET_DATE_SUCCESS + } stage; + bool need_redraw; + DateSelection ds; + TimeSelection ts; +} SetDateScreen; + +/* + * Create a new SetDateScreen. This screen will allow the user to configure the + * date and time. + */ +SetDateScreen *set_date_screen_new(void); + +#endif diff --git a/src/ui/statrange.c b/src/ui/statrange.c index ea2abc8..d80c793 100644 --- a/src/ui/statrange.c +++ b/src/ui/statrange.c @@ -65,24 +65,6 @@ static void stat_range_handle_time(StatRangeScreen *screen, TimeSelection *ts, } } -static uint64_t to_uts_timestamp(DateSelection *ds, TimeSelection *ts) { - struct tm broken_time = { - .tm_sec = ts->second, - .tm_min = ts->minute, - .tm_hour = ts->hour, - .tm_mday = ds->day, - .tm_mon = ds->month - 1, - .tm_year = ds->year - 1900, - .tm_isdst = -1, - }; - time_t it = mktime(&broken_time); - if (it < 0) { - warnx("failed to convert to UTC timestamp"); - return 0; - } - return it; -} - static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { if (state->back_down) { screen->need_redraw = true; @@ -99,10 +81,10 @@ static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { } if (screen->need_redraw) { screen->need_redraw = false; - uint64_t start = to_uts_timestamp(&screen->sds, &screen->sts); - uint64_t end = to_uts_timestamp(&screen->eds, &screen->ets); + int64_t start = timedate_sel_to_uts_timestamp(&screen->sds, &screen->sts); + int64_t end = timedate_sel_to_uts_timestamp(&screen->eds, &screen->ets); if (start > end) { - uint64_t t = end; + int64_t t = end; end = start; start = t; } diff --git a/src/ui/timesel.c b/src/ui/timesel.c index 3804b45..253a0a8 100644 --- a/src/ui/timesel.c +++ b/src/ui/timesel.c @@ -173,3 +173,21 @@ void time_sel_reset(TimeSelection *ts) { ts->minute = 0; ts->stage = TS_STAGE_HOUR; } + +int64_t timedate_sel_to_uts_timestamp(DateSelection *ds, TimeSelection *ts) { + struct tm broken_time = { + .tm_sec = ts->second, + .tm_min = ts->minute, + .tm_hour = ts->hour, + .tm_mday = ds->day, + .tm_mon = ds->month - 1, + .tm_year = ds->year - 1900, + .tm_isdst = -1, + }; + time_t it = mktime(&broken_time); + if (it < 0) { + warnx("failed to convert to UTC timestamp"); + return 0; + } + return it; +} diff --git a/src/ui/timesel.h b/src/ui/timesel.h index 1a18577..7d8dfb6 100644 --- a/src/ui/timesel.h +++ b/src/ui/timesel.h @@ -52,4 +52,9 @@ TimeSelState time_sel_dispatch(TimeSelection *ts, SensorState *state, */ void time_sel_reset(TimeSelection *ts); +/* + * Convert DS and TS to a UTC timestamp; + */ +int64_t timedate_sel_to_uts_timestamp(DateSelection *ds, TimeSelection *ts); + #endif diff --git a/src/ui/viewdatescreen.c b/src/ui/viewdatescreen.c new file mode 100644 index 0000000..e19f2ae --- /dev/null +++ b/src/ui/viewdatescreen.c @@ -0,0 +1,45 @@ +/* + * viewdatescreen.c - Screen for viewing the date and time + * Copyright (C) 2024 Alexander Rosenberg + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See the LICENSE file for more information. + */ +#include "viewdatescreen.h" + +#include + +static bool view_date_screen_dispatch(ViewDateScreen *screen, + SensorState *state) { + time_t now = time(NULL); + if (state->back_down || state->sel_down || state->up_down || + state->down_down) { + return true; + } else if (state->force_draw || now != screen->last_time) { + screen->last_time = now; + lcd_clear(state->lcd); + struct tm broken_time; + localtime_r(&now, &broken_time); + char buf[17]; + snprintf(buf, 17, "%04d/%02d/%02d", broken_time.tm_year + 1900, + broken_time.tm_mon + 1, broken_time.tm_mday); + lcd_move_to(state->lcd, 0, 0); + lcd_write_string(state->lcd, buf); + snprintf(buf, 17, "%02d:%02d:%02d", broken_time.tm_hour, + broken_time.tm_min, broken_time.tm_sec); + lcd_move_to(state->lcd, 1, 0); + lcd_write_string(state->lcd, buf); + } + return false; +} + +ViewDateScreen *view_date_screen_new() { + ViewDateScreen *s = malloc_checked(sizeof(ViewDateScreen)); + screen_init(&s->parent, "View date/time", + (ScreenDispatchFunc) view_date_screen_dispatch, + (ScreenCleanupFunc) free); + s->last_time = INT64_MIN; + return s; +} diff --git a/src/ui/viewdatescreen.h b/src/ui/viewdatescreen.h new file mode 100644 index 0000000..9c0b22a --- /dev/null +++ b/src/ui/viewdatescreen.h @@ -0,0 +1,25 @@ +/* + * viewdatescreen.h - Screen for viewing the date and time + * Copyright (C) 2024 Alexander Rosenberg + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See the LICENSE file for more information. + */ +#ifndef INCLUDED_VIEWDATESCREEN_H +#define INCLUDED_VIEWDATESCREEN_H + +#include "screen.h" + +typedef struct { + Screen parent; + int64_t last_time; +} ViewDateScreen; + +/* + * Create a new view date screen that shows the user the current date and time. + */ +ViewDateScreen *view_date_screen_new(void); + +#endif diff --git a/src/util.c b/src/util.c index 7533780..60bf00e 100644 --- a/src/util.c +++ b/src/util.c @@ -357,7 +357,7 @@ bool get_data_point_info(sqlite3 *db, int64_t time, UtilDataPointInfo *info) { } } -bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, +bool get_average_for_range(sqlite3 *db, int64_t start, int64_t end, UtilAverageRange *data) { bool success = true; sqlite3_bind_int64(AVG_FOR_RANGE_QUERY, 1, start); diff --git a/src/util.h b/src/util.h index 30c19bc..dfcbbff 100644 --- a/src/util.h +++ b/src/util.h @@ -203,7 +203,7 @@ typedef struct { * Get the average DATA for the range of UTC times START to END (inclusive). * Return: false if an error occurred, true otherwise. */ -bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, +bool get_average_for_range(sqlite3 *db, int64_t start, int64_t end, UtilAverageRange *data); /*