From ced5b74ba123052c26019e3820f24738610bc511 Mon Sep 17 00:00:00 2001 From: rjwats Date: Mon, 20 Jan 2020 11:14:46 +0000 Subject: [PATCH] NTP Timezone & Enable/Disable Setting (#80) * quick and dirty WIP to investigate timezones, currently only building under esp8266 platform much of the status stuff has been stripped for now, to test the concepts * support set of common features across ESP32/ESP8266 WRT timezone and sntp return dates as ISO format strings * remove time library, and timelib fix which is no longer required * fix the icons * remove temporary changes to demo project --- data/config/ntpSettings.json | 6 +- interface/.env.development | 2 +- interface/src/constants/NTPStatus.js | 28 +- interface/src/constants/TZ.js | 475 ++++++++++++++++++++++++ interface/src/constants/TimeFormat.js | 3 +- interface/src/containers/NTPSettings.js | 4 +- interface/src/containers/NTPStatus.js | 60 ++- interface/src/forms/NTPSettingsForm.js | 54 ++- lib/framework/NTPSettingsService.cpp | 63 +--- lib/framework/NTPSettingsService.h | 24 +- lib/framework/NTPStatus.cpp | 37 +- lib/framework/NTPStatus.h | 5 +- lib/framework/WiFiScanner.h | 1 - platformio.ini | 2 - scripts/build_interface.py | 2 - scripts/timelib_fix.py | 40 -- 16 files changed, 621 insertions(+), 185 deletions(-) create mode 100644 interface/src/constants/TZ.js delete mode 100644 scripts/timelib_fix.py diff --git a/data/config/ntpSettings.json b/data/config/ntpSettings.json index 4d715f7..42fc0c7 100644 --- a/data/config/ntpSettings.json +++ b/data/config/ntpSettings.json @@ -1,4 +1,6 @@ { - "server": "pool.ntp.org", - "interval": 3600 + "enabled": true, + "server": "time.google.com", + "tz_label": "Europe/London", + "tz_format": "GMT0BST,M3.5.0/1,M10.5.0" } \ No newline at end of file diff --git a/interface/.env.development b/interface/.env.development index a45254b..aeadbc5 100644 --- a/interface/.env.development +++ b/interface/.env.development @@ -1,3 +1,3 @@ # Change the IP address to that of your ESP device to enable local development of the UI. # Remember to also enable CORS in platformio.ini before uploading the code to the device. -REACT_APP_ENDPOINT_ROOT=http://192.168.0.20/rest/ +REACT_APP_ENDPOINT_ROOT=http://192.168.0.29/rest/ diff --git a/interface/src/constants/NTPStatus.js b/interface/src/constants/NTPStatus.js index f630953..48d4565 100644 --- a/interface/src/constants/NTPStatus.js +++ b/interface/src/constants/NTPStatus.js @@ -1,31 +1,27 @@ import * as Highlight from '../constants/Highlight'; -export const NTP_TIME_NOT_SET = 0; -export const NTP_TIME_NEEDS_SYNC = 1; -export const NTP_TIME_SET = 2; +export const NTP_INACTIVE = 0; +export const NTP_ACTIVE = 1; -export const isSynchronized = ntpStatus => ntpStatus && (ntpStatus.status === NTP_TIME_NEEDS_SYNC || ntpStatus.status === NTP_TIME_SET); +export const isNtpActive = ntpStatus => ntpStatus && ntpStatus.status === NTP_ACTIVE; export const ntpStatusHighlight = ntpStatus => { - switch (ntpStatus.status){ - case NTP_TIME_SET: + switch (ntpStatus.status) { + case NTP_INACTIVE: + return Highlight.IDLE; + case NTP_ACTIVE: return Highlight.SUCCESS; - case NTP_TIME_NEEDS_SYNC: - return Highlight.WARN; - case NTP_TIME_NOT_SET: default: return Highlight.ERROR; } } export const ntpStatus = ntpStatus => { - switch (ntpStatus.status){ - case NTP_TIME_SET: - return "Synchronized"; - case NTP_TIME_NEEDS_SYNC: - return "Synchronization required"; - case NTP_TIME_NOT_SET: - return "Time not set" + switch (ntpStatus.status) { + case NTP_INACTIVE: + return "Inactive"; + case NTP_ACTIVE: + return "Active"; default: return "Unknown"; } diff --git a/interface/src/constants/TZ.js b/interface/src/constants/TZ.js new file mode 100644 index 0000000..bb410ff --- /dev/null +++ b/interface/src/constants/TZ.js @@ -0,0 +1,475 @@ +import React from 'react'; +import MenuItem from '@material-ui/core/MenuItem'; + +export const TIME_ZONES = { + "Africa/Abidjan": "GMT0", + "Africa/Accra": "GMT0", + "Africa/Addis_Ababa": "EAT-3", + "Africa/Algiers": "CET-1", + "Africa/Asmara": "EAT-3", + "Africa/Bamako": "GMT0", + "Africa/Bangui": "WAT-1", + "Africa/Banjul": "GMT0", + "Africa/Bissau": "GMT0", + "Africa/Blantyre": "CAT-2", + "Africa/Brazzaville": "WAT-1", + "Africa/Bujumbura": "CAT-2", + "Africa/Cairo": "EET-2", + "Africa/Casablanca": "<+01>-1", + "Africa/Ceuta": "CET-1CEST,M3.5.0,M10.5.0/3", + "Africa/Conakry": "GMT0", + "Africa/Dakar": "GMT0", + "Africa/Dar_es_Salaam": "EAT-3", + "Africa/Djibouti": "EAT-3", + "Africa/Douala": "WAT-1", + "Africa/El_Aaiun": "<+01>-1", + "Africa/Freetown": "GMT0", + "Africa/Gaborone": "CAT-2", + "Africa/Harare": "CAT-2", + "Africa/Johannesburg": "SAST-2", + "Africa/Juba": "EAT-3", + "Africa/Kampala": "EAT-3", + "Africa/Khartoum": "CAT-2", + "Africa/Kigali": "CAT-2", + "Africa/Kinshasa": "WAT-1", + "Africa/Lagos": "WAT-1", + "Africa/Libreville": "WAT-1", + "Africa/Lome": "GMT0", + "Africa/Luanda": "WAT-1", + "Africa/Lubumbashi": "CAT-2", + "Africa/Lusaka": "CAT-2", + "Africa/Malabo": "WAT-1", + "Africa/Maputo": "CAT-2", + "Africa/Maseru": "SAST-2", + "Africa/Mbabane": "SAST-2", + "Africa/Mogadishu": "EAT-3", + "Africa/Monrovia": "GMT0", + "Africa/Nairobi": "EAT-3", + "Africa/Ndjamena": "WAT-1", + "Africa/Niamey": "WAT-1", + "Africa/Nouakchott": "GMT0", + "Africa/Ouagadougou": "GMT0", + "Africa/Porto-Novo": "WAT-1", + "Africa/Sao_Tome": "GMT0", + "Africa/Tripoli": "EET-2", + "Africa/Tunis": "CET-1", + "Africa/Windhoek": "CAT-2", + "America/Adak": "HST10HDT,M3.2.0,M11.1.0", + "America/Anchorage": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Anguilla": "AST4", + "America/Antigua": "AST4", + "America/Araguaina": "<-03>3", + "America/Argentina/Buenos_Aires": "<-03>3", + "America/Argentina/Catamarca": "<-03>3", + "America/Argentina/Cordoba": "<-03>3", + "America/Argentina/Jujuy": "<-03>3", + "America/Argentina/La_Rioja": "<-03>3", + "America/Argentina/Mendoza": "<-03>3", + "America/Argentina/Rio_Gallegos": "<-03>3", + "America/Argentina/Salta": "<-03>3", + "America/Argentina/San_Juan": "<-03>3", + "America/Argentina/San_Luis": "<-03>3", + "America/Argentina/Tucuman": "<-03>3", + "America/Argentina/Ushuaia": "<-03>3", + "America/Aruba": "AST4", + "America/Asuncion": "<-04>4<-03>,M10.1.0/0,M3.4.0/0", + "America/Atikokan": "EST5", + "America/Bahia": "<-03>3", + "America/Bahia_Banderas": "CST6CDT,M4.1.0,M10.5.0", + "America/Barbados": "AST4", + "America/Belem": "<-03>3", + "America/Belize": "CST6", + "America/Blanc-Sablon": "AST4", + "America/Boa_Vista": "<-04>4", + "America/Bogota": "<-05>5", + "America/Boise": "MST7MDT,M3.2.0,M11.1.0", + "America/Cambridge_Bay": "MST7MDT,M3.2.0,M11.1.0", + "America/Campo_Grande": "<-04>4", + "America/Cancun": "EST5", + "America/Caracas": "<-04>4", + "America/Cayenne": "<-03>3", + "America/Cayman": "EST5", + "America/Chicago": "CST6CDT,M3.2.0,M11.1.0", + "America/Chihuahua": "MST7MDT,M4.1.0,M10.5.0", + "America/Costa_Rica": "CST6", + "America/Creston": "MST7", + "America/Cuiaba": "<-04>4", + "America/Curacao": "AST4", + "America/Danmarkshavn": "GMT0", + "America/Dawson": "PST8PDT,M3.2.0,M11.1.0", + "America/Dawson_Creek": "MST7", + "America/Denver": "MST7MDT,M3.2.0,M11.1.0", + "America/Detroit": "EST5EDT,M3.2.0,M11.1.0", + "America/Dominica": "AST4", + "America/Edmonton": "MST7MDT,M3.2.0,M11.1.0", + "America/Eirunepe": "<-05>5", + "America/El_Salvador": "CST6", + "America/Fortaleza": "<-03>3", + "America/Fort_Nelson": "MST7", + "America/Glace_Bay": "AST4ADT,M3.2.0,M11.1.0", + "America/Godthab": "<-03>3<-02>,M3.5.0/-2,M10.5.0/-1", + "America/Goose_Bay": "AST4ADT,M3.2.0,M11.1.0", + "America/Grand_Turk": "EST5EDT,M3.2.0,M11.1.0", + "America/Grenada": "AST4", + "America/Guadeloupe": "AST4", + "America/Guatemala": "CST6", + "America/Guayaquil": "<-05>5", + "America/Guyana": "<-04>4", + "America/Halifax": "AST4ADT,M3.2.0,M11.1.0", + "America/Havana": "CST5CDT,M3.2.0/0,M11.1.0/1", + "America/Hermosillo": "MST7", + "America/Indiana/Indianapolis": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Knox": "CST6CDT,M3.2.0,M11.1.0", + "America/Indiana/Marengo": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Petersburg": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Tell_City": "CST6CDT,M3.2.0,M11.1.0", + "America/Indiana/Vevay": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Vincennes": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Winamac": "EST5EDT,M3.2.0,M11.1.0", + "America/Inuvik": "MST7MDT,M3.2.0,M11.1.0", + "America/Iqaluit": "EST5EDT,M3.2.0,M11.1.0", + "America/Jamaica": "EST5", + "America/Juneau": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Kentucky/Louisville": "EST5EDT,M3.2.0,M11.1.0", + "America/Kentucky/Monticello": "EST5EDT,M3.2.0,M11.1.0", + "America/Kralendijk": "AST4", + "America/La_Paz": "<-04>4", + "America/Lima": "<-05>5", + "America/Los_Angeles": "PST8PDT,M3.2.0,M11.1.0", + "America/Lower_Princes": "AST4", + "America/Maceio": "<-03>3", + "America/Managua": "CST6", + "America/Manaus": "<-04>4", + "America/Marigot": "AST4", + "America/Martinique": "AST4", + "America/Matamoros": "CST6CDT,M3.2.0,M11.1.0", + "America/Mazatlan": "MST7MDT,M4.1.0,M10.5.0", + "America/Menominee": "CST6CDT,M3.2.0,M11.1.0", + "America/Merida": "CST6CDT,M4.1.0,M10.5.0", + "America/Metlakatla": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Mexico_City": "CST6CDT,M4.1.0,M10.5.0", + "America/Miquelon": "<-03>3<-02>,M3.2.0,M11.1.0", + "America/Moncton": "AST4ADT,M3.2.0,M11.1.0", + "America/Monterrey": "CST6CDT,M4.1.0,M10.5.0", + "America/Montevideo": "<-03>3", + "America/Montreal": "EST5EDT,M3.2.0,M11.1.0", + "America/Montserrat": "AST4", + "America/Nassau": "EST5EDT,M3.2.0,M11.1.0", + "America/New_York": "EST5EDT,M3.2.0,M11.1.0", + "America/Nipigon": "EST5EDT,M3.2.0,M11.1.0", + "America/Nome": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Noronha": "<-02>2", + "America/North_Dakota/Beulah": "CST6CDT,M3.2.0,M11.1.0", + "America/North_Dakota/Center": "CST6CDT,M3.2.0,M11.1.0", + "America/North_Dakota/New_Salem": "CST6CDT,M3.2.0,M11.1.0", + "America/Ojinaga": "MST7MDT,M3.2.0,M11.1.0", + "America/Panama": "EST5", + "America/Pangnirtung": "EST5EDT,M3.2.0,M11.1.0", + "America/Paramaribo": "<-03>3", + "America/Phoenix": "MST7", + "America/Port-au-Prince": "EST5EDT,M3.2.0,M11.1.0", + "America/Port_of_Spain": "AST4", + "America/Porto_Velho": "<-04>4", + "America/Puerto_Rico": "AST4", + "America/Punta_Arenas": "<-03>3", + "America/Rainy_River": "CST6CDT,M3.2.0,M11.1.0", + "America/Rankin_Inlet": "CST6CDT,M3.2.0,M11.1.0", + "America/Recife": "<-03>3", + "America/Regina": "CST6", + "America/Resolute": "CST6CDT,M3.2.0,M11.1.0", + "America/Rio_Branco": "<-05>5", + "America/Santarem": "<-03>3", + "America/Santiago": "<-04>4<-03>,M9.1.6/24,M4.1.6/24", + "America/Santo_Domingo": "AST4", + "America/Sao_Paulo": "<-03>3", + "America/Scoresbysund": "<-01>1<+00>,M3.5.0/0,M10.5.0/1", + "America/Sitka": "AKST9AKDT,M3.2.0,M11.1.0", + "America/St_Barthelemy": "AST4", + "America/St_Johns": "NST3:30NDT,M3.2.0,M11.1.0", + "America/St_Kitts": "AST4", + "America/St_Lucia": "AST4", + "America/St_Thomas": "AST4", + "America/St_Vincent": "AST4", + "America/Swift_Current": "CST6", + "America/Tegucigalpa": "CST6", + "America/Thule": "AST4ADT,M3.2.0,M11.1.0", + "America/Thunder_Bay": "EST5EDT,M3.2.0,M11.1.0", + "America/Tijuana": "PST8PDT,M3.2.0,M11.1.0", + "America/Toronto": "EST5EDT,M3.2.0,M11.1.0", + "America/Tortola": "AST4", + "America/Vancouver": "PST8PDT,M3.2.0,M11.1.0", + "America/Whitehorse": "PST8PDT,M3.2.0,M11.1.0", + "America/Winnipeg": "CST6CDT,M3.2.0,M11.1.0", + "America/Yakutat": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Yellowknife": "MST7MDT,M3.2.0,M11.1.0", + "Antarctica/Casey": "<+08>-8", + "Antarctica/Davis": "<+07>-7", + "Antarctica/DumontDUrville": "<+10>-10", + "Antarctica/Macquarie": "<+11>-11", + "Antarctica/Mawson": "<+05>-5", + "Antarctica/McMurdo": "NZST-12NZDT,M9.5.0,M4.1.0/3", + "Antarctica/Palmer": "<-03>3", + "Antarctica/Rothera": "<-03>3", + "Antarctica/Syowa": "<+03>-3", + "Antarctica/Troll": "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3", + "Antarctica/Vostok": "<+06>-6", + "Arctic/Longyearbyen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Aden": "<+03>-3", + "Asia/Almaty": "<+06>-6", + "Asia/Amman": "EET-2EEST,M3.5.4/24,M10.5.5/1", + "Asia/Anadyr": "<+12>-12", + "Asia/Aqtau": "<+05>-5", + "Asia/Aqtobe": "<+05>-5", + "Asia/Ashgabat": "<+05>-5", + "Asia/Atyrau": "<+05>-5", + "Asia/Baghdad": "<+03>-3", + "Asia/Bahrain": "<+03>-3", + "Asia/Baku": "<+04>-4", + "Asia/Bangkok": "<+07>-7", + "Asia/Barnaul": "<+07>-7", + "Asia/Beirut": "EET-2EEST,M3.5.0/0,M10.5.0/0", + "Asia/Bishkek": "<+06>-6", + "Asia/Brunei": "<+08>-8", + "Asia/Chita": "<+09>-9", + "Asia/Choibalsan": "<+08>-8", + "Asia/Colombo": "<+0530>-5:30", + "Asia/Damascus": "EET-2EEST,M3.5.5/0,M10.5.5/0", + "Asia/Dhaka": "<+06>-6", + "Asia/Dili": "<+09>-9", + "Asia/Dubai": "<+04>-4", + "Asia/Dushanbe": "<+05>-5", + "Asia/Famagusta": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Asia/Gaza": "EET-2EEST,M3.5.5/0,M10.5.6/1", + "Asia/Hebron": "EET-2EEST,M3.5.5/0,M10.5.6/1", + "Asia/Ho_Chi_Minh": "<+07>-7", + "Asia/Hong_Kong": "HKT-8", + "Asia/Hovd": "<+07>-7", + "Asia/Irkutsk": "<+08>-8", + "Asia/Jakarta": "WIB-7", + "Asia/Jayapura": "WIT-9", + "Asia/Jerusalem": "IST-2IDT,M3.4.4/26,M10.5.0", + "Asia/Kabul": "<+0430>-4:30", + "Asia/Kamchatka": "<+12>-12", + "Asia/Karachi": "PKT-5", + "Asia/Kathmandu": "<+0545>-5:45", + "Asia/Khandyga": "<+09>-9", + "Asia/Kolkata": "IST-5:30", + "Asia/Krasnoyarsk": "<+07>-7", + "Asia/Kuala_Lumpur": "<+08>-8", + "Asia/Kuching": "<+08>-8", + "Asia/Kuwait": "<+03>-3", + "Asia/Macau": "CST-8", + "Asia/Magadan": "<+11>-11", + "Asia/Makassar": "WITA-8", + "Asia/Manila": "PST-8", + "Asia/Muscat": "<+04>-4", + "Asia/Nicosia": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Asia/Novokuznetsk": "<+07>-7", + "Asia/Novosibirsk": "<+07>-7", + "Asia/Omsk": "<+06>-6", + "Asia/Oral": "<+05>-5", + "Asia/Phnom_Penh": "<+07>-7", + "Asia/Pontianak": "WIB-7", + "Asia/Pyongyang": "KST-9", + "Asia/Qatar": "<+03>-3", + "Asia/Qyzylorda": "<+05>-5", + "Asia/Riyadh": "<+03>-3", + "Asia/Sakhalin": "<+11>-11", + "Asia/Samarkand": "<+05>-5", + "Asia/Seoul": "KST-9", + "Asia/Shanghai": "CST-8", + "Asia/Singapore": "<+08>-8", + "Asia/Srednekolymsk": "<+11>-11", + "Asia/Taipei": "CST-8", + "Asia/Tashkent": "<+05>-5", + "Asia/Tbilisi": "<+04>-4", + "Asia/Tehran": "<+0330>-3:30<+0430>,J79/24,J263/24", + "Asia/Thimphu": "<+06>-6", + "Asia/Tokyo": "JST-9", + "Asia/Tomsk": "<+07>-7", + "Asia/Ulaanbaatar": "<+08>-8", + "Asia/Urumqi": "<+06>-6", + "Asia/Ust-Nera": "<+10>-10", + "Asia/Vientiane": "<+07>-7", + "Asia/Vladivostok": "<+10>-10", + "Asia/Yakutsk": "<+09>-9", + "Asia/Yangon": "<+0630>-6:30", + "Asia/Yekaterinburg": "<+05>-5", + "Asia/Yerevan": "<+04>-4", + "Atlantic/Azores": "<-01>1<+00>,M3.5.0/0,M10.5.0/1", + "Atlantic/Bermuda": "AST4ADT,M3.2.0,M11.1.0", + "Atlantic/Canary": "WET0WEST,M3.5.0/1,M10.5.0", + "Atlantic/Cape_Verde": "<-01>1", + "Atlantic/Faroe": "WET0WEST,M3.5.0/1,M10.5.0", + "Atlantic/Madeira": "WET0WEST,M3.5.0/1,M10.5.0", + "Atlantic/Reykjavik": "GMT0", + "Atlantic/South_Georgia": "<-02>2", + "Atlantic/Stanley": "<-03>3", + "Atlantic/St_Helena": "GMT0", + "Australia/Adelaide": "ACST-9:30ACDT,M10.1.0,M4.1.0/3", + "Australia/Brisbane": "AEST-10", + "Australia/Broken_Hill": "ACST-9:30ACDT,M10.1.0,M4.1.0/3", + "Australia/Currie": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Australia/Darwin": "ACST-9:30", + "Australia/Eucla": "<+0845>-8:45", + "Australia/Hobart": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Australia/Lindeman": "AEST-10", + "Australia/Lord_Howe": "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0", + "Australia/Melbourne": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Australia/Perth": "AWST-8", + "Australia/Sydney": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Europe/Amsterdam": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Andorra": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Astrakhan": "<+04>-4", + "Europe/Athens": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Belgrade": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Berlin": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Bratislava": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Brussels": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Bucharest": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Budapest": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Busingen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Chisinau": "EET-2EEST,M3.5.0,M10.5.0/3", + "Europe/Copenhagen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Dublin": "IST-1GMT0,M10.5.0,M3.5.0/1", + "Europe/Gibraltar": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Guernsey": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Helsinki": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Isle_of_Man": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Istanbul": "<+03>-3", + "Europe/Jersey": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Kaliningrad": "EET-2", + "Europe/Kiev": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Kirov": "<+03>-3", + "Europe/Lisbon": "WET0WEST,M3.5.0/1,M10.5.0", + "Europe/Ljubljana": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/London": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Luxembourg": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Madrid": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Malta": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Mariehamn": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Minsk": "<+03>-3", + "Europe/Monaco": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Moscow": "MSK-3", + "Europe/Oslo": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Paris": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Podgorica": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Prague": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Riga": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Rome": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Samara": "<+04>-4", + "Europe/San_Marino": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Sarajevo": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Saratov": "<+04>-4", + "Europe/Simferopol": "MSK-3", + "Europe/Skopje": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Sofia": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Stockholm": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Tallinn": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Tirane": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Ulyanovsk": "<+04>-4", + "Europe/Uzhgorod": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Vaduz": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Vatican": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Vienna": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Vilnius": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Volgograd": "<+04>-4", + "Europe/Warsaw": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Zagreb": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Zaporozhye": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Zurich": "CET-1CEST,M3.5.0,M10.5.0/3", + "Indian/Antananarivo": "EAT-3", + "Indian/Chagos": "<+06>-6", + "Indian/Christmas": "<+07>-7", + "Indian/Cocos": "<+0630>-6:30", + "Indian/Comoro": "EAT-3", + "Indian/Kerguelen": "<+05>-5", + "Indian/Mahe": "<+04>-4", + "Indian/Maldives": "<+05>-5", + "Indian/Mauritius": "<+04>-4", + "Indian/Mayotte": "EAT-3", + "Indian/Reunion": "<+04>-4", + "Pacific/Apia": "<+13>-13<+14>,M9.5.0/3,M4.1.0/4", + "Pacific/Auckland": "NZST-12NZDT,M9.5.0,M4.1.0/3", + "Pacific/Bougainville": "<+11>-11", + "Pacific/Chatham": "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45", + "Pacific/Chuuk": "<+10>-10", + "Pacific/Easter": "<-06>6<-05>,M9.1.6/22,M4.1.6/22", + "Pacific/Efate": "<+11>-11", + "Pacific/Enderbury": "<+13>-13", + "Pacific/Fakaofo": "<+13>-13", + "Pacific/Fiji": "<+12>-12<+13>,M11.2.0,M1.2.3/99", + "Pacific/Funafuti": "<+12>-12", + "Pacific/Galapagos": "<-06>6", + "Pacific/Gambier": "<-09>9", + "Pacific/Guadalcanal": "<+11>-11", + "Pacific/Guam": "ChST-10", + "Pacific/Honolulu": "HST10", + "Pacific/Kiritimati": "<+14>-14", + "Pacific/Kosrae": "<+11>-11", + "Pacific/Kwajalein": "<+12>-12", + "Pacific/Majuro": "<+12>-12", + "Pacific/Marquesas": "<-0930>9:30", + "Pacific/Midway": "SST11", + "Pacific/Nauru": "<+12>-12", + "Pacific/Niue": "<-11>11", + "Pacific/Norfolk": "<+11>-11<+12>,M10.1.0,M4.1.0/3", + "Pacific/Noumea": "<+11>-11", + "Pacific/Pago_Pago": "SST11", + "Pacific/Palau": "<+09>-9", + "Pacific/Pitcairn": "<-08>8", + "Pacific/Pohnpei": "<+11>-11", + "Pacific/Port_Moresby": "<+10>-10", + "Pacific/Rarotonga": "<-10>10", + "Pacific/Saipan": "ChST-10", + "Pacific/Tahiti": "<-10>10", + "Pacific/Tarawa": "<+12>-12", + "Pacific/Tongatapu": "<+13>-13", + "Pacific/Wake": "<+12>-12", + "Pacific/Wallis": "<+12>-12", + "Etc/GMT": "GMT0", + "Etc/GMT-0": "GMT0", + "Etc/GMT-1": "<+01>-1", + "Etc/GMT-2": "<+02>-2", + "Etc/GMT-3": "<+03>-3", + "Etc/GMT-4": "<+04>-4", + "Etc/GMT-5": "<+05>-5", + "Etc/GMT-6": "<+06>-6", + "Etc/GMT-7": "<+07>-7", + "Etc/GMT-8": "<+08>-8", + "Etc/GMT-9": "<+09>-9", + "Etc/GMT-10": "<+10>-10", + "Etc/GMT-11": "<+11>-11", + "Etc/GMT-12": "<+12>-12", + "Etc/GMT-13": "<+13>-13", + "Etc/GMT-14": "<+14>-14", + "Etc/GMT0": "GMT0", + "Etc/GMT+0": "GMT0", + "Etc/GMT+1": "<-01>1", + "Etc/GMT+2": "<-02>2", + "Etc/GMT+3": "<-03>3", + "Etc/GMT+4": "<-04>4", + "Etc/GMT+5": "<-05>5", + "Etc/GMT+6": "<-06>6", + "Etc/GMT+7": "<-07>7", + "Etc/GMT+8": "<-08>8", + "Etc/GMT+9": "<-09>9", + "Etc/GMT+10": "<-10>10", + "Etc/GMT+11": "<-11>11", + "Etc/GMT+12": "<-12>12", + "Etc/UCT": "UTC0", + "Etc/UTC": "UTC0", + "Etc/Greenwich": "GMT0", + "Etc/Universal": "UTC0", + "Etc/Zulu": "UTC0" +} + +export function selectedTimeZone(label, format){ + return TIME_ZONES[label] === format ? label : undefined; +} + +export function timeZoneSelectItems() { + return Object.keys(TIME_ZONES).map(label => ( + {label} + )); +} diff --git a/interface/src/constants/TimeFormat.js b/interface/src/constants/TimeFormat.js index 47b71f0..e2fe1d5 100644 --- a/interface/src/constants/TimeFormat.js +++ b/interface/src/constants/TimeFormat.js @@ -1,4 +1,3 @@ import moment from 'moment'; -export const TIME_AND_DATE = 'DD/MM/YYYY HH:mm:ss'; -export const unixTimeToTimeAndDate = unixTime => moment.unix(unixTime).format(TIME_AND_DATE); +export const formatIsoDateTime = isoDateString => moment.parseZone(isoDateString).format('ll @ HH:mm:ss'); diff --git a/interface/src/containers/NTPSettings.js b/interface/src/containers/NTPSettings.js index 9393545..6d1b968 100644 --- a/interface/src/containers/NTPSettings.js +++ b/interface/src/containers/NTPSettings.js @@ -13,7 +13,7 @@ class NTPSettings extends Component { } render() { - const { fetched, errorMessage, data, saveData, loadData, handleValueChange } = this.props; + const { fetched, errorMessage, data, saveData, setData, loadData, handleValueChange, handleCheckboxChange } = this.props; return ( } /> diff --git a/interface/src/containers/NTPStatus.js b/interface/src/containers/NTPStatus.js index 77e35cf..8029ee0 100644 --- a/interface/src/containers/NTPStatus.js +++ b/interface/src/containers/NTPStatus.js @@ -8,17 +8,17 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar'; import ListItemText from '@material-ui/core/ListItemText'; import Avatar from '@material-ui/core/Avatar'; import Divider from '@material-ui/core/Divider'; + import SwapVerticalCircleIcon from '@material-ui/icons/SwapVerticalCircle'; import AccessTimeIcon from '@material-ui/icons/AccessTime'; import DNSIcon from '@material-ui/icons/Dns'; -import TimerIcon from '@material-ui/icons/Timer'; import UpdateIcon from '@material-ui/icons/Update'; import AvTimerIcon from '@material-ui/icons/AvTimer'; import RefreshIcon from '@material-ui/icons/Refresh'; -import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus'; +import { isNtpActive, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus'; import * as Highlight from '../constants/Highlight'; -import { unixTimeToTimeAndDate } from '../constants/TimeFormat'; +import { formatIsoDateTime } from '../constants/TimeFormat'; import { NTP_STATUS_ENDPOINT } from '../constants/Endpoints'; import { restComponent } from '../components/RestComponent'; import LoadingNotification from '../components/LoadingNotification'; @@ -60,28 +60,29 @@ class NTPStatus extends Component { - {isSynchronized(data) && - - - - - - - - - - - - - - - - - 0 ? unixTimeToTimeAndDate(data.last_sync) : "never"} /> - - - - } + { + isNtpActive(data) && ( + + + + + + + + + + + + + + + + + + + + + )} @@ -91,15 +92,6 @@ class NTPStatus extends Component { - - - - - - - - - diff --git a/interface/src/forms/NTPSettingsForm.js b/interface/src/forms/NTPSettingsForm.js index a99ae92..1079b3e 100644 --- a/interface/src/forms/NTPSettingsForm.js +++ b/interface/src/forms/NTPSettingsForm.js @@ -1,16 +1,25 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; +import { TextValidator, ValidatorForm, SelectValidator } from 'react-material-ui-form-validator'; import { withStyles } from '@material-ui/core/styles'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import Switch from '@material-ui/core/Switch'; import Button from '@material-ui/core/Button'; import SaveIcon from '@material-ui/icons/Save'; import isIP from '../validators/isIP'; import isHostname from '../validators/isHostname'; import or from '../validators/or'; +import { timeZoneSelectItems, selectedTimeZone, TIME_ZONES } from '../constants/TZ'; const styles = theme => ({ + switchControl: { + width: "100%", + marginTop: theme.spacing(2), + marginBottom: theme.spacing(0.5) + }, textField: { width: "100%" }, @@ -26,10 +35,30 @@ class NTPSettingsForm extends React.Component { ValidatorForm.addValidationRule('isIPOrHostname', or(isIP, isHostname)); } + changeTimeZone = (event) => { + const { ntpSettings, setData } = this.props; + setData({ + ...ntpSettings, + tz_label: event.target.value, + tz_format: TIME_ZONES[event.target.value] + }); + } + render() { - const { classes, ntpSettings, handleValueChange, onSubmit, onReset } = this.props; + const { classes, ntpSettings, handleValueChange, handleCheckboxChange, onSubmit, onReset } = this.props; return ( + + } + label="Enable NTP?" + /> - + > + Time zone... + {timeZoneSelectItems()} + diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index f727375..5835e36 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -14,10 +14,6 @@ NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityM _onStationModeGotIPHandler = WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1)); #endif - NTP.onNTPSyncEvent([this](NTPSyncEvent_t ntpEvent) { - _ntpEvent = ntpEvent; - _syncEventTriggered = true; - }); } NTPSettingsService::~NTPSettingsService() { @@ -29,38 +25,20 @@ void NTPSettingsService::loop() { _reconfigureNTP = false; configureNTP(); } - - // output sync event to serial - if (_syncEventTriggered) { - processSyncEvent(_ntpEvent); - _syncEventTriggered = false; - } - - // keep time synchronized in background - now(); } void NTPSettingsService::readFromJsonObject(JsonObject& root) { + _enabled = root["enabled"] | NTP_SETTINGS_SERVICE_DEFAULT_ENABLED; _server = root["server"] | NTP_SETTINGS_SERVICE_DEFAULT_SERVER; - _interval = root["interval"]; - - // validate server is specified, resorting to default - _server.trim(); - if (!_server) { - _server = NTP_SETTINGS_SERVICE_DEFAULT_SERVER; - } - - // make sure interval is in bounds - if (_interval < NTP_SETTINGS_MIN_INTERVAL) { - _interval = NTP_SETTINGS_MIN_INTERVAL; - } else if (_interval > NTP_SETTINGS_MAX_INTERVAL) { - _interval = NTP_SETTINGS_MAX_INTERVAL; - } + _tzLabel = root["tz_label"] | NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_LABEL; + _tzFormat = root["tz_format"] | NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_FORMAT; } void NTPSettingsService::writeToJsonObject(JsonObject& root) { + root["enabled"] = _enabled; root["server"] = _server; - root["interval"] = _interval; + root["tz_label"] = _tzLabel; + root["tz_format"] = _tzFormat; } void NTPSettingsService::onConfigUpdated() { @@ -76,7 +54,7 @@ void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t i void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { Serial.printf("WiFi connection dropped, stopping NTP.\n"); _reconfigureNTP = false; - NTP.stop(); + sntp_stop(); } #elif defined(ESP8266) void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { @@ -87,30 +65,19 @@ void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& eve void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { Serial.printf("WiFi connection dropped, stopping NTP.\n"); _reconfigureNTP = false; - NTP.stop(); + sntp_stop(); } #endif void NTPSettingsService::configureNTP() { Serial.println("Configuring NTP..."); - - // disable sync - NTP.stop(); - - // enable sync - NTP.begin(_server); - NTP.setInterval(_interval); -} - -void NTPSettingsService::processSyncEvent(NTPSyncEvent_t ntpEvent) { - if (ntpEvent) { - Serial.print("Time Sync error: "); - if (ntpEvent == noResponse) - Serial.println("NTP server not reachable"); - else if (ntpEvent == invalidAddress) - Serial.println("Invalid NTP server address"); + if (_enabled) { +#ifdef ESP32 + configTzTime(_tzFormat.c_str(), _server.c_str()); +#elif defined(ESP8266) + configTime(_tzFormat.c_str(), _server.c_str()); +#endif } else { - Serial.print("Got NTP time: "); - Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync())); + sntp_stop(); } } diff --git a/lib/framework/NTPSettingsService.h b/lib/framework/NTPSettingsService.h index 6c49d1e..940f57f 100644 --- a/lib/framework/NTPSettingsService.h +++ b/lib/framework/NTPSettingsService.h @@ -3,12 +3,18 @@ #include -#include -#include +#include +#ifdef ESP32 +#include +#elif defined(ESP8266) +#include +#endif -// default time server -#define NTP_SETTINGS_SERVICE_DEFAULT_SERVER "pool.ntp.org" -#define NTP_SETTINGS_SERVICE_DEFAULT_INTERVAL 3600 +// default time zone +#define NTP_SETTINGS_SERVICE_DEFAULT_ENABLED true +#define NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_LABEL "Europe/London" +#define NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0" +#define NTP_SETTINGS_SERVICE_DEFAULT_SERVER "time.google.com" // min poll delay of 60 secs, max 1 day #define NTP_SETTINGS_MIN_INTERVAL 60 @@ -28,14 +34,15 @@ class NTPSettingsService : public AdminSettingsService { void readFromJsonObject(JsonObject& root); void writeToJsonObject(JsonObject& root); void onConfigUpdated(); + void receivedNTPtime(); private: + bool _enabled; + String _tzLabel; + String _tzFormat; String _server; - int _interval; bool _reconfigureNTP = false; - bool _syncEventTriggered = false; - NTPSyncEvent_t _ntpEvent; #ifdef ESP32 void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); @@ -49,7 +56,6 @@ class NTPSettingsService : public AdminSettingsService { #endif void configureNTP(); - void processSyncEvent(NTPSyncEvent_t ntpEvent); }; #endif // end NTPSettingsService_h diff --git a/lib/framework/NTPStatus.cpp b/lib/framework/NTPStatus.cpp index 2f100e6..2275d2f 100644 --- a/lib/framework/NTPStatus.cpp +++ b/lib/framework/NTPStatus.cpp @@ -7,24 +7,33 @@ NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { AuthenticationPredicates::IS_AUTHENTICATED)); } +String toISOString(tm* time, bool incOffset) { + char time_string[25]; + strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time); + return String(time_string); +} + void NTPStatus::ntpStatus(AsyncWebServerRequest* request) { AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); JsonObject root = response->getRoot(); - // request time now first, this can sometimes force a sync - time_t timeNow = now(); - timeStatus_t status = timeStatus(); - time_t lastSync = NTP.getLastNTPSync(); - root["status"] = (int)status; - root["last_sync"] = lastSync; - root["server"] = NTP.getNtpServerName(); - root["interval"] = NTP.getInterval(); - root["uptime"] = NTP.getUptime(); - - // only add now to response if we have successfully synced - if (status != timeNotSet) { - root["now"] = timeNow; - } + // grab the current instant in unix seconds + time_t now = time(nullptr); + + // only provide enabled/disabled status for now + root["status"] = sntp_enabled() ? 1 : 0; + + // the current time in UTC + root["time_utc"] = toISOString(gmtime(&now), false); + + // local time as ISO String with TZ + root["time_local"] = toISOString(localtime(&now), true); + + // the sntp server name + root["server"] = sntp_getservername(0); + + // device uptime in seconds + root["uptime"] = millis() / 1000; response->setLength(); request->send(response); diff --git a/lib/framework/NTPStatus.h b/lib/framework/NTPStatus.h index 52ff6d7..7bb9180 100644 --- a/lib/framework/NTPStatus.h +++ b/lib/framework/NTPStatus.h @@ -1,20 +1,21 @@ #ifndef NTPStatus_h #define NTPStatus_h +#include #ifdef ESP32 #include #include +#include #elif defined(ESP8266) #include #include +#include #endif #include #include #include -#include #include -#include #define MAX_NTP_STATUS_SIZE 1024 #define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus" diff --git a/lib/framework/WiFiScanner.h b/lib/framework/WiFiScanner.h index f0f1724..6f91030 100644 --- a/lib/framework/WiFiScanner.h +++ b/lib/framework/WiFiScanner.h @@ -13,7 +13,6 @@ #include #include #include -#include #define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks" #define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks" diff --git a/platformio.ini b/platformio.ini index bf652f2..5b77e62 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,11 +21,9 @@ framework = arduino monitor_speed = 115200 extra_scripts = - pre:scripts/timelib_fix.py pre:scripts/build_interface.py lib_deps = - NtpClientLib@>=2.5.1,<3.0.0 ArduinoJson@>=6.0.0,<7.0.0 ESP Async WebServer@>=1.2.0,<2.0.0 diff --git a/scripts/build_interface.py b/scripts/build_interface.py index 0c927cf..84b7c1d 100644 --- a/scripts/build_interface.py +++ b/scripts/build_interface.py @@ -32,5 +32,3 @@ if (len(BUILD_TARGETS) == 0 or "upload" in BUILD_TARGETS): buildWeb() else: print("Skipping build interface step for target(s): " + ", ".join(BUILD_TARGETS)) - - diff --git a/scripts/timelib_fix.py b/scripts/timelib_fix.py deleted file mode 100644 index 35957ad..0000000 --- a/scripts/timelib_fix.py +++ /dev/null @@ -1,40 +0,0 @@ -import os -import sys -import re - -Import("env") - -# Find files under 'root' of a given 'fileName' in directories matching 'subDirectoryPattern' -# This will allow us to safely find the offending Time.h file for removal prior to building -def findSubDirectoryFiles(root, subDirectoryPattern, fileName): - subDirectories = os.listdir(root) - subDirectories = filter(lambda d: re.match(subDirectoryPattern, d), subDirectories) - result = [] - for subDirectory in subDirectories: - candidateFile = os.path.join(root, subDirectory, fileName) - if os.path.isfile(candidateFile): - result.append(candidateFile) - return result - -def deleteTimeHeader(libDepsDir): - timeHeaderFile = "Time.h" - timeLibDirectoryPattern = "Time(_ID[0-9]+)?" - - # delete the file, as long as we only find one - if os.path.isdir(libDepsDir) : - deletionCandidates = findSubDirectoryFiles(libDepsDir, timeLibDirectoryPattern, timeHeaderFile) - numDeletionCandidates = len(deletionCandidates) - if numDeletionCandidates == 1: - os.remove(deletionCandidates[0]) - elif numDeletionCandidates > 1: - os.write(2, "Can\'t delete Time.h, more than one instance found:\n" + "\n".join(deletionCandidates)) - sys.exit(1) - -# old lib deps directory -deleteTimeHeader(os.path.join(env.subst("$PROJECT_DIR"), ".piolibdeps")) - -# pre 4.x lib deps directory -deleteTimeHeader(os.path.join(env.subst("$PROJECTLIBDEPS_DIR"), env.subst("$PIOENV"))) - -# >4.x lib deps directory -deleteTimeHeader(os.path.join(env.subst("$PROJECT_LIBDEPS_DIR"), env.subst("$PIOENV")))