/*
|
|
|
|
BUILD INFO
|
|
|
|
*/
|
|
|
|
#include "espurna.h"
|
|
#include "utils.h"
|
|
|
|
#include <cstring>
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
namespace espurna {
|
|
namespace build {
|
|
namespace {
|
|
|
|
namespace sdk {
|
|
|
|
espurna::StringView base() {
|
|
// aka `const char SDK_VERSION[]`
|
|
return system_get_sdk_version();
|
|
}
|
|
|
|
espurna::StringView core_version() {
|
|
static const String out = ([]() {
|
|
String out;
|
|
#ifdef ARDUINO_ESP8266_RELEASE
|
|
out = ESP.getCoreVersion();
|
|
if (out.equals("00000000")) {
|
|
out = String(ARDUINO_ESP8266_RELEASE);
|
|
}
|
|
out.replace('_', '.');
|
|
#else
|
|
#define _GET_COREVERSION_STR(X) #X
|
|
#define GET_COREVERSION_STR(X) _GET_COREVERSION_STR(X)
|
|
out = GET_COREVERSION_STR(ARDUINO_ESP8266_GIT_DESC);
|
|
#undef _GET_COREVERSION_STR
|
|
#undef GET_COREVERSION_STR
|
|
#endif
|
|
return out;
|
|
})();
|
|
|
|
return out;
|
|
}
|
|
|
|
espurna::StringView core_revision() {
|
|
static const String out = ([]() {
|
|
#ifdef ARDUINO_ESP8266_GIT_VER
|
|
return String(ARDUINO_ESP8266_GIT_VER, 16);
|
|
#else
|
|
return PSTR("(unspecified)");
|
|
#endif
|
|
})();
|
|
|
|
return out;
|
|
}
|
|
|
|
Sdk get() {
|
|
return Sdk{
|
|
.base = base(),
|
|
.version = core_version(),
|
|
.revision = core_revision(),
|
|
};
|
|
}
|
|
|
|
} // namespace sdk
|
|
|
|
namespace hardware {
|
|
namespace internal {
|
|
|
|
PROGMEM_STRING(Manufacturer, MANUFACTURER);
|
|
PROGMEM_STRING(Device, DEVICE);
|
|
|
|
} // namespace internal
|
|
|
|
constexpr StringView manufacturer() {
|
|
return internal::Manufacturer;
|
|
}
|
|
|
|
constexpr StringView device() {
|
|
return internal::Device;
|
|
}
|
|
|
|
constexpr Hardware get() {
|
|
return Hardware{
|
|
.manufacturer = manufacturer(),
|
|
.device = device(),
|
|
};
|
|
}
|
|
|
|
} // namespace device
|
|
|
|
namespace app {
|
|
namespace internal {
|
|
|
|
alignas(4) static constexpr char Modules[] PROGMEM_STRING_ATTR =
|
|
#if ALEXA_SUPPORT
|
|
"ALEXA "
|
|
#endif
|
|
#if API_SUPPORT
|
|
"API "
|
|
#endif
|
|
#if BUTTON_SUPPORT
|
|
"BUTTON "
|
|
#endif
|
|
#if DEBUG_SERIAL_SUPPORT
|
|
"DEBUG_SERIAL "
|
|
#endif
|
|
#if DEBUG_TELNET_SUPPORT
|
|
"DEBUG_TELNET "
|
|
#endif
|
|
#if DEBUG_UDP_SUPPORT
|
|
"DEBUG_UDP "
|
|
#endif
|
|
#if DEBUG_WEB_SUPPORT
|
|
"DEBUG_WEB "
|
|
#endif
|
|
#if DOMOTICZ_SUPPORT
|
|
"DOMOTICZ "
|
|
#endif
|
|
#if ENCODER_SUPPORT
|
|
"ENCODER "
|
|
#endif
|
|
#if FAN_SUPPORT
|
|
"FAN "
|
|
#endif
|
|
#if HOMEASSISTANT_SUPPORT
|
|
"HOMEASSISTANT "
|
|
#endif
|
|
#if I2C_SUPPORT
|
|
"I2C "
|
|
#endif
|
|
#if INFLUXDB_SUPPORT
|
|
"INFLUXDB "
|
|
#endif
|
|
#if IR_SUPPORT
|
|
"IR "
|
|
#endif
|
|
#if LED_SUPPORT
|
|
"LED "
|
|
#endif
|
|
#if LLMNR_SUPPORT
|
|
"LLMNR "
|
|
#endif
|
|
#if MDNS_SERVER_SUPPORT
|
|
"MDNS "
|
|
#endif
|
|
#if MQTT_SUPPORT
|
|
"MQTT "
|
|
#endif
|
|
#if NETBIOS_SUPPORT
|
|
"NETBIOS "
|
|
#endif
|
|
#if NOFUSS_SUPPORT
|
|
"NOFUSS "
|
|
#endif
|
|
#if NTP_SUPPORT
|
|
"NTP "
|
|
#endif
|
|
#if OTA_ARDUINOOTA_SUPPORT
|
|
"ARDUINO_OTA "
|
|
#endif
|
|
#if OTA_WEB_SUPPORT
|
|
"OTA_WEB "
|
|
#endif
|
|
#if (OTA_CLIENT != OTA_CLIENT_NONE)
|
|
"OTA_CLIENT "
|
|
#endif
|
|
#if PROMETHEUS_SUPPORT
|
|
"METRICS "
|
|
#endif
|
|
#if RELAY_SUPPORT
|
|
"RELAY "
|
|
#endif
|
|
#if RFM69_SUPPORT
|
|
"RFM69 "
|
|
#endif
|
|
#if RFB_SUPPORT
|
|
"RFB "
|
|
#endif
|
|
#if RPN_RULES_SUPPORT
|
|
"RPN_RULES "
|
|
#endif
|
|
#if SCHEDULER_SUPPORT
|
|
"SCHEDULER "
|
|
#endif
|
|
#if SENSOR_SUPPORT
|
|
"SENSOR "
|
|
#endif
|
|
#if SPIFFS_SUPPORT
|
|
"SPIFFS "
|
|
#endif
|
|
#if SSDP_SUPPORT
|
|
"SSDP "
|
|
#endif
|
|
#if TELNET_SUPPORT
|
|
"TELNET "
|
|
#endif
|
|
#if TERMINAL_SUPPORT
|
|
"TERMINAL "
|
|
#endif
|
|
#if GARLAND_SUPPORT
|
|
"GARLAND "
|
|
#endif
|
|
#if THERMOSTAT_SUPPORT
|
|
"THERMOSTAT "
|
|
#endif
|
|
#if THERMOSTAT_DISPLAY_SUPPORT
|
|
"THERMOSTAT_DISPLAY "
|
|
#endif
|
|
#if THINGSPEAK_SUPPORT
|
|
"THINGSPEAK "
|
|
#endif
|
|
#if UART_SUPPORT
|
|
#if UART_SUPPORT_SOFTWARE
|
|
"UART+SW "
|
|
#else
|
|
"UART "
|
|
#endif
|
|
#endif
|
|
#if UART_MQTT_SUPPORT
|
|
"UART_MQTT "
|
|
#endif
|
|
#if WEB_SUPPORT
|
|
#if WEBUI_IMAGE == WEBUI_IMAGE_SMALL
|
|
"WEB_SMALL "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_LIGHT
|
|
"WEB_LIGHT "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_SENSOR
|
|
"WEB_SENSOR "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_RFBRIDGE
|
|
"WEB_RFBRIDGE "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_RFM69
|
|
"WEB_RFM69 "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_LIGHTFOX
|
|
"WEB_LIGHTFOX "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_GARLAND
|
|
"WEB_GARLAND "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_THERMOSTAT
|
|
"WEB_THERMOSTAT "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_CURTAIN
|
|
"WEB_CURTAIN "
|
|
#elif WEBUI_IMAGE == WEBUI_IMAGE_FULL
|
|
"WEB_FULL "
|
|
#endif
|
|
#endif
|
|
"";
|
|
|
|
PROGMEM_STRING(Name, APP_NAME);
|
|
PROGMEM_STRING(Version, APP_VERSION);
|
|
PROGMEM_STRING(Author, APP_AUTHOR);
|
|
PROGMEM_STRING(Website, APP_WEBSITE);
|
|
|
|
PROGMEM_STRING(BuildDate, __DATE__);
|
|
PROGMEM_STRING(BuildTime, __TIME__);
|
|
|
|
} // namespace internal
|
|
|
|
// ref. https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
|
|
//
|
|
// __DATE__
|
|
// > This macro expands to a string constant that describes the date on which the preprocessor is being run.
|
|
// > The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10,
|
|
// > it is padded with a space on the left.
|
|
// >
|
|
// > If GCC cannot determine the current date, it will emit a warning message (once per compilation) and __DATE__ will expand to "??? ?? ????".
|
|
//
|
|
// __TIME__
|
|
// > This macro expands to a string constant that describes the time at which the preprocessor is being run.
|
|
// > The string constant contains eight characters and looks like "23:59:01".
|
|
// >
|
|
// > If GCC cannot determine the current time, it will emit a warning message (once per compilation) and __TIME__ will expand to "??:??:??".
|
|
namespace time {
|
|
|
|
// "Jan 1 1970"
|
|
// ^^^
|
|
constexpr StringView raw_month() {
|
|
return StringView(&internal::BuildDate[0], &internal::BuildDate[3]);
|
|
}
|
|
static_assert(raw_month().length() == 3, "");
|
|
|
|
// "Jan 1 1970"
|
|
// ^^ (with space, or without)
|
|
constexpr StringView raw_day() {
|
|
return (internal::BuildDate[4] == ' ')
|
|
? StringView(&internal::BuildDate[5], &internal::BuildDate[6])
|
|
: StringView(&internal::BuildDate[4], &internal::BuildDate[6]);
|
|
}
|
|
static_assert(raw_day().length() < 3, "");
|
|
|
|
// "Jan 1 1970"
|
|
// ^^^^
|
|
constexpr StringView raw_year() {
|
|
return StringView(&internal::BuildDate[7], &internal::BuildDate[11]);
|
|
}
|
|
static_assert(raw_year().length() == 4, "");
|
|
|
|
// "03:00:00"
|
|
// ^^
|
|
constexpr StringView raw_hour() {
|
|
return StringView(&internal::BuildTime[0], &internal::BuildTime[2]);
|
|
}
|
|
static_assert(raw_hour().length() == 2, "");
|
|
|
|
// "03:00:00"
|
|
// ^^
|
|
constexpr StringView raw_minute() {
|
|
return StringView(&internal::BuildTime[3], &internal::BuildTime[5]);
|
|
}
|
|
static_assert(raw_minute().length() == 2, "");
|
|
|
|
// "03:00:00"
|
|
// ^^
|
|
constexpr StringView raw_second() {
|
|
return StringView(&internal::BuildTime[6], &internal::BuildTime[8]);
|
|
}
|
|
static_assert(raw_second().length() == 2, "");
|
|
|
|
#define STRING_EQUALS(EXPECTED, ACTUAL)\
|
|
(__builtin_memcmp((ACTUAL).c_str(), (EXPECTED), (ACTUAL).length()) == 0)
|
|
|
|
constexpr int from_raw_month(StringView month) {
|
|
return STRING_EQUALS("Jan", month) ? 1 :
|
|
STRING_EQUALS("Feb", month) ? 2 :
|
|
STRING_EQUALS("Mar", month) ? 3 :
|
|
STRING_EQUALS("Apr", month) ? 4 :
|
|
STRING_EQUALS("May", month) ? 5 :
|
|
STRING_EQUALS("Jun", month) ? 6 :
|
|
STRING_EQUALS("Jul", month) ? 7 :
|
|
STRING_EQUALS("Aug", month) ? 8 :
|
|
STRING_EQUALS("Sep", month) ? 9 :
|
|
STRING_EQUALS("Oct", month) ? 10 :
|
|
STRING_EQUALS("Nov", month) ? 11 :
|
|
STRING_EQUALS("Dec", month) ? 12 : 0;
|
|
}
|
|
#undef STRING_EQUALS
|
|
|
|
constexpr int month() {
|
|
return from_raw_month(raw_month());
|
|
}
|
|
|
|
constexpr int from_one_digit(char value) {
|
|
return ((value >= '0') && (value <= '9'))
|
|
? (value - '0')
|
|
: 0;
|
|
}
|
|
|
|
constexpr int from_two_digits(StringView value) {
|
|
return (from_one_digit(value.c_str()[0]) * 10)
|
|
+ from_one_digit(value.c_str()[1]);
|
|
}
|
|
|
|
constexpr int from_raw_day(StringView day) {
|
|
return (day.length() == 2)
|
|
? from_two_digits(day)
|
|
: from_one_digit(*day.c_str());
|
|
}
|
|
|
|
constexpr int day() {
|
|
return from_raw_day(raw_day());
|
|
}
|
|
|
|
constexpr int hour() {
|
|
return from_two_digits(raw_hour());
|
|
}
|
|
|
|
constexpr int minute() {
|
|
return from_two_digits(raw_minute());
|
|
}
|
|
|
|
constexpr int second() {
|
|
return from_two_digits(raw_second());
|
|
}
|
|
|
|
constexpr int from_raw_year(StringView year) {
|
|
return (from_one_digit(year.c_str()[0]) * 1000)
|
|
+ (from_one_digit(year.c_str()[1]) * 100)
|
|
+ (from_one_digit(year.c_str()[2]) * 10)
|
|
+ from_one_digit(year.c_str()[3]);
|
|
}
|
|
|
|
constexpr int year() {
|
|
return from_raw_year(raw_year());
|
|
}
|
|
|
|
} // namespace time
|
|
|
|
constexpr StringView modules() {
|
|
return internal::Modules;
|
|
}
|
|
|
|
constexpr StringView name() {
|
|
return internal::Name;
|
|
}
|
|
|
|
constexpr StringView version() {
|
|
return internal::Version;
|
|
}
|
|
|
|
constexpr StringView author() {
|
|
return internal::Author;
|
|
}
|
|
|
|
constexpr StringView website() {
|
|
return internal::Website;
|
|
}
|
|
|
|
StringView build_time() {
|
|
// 1234-56-78 01:02:03
|
|
static char out[20] = {0};
|
|
|
|
if (out[0] == '\0') {
|
|
// workaround for gcc4.8, explicitly mark as constexpr
|
|
// otherwise, we will read progmem'ed string at runtime
|
|
// (double-check the asm when changing anything here)
|
|
constexpr int year = time::year();
|
|
constexpr int month = time::month();
|
|
constexpr int day = time::day();
|
|
constexpr int hour = time::hour();
|
|
constexpr int minute = time::minute();
|
|
constexpr int second = time::second();
|
|
snprintf_P(out, sizeof(out),
|
|
PSTR("%4d-%02d-%02d %02d:%02d:%02d"),
|
|
year, month, day,
|
|
hour, minute, second);
|
|
}
|
|
|
|
return StringView(out, sizeof(out) - 1);
|
|
}
|
|
|
|
App get() {
|
|
return App{
|
|
.name = name(),
|
|
.version = version(),
|
|
.build_time = build_time(),
|
|
.author = author(),
|
|
.website = website(),
|
|
};
|
|
};
|
|
|
|
} // namespace app
|
|
|
|
Info info() {
|
|
return Info{
|
|
.sdk = sdk::get(),
|
|
.hardware = hardware::get(),
|
|
.app = app::get(),
|
|
};
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace build
|
|
} // namespace espurna
|
|
|
|
espurna::StringView buildTime() {
|
|
return espurna::build::app::build_time();
|
|
}
|
|
|
|
espurna::build::Sdk buildSdk() {
|
|
return espurna::build::sdk::get();
|
|
}
|
|
|
|
espurna::build::Hardware buildHardware() {
|
|
return espurna::build::hardware::get();
|
|
}
|
|
|
|
espurna::build::App buildApp() {
|
|
return espurna::build::app::get();
|
|
}
|
|
|
|
espurna::build::Info buildInfo() {
|
|
return espurna::build::info();
|
|
}
|
|
|
|
espurna::StringView buildModules() {
|
|
return espurna::build::app::modules();
|
|
}
|