diff --git a/include/SunPosition.h b/include/SunPosition.h new file mode 100644 index 0000000..49c5c71 --- /dev/null +++ b/include/SunPosition.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +#define SUNPOS_UPDATE_INTERVAL 60000l + +class SunPositionClass { +public: + SunPositionClass(); + void init(); + void loop(); + + bool isDayPeriod(); + bool sunsetTime(struct tm* info); + bool sunriseTime(struct tm* info); + +private: + void updateSunData(); + + SunSet _sun; + bool _isDayPeriod = true; + uint _sunriseMinutes = 0; + uint _sunsetMinutes = 0; + + uint32_t _lastUpdate = 0; + bool _isValidInfo = false; +}; + +extern SunPositionClass SunPosition; \ No newline at end of file diff --git a/include/Utils.h b/include/Utils.h index dbcea81..33887ff 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -7,4 +7,5 @@ class Utils { public: static uint32_t getChipId(); static uint64_t generateDtuSerial(); + static int getTimezoneOffset(); }; diff --git a/platformio.ini b/platformio.ini index 7b328b8..8686a37 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,6 +27,7 @@ lib_deps = https://github.com/bertmelis/espMqttClient.git#v1.3.3 nrf24/RF24 @ ^1.4.5 olikraus/U8g2 @ ^2.34.13 + buelowp/sunset @ ^1.1.3 extra_scripts = pre:auto_firmware_version.py diff --git a/src/SunPosition.cpp b/src/SunPosition.cpp new file mode 100644 index 0000000..1db5844 --- /dev/null +++ b/src/SunPosition.cpp @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Thomas Basler and others + */ +#include "SunPosition.h" +#include "Configuration.h" +#include "Utils.h" + +SunPositionClass SunPosition; + +SunPositionClass::SunPositionClass() +{ +} + +void SunPositionClass::init() +{ +} + +void SunPositionClass::loop() +{ + if (millis() - _lastUpdate > SUNPOS_UPDATE_INTERVAL) { + updateSunData(); + _lastUpdate = millis(); + } +} + +bool SunPositionClass::isDayPeriod() +{ + return _isDayPeriod; +} + +void SunPositionClass::updateSunData() +{ + CONFIG_T const& config = Configuration.get(); + int offset = Utils::getTimezoneOffset() / 3600; + _sun.setPosition(config.Ntp_Latitude, config.Ntp_Longitude, offset); + + struct tm timeinfo; + if (!getLocalTime(&timeinfo, 5)) { + _isDayPeriod = false; + _sunriseMinutes = 0; + _sunsetMinutes = 0; + _isValidInfo = false; + return; + } + + _sun.setCurrentDate(1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday); + _sunriseMinutes = (int)_sun.calcCustomSunrise(SunSet::SUNSET_NAUTICAL); + _sunsetMinutes = (int)_sun.calcCustomSunset(SunSet::SUNSET_NAUTICAL); + uint minutesPastMidnight = timeinfo.tm_hour * 60 + timeinfo.tm_min; + + _isDayPeriod = (minutesPastMidnight >= _sunriseMinutes) && (minutesPastMidnight < _sunsetMinutes); + _isValidInfo = true; +} + +bool SunPositionClass::sunsetTime(struct tm* info) +{ + // Get today's date + time_t aTime = time(NULL); + + // Set the time to midnight + struct tm* tm = localtime(&aTime); + tm->tm_sec = 0; + tm->tm_min = _sunsetMinutes; + tm->tm_hour = 0; + tm->tm_isdst = -1; + time_t midnight = mktime(tm); + + localtime_r(&midnight, info); + return _isValidInfo; +} + +bool SunPositionClass::sunriseTime(struct tm* info) +{ + // Get today's date + time_t aTime = time(NULL); + + // Set the time to midnight + struct tm* tm = localtime(&aTime); + tm->tm_sec = 0; + tm->tm_min = _sunriseMinutes; + tm->tm_hour = 0; + tm->tm_isdst = -1; + time_t midnight = mktime(tm); + + localtime_r(&midnight, info); + return _isValidInfo; +} \ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp index f1975af..db8363a 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -35,4 +35,21 @@ uint64_t Utils::generateDtuSerial() } return dtuId; +} + +int Utils::getTimezoneOffset() +{ + // see: https://stackoverflow.com/questions/13804095/get-the-time-zone-gmt-offset-in-c/44063597#44063597 + + time_t gmt, rawtime = time(NULL); + struct tm* ptm; + + struct tm gbuf; + ptm = gmtime_r(&rawtime, &gbuf); + + // Request that mktime() looksup dst in timezone database + ptm->tm_isdst = -1; + gmt = mktime(ptm); + + return static_cast(difftime(rawtime, gmt)); } \ No newline at end of file diff --git a/src/WebApi_ntp.cpp b/src/WebApi_ntp.cpp index 12ea4eb..836247a 100644 --- a/src/WebApi_ntp.cpp +++ b/src/WebApi_ntp.cpp @@ -5,6 +5,7 @@ #include "WebApi_ntp.h" #include "Configuration.h" #include "NtpSettings.h" +#include "SunPosition.h" #include "WebApi.h" #include "WebApi_errors.h" #include "helper.h" @@ -51,6 +52,16 @@ void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request) strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); root[F("ntp_localtime")] = timeStringBuff; + SunPosition.sunriseTime(&timeinfo); + strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); + root[F("sun_risetime")] = timeStringBuff; + + SunPosition.sunsetTime(&timeinfo); + strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); + root[F("sun_settime")] = timeStringBuff; + + root[F("sun_isDayPeriod")] = SunPosition.isDayPeriod(); + response->setLength(); request->send(response); } diff --git a/src/main.cpp b/src/main.cpp index 273a7bb..6191e4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ #include "NetworkSettings.h" #include "NtpSettings.h" #include "PinMapping.h" +#include "SunPosition.h" #include "Utils.h" #include "WebApi.h" #include "defaults.h" @@ -80,6 +81,11 @@ void setup() NtpSettings.init(); MessageOutput.println(F("done")); + // Initialize SunPosition + MessageOutput.print(F("Initialize SunPosition... ")); + SunPosition.init(); + MessageOutput.println(F("done")); + // Initialize MqTT MessageOutput.print(F("Initialize MqTT... ")); MqttSettings.init(); @@ -178,6 +184,8 @@ void loop() yield(); Display.loop(); yield(); + SunPosition.loop(); + yield(); MessageOutput.loop(); yield(); } \ No newline at end of file diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index a6682bc..b7bfa67 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -228,7 +228,12 @@ "Status": "Status", "Synced": "synchronisiert", "NotSynced": "nicht synchronisiert", - "LocalTime": "Lokale Uhrzeit" + "LocalTime": "Lokale Uhrzeit", + "Sunrise": "Nautische Morgendämmerung", + "Sunset": "Nautische Abenddämmerung", + "Mode": "Modus", + "Day": "Tag", + "Night": "Nacht" }, "mqttinfo": { "MqttInformation": "MQTT Informationen", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 2308b7c..a11b384 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -228,7 +228,12 @@ "Status": "Status", "Synced": "synced", "NotSynced": "not synced", - "LocalTime": "Local Time" + "LocalTime": "Local Time", + "Sunrise": "Nautical Sunrise", + "Sunset": "Nautical Sunset", + "Mode": "Mode", + "Day": "Day", + "Night": "Night" }, "mqttinfo": { "MqttInformation": "MQTT Information", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 51c74b5..f026fe5 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -228,7 +228,12 @@ "Status": "Statut", "Synced": "synchronisée", "NotSynced": "pas synchronisée", - "LocalTime": "Heure locale" + "LocalTime": "Heure locale", + "Sunrise": "Nautical Sunrise", + "Sunset": "Nautical Sunset", + "Mode": "Mode", + "Day": "Day", + "Night": "Night" }, "mqttinfo": { "MqttInformation": "MQTT Information", diff --git a/webapp/src/types/NtpStatus.ts b/webapp/src/types/NtpStatus.ts index 29b2d4a..5597575 100644 --- a/webapp/src/types/NtpStatus.ts +++ b/webapp/src/types/NtpStatus.ts @@ -4,4 +4,7 @@ export interface NtpStatus { ntp_timezone_descr: string ntp_status: boolean; ntp_localtime: string; + sun_risetime: string; + sun_settime: string; + sun_isDayPeriod: boolean; } \ No newline at end of file diff --git a/webapp/src/views/NtpInfoView.vue b/webapp/src/views/NtpInfoView.vue index 35d9e16..71cc8f2 100644 --- a/webapp/src/views/NtpInfoView.vue +++ b/webapp/src/views/NtpInfoView.vue @@ -39,6 +39,25 @@ {{ $t('ntpinfo.LocalTime') }} {{ ntpDataList.ntp_localtime }} + + + {{ $t('ntpinfo.Sunrise') }} + {{ ntpDataList.sun_risetime }} + + + {{ $t('ntpinfo.Sunset') }} + {{ ntpDataList.sun_settime }} + + + {{ $t('ntpinfo.Mode') }} + + {{ $t('ntpinfo.Day') }} + {{ $t('ntpinfo.Night') }} + +