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

  1. /*
  2. BUILD INFO
  3. */
  4. #include "espurna.h"
  5. #include "utils.h"
  6. #include <cstring>
  7. //--------------------------------------------------------------------------------
  8. namespace espurna {
  9. namespace build {
  10. namespace {
  11. namespace sdk {
  12. espurna::StringView base() {
  13. // aka `const char SDK_VERSION[]`
  14. return system_get_sdk_version();
  15. }
  16. espurna::StringView core_version() {
  17. static const String out = ([]() {
  18. String out;
  19. #ifdef ARDUINO_ESP8266_RELEASE
  20. out = ESP.getCoreVersion();
  21. if (out.equals("00000000")) {
  22. out = String(ARDUINO_ESP8266_RELEASE);
  23. }
  24. out.replace('_', '.');
  25. #else
  26. #define _GET_COREVERSION_STR(X) #X
  27. #define GET_COREVERSION_STR(X) _GET_COREVERSION_STR(X)
  28. out = GET_COREVERSION_STR(ARDUINO_ESP8266_GIT_DESC);
  29. #undef _GET_COREVERSION_STR
  30. #undef GET_COREVERSION_STR
  31. #endif
  32. return out;
  33. })();
  34. return out;
  35. }
  36. espurna::StringView core_revision() {
  37. static const String out = ([]() {
  38. #ifdef ARDUINO_ESP8266_GIT_VER
  39. return String(ARDUINO_ESP8266_GIT_VER, 16);
  40. #else
  41. return PSTR("(unspecified)");
  42. #endif
  43. })();
  44. return out;
  45. }
  46. Sdk get() {
  47. return Sdk{
  48. .base = base(),
  49. .version = core_version(),
  50. .revision = core_revision(),
  51. };
  52. }
  53. } // namespace sdk
  54. namespace hardware {
  55. namespace internal {
  56. PROGMEM_STRING(Manufacturer, MANUFACTURER);
  57. PROGMEM_STRING(Device, DEVICE);
  58. } // namespace internal
  59. constexpr StringView manufacturer() {
  60. return internal::Manufacturer;
  61. }
  62. constexpr StringView device() {
  63. return internal::Device;
  64. }
  65. constexpr Hardware get() {
  66. return Hardware{
  67. .manufacturer = manufacturer(),
  68. .device = device(),
  69. };
  70. }
  71. } // namespace device
  72. namespace app {
  73. namespace internal {
  74. alignas(4) static constexpr char Modules[] PROGMEM_STRING_ATTR =
  75. #if ALEXA_SUPPORT
  76. "ALEXA "
  77. #endif
  78. #if API_SUPPORT
  79. "API "
  80. #endif
  81. #if BUTTON_SUPPORT
  82. "BUTTON "
  83. #endif
  84. #if DEBUG_SERIAL_SUPPORT
  85. "DEBUG_SERIAL "
  86. #endif
  87. #if DEBUG_TELNET_SUPPORT
  88. "DEBUG_TELNET "
  89. #endif
  90. #if DEBUG_UDP_SUPPORT
  91. "DEBUG_UDP "
  92. #endif
  93. #if DEBUG_WEB_SUPPORT
  94. "DEBUG_WEB "
  95. #endif
  96. #if DOMOTICZ_SUPPORT
  97. "DOMOTICZ "
  98. #endif
  99. #if ENCODER_SUPPORT
  100. "ENCODER "
  101. #endif
  102. #if FAN_SUPPORT
  103. "FAN "
  104. #endif
  105. #if HOMEASSISTANT_SUPPORT
  106. "HOMEASSISTANT "
  107. #endif
  108. #if I2C_SUPPORT
  109. "I2C "
  110. #endif
  111. #if INFLUXDB_SUPPORT
  112. "INFLUXDB "
  113. #endif
  114. #if IR_SUPPORT
  115. "IR "
  116. #endif
  117. #if LED_SUPPORT
  118. "LED "
  119. #endif
  120. #if LLMNR_SUPPORT
  121. "LLMNR "
  122. #endif
  123. #if MDNS_SERVER_SUPPORT
  124. "MDNS "
  125. #endif
  126. #if MQTT_SUPPORT
  127. "MQTT "
  128. #endif
  129. #if NETBIOS_SUPPORT
  130. "NETBIOS "
  131. #endif
  132. #if NOFUSS_SUPPORT
  133. "NOFUSS "
  134. #endif
  135. #if NTP_SUPPORT
  136. "NTP "
  137. #endif
  138. #if OTA_ARDUINOOTA_SUPPORT
  139. "ARDUINO_OTA "
  140. #endif
  141. #if OTA_WEB_SUPPORT
  142. "OTA_WEB "
  143. #endif
  144. #if (OTA_CLIENT != OTA_CLIENT_NONE)
  145. "OTA_CLIENT "
  146. #endif
  147. #if PROMETHEUS_SUPPORT
  148. "METRICS "
  149. #endif
  150. #if RELAY_SUPPORT
  151. "RELAY "
  152. #endif
  153. #if RFM69_SUPPORT
  154. "RFM69 "
  155. #endif
  156. #if RFB_SUPPORT
  157. "RFB "
  158. #endif
  159. #if RPN_RULES_SUPPORT
  160. "RPN_RULES "
  161. #endif
  162. #if SCHEDULER_SUPPORT
  163. "SCHEDULER "
  164. #endif
  165. #if SENSOR_SUPPORT
  166. "SENSOR "
  167. #endif
  168. #if SPIFFS_SUPPORT
  169. "SPIFFS "
  170. #endif
  171. #if SSDP_SUPPORT
  172. "SSDP "
  173. #endif
  174. #if TELNET_SUPPORT
  175. "TELNET "
  176. #endif
  177. #if TERMINAL_SUPPORT
  178. "TERMINAL "
  179. #endif
  180. #if GARLAND_SUPPORT
  181. "GARLAND "
  182. #endif
  183. #if THERMOSTAT_SUPPORT
  184. "THERMOSTAT "
  185. #endif
  186. #if THERMOSTAT_DISPLAY_SUPPORT
  187. "THERMOSTAT_DISPLAY "
  188. #endif
  189. #if THINGSPEAK_SUPPORT
  190. "THINGSPEAK "
  191. #endif
  192. #if UART_SUPPORT
  193. #if UART_SUPPORT_SOFTWARE
  194. "UART+SW "
  195. #else
  196. "UART "
  197. #endif
  198. #endif
  199. #if UART_MQTT_SUPPORT
  200. "UART_MQTT "
  201. #endif
  202. #if WEB_SUPPORT
  203. #if WEBUI_IMAGE == WEBUI_IMAGE_SMALL
  204. "WEB_SMALL "
  205. #elif WEBUI_IMAGE == WEBUI_IMAGE_LIGHT
  206. "WEB_LIGHT "
  207. #elif WEBUI_IMAGE == WEBUI_IMAGE_SENSOR
  208. "WEB_SENSOR "
  209. #elif WEBUI_IMAGE == WEBUI_IMAGE_RFBRIDGE
  210. "WEB_RFBRIDGE "
  211. #elif WEBUI_IMAGE == WEBUI_IMAGE_RFM69
  212. "WEB_RFM69 "
  213. #elif WEBUI_IMAGE == WEBUI_IMAGE_LIGHTFOX
  214. "WEB_LIGHTFOX "
  215. #elif WEBUI_IMAGE == WEBUI_IMAGE_GARLAND
  216. "WEB_GARLAND "
  217. #elif WEBUI_IMAGE == WEBUI_IMAGE_THERMOSTAT
  218. "WEB_THERMOSTAT "
  219. #elif WEBUI_IMAGE == WEBUI_IMAGE_CURTAIN
  220. "WEB_CURTAIN "
  221. #elif WEBUI_IMAGE == WEBUI_IMAGE_FULL
  222. "WEB_FULL "
  223. #endif
  224. #endif
  225. "";
  226. PROGMEM_STRING(Name, APP_NAME);
  227. PROGMEM_STRING(Version, APP_VERSION);
  228. PROGMEM_STRING(Author, APP_AUTHOR);
  229. PROGMEM_STRING(Website, APP_WEBSITE);
  230. PROGMEM_STRING(BuildDate, __DATE__);
  231. PROGMEM_STRING(BuildTime, __TIME__);
  232. } // namespace internal
  233. // ref. https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
  234. //
  235. // __DATE__
  236. // > This macro expands to a string constant that describes the date on which the preprocessor is being run.
  237. // > The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10,
  238. // > it is padded with a space on the left.
  239. // >
  240. // > If GCC cannot determine the current date, it will emit a warning message (once per compilation) and __DATE__ will expand to "??? ?? ????".
  241. //
  242. // __TIME__
  243. // > This macro expands to a string constant that describes the time at which the preprocessor is being run.
  244. // > The string constant contains eight characters and looks like "23:59:01".
  245. // >
  246. // > If GCC cannot determine the current time, it will emit a warning message (once per compilation) and __TIME__ will expand to "??:??:??".
  247. namespace time {
  248. // "Jan 1 1970"
  249. // ^^^
  250. constexpr StringView raw_month() {
  251. return StringView(&internal::BuildDate[0], &internal::BuildDate[3]);
  252. }
  253. static_assert(raw_month().length() == 3, "");
  254. // "Jan 1 1970"
  255. // ^^ (with space, or without)
  256. constexpr StringView raw_day() {
  257. return (internal::BuildDate[4] == ' ')
  258. ? StringView(&internal::BuildDate[5], &internal::BuildDate[6])
  259. : StringView(&internal::BuildDate[4], &internal::BuildDate[6]);
  260. }
  261. static_assert(raw_day().length() < 3, "");
  262. // "Jan 1 1970"
  263. // ^^^^
  264. constexpr StringView raw_year() {
  265. return StringView(&internal::BuildDate[7], &internal::BuildDate[11]);
  266. }
  267. static_assert(raw_year().length() == 4, "");
  268. // "03:00:00"
  269. // ^^
  270. constexpr StringView raw_hour() {
  271. return StringView(&internal::BuildTime[0], &internal::BuildTime[2]);
  272. }
  273. static_assert(raw_hour().length() == 2, "");
  274. // "03:00:00"
  275. // ^^
  276. constexpr StringView raw_minute() {
  277. return StringView(&internal::BuildTime[3], &internal::BuildTime[5]);
  278. }
  279. static_assert(raw_minute().length() == 2, "");
  280. // "03:00:00"
  281. // ^^
  282. constexpr StringView raw_second() {
  283. return StringView(&internal::BuildTime[6], &internal::BuildTime[8]);
  284. }
  285. static_assert(raw_second().length() == 2, "");
  286. #define STRING_EQUALS(EXPECTED, ACTUAL)\
  287. (__builtin_memcmp((ACTUAL).c_str(), (EXPECTED), (ACTUAL).length()) == 0)
  288. constexpr int from_raw_month(StringView month) {
  289. return STRING_EQUALS("Jan", month) ? 1 :
  290. STRING_EQUALS("Feb", month) ? 2 :
  291. STRING_EQUALS("Mar", month) ? 3 :
  292. STRING_EQUALS("Apr", month) ? 4 :
  293. STRING_EQUALS("May", month) ? 5 :
  294. STRING_EQUALS("Jun", month) ? 6 :
  295. STRING_EQUALS("Jul", month) ? 7 :
  296. STRING_EQUALS("Aug", month) ? 8 :
  297. STRING_EQUALS("Sep", month) ? 9 :
  298. STRING_EQUALS("Oct", month) ? 10 :
  299. STRING_EQUALS("Nov", month) ? 11 :
  300. STRING_EQUALS("Dec", month) ? 12 : 0;
  301. }
  302. #undef STRING_EQUALS
  303. constexpr int month() {
  304. return from_raw_month(raw_month());
  305. }
  306. constexpr int from_one_digit(char value) {
  307. return ((value >= '0') && (value <= '9'))
  308. ? (value - '0')
  309. : 0;
  310. }
  311. constexpr int from_two_digits(StringView value) {
  312. return (from_one_digit(value.c_str()[0]) * 10)
  313. + from_one_digit(value.c_str()[1]);
  314. }
  315. constexpr int from_raw_day(StringView day) {
  316. return (day.length() == 2)
  317. ? from_two_digits(day)
  318. : from_one_digit(*day.c_str());
  319. }
  320. constexpr int day() {
  321. return from_raw_day(raw_day());
  322. }
  323. constexpr int hour() {
  324. return from_two_digits(raw_hour());
  325. }
  326. constexpr int minute() {
  327. return from_two_digits(raw_minute());
  328. }
  329. constexpr int second() {
  330. return from_two_digits(raw_second());
  331. }
  332. constexpr int from_raw_year(StringView year) {
  333. return (from_one_digit(year.c_str()[0]) * 1000)
  334. + (from_one_digit(year.c_str()[1]) * 100)
  335. + (from_one_digit(year.c_str()[2]) * 10)
  336. + from_one_digit(year.c_str()[3]);
  337. }
  338. constexpr int year() {
  339. return from_raw_year(raw_year());
  340. }
  341. } // namespace time
  342. constexpr StringView modules() {
  343. return internal::Modules;
  344. }
  345. constexpr StringView name() {
  346. return internal::Name;
  347. }
  348. constexpr StringView version() {
  349. return internal::Version;
  350. }
  351. constexpr StringView author() {
  352. return internal::Author;
  353. }
  354. constexpr StringView website() {
  355. return internal::Website;
  356. }
  357. StringView build_time() {
  358. // 1234-56-78 01:02:03
  359. static char out[20] = {0};
  360. if (out[0] == '\0') {
  361. // workaround for gcc4.8, explicitly mark as constexpr
  362. // otherwise, we will read progmem'ed string at runtime
  363. // (double-check the asm when changing anything here)
  364. constexpr int year = time::year();
  365. constexpr int month = time::month();
  366. constexpr int day = time::day();
  367. constexpr int hour = time::hour();
  368. constexpr int minute = time::minute();
  369. constexpr int second = time::second();
  370. snprintf_P(out, sizeof(out),
  371. PSTR("%4d-%02d-%02d %02d:%02d:%02d"),
  372. year, month, day,
  373. hour, minute, second);
  374. }
  375. return StringView(out, sizeof(out) - 1);
  376. }
  377. App get() {
  378. return App{
  379. .name = name(),
  380. .version = version(),
  381. .build_time = build_time(),
  382. .author = author(),
  383. .website = website(),
  384. };
  385. };
  386. } // namespace app
  387. Info info() {
  388. return Info{
  389. .sdk = sdk::get(),
  390. .hardware = hardware::get(),
  391. .app = app::get(),
  392. };
  393. }
  394. } // namespace
  395. } // namespace build
  396. } // namespace espurna
  397. espurna::StringView buildTime() {
  398. return espurna::build::app::build_time();
  399. }
  400. espurna::build::Sdk buildSdk() {
  401. return espurna::build::sdk::get();
  402. }
  403. espurna::build::Hardware buildHardware() {
  404. return espurna::build::hardware::get();
  405. }
  406. espurna::build::App buildApp() {
  407. return espurna::build::app::get();
  408. }
  409. espurna::build::Info buildInfo() {
  410. return espurna::build::info();
  411. }
  412. espurna::StringView buildModules() {
  413. return espurna::build::app::modules();
  414. }