Add cleaer screen, timezone, and some other additions
This commit is contained in:
parent
11b40d6b1b
commit
0bac96e23e
8
Makefile
8
Makefile
@ -9,7 +9,7 @@ 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/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/statrange.c src/config.c src/ui/blankscreen.c src/drive.c\
|
||||||
src/ui/exportscreen.c src/ui/setdatescreen.c src/ui/viewdatescreen.c\
|
src/ui/exportscreen.c src/ui/setdatescreen.c src/ui/viewdatescreen.c\
|
||||||
src/ui/powerscreen.c
|
src/ui/powerscreen.c src/ui/settzscreen.c src/ui/cleardatascreen.c
|
||||||
PROG=rpi4b-temp-humidity
|
PROG=rpi4b-temp-humidity
|
||||||
|
|
||||||
all: config.conf bin/${PROG}
|
all: config.conf bin/${PROG}
|
||||||
@ -24,16 +24,19 @@ 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/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/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/ui/viewdatescreen.o bin/ui/setdatescreen.o bin/ui/powerscreen.o: src/util.h
|
||||||
|
bin/ui/settzscreen.o bin/ui/cleardatascreen.o: src/util.h
|
||||||
|
|
||||||
bin/main.o bin/lcd.o bin/screen.o bin/ui/datesel.o: src/lcd.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/statsby.o bin/ui/timesel.o bin/ui/statrange.o: src/lcd.h
|
||||||
bin/ui/datapoints.o bin/ui/viewdatescreen.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/ui/setdatescreen.o bin/ui/powerscreen.o: src/lcd.h
|
||||||
|
bin/ui/settzscreen.o bin/ui/cleardatascreen.o: src/lcd.h
|
||||||
|
|
||||||
bin/main.o bin/ui/screen.o bin/ui/statsby.o: src/ui/screen.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/datesel.o bin/ui/datapoints.o bin/ui/timesel.o: src/ui/screen.h
|
||||||
bin/ui/statrange.o bin/ui/setdatescreen.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/ui/viewdatescreen.o bin/ui/powerscreen.o: src/ui/screen.h
|
||||||
|
bin/ui/settzscreen.o bin/ui/cleardatascreen.o: src/ui/screen.h
|
||||||
|
|
||||||
bin/main.o bin/ui/datesel.o bin/ui/statsby.o: src/ui/datesel.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/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/ui/datesel.h
|
||||||
@ -53,6 +56,8 @@ 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/setdatescreen.o: src/ui/setdatescreen.h
|
||||||
bin/main.o bin/ui/viewdatescreen.o: src/ui/viewdatescreen.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/ui/powerscreen.o: src/ui/powerscreen.h
|
||||||
|
bin/main.o bin/ui/settzscreen.o: src/ui/settzscreen.h
|
||||||
|
bin/main.o bin/ui/cleardatascreen.o: src/ui/cleardatascreen.h
|
||||||
bin/main.o bin/drive.o bin/ui/exportscreen.o: src/drive.h
|
bin/main.o bin/drive.o bin/ui/exportscreen.o: src/drive.h
|
||||||
|
|
||||||
.if "${DEFAULT_TEMP_UNIT}" != "F" && "${DEFAULT_TEMP_UNIT}" != "C" &&\
|
.if "${DEFAULT_TEMP_UNIT}" != "F" && "${DEFAULT_TEMP_UNIT}" != "C" &&\
|
||||||
@ -71,6 +76,7 @@ DEFINES:=\
|
|||||||
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
||||||
-DDEFAULT_TEMP_UNIT="${DEFAULT_TEMP_UNIT}"\
|
-DDEFAULT_TEMP_UNIT="${DEFAULT_TEMP_UNIT}"\
|
||||||
-DDEFAULT_EXPORT_FILE_NAME="${DEFAULT_EXPORT_FILE_NAME}"\
|
-DDEFAULT_EXPORT_FILE_NAME="${DEFAULT_EXPORT_FILE_NAME}"\
|
||||||
|
-DDEFAULT_TIMEZONE="${DEFAULT_TIMEZONE}"\
|
||||||
|
|
||||||
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||||
@mkdir -p ${.TARGET:H}
|
@mkdir -p ${.TARGET:H}
|
||||||
|
@ -47,3 +47,8 @@ down_pin = 23
|
|||||||
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||||
# .csv will be appended for CSV exports
|
# .csv will be appended for CSV exports
|
||||||
#export_file_name = DEFAULT_EXPORT_FILE_NAME
|
#export_file_name = DEFAULT_EXPORT_FILE_NAME
|
||||||
|
|
||||||
|
# The timezone used. This is overridden by the environment variable TZ. See the
|
||||||
|
# tzset(3) manual page for more information about the format of this. Leave
|
||||||
|
# blank for system default
|
||||||
|
#timezone = DEFAULT_TIMEZONE
|
||||||
|
@ -34,3 +34,8 @@ DEFAULT_TEMP_UNIT=F
|
|||||||
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||||
# .csv will be appended for CSV exports
|
# .csv will be appended for CSV exports
|
||||||
DEFAULT_EXPORT_FILE_NAME=env_data
|
DEFAULT_EXPORT_FILE_NAME=env_data
|
||||||
|
|
||||||
|
# The timezone used. This is overridden by the environment variable TZ. See the
|
||||||
|
# tzset(3) manual page for more information about the format of this. Leave
|
||||||
|
# blank for system default
|
||||||
|
DEFAULT_TIMEZONE=
|
||||||
|
10
src/config.c
10
src/config.c
@ -205,6 +205,10 @@ static void set_options_from_entries(struct figpar_config *entries,
|
|||||||
GLOBAL_OPTS.export_file_name = steal_opt_if_set(entries[17].value.str);
|
GLOBAL_OPTS.export_file_name = steal_opt_if_set(entries[17].value.str);
|
||||||
LOG_VERBOSE("Using export_file_name: \"%s\"\n",
|
LOG_VERBOSE("Using export_file_name: \"%s\"\n",
|
||||||
GLOBAL_OPTS.export_file_name);
|
GLOBAL_OPTS.export_file_name);
|
||||||
|
|
||||||
|
entries[18].type = FIGPAR_TYPE_NONE;
|
||||||
|
GLOBAL_OPTS.timezone = steal_opt_if_set(entries[18].value.str);
|
||||||
|
LOG_VERBOSE("Using timezone: \"%s\"\n", GLOBAL_OPTS.timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *strdup_default_opt(const char *def) {
|
static char *strdup_default_opt(const char *def) {
|
||||||
@ -317,6 +321,12 @@ void parse_config_file(const char *path) {
|
|||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_EXPORT_FILE_NAME))},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_EXPORT_FILE_NAME))},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.directive = "timezone",
|
||||||
|
.type = FIGPAR_TYPE_STR,
|
||||||
|
.action = parse_str_callback,
|
||||||
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_TIMEZONE))},
|
||||||
|
},
|
||||||
{ .directive = NULL },
|
{ .directive = NULL },
|
||||||
};
|
};
|
||||||
size_t entry_count = sizeof(entries) / sizeof(struct figpar_config);
|
size_t entry_count = sizeof(entries) / sizeof(struct figpar_config);
|
||||||
|
36
src/main.c
36
src/main.c
@ -22,6 +22,8 @@
|
|||||||
#include "ui/viewdatescreen.h"
|
#include "ui/viewdatescreen.h"
|
||||||
#include "ui/setdatescreen.h"
|
#include "ui/setdatescreen.h"
|
||||||
#include "ui/powerscreen.h"
|
#include "ui/powerscreen.h"
|
||||||
|
#include "ui/settzscreen.h"
|
||||||
|
#include "ui/cleardatascreen.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
@ -84,9 +86,22 @@ void lock_stat_globals(void);
|
|||||||
*/
|
*/
|
||||||
#define unlock_stat_globals() pthread_mutex_unlock(&STAT_MUTEX)
|
#define unlock_stat_globals() pthread_mutex_unlock(&STAT_MUTEX)
|
||||||
|
|
||||||
|
// should we restart instead of exiting
|
||||||
|
bool SHOULD_RESTART = false;
|
||||||
|
bool NEED_CLEAR_TZ = false;
|
||||||
|
|
||||||
int main(int argc, char *const *argv) {
|
int main(int argc, char *const *argv) {
|
||||||
parse_arguments(argc, argv);
|
parse_arguments(argc, argv);
|
||||||
parse_config_file(GLOBAL_OPTS.config_path);
|
parse_config_file(GLOBAL_OPTS.config_path);
|
||||||
|
if (GLOBAL_OPTS.timezone) {
|
||||||
|
if (!getenv("TZ")) {
|
||||||
|
setenv("TZ", GLOBAL_OPTS.timezone, true);
|
||||||
|
NEED_CLEAR_TZ = true;
|
||||||
|
} else {
|
||||||
|
LOG_VERBOSE("Config timezone option shadowed by local environment variable\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tzset();
|
||||||
|
|
||||||
gpio_handle_t handle = gpio_open_device(GLOBAL_OPTS.gpio_path);
|
gpio_handle_t handle = gpio_open_device(GLOBAL_OPTS.gpio_path);
|
||||||
if (handle == GPIO_INVALID_HANDLE) {
|
if (handle == GPIO_INVALID_HANDLE) {
|
||||||
@ -121,9 +136,11 @@ int main(int argc, char *const *argv) {
|
|||||||
if (GLOBAL_OPTS.bl_pin >= 0) {
|
if (GLOBAL_OPTS.bl_pin >= 0) {
|
||||||
screen_manager_add(screen_manager, blank_screen_new());
|
screen_manager_add(screen_manager, blank_screen_new());
|
||||||
}
|
}
|
||||||
|
screen_manager_add(screen_manager, (Screen *) power_screen_new());
|
||||||
screen_manager_add(screen_manager, (Screen *) view_date_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 *) set_date_screen_new());
|
||||||
screen_manager_add(screen_manager, (Screen *) power_screen_new());
|
screen_manager_add(screen_manager, (Screen *) set_tz_screen_new());
|
||||||
|
screen_manager_add(screen_manager, (Screen *) clear_data_screen_new());
|
||||||
while (RUNNING) {
|
while (RUNNING) {
|
||||||
lock_stat_globals();
|
lock_stat_globals();
|
||||||
uint32_t temp = LAST_TEMP;
|
uint32_t temp = LAST_TEMP;
|
||||||
@ -133,8 +150,6 @@ int main(int argc, char *const *argv) {
|
|||||||
// 10ms is probably faster than anyone will press a button
|
// 10ms is probably faster than anyone will press a button
|
||||||
usleep(10 * 1000);
|
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);
|
screen_manager_delete(screen_manager);
|
||||||
lcd_close(lcd);
|
lcd_close(lcd);
|
||||||
if (pthread_join(bg_update, NULL) != 0) {
|
if (pthread_join(bg_update, NULL) != 0) {
|
||||||
@ -147,6 +162,14 @@ int main(int argc, char *const *argv) {
|
|||||||
pthread_mutex_destroy(&STAT_MUTEX);
|
pthread_mutex_destroy(&STAT_MUTEX);
|
||||||
gpio_close(handle);
|
gpio_close(handle);
|
||||||
cleanup_options(&GLOBAL_OPTS);
|
cleanup_options(&GLOBAL_OPTS);
|
||||||
|
if (SHOULD_RESTART) {
|
||||||
|
LOG_VERBOSE("Re-invoking...\n");
|
||||||
|
if (NEED_CLEAR_TZ) {
|
||||||
|
unsetenv("TZ");
|
||||||
|
}
|
||||||
|
execv(argv[0], argv);
|
||||||
|
err(1, "re-invoke with execv(2) failed");
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,8 +286,13 @@ void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
|||||||
sqlite3_bind_int(insert_statement, 3, humid);
|
sqlite3_bind_int(insert_statement, 3, humid);
|
||||||
int status = sqlite3_step(insert_statement);
|
int status = sqlite3_step(insert_statement);
|
||||||
if (status != SQLITE_DONE) {
|
if (status != SQLITE_DONE) {
|
||||||
errx(1, "failed to insert temp. and humid. data into database: %s",
|
warnx("failed to insert temp. and humid. data into database: %s",
|
||||||
sqlite3_errstr(status));
|
sqlite3_errstr(status));
|
||||||
|
lock_stat_globals();
|
||||||
|
// this means error
|
||||||
|
LAST_TEMP = UINT32_MAX;
|
||||||
|
LAST_HUMID = UINT32_MAX;
|
||||||
|
unlock_stat_globals();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
72
src/ui/cleardatascreen.c
Normal file
72
src/ui/cleardatascreen.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* cleardatascreen.c - 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 "cleardatascreen.h"
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
static bool clear_data_screen_display(ClearDataScreen *screen,
|
||||||
|
SensorState *state) {
|
||||||
|
if (state->back_down || (!screen->choice && state->sel_down) ||
|
||||||
|
(screen->done && (state->sel_down || state->up_down ||
|
||||||
|
state->down_down))) {
|
||||||
|
screen->done = false;
|
||||||
|
screen->error = false;
|
||||||
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||||
|
LCD_DISPLAY_ON);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (state->sel_down) {
|
||||||
|
// TODO clear data
|
||||||
|
char *errmsg;
|
||||||
|
sqlite3_exec(state->db, "DELETE FROM env_data;", NULL, NULL, &errmsg);
|
||||||
|
if (errmsg) {
|
||||||
|
warnx("sqlite3 error when clearing database: \"%s\"\n", errmsg);
|
||||||
|
sqlite3_free(errmsg);
|
||||||
|
screen->error = true;
|
||||||
|
} else {
|
||||||
|
LOG_VERBOSE("Database cleared!\n");
|
||||||
|
}
|
||||||
|
screen->need_redraw = true;
|
||||||
|
screen->done = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (screen->need_redraw || state->force_draw || state->up_down ||
|
||||||
|
state->down_down) {
|
||||||
|
screen->need_redraw = false;
|
||||||
|
screen->choice = abs((screen->choice + state->up_down -
|
||||||
|
state->down_down) % 2);
|
||||||
|
lcd_clear(state->lcd);
|
||||||
|
lcd_move_to(state->lcd, 0, 0);
|
||||||
|
if (screen->done) {
|
||||||
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||||
|
LCD_DISPLAY_ON);
|
||||||
|
lcd_write_string(state->lcd, screen->error ? "Error!" : "Done!");
|
||||||
|
} else {
|
||||||
|
lcd_write_string(state->lcd, "Really clear?");
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
lcd_write_string(state->lcd, ">No >Yes");
|
||||||
|
lcd_move_to(state->lcd, 1, 4 * screen->choice);
|
||||||
|
lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON,
|
||||||
|
LCD_DISPLAY_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearDataScreen *clear_data_screen_new() {
|
||||||
|
ClearDataScreen *s = malloc_checked(sizeof(ClearDataScreen));
|
||||||
|
screen_init(&s->parent, "Clear data",
|
||||||
|
(ScreenDispatchFunc) clear_data_screen_display,
|
||||||
|
(ScreenCleanupFunc) free);
|
||||||
|
s->choice = 0;
|
||||||
|
s->need_redraw = false;
|
||||||
|
s->done = false;
|
||||||
|
return s;
|
||||||
|
}
|
29
src/ui/cleardatascreen.h
Normal file
29
src/ui/cleardatascreen.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* cleardatascreen.c - 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_CLEARDATASCREEN_H
|
||||||
|
#define INCLUDED_CLEARDATASCREEN_H
|
||||||
|
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Screen parent;
|
||||||
|
int choice;
|
||||||
|
bool done;
|
||||||
|
bool need_redraw;
|
||||||
|
bool error;
|
||||||
|
} ClearDataScreen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new ClearDataScreen that will allow the user to wipe data collected
|
||||||
|
* by the device.
|
||||||
|
*/
|
||||||
|
ClearDataScreen *clear_data_screen_new(void);
|
||||||
|
|
||||||
|
#endif
|
@ -375,7 +375,9 @@ static bool export_screen_dispatch(ExportScreen *screen,
|
|||||||
if (confirm_status == EXPORT_CONFIRM_YES) {
|
if (confirm_status == EXPORT_CONFIRM_YES) {
|
||||||
kill(0, SIGUSR1);
|
kill(0, SIGUSR1);
|
||||||
pthread_cancel(screen->backgroud_oper);
|
pthread_cancel(screen->backgroud_oper);
|
||||||
|
pthread_detach(screen->backgroud_oper);
|
||||||
drive_unmount(cur_drive, true);
|
drive_unmount(cur_drive, true);
|
||||||
|
screen->bg_inited = false;
|
||||||
screen->stage = EXPORT_SCREEN_CANCELED;
|
screen->stage = EXPORT_SCREEN_CANCELED;
|
||||||
screen->need_redraw = true;
|
screen->need_redraw = true;
|
||||||
} else if (confirm_status == EXPORT_CONFIRM_NO) {
|
} else if (confirm_status == EXPORT_CONFIRM_NO) {
|
||||||
@ -395,11 +397,18 @@ static bool export_screen_dispatch(ExportScreen *screen,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void export_screen_cleanup(ExportScreen *screen) {
|
||||||
|
if (screen->bg_inited) {
|
||||||
|
pthread_cancel(screen->backgroud_oper);
|
||||||
|
pthread_detach(screen->backgroud_oper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExportScreen *export_screen_new() {
|
ExportScreen *export_screen_new() {
|
||||||
ExportScreen *s = malloc_checked(sizeof(ExportScreen));
|
ExportScreen *s = malloc_checked(sizeof(ExportScreen));
|
||||||
screen_init(&s->parent, "Export",
|
screen_init(&s->parent, "Export",
|
||||||
(ScreenDispatchFunc) export_screen_dispatch,
|
(ScreenDispatchFunc) export_screen_dispatch,
|
||||||
(ScreenCleanupFunc) free);
|
(ScreenCleanupFunc) export_screen_cleanup);
|
||||||
s->read_drives = true;
|
s->read_drives = true;
|
||||||
s->stage = EXPORT_SCREEN_FORMAT;
|
s->stage = EXPORT_SCREEN_FORMAT;
|
||||||
s->format = EXPORT_FORMAT_SQLITE;
|
s->format = EXPORT_FORMAT_SQLITE;
|
||||||
|
@ -18,11 +18,14 @@ static bool power_screen_dispatch(PowerScreen *screen,
|
|||||||
if (state->back_down) {
|
if (state->back_down) {
|
||||||
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||||
LCD_DISPLAY_ON);
|
LCD_DISPLAY_ON);
|
||||||
screen->choice = POWER_SCREEN_REBOOT;
|
screen->choice = POWER_SCREEN_RESTART_PROGRAM;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (state->sel_down) {
|
if (state->sel_down) {
|
||||||
switch (screen->choice) {
|
switch (screen->choice) {
|
||||||
|
case POWER_SCREEN_RESTART_PROGRAM:
|
||||||
|
request_restart();
|
||||||
|
break;
|
||||||
case POWER_SCREEN_REBOOT:
|
case POWER_SCREEN_REBOOT:
|
||||||
LOG_VERBOSE("Rebooting...");
|
LOG_VERBOSE("Rebooting...");
|
||||||
reboot(RB_AUTOBOOT);
|
reboot(RB_AUTOBOOT);
|
||||||
@ -51,6 +54,9 @@ static bool power_screen_dispatch(PowerScreen *screen,
|
|||||||
lcd_move_to(state->lcd, 1, 0);
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
lcd_write_char(state->lcd, '>');
|
lcd_write_char(state->lcd, '>');
|
||||||
switch (screen->choice) {
|
switch (screen->choice) {
|
||||||
|
case POWER_SCREEN_RESTART_PROGRAM:
|
||||||
|
lcd_write_string(state->lcd, "REINIT");
|
||||||
|
break;
|
||||||
case POWER_SCREEN_REBOOT:
|
case POWER_SCREEN_REBOOT:
|
||||||
lcd_write_string(state->lcd, "REBOOT");
|
lcd_write_string(state->lcd, "REBOOT");
|
||||||
break;
|
break;
|
||||||
@ -70,6 +76,6 @@ PowerScreen *power_screen_new(void) {
|
|||||||
screen_init(&s->parent, "Power options",
|
screen_init(&s->parent, "Power options",
|
||||||
(ScreenDispatchFunc) power_screen_dispatch,
|
(ScreenDispatchFunc) power_screen_dispatch,
|
||||||
(ScreenCleanupFunc) free);
|
(ScreenCleanupFunc) free);
|
||||||
s->choice = POWER_SCREEN_REBOOT;
|
s->choice = POWER_SCREEN_RESTART_PROGRAM;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
Screen parent;
|
Screen parent;
|
||||||
enum {
|
enum {
|
||||||
POWER_SCREEN_REBOOT = 0,
|
POWER_SCREEN_RESTART_PROGRAM = 0,
|
||||||
|
POWER_SCREEN_REBOOT,
|
||||||
POWER_SCREEN_POWEROFF,
|
POWER_SCREEN_POWEROFF,
|
||||||
POWER_SCREEN_NCHOICE,
|
POWER_SCREEN_NCHOICE,
|
||||||
};
|
};
|
||||||
|
@ -149,9 +149,14 @@ static bool stats_screen_dispatch(StatsScreen *screen, SensorState *state) {
|
|||||||
lcd_clear(state->lcd);
|
lcd_clear(state->lcd);
|
||||||
lcd_write_string(state->lcd, "temp humi time");
|
lcd_write_string(state->lcd, "temp humi time");
|
||||||
char buff[17];
|
char buff[17];
|
||||||
int cur_len = snprintf(buff, sizeof(buff), "%-4.1f%c %3" PRIu32 "%% ",
|
int cur_len;
|
||||||
|
if (state->temp == UINT32_MAX || state->humid == UINT32_MAX) {
|
||||||
|
cur_len = snprintf(buff, sizeof(buff), "ERROR! ");
|
||||||
|
} else {
|
||||||
|
cur_len = snprintf(buff, sizeof(buff), "%-4.1f%c %3" PRIu32 "%% ",
|
||||||
convert_temperature(state->temp),
|
convert_temperature(state->temp),
|
||||||
GLOBAL_OPTS.temp_unit, state->humid);
|
GLOBAL_OPTS.temp_unit, state->humid);
|
||||||
|
}
|
||||||
struct tm lt;
|
struct tm lt;
|
||||||
localtime_r(&cur_time, <);
|
localtime_r(&cur_time, <);
|
||||||
strftime(buff + cur_len, sizeof(buff) - cur_len,
|
strftime(buff + cur_len, sizeof(buff) - cur_len,
|
||||||
|
585
src/ui/settzscreen.c
Normal file
585
src/ui/settzscreen.c
Normal file
@ -0,0 +1,585 @@
|
|||||||
|
/*
|
||||||
|
* settzscreen.c - Screen for setting the timezone
|
||||||
|
* 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 "settzscreen.h"
|
||||||
|
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
static void set_tz_screen_reset(SetTZScreen *screen, SensorState *state) {
|
||||||
|
screen->has_dst = false;
|
||||||
|
screen->stage = SET_TZ_OFFSET;
|
||||||
|
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_TYPE;
|
||||||
|
tz_offset_reset(&screen->offset);
|
||||||
|
tz_offset_reset(&screen->dst_offset);
|
||||||
|
screen->did_set = false;
|
||||||
|
if (state) {
|
||||||
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||||
|
LCD_DISPLAY_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_tz_offset_normalize(TZOffset *offset, bool allow_negative) {
|
||||||
|
if (offset->second < 0) {
|
||||||
|
offset->second = 59;
|
||||||
|
--offset->minute;
|
||||||
|
} else if (offset->second > 59) {
|
||||||
|
offset->second = 0;
|
||||||
|
++offset->minute;
|
||||||
|
}
|
||||||
|
if (offset->minute < 0) {
|
||||||
|
offset->minute = 59;
|
||||||
|
--offset->hour;
|
||||||
|
} else if (offset->minute > 59) {
|
||||||
|
offset->minute = 0;
|
||||||
|
++offset->hour;
|
||||||
|
}
|
||||||
|
if (allow_negative) {
|
||||||
|
if (offset->hour < -24) {
|
||||||
|
offset->hour = 24;
|
||||||
|
} else if (offset->hour > 24) {
|
||||||
|
offset->hour = -24;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (offset->hour < 0) {
|
||||||
|
offset->hour = 24;
|
||||||
|
} else if (offset->hour > 24) {
|
||||||
|
offset->hour = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SetTZScreenAction set_tz_dispatch_select_offset(TZOffsetSelectStage *stage,
|
||||||
|
SensorState *state,
|
||||||
|
TZOffset *target,
|
||||||
|
const char *label,
|
||||||
|
bool force_redraw,
|
||||||
|
bool allow_negative) {
|
||||||
|
if (state->back_down) {
|
||||||
|
if (*stage == TZ_OFFSET_SEL_HOUR) {
|
||||||
|
*stage = TZ_OFFSET_SEL_SECOND;
|
||||||
|
return SET_TZ_BACK;
|
||||||
|
}
|
||||||
|
--*stage;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
if (state->up_down || state->down_down) {
|
||||||
|
force_redraw = true;
|
||||||
|
int change = state->up_down - state->down_down;
|
||||||
|
switch (*stage) {
|
||||||
|
case TZ_OFFSET_SEL_HOUR:
|
||||||
|
target->hour += change;
|
||||||
|
break;
|
||||||
|
case TZ_OFFSET_SEL_MINUTE:
|
||||||
|
target->minute += change;
|
||||||
|
break;
|
||||||
|
case TZ_OFFSET_SEL_SECOND:
|
||||||
|
target->second += change;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
set_tz_offset_normalize(target, allow_negative);
|
||||||
|
}
|
||||||
|
if (state->sel_down) {
|
||||||
|
if (*stage == TZ_OFFSET_SEL_SECOND) {
|
||||||
|
*stage = TZ_OFFSET_SEL_HOUR;
|
||||||
|
return SET_TZ_NEXT;
|
||||||
|
}
|
||||||
|
++*stage;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
if (force_redraw) {
|
||||||
|
const char *format;
|
||||||
|
int cursor_pos;
|
||||||
|
switch (*stage) {
|
||||||
|
case TZ_OFFSET_SEL_HOUR:
|
||||||
|
format = ">%0*d:%02d:%02d";
|
||||||
|
cursor_pos = 0;
|
||||||
|
break;
|
||||||
|
case TZ_OFFSET_SEL_MINUTE:
|
||||||
|
format = "%0*d:>%02d:%02d";
|
||||||
|
cursor_pos = target->hour < 0 ? 4 : 3;
|
||||||
|
break;
|
||||||
|
case TZ_OFFSET_SEL_SECOND:
|
||||||
|
format = "%0*d:%02d:>%02d";
|
||||||
|
cursor_pos = target->hour < 0 ? 7 : 6;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
format = "";
|
||||||
|
cursor_pos = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char time_str[17];
|
||||||
|
snprintf(time_str, 17, format, target->hour < 0 ? 3 : 2, target->hour,
|
||||||
|
target->minute, target->second);
|
||||||
|
lcd_clear(state->lcd);
|
||||||
|
lcd_move_to(state->lcd, 0, 0);
|
||||||
|
lcd_write_string(state->lcd, label);
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
lcd_write_string(state->lcd, time_str);
|
||||||
|
lcd_move_to(state->lcd, 1, cursor_pos);
|
||||||
|
lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON,
|
||||||
|
LCD_DISPLAY_ON);
|
||||||
|
}
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_tz_prompt_has_dst(SetTZScreen *screen,
|
||||||
|
SensorState *state) {
|
||||||
|
if (state->back_down) {
|
||||||
|
--screen->stage;
|
||||||
|
screen->need_redraw = true;
|
||||||
|
screen->off_sel_stage = TZ_OFFSET_SEL_SECOND;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state->sel_down) {
|
||||||
|
if (screen->has_dst) {
|
||||||
|
screen->stage = SET_TZ_DST_OFFSET;
|
||||||
|
} else {
|
||||||
|
screen->stage = SET_TZ_FINISH;
|
||||||
|
}
|
||||||
|
screen->need_redraw = true;
|
||||||
|
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state->up_down || state->down_down) {
|
||||||
|
screen->has_dst = abs((((int) screen->has_dst) + state->up_down -
|
||||||
|
state->down_down) % 2);
|
||||||
|
screen->need_redraw = 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, "Has DST?");
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
lcd_write_string(state->lcd, ">No >Yes");
|
||||||
|
lcd_move_to(state->lcd, 1, screen->has_dst ? 4 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *DST_BOUND_TYPE_LABELS[] = {
|
||||||
|
"Exact Julian",
|
||||||
|
"Leap Julian",
|
||||||
|
"Month Day Week"
|
||||||
|
};
|
||||||
|
static SetTZScreenAction set_tz_bound_select_type(SetTZScreen *screen,
|
||||||
|
SensorState *state,
|
||||||
|
DSTBound *target,
|
||||||
|
const char *label) {
|
||||||
|
if (state->back_down) {
|
||||||
|
screen->off_sel_stage = TZ_OFFSET_SEL_SECOND;
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_TIME;
|
||||||
|
return SET_TZ_BACK;
|
||||||
|
}
|
||||||
|
if (state->sel_down) {
|
||||||
|
switch (target->type) {
|
||||||
|
case DST_BOUND_EXACT_JULIAN:
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_JULIAN;
|
||||||
|
target->julian_day = 1;
|
||||||
|
break;
|
||||||
|
case DST_BOUND_LEAP_JULAIN:
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_JULIAN;
|
||||||
|
target->julian_day = 0;
|
||||||
|
break;
|
||||||
|
case DST_BOUND_MND:
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_MONTH;
|
||||||
|
target->month = 1;
|
||||||
|
target->week = 1;
|
||||||
|
target->day = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
target->time.hour = 2;
|
||||||
|
target->time.minute = 0;
|
||||||
|
target->time.second = 0;
|
||||||
|
screen->julian_didgit = 3;
|
||||||
|
screen->need_redraw = true;
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
if (state->up_down || state->down_down) {
|
||||||
|
int new_type = (((int) target->type) + state->up_down - state->down_down) % 3;
|
||||||
|
if (new_type < 0) {
|
||||||
|
target->type = 2;
|
||||||
|
} else {
|
||||||
|
target->type = new_type;
|
||||||
|
}
|
||||||
|
screen->need_redraw = 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, label);
|
||||||
|
lcd_write_string(state->lcd, " Type:");
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
lcd_write_char(state->lcd, '>');
|
||||||
|
lcd_write_string(state->lcd, DST_BOUND_TYPE_LABELS[target->type]);
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
}
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *MONTH_ABBREVS[] = {
|
||||||
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
|
||||||
|
"Dec",
|
||||||
|
};
|
||||||
|
static const char *WEEKDAY_ABBREVS[] = {
|
||||||
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||||
|
};
|
||||||
|
static SetTZScreenAction set_tz_bound_select_mnd(SetTZScreen *screen,
|
||||||
|
SensorState *state,
|
||||||
|
DSTBound *target,
|
||||||
|
const char *label) {
|
||||||
|
if (state->back_down) {
|
||||||
|
--screen->bound_sel_stage;
|
||||||
|
screen->need_redraw = true;
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
if (state->sel_down) {
|
||||||
|
screen->need_redraw = true;
|
||||||
|
if (screen->bound_sel_stage == DST_BOUND_SEL_DAY) {
|
||||||
|
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_TIME;
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
++screen->bound_sel_stage;
|
||||||
|
}
|
||||||
|
if (state->up_down || state->down_down) {
|
||||||
|
int change = state->up_down - state->down_down;
|
||||||
|
switch (screen->bound_sel_stage) {
|
||||||
|
case DST_BOUND_SEL_MONTH:
|
||||||
|
target->month += change;
|
||||||
|
if (target->month < 1) {
|
||||||
|
target->month = 1;
|
||||||
|
} else if (target->month > 12) {
|
||||||
|
target->month = 12;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DST_BOUND_SEL_WEEK:
|
||||||
|
target->week += change;
|
||||||
|
if (target->week < 1) {
|
||||||
|
target->week = 1;
|
||||||
|
} else if (target->week > 5) {
|
||||||
|
target->week = 5;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DST_BOUND_SEL_DAY:
|
||||||
|
target->day += change;
|
||||||
|
if (target->day < 0) {
|
||||||
|
target->day = 0;
|
||||||
|
} else if (target->day > 6) {
|
||||||
|
target->day = 6;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; // should not happen
|
||||||
|
}
|
||||||
|
screen->need_redraw = true;
|
||||||
|
}
|
||||||
|
if (screen->need_redraw) {
|
||||||
|
screen->need_redraw = false;
|
||||||
|
const char *format;
|
||||||
|
int cursor_pos;
|
||||||
|
switch(screen->bound_sel_stage) {
|
||||||
|
case DST_BOUND_SEL_MONTH:
|
||||||
|
format = ">%s-%d-%s";
|
||||||
|
cursor_pos = 0;
|
||||||
|
break;
|
||||||
|
case DST_BOUND_SEL_WEEK:
|
||||||
|
format = "%s->%d-%s";
|
||||||
|
cursor_pos = 4;
|
||||||
|
break;
|
||||||
|
case DST_BOUND_SEL_DAY:
|
||||||
|
format = "%s-%d->%s";
|
||||||
|
cursor_pos = 7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// should not happen
|
||||||
|
format = "";
|
||||||
|
cursor_pos = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char sel_str[17];
|
||||||
|
snprintf(sel_str, 17, format, MONTH_ABBREVS[target->month - 1],
|
||||||
|
target->week, WEEKDAY_ABBREVS[target->day]);
|
||||||
|
lcd_clear(state->lcd);
|
||||||
|
lcd_move_to(state->lcd, 0, 0);
|
||||||
|
lcd_write_string(state->lcd, "Mon-Week-Day");
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
lcd_write_string(state->lcd, sel_str);
|
||||||
|
lcd_move_to(state->lcd, 1, cursor_pos);
|
||||||
|
}
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_tz_normalize_julian(int julian, bool allow_zero) {
|
||||||
|
if (julian <= 0) {
|
||||||
|
return allow_zero ? 0 : 1;
|
||||||
|
} else if (julian > 365) {
|
||||||
|
return 365;
|
||||||
|
}
|
||||||
|
return julian;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SetTZScreenAction set_tz_dispatch_select_julian(SetTZScreen *screen,
|
||||||
|
SensorState *state,
|
||||||
|
DSTBound *target,
|
||||||
|
const char *label) {
|
||||||
|
if (state->back_down) {
|
||||||
|
screen->need_redraw = true;
|
||||||
|
if (screen->julian_didgit == 3) {
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_TYPE;
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
++screen->julian_didgit;
|
||||||
|
}
|
||||||
|
if (state->sel_down) {
|
||||||
|
screen->need_redraw = true;
|
||||||
|
if (screen->julian_didgit == 1) {
|
||||||
|
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_TIME;
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
--screen->julian_didgit;
|
||||||
|
}
|
||||||
|
if (state->up_down || state->down_down) {
|
||||||
|
screen->need_redraw = true;
|
||||||
|
int change = state->up_down - state->down_down;
|
||||||
|
int scale = 0;
|
||||||
|
switch (screen->julian_didgit) {
|
||||||
|
case 1: // ones
|
||||||
|
scale = 1;
|
||||||
|
break;
|
||||||
|
case 2: // tens
|
||||||
|
scale = 10;
|
||||||
|
break;
|
||||||
|
case 3: // hundreds
|
||||||
|
scale = 100;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bool allow_zero = screen->off_sel_stage == DST_BOUND_LEAP_JULAIN;
|
||||||
|
int new_day = target->julian_day + scale * change;
|
||||||
|
target->julian_day = set_tz_normalize_julian(new_day, allow_zero);
|
||||||
|
}
|
||||||
|
if (screen->need_redraw) {
|
||||||
|
screen->need_redraw = false;
|
||||||
|
char jul_str[4];
|
||||||
|
snprintf(jul_str, 4, "%03d", target->julian_day);
|
||||||
|
lcd_clear(state->lcd);
|
||||||
|
lcd_move_to(state->lcd, 0, 0);
|
||||||
|
lcd_write_string(state->lcd, "Julian Day");
|
||||||
|
lcd_move_to(state->lcd, 1, 0);
|
||||||
|
lcd_write_string(state->lcd, jul_str);
|
||||||
|
lcd_move_to(state->lcd, 1, 3 - screen->julian_didgit);
|
||||||
|
}
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SetTZScreenAction set_tz_dispatch_select_bound(SetTZScreen *screen,
|
||||||
|
SensorState *state,
|
||||||
|
DSTBound *target,
|
||||||
|
const char *label) {
|
||||||
|
SetTZScreenAction action;
|
||||||
|
switch (screen->bound_sel_stage) {
|
||||||
|
case DST_BOUND_SEL_TYPE:
|
||||||
|
return set_tz_bound_select_type(screen, state, target, label);
|
||||||
|
case DST_BOUND_SEL_MONTH:
|
||||||
|
case DST_BOUND_SEL_WEEK:
|
||||||
|
case DST_BOUND_SEL_DAY:
|
||||||
|
return set_tz_bound_select_mnd(screen, state, target, label);
|
||||||
|
case DST_BOUND_SEL_JULIAN:
|
||||||
|
return set_tz_dispatch_select_julian(screen, state, target, label);
|
||||||
|
case DST_BOUND_SEL_TIME:
|
||||||
|
action = set_tz_dispatch_select_offset(&screen->off_sel_stage, state,
|
||||||
|
&target->time, "Effect Time:",
|
||||||
|
screen->need_redraw, false);
|
||||||
|
switch (action) {
|
||||||
|
case SET_TZ_NEXT:
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_TYPE;
|
||||||
|
case SET_TZ_CONTINUE:
|
||||||
|
screen->need_redraw = false;
|
||||||
|
return action;
|
||||||
|
case SET_TZ_BACK:
|
||||||
|
if (target->type == DST_BOUND_MND) {
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_DAY;
|
||||||
|
} else {
|
||||||
|
screen->julian_didgit = 1;
|
||||||
|
screen->bound_sel_stage = DST_BOUND_SEL_JULIAN;
|
||||||
|
}
|
||||||
|
screen->need_redraw = true;
|
||||||
|
return SET_TZ_CONTINUE;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return SET_TZ_BACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_tz_write_dst_bound(FILE *tz, DSTBound *bound) {
|
||||||
|
fputc(',', tz);
|
||||||
|
switch (bound->type) {
|
||||||
|
case DST_BOUND_EXACT_JULIAN:
|
||||||
|
fprintf(tz, "J%d", bound->julian_day);
|
||||||
|
break;
|
||||||
|
case DST_BOUND_LEAP_JULAIN:
|
||||||
|
fprintf(tz, "%d", bound->julian_day);
|
||||||
|
break;
|
||||||
|
case DST_BOUND_MND:
|
||||||
|
fprintf(tz, "M%d.%d.%d", bound->month, bound->week, bound->day);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(tz, "/%02d:%02d:%02d", bound->time.hour, bound->time.minute,
|
||||||
|
bound->time.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool set_tz_screen_finish(SetTZScreen *screen,
|
||||||
|
SensorState *state) {
|
||||||
|
if (!screen->did_set) {
|
||||||
|
screen->did_set = true;
|
||||||
|
size_t len;
|
||||||
|
char *buf;
|
||||||
|
FILE *tz = open_memstream(&buf, &len);
|
||||||
|
TZOffset *off = &screen->offset;
|
||||||
|
fprintf(tz, "timezone=CTZ%02d:%02d:%02d", off->hour, off->minute, off->second);
|
||||||
|
if (screen->has_dst) {
|
||||||
|
TZOffset *dst_off = &screen->dst_offset;
|
||||||
|
fprintf(tz, "CDST%02d:%02d:%02d", dst_off->hour, dst_off->minute,
|
||||||
|
dst_off->second);
|
||||||
|
set_tz_write_dst_bound(tz, &screen->start_bound);
|
||||||
|
set_tz_write_dst_bound(tz, &screen->end_bound);
|
||||||
|
}
|
||||||
|
fclose(tz);
|
||||||
|
char *const args[] = {
|
||||||
|
"sysrc",
|
||||||
|
"-f",
|
||||||
|
GLOBAL_OPTS.config_path,
|
||||||
|
buf,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
pid_t pid;
|
||||||
|
int exit_status;
|
||||||
|
if (posix_spawnp(&pid, "sysrc", NULL, NULL, args, environ) != 0) {
|
||||||
|
warnx("failed to start sysrc(8)");
|
||||||
|
} else if (waitpid(pid, &exit_status, 0) < 0) {
|
||||||
|
warn("waitpid(2) for sysrc(8) failed");
|
||||||
|
} else if (exit_status != 0) {
|
||||||
|
warnx("sysrc(8) failed to set timezone");
|
||||||
|
} else {
|
||||||
|
LOG_VERBOSE("Set timezone to: \"%s\"\n", buf);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
request_restart();
|
||||||
|
}
|
||||||
|
if (state->up_down || state->back_down ||
|
||||||
|
state->sel_down || state->down_down) {
|
||||||
|
set_tz_screen_reset(screen, state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (screen->need_redraw) {
|
||||||
|
screen->need_redraw = false;
|
||||||
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||||
|
LCD_DISPLAY_ON);
|
||||||
|
lcd_clear(state->lcd);
|
||||||
|
lcd_move_to(state->lcd, 0, 0);
|
||||||
|
lcd_write_string(state->lcd, "Done!");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool set_tz_screen_dispatch(SetTZScreen *screen,
|
||||||
|
SensorState *state) {
|
||||||
|
if (state->force_draw) {
|
||||||
|
screen->need_redraw = true;
|
||||||
|
}
|
||||||
|
SetTZScreenAction action;
|
||||||
|
switch (screen->stage) {
|
||||||
|
case SET_TZ_OFFSET:
|
||||||
|
action = set_tz_dispatch_select_offset(&screen->off_sel_stage, state,
|
||||||
|
&screen->offset, "Offset",
|
||||||
|
screen->need_redraw, true);
|
||||||
|
screen->need_redraw = false;
|
||||||
|
switch (action) {
|
||||||
|
case SET_TZ_BACK:
|
||||||
|
set_tz_screen_reset(screen, state);
|
||||||
|
return true;
|
||||||
|
case SET_TZ_CONTINUE:
|
||||||
|
break; // ignore
|
||||||
|
case SET_TZ_NEXT:
|
||||||
|
screen->need_redraw = true;
|
||||||
|
++screen->stage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_TZ_HAS_DST:
|
||||||
|
set_tz_prompt_has_dst(screen, state);
|
||||||
|
break;
|
||||||
|
case SET_TZ_DST_OFFSET:
|
||||||
|
action = set_tz_dispatch_select_offset(&screen->off_sel_stage, state,
|
||||||
|
&screen->dst_offset,
|
||||||
|
"DST Offset",
|
||||||
|
screen->need_redraw, true);
|
||||||
|
screen->need_redraw = false;
|
||||||
|
switch (action) {
|
||||||
|
case SET_TZ_BACK:
|
||||||
|
--screen->stage;
|
||||||
|
screen->need_redraw = true;
|
||||||
|
case SET_TZ_CONTINUE:
|
||||||
|
break; // ignore
|
||||||
|
case SET_TZ_NEXT:
|
||||||
|
screen->need_redraw = true;
|
||||||
|
++screen->stage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_TZ_DST_START:
|
||||||
|
action = set_tz_dispatch_select_bound(screen, state,
|
||||||
|
&screen->start_bound, "Start");
|
||||||
|
switch (action) {
|
||||||
|
case SET_TZ_BACK:
|
||||||
|
--screen->stage;
|
||||||
|
screen->need_redraw = true;
|
||||||
|
case SET_TZ_CONTINUE:
|
||||||
|
break; // ignore
|
||||||
|
case SET_TZ_NEXT:
|
||||||
|
screen->need_redraw = true;
|
||||||
|
++screen->stage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_TZ_DST_END:
|
||||||
|
action = set_tz_dispatch_select_bound(screen, state,
|
||||||
|
&screen->end_bound, "End");
|
||||||
|
switch (action) {
|
||||||
|
case SET_TZ_BACK:
|
||||||
|
--screen->stage;
|
||||||
|
screen->need_redraw = true;
|
||||||
|
case SET_TZ_CONTINUE:
|
||||||
|
break; // ignore
|
||||||
|
case SET_TZ_NEXT:
|
||||||
|
screen->need_redraw = true;
|
||||||
|
++screen->stage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_TZ_FINISH:
|
||||||
|
return set_tz_screen_finish(screen, state);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTZScreen *set_tz_screen_new() {
|
||||||
|
SetTZScreen *s = malloc_checked(sizeof(SetTZScreen));
|
||||||
|
screen_init(&s->parent, "Set timezone",
|
||||||
|
(ScreenDispatchFunc) set_tz_screen_dispatch,
|
||||||
|
(ScreenCleanupFunc) free);
|
||||||
|
set_tz_screen_reset(s, NULL);
|
||||||
|
return s;
|
||||||
|
}
|
88
src/ui/settzscreen.h
Normal file
88
src/ui/settzscreen.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* settzscreen.h - Screen for setting the timezone
|
||||||
|
* 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_SETTZSCREEN_H
|
||||||
|
#define INCLUDED_SETTZSCREEN_H
|
||||||
|
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int hour;
|
||||||
|
int minute;
|
||||||
|
int second;
|
||||||
|
} TZOffset;
|
||||||
|
|
||||||
|
#define tz_offset_reset(o) ((o)->hour = (o)->minute = (o)->second = 0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum {
|
||||||
|
DST_BOUND_EXACT_JULIAN = 0,
|
||||||
|
DST_BOUND_LEAP_JULAIN,
|
||||||
|
DST_BOUND_MND,
|
||||||
|
} type;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
int month;
|
||||||
|
int week;
|
||||||
|
int day;
|
||||||
|
};
|
||||||
|
int julian_day;
|
||||||
|
};
|
||||||
|
TZOffset time;
|
||||||
|
} DSTBound;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TZ_OFFSET_SEL_HOUR,
|
||||||
|
TZ_OFFSET_SEL_MINUTE,
|
||||||
|
TZ_OFFSET_SEL_SECOND,
|
||||||
|
} TZOffsetSelectStage;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DST_BOUND_SEL_TYPE,
|
||||||
|
DST_BOUND_SEL_MONTH,
|
||||||
|
DST_BOUND_SEL_WEEK,
|
||||||
|
DST_BOUND_SEL_DAY,
|
||||||
|
DST_BOUND_SEL_JULIAN,
|
||||||
|
DST_BOUND_SEL_TIME,
|
||||||
|
} DSTBoundSelectStage;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SET_TZ_BACK,
|
||||||
|
SET_TZ_CONTINUE,
|
||||||
|
SET_TZ_NEXT,
|
||||||
|
} SetTZScreenAction;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Screen parent;
|
||||||
|
enum {
|
||||||
|
SET_TZ_OFFSET,
|
||||||
|
SET_TZ_HAS_DST,
|
||||||
|
SET_TZ_DST_OFFSET,
|
||||||
|
SET_TZ_DST_START,
|
||||||
|
SET_TZ_DST_END,
|
||||||
|
SET_TZ_FINISH,
|
||||||
|
} stage;
|
||||||
|
bool need_redraw;
|
||||||
|
TZOffsetSelectStage off_sel_stage;
|
||||||
|
DSTBoundSelectStage bound_sel_stage;
|
||||||
|
int julian_didgit;
|
||||||
|
TZOffset offset;
|
||||||
|
bool has_dst;
|
||||||
|
TZOffset dst_offset;
|
||||||
|
DSTBound start_bound;
|
||||||
|
DSTBound end_bound;
|
||||||
|
bool did_set;
|
||||||
|
} SetTZScreen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new SetTZScreen that will allow the user the set the time zone.
|
||||||
|
*/
|
||||||
|
SetTZScreen *set_tz_screen_new(void);
|
||||||
|
|
||||||
|
#endif
|
11
src/util.c
11
src/util.c
@ -19,6 +19,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
const char *PERIOD_LABELS[] = {
|
const char *PERIOD_LABELS[] = {
|
||||||
"HOUR",
|
"HOUR",
|
||||||
@ -29,6 +30,10 @@ const char *PERIOD_LABELS[] = {
|
|||||||
};
|
};
|
||||||
const size_t NPERIOD = sizeof(PERIOD_LABELS) / sizeof(char *);
|
const size_t NPERIOD = sizeof(PERIOD_LABELS) / sizeof(char *);
|
||||||
|
|
||||||
|
// these are defined in main.c
|
||||||
|
extern _Atomic bool RUNNING;
|
||||||
|
extern bool SHOULD_RESTART;
|
||||||
|
|
||||||
Options GLOBAL_OPTS;
|
Options GLOBAL_OPTS;
|
||||||
|
|
||||||
void cleanup_options(Options *opts) {
|
void cleanup_options(Options *opts) {
|
||||||
@ -39,6 +44,7 @@ void cleanup_options(Options *opts) {
|
|||||||
free(opts->fail_key);
|
free(opts->fail_key);
|
||||||
free(opts->database_location);
|
free(opts->database_location);
|
||||||
free(opts->export_file_name);
|
free(opts->export_file_name);
|
||||||
|
free(opts->timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *malloc_checked(size_t n) {
|
void *malloc_checked(size_t n) {
|
||||||
@ -565,3 +571,8 @@ bool mkdirs(const char *path, mode_t mode) {
|
|||||||
free(copy);
|
free(copy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void request_restart() {
|
||||||
|
RUNNING = false;
|
||||||
|
SHOULD_RESTART = true;
|
||||||
|
}
|
||||||
|
@ -54,6 +54,7 @@ typedef struct {
|
|||||||
TemperatureUnit temp_unit;
|
TemperatureUnit temp_unit;
|
||||||
|
|
||||||
char *export_file_name; // file to export to
|
char *export_file_name; // file to export to
|
||||||
|
char *timezone; // default timezone
|
||||||
} Options;
|
} Options;
|
||||||
extern Options GLOBAL_OPTS;
|
extern Options GLOBAL_OPTS;
|
||||||
|
|
||||||
@ -248,4 +249,10 @@ bool export_database_as_csv(sqlite3 *db, const char *dest);
|
|||||||
*/
|
*/
|
||||||
bool mkdirs(const char *path, mode_t mode);
|
bool mkdirs(const char *path, mode_t mode);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request that this program re-invoke itself with the same arguments. Used to
|
||||||
|
* re-initialize configuration values.
|
||||||
|
*/
|
||||||
|
void request_restart(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user