Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions aura/translations.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct LocalizedStrings {
const char* language_label;
const char* weekdays[7];
const char* use_night_mode;
const char* night_mode_brightness;
};

#define DEFAULT_CAPTIVE_SSID "Aura"
Expand Down Expand Up @@ -65,7 +66,8 @@ static const LocalizedStrings strings_en = {
"reconfigure Wi-Fi credentials.",
"Language:",
{"Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"},
"Dim screen at night"
"Dim screen at night",
"Night brightness:"
};

static const LocalizedStrings strings_es = {
Expand Down Expand Up @@ -96,7 +98,8 @@ static const LocalizedStrings strings_es = {
"credenciales Wi-Fi.",
"Idioma:",
{"Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"},
"Pantalla noche"
"Pantalla noche",
"Brillo nocturno:"
};

static const LocalizedStrings strings_de = {
Expand Down Expand Up @@ -129,7 +132,8 @@ static const LocalizedStrings strings_de = {
"neu zu konfigurieren.",
"Sprache:",
{"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"},
"Nacht-Dimmen"
"Nacht-Dimmen",
"Nacht-Helligkeit:"
};

static const LocalizedStrings strings_fr = {
Expand Down Expand Up @@ -162,7 +166,8 @@ static const LocalizedStrings strings_fr = {
"les identifiants Wi-Fi.",
"Langue:",
{"Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"},
"Nuit écran"
"Nuit écran",
"Luminosité nuit:"
};

static const LocalizedStrings strings_tr = {
Expand Down Expand Up @@ -192,7 +197,8 @@ static const LocalizedStrings strings_tr = {
"gerekecek.",
"Dil:",
{"Paz", "Pzt", "Sal", "Çar", "Per", "Cum", "Cmt"},
"Gece kısık"
"Gece kısık",
"Gece parlaklığı:"
};

static const LocalizedStrings strings_sv = {
Expand Down Expand Up @@ -226,7 +232,8 @@ static const LocalizedStrings strings_sv = {
"autentiseringsuppgifter.",
"Sprak:",
{"Sön", "Man", "Tis", "Ons", "Tor", "Fre", "Lör"},
"Nattdämpning"
"Nattdämpning",
"Nattljusstyrka:"
};

static const LocalizedStrings strings_it = {
Expand Down Expand Up @@ -255,7 +262,8 @@ static const LocalizedStrings strings_it = {
"riconfigurare le credenziali Wi-Fi.",
"Lingua:",
{"Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"},
"Schermo notte"
"Schermo notte",
"Luminosità notte:"
};

static const LocalizedStrings* get_strings(Language current_language) {
Expand Down
132 changes: 111 additions & 21 deletions aura/weather.ino
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
#define DEFAULT_CAPTIVE_SSID "Aura"
#define UPDATE_INTERVAL 600000UL // 10 minutes

// Night mode starts at 10pm and ends at 6am
#define NIGHT_MODE_START_HOUR 22
#define NIGHT_MODE_END_HOUR 6
// Night mode variables for sunrise/sunset
static String sunrise_time = "";
static String sunset_time = "";
static bool sunrise_sunset_available = false;

LV_FONT_DECLARE(lv_font_montserrat_latin_12);
LV_FONT_DECLARE(lv_font_montserrat_latin_14);
Expand Down Expand Up @@ -69,6 +70,7 @@ static Preferences prefs;
static bool use_fahrenheit = false;
static bool use_24_hour = false;
static bool use_night_mode = false;
static uint32_t night_mode_brightness = 16;
static char latitude[16] = LATITUDE_DEFAULT;
static char longitude[16] = LONGITUDE_DEFAULT;
static String location = String(LOCATION_DEFAULT);
Expand Down Expand Up @@ -107,6 +109,8 @@ static lv_obj_t *location_win = nullptr;
static lv_obj_t *unit_switch;
static lv_obj_t *clock_24hr_switch;
static lv_obj_t *night_mode_switch;
static lv_obj_t *lbl_night_brightness = nullptr;
static lv_obj_t *night_brightness_slider = nullptr;
static lv_obj_t *language_dropdown;
static lv_obj_t *lbl_clock;

Expand Down Expand Up @@ -180,6 +184,8 @@ void activate_night_mode();
void deactivate_night_mode();
void check_for_night_mode();
void handle_temp_screen_wakeup_timeout(lv_timer_t *timer);
int time_to_minutes(const String &time_str);
bool is_night_time();


int day_of_week(int y, int m, int d) {
Expand Down Expand Up @@ -340,6 +346,7 @@ void setup() {
use_fahrenheit = prefs.getBool("useFahrenheit", false);
location = prefs.getString("location", LOCATION_DEFAULT);
use_night_mode = prefs.getBool("useNightMode", false);
night_mode_brightness = prefs.getUInt("nightModeBrightness", 16);
uint32_t brightness = prefs.getUInt("brightness", 255);
use_24_hour = prefs.getBool("use24Hour", false);
current_language = (Language)prefs.getUInt("language", LANG_EN);
Expand Down Expand Up @@ -726,33 +733,33 @@ void create_location_dialog() {
void create_settings_window() {
if (settings_win) return;

int vertical_element_spacing = 21;
int vertical_element_spacing = 18;

const LocalizedStrings* strings = get_strings(current_language);
settings_win = lv_win_create(lv_scr_act());

lv_obj_t *header = lv_win_get_header(settings_win);
lv_obj_set_style_height(header, 30, 0);
lv_obj_set_style_height(header, 28, 0);

lv_obj_t *title = lv_win_add_title(settings_win, strings->aura_settings);
lv_obj_set_style_text_font(title, get_font_16(), 0);
lv_obj_set_style_margin_left(title, 10, 0);

lv_obj_center(settings_win);
lv_obj_set_width(settings_win, 240);
lv_obj_set_size(settings_win, 240, 320);

lv_obj_t *cont = lv_win_get_content(settings_win);

// Brightness
lv_obj_t *lbl_b = lv_label_create(cont);
lv_label_set_text(lbl_b, strings->brightness);
lv_obj_set_style_text_font(lbl_b, get_font_12(), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_align(lbl_b, LV_ALIGN_TOP_LEFT, 0, 5);
lv_obj_align(lbl_b, LV_ALIGN_TOP_LEFT, 0, 2);
lv_obj_t *slider = lv_slider_create(cont);
lv_slider_set_range(slider, 1, 255);
uint32_t saved_b = prefs.getUInt("brightness", 128);
lv_slider_set_value(slider, saved_b, LV_ANIM_OFF);
lv_obj_set_width(slider, 100);
lv_obj_set_width(slider, 90);
lv_obj_align_to(slider, lbl_b, LV_ALIGN_OUT_RIGHT_MID, 10, 0);

lv_obj_add_event_cb(slider, [](lv_event_t *e){
Expand All @@ -777,11 +784,36 @@ void create_settings_window() {
}
lv_obj_add_event_cb(night_mode_switch, settings_event_handler, LV_EVENT_VALUE_CHANGED, NULL);

// Night mode brightness slider (only visible when night mode is enabled)
lbl_night_brightness = lv_label_create(cont);
lv_label_set_text(lbl_night_brightness, strings->night_mode_brightness);
lv_obj_set_style_text_font(lbl_night_brightness, get_font_12(), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_align_to(lbl_night_brightness, lbl_night_mode, LV_ALIGN_OUT_BOTTOM_LEFT, 0, vertical_element_spacing);

night_brightness_slider = lv_slider_create(cont);
lv_slider_set_range(night_brightness_slider, 0, 45);
lv_slider_set_value(night_brightness_slider, night_mode_brightness, LV_ANIM_OFF);
lv_obj_set_width(night_brightness_slider, 90);
lv_obj_align_to(night_brightness_slider, lbl_night_brightness, LV_ALIGN_OUT_RIGHT_MID, 10, 0);

// Hide night brightness controls if night mode is disabled
if (!use_night_mode) {
lv_obj_add_flag(lbl_night_brightness, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(night_brightness_slider, LV_OBJ_FLAG_HIDDEN);
}

lv_obj_add_event_cb(night_brightness_slider, [](lv_event_t *e){
lv_obj_t *s = (lv_obj_t*)lv_event_get_target(e);
uint32_t v = lv_slider_get_value(s);
night_mode_brightness = v;
prefs.putUInt("nightModeBrightness", night_mode_brightness);
}, LV_EVENT_VALUE_CHANGED, NULL);

// 'Use F' switch
lv_obj_t *lbl_u = lv_label_create(cont);
lv_label_set_text(lbl_u, strings->use_fahrenheit);
lv_obj_set_style_text_font(lbl_u, get_font_12(), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_align_to(lbl_u, lbl_night_mode, LV_ALIGN_OUT_BOTTOM_LEFT, 0, vertical_element_spacing);
lv_obj_align_to(lbl_u, lbl_night_brightness, LV_ALIGN_OUT_BOTTOM_LEFT, 0, vertical_element_spacing);

unit_switch = lv_switch_create(cont);
lv_obj_align_to(unit_switch, lbl_u, LV_ALIGN_OUT_RIGHT_MID, 6, 0);
Expand Down Expand Up @@ -827,7 +859,7 @@ void create_settings_window() {
language_dropdown = lv_dropdown_create(cont);
lv_dropdown_set_options(language_dropdown, "English\nEspañol\nDeutsch\nFrançais\nTürkçe\nSvenska\nItaliano");
lv_dropdown_set_selected(language_dropdown, current_language);
lv_obj_set_width(language_dropdown, 120);
lv_obj_set_width(language_dropdown, 110);
lv_obj_set_style_text_font(language_dropdown, get_font_12(), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(language_dropdown, get_font_12(), LV_PART_SELECTED | LV_STATE_DEFAULT);
lv_obj_t *list = lv_dropdown_get_list(language_dropdown);
Expand All @@ -839,7 +871,7 @@ void create_settings_window() {
lv_obj_t *btn_change_loc = lv_btn_create(cont);
lv_obj_align_to(btn_change_loc, lbl_lang, LV_ALIGN_OUT_BOTTOM_LEFT, 0, vertical_element_spacing);

lv_obj_set_size(btn_change_loc, 100, 40);
lv_obj_set_size(btn_change_loc, 90, 35);
lv_obj_add_event_cb(btn_change_loc, change_location_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *lbl_chg = lv_label_create(btn_change_loc);
lv_label_set_text(lbl_chg, strings->location_btn);
Expand All @@ -860,8 +892,8 @@ void create_settings_window() {
lv_obj_set_style_bg_color(btn_reset, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(btn_reset, lv_palette_darken(LV_PALETTE_RED, 1), LV_PART_MAIN | LV_STATE_PRESSED);
lv_obj_set_style_text_color(btn_reset, lv_color_white(), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_size(btn_reset, 100, 40);
lv_obj_align_to(btn_reset, btn_change_loc, LV_ALIGN_OUT_RIGHT_MID, 12, 0);
lv_obj_set_size(btn_reset, 90, 35);
lv_obj_align_to(btn_reset, btn_change_loc, LV_ALIGN_OUT_RIGHT_MID, 10, 0);

lv_obj_add_event_cb(btn_reset, reset_wifi_event_handler, LV_EVENT_CLICKED, nullptr);

Expand All @@ -872,7 +904,7 @@ void create_settings_window() {

// Close Settings button
btn_close_obj = lv_btn_create(cont);
lv_obj_set_size(btn_close_obj, 80, 40);
lv_obj_set_size(btn_close_obj, 75, 35);
lv_obj_align(btn_close_obj, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
lv_obj_add_event_cb(btn_close_obj, settings_event_handler, LV_EVENT_CLICKED, NULL);

Expand All @@ -897,6 +929,15 @@ static void settings_event_handler(lv_event_t *e) {

if (tgt == night_mode_switch && code == LV_EVENT_VALUE_CHANGED) {
use_night_mode = lv_obj_has_state(night_mode_switch, LV_STATE_CHECKED);

// Show/hide night mode brightness controls
if (use_night_mode) {
lv_obj_clear_flag(lbl_night_brightness, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(night_brightness_slider, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(lbl_night_brightness, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(night_brightness_slider, LV_OBJ_FLAG_HIDDEN);
}
}

if (tgt == language_dropdown && code == LV_EVENT_VALUE_CHANGED) {
Expand All @@ -909,6 +950,7 @@ static void settings_event_handler(lv_event_t *e) {
prefs.putBool("useFahrenheit", use_fahrenheit);
prefs.putBool("use24Hour", use_24_hour);
prefs.putBool("useNightMode", use_night_mode);
prefs.putUInt("nightModeBrightness", night_mode_brightness);
prefs.putUInt("language", current_language);

lv_keyboard_set_textarea(kb, nullptr);
Expand All @@ -925,6 +967,7 @@ static void settings_event_handler(lv_event_t *e) {
prefs.putBool("useFahrenheit", use_fahrenheit);
prefs.putBool("use24Hour", use_24_hour);
prefs.putBool("useNightMode", use_night_mode);
prefs.putUInt("nightModeBrightness", night_mode_brightness);
prefs.putUInt("language", current_language);

lv_keyboard_set_textarea(kb, nullptr);
Expand All @@ -937,19 +980,50 @@ static void settings_event_handler(lv_event_t *e) {
}
}

// Screen dimming functions implementation
bool night_mode_should_be_active() {
// Helper function to convert time string (HH:MM) to minutes since midnight
int time_to_minutes(const String &time_str) {
if (time_str.length() < 5) return -1;

int hour = time_str.substring(0, 2).toInt();
int minute = time_str.substring(3, 5).toInt();

return hour * 60 + minute;
}

// Check if it's night time based on sunrise/sunset
bool is_night_time() {
if (!sunrise_sunset_available) {
// Fallback to hours if sunrise/sunset data unavailable
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) return false;
int hour = timeinfo.tm_hour;
return (hour >= 20 || hour < 6); // 8pm to 6am fallback
}

struct tm timeinfo;
if (!getLocalTime(&timeinfo)) return false;

int current_minutes = timeinfo.tm_hour * 60 + timeinfo.tm_min;
int sunrise_minutes = time_to_minutes(sunrise_time);
int sunset_minutes = time_to_minutes(sunset_time);

if (sunrise_minutes == -1 || sunset_minutes == -1) {
// Fallback if time parsing failed
int hour = timeinfo.tm_hour;
return (hour >= 20 || hour < 6);
}

// Night time is after sunset or before sunrise
return (current_minutes >= sunset_minutes || current_minutes < sunrise_minutes);
}

bool night_mode_should_be_active() {
if (!use_night_mode) return false;

int hour = timeinfo.tm_hour;
return (hour >= NIGHT_MODE_START_HOUR || hour < NIGHT_MODE_END_HOUR);
return is_night_time();
}

void activate_night_mode() {
analogWrite(LCD_BACKLIGHT_PIN, 0);
analogWrite(LCD_BACKLIGHT_PIN, night_mode_brightness);
night_mode_active = true;
}

Expand Down Expand Up @@ -1022,7 +1096,7 @@ void fetch_and_update_weather() {
String url = String("http://api.open-meteo.com/v1/forecast?latitude=")
+ latitude + "&longitude=" + longitude
+ "&current=temperature_2m,apparent_temperature,is_day,weather_code"
+ "&daily=temperature_2m_min,temperature_2m_max,weather_code"
+ "&daily=temperature_2m_min,temperature_2m_max,weather_code,sunrise,sunset"
+ "&hourly=temperature_2m,precipitation_probability,is_day,weather_code"
+ "&forecast_hours=7"
+ "&timezone=auto";
Expand Down Expand Up @@ -1062,6 +1136,22 @@ void fetch_and_update_weather() {
JsonArray tmin = doc["daily"]["temperature_2m_min"].as<JsonArray>();
JsonArray tmax = doc["daily"]["temperature_2m_max"].as<JsonArray>();
JsonArray weather_codes = doc["daily"]["weather_code"].as<JsonArray>();
JsonArray sunrise_times = doc["daily"]["sunrise"].as<JsonArray>();
JsonArray sunset_times = doc["daily"]["sunset"].as<JsonArray>();

// Extract today's sunrise and sunset times
if (sunrise_times.size() > 0 && sunset_times.size() > 0) {
String sunrise_full = sunrise_times[0].as<String>();
String sunset_full = sunset_times[0].as<String>();

int t_index = sunrise_full.indexOf('T');
if (t_index != -1) {
sunrise_time = sunrise_full.substring(t_index + 1, t_index + 6);
sunset_time = sunset_full.substring(t_index + 1, t_index + 6);
sunrise_sunset_available = true;
Serial.println("Sunrise: " + sunrise_time + ", Sunset: " + sunset_time);
}
}

for (int i = 0; i < 7; i++) {
const char *date = times[i];
Expand Down