Config.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #define LOG_LOCAL_LEVEL ESP_LOG_INFO
  2. #include "Config.h"
  3. #include "PBW.h"
  4. #include "WifiList.h"
  5. #include "bootstate.h"
  6. #include "DAC.pb.h"
  7. #include "esp_log.h"
  8. #include "esp_system.h"
  9. #include "pb_common.h" // Nanopb header for encoding (serialization)
  10. #include "pb_decode.h" // Nanopb header for decoding (deserialization)
  11. #include "pb_encode.h" // Nanopb header for encoding (serialization)
  12. #include "tools.h"
  13. #include <algorithm>
  14. #include <cctype>
  15. #include <sstream>
  16. #include <stdexcept>
  17. #include <string.h>
  18. static const char* TAG = "Configurator";
  19. using namespace System;
  20. __attribute__((section(".ext_ram.bss"))) sys_config* platform;
  21. __attribute__((section(".ext_ram.bss"))) sys_state_data* sys_state;
  22. __attribute__((section(".ext_ram.bss"))) sys_dac_default_sets* default_dac_sets;
  23. __attribute__((section(".ext_ram.bss"))) System::PB<sys_config> configWrapper("config", &sys_config_msg, sizeof(sys_config_msg));
  24. __attribute__((section(".ext_ram.bss"))) System::PB<sys_state_data> stateWrapper("state", &sys_state_data_msg, sizeof(sys_state_data_msg));
  25. __attribute__((section(".ext_ram.bss"))) System::PB<sys_dac_default_sets> defaultSets("default_sets", &sys_dac_default_sets_msg, sizeof(sys_dac_default_sets_msg));
  26. const int MaxDelay = 1000;
  27. bool config_update_mac_string(const pb_msgdesc_t* desc, uint32_t field_tag, void* message) {
  28. pb_field_iter_t iter;
  29. if (pb_field_iter_begin(&iter, desc, message) && pb_field_iter_find(&iter, field_tag)) {
  30. if (!iter.pData) {
  31. ESP_LOGW(TAG, "Unable to check mac string member. Data not initialized");
  32. return false;
  33. }
  34. if (iter.pData) {
  35. auto curvalue = std::string((char*)iter.pData);
  36. if (curvalue.find(get_mac_str()) != std::string::npos) {
  37. ESP_LOGD(TAG, "Entry already has mac string: %s", curvalue.c_str());
  38. return true;
  39. }
  40. if (curvalue.find("@@init_from_mac@@") == std::string::npos) {
  41. ESP_LOGW(TAG, "Member not configured for mac address or was overwritten: %s", curvalue.c_str());
  42. return false;
  43. }
  44. auto newval = std::string("squeezelite-") + get_mac_str();
  45. if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER) {
  46. ESP_LOGD(TAG, "Field is a pointer. Freeing previous value if any: %s", STR_OR_BLANK((char*)iter.pField));
  47. FREE_AND_NULL(*(char**)iter.pField);
  48. ESP_LOGD(TAG, "Field is a pointer. Setting new value as %s", newval.c_str());
  49. *(char**)iter.pField = strdup_psram(newval.c_str());
  50. } else if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC) {
  51. ESP_LOGD(TAG, "Static string. Setting new value as %s from %s", newval.c_str(), STR_OR_BLANK((char*)iter.pData));
  52. memset((char*)iter.pData, 0x00, iter.data_size);
  53. strncpy((char*)iter.pData, newval.c_str(), iter.data_size);
  54. }
  55. } else {
  56. ESP_LOGE(TAG, "Set mac string failed: member should be initialized with default");
  57. return false;
  58. }
  59. }
  60. return true;
  61. }
  62. bool set_pb_string_from_mac(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) {
  63. if (!stream) {
  64. // This is a size calculation pass, return true to indicate field presence
  65. return true;
  66. }
  67. // Generate the string based on MAC and prefix
  68. const char* prefix = reinterpret_cast<const char*>(*arg);
  69. char* value = alloc_get_string_with_mac(prefix && strlen(prefix) > 0 ? prefix : "squeezelite-");
  70. // Write the string to the stream
  71. if (!pb_encode_string(stream, (uint8_t*)value, strlen(value))) {
  72. free(value); // Free memory if encoding fails
  73. return false;
  74. }
  75. free(value); // Free memory after encoding
  76. return true;
  77. }
  78. bool config_erase_config() {
  79. // make sure the config object doesn't have
  80. // any pending changes to commit
  81. ESP_LOGW(TAG, "Erasing configuration object and rebooting");
  82. configWrapper.ResetModified();
  83. // Erase the file and reboot
  84. erase_path(configWrapper.GetFileName().c_str(), false);
  85. guided_factory();
  86. return true;
  87. }
  88. void set_mac_string() {
  89. auto expected = std::string("squeezelite-") + get_mac_str();
  90. bool changed = false;
  91. auto config = configWrapper.get();
  92. changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_device_tag, &config->names) || changed;
  93. changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_airplay_tag, &config->names) || changed;
  94. changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_spotify_tag, &config->names) || changed;
  95. changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_bluetooth_tag, &config->names) || changed;
  96. changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_squeezelite_tag, &config->names) || changed;
  97. changed = config_update_mac_string(&sys_names_config_msg, sys_names_config_wifi_ap_name_tag, &config->names) || changed;
  98. if (changed) {
  99. ESP_LOGI(TAG, "One or more name was changed. Committing");
  100. configWrapper.RaiseChangedAsync();
  101. }
  102. }
  103. void config_load() {
  104. ESP_LOGI(TAG, "Loading configuration.");
  105. bool restart = false;
  106. sys_state = stateWrapper.get();
  107. platform = configWrapper.get();
  108. default_dac_sets = defaultSets.get();
  109. assert(platform != nullptr);
  110. assert(sys_state != nullptr);
  111. assert(default_dac_sets != nullptr);
  112. configWrapper.get()->net.credentials = reinterpret_cast<sys_net_wifi_entry*>(new WifiList("wifi"));
  113. assert(configWrapper.get()->net.credentials != nullptr);
  114. if (!stateWrapper.FileExists()) {
  115. ESP_LOGI(TAG, "State file not found or is empty. ");
  116. stateWrapper.Reinitialize(true);
  117. stateWrapper.SetTarget(CONFIG_FW_PLATFORM_NAME);
  118. } else {
  119. stateWrapper.LoadFile();
  120. }
  121. if (!configWrapper.FileExists()) {
  122. ESP_LOGI(TAG, "Configuration file not found or is empty. ");
  123. configWrapper.Reinitialize(true);
  124. ESP_LOGI(TAG, "Current device name after load: %s", platform->names.device);
  125. configWrapper.SetTarget(CONFIG_FW_PLATFORM_NAME,true);
  126. set_mac_string();
  127. ESP_LOGW(TAG, "Restart required after initializing configuration");
  128. restart = true;
  129. } else {
  130. configWrapper.LoadFile();
  131. if (configWrapper.GetTargetName().empty() && !std::string(CONFIG_FW_PLATFORM_NAME).empty()) {
  132. ESP_LOGW(TAG, "Config target is empty. Updating to %s", CONFIG_FW_PLATFORM_NAME);
  133. configWrapper.Reinitialize(false,true,std::string(CONFIG_FW_PLATFORM_NAME));
  134. ESP_LOGW(TAG, "Restart required due to target change");
  135. restart = true;
  136. }
  137. }
  138. if (!defaultSets.FileExists()) {
  139. ESP_LOGE(TAG, "Default Sets file not found or is empty. (%s)", defaultSets.GetFileName().c_str());
  140. } else {
  141. defaultSets.LoadFile();
  142. }
  143. if (restart) {
  144. network_async_reboot(OTA);
  145. }
  146. }
  147. void config_dump_config() {
  148. auto serialized = configWrapper.Encode();
  149. dump_data(serialized.data(), serialized.size());
  150. }
  151. void config_set_target(const char* target_name, bool reset) {
  152. std::string new_target = std::string(target_name);
  153. bool restart = false;
  154. if (configWrapper.GetTargetName() != new_target) {
  155. ESP_LOGI(TAG, "Setting configuration target name to %s, %s", target_name, reset ? "full reset" : "reapply only");
  156. if (reset) {
  157. ESP_LOGD(TAG, "Reinitializing Config structure");
  158. configWrapper.Reinitialize(false,true,new_target);
  159. } else {
  160. ESP_LOGD(TAG, "Loading Config target values for %s", target_name);
  161. configWrapper.SetTarget(target_name,true);
  162. configWrapper.LoadTargetValues();
  163. configWrapper.RaiseChangedAsync();
  164. }
  165. restart = true;
  166. } else {
  167. ESP_LOGW(TAG, "Target name has no change");
  168. }
  169. if (stateWrapper.GetTargetName() != new_target) {
  170. ESP_LOGI(TAG, "Setting state target name to %s, %s", target_name, reset ? "full reset" : "reapply only");
  171. restart=true;
  172. if (reset) {
  173. ESP_LOGD(TAG, "Reinitializing State structure");
  174. stateWrapper.Reinitialize(false,true,new_target);
  175. } else {
  176. ESP_LOGD(TAG, "Loading State target values for %s", target_name);
  177. stateWrapper.SetTarget(target_name,true);
  178. stateWrapper.LoadTargetValues();
  179. stateWrapper.RaiseChangedAsync();
  180. }
  181. }
  182. ESP_LOGD(TAG, "Done updating target to %s", target_name);
  183. if(restart){
  184. network_async_reboot(RESTART);
  185. }
  186. }
  187. void config_set_target_no_reset(const char* target_name) { config_set_target(target_name, false); }
  188. void config_set_target_reset(const char* target_name) { config_set_target(target_name, true); }
  189. void config_raise_changed(bool sync) {
  190. if (sync) {
  191. configWrapper.CommitChanges();
  192. } else {
  193. configWrapper.RaiseChangedAsync();
  194. }
  195. }
  196. void config_commit_protowrapper(void* protoWrapper) {
  197. ESP_LOGD(TAG, "Committing synchronously");
  198. PBHelper::SyncCommit(protoWrapper);
  199. }
  200. void config_commit_state() { stateWrapper.CommitChanges(); }
  201. void config_raise_state_changed() { stateWrapper.RaiseChangedAsync(); }
  202. bool config_has_changes() { return configWrapper.HasChanges() || stateWrapper.HasChanges(); }
  203. bool config_waitcommit() { return configWrapper.WaitForCommit(2) && stateWrapper.WaitForCommit(2); }
  204. bool config_http_send_config(httpd_req_t* req) {
  205. try {
  206. auto data = configWrapper.Encode();
  207. httpd_resp_send(req, (const char*)data.data(), data.size());
  208. return true;
  209. } catch (const std::runtime_error& e) {
  210. std::string errdesc = (std::string("Unable to get configuration: ") + e.what());
  211. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, errdesc.c_str());
  212. }
  213. return false;
  214. }
  215. bool system_set_string(const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value) {
  216. pb_field_iter_t iter;
  217. ESP_LOGD(TAG,"system_set_string. Getting new value");
  218. std::string newval = std::string(STR_OR_BLANK(value));
  219. ESP_LOGD(TAG,"system_set_string. Done getting new value");
  220. ESP_LOGD(TAG, "Setting value [%s] in message field tag %d", newval.c_str(), field_tag);
  221. if (pb_field_iter_begin(&iter, desc, message) && pb_field_iter_find(&iter, field_tag)) {
  222. std::string old= std::string((char*)iter.pData);
  223. if (iter.pData && old == newval) {
  224. ESP_LOGW(TAG, "No change, from and to values are the same: [%s]", newval.c_str());
  225. return false;
  226. }
  227. if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER) {
  228. ESP_LOGD(TAG, "Field is a pointer. Freeing previous value if any: %s", STR_OR_BLANK((char*)iter.pField));
  229. FREE_AND_NULL(*(char**)iter.pField);
  230. ESP_LOGD(TAG, "Field is a pointer. Setting new value ");
  231. if (!newval.empty()) {
  232. *(char**)iter.pField = strdup_psram(newval.c_str());
  233. }
  234. } else if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC) {
  235. ESP_LOGD(TAG, "Static string. Setting new value. Existing value: %s", (char*)iter.pData);
  236. memset((char*)iter.pData, 0x00, iter.data_size);
  237. if (!newval.empty()) {
  238. strncpy((char*)iter.pData, newval.c_str(), iter.data_size);
  239. }
  240. }
  241. ESP_LOGD(TAG, "Done setting value ");
  242. }
  243. return true;
  244. }