new release
This commit is contained in:
@@ -1,14 +1,24 @@
|
||||
set(srcs
|
||||
"src/protocols.c"
|
||||
"src/mqtt.c"
|
||||
"src/date_time.c"
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"src/protocols.c"
|
||||
"src/json.c"
|
||||
"src/mqtt.c"
|
||||
INCLUDE_DIRS
|
||||
"include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"src"
|
||||
PRIV_REQUIRES
|
||||
nvs_flash
|
||||
mqtt
|
||||
cjson
|
||||
vfs
|
||||
spiffs
|
||||
REQUIRES
|
||||
logger
|
||||
network
|
||||
config
|
||||
evse
|
||||
peripherals
|
||||
ocpp
|
||||
auth
|
||||
)
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "src"
|
||||
PRIV_REQUIRES nvs_flash esp_http_server esp_netif esp_https_ota app_update json mqtt vfs spiffs
|
||||
REQUIRES config api logger)
|
||||
|
||||
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
ZONES_DIR = "/usr/share/zoneinfo/"
|
||||
ZONES = [
|
||||
"Africa/Abidjan",
|
||||
"Africa/Algiers",
|
||||
"Africa/Bissau",
|
||||
"Africa/Cairo",
|
||||
"Africa/Casablanca",
|
||||
"Africa/Ceuta",
|
||||
"Africa/El_Aaiun",
|
||||
"Africa/Johannesburg",
|
||||
"Africa/Juba",
|
||||
"Africa/Khartoum",
|
||||
"Africa/Lagos",
|
||||
"Africa/Maputo",
|
||||
"Africa/Monrovia",
|
||||
"Africa/Nairobi",
|
||||
"Africa/Ndjamena",
|
||||
"Africa/Sao_Tome",
|
||||
"Africa/Tripoli",
|
||||
"Africa/Tunis",
|
||||
"Africa/Windhoek",
|
||||
"America/Adak",
|
||||
"America/Anchorage",
|
||||
"America/Araguaina",
|
||||
"America/Argentina/Buenos_Aires",
|
||||
"America/Argentina/Catamarca",
|
||||
"America/Argentina/Cordoba",
|
||||
"America/Argentina/Jujuy",
|
||||
"America/Argentina/La_Rioja",
|
||||
"America/Argentina/Mendoza",
|
||||
"America/Argentina/Rio_Gallegos",
|
||||
"America/Argentina/Salta",
|
||||
"America/Argentina/San_Juan",
|
||||
"America/Argentina/San_Luis",
|
||||
"America/Argentina/Tucuman",
|
||||
"America/Argentina/Ushuaia",
|
||||
"America/Asuncion",
|
||||
"America/Bahia",
|
||||
"America/Bahia_Banderas",
|
||||
"America/Barbados",
|
||||
"America/Belem",
|
||||
"America/Belize",
|
||||
"America/Boa_Vista",
|
||||
"America/Bogota",
|
||||
"America/Boise",
|
||||
"America/Cambridge_Bay",
|
||||
"America/Campo_Grande",
|
||||
"America/Cancun",
|
||||
"America/Caracas",
|
||||
"America/Cayenne",
|
||||
"America/Cayman",
|
||||
"America/Chicago",
|
||||
"America/Chihuahua",
|
||||
"America/Costa_Rica",
|
||||
"America/Creston",
|
||||
"America/Cuiaba",
|
||||
"America/Curacao",
|
||||
"America/Danmarkshavn",
|
||||
"America/Dawson",
|
||||
"America/Dawson_Creek",
|
||||
"America/Denver",
|
||||
"America/Detroit",
|
||||
"America/Dominica",
|
||||
"America/Edmonton",
|
||||
"America/Eirunepe",
|
||||
"America/El_Salvador",
|
||||
"America/Fortaleza",
|
||||
"America/Fort_Nelson",
|
||||
"America/Glace_Bay",
|
||||
"America/Godthab",
|
||||
"America/Goose_Bay",
|
||||
"America/Grand_Turk",
|
||||
"America/Grenada",
|
||||
"America/Guadeloupe",
|
||||
"America/Guatemala",
|
||||
"America/Guayaquil",
|
||||
"America/Guyana",
|
||||
"America/Halifax",
|
||||
"America/Havana",
|
||||
"America/Hermosillo",
|
||||
"America/Indiana/Indianapolis",
|
||||
"America/Indiana/Knox",
|
||||
"America/Indiana/Marengo",
|
||||
"America/Indiana/Petersburg",
|
||||
"America/Indiana/Tell_City",
|
||||
"America/Indiana/Vevay",
|
||||
"America/Indiana/Vincennes",
|
||||
"America/Indiana/Winamac",
|
||||
"America/Inuvik",
|
||||
"America/Iqaluit",
|
||||
"America/Jamaica",
|
||||
"America/Juneau",
|
||||
"America/Kentucky/Louisville",
|
||||
"America/Kentucky/Monticello",
|
||||
"America/La_Paz",
|
||||
"America/Lima",
|
||||
"America/Los_Angeles",
|
||||
"America/Maceio",
|
||||
"America/Managua",
|
||||
"America/Manaus",
|
||||
"America/Marigot",
|
||||
"America/Martinique",
|
||||
"America/Matamoros",
|
||||
"America/Mazatlan",
|
||||
"America/Menominee",
|
||||
"America/Merida",
|
||||
"America/Metlakatla",
|
||||
"America/Mexico_City",
|
||||
"America/Miquelon",
|
||||
"America/Moncton",
|
||||
"America/Monterrey",
|
||||
"America/Montevideo",
|
||||
"America/New_York",
|
||||
"America/Nome",
|
||||
"America/Noronha",
|
||||
"America/North_Dakota/Beulah",
|
||||
"America/North_Dakota/Center",
|
||||
"America/North_Dakota/New_Salem",
|
||||
"America/Nuuk",
|
||||
"America/Ojinaga",
|
||||
"America/Panama",
|
||||
"America/Paramaribo",
|
||||
"America/Phoenix",
|
||||
"America/Port-au-Prince",
|
||||
"America/Porto_Velho",
|
||||
"America/Puerto_Rico",
|
||||
"America/Punta_Arenas",
|
||||
"America/Rankin_Inlet",
|
||||
"America/Recife",
|
||||
"America/Regina",
|
||||
"America/Resolute",
|
||||
"America/Rio_Branco",
|
||||
"America/Santarem",
|
||||
"America/Santiago",
|
||||
"America/Santo_Domingo",
|
||||
"America/Sao_Paulo",
|
||||
"America/Scoresbysund",
|
||||
"America/Sitka",
|
||||
"America/St_Johns",
|
||||
"America/Swift_Current",
|
||||
"America/Tegucigalpa",
|
||||
"America/Thule",
|
||||
"America/Tijuana",
|
||||
"America/Toronto",
|
||||
"America/Vancouver",
|
||||
"America/Whitehorse",
|
||||
"America/Winnipeg",
|
||||
"America/Yakutat",
|
||||
"Antarctica/Casey",
|
||||
"Antarctica/Davis",
|
||||
"Antarctica/Macquarie",
|
||||
"Antarctica/Mawson",
|
||||
"Antarctica/Palmer",
|
||||
"Antarctica/Rothera",
|
||||
"Antarctica/Troll",
|
||||
"Asia/Almaty",
|
||||
"Asia/Amman",
|
||||
"Asia/Anadyr",
|
||||
"Asia/Aqtau",
|
||||
"Asia/Aqtobe",
|
||||
"Asia/Ashgabat",
|
||||
"Asia/Atyrau",
|
||||
"Asia/Baghdad",
|
||||
"Asia/Bahrain",
|
||||
"Asia/Baku",
|
||||
"Asia/Bangkok",
|
||||
"Asia/Barnaul",
|
||||
"Asia/Beirut",
|
||||
"Asia/Bishkek",
|
||||
"Asia/Brunei",
|
||||
"Asia/Chita",
|
||||
"Asia/Choibalsan",
|
||||
"Asia/Colombo",
|
||||
"Asia/Damascus",
|
||||
"Asia/Dhaka",
|
||||
"Asia/Dili",
|
||||
"Asia/Dubai",
|
||||
"Asia/Dushanbe",
|
||||
"Asia/Famagusta",
|
||||
"Asia/Gaza",
|
||||
"Asia/Hebron",
|
||||
"Asia/Ho_Chi_Minh",
|
||||
"Asia/Hong_Kong",
|
||||
"Asia/Hovd",
|
||||
"Asia/Irkutsk",
|
||||
"Asia/Jakarta",
|
||||
"Asia/Jayapura",
|
||||
"Asia/Jerusalem",
|
||||
"Asia/Kabul",
|
||||
"Asia/Kamchatka",
|
||||
"Asia/Karachi",
|
||||
"Asia/Kathmandu",
|
||||
"Asia/Khandyga",
|
||||
"Asia/Kolkata",
|
||||
"Asia/Krasnoyarsk",
|
||||
"Asia/Kuala_Lumpur",
|
||||
"Asia/Kuching",
|
||||
"Asia/Kuwait",
|
||||
"Asia/Macau",
|
||||
"Asia/Magadan",
|
||||
"Asia/Makassar",
|
||||
"Asia/Manila",
|
||||
"Asia/Nicosia",
|
||||
"Asia/Novokuznetsk",
|
||||
"Asia/Novosibirsk",
|
||||
"Asia/Omsk",
|
||||
"Asia/Oral",
|
||||
"Asia/Pontianak",
|
||||
"Asia/Pyongyang",
|
||||
"Asia/Qatar",
|
||||
"Asia/Qyzylorda",
|
||||
"Asia/Riyadh",
|
||||
"Asia/Sakhalin",
|
||||
"Asia/Samarkand",
|
||||
"Asia/Seoul",
|
||||
"Asia/Shanghai",
|
||||
"Asia/Singapore",
|
||||
"Asia/Srednekolymsk",
|
||||
"Asia/Taipei",
|
||||
"Asia/Tashkent",
|
||||
"Asia/Tbilisi",
|
||||
"Asia/Tehran",
|
||||
"Asia/Thimphu",
|
||||
"Asia/Tokyo",
|
||||
"Asia/Tomsk",
|
||||
"Asia/Ulaanbaatar",
|
||||
"Asia/Urumqi",
|
||||
"Asia/Ust-Nera",
|
||||
"Asia/Vientiane",
|
||||
"Asia/Vladivostok",
|
||||
"Asia/Yakutsk",
|
||||
"Asia/Yangon",
|
||||
"Asia/Yekaterinburg",
|
||||
"Asia/Yerevan",
|
||||
"Atlantic/Azores",
|
||||
"Atlantic/Bermuda",
|
||||
"Atlantic/Canary",
|
||||
"Atlantic/Cape_Verde",
|
||||
"Atlantic/Faroe",
|
||||
"Atlantic/Madeira",
|
||||
"Atlantic/South_Georgia",
|
||||
"Atlantic/Stanley",
|
||||
"Australia/Adelaide",
|
||||
"Australia/Brisbane",
|
||||
"Australia/Broken_Hill",
|
||||
"Australia/Darwin",
|
||||
"Australia/Eucla",
|
||||
"Australia/Hobart",
|
||||
"Australia/Lindeman",
|
||||
"Australia/Lord_Howe",
|
||||
"Australia/Melbourne",
|
||||
"Australia/Perth",
|
||||
"Australia/Sydney",
|
||||
"Europe/Andorra",
|
||||
"Europe/Astrakhan",
|
||||
"Europe/Athens",
|
||||
"Europe/Belgrade",
|
||||
"Europe/Berlin",
|
||||
"Europe/Brussels",
|
||||
"Europe/Bucharest",
|
||||
"Europe/Budapest",
|
||||
"Europe/Chisinau",
|
||||
"Europe/Dublin",
|
||||
"Europe/Gibraltar",
|
||||
"Europe/Helsinki",
|
||||
"Europe/Istanbul",
|
||||
"Europe/Kaliningrad",
|
||||
"Europe/Kyiv",
|
||||
"Europe/Kirov",
|
||||
"Europe/Lisbon",
|
||||
"Europe/London",
|
||||
"Europe/Madrid",
|
||||
"Europe/Malta",
|
||||
"Europe/Minsk",
|
||||
"Europe/Moscow",
|
||||
"Europe/Paris",
|
||||
"Europe/Prague",
|
||||
"Europe/Riga",
|
||||
"Europe/Rome",
|
||||
"Europe/Samara",
|
||||
"Europe/Saratov",
|
||||
"Europe/Simferopol",
|
||||
"Europe/Sofia",
|
||||
"Europe/Tallinn",
|
||||
"Europe/Tirane",
|
||||
"Europe/Ulyanovsk",
|
||||
"Europe/Vienna",
|
||||
"Europe/Vilnius",
|
||||
"Europe/Volgograd",
|
||||
"Europe/Warsaw",
|
||||
"Europe/Zurich",
|
||||
"Indian/Chagos",
|
||||
"Indian/Maldives",
|
||||
"Indian/Mauritius",
|
||||
"Pacific/Apia",
|
||||
"Pacific/Auckland",
|
||||
"Pacific/Bougainville",
|
||||
"Pacific/Chatham",
|
||||
"Pacific/Chuuk",
|
||||
"Pacific/Easter",
|
||||
"Pacific/Efate",
|
||||
"Pacific/Enderbury",
|
||||
"Pacific/Fakaofo",
|
||||
"Pacific/Fiji",
|
||||
"Pacific/Funafuti",
|
||||
"Pacific/Galapagos",
|
||||
"Pacific/Gambier",
|
||||
"Pacific/Guadalcanal",
|
||||
"Pacific/Guam",
|
||||
"Pacific/Honolulu",
|
||||
"Pacific/Kiritimati",
|
||||
"Pacific/Kosrae",
|
||||
"Pacific/Kwajalein",
|
||||
"Pacific/Majuro",
|
||||
"Pacific/Marquesas",
|
||||
"Pacific/Midway",
|
||||
"Pacific/Nauru",
|
||||
"Pacific/Niue",
|
||||
"Pacific/Norfolk",
|
||||
"Pacific/Noumea",
|
||||
"Pacific/Pago_Pago",
|
||||
"Pacific/Palau",
|
||||
"Pacific/Pitcairn",
|
||||
"Pacific/Port_Moresby",
|
||||
"Pacific/Rarotonga",
|
||||
"Pacific/Tahiti",
|
||||
"Pacific/Tarawa",
|
||||
"Pacific/Tongatapu",
|
||||
"Etc/GMT",
|
||||
"Etc/GMT-1",
|
||||
"Etc/GMT-2",
|
||||
"Etc/GMT-3",
|
||||
"Etc/GMT-4",
|
||||
"Etc/GMT-5",
|
||||
"Etc/GMT-6",
|
||||
"Etc/GMT-7",
|
||||
"Etc/GMT-8",
|
||||
"Etc/GMT-9",
|
||||
"Etc/GMT-10",
|
||||
"Etc/GMT-11",
|
||||
"Etc/GMT-12",
|
||||
"Etc/GMT-13",
|
||||
"Etc/GMT-14",
|
||||
"Etc/GMT+1",
|
||||
"Etc/GMT+2",
|
||||
"Etc/GMT+3",
|
||||
"Etc/GMT+4",
|
||||
"Etc/GMT+5",
|
||||
"Etc/GMT+6",
|
||||
"Etc/GMT+7",
|
||||
"Etc/GMT+8",
|
||||
"Etc/GMT+9",
|
||||
"Etc/GMT+10",
|
||||
"Etc/GMT+11",
|
||||
"Etc/GMT+12",
|
||||
"Etc/UTC"
|
||||
]
|
||||
|
||||
def get_tz_string(timezone):
|
||||
data = open(ZONES_DIR + timezone, "rb").read().split(b"\n")[-2]
|
||||
return data.decode("utf-8")
|
||||
|
||||
if __name__ == "__main__":
|
||||
f = open("./src/tz_data.h", "w")
|
||||
for timezone in ZONES:
|
||||
f.write('\t{{"{}", "{}"}},\n'.format(timezone, get_tz_string(timezone)))
|
||||
f.close()
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef DATE_TIME_H
|
||||
#define DATE_TIME_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief Initialize timezone and NTP
|
||||
*
|
||||
*/
|
||||
void date_time_init(void);
|
||||
|
||||
/**
|
||||
* @brief Return true if NTP is enabled, stored in NVS
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool date_time_is_ntp_enabled(void);
|
||||
|
||||
/**
|
||||
* @brief Get NTP server, string length 64, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
void date_time_get_ntp_server(char* value);
|
||||
|
||||
/**
|
||||
* @brief Return true if NTP server from DHCP fetch is enabled, stored in NVS
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool date_time_is_ntp_from_dhcp(void);
|
||||
|
||||
/**
|
||||
* @brief Set timezone and NTP config
|
||||
*
|
||||
* @param enabled
|
||||
* @param server
|
||||
* @param from_dhcp
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t date_time_set_ntp_config(bool enabled, const char* server, bool from_dhcp);
|
||||
|
||||
/**
|
||||
* @brief Get timezone, string length 64, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
void date_time_get_timezone(char* value);
|
||||
|
||||
/**
|
||||
* @brief Set timezone, string length 64, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
esp_err_t date_time_set_timezone(const char* value);
|
||||
|
||||
|
||||
#endif /* DATE_TIME_H */
|
||||
38
components/protocols/include/json.h
Executable file
38
components/protocols/include/json.h
Executable file
@@ -0,0 +1,38 @@
|
||||
#ifndef JSON_H_
|
||||
#define JSON_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
/**
|
||||
* @brief Gera um objeto JSON com a configuração atual do EVSE.
|
||||
*
|
||||
* Contém parâmetros como corrente máxima, limites de tempo,
|
||||
* trava do conector, temperatura e configuração do OCPP.
|
||||
*
|
||||
* @return Ponteiro para cJSON (deve ser liberado com cJSON_Delete()).
|
||||
*/
|
||||
cJSON* json_get_evse_config(void);
|
||||
|
||||
/**
|
||||
* @brief Define a configuração do EVSE a partir de um objeto JSON.
|
||||
*
|
||||
* Aplica valores recebidos de protocolos como MQTT ou REST.
|
||||
*
|
||||
* @param root Objeto JSON com os campos válidos.
|
||||
* @return ESP_OK se todos os parâmetros foram aplicados com sucesso.
|
||||
*/
|
||||
esp_err_t json_set_evse_config(cJSON* root);
|
||||
|
||||
/**
|
||||
* @brief Retorna o estado atual do EVSE em formato JSON.
|
||||
*
|
||||
* Inclui estado de operação, erros, limites, sessão atual e medições elétricas.
|
||||
*
|
||||
* @return Ponteiro para cJSON (deve ser liberado com cJSON_Delete()).
|
||||
*/
|
||||
cJSON* json_get_state(void);
|
||||
|
||||
|
||||
#endif /* JSON_H_ */
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef MODBUS_TCP_H
|
||||
#define MODBUS_TCP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Init modbus tcp
|
||||
*
|
||||
*/
|
||||
void modbus_tcp_init(void);
|
||||
|
||||
/**
|
||||
* @brief Set enabled, stored in NVS
|
||||
*
|
||||
* @param enabled
|
||||
*/
|
||||
void modbus_tcp_set_enabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Get enabled, stored in NVS
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool modbus_tcp_is_enabled(void);
|
||||
|
||||
#endif /* MODBUS_TCP_H */
|
||||
@@ -6,63 +6,108 @@
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief Initializes MQTT client and starts background task if enabled in NVS
|
||||
* @file mqtt.h
|
||||
* @brief MQTT configuration and control interface.
|
||||
*
|
||||
* This module provides initialization, configuration,
|
||||
* and runtime access functions for the MQTT client.
|
||||
*
|
||||
* Configuration is persisted in NVS under namespace "mqtt".
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Definitions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define MQTT_MAX_SERVER_LEN 64 /**< Max length for MQTT server URI */
|
||||
#define MQTT_MAX_USER_LEN 32 /**< Max length for MQTT username */
|
||||
#define MQTT_MAX_PASSWORD_LEN 64 /**< Max length for MQTT password */
|
||||
#define MQTT_MAX_BASE_TOPIC_LEN 64 /**< Max length for MQTT base topic */
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Public Functions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Initialize MQTT subsystem.
|
||||
*
|
||||
* Loads configuration from NVS and starts background publish task
|
||||
* if MQTT is enabled. Must be called once during system startup.
|
||||
*/
|
||||
void mqtt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT config
|
||||
*
|
||||
* @param enabled
|
||||
* @param server NULL value will be skiped
|
||||
* @param base_topic NULL value will be skiped
|
||||
* @param user NULL value will be skiped
|
||||
* @param password NULL value will be skiped
|
||||
* @param periodicity 0 value will be skiped
|
||||
* @return esp_err_t
|
||||
* @brief Restart the MQTT client safely.
|
||||
*
|
||||
* Stops the current MQTT client (if running) and starts a new one
|
||||
* with the configuration currently stored in NVS.
|
||||
*
|
||||
* Useful when changing Wi-Fi networks, credentials, or broker settings.
|
||||
*
|
||||
* @return ESP_OK on success, or an ESP_ERR_* code otherwise.
|
||||
*/
|
||||
esp_err_t mqtt_set_config(bool enabled, const char* server, const char* base_topic, const char* user, const char* password, uint16_t periodicity);
|
||||
esp_err_t mqtt_restart(void);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT enabled, stored in NVS
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
* @brief Set and persist MQTT configuration parameters in NVS.
|
||||
*
|
||||
* Any NULL parameter will be skipped (the previous value remains stored).
|
||||
*
|
||||
* @param enabled Whether MQTT should be enabled (true/false).
|
||||
* @param server Broker URI (e.g. "mqtt://192.168.1.10").
|
||||
* @param base_topic Base topic prefix for publish/subscribe.
|
||||
* @param user MQTT username (optional).
|
||||
* @param password MQTT password (optional).
|
||||
* @param periodicity Publish interval (in seconds). Must be >0 when enabled.
|
||||
*
|
||||
* @return ESP_OK on success, or an ESP_ERR_* code otherwise.
|
||||
*/
|
||||
esp_err_t mqtt_set_config(bool enabled,
|
||||
const char *server,
|
||||
const char *base_topic,
|
||||
const char *user,
|
||||
const char *password,
|
||||
uint16_t periodicity);
|
||||
|
||||
/**
|
||||
* @brief Get whether MQTT is enabled.
|
||||
*
|
||||
* @return true if enabled, false otherwise.
|
||||
*/
|
||||
bool mqtt_get_enabled(void);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT server, string length 64, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
* @brief Get MQTT broker URI stored in NVS.
|
||||
*
|
||||
* @param[out] value Buffer to receive the URI (min length: MQTT_MAX_SERVER_LEN).
|
||||
*/
|
||||
void mqtt_get_server(char* value);
|
||||
void mqtt_get_server(char *value);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT password, string length 64, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
* @brief Get MQTT base topic stored in NVS.
|
||||
*
|
||||
* @param[out] value Buffer to receive the base topic (min length: MQTT_MAX_BASE_TOPIC_LEN).
|
||||
*/
|
||||
void mqtt_get_password(char* value);
|
||||
void mqtt_get_base_topic(char *value);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT base topic, string length 32, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
* @brief Get MQTT username stored in NVS.
|
||||
*
|
||||
* @param[out] value Buffer to receive the username (min length: MQTT_MAX_USER_LEN).
|
||||
*/
|
||||
void mqtt_get_base_topic(char* value);
|
||||
void mqtt_get_user(char *value);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT user, string length 32, stored in NVS
|
||||
*
|
||||
* @param value
|
||||
* @brief Get MQTT password stored in NVS.
|
||||
*
|
||||
* @param[out] value Buffer to receive the password (min length: MQTT_MAX_PASSWORD_LEN).
|
||||
*/
|
||||
void mqtt_get_user(char* value);
|
||||
void mqtt_get_password(char *value);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT periodicity in second, stored in NVS
|
||||
*
|
||||
* @return uint16_t
|
||||
* @brief Get MQTT publish periodicity in seconds.
|
||||
*
|
||||
* @return Publish interval in seconds (default: 30).
|
||||
*/
|
||||
uint16_t mqtt_get_periodicity(void);
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
|
||||
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
|
||||
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
|
||||
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
|
||||
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
|
||||
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
|
||||
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
|
||||
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
|
||||
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
|
||||
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
|
||||
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
|
||||
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
|
||||
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
|
||||
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,136 +0,0 @@
|
||||
|
||||
#include <time.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_sntp.h"
|
||||
#include "esp_netif_sntp.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#include "date_time.h"
|
||||
|
||||
#define NVS_NAMESPACE "date_time"
|
||||
#define NVS_NTP_ENABLED "ntp_en"
|
||||
#define NVS_NTP_SERVER "ntp_server"
|
||||
#define NVS_NTP_FROM_DHCP "ntp_from_dhcp"
|
||||
#define NVS_TIMEZONE "timezone"
|
||||
|
||||
static const char* TAG = "date_time";
|
||||
|
||||
static nvs_handle nvs;
|
||||
|
||||
static char ntp_server[64]; // if renew_servers_after_new_IP = false, will be used static string reference
|
||||
|
||||
static const char* tz_data[][2] = {
|
||||
#include "tz_data.h"
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const char* find_tz(const char* name)
|
||||
{
|
||||
if (name != NULL) {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
if (tz_data[index][0] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(tz_data[index][0], name) == 0) {
|
||||
return tz_data[index][1];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void date_time_init(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs));
|
||||
|
||||
if (date_time_is_ntp_enabled()) {
|
||||
date_time_get_ntp_server(ntp_server);
|
||||
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(ntp_server);
|
||||
|
||||
esp_err_t ret = esp_netif_sntp_init(&config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SNTP init return %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
char str[64];
|
||||
date_time_get_timezone(str);
|
||||
const char *tz = find_tz(str);
|
||||
if (tz) {
|
||||
setenv("TZ", tz, 1);
|
||||
tzset();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Unknown timezone %s", str);
|
||||
}
|
||||
}
|
||||
|
||||
bool date_time_is_ntp_enabled(void)
|
||||
{
|
||||
uint8_t value = false;
|
||||
nvs_get_u8(nvs, NVS_NTP_ENABLED, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void date_time_get_ntp_server(char* value)
|
||||
{
|
||||
size_t len = 64;
|
||||
value[0] = '\0';
|
||||
nvs_get_str(nvs, NVS_NTP_SERVER, value, &len);
|
||||
}
|
||||
|
||||
bool date_time_is_ntp_from_dhcp(void)
|
||||
{
|
||||
uint8_t value = false;
|
||||
nvs_get_u8(nvs, NVS_NTP_FROM_DHCP, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
esp_err_t date_time_set_ntp_config(bool enabled, const char* server, bool from_dhcp)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
esp_netif_sntp_deinit();
|
||||
if (enabled) {
|
||||
strcpy(ntp_server, server);
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(ntp_server);
|
||||
config.renew_servers_after_new_IP = from_dhcp;
|
||||
ret = esp_netif_sntp_init(&config);
|
||||
}
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
nvs_set_u8(nvs, NVS_NTP_ENABLED, enabled);
|
||||
nvs_set_str(nvs, NVS_NTP_SERVER, server);
|
||||
nvs_set_u8(nvs, NVS_NTP_FROM_DHCP, from_dhcp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void date_time_get_timezone(char* value)
|
||||
{
|
||||
size_t len = 64;
|
||||
value[0] = '\0';
|
||||
strcpy(value, "Etc/UTC");
|
||||
nvs_get_str(nvs, NVS_TIMEZONE, value, &len);
|
||||
}
|
||||
|
||||
esp_err_t date_time_set_timezone(const char* value)
|
||||
{
|
||||
const char* tz = find_tz(value);
|
||||
|
||||
if (tz) {
|
||||
setenv("TZ", tz, 1);
|
||||
tzset();
|
||||
|
||||
nvs_set_str(nvs, NVS_TIMEZONE, value);
|
||||
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Unknown timezone %s", value);
|
||||
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
204
components/protocols/src/json.c
Executable file
204
components/protocols/src/json.c
Executable file
@@ -0,0 +1,204 @@
|
||||
// === Início de: components/protocols/src/json.c ===
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "json.h"
|
||||
#include "mqtt.h"
|
||||
#include "network.h"
|
||||
#include "evse_error.h"
|
||||
#include "evse_api.h"
|
||||
#include "auth.h"
|
||||
#include "evse_limits.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_config.h"
|
||||
#include "ocpp.h"
|
||||
#include "board_config.h"
|
||||
#include "socket_lock.h"
|
||||
#include "proximity.h"
|
||||
#include "temp_sensor.h"
|
||||
#include "evse_meter.h"
|
||||
|
||||
static const char *TAG = "json";
|
||||
|
||||
//
|
||||
// ===== EVSE CONFIG JSON =====
|
||||
//
|
||||
cJSON *json_get_evse_config(void)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
if (!root)
|
||||
return NULL;
|
||||
|
||||
cJSON_AddNumberToObject(root, "maxChargingCurrent", evse_get_max_charging_current());
|
||||
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current());
|
||||
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current());
|
||||
cJSON_AddBoolToObject(root, "requireAuth", auth_get_mode());
|
||||
cJSON_AddBoolToObject(root, "socketOutlet", evse_get_socket_outlet());
|
||||
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
|
||||
cJSON_AddNumberToObject(root, "temperatureThreshold", evse_get_temp_threshold());
|
||||
cJSON_AddNumberToObject(root, "consumptionLimit", evse_get_consumption_limit());
|
||||
cJSON_AddNumberToObject(root, "defaultConsumptionLimit", evse_get_default_consumption_limit());
|
||||
cJSON_AddNumberToObject(root, "chargingTimeLimit", evse_get_charging_time_limit());
|
||||
cJSON_AddNumberToObject(root, "defaultChargingTimeLimit", evse_get_default_charging_time_limit());
|
||||
cJSON_AddNumberToObject(root, "underPowerLimit", evse_get_under_power_limit());
|
||||
cJSON_AddNumberToObject(root, "defaultUnderPowerLimit", evse_get_default_under_power_limit());
|
||||
|
||||
cJSON_AddNumberToObject(root, "socketLockOperatingTime", socket_lock_get_operating_time());
|
||||
cJSON_AddNumberToObject(root, "socketLockBreakTime", socket_lock_get_break_time());
|
||||
cJSON_AddBoolToObject(root, "socketLockDetectionHigh", socket_lock_is_detection_high());
|
||||
cJSON_AddNumberToObject(root, "socketLockRetryCount", socket_lock_get_retry_count());
|
||||
|
||||
char str[64];
|
||||
cJSON_AddBoolToObject(root, "enabledocpp", ocpp_get_enabled());
|
||||
ocpp_get_server(str);
|
||||
cJSON_AddStringToObject(root, "serverocpp", str);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
//
|
||||
// ===== SET EVSE CONFIG (from MQTT or REST) =====
|
||||
//
|
||||
esp_err_t json_set_evse_config(cJSON *root)
|
||||
{
|
||||
if (!root)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
// Alguns setters retornam esp_err_t, outros void. Para manter compatibilidade,
|
||||
// chamamos sem propagar erro (se existir, será tratado internamente).
|
||||
#define SET_NUM(key, fn) \
|
||||
do \
|
||||
{ \
|
||||
const cJSON *item = cJSON_GetObjectItem(root, key); \
|
||||
if (cJSON_IsNumber(item)) \
|
||||
{ \
|
||||
fn(item->valuedouble); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SET_BOOL(key, fn) \
|
||||
do \
|
||||
{ \
|
||||
const cJSON *item = cJSON_GetObjectItem(root, key); \
|
||||
if (cJSON_IsBool(item)) \
|
||||
{ \
|
||||
fn(cJSON_IsTrue(item)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
SET_NUM("maxChargingCurrent", evse_set_max_charging_current);
|
||||
SET_NUM("chargingCurrent", evse_set_charging_current);
|
||||
SET_NUM("defaultChargingCurrent", evse_set_default_charging_current);
|
||||
SET_BOOL("socketOutlet", evse_set_socket_outlet);
|
||||
SET_BOOL("rcm", evse_set_rcm);
|
||||
SET_NUM("temperatureThreshold", evse_set_temp_threshold);
|
||||
SET_NUM("consumptionLimit", evse_set_consumption_limit);
|
||||
SET_NUM("defaultConsumptionLimit", evse_set_default_consumption_limit);
|
||||
SET_NUM("chargingTimeLimit", evse_set_charging_time_limit);
|
||||
SET_NUM("defaultChargingTimeLimit", evse_set_default_charging_time_limit);
|
||||
SET_NUM("underPowerLimit", evse_set_under_power_limit);
|
||||
SET_NUM("defaultUnderPowerLimit", evse_set_default_under_power_limit);
|
||||
SET_NUM("socketLockOperatingTime", socket_lock_set_operating_time);
|
||||
SET_NUM("socketLockBreakTime", socket_lock_set_break_time);
|
||||
|
||||
const cJSON *retry = cJSON_GetObjectItem(root, "socketLockRetryCount");
|
||||
if (cJSON_IsNumber(retry))
|
||||
socket_lock_set_retry_count(retry->valueint);
|
||||
|
||||
const cJSON *detect = cJSON_GetObjectItem(root, "socketLockDetectionHigh");
|
||||
if (cJSON_IsBool(detect))
|
||||
socket_lock_set_detection_high(cJSON_IsTrue(detect));
|
||||
|
||||
const cJSON *ocpp_enabled = cJSON_GetObjectItem(root, "enabledocpp");
|
||||
if (cJSON_IsBool(ocpp_enabled))
|
||||
ocpp_set_enabled(cJSON_IsTrue(ocpp_enabled));
|
||||
|
||||
const cJSON *ocpp_server = cJSON_GetObjectItem(root, "serverocpp");
|
||||
if (cJSON_IsString(ocpp_server))
|
||||
ocpp_set_server(cJSON_GetStringValue(ocpp_server));
|
||||
|
||||
ESP_LOGI(TAG, "EVSE configuration updated successfully");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// ===== EVSE STATE JSON =====
|
||||
//
|
||||
cJSON *json_get_state(void)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
if (!root)
|
||||
return NULL;
|
||||
|
||||
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
|
||||
cJSON_AddBoolToObject(root, "available", evse_config_is_available());
|
||||
cJSON_AddBoolToObject(root, "enabled", evse_config_is_enabled());
|
||||
cJSON_AddBoolToObject(root, "pendingAuth", auth_get_mode());
|
||||
cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached());
|
||||
|
||||
// Add error list
|
||||
uint32_t error = evse_error_get_bits();
|
||||
if (error == 0)
|
||||
{
|
||||
cJSON_AddNullToObject(root, "errors");
|
||||
}
|
||||
else
|
||||
{
|
||||
cJSON *errors = cJSON_CreateArray();
|
||||
if (error & EVSE_ERR_PILOT_FAULT_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("pilot_fault"));
|
||||
if (error & EVSE_ERR_DIODE_SHORT_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("diode_short"));
|
||||
if (error & EVSE_ERR_LOCK_FAULT_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("lock_fault"));
|
||||
if (error & EVSE_ERR_UNLOCK_FAULT_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("unlock_fault"));
|
||||
if (error & EVSE_ERR_RCM_TRIGGERED_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("rcm_triggered"));
|
||||
if (error & EVSE_ERR_RCM_SELFTEST_FAULT_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("rcm_selftest_fault"));
|
||||
if (error & EVSE_ERR_TEMPERATURE_HIGH_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("temperature_high"));
|
||||
if (error & EVSE_ERR_TEMPERATURE_FAULT_BIT)
|
||||
cJSON_AddItemToArray(errors, cJSON_CreateString("temperature_fault"));
|
||||
cJSON_AddItemToObject(root, "errors", errors);
|
||||
}
|
||||
|
||||
// Session info
|
||||
evse_session_t sess;
|
||||
if (evse_get_session(&sess))
|
||||
{
|
||||
cJSON_AddNumberToObject(root, "sessionTime", (double)sess.start_tick);
|
||||
cJSON_AddNumberToObject(root, "chargingTime", (double)sess.duration_s);
|
||||
cJSON_AddNumberToObject(root, "consumption", (double)sess.energy_wh);
|
||||
}
|
||||
else
|
||||
{
|
||||
cJSON_AddNullToObject(root, "sessionTime");
|
||||
cJSON_AddNumberToObject(root, "chargingTime", 0);
|
||||
cJSON_AddNumberToObject(root, "consumption", 0);
|
||||
}
|
||||
|
||||
// Meter readings
|
||||
float voltage[EVSE_METER_PHASE_COUNT];
|
||||
float current[EVSE_METER_PHASE_COUNT];
|
||||
int power[EVSE_METER_PHASE_COUNT];
|
||||
|
||||
evse_meter_get_voltage(voltage);
|
||||
evse_meter_get_current(current);
|
||||
evse_meter_get_power(power);
|
||||
|
||||
cJSON_AddItemToObject(root, "power", cJSON_CreateIntArray(power, EVSE_METER_PHASE_COUNT));
|
||||
cJSON_AddItemToObject(root, "voltage", cJSON_CreateFloatArray(voltage, EVSE_METER_PHASE_COUNT));
|
||||
cJSON_AddItemToObject(root, "current", cJSON_CreateFloatArray(current, EVSE_METER_PHASE_COUNT));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// === Fim de: components/protocols/src/json.c ===
|
||||
@@ -1,304 +1,437 @@
|
||||
// === Início de: components/protocols/src/mqtt.c ===
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#include "mqtt.h"
|
||||
#include "json.h"
|
||||
#include "board_config.h"
|
||||
#include "timeout_utils.h"
|
||||
#include "mqtt_client.h"
|
||||
|
||||
#define NVS_NAMESPACE "mqtt"
|
||||
#define NVS_ENABLED "enabled"
|
||||
#define NVS_SERVER "server"
|
||||
#define NVS_BASE_TOPIC "base_topic"
|
||||
#define NVS_USER "user"
|
||||
#define NVS_PASSWORD "password"
|
||||
#define NVS_PERIODICITY "periodicity"
|
||||
#define NVS_NAMESPACE "mqtt"
|
||||
#define NVS_ENABLED "enabled"
|
||||
#define NVS_SERVER "server"
|
||||
#define NVS_BASE_TOPIC "base_topic"
|
||||
#define NVS_USER "user"
|
||||
#define NVS_PASSWORD "password"
|
||||
#define NVS_PERIODICITY "periodicity"
|
||||
|
||||
static const char *TAG = "mqtt";
|
||||
|
||||
static const char* TAG = "mqtt";
|
||||
static TaskHandle_t client_task = NULL;
|
||||
static esp_mqtt_client_handle_t client = NULL;
|
||||
static uint16_t periodicity = 30;
|
||||
static SemaphoreHandle_t mqtt_mutex = NULL;
|
||||
static uint16_t periodicity = 30; // seconds
|
||||
// Nem todas as versões do IDF têm esp_mqtt_client_is_connected(); usamos um flag.
|
||||
static volatile bool mqtt_connected = false;
|
||||
|
||||
static esp_err_t open_mqtt_nvs(nvs_handle_t *handle) {
|
||||
static esp_err_t open_mqtt_nvs(nvs_handle_t *handle)
|
||||
{
|
||||
return nvs_open(NVS_NAMESPACE, NVS_READWRITE, handle);
|
||||
}
|
||||
|
||||
static void subcribe_topics(void)
|
||||
//
|
||||
// Helper: safely publish JSON message
|
||||
//
|
||||
static void publish_message(const char *topic_suffix, cJSON *root)
|
||||
{
|
||||
ESP_LOGI(TAG, "[MQTT] Subscribing to topics");
|
||||
if (!root) return;
|
||||
|
||||
char base[32];
|
||||
mqtt_get_base_topic(base);
|
||||
if (xSemaphoreTake(mqtt_mutex, pdMS_TO_TICKS(500)) == pdTRUE)
|
||||
{
|
||||
if (client && mqtt_connected)
|
||||
{
|
||||
char base[MQTT_MAX_BASE_TOPIC_LEN] = {0};
|
||||
mqtt_get_base_topic(base);
|
||||
|
||||
char topic[64];
|
||||
char topic[128];
|
||||
snprintf(topic, sizeof(topic), "%s%s", base, topic_suffix);
|
||||
|
||||
snprintf(topic, sizeof(topic), "%s/request/#", base);
|
||||
esp_mqtt_client_subscribe(client, topic, 0);
|
||||
ESP_LOGI(TAG, " subscribed: %s", topic);
|
||||
|
||||
snprintf(topic, sizeof(topic), "%s/set/config/#", base);
|
||||
esp_mqtt_client_subscribe(client, topic, 0);
|
||||
ESP_LOGI(TAG, " subscribed: %s", topic);
|
||||
|
||||
snprintf(topic, sizeof(topic), "%s/enable", base);
|
||||
esp_mqtt_client_subscribe(client, topic, 0);
|
||||
ESP_LOGI(TAG, " subscribed: %s", topic);
|
||||
}
|
||||
|
||||
static void publish_message(const char* topic, cJSON* root)
|
||||
{
|
||||
char target_topic[64];
|
||||
mqtt_get_base_topic(target_topic);
|
||||
strncat(target_topic, topic, sizeof(target_topic) - strlen(target_topic) - 1);
|
||||
|
||||
const char* json = cJSON_PrintUnformatted(root);
|
||||
esp_mqtt_client_publish(client, target_topic, json, 0, 1, 0);
|
||||
free((void*)json);
|
||||
}
|
||||
|
||||
|
||||
static void handle_message(const char* topic, const char* data)
|
||||
{
|
||||
char base_topic[32];
|
||||
mqtt_get_base_topic(base_topic);
|
||||
|
||||
ESP_LOGI(TAG, "Topic: %s", topic);
|
||||
ESP_LOGI(TAG, "data: %s", data);
|
||||
ESP_LOGI(TAG, "base_topic: %s", base_topic);
|
||||
|
||||
if (strncmp(topic, base_topic, strlen(base_topic)) == 0) {
|
||||
const char* sub_topic = &topic[strlen(base_topic)];
|
||||
|
||||
ESP_LOGI(TAG, "Sub_topic: %s", sub_topic);
|
||||
|
||||
if (strcmp(sub_topic, "/request/config/evse") == 0) {
|
||||
cJSON* root = json_get_evse_config();
|
||||
publish_message("/response/config/evse", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/config/wifi") == 0) {
|
||||
//cJSON* root = json_get_wifi_config();
|
||||
//publish_message("/response/config/wifi", root);
|
||||
//cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/config/mqtt") == 0) {
|
||||
cJSON* root = json_get_mqtt_config();
|
||||
publish_message("/response/config/mqtt", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/boardConfig") == 0) {
|
||||
cJSON* root = json_get_board_config();
|
||||
publish_message("/response/boardConfig", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/info") == 0) {
|
||||
cJSON* root = json_get_info();
|
||||
publish_message("/response/info", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/restart") == 0) {
|
||||
timeout_restart();
|
||||
} else if (strcmp(sub_topic, "/set/config/evse") == 0) {
|
||||
cJSON* root = cJSON_Parse(data);
|
||||
json_set_evse_config(root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/set/config/wifi") == 0) {
|
||||
//cJSON* root = cJSON_Parse(data);
|
||||
//json_set_wifi_config(root, true);
|
||||
//cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/set/config/mqtt") == 0) {
|
||||
cJSON* root = cJSON_Parse(data);
|
||||
json_set_mqtt_config(root);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
char *json = cJSON_PrintUnformatted(root);
|
||||
if (json)
|
||||
{
|
||||
esp_mqtt_client_publish(client, topic, json, 0, 1, 0);
|
||||
ESP_LOGI(TAG, "Published to %s", topic);
|
||||
free(json);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "MQTT client not connected — skipping publish");
|
||||
}
|
||||
xSemaphoreGive(mqtt_mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "MQTT mutex timeout during publish");
|
||||
}
|
||||
}
|
||||
|
||||
static void event_handler(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data)
|
||||
//
|
||||
// Subscriptions
|
||||
//
|
||||
static void subscribe_topics(void)
|
||||
{
|
||||
if (!client) return;
|
||||
|
||||
char base[MQTT_MAX_BASE_TOPIC_LEN] = {0};
|
||||
mqtt_get_base_topic(base);
|
||||
|
||||
const char *subs[] = {
|
||||
"/request/#",
|
||||
"/set/config/#",
|
||||
"/enable",
|
||||
"/request/restart",
|
||||
"/request/config/evse",
|
||||
"/request/boardConfig"
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(subs) / sizeof(subs[0]); i++)
|
||||
{
|
||||
char topic[MQTT_MAX_BASE_TOPIC_LEN + 32];
|
||||
snprintf(topic, sizeof(topic), "%s%s", base, subs[i]);
|
||||
esp_mqtt_client_subscribe(client, topic, 0);
|
||||
ESP_LOGI(TAG, "[MQTT] Subscribed: %s", topic);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Message handler
|
||||
//
|
||||
static void handle_message(const char *topic, const char *data)
|
||||
{
|
||||
if (!topic || !data) return;
|
||||
|
||||
char base[MQTT_MAX_BASE_TOPIC_LEN] = {0};
|
||||
mqtt_get_base_topic(base);
|
||||
|
||||
if (strncmp(topic, base, strlen(base)) != 0) return;
|
||||
|
||||
const char *sub_topic = &topic[strlen(base)];
|
||||
ESP_LOGI(TAG, "[MQTT] Received on %s: %s", sub_topic, data);
|
||||
|
||||
if (strcmp(sub_topic, "/request/config/evse") == 0)
|
||||
{
|
||||
cJSON *root = json_get_evse_config();
|
||||
publish_message("/response/config/evse", root);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
else if (strcmp(sub_topic, "/set/config/evse") == 0)
|
||||
{
|
||||
cJSON *root = cJSON_Parse(data);
|
||||
if (!root)
|
||||
{
|
||||
ESP_LOGE(TAG, "Invalid JSON payload on topic %s", topic);
|
||||
return;
|
||||
}
|
||||
json_set_evse_config(root);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
else if (strcmp(sub_topic, "/request/restart") == 0)
|
||||
{
|
||||
ESP_LOGW(TAG, "Restart request received (TODO: add auth check)");
|
||||
// esp_restart();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MQTT event handler
|
||||
//
|
||||
static void event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
|
||||
switch (event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "[MQTT] Connected to broker");
|
||||
if (client_task) vTaskResume(client_task);
|
||||
subcribe_topics();
|
||||
break;
|
||||
switch (event_id)
|
||||
{
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "[MQTT] Connected to broker");
|
||||
mqtt_connected = true;
|
||||
if (client_task) vTaskResume(client_task);
|
||||
subscribe_topics();
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DATA: {
|
||||
char topic[64] = {0};
|
||||
char data[256] = {0};
|
||||
case MQTT_EVENT_DATA:
|
||||
{
|
||||
char topic[128] = {0};
|
||||
char data_buf[512] = {0};
|
||||
|
||||
int tlen = MIN(event->topic_len, sizeof(topic) - 1);
|
||||
int dlen = MIN(event->data_len, sizeof(data) - 1);
|
||||
int tlen = MIN(event->topic_len, (int)sizeof(topic) - 1);
|
||||
int dlen = MIN(event->data_len, (int)sizeof(data_buf) - 1);
|
||||
|
||||
memcpy(topic, event->topic, tlen);
|
||||
memcpy(data, event->data, dlen);
|
||||
memcpy(topic, event->topic, tlen);
|
||||
memcpy(data_buf, event->data, dlen);
|
||||
|
||||
handle_message(topic, data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
handle_message(topic, data_buf);
|
||||
break;
|
||||
}
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGW(TAG, "[MQTT] Disconnected from broker");
|
||||
mqtt_connected = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void client_task_func(void* param)
|
||||
//
|
||||
// Background periodic publisher
|
||||
//
|
||||
static void client_task_func(void *param)
|
||||
{
|
||||
while (true) {
|
||||
if (!client) vTaskSuspend(NULL);
|
||||
cJSON* root = json_get_state();
|
||||
publish_message("/state", root);
|
||||
cJSON_Delete(root);
|
||||
for (;;)
|
||||
{
|
||||
if (client && mqtt_connected)
|
||||
{
|
||||
cJSON *root = json_get_state();
|
||||
publish_message("/state", root);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "[MQTT] Not connected — skipping state publish");
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(periodicity * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Start/stop MQTT
|
||||
//
|
||||
static void client_start(void)
|
||||
{
|
||||
char server[64], user[32], password[64];
|
||||
char server[MQTT_MAX_SERVER_LEN] = {0};
|
||||
char user[MQTT_MAX_USER_LEN] = {0};
|
||||
char password[MQTT_MAX_PASSWORD_LEN] = {0};
|
||||
|
||||
mqtt_get_server(server);
|
||||
mqtt_get_user(user);
|
||||
mqtt_get_password(password);
|
||||
|
||||
if (!strlen(server))
|
||||
{
|
||||
ESP_LOGW(TAG, "[MQTT] No server configured");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_mqtt_client_config_t cfg = {
|
||||
.broker.address.uri = server,
|
||||
.credentials.username = user,
|
||||
.credentials.authentication.password = password
|
||||
};
|
||||
|
||||
if (!client) {
|
||||
if (!client)
|
||||
{
|
||||
client = esp_mqtt_client_init(&cfg);
|
||||
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, event_handler, client);
|
||||
esp_mqtt_client_start(client);
|
||||
ESP_LOGI(TAG, "[MQTT] Client started");
|
||||
}
|
||||
}
|
||||
|
||||
static void client_stop(void)
|
||||
{
|
||||
if (client) {
|
||||
if (client)
|
||||
{
|
||||
esp_mqtt_client_stop(client);
|
||||
esp_mqtt_client_destroy(client);
|
||||
client = NULL;
|
||||
mqtt_connected = false;
|
||||
ESP_LOGI(TAG, "[MQTT] Client stopped");
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t mqtt_restart(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "[MQTT] Restarting client...");
|
||||
|
||||
if (client)
|
||||
{
|
||||
esp_mqtt_client_stop(client);
|
||||
esp_mqtt_client_destroy(client);
|
||||
client = NULL;
|
||||
mqtt_connected = false;
|
||||
ESP_LOGI(TAG, "[MQTT] Existing client stopped");
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
client_start();
|
||||
|
||||
if (client)
|
||||
{
|
||||
ESP_LOGI(TAG, "[MQTT] Restart complete");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "[MQTT] Restart failed — no valid configuration");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
void mqtt_init(void)
|
||||
{
|
||||
if (!mqtt_mutex)
|
||||
mqtt_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK) {
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_u16(handle, NVS_PERIODICITY, &periodicity);
|
||||
nvs_close(handle);
|
||||
if (periodicity == 0) periodicity = 30;
|
||||
}
|
||||
|
||||
esp_register_shutdown_handler(&client_stop);
|
||||
xTaskCreate(client_task_func, "mqtt_client_task", 3 * 1024, NULL, 5, &client_task);
|
||||
xTaskCreate(client_task_func, "mqtt_client_task", 4 * 1024, NULL, 5, &client_task);
|
||||
|
||||
if (mqtt_get_enabled()) {
|
||||
if (mqtt_get_enabled())
|
||||
{
|
||||
client_start();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t mqtt_set_config(bool enabled, const char* server, const char* base_topic, const char* user, const char* password, uint16_t _periodicity)
|
||||
esp_err_t mqtt_set_config(bool enabled, const char *server, const char *base_topic,
|
||||
const char *user, const char *password, uint16_t _periodicity)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) != ESP_OK) return ESP_ERR_INVALID_STATE;
|
||||
if (open_mqtt_nvs(&handle) != ESP_OK)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
|
||||
char full_server[64];
|
||||
if (server && strncmp(server, "mqtt://", 7) != 0 && strncmp(server, "tcp://", 6) != 0) {
|
||||
char full_server[MQTT_MAX_SERVER_LEN];
|
||||
if (server && strncmp(server, "mqtt://", 7) != 0 && strncmp(server, "tcp://", 6) != 0)
|
||||
{
|
||||
snprintf(full_server, sizeof(full_server), "mqtt://%s", server);
|
||||
server = full_server;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (!server || !*server) return ESP_ERR_INVALID_ARG;
|
||||
if (!base_topic || !*base_topic) return ESP_ERR_INVALID_ARG;
|
||||
if (_periodicity == 0) return ESP_ERR_INVALID_ARG;
|
||||
if (enabled)
|
||||
{
|
||||
if (!server || !*server || !base_topic || !*base_topic || !_periodicity)
|
||||
{
|
||||
nvs_close(handle);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
if (server) nvs_set_str(handle, NVS_SERVER, server);
|
||||
if (server) nvs_set_str(handle, NVS_SERVER, server);
|
||||
if (base_topic) nvs_set_str(handle, NVS_BASE_TOPIC, base_topic);
|
||||
if (user) nvs_set_str(handle, NVS_USER, user);
|
||||
if (password) nvs_set_str(handle, NVS_PASSWORD, password);
|
||||
nvs_set_u8(handle, NVS_ENABLED, enabled);
|
||||
nvs_set_u16(handle, NVS_PERIODICITY, _periodicity);
|
||||
if (user) nvs_set_str(handle, NVS_USER, user);
|
||||
if (password) nvs_set_str(handle, NVS_PASSWORD, password);
|
||||
|
||||
periodicity = _periodicity;
|
||||
nvs_set_u8(handle, NVS_ENABLED, enabled ? 1 : 0);
|
||||
nvs_set_u16(handle, NVS_PERIODICITY, _periodicity);
|
||||
periodicity = _periodicity ? _periodicity : periodicity;
|
||||
|
||||
esp_err_t err = nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) return err;
|
||||
if (err != ESP_OK)
|
||||
return err;
|
||||
|
||||
if (enabled) {
|
||||
if (enabled)
|
||||
client_start();
|
||||
} else {
|
||||
else
|
||||
client_stop();
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* ---------------------- Getters públicos (faltavam) ---------------------- */
|
||||
|
||||
bool mqtt_get_enabled(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
uint8_t val = false;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK) {
|
||||
nvs_get_u8(handle, NVS_ENABLED, &val);
|
||||
uint8_t en = 0;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_u8(handle, NVS_ENABLED, &en);
|
||||
nvs_close(handle);
|
||||
}
|
||||
return val;
|
||||
return en != 0;
|
||||
}
|
||||
|
||||
void mqtt_get_server(char* value)
|
||||
static void nvs_get_str_into(nvs_handle_t handle, const char *key, char *out, size_t outlen)
|
||||
{
|
||||
if (!value) return;
|
||||
value[0] = '\0';
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK) {
|
||||
size_t len = 64;
|
||||
nvs_get_str(handle, NVS_SERVER, value, &len);
|
||||
nvs_close(handle);
|
||||
}
|
||||
if (!out || outlen == 0) return;
|
||||
out[0] = '\0';
|
||||
size_t len = outlen;
|
||||
esp_err_t err = nvs_get_str(handle, key, out, &len);
|
||||
if (err != ESP_OK) out[0] = '\0';
|
||||
}
|
||||
|
||||
void mqtt_get_base_topic(char* value)
|
||||
void mqtt_get_server(char *value)
|
||||
{
|
||||
if (!value) return;
|
||||
value[0] = '\0';
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK) {
|
||||
size_t len = 32;
|
||||
nvs_get_str(handle, NVS_BASE_TOPIC, value, &len);
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_str_into(handle, NVS_SERVER, value, MQTT_MAX_SERVER_LEN);
|
||||
nvs_close(handle);
|
||||
}
|
||||
else if (value) value[0] = '\0';
|
||||
}
|
||||
|
||||
void mqtt_get_user(char* value)
|
||||
void mqtt_get_base_topic(char *value)
|
||||
{
|
||||
if (!value) return;
|
||||
value[0] = '\0';
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK) {
|
||||
size_t len = 32;
|
||||
nvs_get_str(handle, NVS_USER, value, &len);
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_str_into(handle, NVS_BASE_TOPIC, value, MQTT_MAX_BASE_TOPIC_LEN);
|
||||
nvs_close(handle);
|
||||
}
|
||||
else if (value) value[0] = '\0';
|
||||
}
|
||||
|
||||
void mqtt_get_password(char* value)
|
||||
void mqtt_get_user(char *value)
|
||||
{
|
||||
if (!value) return;
|
||||
value[0] = '\0';
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK) {
|
||||
size_t len = 64;
|
||||
nvs_get_str(handle, NVS_PASSWORD, value, &len);
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_str_into(handle, NVS_USER, value, MQTT_MAX_USER_LEN);
|
||||
nvs_close(handle);
|
||||
}
|
||||
else if (value) value[0] = '\0';
|
||||
}
|
||||
|
||||
void mqtt_get_password(char *value)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_str_into(handle, NVS_PASSWORD, value, MQTT_MAX_PASSWORD_LEN);
|
||||
nvs_close(handle);
|
||||
}
|
||||
else if (value) value[0] = '\0';
|
||||
}
|
||||
|
||||
uint16_t mqtt_get_periodicity(void)
|
||||
{
|
||||
// devolve o cache atual; se não tiver sido carregado, lê NVS
|
||||
if (periodicity == 0)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (open_mqtt_nvs(&handle) == ESP_OK)
|
||||
{
|
||||
nvs_get_u16(handle, NVS_PERIODICITY, &periodicity);
|
||||
nvs_close(handle);
|
||||
if (periodicity == 0) periodicity = 30;
|
||||
}
|
||||
else
|
||||
{
|
||||
periodicity = 30;
|
||||
}
|
||||
}
|
||||
return periodicity;
|
||||
}
|
||||
}
|
||||
|
||||
// === Fim de: components/protocols/src/mqtt.c ===
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
#include "protocols.h"
|
||||
#include "date_time.h"
|
||||
//#include "rest.h"
|
||||
#include "mqtt.h"
|
||||
//#include "modbus_tcp.h"
|
||||
|
||||
void protocols_init(void)
|
||||
{
|
||||
date_time_init();
|
||||
/* Serve static files from the SPIFFS data partition */
|
||||
// rest_init("/data");
|
||||
mqtt_init();
|
||||
//modbus_tcp_init();
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
{"Africa/Abidjan", "GMT0"},
|
||||
{"Africa/Algiers", "CET-1"},
|
||||
{"Africa/Bissau", "GMT0"},
|
||||
{"Africa/Cairo", "EET-2EEST,M4.5.5/0,M10.5.4/24"},
|
||||
{"Africa/Casablanca", "<+01>-1"},
|
||||
{"Africa/Ceuta", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Africa/El_Aaiun", "<+01>-1"},
|
||||
{"Africa/Johannesburg", "SAST-2"},
|
||||
{"Africa/Juba", "CAT-2"},
|
||||
{"Africa/Khartoum", "CAT-2"},
|
||||
{"Africa/Lagos", "WAT-1"},
|
||||
{"Africa/Maputo", "CAT-2"},
|
||||
{"Africa/Monrovia", "GMT0"},
|
||||
{"Africa/Nairobi", "EAT-3"},
|
||||
{"Africa/Ndjamena", "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/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/Asuncion", "<-04>4<-03>,M10.1.0/0,M3.4.0/0"},
|
||||
{"America/Bahia", "<-03>3"},
|
||||
{"America/Bahia_Banderas", "CST6"},
|
||||
{"America/Barbados", "AST4"},
|
||||
{"America/Belem", "<-03>3"},
|
||||
{"America/Belize", "CST6"},
|
||||
{"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", "CST6"},
|
||||
{"America/Costa_Rica", "CST6"},
|
||||
{"America/Creston", "MST7"},
|
||||
{"America/Cuiaba", "<-04>4"},
|
||||
{"America/Curacao", "AST4"},
|
||||
{"America/Danmarkshavn", "GMT0"},
|
||||
{"America/Dawson", "MST7"},
|
||||
{"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", "<-02>2<-01>,M3.5.0/-1,M10.5.0/0"},
|
||||
{"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/La_Paz", "<-04>4"},
|
||||
{"America/Lima", "<-05>5"},
|
||||
{"America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0"},
|
||||
{"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", "MST7"},
|
||||
{"America/Menominee", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Merida", "CST6"},
|
||||
{"America/Metlakatla", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/Mexico_City", "CST6"},
|
||||
{"America/Miquelon", "<-03>3<-02>,M3.2.0,M11.1.0"},
|
||||
{"America/Moncton", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"America/Monterrey", "CST6"},
|
||||
{"America/Montevideo", "<-03>3"},
|
||||
{"America/New_York", "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/Nuuk", "<-02>2<-01>,M3.5.0/-1,M10.5.0/0"},
|
||||
{"America/Ojinaga", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Panama", "EST5"},
|
||||
{"America/Paramaribo", "<-03>3"},
|
||||
{"America/Phoenix", "MST7"},
|
||||
{"America/Port-au-Prince", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Porto_Velho", "<-04>4"},
|
||||
{"America/Puerto_Rico", "AST4"},
|
||||
{"America/Punta_Arenas", "<-03>3"},
|
||||
{"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_Johns", "NST3:30NDT,M3.2.0,M11.1.0"},
|
||||
{"America/Swift_Current", "CST6"},
|
||||
{"America/Tegucigalpa", "CST6"},
|
||||
{"America/Thule", "AST4ADT,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/Vancouver", "PST8PDT,M3.2.0,M11.1.0"},
|
||||
{"America/Whitehorse", "MST7"},
|
||||
{"America/Winnipeg", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Yakutat", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"Antarctica/Casey", "<+11>-11"},
|
||||
{"Antarctica/Davis", "<+07>-7"},
|
||||
{"Antarctica/Macquarie", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
|
||||
{"Antarctica/Mawson", "<+05>-5"},
|
||||
{"Antarctica/Palmer", "<-03>3"},
|
||||
{"Antarctica/Rothera", "<-03>3"},
|
||||
{"Antarctica/Troll", "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3"},
|
||||
{"Asia/Almaty", "<+06>-6"},
|
||||
{"Asia/Amman", "<+03>-3"},
|
||||
{"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", "<+03>-3"},
|
||||
{"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.4.4/50,M10.4.4/50"},
|
||||
{"Asia/Hebron", "EET-2EEST,M3.4.4/50,M10.4.4/50"},
|
||||
{"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/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/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"},
|
||||
{"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/South_Georgia", "<-02>2"},
|
||||
{"Atlantic/Stanley", "<-03>3"},
|
||||
{"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/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/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/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/Chisinau", "EET-2EEST,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/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Istanbul", "<+03>-3"},
|
||||
{"Europe/Kaliningrad", "EET-2"},
|
||||
{"Europe/Kyiv", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Kirov", "MSK-3"},
|
||||
{"Europe/Lisbon", "WET0WEST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/London", "GMT0BST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/Madrid", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Malta", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Minsk", "<+03>-3"},
|
||||
{"Europe/Moscow", "MSK-3"},
|
||||
{"Europe/Paris", "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/Saratov", "<+04>-4"},
|
||||
{"Europe/Simferopol", "MSK-3"},
|
||||
{"Europe/Sofia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"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/Vienna", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Vilnius", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Volgograd", "MSK-3"},
|
||||
{"Europe/Warsaw", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Zurich", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Indian/Chagos", "<+06>-6"},
|
||||
{"Indian/Maldives", "<+05>-5"},
|
||||
{"Indian/Mauritius", "<+04>-4"},
|
||||
{"Pacific/Apia", "<+13>-13"},
|
||||
{"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"},
|
||||
{"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/Port_Moresby", "<+10>-10"},
|
||||
{"Pacific/Rarotonga", "<-10>10"},
|
||||
{"Pacific/Tahiti", "<-10>10"},
|
||||
{"Pacific/Tarawa", "<+12>-12"},
|
||||
{"Pacific/Tongatapu", "<+13>-13"},
|
||||
{"Etc/GMT", "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/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/UTC", "UTC0"},
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> -->
|
||||
<!-- Remover link para tailwind.min.css -->
|
||||
<!-- <link href="/tailwind.min.css" rel="stylesheet"> -->
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', 'Helvetica', 'sans-serif'; /* Usando fontes genéricas do sistema */
|
||||
}
|
||||
</style>
|
||||
<title>Vite + React</title>
|
||||
<script type="module" crossorigin src="/assets/index-C_08vMAY.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Doq3307m.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<!-- Adicionar o link para o seu arquivo CSS com o Tailwind configurado -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
Reference in New Issue
Block a user