Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

478 lines
10 KiB

/*
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();
}