mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 19:47:02 +03:00
Compare commits
5 Commits
build-numb
...
Muse.16.16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa5b2c8e45 | ||
|
|
9b97404fa2 | ||
|
|
a0d3c60f62 | ||
|
|
b60aed659a | ||
|
|
484d8c54a8 |
@@ -1,3 +1,7 @@
|
||||
2023-10-11
|
||||
- Reduce the size of binaries (Fixes https://github.com/sle118/squeezelite-esp32/issues/329)
|
||||
- [WEB] Allow running without LMS with option "Audio/Disable Squeezelite"
|
||||
|
||||
2023-10.07
|
||||
- catchup with official cspot
|
||||
|
||||
|
||||
48
ToggleGitTracking.ps1
Normal file
48
ToggleGitTracking.ps1
Normal file
@@ -0,0 +1,48 @@
|
||||
param (
|
||||
[Parameter(Position=0, Mandatory=$false)]
|
||||
[ValidateSet("t", "u")]
|
||||
[string]$option
|
||||
)
|
||||
|
||||
# Define the directory to apply changes to
|
||||
$targetDir = "components\wifi-manager\webapp\dist"
|
||||
|
||||
# Get the current directory
|
||||
$currentDir = Get-Location
|
||||
|
||||
# Get list of files from the file system
|
||||
$fsFiles = Get-ChildItem -Recurse $targetDir -File | ForEach-Object {
|
||||
$_.FullName.Substring($currentDir.Path.Length + 1).Replace("\", "/")
|
||||
}
|
||||
|
||||
# Get list of files from the Git index
|
||||
$indexFiles = git ls-files -s $targetDir | ForEach-Object {
|
||||
($_ -split "\s+")[3]
|
||||
}
|
||||
|
||||
# Combine and remove duplicates
|
||||
$allFiles = $fsFiles + $indexFiles | Sort-Object -Unique
|
||||
|
||||
# Apply the git command based on the option
|
||||
$allFiles | ForEach-Object {
|
||||
$relativePath = $_
|
||||
$isInIndex = $indexFiles -contains $relativePath
|
||||
|
||||
if ($null -eq $option) {
|
||||
$status = if ($isInIndex) { 'tracked' } else { 'not tracked' }
|
||||
Write-Host "$relativePath is $status"
|
||||
}
|
||||
elseif ($isInIndex) {
|
||||
if ($option -eq "t") {
|
||||
git update-index --no-skip-worktree $relativePath
|
||||
Write-Host "Started tracking changes in $relativePath"
|
||||
}
|
||||
elseif ($option -eq "u") {
|
||||
git update-index --skip-worktree $relativePath
|
||||
Write-Host "Stopped tracking changes in $relativePath"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host "File $relativePath is not tracked."
|
||||
}
|
||||
}
|
||||
124
components/metrics/Batch.cpp
Normal file
124
components/metrics/Batch.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
|
||||
#include "Batch.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_tls.h"
|
||||
#include "nvs_flash.h"
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
#include "esp_system.h"
|
||||
#include "http_handlers.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs_utilities.h"
|
||||
#include "tools.h"
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <sys/param.h>
|
||||
#if CONFIG_WITH_METRICS
|
||||
static const char* const TAG = "MetricsBatch";
|
||||
static const char* const feature_evt_name = "$feature_flag_called";
|
||||
static const char* const feature_flag_name = "$feature_flag";
|
||||
static const char* const feature_flag_response_name = "$feature_flag_response";
|
||||
|
||||
namespace Metrics {
|
||||
|
||||
Event& Batch::add_feature_event() { return add_event(feature_evt_name); }
|
||||
void Batch::add_remove_feature_event(const char* name, bool active) {
|
||||
if (!active) {
|
||||
remove_feature_event(name);
|
||||
} else {
|
||||
add_event(feature_evt_name).add_property(feature_flag_name, name);
|
||||
}
|
||||
}
|
||||
Event& Batch::add_feature_variant_event(const char* const name, const char* const value) {
|
||||
return add_event(feature_evt_name)
|
||||
.add_property(feature_flag_name, name)
|
||||
.add_property(feature_flag_response_name, value);
|
||||
}
|
||||
void Batch::remove_feature_event(const char* name) {
|
||||
for (Metrics::Event& e : _events) {
|
||||
if (strcmp(e.get_name(), feature_evt_name) == 0) {
|
||||
e.remove_property(feature_flag_name, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
cJSON* Batch::to_json() {
|
||||
cJSON* batch_json = cJSON_CreateArray();
|
||||
for (Metrics::Event& e : _events) {
|
||||
cJSON_AddItemToArray(batch_json, e.to_json(_metrics_uid.c_str()));
|
||||
}
|
||||
cJSON* message = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(message, "batch", batch_json);
|
||||
cJSON_AddStringToObject(message, "api_key", _api_key);
|
||||
return batch_json;
|
||||
}
|
||||
char* Batch::to_json_str() {
|
||||
cJSON* json = to_json();
|
||||
char* json_str = cJSON_PrintUnformatted(json);
|
||||
cJSON_Delete(json);
|
||||
return json_str;
|
||||
}
|
||||
|
||||
void Batch::push() {
|
||||
int status_code = 0;
|
||||
if (_metrics_uid.empty() && !_warned) {
|
||||
ESP_LOGW(TAG, "Metrics disabled; no CID found");
|
||||
_warned = true;
|
||||
return;
|
||||
}
|
||||
|
||||
char* json_str = to_json_str();
|
||||
ESP_LOGV(TAG, "Metrics payload: %s", json_str);
|
||||
time_t start_time = millis();
|
||||
|
||||
status_code = metrics_http_post_request(json_str, _url);
|
||||
|
||||
if (status_code == 200 || status_code == 204) {
|
||||
_events.clear();
|
||||
}
|
||||
FREE_AND_NULL(json_str)
|
||||
ESP_LOGD(TAG, "Total duration for metrics call: %lu. ", millis() - start_time);
|
||||
}
|
||||
|
||||
void Batch::build_guid() {
|
||||
uint8_t raw[16];
|
||||
std::ostringstream oss;
|
||||
esp_fill_random(raw, 16);
|
||||
std::for_each(std::begin(raw), std::end(raw), [&oss](const uint8_t& byte) {
|
||||
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
|
||||
});
|
||||
_metrics_uid = oss.str();
|
||||
}
|
||||
void Batch::assign_id() {
|
||||
size_t size = 0;
|
||||
esp_err_t esp_err = ESP_OK;
|
||||
_metrics_uid = std::string((char*)get_nvs_value_alloc_for_partition(
|
||||
NVS_DEFAULT_PART_NAME, TAG, NVS_TYPE_BLOB, "cid", &size));
|
||||
if (_metrics_uid[0] == 'G') {
|
||||
ESP_LOGW(TAG, "Invalid ID. %s", _metrics_uid.c_str());
|
||||
_metrics_uid.clear();
|
||||
}
|
||||
if (_metrics_uid.empty()) {
|
||||
build_guid();
|
||||
if (_metrics_uid.empty()) {
|
||||
ESP_LOGE(TAG, "ID Failed");
|
||||
return;
|
||||
}
|
||||
ESP_LOGW(TAG, "Metrics ID: %s", _metrics_uid.c_str());
|
||||
esp_err = store_nvs_value_len_for_partition(NVS_DEFAULT_PART_NAME, TAG, NVS_TYPE_BLOB,
|
||||
"cid", _metrics_uid.c_str(), _metrics_uid.length() + 1);
|
||||
if (esp_err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Store ID failed: %s", esp_err_to_name(esp_err));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Metrics
|
||||
#endif
|
||||
46
components/metrics/Batch.h
Normal file
46
components/metrics/Batch.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "Events.h"
|
||||
#include <string>
|
||||
#ifdef __cplusplus
|
||||
namespace Metrics {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
class Batch {
|
||||
private:
|
||||
std::list<Event> _events;
|
||||
bool _warned = false;
|
||||
std::string _metrics_uid = nullptr;
|
||||
const char* _api_key = nullptr;
|
||||
const char* _url = nullptr;
|
||||
void build_guid();
|
||||
void assign_id();
|
||||
|
||||
public:
|
||||
Batch() = default;
|
||||
void configure(const char* api_key, const char* url) {
|
||||
_api_key = api_key;
|
||||
_url = url;
|
||||
assign_id();
|
||||
}
|
||||
Event& add_feature_event();
|
||||
void add_remove_feature_event(const char* name, bool active);
|
||||
Event& add_feature_variant_event(const char* const name, const char* const value);
|
||||
Event& add_event(const char* name) {
|
||||
_events.emplace_back(name);
|
||||
return _events.back();
|
||||
}
|
||||
|
||||
bool has_events() const { return !_events.empty(); }
|
||||
void remove_feature_event(const char* name);
|
||||
cJSON* to_json();
|
||||
char* to_json_str();
|
||||
void push();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
5
components/metrics/CMakeLists.txt
Normal file
5
components/metrics/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES json tools platform_config wifi-manager esp-tls platform_config
|
||||
PRIV_REQUIRES esp32 freertos
|
||||
)
|
||||
98
components/metrics/Events.cpp
Normal file
98
components/metrics/Events.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "Events.h"
|
||||
#include <algorithm>
|
||||
#include "esp_app_format.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#if CONFIG_WITH_METRICS
|
||||
static const char* const TAG = "MetricsEvent";
|
||||
namespace Metrics {
|
||||
Event& Event::add_property(const char* name, const char* value) {
|
||||
ESP_LOGV(TAG, "Adding property %s:%s to event %s",name,value,_name);
|
||||
char* mutable_name = strdup_psram(name); // Cast away const-ness, be careful with this
|
||||
auto elem = properties.find(mutable_name);
|
||||
FREE_AND_NULL(mutable_name)
|
||||
if (elem == properties.end()) {
|
||||
ESP_LOGV(TAG, "Adding property %s:%s to event %s",name,value,_name);
|
||||
properties.insert({strdup_psram(name), strdup_psram(value)});
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Replacing value for property %s. Old: %s New: %s, Event: %s",name,elem->second,value,name);
|
||||
FREE_AND_NULL(elem->second)
|
||||
elem->second = strdup_psram(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Event::has_property_value(const char* name, const char* value) const {
|
||||
ESP_LOGV(TAG, "Checking if event %s property %s has value %s",_name, name,value);
|
||||
return std::any_of(properties.begin(), properties.end(),
|
||||
[name, value](const std::pair<const char* const, char*>& kv) {
|
||||
ESP_LOGV(TAG, "Found property %s=%s", name,value);
|
||||
return strcmp(kv.first, name) == 0 && strcmp(kv.second, value) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
void Event::remove_property(const char* name, const char* value) {
|
||||
auto it = properties.begin();
|
||||
ESP_LOGV(TAG, "Removing event %s property %s=%s",_name, name,value);
|
||||
while (it != properties.end()) {
|
||||
if (strcmp(it->first, name) == 0 && strcmp(it->second, value)) {
|
||||
properties.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ESP_LOGV(TAG, "Property %s=%s not found.", name,value);
|
||||
}
|
||||
cJSON* Event::properties_to_json() {
|
||||
ESP_LOGV(TAG, "Event %s properties to json.",_name);
|
||||
const esp_app_desc_t* desc = esp_ota_get_app_description();
|
||||
#ifdef CONFIG_FW_PLATFORM_NAME
|
||||
const char* platform = CONFIG_FW_PLATFORM_NAME;
|
||||
#else
|
||||
const char* platform = desc->project_name;
|
||||
#endif
|
||||
cJSON* prop_json = cJSON_CreateObject();
|
||||
auto it = properties.begin();
|
||||
|
||||
while (it != properties.end()) {
|
||||
cJSON_AddStringToObject(prop_json, it->first, it->second);
|
||||
++it;
|
||||
}
|
||||
cJSON_AddStringToObject(prop_json, "platform", platform);
|
||||
cJSON_AddStringToObject(prop_json, "build", desc->version);
|
||||
dump_json_content("User properties for event:", prop_json, ESP_LOG_VERBOSE);
|
||||
return prop_json;
|
||||
}
|
||||
cJSON* Event::to_json(const char* distinct_id) {
|
||||
// The target structure looks like this
|
||||
// {
|
||||
// "event": "batched_event_name_1",
|
||||
// "properties": {
|
||||
// "distinct_id": "user distinct id",
|
||||
// "account_type": "pro"
|
||||
// },
|
||||
// "timestamp": "[optional timestamp in ISO 8601 format]"
|
||||
// }
|
||||
ESP_LOGV(TAG,"Event %s to json",_name);
|
||||
|
||||
free_json();
|
||||
_json = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(_json, "name", _name);
|
||||
cJSON_AddItemToObject(_json, "properties", properties_to_json());
|
||||
|
||||
char buf[26] = {};
|
||||
strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&_time));
|
||||
// this will work too, if your compiler doesn't support %F or %T:
|
||||
// strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
|
||||
cJSON_AddStringToObject(_json, "timestamp", buf);
|
||||
cJSON* prop_json = properties_to_json();
|
||||
cJSON_AddStringToObject(prop_json, "distinct_id", distinct_id);
|
||||
dump_json_content("Full Event:", _json, ESP_LOG_VERBOSE);
|
||||
return _json;
|
||||
}
|
||||
void Event::free_json() { cJSON_Delete(_json); }
|
||||
void Event::update_time() {
|
||||
if (_time == 0) {
|
||||
_time = time(nullptr);
|
||||
}
|
||||
}
|
||||
} // namespace Metrics
|
||||
#endif
|
||||
53
components/metrics/Events.h
Normal file
53
components/metrics/Events.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "esp_log.h"
|
||||
#include "tools.h"
|
||||
#include <cJSON.h>
|
||||
#include <ctime>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
namespace Metrics {
|
||||
struct StrCompare {
|
||||
bool operator()(const char* a, const char* b) const { return strcmp(a, b) < 0; }
|
||||
};
|
||||
|
||||
class Event {
|
||||
|
||||
public:
|
||||
std::map<char*, char*, StrCompare> properties;
|
||||
Event& add_property(const char* name, const char* value);
|
||||
bool has_property_value(const char* name, const char* value) const;
|
||||
void remove_property(const char* name, const char* value);
|
||||
cJSON* properties_to_json();
|
||||
cJSON* to_json(const char* distinct_id);
|
||||
void free_json();
|
||||
void update_time();
|
||||
explicit Event(const char* name) {
|
||||
_name = strdup_psram(name);
|
||||
memset(&_time, 0x00, sizeof(_time));
|
||||
}
|
||||
const char* get_name() const { return _name; }
|
||||
~Event() {
|
||||
FREE_AND_NULL(_name);
|
||||
|
||||
// Iterate through the map and free the elements
|
||||
for (auto& kv : properties) {
|
||||
free((void*)kv.first);
|
||||
free(kv.second);
|
||||
}
|
||||
properties.clear(); // Clear the map after freeing memory
|
||||
FREE_AND_NULL(_json);
|
||||
}
|
||||
private:
|
||||
char* _name = nullptr;
|
||||
time_t _time;
|
||||
cJSON* _json = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Metrics
|
||||
#endif
|
||||
148
components/metrics/Metrics.cpp
Normal file
148
components/metrics/Metrics.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
|
||||
#include "Metrics.h"
|
||||
#include "Batch.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_tls.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "tools.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "network_manager.h"
|
||||
#include "platform_config.h"
|
||||
|
||||
static const char* TAG = "metrics";
|
||||
|
||||
#if CONFIG_WITH_METRICS
|
||||
extern bool is_network_connected();
|
||||
#define METRICS_CLIENT_ID_LEN 50
|
||||
#define MAX_HTTP_RECV_BUFFER 512
|
||||
|
||||
static bool metrics_usage_gen = false;
|
||||
static time_t metrics_usage_gen_time = 0;
|
||||
#ifndef METRICS_API_KEY
|
||||
#pragma message "Metrics API key needs to be passed from the environment"
|
||||
#define METRICS_API_KEY "ZZZ"
|
||||
#endif
|
||||
static const char* metrics_api_key =
|
||||
static const char* parms_str = "params";
|
||||
static const char* properties_str = "properties";
|
||||
static const char* user_properties_str = "user_properties";
|
||||
static const char* items_str = "items";
|
||||
static const char* quantity_str = "quantity";
|
||||
static const char* metrics_url = "https://app.posthog.com";
|
||||
static TimerHandle_t timer;
|
||||
extern cJSON* get_cmd_list();
|
||||
Metrics::Batch batch;
|
||||
|
||||
static void metrics_timer_cb(void* timer_id) {
|
||||
if (batch.has_events()) {
|
||||
if (!is_network_connected()) {
|
||||
ESP_LOGV(TAG, "Network not connected. can't flush");
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Pushing events");
|
||||
batch.push();
|
||||
}
|
||||
}
|
||||
if (millis() > metrics_usage_gen_time && !metrics_usage_gen) {
|
||||
metrics_usage_gen = true;
|
||||
ESP_LOGV(TAG, "Generate command list to pull features");
|
||||
cJSON* cmdlist = get_cmd_list();
|
||||
dump_json_content("generated cmd list", cmdlist, ESP_LOG_VERBOSE);
|
||||
cJSON_Delete(cmdlist);
|
||||
}
|
||||
}
|
||||
void metrics_init() {
|
||||
ESP_LOGV(TAG, "Initializing metrics");
|
||||
batch.configure(metrics_api_key, metrics_url);
|
||||
if (!timer) {
|
||||
ESP_LOGE(TAG, "Metrics Timer failure");
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Starting timer");
|
||||
xTimerStart(timer, portMAX_DELAY);
|
||||
}
|
||||
// set a 20 seconds delay before generating the
|
||||
// features so the system has time to boot
|
||||
metrics_usage_gen_time = millis() + 20000;
|
||||
}
|
||||
|
||||
void metrics_event_playback(const char* source) {
|
||||
ESP_LOGV(TAG, "Playback event: %s", source);
|
||||
auto event = batch.add_event("play").add_property("source", source);
|
||||
}
|
||||
void metrics_event_boot(const char* partition) {
|
||||
ESP_LOGV(TAG, "Boot event %s", partition);
|
||||
auto event = batch.add_event("start");
|
||||
event.add_property("partition", partition);
|
||||
}
|
||||
void metrics_add_feature_variant(const char* name, const char* format, ...) {
|
||||
va_list args;
|
||||
ESP_LOGV(TAG, "Feature %s", name);
|
||||
va_start(args, format);
|
||||
|
||||
// Determine the required buffer size
|
||||
int size = vsnprintf(nullptr, 0, format, args);
|
||||
va_end(args); // Reset the va_list
|
||||
|
||||
// Allocate buffer and format the string
|
||||
std::vector<char> buffer(size + 1); // +1 for the null-terminator
|
||||
va_start(args, format);
|
||||
vsnprintf(buffer.data(), buffer.size(), format, args);
|
||||
va_end(args);
|
||||
|
||||
// Now buffer.data() contains the formatted string
|
||||
batch.add_feature_variant_event(name, buffer.data());
|
||||
}
|
||||
void metrics_add_feature(const char* name, bool active) {
|
||||
ESP_LOGV(TAG, "Adding feature %s: %s", name, active ? "ACTIVE" : "INACTIVE");
|
||||
batch.add_remove_feature_event(name, active);
|
||||
}
|
||||
void metrics_event(const char* name) {
|
||||
ESP_LOGV(TAG, "Adding Event %s", name);
|
||||
batch.add_event(name);
|
||||
}
|
||||
#else
|
||||
static const char * not_enabled = " - (metrics not enabled, this is just marking where the call happens)";
|
||||
void metrics_init(){
|
||||
#pragma message("Metrics disabled")
|
||||
ESP_LOGD(TAG,"Metrics init%s",not_enabled);
|
||||
}
|
||||
void metrics_event_boot(const char* partition){
|
||||
ESP_LOGD(TAG,"Metrics Event Boot from partition %s%s",partition,not_enabled);
|
||||
}
|
||||
void metrics_event(const char* name){
|
||||
ESP_LOGD(TAG,"Metrics Event %s%s",name,not_enabled);
|
||||
}
|
||||
void metrics_add_feature(const char* name, bool active) {
|
||||
ESP_LOGD(TAG,"Metrics add feature %s%s%s",name,active?"ACTIVE":"INACTIVE",not_enabled);
|
||||
}
|
||||
void metrics_add_feature_variant(const char* name, const char* format, ...){
|
||||
va_list args;
|
||||
ESP_LOGV(TAG, "Feature %s", name);
|
||||
va_start(args, format);
|
||||
|
||||
// Determine the required buffer size
|
||||
int size = vsnprintf(nullptr, 0, format, args);
|
||||
va_end(args); // Reset the va_list
|
||||
|
||||
// Allocate buffer and format the string
|
||||
std::vector<char> buffer(size + 1); // +1 for the null-terminator
|
||||
va_start(args, format);
|
||||
vsnprintf(buffer.data(), buffer.size(), format, args);
|
||||
va_end(args);
|
||||
|
||||
ESP_LOGD(TAG,"Metrics add feature %s variant %s%s",name,buffer.data(),not_enabled);
|
||||
}
|
||||
#endif
|
||||
18
components/metrics/Metrics.h
Normal file
18
components/metrics/Metrics.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void metrics_event_playback(const char* source);
|
||||
void metrics_event_boot(const char* partition);
|
||||
void metrics_event(const char* name);
|
||||
void metrics_add_feature(const char* name, bool active);
|
||||
void metrics_add_feature_variant(const char* name, const char* format, ...);
|
||||
void metrics_init();
|
||||
void metrics_flush();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
163
components/metrics/http_handlers.c
Normal file
163
components/metrics/http_handlers.c
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "http_handlers.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_tls.h"
|
||||
#include "tools.h"
|
||||
#include <sys/param.h>
|
||||
#if CONFIG_WITH_METRICS
|
||||
static const char* TAG = "metrics_http";
|
||||
static char* output_buffer; // Buffer to store response of http request from
|
||||
// event handler
|
||||
static int output_len = 0; // Stores number of bytes read
|
||||
#define MAX_HTTP_OUTPUT_BUFFER 2048
|
||||
// Common function signature for event handlers
|
||||
typedef void (*HttpEventHandler)(esp_http_client_event_t* evt);
|
||||
|
||||
static void handle_http_error(esp_http_client_event_t* evt) { ESP_LOGV(TAG, "ERROR"); }
|
||||
|
||||
static void handle_http_connected(esp_http_client_event_t* evt) {
|
||||
ESP_LOGV(TAG, "ON_CONNECTED");
|
||||
}
|
||||
|
||||
static void handle_http_header_sent(esp_http_client_event_t* evt) {
|
||||
ESP_LOGV(TAG, "HEADER_SENT");
|
||||
}
|
||||
|
||||
static void handle_http_on_header(esp_http_client_event_t* evt) {
|
||||
ESP_LOGV(TAG, "ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
|
||||
}
|
||||
|
||||
static void handle_http_on_data(esp_http_client_event_t* evt) {
|
||||
ESP_LOGV(TAG, "ON_DATA, len=%d", evt->data_len);
|
||||
ESP_LOGV(TAG, "ON_DATA, len=%d", evt->data_len);
|
||||
// Clean the buffer in case of a new request
|
||||
if (output_len == 0 && evt->user_data) {
|
||||
// we are just starting to copy the output data into the use
|
||||
ESP_LOGV(TAG, "Resetting buffer");
|
||||
memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER);
|
||||
}
|
||||
/*
|
||||
* Check for chunked encoding is added as the URL for chunked encoding used in this example
|
||||
* returns binary data. However, event handler can also be used in case chunked encoding is
|
||||
* used.
|
||||
*/
|
||||
|
||||
// If user_data buffer is configured, copy the response into the buffer
|
||||
int copy_len = 0;
|
||||
if (evt->user_data) {
|
||||
ESP_LOGV(TAG, "Not Chunked response, with user data");
|
||||
// The last byte in evt->user_data is kept for the NULL character in
|
||||
// case of out-of-bound access.
|
||||
copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len));
|
||||
if (copy_len) {
|
||||
memcpy(evt->user_data + output_len, evt->data, copy_len);
|
||||
}
|
||||
} else {
|
||||
int content_len = esp_http_client_get_content_length(evt->client);
|
||||
if (esp_http_client_is_chunked_response(evt->client)) {
|
||||
esp_http_client_get_chunk_length(evt->client, &content_len);
|
||||
}
|
||||
|
||||
if (output_buffer == NULL) {
|
||||
// We initialize output_buffer with 0 because it is used by
|
||||
// strlen() and similar functions therefore should be null
|
||||
// terminated.
|
||||
size_t len=(content_len + 1) * sizeof(char);
|
||||
ESP_LOGV(TAG, "Init buffer %d",len);
|
||||
output_buffer = (char*)malloc_init_external(len);
|
||||
output_len = 0;
|
||||
if (output_buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Buffer alloc failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
copy_len = MIN(evt->data_len, (content_len - output_len));
|
||||
if (copy_len) {
|
||||
memcpy(output_buffer + output_len, evt->data, copy_len);
|
||||
}
|
||||
}
|
||||
output_len += copy_len;
|
||||
}
|
||||
|
||||
static void handle_http_on_finish(esp_http_client_event_t* evt) {
|
||||
ESP_LOGD(TAG, "ON_FINISH");
|
||||
if (output_buffer != NULL) {
|
||||
ESP_LOGV(TAG, "Response: %s", output_buffer);
|
||||
free(output_buffer);
|
||||
output_buffer = NULL;
|
||||
}
|
||||
output_len = 0;
|
||||
}
|
||||
static void handle_http_disconnected(esp_http_client_event_t* evt) {
|
||||
ESP_LOGI(TAG, "DISCONNECTED");
|
||||
int mbedtls_err = 0;
|
||||
esp_err_t err =
|
||||
esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
|
||||
if (err != 0) {
|
||||
ESP_LOGI(TAG, "Last error : %s", esp_err_to_name(err));
|
||||
ESP_LOGI(TAG, "Last mbedtls err 0x%x", mbedtls_err);
|
||||
}
|
||||
if (output_buffer != NULL) {
|
||||
free(output_buffer);
|
||||
output_buffer = NULL;
|
||||
}
|
||||
output_len = 0;
|
||||
}
|
||||
static const HttpEventHandler eventHandlers[] = {
|
||||
handle_http_error, // HTTP_EVENT_ERROR
|
||||
handle_http_connected, // HTTP_EVENT_ON_CONNECTED
|
||||
handle_http_header_sent, // HTTP_EVENT_HEADERS_SENT
|
||||
handle_http_header_sent, // HTTP_EVENT_HEADER_SENT (alias for HTTP_EVENT_HEADERS_SENT)
|
||||
handle_http_on_header, // HTTP_EVENT_ON_HEADER
|
||||
handle_http_on_data, // HTTP_EVENT_ON_DATA
|
||||
handle_http_on_finish, // HTTP_EVENT_ON_FINISH
|
||||
handle_http_disconnected // HTTP_EVENT_DISCONNECTED
|
||||
};
|
||||
esp_err_t metrics_http_event_handler(esp_http_client_event_t* evt) {
|
||||
|
||||
if (evt->event_id < 0 || evt->event_id >= sizeof(eventHandlers) / sizeof(eventHandlers[0])) {
|
||||
ESP_LOGE(TAG, "Invalid event ID: %d", evt->event_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
eventHandlers[evt->event_id](evt);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
int metrics_http_post_request(const char* payload, const char* url) {
|
||||
int status_code = 0;
|
||||
esp_http_client_config_t config = {.url = url,
|
||||
.disable_auto_redirect = false,
|
||||
.event_handler = metrics_http_event_handler,
|
||||
.transport_type = HTTP_TRANSPORT_OVER_SSL,
|
||||
.user_data = NULL, // local_response_buffer, // Pass address of
|
||||
// local buffer to get response
|
||||
.skip_cert_common_name_check = true
|
||||
|
||||
};
|
||||
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||
esp_err_t err = esp_http_client_set_method(client, HTTP_METHOD_POST);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
err = esp_http_client_set_header(client, "Content-Type", "application/json");
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGV(TAG, "Setting payload: %s", payload);
|
||||
err = esp_http_client_set_post_field(client, payload, strlen(payload));
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = esp_http_client_perform(client);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
status_code = esp_http_client_get_status_code(client);
|
||||
ESP_LOGD(TAG, "metrics call Status = %d, content_length = %d",
|
||||
esp_http_client_get_status_code(client), esp_http_client_get_content_length(client));
|
||||
|
||||
} else {
|
||||
status_code = 500;
|
||||
ESP_LOGW(TAG, "metrics call Status failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
esp_http_client_cleanup(client);
|
||||
return status_code;
|
||||
}
|
||||
#endif
|
||||
11
components/metrics/http_handlers.h
Normal file
11
components/metrics/http_handlers.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int metrics_http_post_request(const char* payload, const char* url);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -13,14 +13,14 @@ esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data, siz
|
||||
esp_err_t store_nvs_value(nvs_type_t type, const char *key, void * data);
|
||||
esp_err_t get_nvs_value(nvs_type_t type, const char *key, void*value, const uint8_t buf_size);
|
||||
void * get_nvs_value_alloc(nvs_type_t type, const char *key);
|
||||
void * get_nvs_value_alloc_for_partition(const char * partition,const char * ns,nvs_type_t type, const char *key, size_t * size);
|
||||
esp_err_t erase_nvs_for_partition(const char * partition, const char * ns,const char *key);
|
||||
esp_err_t store_nvs_value_len_for_partition(const char * partition,const char * ns,nvs_type_t type, const char *key, const void * data,size_t data_len);
|
||||
void * get_nvs_value_alloc_for_partition(const char * partition,const char * name_space,nvs_type_t type, const char *key, size_t * size);
|
||||
esp_err_t erase_nvs_for_partition(const char * partition, const char * name_space,const char *key);
|
||||
esp_err_t store_nvs_value_len_for_partition(const char * partition,const char * name_space,nvs_type_t type, const char *key, const void * data,size_t data_len);
|
||||
esp_err_t erase_nvs(const char *key);
|
||||
void print_blob(const char *blob, size_t len);
|
||||
const char *type_to_str(nvs_type_t type);
|
||||
nvs_type_t str_to_type(const char *type);
|
||||
esp_err_t erase_nvs_partition(const char * partition, const char * ns);
|
||||
esp_err_t erase_nvs_partition(const char * partition, const char * name_space);
|
||||
void erase_settings_partition();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -634,7 +634,7 @@ cJSON * config_alloc_get_cjson(const char *key){
|
||||
}
|
||||
return conf_json;
|
||||
}
|
||||
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
|
||||
esp_err_t config_set_cjson(const char *key, cJSON *value, bool free_cjson){
|
||||
char * value_str = cJSON_PrintUnformatted(value);
|
||||
if(value_str==NULL){
|
||||
ESP_LOGE(TAG, "Unable to print cJSON for key [%s]", key);
|
||||
@@ -642,9 +642,14 @@ esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
|
||||
}
|
||||
esp_err_t err = config_set_value(NVS_TYPE_STR,key, value_str);
|
||||
free(value_str);
|
||||
cJSON_Delete(value);
|
||||
if(free_cjson){
|
||||
cJSON_Delete(value);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
|
||||
return config_set_cjson(key, value, true);
|
||||
}
|
||||
void config_get_uint16t_from_str(const char *key, uint16_t *value, uint16_t default_value){
|
||||
char * str_value = config_alloc_get(NVS_TYPE_STR, key);
|
||||
if(str_value == NULL){
|
||||
@@ -786,6 +791,44 @@ cJSON* cjson_update_number(cJSON** root, const char* key, int value) {
|
||||
}
|
||||
return *root;
|
||||
}
|
||||
bool config_parse_param_int(const char * config,const char * param, char delimiter,int * value){
|
||||
const char *p;
|
||||
if(!value){
|
||||
return false;
|
||||
}
|
||||
if ((p = strcasestr(config, param)) && (p = strchr(p, delimiter))) {
|
||||
*value = atoi(p+1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool config_parse_param_float(const char * config,const char * param, char delimiter,double * value){
|
||||
const char *p;
|
||||
if(!value){
|
||||
return false;
|
||||
}
|
||||
if ((p = strcasestr(config, param)) && (p = strchr(p, delimiter))) {
|
||||
*value = atof(p+1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool config_parse_param_str(const char *source, const char *param, char delimiter, char *value, size_t value_size) {
|
||||
char *p;
|
||||
if ((p = strstr(source, param)) && (p = strchr(p, delimiter))) {
|
||||
while (*++p == ' '); // Skip spaces
|
||||
// Read the value into the buffer, making sure not to overflow
|
||||
snprintf(value, value_size, "%s", p);
|
||||
char *end = strchr(value, ',');
|
||||
if (end) {
|
||||
*end = '\0'; // Null-terminate at the comma, if found
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_SET_DEFAULT(uint8_t,NVS_TYPE_U8);
|
||||
IMPLEMENT_SET_DEFAULT(int8_t,NVS_TYPE_I8);
|
||||
IMPLEMENT_SET_DEFAULT(uint16_t,NVS_TYPE_U16);
|
||||
|
||||
@@ -8,25 +8,30 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define PARSE_WITH_FUNC 1
|
||||
#ifdef PARSE_WITH_FUNC
|
||||
#define PARSE_PARAM(S,P,C,V) config_parse_param_int(S,P,C,(int*)&V)
|
||||
#define PARSE_PARAM_STR(S,P,C,V,I) config_parse_param_str(S,P,C,V,I)
|
||||
#define PARSE_PARAM_FLOAT(S,P,C,V) config_parse_param_float(S,P,C,&V)
|
||||
#else
|
||||
#define PARSE_PARAM(S,P,C,V) do { \
|
||||
char *__p; \
|
||||
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
|
||||
} while (0)
|
||||
|
||||
#define PARSE_PARAM(S,P,C,V) do { \
|
||||
char *__p; \
|
||||
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
|
||||
} while (0)
|
||||
|
||||
#define PARSE_PARAM_FLOAT(S,P,C,V) do { \
|
||||
char *__p; \
|
||||
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atof(__p+1); \
|
||||
} while (0)
|
||||
|
||||
#define PARSE_PARAM_STR(S,P,C,V,I) do { \
|
||||
char *__p; \
|
||||
if ((__p = strstr(S, P)) && (__p = strchr(__p, C))) { \
|
||||
while (*++__p == ' '); \
|
||||
sscanf(__p,"%" #I "[^,]", V); \
|
||||
} \
|
||||
} while (0)
|
||||
#define PARSE_PARAM_FLOAT(S,P,C,V) do { \
|
||||
char *__p; \
|
||||
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atof(__p+1); \
|
||||
} while (0)
|
||||
|
||||
#define PARSE_PARAM_STR(S,P,C,V,I) do { \
|
||||
char *__p; \
|
||||
if ((__p = strstr(S, P)) && (__p = strchr(__p, C))) { \
|
||||
while (*++__p == ' '); \
|
||||
sscanf(__p,"%" #I "[^,]", V); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
#define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value);
|
||||
#define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value);
|
||||
#ifndef FREE_RESET
|
||||
@@ -50,10 +55,14 @@ bool config_has_changes();
|
||||
void config_commit_to_nvs();
|
||||
void config_start_timer();
|
||||
void config_init();
|
||||
bool config_parse_param_int(const char * config,const char * param, char delimiter,int * value);
|
||||
bool config_parse_param_float(const char * config,const char * param, char delimiter,double * value);
|
||||
bool config_parse_param_str(const char *source, const char *param, char delimiter, char *value, size_t value_size);
|
||||
void * config_alloc_get_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
|
||||
void * config_alloc_get_str(const char *key, char *lead, char *fallback);
|
||||
cJSON * config_alloc_get_cjson(const char *key);
|
||||
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value);
|
||||
esp_err_t config_set_cjson(const char *key, cJSON *value, bool free_cjson);
|
||||
void config_get_uint16t_from_str(const char *key, uint16_t *value, uint16_t default_value);
|
||||
void config_delete_key(const char *key);
|
||||
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
|
||||
|
||||
@@ -8,7 +8,7 @@ idf_component_register( SRCS
|
||||
cmd_config.c
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES nvs_flash
|
||||
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools)
|
||||
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools metrics)
|
||||
|
||||
set_source_files_properties(cmd_config.c
|
||||
PROPERTIES COMPILE_FLAGS
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
idf_component_register( SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
PRIV_REQUIRES bootloader_support
|
||||
PRIV_REQUIRES bootloader_support json
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
#include "application_name.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_app_format.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "stdbool.h"
|
||||
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
|
||||
|
||||
extern cJSON * gpio_list;
|
||||
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
||||
.magic_word = ESP_APP_DESC_MAGIC_WORD,
|
||||
.version = PROJECT_VER,
|
||||
@@ -26,7 +27,12 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
||||
.date = "",
|
||||
#endif
|
||||
};
|
||||
|
||||
cJSON * get_gpio_list(bool refresh){
|
||||
if(!gpio_list){
|
||||
gpio_list = cJSON_CreateArray();
|
||||
}
|
||||
return gpio_list;
|
||||
}
|
||||
void register_optional_cmd(void) {
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +43,25 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
||||
extern void register_audio_config(void);
|
||||
extern void register_rotary_config(void);
|
||||
extern void register_ledvu_config(void);
|
||||
|
||||
extern void register_nvs();
|
||||
extern cJSON * get_gpio_list_handler(bool refresh);
|
||||
void register_optional_cmd(void) {
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
register_rotary_config();
|
||||
register_ledvu_config();
|
||||
#endif
|
||||
register_audio_config();
|
||||
}
|
||||
register_ledvu_config();
|
||||
register_nvs();
|
||||
|
||||
}
|
||||
cJSON * get_gpio_list(bool refresh){
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
return get_gpio_list_handler(refresh);
|
||||
#else
|
||||
return cJSON_CreateArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
extern int squeezelite_main(int argc, char **argv);
|
||||
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
#include "tools.h"
|
||||
#include "cJSON.h"
|
||||
#include "cmd_i2ctools.h"
|
||||
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
#include "metrics.h"
|
||||
#endif
|
||||
#include "cmd_system.h"
|
||||
const char * desc_squeezelite ="Squeezelite Options";
|
||||
const char * desc_dac= "DAC Options";
|
||||
const char * desc_cspotc= "Spotify (cSpot) Options";
|
||||
@@ -330,9 +333,8 @@ static int do_bt_source_cmd(int argc, char **argv){
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
// char value[100] ={0};
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -441,9 +443,8 @@ static int do_audio_cmd(int argc, char **argv){
|
||||
int nerrors = arg_parse(argc, argv,(void **)&audio_args);
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -529,9 +530,8 @@ static int do_spdif_cmd(int argc, char **argv){
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -568,9 +568,8 @@ static int do_rotary_cmd(int argc, char **argv){
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -640,9 +639,8 @@ static int do_cspot_config(int argc, char **argv){
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -699,9 +697,8 @@ static int do_ledvu_cmd(int argc, char **argv){
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -759,10 +756,8 @@ static int do_i2s_cmd(int argc, char **argv)
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "do_i2s_cmd: Failed to open memstream");
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -873,7 +868,9 @@ cJSON * i2s_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
|
||||
const i2s_platform_config_t * i2s_conf= config_dac_get( );
|
||||
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("i2s",i2s_conf->pin.data_out_num>=0);
|
||||
#endif
|
||||
if(i2s_conf->pin.bck_io_num>0 ) {
|
||||
cJSON_AddNumberToObject(values,i2s_args.clock->hdr.longopts,i2s_conf->pin.bck_io_num);
|
||||
}
|
||||
@@ -910,6 +907,11 @@ cJSON * i2s_cb(){
|
||||
cJSON * spdif_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
const i2s_platform_config_t * spdif_conf= config_spdif_get( );
|
||||
if(spdif_conf->pin.data_out_num>=0) {
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("spdif","enabled");
|
||||
#endif
|
||||
}
|
||||
if(spdif_conf->pin.bck_io_num>0 ) {
|
||||
cJSON_AddNumberToObject(values,"clock",spdif_conf->pin.bck_io_num);
|
||||
}
|
||||
@@ -928,7 +930,9 @@ cJSON * rotary_cb(){
|
||||
bool raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
|
||||
free(p);
|
||||
const rotary_struct_t *rotary= config_rotary_get();
|
||||
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("rotary",GPIO_IS_VALID_GPIO(rotary->A ));
|
||||
#endif
|
||||
if(GPIO_IS_VALID_GPIO(rotary->A ) && rotary->A>=0 && GPIO_IS_VALID_GPIO(rotary->B) && rotary->B>=0){
|
||||
cJSON_AddNumberToObject(values,rotary_args.A->hdr.longopts,rotary->A);
|
||||
cJSON_AddNumberToObject(values,rotary_args.B->hdr.longopts,rotary->B);
|
||||
@@ -947,7 +951,11 @@ cJSON * rotary_cb(){
|
||||
cJSON * ledvu_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
const ledvu_struct_t *ledvu= config_ledvu_get();
|
||||
|
||||
if(GPIO_IS_VALID_GPIO(ledvu->gpio )){
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("led_vu","enabled");
|
||||
#endif
|
||||
}
|
||||
if(GPIO_IS_VALID_GPIO(ledvu->gpio) && ledvu->gpio>=0 && ledvu->length > 0){
|
||||
cJSON_AddNumberToObject(values,"gpio",ledvu->gpio);
|
||||
cJSON_AddNumberToObject(values,"length",ledvu->length);
|
||||
@@ -965,8 +973,14 @@ cJSON * audio_cb(){
|
||||
cJSON * values = cJSON_CreateObject();
|
||||
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
|
||||
cJSON_AddStringToObject(values,"jack_behavior",(strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0)?"Headphones":"Subwoofer");
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("jack_mute",atoi(p)>=0);
|
||||
#endif
|
||||
FREE_AND_NULL(p);
|
||||
p = config_alloc_get_default(NVS_TYPE_STR, "loudness", "0", 0);
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("loudness",atoi(p)>=0);
|
||||
#endif
|
||||
cJSON_AddStringToObject(values,"loudness",p);
|
||||
FREE_AND_NULL(p);
|
||||
return values;
|
||||
@@ -976,6 +990,9 @@ cJSON * bt_source_cb(){
|
||||
char * p = config_alloc_get_default(NVS_TYPE_STR, "a2dp_sink_name", NULL, 0);
|
||||
if(p){
|
||||
cJSON_AddStringToObject(values,"sink_name",p);
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("btsource",strlen(p)>0);
|
||||
#endif
|
||||
}
|
||||
FREE_AND_NULL(p);
|
||||
// p = config_alloc_get_default(NVS_TYPE_STR, "a2dp_ctmt", NULL, 0);
|
||||
@@ -1026,9 +1043,8 @@ static int do_squeezelite_cmd(int argc, char **argv)
|
||||
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr ** )&squeezelite_args);
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
fprintf(f,"Not yet implemented!");
|
||||
@@ -1047,59 +1063,62 @@ cJSON * squeezelite_cb(){
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
int nerrors=1;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
log_send_messaging(MESSAGING_ERROR,"Unable to parse squeezelite parameters");
|
||||
return values;
|
||||
}
|
||||
|
||||
if(nvs_config && strlen(nvs_config)>0){
|
||||
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
|
||||
argv = (char **) calloc(22, sizeof(char *));
|
||||
if (argv == NULL) {
|
||||
FREE_AND_NULL(nvs_config);
|
||||
fclose(f);
|
||||
return values;
|
||||
}
|
||||
size_t argc = esp_console_split_argv(nvs_config, argv,22);
|
||||
if (argc != 0) {
|
||||
nerrors = arg_parse(argc, argv,(void **)&squeezelite_args);
|
||||
ESP_LOGD(TAG,"Parsing completed");
|
||||
}
|
||||
}
|
||||
if (nerrors == 0) {
|
||||
get_str_parm_json(squeezelite_args.buffers, values);
|
||||
get_str_parm_json(squeezelite_args.codecs, values);
|
||||
get_lit_parm_json(squeezelite_args.header_format, values);
|
||||
get_str_parm_json(squeezelite_args.log_level, values);
|
||||
|
||||
// get_str_parm_json(squeezelite_args.log_level_all, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_decode, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_output, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_slimproto, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_stream, values);
|
||||
get_str_parm_json(squeezelite_args.mac_addr, values);
|
||||
get_str_parm_json(squeezelite_args.output_device, values);
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
if(squeezelite_args.output_device->sval[0]!=NULL && strlen(squeezelite_args.output_device->sval[0])>0){
|
||||
metrics_add_feature_variant("output",squeezelite_args.output_device->sval[0]);
|
||||
}
|
||||
#endif
|
||||
get_str_parm_json(squeezelite_args.model_name, values);
|
||||
get_str_parm_json(squeezelite_args.name, values);
|
||||
get_int_parm_json(squeezelite_args.rate, values);
|
||||
get_str_parm_json(squeezelite_args.rates, values);
|
||||
get_str_parm_json(squeezelite_args.server, values);
|
||||
get_int_parm_json(squeezelite_args.timeout, values);
|
||||
char * p = cJSON_Print(values);
|
||||
ESP_LOGD(TAG,"%s",p);
|
||||
free(p);
|
||||
}
|
||||
else {
|
||||
|
||||
if(nvs_config && strlen(nvs_config)>0){
|
||||
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
|
||||
argv = (char **) calloc(22, sizeof(char *));
|
||||
if (argv == NULL) {
|
||||
FREE_AND_NULL(nvs_config);
|
||||
fclose(f);
|
||||
return values;
|
||||
}
|
||||
size_t argc = esp_console_split_argv(nvs_config, argv,22);
|
||||
if (argc != 0) {
|
||||
nerrors = arg_parse(argc, argv,(void **)&squeezelite_args);
|
||||
ESP_LOGD(TAG,"Parsing completed");
|
||||
}
|
||||
}
|
||||
if (nerrors == 0) {
|
||||
get_str_parm_json(squeezelite_args.buffers, values);
|
||||
get_str_parm_json(squeezelite_args.codecs, values);
|
||||
get_lit_parm_json(squeezelite_args.header_format, values);
|
||||
get_str_parm_json(squeezelite_args.log_level, values);
|
||||
|
||||
// get_str_parm_json(squeezelite_args.log_level_all, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_decode, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_output, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_slimproto, values);
|
||||
// get_str_parm_json(squeezelite_args.log_level_stream, values);
|
||||
get_str_parm_json(squeezelite_args.mac_addr, values);
|
||||
get_str_parm_json(squeezelite_args.output_device, values);
|
||||
get_str_parm_json(squeezelite_args.model_name, values);
|
||||
get_str_parm_json(squeezelite_args.name, values);
|
||||
get_int_parm_json(squeezelite_args.rate, values);
|
||||
get_str_parm_json(squeezelite_args.rates, values);
|
||||
get_str_parm_json(squeezelite_args.server, values);
|
||||
get_int_parm_json(squeezelite_args.timeout, values);
|
||||
char * p = cJSON_Print(values);
|
||||
ESP_LOGD(TAG,"%s",p);
|
||||
free(p);
|
||||
}
|
||||
else {
|
||||
arg_print_errors(f, squeezelite_args.end, desc_squeezelite);
|
||||
}
|
||||
fflush (f);
|
||||
if(strlen(buf)>0){
|
||||
log_send_messaging(nerrors?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
|
||||
}
|
||||
fclose(f);
|
||||
FREE_AND_NULL(buf);
|
||||
arg_print_errors(f, squeezelite_args.end, desc_squeezelite);
|
||||
}
|
||||
fflush (f);
|
||||
if(strlen(buf)>0){
|
||||
log_send_messaging(nerrors?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
|
||||
}
|
||||
fclose(f);
|
||||
FREE_AND_NULL(buf);
|
||||
FREE_AND_NULL(nvs_config);
|
||||
FREE_AND_NULL(argv);
|
||||
return values;
|
||||
@@ -1212,9 +1231,8 @@ static int do_register_known_templates_config(int argc, char **argv){
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
cJSON * config_name =NULL;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||
return 1;
|
||||
}
|
||||
if(nerrors >0){
|
||||
@@ -1396,7 +1414,7 @@ void register_ledvu_config(void){
|
||||
|
||||
void register_audio_config(void){
|
||||
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
|
||||
audio_args.loudness = arg_int0("l", "loudness","0-10","Sets the loudness level, from 0 to 10. 0 will disable the loudness completely.");
|
||||
audio_args.loudness = arg_int0("l", "loudness","0-10","Sets a loudness level, from 0 to 10. 0 will disable the loudness completely. Note that LMS has priority over setting this value, so use it only when away from your server.");
|
||||
audio_args.end = arg_end(6);
|
||||
audio_args.end = arg_end(6);
|
||||
const esp_console_cmd_t cmd = {
|
||||
@@ -1468,22 +1486,36 @@ static void register_squeezelite_config(void){
|
||||
cmd_to_json_with_cb(&cmd,&squeezelite_cb);
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
|
||||
void dummy_register_cmd(){
|
||||
|
||||
}
|
||||
void register_config_cmd(void){
|
||||
if(!is_dac_config_locked()){
|
||||
register_known_templates_config();
|
||||
|
||||
register_known_templates_config();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CSPOT_SINK
|
||||
register_cspot_config();
|
||||
#endif
|
||||
register_bt_source_config();
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
if(!is_dac_config_locked()){
|
||||
register_i2s_config();
|
||||
}
|
||||
else {
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("i2s",true);
|
||||
#endif
|
||||
}
|
||||
if(!is_spdif_config_locked()){
|
||||
register_spdif_config();
|
||||
}
|
||||
else {
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature("spdif",true);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
register_optional_cmd();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -589,6 +589,7 @@ void register_nvs()
|
||||
.func = &list_entries,
|
||||
.argtable = &list_args
|
||||
};
|
||||
|
||||
MEMTRACE_PRINT_DELTA_MESSAGE("registering list_entries_cmd");
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd));
|
||||
MEMTRACE_PRINT_DELTA_MESSAGE("registering set_cmd");
|
||||
|
||||
@@ -62,7 +62,7 @@ static int perform_ota_update(int argc, char **argv)
|
||||
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "update",
|
||||
.help = "Updates the application binary from the provided URL",
|
||||
.help = "Update from URL",
|
||||
.hint = NULL,
|
||||
.func = &perform_ota_update,
|
||||
.argtable = &ota_args
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "messaging.h"
|
||||
#include "platform_console.h"
|
||||
#include "tools.h"
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
#include "Metrics.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
|
||||
#pragma message("Runtime stats enabled")
|
||||
@@ -73,24 +76,42 @@ static void register_set_services();
|
||||
static void register_tasks();
|
||||
#endif
|
||||
extern BaseType_t network_manager_task;
|
||||
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size){
|
||||
FILE *f = open_memstream(buf, buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(cmdname,MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
}
|
||||
return f;
|
||||
}
|
||||
void register_system()
|
||||
{
|
||||
register_free();
|
||||
|
||||
register_set_services();
|
||||
register_setdevicename();
|
||||
register_free();
|
||||
register_heap();
|
||||
register_dump_heap();
|
||||
register_setdevicename();
|
||||
register_version();
|
||||
register_restart();
|
||||
register_deep_sleep();
|
||||
register_light_sleep();
|
||||
register_factory_boot();
|
||||
register_restart_ota();
|
||||
#if WITH_TASKS_INFO
|
||||
register_tasks();
|
||||
#endif
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
register_deep_sleep();
|
||||
register_light_sleep();
|
||||
#endif
|
||||
}
|
||||
void simple_restart()
|
||||
{
|
||||
log_send_messaging(MESSAGING_WARNING,"Rebooting.");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/* 'version' command */
|
||||
static int get_version(int argc, char **argv)
|
||||
{
|
||||
@@ -128,36 +149,23 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
{
|
||||
if(is_recovery_running){
|
||||
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
|
||||
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
// log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
|
||||
simple_restart();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
|
||||
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
// log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
|
||||
simple_restart();
|
||||
}
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
bool bFound=false;
|
||||
log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
|
||||
// log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
|
||||
const esp_partition_t *partition;
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
|
||||
|
||||
if(it == NULL){
|
||||
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Cannot iterate through partitions");
|
||||
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Partitions error");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -166,15 +174,11 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
ESP_LOGD(TAG, "Releasing partition iterator");
|
||||
esp_partition_iterator_release(it);
|
||||
if(partition != NULL){
|
||||
log_send_messaging(MESSAGING_INFO, "Found application partition %s sub type %u", partition->label,partition_subtype);
|
||||
log_send_messaging(MESSAGING_INFO, "Rebooting to %s", partition->label);
|
||||
err=esp_ota_set_boot_partition(partition);
|
||||
if(err!=ESP_OK){
|
||||
bFound=false;
|
||||
log_send_messaging(MESSAGING_ERROR,"Unable to select partition for reboot: %s",esp_err_to_name(err));
|
||||
}
|
||||
else{
|
||||
bFound=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -183,13 +187,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
}
|
||||
ESP_LOGD(TAG, "Yielding to other processes");
|
||||
taskYIELD();
|
||||
if(bFound) {
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration changes. ");
|
||||
}
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
simple_restart();
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
@@ -197,46 +195,31 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
|
||||
static int restart(int argc, char **argv)
|
||||
{
|
||||
log_send_messaging(MESSAGING_WARNING, "\n\nPerforming a simple restart to the currently active partition.");
|
||||
if(!wait_for_commit()){
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
simple_restart();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void simple_restart()
|
||||
{
|
||||
log_send_messaging(MESSAGING_WARNING,"System reboot requested.");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
|
||||
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
esp_err_t guided_restart_ota(){
|
||||
log_send_messaging(MESSAGING_WARNING,"System reboot to Application requested");
|
||||
log_send_messaging(MESSAGING_WARNING,"Booting to Squeezelite");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
|
||||
return ESP_FAIL; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
esp_err_t guided_factory(){
|
||||
log_send_messaging(MESSAGING_WARNING,"System reboot to recovery requested");
|
||||
log_send_messaging(MESSAGING_WARNING,"Booting to recovery");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
|
||||
return ESP_FAIL; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
static int restart_factory(int argc, char **argv)
|
||||
{
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into recovery");
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Recovery");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
|
||||
return 0; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
static int restart_ota(int argc, char **argv)
|
||||
{
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into ota app 0");
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Squeezelite");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
|
||||
return 0; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
@@ -248,7 +231,9 @@ static void register_restart()
|
||||
.hint = NULL,
|
||||
.func = &restart,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
static void register_restart_ota()
|
||||
@@ -259,7 +244,9 @@ static void register_restart_ota()
|
||||
.hint = NULL,
|
||||
.func = &restart_ota,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
|
||||
@@ -271,7 +258,9 @@ static void register_factory_boot()
|
||||
.hint = NULL,
|
||||
.func = &restart_factory,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
/** 'free' command prints available heap memory */
|
||||
@@ -287,11 +276,14 @@ static void register_free()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "free",
|
||||
.help = "Get the current size of free heap memory",
|
||||
.help = "Get free heap memory",
|
||||
.hint = NULL,
|
||||
.func = &free_mem,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
static int dump_heap(int argc, char **argv)
|
||||
@@ -303,16 +295,16 @@ static int dump_heap(int argc, char **argv)
|
||||
/* 'heap' command prints minumum heap size */
|
||||
static int heap_size(int argc, char **argv)
|
||||
{
|
||||
ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
|
||||
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_free_size(MALLOC_CAP_DMA),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
// ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
|
||||
// heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||
// heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
// heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
|
||||
// heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||
// heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
|
||||
// heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
|
||||
// heap_caps_get_free_size(MALLOC_CAP_DMA),
|
||||
// heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
|
||||
// heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
cmd_send_messaging(argv[0],MESSAGING_INFO,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
|
||||
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
@@ -457,9 +449,8 @@ static int setdevicename(int argc, char **argv)
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
return 1;
|
||||
}
|
||||
nerrors+=setnamevar("a2dp_dev_name", f, name);
|
||||
@@ -488,11 +479,13 @@ static void register_heap()
|
||||
{
|
||||
const esp_console_cmd_t heap_cmd = {
|
||||
.command = "heap",
|
||||
.help = "Get minimum size of free heap memory found during execution",
|
||||
.help = "Get minimum size of free heap memory",
|
||||
.hint = NULL,
|
||||
.func = &heap_size,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&heap_cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
|
||||
|
||||
}
|
||||
@@ -521,6 +514,7 @@ static void register_setdevicename()
|
||||
.func = &setdevicename,
|
||||
.argtable = &name_args
|
||||
};
|
||||
|
||||
cmd_to_json_with_cb(&set_name,&setdevicename_cb);
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&set_name));
|
||||
}
|
||||
@@ -618,9 +612,7 @@ static void register_deep_sleep()
|
||||
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "deep_sleep",
|
||||
.help = "Enter deep sleep mode. "
|
||||
"Two wakeup modes are supported: timer and GPIO. "
|
||||
"If no wakeup option is specified, will sleep indefinitely.",
|
||||
.help = "Enter deep sleep mode. ",
|
||||
.hint = NULL,
|
||||
.func = &deep_sleep,
|
||||
.argtable = &deep_sleep_args
|
||||
@@ -649,9 +641,8 @@ static int do_set_services(int argc, char **argv)
|
||||
}
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -674,7 +665,7 @@ static int do_set_services(int argc, char **argv)
|
||||
|
||||
if(err!=ESP_OK){
|
||||
nerrors++;
|
||||
fprintf(f,"Error setting telnet service to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
|
||||
fprintf(f,"Error setting telnet to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
|
||||
}
|
||||
else {
|
||||
fprintf(f,"Telnet service changed to %s\n",set_services_args.telnet->sval[0]);
|
||||
@@ -706,7 +697,6 @@ cJSON * set_services_cb(){
|
||||
#if WITH_TASKS_INFO
|
||||
console_set_bool_parameter(values,"stats",set_services_args.stats);
|
||||
#endif
|
||||
|
||||
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
|
||||
if(strcasestr("YX",p)!=NULL){
|
||||
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Telnet Only");
|
||||
@@ -717,7 +707,9 @@ cJSON * set_services_cb(){
|
||||
else {
|
||||
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Disabled");
|
||||
}
|
||||
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_add_feature_variant("telnet",p);
|
||||
#endif
|
||||
FREE_AND_NULL(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ void register_system();
|
||||
esp_err_t guided_factory();
|
||||
esp_err_t guided_restart_ota();
|
||||
void simple_restart();
|
||||
|
||||
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,9 @@ extern bool bypass_network_manager;
|
||||
#define JOIN_TIMEOUT_MS (10000)
|
||||
#include "platform_console.h"
|
||||
|
||||
// To enable wifi configuration from the command line, uncomment the line below
|
||||
// define WIFI_CMDLINE 1
|
||||
|
||||
|
||||
extern EventGroupHandle_t network_event_group;
|
||||
extern const int CONNECTED_BIT;
|
||||
@@ -53,13 +56,6 @@ static struct {
|
||||
|
||||
// todo: implement access point config - cmd_to_json(&i2cdetect_cmd);
|
||||
|
||||
|
||||
///** Arguments used by 'join' function */
|
||||
//static struct {
|
||||
// struct arg_int *autoconnect;
|
||||
// struct arg_end *end;
|
||||
//} auto_connect_args;
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
@@ -72,27 +68,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
xEventGroupSetBits(network_event_group, CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
//bool wait_for_wifi(){
|
||||
//
|
||||
// bool connected=(xEventGroupGetBits(wifi_event_group) & CONNECTED_BIT)!=0;
|
||||
//
|
||||
// if(!connected){
|
||||
// ESP_LOGD(TAG,"Waiting for WiFi...");
|
||||
// connected = (xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
|
||||
// pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS)& CONNECTED_BIT)!=0;
|
||||
// if(!connected){
|
||||
// ESP_LOGD(TAG,"wifi timeout.");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ESP_LOGI(TAG,"WiFi Connected!");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return connected;
|
||||
//
|
||||
//}
|
||||
|
||||
static void initialise_wifi(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
@@ -204,8 +180,10 @@ void register_wifi_join()
|
||||
|
||||
void register_wifi()
|
||||
{
|
||||
#ifdef WIFI_CMDLINE
|
||||
register_wifi_join();
|
||||
if(bypass_network_manager){
|
||||
initialise_wifi();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
#include "platform_config.h"
|
||||
#include "telnet.h"
|
||||
#include "tools.h"
|
||||
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
#include "metrics.h"
|
||||
#endif
|
||||
#include "messaging.h"
|
||||
|
||||
#include "config.h"
|
||||
@@ -85,14 +87,22 @@ cJSON * get_cmd_list(){
|
||||
}
|
||||
void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg){
|
||||
char * p=NULL;
|
||||
if(!root) {
|
||||
bool enabled = false;
|
||||
if(!root) {
|
||||
ESP_LOGE(TAG,"Invalid json parameter. Cannot set %s from %s",arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary,nvs_name);
|
||||
return;
|
||||
}
|
||||
if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
|
||||
cJSON_AddBoolToObject(root,arg->hdr.longopts,strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
|
||||
enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
|
||||
cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
|
||||
FREE_AND_NULL(p);
|
||||
}
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
if(enabled){
|
||||
metrics_add_feature(nvs_name,"enabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
struct arg_end *getParmsEnd(struct arg_hdr * * argtable){
|
||||
if(!argtable) return NULL;
|
||||
@@ -360,8 +370,6 @@ void console_start() {
|
||||
register_system();
|
||||
MEMTRACE_PRINT_DELTA_MESSAGE("Registering config commands");
|
||||
register_config_cmd();
|
||||
MEMTRACE_PRINT_DELTA_MESSAGE("Registering nvs commands");
|
||||
register_nvs();
|
||||
MEMTRACE_PRINT_DELTA_MESSAGE("Registering wifi commands");
|
||||
register_wifi();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES json tools platform_config display wifi-manager
|
||||
REQUIRES json tools platform_config display wifi-manager esp-tls platform_config
|
||||
PRIV_REQUIRES soc esp32
|
||||
)
|
||||
|
||||
@@ -47,6 +47,7 @@ cJSON * gpio_list=NULL;
|
||||
#define STR(macro) QUOTE(macro)
|
||||
#endif
|
||||
|
||||
extern cJSON * get_gpio_list(bool refresh);
|
||||
bool are_statistics_enabled(){
|
||||
#if defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) && defined (CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS)
|
||||
return true;
|
||||
@@ -592,7 +593,7 @@ const gpio_exp_config_t* config_gpio_exp_get(int index) {
|
||||
PARSE_PARAM(item, "intr", '=', config.intr);
|
||||
PARSE_PARAM(item, "base", '=', config.base);
|
||||
PARSE_PARAM(item, "count", '=', config.count);
|
||||
PARSE_PARAM_STR(item, "model", '=', config.model, 31);
|
||||
PARSE_PARAM_STR(item, "model", '=', config.model, sizeof(config.model)-1);
|
||||
|
||||
if ((p = strcasestr(item, "port")) != NULL) {
|
||||
char port[8] = "";
|
||||
@@ -646,6 +647,12 @@ const set_GPIO_struct_t * get_gpio_struct(){
|
||||
#endif
|
||||
#ifdef CONFIG_LED_RED_GPIO
|
||||
gpio_struct.red.gpio = CONFIG_LED_RED_GPIO;
|
||||
#endif
|
||||
#if defined(CONFIG_POWER_GPIO) && CONFIG_POWER_GPIO != -1
|
||||
gpio_struct.power.gpio = CONFIG_POWER_GPIO;
|
||||
#endif
|
||||
#ifdef CONFIG_POWER_GPIO_LEVEL
|
||||
gpio_struct.power.level = CONFIG_POWER_GPIO_LEVEL;
|
||||
#endif
|
||||
if(nvs_item){
|
||||
HANDLE_GPIO_STRUCT_MEMBER(amp,false);
|
||||
@@ -658,6 +665,7 @@ const set_GPIO_struct_t * get_gpio_struct(){
|
||||
HANDLE_GPIO_STRUCT_MEMBER(vcc,false);
|
||||
HANDLE_GPIO_STRUCT_MEMBER(gnd,false);
|
||||
HANDLE_GPIO_STRUCT_MEMBER(ir,false);
|
||||
HANDLE_GPIO_STRUCT_MEMBER(power,false);
|
||||
free(nvs_item);
|
||||
}
|
||||
|
||||
@@ -823,6 +831,7 @@ cJSON * get_GPIO_nvs_list(cJSON * list) {
|
||||
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,jack,"other");
|
||||
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,green,"other");
|
||||
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,red,"other");
|
||||
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,power,"other");
|
||||
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,spkfault,"other");
|
||||
return ilist;
|
||||
}
|
||||
@@ -1169,7 +1178,7 @@ cJSON * get_psram_gpio_list(cJSON * list){
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
cJSON * get_gpio_list(bool refresh) {
|
||||
cJSON * get_gpio_list_handler(bool refresh) {
|
||||
gpio_num_t gpio_num;
|
||||
if(gpio_list && !refresh){
|
||||
return gpio_list;
|
||||
|
||||
@@ -73,6 +73,7 @@ typedef struct {
|
||||
gpio_with_level_t green;
|
||||
gpio_with_level_t red;
|
||||
gpio_with_level_t spkfault;
|
||||
gpio_with_level_t power;
|
||||
} set_GPIO_struct_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -117,7 +118,7 @@ bool is_spdif_config_locked();
|
||||
esp_err_t free_gpio_entry( gpio_entry_t ** gpio);
|
||||
gpio_entry_t * get_gpio_by_name(char * name,char * group, bool refresh);
|
||||
gpio_entry_t * get_gpio_by_no(int gpionum, bool refresh);
|
||||
cJSON * get_gpio_list(bool refresh);
|
||||
|
||||
bool is_dac_config_locked();
|
||||
bool are_statistics_enabled();
|
||||
const rotary_struct_t * config_rotary_get();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
idf_component_register( SRCS operator.cpp tools.c trace.c
|
||||
REQUIRES esp_common pthread
|
||||
PRIV_REQUIRES esp_http_client esp-tls
|
||||
PRIV_REQUIRES esp_http_client esp-tls json
|
||||
INCLUDE_DIRS .
|
||||
)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#error CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS must be at least 2
|
||||
#endif
|
||||
|
||||
#include "cJSON.h"
|
||||
const static char TAG[] = "tools";
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -318,11 +319,30 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
time_t millis() {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
void dump_json_content(const char* prefix, cJSON* json, int level) {
|
||||
if (!json) {
|
||||
ESP_LOG_LEVEL(level,TAG, "%s: empty!", prefix);
|
||||
return;
|
||||
}
|
||||
char* output = cJSON_Print(json);
|
||||
if (output) {
|
||||
ESP_LOG_LEVEL(level,TAG, "%s: \n%s", prefix, output);
|
||||
}
|
||||
FREE_AND_NULL(output);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "cJSON.h"
|
||||
#include "time.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -70,6 +74,9 @@ void vTaskDeleteEXTRAM(TaskHandle_t xTask);
|
||||
|
||||
extern const char unknown_string_placeholder[];
|
||||
|
||||
time_t millis();
|
||||
void dump_json_content(const char* prefix, cJSON* json, int level);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -44,7 +44,7 @@ typedef struct session_context {
|
||||
char * sess_ip_address;
|
||||
u16_t port;
|
||||
} session_context_t;
|
||||
|
||||
extern cJSON * get_gpio_list(bool refresh);
|
||||
|
||||
union sockaddr_aligned {
|
||||
struct sockaddr sa;
|
||||
|
||||
@@ -331,8 +331,7 @@
|
||||
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" id="disable-squeezelite"
|
||||
value="" checked="">
|
||||
<input class="form-check-input" type="checkbox" id="disable-squeezelite" value="" >
|
||||
Disable Squeezelite
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -923,10 +923,10 @@ window.saveAutoexec1 = function (apply) {
|
||||
};
|
||||
data.config = {
|
||||
autoexec1: { value: commandLine, type: 33 },
|
||||
autoexec: {
|
||||
value: $('#disable-squeezelite').prop('checked') ? '0' : '1',
|
||||
type: 33,
|
||||
},
|
||||
// autoexec: {
|
||||
// value: $('#disable-squeezelite').prop('checked') ? '0' : '1',
|
||||
// type: 33,
|
||||
// },
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
@@ -1216,6 +1216,28 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
$('#disable-squeezelite').on('click', function () {
|
||||
// this.checked = this.checked ? 1 : 0;
|
||||
// $('#disable-squeezelite').prop('checked')
|
||||
if (this.checked) {
|
||||
// Store the current value before overwriting it
|
||||
const currentValue = $('#cmd_opt_s').val();
|
||||
$('#cmd_opt_s').data('originalValue', currentValue);
|
||||
|
||||
// Overwrite the value with '-disable'
|
||||
$('#cmd_opt_s').val('-disable');
|
||||
} else {
|
||||
// Retrieve the original value
|
||||
const originalValue = $('#cmd_opt_s').data('originalValue');
|
||||
|
||||
// Restore the original value if it exists, otherwise set it to an empty string
|
||||
$('#cmd_opt_s').val(originalValue ? originalValue : '');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('input#show-nvs').on('click', function () {
|
||||
this.checked = this.checked ? 1 : 0;
|
||||
Cookies.set("show-nvs", this.checked ? 'Y' : 'N');
|
||||
@@ -2199,13 +2221,7 @@ function getConfig() {
|
||||
.sort()
|
||||
.forEach(function (key) {
|
||||
let val = data[key].value;
|
||||
if (key === 'autoexec') {
|
||||
if (data.autoexec.value === '0') {
|
||||
$('#disable-squeezelite')[0].checked = true;
|
||||
} else {
|
||||
$('#disable-squeezelite')[0].checked = false;
|
||||
}
|
||||
} else if (key === 'autoexec1') {
|
||||
if (key === 'autoexec1') {
|
||||
/* call new function to parse the squeezelite options */
|
||||
processSqueezeliteCommandLine(val);
|
||||
} else if (key === 'host_name') {
|
||||
@@ -2294,6 +2310,7 @@ function processSqueezeliteCommandLine(val) {
|
||||
commandBTSinkName= parsed.otherOptions.btname;
|
||||
}
|
||||
handleTemplateTypeRadio('bt');
|
||||
|
||||
}
|
||||
Object.keys(parsed.options).forEach(function (key) {
|
||||
const option = parsed.options[key];
|
||||
@@ -2312,6 +2329,17 @@ function processSqueezeliteCommandLine(val) {
|
||||
$('#resample_i').prop('checked', true);
|
||||
}
|
||||
}
|
||||
if (parsed.options.hasOwnProperty('s')) {
|
||||
// parse -u v[:i] and check the appropriate radio button with id #resample_v
|
||||
if(parsed.options.s === '-disable'){
|
||||
$('#disable-squeezelite')[0].checked = true;
|
||||
}
|
||||
else {
|
||||
$('#disable-squeezelite')[0].checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip
|
||||
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip metrics
|
||||
LDFRAGMENTS "linker.lf"
|
||||
)
|
||||
|
||||
@@ -74,6 +74,16 @@ menu "Squeezelite-ESP32"
|
||||
select I2C_LOCKED
|
||||
select TARGET_LOCKED
|
||||
endchoice
|
||||
config WITH_CONFIG_UI
|
||||
bool "Enable config UI"
|
||||
default n
|
||||
help
|
||||
Enable configuring system options with the UI
|
||||
config WITH_METRICS
|
||||
bool "Enable Metrics"
|
||||
default n
|
||||
help
|
||||
Enable capturing and reporting anonymous metrics
|
||||
config RELEASE_API
|
||||
string "Software update URL"
|
||||
default "https://api.github.com/repos/sle118/squeezelite-esp32/releases"
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
#include "accessors.h"
|
||||
#include "cmd_system.h"
|
||||
#include "tools.h"
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
#include "Metrics.h"
|
||||
#endif
|
||||
|
||||
const char unknown_string_placeholder[] = "unknown";
|
||||
const char null_string_placeholder[] = "null";
|
||||
@@ -68,7 +71,68 @@ bool cold_boot=true;
|
||||
extern const char _ctype_[];
|
||||
const char* __ctype_ptr__ = _ctype_;
|
||||
#endif
|
||||
typedef struct {
|
||||
const char *key;
|
||||
const char *value;
|
||||
} DefaultStringVal;
|
||||
typedef struct {
|
||||
const char *key;
|
||||
unsigned int uint_value;
|
||||
bool is_signed;
|
||||
} DefaultNumVal;
|
||||
|
||||
const DefaultNumVal defaultNumVals[] = {
|
||||
{"ota_erase_blk", OTA_FLASH_ERASE_BLOCK, 0},
|
||||
{"ota_stack", OTA_STACK_SIZE, 0},
|
||||
{"ota_prio", OTA_TASK_PRIOTITY, 1}
|
||||
};
|
||||
const DefaultStringVal defaultStringVals[] = {
|
||||
{"equalizer", ""},
|
||||
{"loudness", "0"},
|
||||
{"actrls_config", ""},
|
||||
{"lms_ctrls_raw", "n"},
|
||||
{"rotary_config", CONFIG_ROTARY_ENCODER},
|
||||
{"display_config", CONFIG_DISPLAY_CONFIG},
|
||||
{"eth_config", CONFIG_ETH_CONFIG},
|
||||
{"i2c_config", CONFIG_I2C_CONFIG},
|
||||
{"spi_config", CONFIG_SPI_CONFIG},
|
||||
{"set_GPIO", CONFIG_SET_GPIO},
|
||||
{"sleep_config", ""},
|
||||
{"led_brightness", ""},
|
||||
{"spdif_config", ""},
|
||||
{"dac_config", ""},
|
||||
{"dac_controlset", ""},
|
||||
{"jack_mutes_amp", "n"},
|
||||
{"gpio_exp_config", CONFIG_GPIO_EXP_CONFIG},
|
||||
{"bat_config", ""},
|
||||
{"metadata_config", ""},
|
||||
{"telnet_enable", ""},
|
||||
{"telnet_buffer", "40000"},
|
||||
{"telnet_block", "500"},
|
||||
{"stats", "n"},
|
||||
{"rel_api", CONFIG_RELEASE_API},
|
||||
{"pollmx", "600"},
|
||||
{"pollmin", "15"},
|
||||
{"ethtmout", "8"},
|
||||
{"dhcp_tmout", "8"},
|
||||
{"target", CONFIG_TARGET},
|
||||
{"led_vu_config", ""},
|
||||
#ifdef CONFIG_BT_SINK
|
||||
{"bt_sink_pin", STR(CONFIG_BT_SINK_PIN)},
|
||||
{"bt_sink_volume", "127"},
|
||||
// Note: register_default_with_mac("bt_name", CONFIG_BT_NAME); is a special case
|
||||
{"enable_bt_sink", STR(CONFIG_BT_SINK)},
|
||||
{"a2dp_dev_name", CONFIG_A2DP_DEV_NAME},
|
||||
{"a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS)},
|
||||
{"a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS)},
|
||||
{"a2dp_sink_name", CONFIG_A2DP_SINK_NAME},
|
||||
{"autoexec", "1"},
|
||||
#ifdef CONFIG_AIRPLAY_SINK
|
||||
{"airplay_port", CONFIG_AIRPLAY_PORT},
|
||||
{"enable_airplay", STR(CONFIG_AIRPLAY_SINK)}
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
static bool bNetworkConnected=false;
|
||||
|
||||
// as an exception _init function don't need include
|
||||
@@ -80,7 +144,9 @@ extern void target_init(char *target);
|
||||
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
|
||||
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
|
||||
bool is_recovery_running;
|
||||
|
||||
bool is_network_connected(){
|
||||
return bNetworkConnected;
|
||||
}
|
||||
void cb_connection_got_ip(nm_state_t new_state, int sub_state){
|
||||
const char *hostname;
|
||||
static ip4_addr_t ip;
|
||||
@@ -163,7 +229,7 @@ void set_log_level(char * tag, char * level){
|
||||
}
|
||||
|
||||
#define DEFAULT_NAME_WITH_MAC(var,defval) char var[strlen(defval)+sizeof(macStr)]; strcpy(var,defval); strcat(var,macStr)
|
||||
void register_default_string_val(const char * key, char * value){
|
||||
void register_default_string_val(const char * key, const char * value){
|
||||
char * existing =(char *)config_alloc_get(NVS_TYPE_STR,key );
|
||||
ESP_LOGD(TAG,"Register default called with: %s= %s",key,value );
|
||||
if(!existing) {
|
||||
@@ -175,7 +241,15 @@ void register_default_string_val(const char * key, char * value){
|
||||
}
|
||||
FREE_AND_NULL(existing);
|
||||
}
|
||||
|
||||
void register_single_default_num_val(const DefaultNumVal *entry) {
|
||||
char number_buffer[101] = {};
|
||||
if (entry->is_signed) {
|
||||
snprintf(number_buffer, sizeof(number_buffer) - 1, "%d", entry->uint_value);
|
||||
} else {
|
||||
snprintf(number_buffer, sizeof(number_buffer) - 1, "%u", entry->uint_value);
|
||||
}
|
||||
register_default_string_val(entry->key, number_buffer);
|
||||
}
|
||||
char * alloc_get_string_with_mac(const char * val) {
|
||||
uint8_t mac[6];
|
||||
char macStr[LOCAL_MAC_SIZE + 1];
|
||||
@@ -188,7 +262,7 @@ char * alloc_get_string_with_mac(const char * val) {
|
||||
strcat(fullvalue, macStr);
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG,"Memory allocation failed when getting mac value for %s", val);
|
||||
ESP_LOGE(TAG,"malloc failed for value %s", val);
|
||||
}
|
||||
return fullvalue;
|
||||
|
||||
@@ -200,7 +274,7 @@ void register_default_with_mac(const char* key, char* defval) {
|
||||
FREE_AND_NULL(fullvalue);
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG,"Memory allocation failed when registering default value for %s", key);
|
||||
ESP_LOGE(TAG,"malloc failed for value %s", key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,70 +301,20 @@ void register_default_nvs(){
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_SINK
|
||||
register_default_with_mac("airplay_name", CONFIG_AIRPLAY_NAME);
|
||||
register_default_string_val("airplay_port", CONFIG_AIRPLAY_PORT);
|
||||
register_default_string_val( "enable_airplay", STR(CONFIG_AIRPLAY_SINK));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_SINK
|
||||
register_default_string_val( "bt_sink_pin", STR(CONFIG_BT_SINK_PIN));
|
||||
register_default_string_val( "bt_sink_volume", "127");
|
||||
register_default_with_mac("bt_name", CONFIG_BT_NAME);
|
||||
register_default_string_val( "enable_bt_sink", STR(CONFIG_BT_SINK));
|
||||
register_default_string_val("a2dp_dev_name", CONFIG_A2DP_DEV_NAME);
|
||||
register_default_string_val("a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS));
|
||||
register_default_string_val("a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS));
|
||||
register_default_string_val("a2dp_sink_name", CONFIG_A2DP_SINK_NAME);
|
||||
#endif
|
||||
|
||||
register_default_with_mac("host_name", DEFAULT_HOST_NAME);
|
||||
register_default_with_mac("ap_ssid", CONFIG_DEFAULT_AP_SSID);
|
||||
register_default_string_val("autoexec","1");
|
||||
register_default_with_mac("autoexec1",CONFIG_DEFAULT_COMMAND_LINE " -n " DEFAULT_HOST_NAME);
|
||||
for (int i = 0; i < sizeof(defaultStringVals) / sizeof(DefaultStringVal); ++i) {
|
||||
register_default_string_val(defaultStringVals[i].key, defaultStringVals[i].value);
|
||||
}
|
||||
for (int i = 0; i < sizeof(defaultNumVals) / sizeof(DefaultNumVal); ++i) {
|
||||
register_single_default_num_val(&defaultNumVals[i]);
|
||||
}
|
||||
|
||||
register_default_string_val("release_url", CONFIG_SQUEEZELITE_ESP32_RELEASE_URL);
|
||||
register_default_string_val("ap_ip_address",CONFIG_DEFAULT_AP_IP);
|
||||
register_default_string_val("ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY );
|
||||
register_default_string_val("ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK);
|
||||
register_default_string_val("ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL));
|
||||
register_default_string_val("ap_pwd", CONFIG_DEFAULT_AP_PASSWORD);
|
||||
register_default_string_val("bypass_wm", "0");
|
||||
register_default_string_val("equalizer", "");
|
||||
register_default_string_val("loudness", "0");
|
||||
register_default_string_val("actrls_config", "");
|
||||
register_default_string_val("lms_ctrls_raw", "n");
|
||||
register_default_string_val("rotary_config", CONFIG_ROTARY_ENCODER);
|
||||
char number_buffer[101] = {};
|
||||
snprintf(number_buffer,sizeof(number_buffer)-1,"%u",OTA_FLASH_ERASE_BLOCK);
|
||||
register_default_string_val( "ota_erase_blk", number_buffer);
|
||||
snprintf(number_buffer,sizeof(number_buffer)-1,"%u",OTA_STACK_SIZE);
|
||||
register_default_string_val( "ota_stack", number_buffer);
|
||||
snprintf(number_buffer,sizeof(number_buffer)-1,"%d",OTA_TASK_PRIOTITY);
|
||||
register_default_string_val( "ota_prio", number_buffer);
|
||||
register_default_string_val( "display_config", CONFIG_DISPLAY_CONFIG);
|
||||
register_default_string_val( "eth_config", CONFIG_ETH_CONFIG);
|
||||
register_default_string_val( "i2c_config", CONFIG_I2C_CONFIG);
|
||||
register_default_string_val( "spi_config", CONFIG_SPI_CONFIG);
|
||||
register_default_string_val( "set_GPIO", CONFIG_SET_GPIO);
|
||||
register_default_string_val( "sleep_config", "");
|
||||
register_default_string_val( "led_brightness", "");
|
||||
register_default_string_val( "spdif_config", "");
|
||||
register_default_string_val( "dac_config", "");
|
||||
register_default_string_val( "dac_controlset", "");
|
||||
register_default_string_val( "jack_mutes_amp", "n");
|
||||
register_default_string_val("gpio_exp_config", CONFIG_GPIO_EXP_CONFIG);
|
||||
register_default_string_val( "bat_config", "");
|
||||
register_default_string_val( "metadata_config", "");
|
||||
register_default_string_val( "telnet_enable", "");
|
||||
register_default_string_val( "telnet_buffer", "40000");
|
||||
register_default_string_val( "telnet_block", "500");
|
||||
register_default_string_val( "stats", "n");
|
||||
register_default_string_val( "rel_api", CONFIG_RELEASE_API);
|
||||
register_default_string_val("pollmx","600");
|
||||
register_default_string_val("pollmin","15");
|
||||
register_default_string_val("ethtmout","8");
|
||||
register_default_string_val("dhcp_tmout","8");
|
||||
register_default_string_val("target", CONFIG_TARGET);
|
||||
register_default_string_val("led_vu_config", "");
|
||||
wait_for_commit();
|
||||
ESP_LOGD(TAG,"Done setting default values in nvs.");
|
||||
}
|
||||
@@ -303,7 +327,7 @@ uint32_t halSTORAGE_RebootCounterUpdate(int32_t xValue) {
|
||||
}
|
||||
RebootCounter = (xValue != 0) ? (RebootCounter + xValue) : 0;
|
||||
RecoveryRebootCounter = (xValue != 0) && is_recovery_running ? (RecoveryRebootCounter + xValue) : 0;
|
||||
return (RebootCounter) ;
|
||||
return RebootCounter ;
|
||||
}
|
||||
|
||||
void handle_ap_connect(nm_state_t new_state, int sub_state){
|
||||
@@ -356,11 +380,17 @@ void app_main()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char * fwurl = NULL;
|
||||
MEMTRACE_PRINT_DELTA();
|
||||
ESP_LOGI(TAG,"Starting app_main");
|
||||
initialize_nvs();
|
||||
MEMTRACE_PRINT_DELTA();
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
ESP_LOGI(TAG,"Setting up metrics.");
|
||||
metrics_init();
|
||||
MEMTRACE_PRINT_DELTA();
|
||||
#endif
|
||||
ESP_LOGI(TAG,"Setting up telnet.");
|
||||
init_telnet(); // align on 32 bits boundaries
|
||||
MEMTRACE_PRINT_DELTA();
|
||||
@@ -371,7 +401,6 @@ void app_main()
|
||||
network_event_group = xEventGroupCreate();
|
||||
ESP_LOGD(TAG,"Clearing CONNECTED_BIT from wifi group");
|
||||
xEventGroupClearBits(network_event_group, CONNECTED_BIT);
|
||||
|
||||
ESP_LOGI(TAG,"Registering default values");
|
||||
register_default_nvs();
|
||||
MEMTRACE_PRINT_DELTA();
|
||||
@@ -399,7 +428,10 @@ void app_main()
|
||||
led_vu_color_yellow(LED_VU_BRIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_event_boot(is_recovery_running?"recovery":"ota");
|
||||
#endif
|
||||
|
||||
ESP_LOGD(TAG,"Getting firmware OTA URL (if any)");
|
||||
fwurl = process_ota_url();
|
||||
|
||||
@@ -457,6 +489,9 @@ void app_main()
|
||||
taskYIELD();
|
||||
}
|
||||
ESP_LOGI(TAG,"Updating firmware from link: %s",fwurl);
|
||||
#if defined(CONFIG_WITH_METRICS)
|
||||
metrics_event("fw_update");
|
||||
#endif
|
||||
start_ota(fwurl, NULL, 0);
|
||||
}
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user