Calendars now sync

This commit is contained in:
Fred Boniface 2024-12-05 22:21:46 +00:00
parent a702f2f9aa
commit b633bab7cb
6 changed files with 6265 additions and 26 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@
#include <Arduino.h> #include <Arduino.h>
extern const unsigned char esphub_lrg [] PROGMEM; extern const unsigned char esphub_lrg [] PROGMEM;
extern const unsigned char sun [] PROGMEM; extern const unsigned char full_art_1 [] PROGMEM;
extern const unsigned char* full_art_array[2];
extern const int full_art_array_size;
#endif #endif

View File

@ -37,6 +37,7 @@ SPIClass hspi(HSPI);
#include "config_loader.h" #include "config_loader.h"
#include "weather.h" #include "weather.h"
#include "helper_fn.h" #include "helper_fn.h"
#include "nextcloud_cal.h"
// <Fonts> // <Fonts>
#include <Fonts/FreeMono9pt7b.h> #include <Fonts/FreeMono9pt7b.h>
@ -123,33 +124,63 @@ void setup()
} }
text_y += line_height; text_y += line_height;
// Check Nextcloud reachable here // Check Nextcloud reachable here
// Nextcloud dev check:
StartEnd timestamp = calculateStartEnd();
String cal_test = downloadICSFile(conf.calUrls[0], String(timestamp.start), String(timestamp.end), conf.calUser, conf.calKey);
Serial.println(cal_test);
displayActiveFeatures(activeFeatures); displayActiveFeatures(activeFeatures);
bootMsg("Weather Icons sourced from flaticon.com", text_y); bootMsg("Weather Icons sourced from flaticon.com", text_y);
text_y += 2 * line_height; text_y += 2 * line_height;
bootMsg("Starting main loop...", text_y); bootMsg("Starting main loop...", text_y);
} }
void loop() void loop() {
{ // Loop Vars
WeatherData weather;
// CalendarData calendar;
display.setFullWindow(); // Set Full Update Mode
if (!activeFeatures.network ||
(activeFeatures.homeassistant && haAnybodyHome() < 1)) {
displayFullScreenArt(); // Show artwork if no network or nobody is home
} else {
struct tm timeinfo; struct tm timeinfo;
if (!getLocalTime(&timeinfo)) { if (!getLocalTime(&timeinfo)) {
Serial.println("Unable to get time, some features will be buggy"); Serial.println("Unable to get time, some features will be buggy");
} }
WeatherData weather = getWeather(conf.latitude, conf.longitude);
Serial.printf("Loop iteration %d\n", loopIteration);
display.setFullWindow();
// Fetch weather data if it is active
if (activeFeatures.weather) {
weather = getWeather(conf.latitude, conf.longitude);
}
// Fetch calendar data if it is active
if (activeFeatures.calendar) {
// FETCH CALENDAR DATA HERE
}
Serial.printf("Loop iteration %d\n", loopIteration);
// Update the display
do { do {
display.fillScreen(GxEPD_WHITE); display.fillScreen(GxEPD_WHITE);
display.fillRect(0, 0, 800, 80, GxEPD_BLACK); display.fillRect(0, 0, 800, 80, GxEPD_BLACK); // Header background
displayHeaderText(timeinfo); displayHeaderText(timeinfo);
if (activeFeatures.weather && weather.latitude != 0 && weather.longitude != 0) {
displayWeather(weather); displayWeather(weather);
}
if (!activeFeatures.calendar) { // Temporarily inverse the statenment during tests
displayCalendar(timeinfo);
}
} while (display.nextPage()); } while (display.nextPage());
}
display.hibernate(); display.hibernate();
delay(3600000); delay(3600000); // Sleep for 60 minutes
loopIteration ++; loopIteration++;
} }
// Places the boot logo in position at the top of the screen // Places the boot logo in position at the top of the screen
void bootLogo() { void bootLogo() {
constexpr int img_x = (800 - 648) / 2; constexpr int img_x = (800 - 648) / 2;
@ -378,18 +409,36 @@ void displayHeaderText(struct tm timeinfo) {
void displayWeather(WeatherData weather) { void displayWeather(WeatherData weather) {
display.setFont(&FreeMonoBold12pt7b); display.setFont(&FreeMonoBold12pt7b);
display.setTextColor(GxEPD_WHITE); display.setTextColor(GxEPD_WHITE);
display.setCursor(5, 23);
// Display Current Temperature
display.setCursor(14, 23);
display.print(String((int)round(weather.current.temperature_2m)) + "°C"); display.print(String((int)round(weather.current.temperature_2m)) + "°C");
display.setFont(&FreeMonoBold9pt7b); // Set font for remaining prints
// Display Max Temp
display.drawBitmap(5, 27, hi, 22, 22, GxEPD_WHITE); // Draw 'max temp' bitmap display.drawBitmap(5, 27, hi, 22, 22, GxEPD_WHITE); // Draw 'max temp' bitmap
display.setFont(&FreeMonoBold9pt7b); display.setCursor(23, 44);
display.setCursor(18, 44);
display.print(String((int)round(weather.daily.temperature_2m_max)) + "°C"); display.print(String((int)round(weather.daily.temperature_2m_max)) + "°C");
// Display Min Temp
display.drawBitmap(5, 50, lo, 22, 22, GxEPD_WHITE); // Draw 'min temp' bitmap display.drawBitmap(5, 50, lo, 22, 22, GxEPD_WHITE); // Draw 'min temp' bitmap
display.setCursor(18, 64); display.setCursor(23, 64);
display.print(String((int)round(weather.daily.temperature_2m_min)) + "°C"); display.print(String((int)round(weather.daily.temperature_2m_min)) + "°C");
display.drawBitmap(65, 10, getWeatherBitmap(weather.daily.weather_code), 64, 64, GxEPD_WHITE); // Draw Weather Bitmap
display.drawBitmap(129, 8, wind, 22, 22, GxEPD_WHITE); // Draw 'wind speed' bitmap // Calculate the width of the longest temperature string & adjust x-position of weather bitmap and display
display.setCursor(153, 23); int longestTempWidth = 0;
int tempMaxWidth = String((int)round(weather.daily.temperature_2m_max)).length();
int tempMinWidth = String((int)round(weather.daily.temperature_2m_min)).length();
longestTempWidth = max(tempMaxWidth, tempMinWidth);
int weather_x = 58; // Default position
if (longestTempWidth > 2) {
weather_x += (longestTempWidth > 3 ? 9 : 5); // 67 is the correct X value if more than three. =2 will be halfway btwn Default and 67.
}
display.drawBitmap(weather_x, 8, getWeatherBitmap(weather.daily.weather_code), 64, 64, GxEPD_WHITE); // Draw Weather Bitmap
// Display wind & gust speed
display.drawBitmap(126, 8, wind, 22, 22, GxEPD_WHITE); // Draw 'wind speed' bitmap
display.setCursor(150, 23);
display.print("km/h"); display.print("km/h");
display.setCursor(133, 45); display.setCursor(133, 45);
display.print(String(weather.daily.wind_speed_10m_max)); display.print(String(weather.daily.wind_speed_10m_max));
@ -397,4 +446,47 @@ void displayWeather(WeatherData weather) {
display.print(String(weather.daily.wind_gusts_10m_max)); display.print(String(weather.daily.wind_gusts_10m_max));
} }
void displayFullScreenArt() {} void displayFullScreenArt() {
display.setFullWindow();
int randomIdx = random(0, full_art_array_size);
display.firstPage();
do
{
display.fillScreen(GxEPD_WHITE);
display.drawBitmap(0, 0, full_art_array[randomIdx], 800, 480, GxEPD_BLACK);
}
while (display.nextPage());
}
void displayCalendar(struct tm timeinfo) {
const int cal_y = 80;
const int cal_head = 30;
const int cal_margin = 30;
const char* weekDays[] = {"S", "M", "T", "W", "T", "F", "S"};
const char* calNames[] = {"Shared", "Fred", "Jade", "Lucy", "Ava-Rose"};
// Draw Calendar Lines
display.drawLine(0, cal_y + cal_head, 480, cal_y + cal_head, GxEPD_BLACK); // Bottom of Header
for (int y = cal_y + cal_head; y <= 480; y += 74) { // Loop to separate into five rows
display.drawLine(0, y, 800, y, GxEPD_BLACK);
}
display.drawLine(cal_margin, cal_y, cal_margin, 480, GxEPD_BLACK); // Right of margin
for (int x = cal_margin; x <= 800; x += 154) { // Loop to separate into seven columns
display.drawLine(x, cal_y, x, 480, GxEPD_BLACK);
}
// Print Calendar Labels
display.setFont(&FreeMonoBold12pt7b);
display.setTextColor(GxEPD_BLACK);
for (int i = 0; i < 5; i++) {
display.setCursor(cal_margin + i * 154 + 10, cal_y + 23);
display.print(calNames[i]);
}
// Print Day/Date
for (int i = 0; i < 5; i++) {
int dayOfWk = (timeinfo.tm_wday + i) % 7;
display.setCursor(3, cal_head + i * 74 + 96);
display.print(weekDays[dayOfWk]);
}
}

View File

@ -4,8 +4,11 @@
NotableDay notableDays[] = { // Max characters in a notable day is 39 characters NotableDay notableDays[] = { // Max characters in a notable day is 39 characters
{1, 1, {"New Years Day!"}}, {1, 1, {"New Years Day!"}},
{1, 2, {"Ninth Day of Christmas"}},
{1, 4, {"National Trivia Day", "World Braille Day"}}, {1, 4, {"National Trivia Day", "World Braille Day"}},
{1, 5, {"The Twelfth Night of Christmas"}}, {1, 5, {"The Twelfth Night of Christmas"}},
{1, 7, {"Festa del Tricolore"}},
{1, 8, {"International Typing Day"}},
{1, 16, {"Blue Monday"}}, {1, 16, {"Blue Monday"}},
{1, 18, {"Whinnie the Pooh Day"}}, {1, 18, {"Whinnie the Pooh Day"}},
{1, 24, {"International Day of Education"}}, {1, 24, {"International Day of Education"}},
@ -38,12 +41,39 @@ NotableDay notableDays[] = { // Max characters in a notable day is 39 characters
{3, 25, {"Transatlantic Slavery Remembrance Day"}}, {3, 25, {"Transatlantic Slavery Remembrance Day"}},
{3, 30, {"International Zero Waste Day"}}, {3, 30, {"International Zero Waste Day"}},
{5, 5, {"St. Piran's Day"}}, {5, 5, {"St. Piran's Day"}},
{6, 5, {"St. Boniface's Day"}},
{11, 5, {"World Tsunami Awareness Day"}},
{11, 14, {"World Diabetes Day"}},
{11, 16, {"International Day for Tolerance"}},
{11, 19, {"World Toilet Day"}},
{11, 20, {"Africa Indistrialisation Day", "World Children's Day"}},
{11, 21, {"World Television Day", "World Philosophy Day"}},
{11, 24, {"World Conjoined Twins Day"}},
{11, 25, {"World Sustainable Transport Day"}},
{11, 28, {"Panaman Independence Day", "Bedfordshire Day"}},
{11, 29, {"Feast of Illuminata"}},
{11, 30, {"St. Andrew's Day", "Computer Security Day"}}, {11, 30, {"St. Andrew's Day", "Computer Security Day"}},
{12, 1, {"World AIDS Day"}},
{12, 2, {"Day for the Abolition of Slavery"}},
{12, 4, {"International Day of Banks"}},
{12, 5, {"World Soil Day"}},
{12, 7, {"International Civil Aviation Day"}},
{12, 9, {"International Anti-Corruption Day"}},
{12, 10, {"Human Rights Day"}},
{12, 11, {"International Mountain Day"}},
{12, 12, {"Internation Day of Neutrality"}},
{12, 18, {"Arabic Language Day", "International Migrants Day"}},
{12, 20, {"International Human Solidarity Day"}},
{12, 21, {"Abolishion of Slavery Day"}}, {12, 21, {"Abolishion of Slavery Day"}},
{12, 22, {"Dongzhi Festival"}},
{12, 23, {"Flag Flying Day (Sweden)", "Festivus"}},
{12, 24, {"Christmas Eve"}}, {12, 24, {"Christmas Eve"}},
{12, 25, {"Christmas Day"}}, {12, 25, {"Christmas Day"}},
{12, 26, {"Boxing Day"}}, {12, 26, {"Boxing Day"}},
{12, 27, {"Epidemic Preparedness Day"}}, {12, 27, {"Epidemic Preparedness Day"}},
{12, 28, {"Fourth Day of Christmas"}},
{12, 29, {"Fifth Day of Christmas"}},
{12, 30, {"Sixth Day of Christmas"}},
{12, 31, {"New Year's Eve"}} {12, 31, {"New Year's Eve"}}
}; };

View File

@ -0,0 +1,87 @@
#include <WiFiClientSecure.h>
#include <base64.h>
#include <Arduino.h>
#include <time.h>
#include <utility>
#include "nextcloud_cal.h"
#include "tls_certificates.h"
String downloadICSFile(const String& url, const String& startTime, const String& endTime, const String& username, const String& password) {
WiFiClientSecure client;
client.setCACert(letsencrypt_root_ca);
// Construct full URL with query parameters
String fullUrl = url + "?export&start=" + startTime + "&end=" + endTime + "&expand=1";
Serial.println("Requesting URL: " + fullUrl);
// Parse the host and path from the URL
int index = fullUrl.indexOf('/');
String host = fullUrl.substring(0, index);
String path = fullUrl.substring(index);
// Connect to the server
if (!client.connect(host.c_str(), 443)) {
Serial.println("Connection failed!");
Serial.print("Host: ");
Serial.println(host);
return "";
}
// Encode credentials in Base64
String credentials = username + ":" + password;
String authHeader = "Authorization: Basic " + base64::encode(credentials);
// Send the GET request with the Authorization header
client.print(String("GET ") + path + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
authHeader + "\r\n" +
"Connection: close\r\n\r\n");
// Read the response
String response = "";
while (client.connected() || client.available()) {
response += client.readString();
}
// Check for HTTP status code (assuming response includes headers)
int statusCodeStart = response.indexOf(" ") + 1;
int statusCodeEnd = response.indexOf(" ", statusCodeStart);
int statusCode = response.substring(statusCodeStart, statusCodeEnd).toInt();
if (statusCode == 200) {
// Strip headers if needed
int headerEnd = response.indexOf("\r\n\r\n");
if (headerEnd != -1) {
response = response.substring(headerEnd + 4);
}
return response;
} else {
Serial.println("Failed to fetch: " + String(statusCode));
return "";
}
}
StartEnd calculateStartEnd() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return {0, 0}; // Return a struct with zero timestamps if time retrieval fails
}
// Calculate 'start' - 00:01 today
timeinfo.tm_hour = 0;
timeinfo.tm_min = 1;
timeinfo.tm_sec = 0;
time_t startTimestamp = mktime(&timeinfo);
// Calculate 'end' - 23:59 10 days later
timeinfo.tm_hour = 23;
timeinfo.tm_min = 59;
timeinfo.tm_sec = 59;
timeinfo.tm_mday += 10; // Move forward 10 days
time_t endTimestamp = mktime(&timeinfo);
// Return a struct containing both Unix timestamps
StartEnd result = {startTimestamp, endTimestamp};
return result;
}

View File

@ -0,0 +1,15 @@
#ifndef NEXTCLOUD_CAL_H
#define NEXTCLOUD_CAL_H
#include <Arduino.h>
#include <time.h>
struct StartEnd {
time_t start;
time_t end;
};
struct StartEnd calculateStartEnd();
String downloadICSFile(const String& url, const String& startTime, const String& endTime, const String& username, const String& password);
#endif