Compare commits

..

44 Commits

Author SHA1 Message Date
philippe44
d31697e7ef force pad_select for gpio use in dac_controlset - release 2023-11-09 13:47:08 -08:00
philippe44
e830b4db73 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-11-09 13:42:11 -08:00
philippe44
8fe21327b8 Update README.md 2023-11-08 16:54:54 -08:00
philippe44
3f487366ee Update README.md 2023-11-08 16:51:47 -08:00
philippe44
b875585aec Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-11-08 16:37:53 -08:00
philippe44
b46fccfc83 execute dac_controlset even w/o i2c (for gpio) - release 2023-11-08 16:37:49 -08:00
philippe44
655c17fb29 Update README.md 2023-11-08 11:36:29 -08:00
philippe44
b8bda0435a Update README.md 2023-11-08 11:34:17 -08:00
github-actions
dc5cb31efb Update prebuilt objects [skip actions] 2023-11-08 06:18:54 +00:00
philippe44
44ccbb49a6 ledvu update 2023-11-07 22:15:54 -08:00
philippe44
6589387cd3 Merge pull request #351 from wizmo2/ledvu-update-clean
Ledvu update (clean)
2023-11-07 22:08:42 -08:00
github-actions
0d3f6a8870 Update prebuilt objects [skip actions] 2023-11-06 01:24:27 +00:00
philippe44
bb6ec4a629 chmod... 2023-11-05 17:12:55 -08:00
philippe44
a029304776 chmod... 2023-11-05 15:52:02 -08:00
Wizmo2
dad8efff8b Merge branch 'ledvu-update-clean' of https://github.com/wizmo2/squeezelite-esp32 into ledvu-update-clean 2023-11-05 14:30:23 -05:00
Wizmo2
e8c6169e09 formating changes 2023-11-05 14:27:45 -05:00
Wizmo2
fc8d15f58d formating changes 2023-11-04 23:07:25 -04:00
Wizmo2
f9d7e15d4b add suspend service 2023-11-04 22:56:37 -04:00
github-actions
67722bef94 Update prebuilt objects [skip actions] 2023-11-04 02:25:17 +00:00
github-actions
8ea777ddff Update prebuilt objects [skip actions] 2023-11-04 01:11:45 +00:00
philippe44
ad8d587e94 no reboot on external active decoder - release 2023-11-03 18:09:39 -07:00
Wizmo2
667b90fafc ledvu-update-clean 2023-10-30 19:24:23 -04:00
philippe44
7be653f722 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-10-30 01:06:41 -07:00
philippe44
f6269a2f7a gap when changing queue workaround 2023-10-30 01:06:37 -07:00
philippe44
4b70514fa3 Update CHANGELOG 2023-10-29 00:08:20 -07:00
philippe44
00a0b8c36e more warnings 2023-10-28 20:54:37 -07:00
philippe44
9f4474a19c remove warnings 2023-10-28 20:51:57 -07:00
github-actions
58fac99e95 Update prebuilt objects [skip actions] 2023-10-29 03:19:48 +00:00
Sebastien L
cb7c8fd6be Merge remote-tracking branch 'origin/master-v4.3' into master-v4.3 2023-10-28 23:13:38 -04:00
Sebastien L
b9a1bf0432 remove bootswatch 2023-10-28 23:13:20 -04:00
github-actions
63658efefe Update prebuilt objects [skip actions] 2023-10-29 02:53:20 +00:00
Sebastien L
c76bbc3524 Fix recovery 2023-10-28 22:48:12 -04:00
philippe44
9619b1d792 Update CHANGELOG 2023-10-28 00:13:39 -07:00
philippe44
0bbd5a064f Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-10-27 18:27:24 -07:00
philippe44
986521fd4a fix vorbis & opus memory leaks 2023-10-27 18:26:57 -07:00
github-actions
406a56a3a3 Update prebuilt objects [skip actions] 2023-10-26 05:59:15 +00:00
philippe44
c9455f70ff Update CHANGELOG 2023-10-25 22:47:14 -07:00
philippe44
b5b76480e4 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-10-25 22:45:37 -07:00
philippe44
e6744deab8 fix vorbis not closing properly - release 2023-10-25 22:45:26 -07:00
github-actions
94baf86989 Update prebuilt objects [skip actions] 2023-10-25 01:48:05 +00:00
philippe44
0c856a37c1 well, fix 16 bits as well... 2023-10-24 16:27:27 -07:00
philippe44
d5f28375ce fix SPDIF miscount 2023-10-24 13:39:56 -07:00
philippe44
7ea5a93647 backport webUI changes as well (-disable) 2023-10-23 17:15:02 -07:00
philippe44
ca38a14420 cspot fixes backport 2023-10-23 17:09:50 -07:00
126 changed files with 2292 additions and 5847 deletions

View File

@@ -1,5 +1,28 @@
2023-10-11
- Reduce the size of binaries (Fixes https://github.com/sle118/squeezelite-esp32/issues/329)
2023-11-09
- force gpio_pad_select_gpio in dac_controlset in case somebody uses UART gpio's (or other pre-programmed)
2023-11-08
- execute dac_controlset even whne there is no i2s (for gpio)
2023-11-07
- led-vu gain + misc fixes
- bump plugin version to 0.600
2023-11-03
- don't reboot when external decoder is connected even with a LMS server
2023-10-28
- fix recovery size (remove bootstrap)
- improve NVS initialization structure
2023-10-27
- fix vorbis (and opus) memory leak
2023-10-25
- fix vorbis codec close
2023-10-23
- fix Spotify track insertion
- [WEB] Allow running without LMS with option "Audio/Disable Squeezelite"
2023-10.07

View File

@@ -25,7 +25,11 @@ ENV GCC_TOOLS_BASE=/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtens
# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
#
# to run the docker with netwotrk port published on the host:
# (windows)
# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
# (linux)
# docker run --rm -p 5000:5000/tcp -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv435
ARG IDF_CLONE_URL=https://github.com/espressif/esp-idf.git
ARG IDF_CLONE_BRANCH_OR_TAG=master

View File

@@ -187,15 +187,23 @@ bck=<gpio>,ws=<gpio>,do=<gpio>[,mck=0|1|2][,mute=<gpio>[:0|1][,model=TAS57xx|TAS
```
if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). By default GPIO0 is used as MCLK and only recent builds (post mid-2023) can use 1 or 2. Also be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed.
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power, speakder and headset on and off using a JSON syntax:
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power, speaker and headset on and off using a JSON syntax:
```json
{ <command>: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
<command>: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
{ <command>: [ <item1>, <item2>, ... <item3> ],
<command>: [ <item1>, <item2>, ... <item3> ],
... }
```
Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, headseton, headsetoff
Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, headseton, headsetoff (it **must** be an array even for a single item). Item is any of the following elements
```
{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}
{"gpio":<gpio>,"level":0|1}
{"delay":<ms>}
```
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here.
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. The 'val parameter can be an array [v1, v2,...] to write a serie of bytes in a single i2c burst (in that case 'mode' is ignored). **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
The `reg` key allow to write registers on i2c bus. The parameter `mode` allows to *or* the register with the value or to *and* it. Don't set `mode` if you simply want to write. The `val` parameter can be an array [v1, v2,...] to write a serie of bytes in a single i2c burst (in that case 'mode' is ignored). **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax. The `gpio` key is simply to set a gpio as part of DAC action and `delay` allows a pause between elements.
The 'power' command is used when powering on/off the DAC after the idle period (see -C option of squeezelite) and the 'speaker/headset' commands are sent when switching between speakers and headsets (see headset jack detection).
NB: For named configurations ((SqueezeAMP, Muse ... all except I2S), all this is ignored. For know codecs, the built-in sequences can be overwritten using dac_controlset
@@ -275,7 +283,7 @@ GPIO can be set to GND provide or Vcc at boot. This is convenient to power devic
The `<amp>` parameter can use used to assign a GPIO that will be set to active level (default 1) when playback starts. It will be reset when squeezelite becomes idle. The idle timeout is set on the squeezelite command line through `-C <timeout>`
The `<power>` parameter can use used to assign a GPIO that will be set to active level (default 1) when player is powered on and reset when powered off
The `<power>` parameter can use used to assign a GPIO that will be set to active level (default 1) when player is powered on and reset when powered off (in LMS, does not apply to AirPlay, Spotify or BT).
If you have an audio jack that supports insertion (use :0 or :1 to set the level when inserted), you can specify which GPIO it's connected to. Using the parameter jack_mutes_amp allows to mute the amp when headset (e.g.) is inserted.
@@ -319,18 +327,18 @@ See [set_GPIO](#set-gpio) for how to set the green and red LEDs (including addre
NB: For named configuration, GPIO affected to green and red LED cannot be changed but brightness option applies
### LED Strip
One LED strip with up to 255 addressable LEDs can be configured to offer enhanced visualizations. The LED strip can also be controlled remotely though the LMS server (using the CLI interface). Currently only WS2812B LEDs are supported. Set the LED Strip configuration (or NVS led_vu_config) to `WS2812,length=<n>,gpio=<gpio>, where <n> is the number of leds in the strip (1..255), and <gpio> is the data pin.`
The latest LMS plugin update is required to set the visualizer mode and brightness, in the ESP32 settings page for the player. The plugin also adds the following CLI command options
One LED strip with up to 255 addressable LEDs can be configured to offer enhanced visualizations. The VU Meter visualizer includes a battery status indicator (see Battery). Currently only WS2812B LEDs are supported. Set the LED Strip hardware configuration, or the NVS led_vu_config syntax is
```
<playerid> led_visual [<mode>] [brightness(1-255)]
Toggles or selects the visulaizer mode.
The visualizer brighness can be controled using the optional <brighness> tag.
<playerid> dmx <R,G,B|R,G,B,R,G,B ... R,G,B> [<offset>]
Sets the LED at position "offset" to any RGB color where "R"(red),"G"(green), and "B"(blue) are values from 0(off) to 255(max brightness).
Add additional RGB values to the delimited string to set multiple LEDs.
type=[WS2812],length=<n>,gpio=<dataPin>[,scale=<gain>]
```
where `<n>` is the number of LEDs in the strip (1..255). A `<scale>` gain value (percentage) can be added to enhance effect responses.
The latest LMS plugin update is required to set the visualizer mode and brightness in the ESP32 Settings page for the player, or a controllable display (see Extra/SqueezeESP32 menus). The plugin adds additional LMS CLI commands.
| Command | Notes |
| -------------------------------------------------- | ----------- |
| \<playerid\> led_visual \[\<mode\>\] \[\<brightness\>\] | Toggles or selects the visualizer "mode".<br />The visualizer brightness(0..255) can be controlled using the "brightness" tag. |
| \<playerid\> dmx \<R,G,B,R,G,B, ... R,G,B\> \[\<offset\>\] | Sets the LED color starting at position "offset"<br /> with "R"(red),"G"(green),and "B"(blue) color sequences.<br />Add additional RGB values to the delimited string to set multiple LEDs.<br /> |
### Rotary Encoder
One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software.

View File

@@ -1,48 +0,0 @@
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."
}
}

View File

@@ -293,11 +293,8 @@ bool led_strip_init(struct led_strip_t *led_strip)
static EXT_RAM_ATTR StackType_t xStack[LED_STRIP_TASK_SIZE] __attribute__ ((aligned (4)));
if ((led_strip == NULL) ||
(led_strip->rmt_channel >= RMT_CHANNEL_MAX) ||
(led_strip->gpio > GPIO_NUM_33) ||
(led_strip->led_strip_working == NULL) ||
(led_strip->led_strip_showing == NULL) ||
(led_strip->led_strip_length == 0) ||
(led_strip->access_semaphore == NULL)) {
return false;
}

View File

@@ -24,6 +24,7 @@
#include "monitor.h"
#include "led_strip.h"
#include "platform_config.h"
#include "services.h"
#include "led_vu.h"
static const char *TAG = "led_vu";
@@ -55,6 +56,7 @@ static EXT_RAM_ATTR struct {
int vu_start_l;
int vu_start_r;
int vu_status;
int vu_scale;
} strip;
static int led_addr(int pos ) {
@@ -70,31 +72,31 @@ static void battery_svc(float value, int cells) {
if (battery_handler_chain) battery_handler_chain(value, cells);
}
/****************************************************************************************
* Suspend.
*
*/
static void led_vu_sleep(void) {
led_vu_clear(led_display);
}
/****************************************************************************************
* Initialize the led vu strip if configured.
*
*/
void led_vu_init()
{
char* p;
char* config = config_alloc_get_str("led_vu_config", NULL, "N/A");
// Initialize led VU strip
char* drivername = strcasestr(config, "WS2812");
if ((p = strcasestr(config, "length")) != NULL) {
strip.length = atoi(strchr(p, '=') + 1);
} // else 0
if ((p = strcasestr(config, "gpio")) != NULL) {
strip.gpio = atoi(strchr(p, '=') + 1);
} else {
strip.gpio = LED_VU_DEFAULT_GPIO;
}
PARSE_PARAM(config, "length",'=', strip.length);
PARSE_PARAM(config, "gpio",'=', strip.gpio);
// check for valid configuration
if (!drivername || !strip.gpio) {
if (!strip.gpio) {
ESP_LOGI(TAG, "led_vu configuration invalid");
goto done;
}
strip.vu_scale = 100;
PARSE_PARAM(config, "scale",'=',strip.vu_scale);
battery_handler_chain = battery_handler_svc;
battery_handler_svc = battery_svc;
@@ -114,7 +116,7 @@ void led_vu_init()
strip.vu_start_r = strip.vu_length + 1;
strip.vu_status = strip.vu_length;
}
ESP_LOGI(TAG, "vu meter using length:%d left:%d right:%d status:%d", strip.vu_length, strip.vu_start_l, strip.vu_start_r, strip.vu_status);
ESP_LOGI(TAG, "vu meter using length:%d left:%d right:%d status:%d scale:%d", strip.vu_length, strip.vu_start_l, strip.vu_start_r, strip.vu_status, strip.vu_scale);
// create driver configuration
led_strip_config.rgb_led_type = RGB_LED_TYPE_WS2812;
@@ -138,6 +140,8 @@ void led_vu_init()
// reserver max memory for remote management systems
rmt_set_mem_block_num(led_strip_config.rmt_channel, 7);
services_sleep_setsuspend(led_vu_sleep);
led_vu_clear(led_display);
done:
@@ -157,6 +161,14 @@ uint16_t led_vu_string_length() {
return (uint16_t)strip.length;
}
/****************************************************************************************
* Returns a user defined scale (percent)
*/
uint16_t led_vu_scale() {
if (!led_display) return 0;
return (uint16_t)strip.vu_scale;
}
/****************************************************************************************
* Turns all LEDs off (Black)
*/

View File

@@ -21,6 +21,7 @@
extern struct led_strip_t* led_display;
uint16_t led_vu_string_length();
uint16_t led_vu_scale();
void led_vu_progress_bar(int pct, int bright);
void led_vu_display(int vu_l, int vu_r, int bright, bool comet);
void led_vu_spin_dial(int gain, int rate, int speed, bool comet);

View File

@@ -1,124 +0,0 @@
#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

View File

@@ -1,46 +0,0 @@
#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

View File

@@ -1,5 +0,0 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
REQUIRES json tools platform_config wifi-manager esp-tls platform_config
PRIV_REQUIRES esp32 freertos
)

View File

@@ -1,98 +0,0 @@
#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

View File

@@ -1,53 +0,0 @@
#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

View File

@@ -1,148 +0,0 @@
#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

View File

@@ -1,18 +0,0 @@
#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

View File

@@ -1,163 +0,0 @@
#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

View File

@@ -1,11 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int metrics_http_post_request(const char* payload, const char* url);
#ifdef __cplusplus
}
#endif

View File

@@ -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 * 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);
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);
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 * name_space);
esp_err_t erase_nvs_partition(const char * partition, const char * ns);
void erase_settings_partition();
#ifdef __cplusplus
}

View File

@@ -538,7 +538,7 @@ bool config_set_group_bit(int bit_num,bool flag){
return result;
}
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) {
void config_set_default(nvs_type_t type, const char *key, const void * default_value, size_t blob_size) {
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "Unable to lock config");
return;
@@ -634,7 +634,7 @@ cJSON * config_alloc_get_cjson(const char *key){
}
return conf_json;
}
esp_err_t config_set_cjson(const char *key, cJSON *value, bool free_cjson){
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
char * value_str = cJSON_PrintUnformatted(value);
if(value_str==NULL){
ESP_LOGE(TAG, "Unable to print cJSON for key [%s]", key);
@@ -642,14 +642,9 @@ esp_err_t config_set_cjson(const char *key, cJSON *value, bool free_cjson){
}
esp_err_t err = config_set_value(NVS_TYPE_STR,key, value_str);
free(value_str);
if(free_cjson){
cJSON_Delete(value);
}
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){
@@ -791,44 +786,6 @@ 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);

View File

@@ -8,30 +8,25 @@
#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_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(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_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
@@ -55,17 +50,13 @@ 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);
void config_set_default(nvs_type_t type, const char *key, const void * default_value, size_t blob_size);
void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
bool wait_for_commit();
char * config_alloc_get_json(bool bFormatted);

View File

@@ -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 metrics)
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools)
set_source_files_properties(cmd_config.c
PROPERTIES COMPILE_FLAGS

View File

@@ -1,6 +1,6 @@
idf_component_register( SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES bootloader_support json
PRIV_REQUIRES bootloader_support
)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")

View File

@@ -3,10 +3,9 @@
#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,
@@ -27,12 +26,7 @@ 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) {
}

View File

@@ -44,24 +44,15 @@ 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
#if CONFIG_WITH_CONFIG_UI
register_rotary_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);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -589,7 +589,6 @@ 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");

View File

@@ -62,7 +62,7 @@ static int perform_ota_update(int argc, char **argv)
const esp_console_cmd_t cmd = {
.command = "update",
.help = "Update from URL",
.help = "Updates the application binary from the provided URL",
.hint = NULL,
.func = &perform_ota_update,
.argtable = &ota_args

View File

@@ -31,9 +31,6 @@
#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")
@@ -67,8 +64,10 @@ static void register_heap();
static void register_dump_heap();
static void register_version();
static void register_restart();
#if CONFIG_WITH_CONFIG_UI
static void register_deep_sleep();
static void register_light_sleep();
#endif
static void register_factory_boot();
static void register_restart_ota();
static void register_set_services();
@@ -86,8 +85,8 @@ FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size){
void register_system()
{
register_set_services();
register_setdevicename();
register_set_services();
register_free();
register_heap();
register_dump_heap();
@@ -555,7 +554,7 @@ static void register_tasks()
/** 'deep_sleep' command puts the chip into deep sleep mode */
#if CONFIG_WITH_CONFIG_UI
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
@@ -619,6 +618,8 @@ static void register_deep_sleep()
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#endif
static int enable_disable(FILE * f,char * nvs_name, struct arg_lit *arg){
esp_err_t err = config_set_value(NVS_TYPE_STR, nvs_name, arg->count>0?"Y":"N");
const char * name = arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary;
@@ -707,9 +708,6 @@ 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);
}
@@ -737,6 +735,8 @@ static void register_set_services(){
cmd_to_json_with_cb(&cmd,&set_services_cb);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#if CONFIG_WITH_CONFIG_UI
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
@@ -828,4 +828,4 @@ static void register_light_sleep()
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#endif

View File

@@ -18,6 +18,7 @@ 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

View File

@@ -37,9 +37,6 @@ 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;
@@ -56,6 +53,13 @@ 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)
{
@@ -68,7 +72,27 @@ 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;
@@ -180,10 +204,10 @@ void register_wifi_join()
void register_wifi()
{
#ifdef WIFI_CMDLINE
#ifdef WIFI_CMDLINE
register_wifi_join();
if(bypass_network_manager){
initialise_wifi();
}
#endif
#endif
}

View File

@@ -27,9 +27,7 @@
#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"
@@ -87,22 +85,14 @@ cJSON * get_cmd_list(){
}
void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg){
char * p=NULL;
bool enabled = false;
if(!root) {
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) {
enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
cJSON_AddBoolToObject(root,arg->hdr.longopts,strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
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;
@@ -370,6 +360,8 @@ 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();

View File

@@ -1,5 +1,5 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
REQUIRES json tools platform_config display wifi-manager esp-tls platform_config
REQUIRES json tools platform_config display wifi-manager
PRIV_REQUIRES soc esp32
)

View File

@@ -47,7 +47,6 @@ 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;
@@ -330,7 +329,7 @@ esp_err_t config_ledvu_set(ledvu_struct_t * config){
esp_err_t err=ESP_OK;
char * config_buffer=malloc_init_external(buffer_size);
if(config_buffer) {
snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i",config->type, config->length, config->gpio);
snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i,scale=%i",config->type, config->length, config->gpio, config->scale);
log_send_messaging(MESSAGING_INFO,"Updating ledvu configuration to %s",config_buffer);
err = config_set_value(NVS_TYPE_STR, "led_vu_config", config_buffer);
if(err!=ESP_OK){
@@ -593,7 +592,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, sizeof(config.model)-1);
PARSE_PARAM_STR(item, "model", '=', config.model, 31);
if ((p = strcasestr(item, "port")) != NULL) {
char port[8] = "";
@@ -647,12 +646,6 @@ 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);
@@ -665,7 +658,6 @@ 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);
}
@@ -769,14 +761,13 @@ const rotary_struct_t * config_rotary_get() {
*/
const ledvu_struct_t * config_ledvu_get() {
static ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0};
static ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0, .scale= 100 };
char *config = config_alloc_get_default(NVS_TYPE_STR, "led_vu_config", NULL, 0);
if (config && *config) {
char *p;
// ToDo: Add code for future support of alternate led types
if ((p = strcasestr(config, "gpio")) != NULL) ledvu.gpio = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "length")) != NULL) ledvu.length = atoi(strchr(p, '=') + 1);
PARSE_PARAM_STR(config, "type", '=', ledvu.type, 15);
PARSE_PARAM(config, "gpio", '=', ledvu.gpio);
PARSE_PARAM(config, "length", '=', ledvu.length);
PARSE_PARAM(config, "scale", '=', ledvu.scale);
free(config);
}
return &ledvu;
@@ -831,7 +822,6 @@ 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;
}
@@ -1178,7 +1168,7 @@ cJSON * get_psram_gpio_list(cJSON * list){
/****************************************************************************************
*
*/
cJSON * get_gpio_list_handler(bool refresh) {
cJSON * get_gpio_list(bool refresh) {
gpio_num_t gpio_num;
if(gpio_list && !refresh){
return gpio_list;

View File

@@ -13,7 +13,7 @@
#include "driver/i2s.h"
#include "driver/spi_master.h"
#include "gpio_exp.h"
#include "cJSON.h"
extern const char *i2c_name_type;
extern const char *spi_name_type;
@@ -73,7 +73,6 @@ 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 {
@@ -90,6 +89,7 @@ typedef struct {
char type[16];
int length;
int gpio;
int scale;
} ledvu_struct_t;
typedef struct {
@@ -118,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();

View File

@@ -53,6 +53,7 @@ private:
std::atomic<states> state;
std::string credentials;
bool zeroConf;
std::atomic<bool> flushed = false, notify = true;
int startOffset, volume = 0, bitrate = 160;
httpd_handle_t serverHandle;
@@ -60,6 +61,7 @@ private:
cspot_cmd_cb_t cmdHandler;
cspot_data_cb_t dataHandler;
std::string lastTrackId;
cspot::TrackInfo trackInfo;
std::shared_ptr<cspot::LoginBlob> blob;
std::unique_ptr<cspot::SpircHandler> spirc;
@@ -206,11 +208,13 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
trackStatus = TRACK_INIT;
// memorize position for when track's beginning will be detected
startOffset = std::get<int>(event->data);
notify = !flushed;
flushed = false;
// Spotify servers do not send volume at connection
spirc->setRemoteVolume(volume);
cmdHandler(CSPOT_START, 44100);
CSPOT_LOG(info, "(re)start playing");
CSPOT_LOG(info, "(re)start playing at %d", startOffset);
break;
}
case cspot::SpircHandler::EventType::PLAY_PAUSE: {
@@ -219,16 +223,14 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
break;
}
case cspot::SpircHandler::EventType::TRACK_INFO: {
auto trackInfo = std::get<cspot::TrackInfo>(event->data);
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
spirc->updatePositionMs(startOffset);
startOffset = 0;
trackInfo = std::get<cspot::TrackInfo>(event->data);
break;
}
case cspot::SpircHandler::EventType::FLUSH:
flushed = true;
__attribute__ ((fallthrough));
case cspot::SpircHandler::EventType::NEXT:
case cspot::SpircHandler::EventType::PREV:
case cspot::SpircHandler::EventType::FLUSH: {
case cspot::SpircHandler::EventType::PREV: {
cmdHandler(CSPOT_FLUSH);
break;
}
@@ -411,8 +413,13 @@ void cspotPlayer::runTask() {
uint32_t started;
cmdHandler(CSPOT_QUERY_STARTED, &started);
if (started) {
CSPOT_LOG(info, "next track's audio has reached DAC");
spirc->notifyAudioReachedPlayback();
CSPOT_LOG(info, "next track's audio has reached DAC (offset %d)", startOffset);
if (notify) spirc->notifyAudioReachedPlayback();
else notify = true;
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
spirc->updatePositionMs(startOffset);
startOffset = 0;
trackStatus = TRACK_STREAM;
}
} else if (trackStatus == TRACK_END) {

View File

View File

0
components/spotify/cspot/bell/external/nanopb/generator/protoc vendored Normal file → Executable file
View File

View File

View File

@@ -37,7 +37,6 @@ class TrackPlayer : bell::Task {
typedef std::function<size_t(uint8_t*, size_t, std::string_view)>
DataCallback;
typedef std::function<void()> EOFCallback;
typedef std::function<bool()> isAiringCallback;
TrackPlayer(std::shared_ptr<cspot::Context> ctx,
std::shared_ptr<cspot::TrackQueue> trackQueue,

View File

@@ -54,6 +54,7 @@ class QueuedTrack {
uint32_t requestedPosition;
std::string identifier;
bool loading = false;
// Will return nullptr if the track is not ready
std::shared_ptr<cspot::CDNAudioFile> getAudioFile();
@@ -100,7 +101,7 @@ class TrackQueue : public bell::Task {
bool hasTracks();
bool isFinished();
bool skipTrack(SkipDirection dir, bool expectNotify = true);
void updateTracks(uint32_t requestedPosition = 0, bool initial = false);
bool updateTracks(uint32_t requestedPosition = 0, bool initial = false);
TrackInfo getTrackInfo(std::string_view identifier);
std::shared_ptr<QueuedTrack> consumeTrack(
std::shared_ptr<QueuedTrack> prevSong, int& offset);

View File

@@ -204,12 +204,19 @@ void SpircHandler::handleFrame(std::vector<uint8_t>& data) {
CSPOT_LOG(debug, "Got replace frame");
playbackState->syncWithRemote();
trackQueue->updateTracks(playbackState->remoteFrame.state.position_ms,
false);
// 1st track is the current one, but update the position
bool cleared = trackQueue->updateTracks(
playbackState->remoteFrame.state.position_ms +
ctx->timeProvider->getSyncedTimestamp() -
playbackState->innerFrame.state.position_measured_at);
this->notify();
sendEvent(EventType::FLUSH);
trackPlayer->resetState();
// need to re-load all if streaming track is completed
if (cleared) {
sendEvent(EventType::FLUSH);
trackPlayer->resetState();
}
break;
}
case MessageType_kMessageTypeShuffle: {

View File

@@ -201,6 +201,7 @@ void TrackPlayer::runTask() {
}
eof = false;
track->loading = true;
CSPOT_LOG(info, "Playing");
@@ -255,6 +256,7 @@ void TrackPlayer::runTask() {
// always move back to LOADING (ensure proper seeking after last track has been loaded)
currentTrackStream = nullptr;
track->loading = false;
}
if (eof) {

View File

@@ -587,8 +587,9 @@ bool TrackQueue::isFinished() {
return currentTracksIndex >= currentTracks.size() - 1;
}
void TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
bool TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
std::scoped_lock lock(tracksMutex);
bool cleared = true;
if (initial) {
// Clear preloaded tracks
@@ -609,13 +610,28 @@ void TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
playableSemaphore->give();
} else {
// Clear preloaded tracks
preloadedTracks.clear();
// Copy requested track list
currentTracks = playbackState->remoteTracks;
// Push a song on the preloaded queue
queueNextTrack(0, requestedPosition);
// try to not re-load track if we are still loading it
if (preloadedTracks[0]->loading) {
// remove everything except first track
preloadedTracks.erase(preloadedTracks.begin() + 1, preloadedTracks.end());
// Push a song on the preloaded queue
CSPOT_LOG(info, "Keeping current track");
queueNextTrack(1);
cleared = false;
} else {
// Clear preloaded tracks
preloadedTracks.clear();
// Push a song on the preloaded queue
CSPOT_LOG(info, "Re-loading current track");
queueNextTrack(0, requestedPosition);
}
}
return cleared;
}

View File

@@ -216,7 +216,7 @@ static EXT_RAM_ATTR struct {
static EXT_RAM_ATTR struct {
int mode;
int n, style, max;
int n, style, max, gain;
u16_t config;
struct bar_s bars[MAX_BARS] ;
} led_visu;
@@ -1089,10 +1089,10 @@ static void displayer_update(void) {
if (led_display && led_visu.mode) {
// run built in visualizer effects
if (led_visu.mode == VISU_VUMETER) {
vu_scale(led_visu.bars, led_visu.max, meters.levels);
vu_scale(led_visu.bars, led_visu.gain, meters.levels);
led_vu_display(led_visu.bars[0].current, led_visu.bars[1].current, led_visu.max, led_visu.style);
} else if (led_visu.mode == VISU_SPECTRUM) {
spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples);
spectrum_scale(led_visu.n, led_visu.bars, led_visu.gain, meters.samples);
uint8_t* p = (uint8_t*) led_data;
for (int i = 0; i < led_visu.n; i++) {
*p = led_visu.bars[i].current;
@@ -1100,7 +1100,7 @@ static void displayer_update(void) {
}
led_vu_spectrum(led_data, led_visu.max, led_visu.n, led_visu.style);
} else if (led_visu.mode == VISU_WAVEFORM) {
spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples);
spectrum_scale(led_visu.n, led_visu.bars, led_visu.gain, meters.samples);
led_vu_spin_dial(
led_visu.bars[led_visu.n-2].current,
led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max,
@@ -1277,8 +1277,8 @@ static void ledv_handler( u8_t *data, int len) {
led_visu.mode = pkt->which;
led_visu.style = pkt->style;
led_visu.max = pkt->bright;
led_visu.gain = led_visu.max * led_vu_scale() / 100;
led_vu_clear();
if (led_visu.mode) {
if (led_visu.mode == VISU_SPECTRUM) {
led_visu.n = (led_visu.config < MAX_BARS) ? led_visu.config : MAX_BARS;
@@ -1293,8 +1293,10 @@ static void ledv_handler( u8_t *data, int len) {
// reset bars maximum
for (int i = led_visu.n; --i >= 0;) led_visu.bars[i].max = 0;
LOG_INFO("LED Visualizer mode %u with bars:%u max:%u style:%d", led_visu.mode, led_visu.n, led_visu.max, led_visu.style);
LOG_INFO("LED Visualizer mode %u with bars:%u max:%u style:%d gain:%u", led_visu.mode, led_visu.n, led_visu.max, led_visu.style, led_visu.gain);
} else {
led_vu_clear();
LOG_INFO("Stopping led visualizer");
}

View File

@@ -61,7 +61,6 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool
char *p;
i2c_addr = adac_init(config, i2c_port_num);
if (!i2c_addr) return true;
ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
@@ -137,6 +136,7 @@ bool i2c_json_execute(char *set) {
if ((action = cJSON_GetObjectItemCaseSensitive(item, "gpio")) != NULL) {
cJSON *level = cJSON_GetObjectItemCaseSensitive(item, "level");
ESP_LOGI(TAG, "set GPIO %d at %d", action->valueint, level->valueint);
if (action->valueint < GPIO_NUM_MAX) gpio_pad_select_gpio(action->valueint);
gpio_set_direction_x(action->valueint, GPIO_MODE_OUTPUT);
gpio_set_level_x(action->valueint, level->valueint);
continue;

View File

@@ -186,7 +186,7 @@ static int read_opus_header(void) {
switch (u->status) {
case OGG_SYNC:
u->status = OGG_ID_HEADER;
OG(&gu, stream_init, &u->state, OG(&gu, page_serialno, &u->page));
OG(&gu, stream_reset_serialno, &u->state, OG(&gu, page_serialno, &u->page));
fetch = false;
break;
case OGG_ID_HEADER:
@@ -359,10 +359,10 @@ static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
u->status = OGG_SYNC;
u->overframes = 0;
OG(&gu, sync_clear, &u->sync);
OG(&gu, stream_clear, &u->state);
OG(&gu, stream_init, &u->state, -1);
OG(&go, stream_clear, &u->state);
OG(&go, sync_clear, &u->sync);
OG(&go, stream_init, &u->state, -1);
}
static void opus_close(void) {
@@ -372,8 +372,8 @@ static void opus_close(void) {
free(u->overbuf);
u->overbuf = NULL;
OG(&gu, stream_clear, &u->state);
OG(&gu, sync_clear, &u->sync);
OG(&go, stream_clear, &u->state);
OG(&go, sync_clear, &u->sync);
}
static bool load_opus(void) {
@@ -394,7 +394,7 @@ static bool load_opus(void) {
}
g_handle->ogg_stream_clear = dlsym(g_handle->handle, "ogg_stream_clear");
g_handle->.ogg_stream_reset = dlsym(g_handle->handle, "ogg_stream_reset");
g_handle->ogg_stream_reset = dlsym(g_handle->handle, "ogg_stream_reset");
g_handle->ogg_stream_eos = dlsym(g_handle->handle, "ogg_stream_eos");
g_handle->ogg_stream_reset_serialno = dlsym(g_handle->handle, "ogg_stream_reset_serialno");
g_handle->ogg_sync_clear = dlsym(g_handle->handle, "ogg_sync_clear");

View File

@@ -744,7 +744,7 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst) {
// we assume frame == 0 as well...
if (!src) {
count = 192;
count = 0;
vu = VUCP24[0];
}
@@ -757,7 +757,7 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst) {
if (!count--) {
*dst++ = (vu << 24) | (PREAMBLE_B << 16) | 0xCCCC;
count = 192;
count = 191;
} else {
*dst++ = (vu << 24) | (PREAMBLE_M << 16) | 0xCCCC;
}
@@ -771,7 +771,7 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst) {
if (!count--) {
*dst++ = (vu << 24) | (PREAMBLE_B << 16) | aux;
count = 192;
count = 191;
} else {
*dst++ = (vu << 24) | (PREAMBLE_M << 16) | aux;
}

View File

@@ -982,8 +982,8 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con
// in embedded we give up after a while no matter what
if (++failed_connect > MAX_SERVER_RETRIES && !server) {
slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, MAX_SERVER_RETRIES);
if (!slimproto_ip) return;
} else if (reconnect && MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES) return;
if (!slimproto_ip && !output.external) return;
} else if (reconnect && MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES && !output.external) return;
#else
// rediscover server if it was not set at startup or exit
if (!server && ++failed_connect > 5) {

View File

@@ -72,6 +72,13 @@ static struct vorbis {
// vorbis symbols to be dynamically loaded - from either vorbisfile or vorbisidec (tremor) version of library
vorbis_info *(* ov_info)(OggVorbis_File *vf, int link);
int (* ov_clear)(OggVorbis_File *vf);
void (* ov_info_init)(vorbis_info* vi);
void (* ov_info_clear)(vorbis_info* vi);
void (* ov_comment_init)(vorbis_comment* vc);
void (* ov_comment_clear(vorbis_comment *vc);
int (* ov_block_init)(vorbis_dsp_state* v, vorbis_block* vb);
int (* ov_block_clear)(vorbis_block* vb);
void (* ov_vorbis_dsp_clear)(vorbis_dsp_state* v);
long (* ov_read)(OggVorbis_File *vf, char *buffer, int length, int bigendianp, int word, int sgned, int *bitstream);
long (* ov_read_tremor)(OggVorbis_File *vf, char *buffer, int length, int *bitstream);
int (* ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks);
@@ -396,33 +403,32 @@ static decode_state vorbis_decode(void) {
}
static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
LOG_INFO("OPENING CODEC");
if (v->opened) {
OV(&go, block_clear, &v->block);
OV(&go, info_clear, &v->info);
OV(&go, dsp_clear, &v->decoder);
OV(&gv, block_clear, &v->block);
OV(&gv, dsp_clear, &v->decoder);
OV(&gv, info_clear, &v->info);
}
v->opened = false;
v->status = OGG_SYNC;
v->overflow = 0;
OG(&gu, sync_clear, &v->sync);
OG(&gu, stream_clear, &v->state);
OG(&gu, stream_init, &v->state, -1);
OG(&go, stream_clear, &v->state);
OG(&go, sync_clear, &v->sync);
OG(&go, stream_init, &v->state, -1);
}
static void vorbis_close() {
return;
LOG_INFO("CLOSING CODEC");
if (v->opened) {
OV(&go, block_clear, &v->block);
OV(&go, info_clear, &v->info);
OV(&go, dsp_clear, &v->decoder);
OV(&gv, block_clear, &v->block);
OV(&gv, dsp_clear, &v->decoder);
// info must be last otherwise there is memory leak (where is it said... nowhere)
OV(&gv, info_clear, &v->info);
// we don' t have comments to free
}
v->opened = false;
OG(&go, stream_clear, &v->state);
OG(&go, sync_clear, &v->sync);
}
@@ -469,6 +475,11 @@ static bool load_vorbis() {
v_handle.ov_read = dlsym(handle, "ov_read");
v_handle.ov_info = dlsym(handle, "ov_info");
v_handle.ov_clear = dlsym(handle, "ov_clear");
v.handle.ov_info_clear = dlsym(gv.handle, "vorbis_info_clear");
v.handle.ov_comment_init = dlsym(gv.handle, "vorbis_comment_init");
v.handle.ov_comment_clear = dlsym(gv.handle, "vorbis_comment_clear");
v.handle.ov_block_init = dlsym(gv.handle, "vorbis_block_init");
v.handle.ov_block_clear = dlsym(gv.handle, "vorbis_block_clear");
v_handle.ov_open_callbacks = dlsym(handle, "ov_open_callbacks");
if ((err = dlerror()) != NULL) {

View File

@@ -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 json
PRIV_REQUIRES esp_http_client esp-tls
INCLUDE_DIRS .
)

View File

@@ -24,7 +24,6 @@
#error CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS must be at least 2
#endif
#include "cJSON.h"
const static char TAG[] = "tools";
/****************************************************************************************
@@ -319,30 +318,11 @@ 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);
}

View File

@@ -9,10 +9,6 @@
*/
#pragma once
#include "cJSON.h"
#include "time.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef __cplusplus
extern "C" {
@@ -74,9 +70,6 @@ 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

View File

@@ -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;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
declare function _exports(grunt: any): void;
export = _exports;

View File

@@ -1,2 +0,0 @@
declare function _exports(grunt: any): void;
export = _exports;

View File

@@ -1,2 +0,0 @@
declare function _exports(grunt: any): void;
export = _exports;

View File

@@ -1,688 +0,0 @@
/**
* Class definition
*/
export class Alert extends BaseComponent {
static get NAME(): string;
static jQueryInterface(config: any): any;
close(): void;
_destroyElement(): void;
}
/**
* Class definition
*/
export class Button extends BaseComponent {
static get NAME(): string;
static jQueryInterface(config: any): any;
toggle(): void;
}
/**
* Class definition
*/
export class Carousel extends BaseComponent {
static get Default(): {
interval: number;
keyboard: boolean;
pause: string;
ride: boolean;
touch: boolean;
wrap: boolean;
};
static get DefaultType(): {
interval: string;
keyboard: string;
pause: string;
ride: string;
touch: string;
wrap: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
_interval: NodeJS.Timeout;
_activeElement: any;
_isSliding: boolean;
touchTimeout: NodeJS.Timeout;
_swipeHelper: Swipe;
_indicatorsElement: any;
next(): void;
nextWhenVisible(): void;
prev(): void;
pause(): void;
cycle(): void;
_maybeEnableCycle(): void;
to(index: any): void;
_addEventListeners(): void;
_addTouchEventListeners(): void;
_keydown(event: any): void;
_getItemIndex(element: any): number;
_setActiveIndicatorElement(index: any): void;
_updateInterval(): void;
_slide(order: any, element?: any): void;
_isAnimated(): any;
_getActive(): any;
_getItems(): any[];
_clearInterval(): void;
_directionToOrder(direction: any): "next" | "prev";
_orderToDirection(order: any): "right" | "left";
}
/**
* Class definition
*/
export class Collapse extends BaseComponent {
static get Default(): {
parent: any;
toggle: boolean;
};
static get DefaultType(): {
parent: string;
toggle: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
_isTransitioning: boolean;
_triggerArray: any[];
toggle(): void;
show(): void;
hide(): void;
_isShown(element?: any): any;
_getDimension(): "width" | "height";
_initializeChildren(): void;
_getFirstLevelChildren(selector: any): any[];
_addAriaAndCollapsedClass(triggerArray: any, isOpen: any): void;
}
/**
* Class definition
*/
export class Dropdown extends BaseComponent {
static get Default(): {
autoClose: boolean;
boundary: string;
display: string;
offset: number[];
popperConfig: any;
reference: string;
};
static get DefaultType(): {
autoClose: string;
boundary: string;
display: string;
offset: string;
popperConfig: string;
reference: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
static clearMenus(event: any): void;
static dataApiKeydownHandler(event: any): void;
_popper: Popper.Instance;
_parent: any;
_menu: any;
_inNavbar: boolean;
toggle(): void;
show(): void;
hide(): void;
update(): void;
_completeHide(relatedTarget: any): void;
_createPopper(): void;
_isShown(): any;
_getPlacement(): "top" | "bottom" | "top-end" | "top-start" | "bottom-end" | "bottom-start" | "left-start" | "right-start";
_detectNavbar(): boolean;
_getOffset(): any;
_getPopperConfig(): any;
_selectMenuItem({ key, target }: {
key: any;
target: any;
}): void;
}
/**
* Class definition
*/
export class Modal extends BaseComponent {
static get Default(): {
backdrop: boolean;
focus: boolean;
keyboard: boolean;
};
static get DefaultType(): {
backdrop: string;
focus: string;
keyboard: string;
};
static get NAME(): string;
static jQueryInterface(config: any, relatedTarget: any): any;
_dialog: any;
_backdrop: Backdrop;
_focustrap: FocusTrap;
_isShown: boolean;
_isTransitioning: boolean;
_scrollBar: ScrollBarHelper;
toggle(relatedTarget: any): void;
show(relatedTarget: any): void;
hide(): void;
handleUpdate(): void;
_initializeBackDrop(): Backdrop;
_initializeFocusTrap(): FocusTrap;
_showElement(relatedTarget: any): void;
_addEventListeners(): void;
_hideModal(): void;
_isAnimated(): any;
_triggerBackdropTransition(): void;
/**
* The following methods are used to handle overflowing modals
*/
_adjustDialog(): void;
_resetAdjustments(): void;
}
/**
* Class definition
*/
export class Offcanvas extends BaseComponent {
static get Default(): {
backdrop: boolean;
keyboard: boolean;
scroll: boolean;
};
static get DefaultType(): {
backdrop: string;
keyboard: string;
scroll: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
_isShown: boolean;
_backdrop: Backdrop;
_focustrap: FocusTrap;
toggle(relatedTarget: any): void;
show(relatedTarget: any): void;
hide(): void;
_initializeBackDrop(): Backdrop;
_initializeFocusTrap(): FocusTrap;
_addEventListeners(): void;
}
/**
* Class definition
*/
export class Popover extends Tooltip {
static get Default(): {
content: string;
offset: number[];
placement: string;
template: string;
trigger: string;
allowList: {
'*': (string | RegExp)[];
a: string[];
area: any[];
b: any[];
br: any[];
col: any[];
code: any[];
div: any[];
em: any[];
hr: any[];
h1: any[];
h2: any[];
h3: any[];
h4: any[];
h5: any[];
h6: any[];
i: any[];
img: string[];
li: any[];
ol: any[];
p: any[];
pre: any[];
s: any[];
small: any[];
span: any[];
sub: any[];
sup: any[];
strong: any[];
u: any[];
ul: any[];
};
animation: boolean;
boundary: string;
container: boolean;
customClass: string;
delay: number;
fallbackPlacements: string[];
html: boolean;
popperConfig: any;
sanitize: boolean;
sanitizeFn: any;
selector: boolean;
title: string;
};
static get DefaultType(): {
content: string;
allowList: string;
animation: string;
boundary: string;
container: string;
customClass: string;
delay: string;
fallbackPlacements: string;
html: string;
offset: string;
placement: string;
popperConfig: string;
sanitize: string;
sanitizeFn: string;
selector: string;
template: string;
title: string;
trigger: string;
};
_isWithContent(): any;
_getContentForTemplate(): {
".popover-header": any;
".popover-body": any;
};
_getContent(): any;
}
/**
* Class definition
*/
export class ScrollSpy extends BaseComponent {
static get Default(): {
offset: any;
rootMargin: string;
smoothScroll: boolean;
target: any;
threshold: number[];
};
static get DefaultType(): {
offset: string;
rootMargin: string;
smoothScroll: string;
target: string;
threshold: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
_targetLinks: Map<any, any>;
_observableSections: Map<any, any>;
_rootElement: any;
_activeTarget: any;
_observer: IntersectionObserver;
_previousScrollData: {
visibleEntryTop: number;
parentScrollTop: number;
};
refresh(): void;
_maybeEnableSmoothScroll(): void;
_getNewObserver(): IntersectionObserver;
_observerCallback(entries: any): void;
_initializeTargetsAndObservables(): void;
_process(target: any): void;
_activateParents(target: any): void;
_clearActiveClass(parent: any): void;
}
/**
* Class definition
*/
export class Tab extends BaseComponent {
static get NAME(): string;
static jQueryInterface(config: any): any;
constructor(element: any);
_parent: any;
show(): void;
_activate(element: any, relatedElem: any): void;
_deactivate(element: any, relatedElem: any): void;
_keydown(event: any): void;
_getChildren(): any[];
_getActiveElem(): any;
_setInitialAttributes(parent: any, children: any): void;
_setInitialAttributesOnChild(child: any): void;
_setInitialAttributesOnTargetPanel(child: any): void;
_toggleDropDown(element: any, open: any): void;
_setAttributeIfNotExists(element: any, attribute: any, value: any): void;
_elemIsActive(elem: any): any;
_getInnerElement(elem: any): any;
_getOuterElement(elem: any): any;
}
/**
* Class definition
*/
export class Toast extends BaseComponent {
static get Default(): {
animation: boolean;
autohide: boolean;
delay: number;
};
static get DefaultType(): {
animation: string;
autohide: string;
delay: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
_timeout: NodeJS.Timeout;
_hasMouseInteraction: boolean;
_hasKeyboardInteraction: boolean;
show(): void;
hide(): void;
isShown(): any;
_maybeScheduleHide(): void;
_onInteraction(event: any, isInteracting: any): void;
_setListeners(): void;
_clearTimeout(): void;
}
/**
* Class definition
*/
export class Tooltip extends BaseComponent {
static get Default(): {
allowList: {
'*': (string | RegExp)[];
a: string[];
area: any[];
b: any[];
br: any[];
col: any[];
code: any[];
div: any[];
em: any[];
hr: any[];
h1: any[];
h2: any[];
h3: any[];
h4: any[];
h5: any[];
h6: any[];
i: any[];
img: string[];
li: any[];
ol: any[];
p: any[];
pre: any[];
s: any[];
small: any[];
span: any[];
sub: any[];
sup: any[];
strong: any[];
u: any[];
ul: any[];
};
animation: boolean;
boundary: string;
container: boolean;
customClass: string;
delay: number;
fallbackPlacements: string[];
html: boolean;
offset: number[];
placement: string;
popperConfig: any;
sanitize: boolean;
sanitizeFn: any;
selector: boolean;
template: string;
title: string;
trigger: string;
};
static get DefaultType(): {
allowList: string;
animation: string;
boundary: string;
container: string;
customClass: string;
delay: string;
fallbackPlacements: string;
html: string;
offset: string;
placement: string;
popperConfig: string;
sanitize: string;
sanitizeFn: string;
selector: string;
template: string;
title: string;
trigger: string;
};
static get NAME(): string;
static jQueryInterface(config: any): any;
_isEnabled: boolean;
_timeout: number;
_isHovered: boolean;
_activeTrigger: {};
_popper: Popper.Instance;
_templateFactory: TemplateFactory;
_newContent: any;
tip: Element;
enable(): void;
disable(): void;
toggleEnabled(): void;
toggle(): void;
show(): void;
hide(): void;
update(): void;
_isWithContent(): boolean;
_getTipElement(): Element;
_createTipElement(content: any): Element;
setContent(content: any): void;
_getTemplateFactory(content: any): TemplateFactory;
_getContentForTemplate(): {
".tooltip-inner": any;
};
_getTitle(): any;
_initializeOnDelegatedTarget(event: any): any;
_isAnimated(): any;
_isShown(): boolean;
_createPopper(tip: any): Popper.Instance;
_getOffset(): any;
_resolvePossibleFunction(arg: any): any;
_getPopperConfig(attachment: any): any;
_setListeners(): void;
_hideModalHandler: () => void;
_fixTitle(): void;
_enter(): void;
_leave(): void;
_setTimeout(handler: any, timeout: any): void;
_isWithActiveTrigger(): boolean;
_getDelegateConfig(): {
selector: boolean;
trigger: string;
};
_disposePopper(): void;
}
/**
* Class definition
*/
declare class BaseComponent extends Config {
static getInstance(element: any): any;
static getOrCreateInstance(element: any, config?: {}): any;
static get VERSION(): string;
static get DATA_KEY(): string;
static get EVENT_KEY(): string;
static eventName(name: any): string;
constructor(element: any, config: any);
_element: any;
_config: any;
dispose(): void;
_queueCallback(callback: any, element: any, isAnimated?: boolean): void;
}
/**
* Class definition
*/
declare class Swipe extends Config {
static get Default(): {
endCallback: any;
leftCallback: any;
rightCallback: any;
};
static get DefaultType(): {
endCallback: string;
leftCallback: string;
rightCallback: string;
};
static get NAME(): string;
static isSupported(): boolean;
constructor(element: any, config: any);
_element: any;
_config: any;
_deltaX: number;
_supportPointerEvents: boolean;
dispose(): void;
_start(event: any): void;
_end(event: any): void;
_move(event: any): void;
_handleSwipe(): void;
_initEvents(): void;
_eventIsPointerPenTouch(event: any): boolean;
}
import * as Popper from "@popperjs/core";
/**
* Class definition
*/
declare class Backdrop extends Config {
static get Default(): {
className: string;
clickCallback: any;
isAnimated: boolean;
isVisible: boolean;
rootElement: string;
};
static get DefaultType(): {
className: string;
clickCallback: string;
isAnimated: string;
isVisible: string;
rootElement: string;
};
static get NAME(): string;
constructor(config: any);
_config: any;
_isAppended: boolean;
_element: HTMLDivElement;
show(callback: any): void;
hide(callback: any): void;
dispose(): void;
_getElement(): HTMLDivElement;
_append(): void;
_emulateAnimation(callback: any): void;
}
/**
* Class definition
*/
declare class FocusTrap extends Config {
static get Default(): {
autofocus: boolean;
trapElement: any;
};
static get DefaultType(): {
autofocus: string;
trapElement: string;
};
static get NAME(): string;
constructor(config: any);
_config: any;
_isActive: boolean;
_lastTabNavDirection: string;
activate(): void;
deactivate(): void;
_handleFocusin(event: any): void;
_handleKeydown(event: any): void;
}
/**
* Class definition
*/
declare class ScrollBarHelper {
_element: HTMLElement;
getWidth(): number;
hide(): void;
reset(): void;
isOverflowing(): boolean;
_disableOverFlow(): void;
_setElementAttributes(selector: any, styleProperty: any, callback: any): void;
_saveInitialAttribute(element: any, styleProperty: any): void;
_resetElementAttributes(selector: any, styleProperty: any): void;
_applyManipulationCallback(selector: any, callBack: any): void;
}
/**
* Class definition
*/
declare class TemplateFactory extends Config {
static get Default(): {
allowList: {
'*': (string | RegExp)[];
a: string[];
area: any[];
b: any[];
br: any[];
col: any[];
code: any[];
div: any[];
em: any[];
hr: any[];
h1: any[];
h2: any[];
h3: any[];
h4: any[];
h5: any[];
h6: any[];
i: any[];
img: string[];
li: any[];
ol: any[];
p: any[];
pre: any[];
s: any[];
small: any[];
span: any[];
sub: any[];
sup: any[];
strong: any[];
u: any[];
ul: any[];
};
content: {};
extraClass: string;
html: boolean;
sanitize: boolean;
sanitizeFn: any;
template: string;
};
static get DefaultType(): {
allowList: string;
content: string;
extraClass: string;
html: string;
sanitize: string;
sanitizeFn: string;
template: string;
};
static get NAME(): string;
constructor(config: any);
_config: any;
getContent(): any[];
hasContent(): boolean;
changeContent(content: any): TemplateFactory;
toHtml(): Element;
_typeCheckConfig(config: any): void;
_checkContent(arg: any): void;
_setContent(template: any, content: any, selector: any): void;
_maybeSanitize(arg: any): any;
_resolvePossibleFunction(arg: any): any;
_putElementInTemplate(element: any, templateElement: any): void;
}
/**
* --------------------------------------------------------------------------
* Bootstrap util/config.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Class definition
*/
declare class Config {
static get Default(): {};
static get DefaultType(): {};
static get NAME(): void;
_getConfig(config: any): any;
_configAfterMerge(config: any): any;
_mergeConfigObj(config: any, element: any): any;
_typeCheckConfig(config: any, configTypes?: any): void;
}
export {};

View File

@@ -1,93 +0,0 @@
/// <reference lib="webworker" />
export namespace languages {
export namespace markup {
namespace tag { }
}
import html = markup;
export { html };
import mathml = markup;
export { mathml };
import svg = markup;
export { svg };
export const xml: {
[x: string]: RegExp | GrammarToken | (RegExp | GrammarToken)[];
};
import ssml = xml;
export { ssml };
import atom = xml;
export { atom };
import rss = xml;
export { rss };
export const clike: {
comment: {
pattern: RegExp;
lookbehind: boolean;
greedy: boolean;
}[];
string: {
pattern: RegExp;
greedy: boolean;
};
'class-name': {
pattern: RegExp;
lookbehind: boolean;
inside: {
punctuation: RegExp;
};
};
keyword: RegExp;
boolean: RegExp;
function: RegExp;
number: RegExp;
operator: RegExp;
punctuation: RegExp;
};
export const javascript: {
[x: string]: RegExp | GrammarToken | (RegExp | GrammarToken)[];
};
import js = javascript;
export { js };
}
/**
* The expansion of a simple `RegExp` literal to support additional properties.
*/
export type GrammarToken = {
/**
* The regular expression of the token.
*/
pattern: RegExp;
/**
* If `true`, then the first capturing group of `pattern` will (effectively)
* behave as a lookbehind group meaning that the captured text will not be part of the matched text of the new token.
*/
lookbehind?: boolean;
/**
* Whether the token is greedy.
*/
greedy?: boolean;
/**
* An optional alias or list of aliases.
*/
alias?: string | string[];
/**
* The nested grammar of this token.
*
* The `inside` grammar will be used to tokenize the text value of each token of this kind.
*
* This can be used to make nested and even recursive language definitions.
*
* Note: This can cause infinite recursion. Be careful when you embed different languages or even the same language into
* each another.
*/
inside?: Grammar;
};
export type Grammar = {
[x: string]: RegExp | GrammarToken | Array<RegExp | GrammarToken>;
};
/**
* A function which will invoked after an element was successfully highlighted.
*/
export type HighlightCallback = (element: Element) => void;
export type HookCallback = (env: {
[x: string]: any;
}) => void;

View File

@@ -64,6 +64,20 @@ declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
@@ -196,6 +210,13 @@ declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare let sd: {};
declare let rf: boolean;
declare function refreshStatus(): void;

View File

@@ -0,0 +1,8 @@
export class BuildEventsHook {
constructor(name: any, fn: any, stage?: string);
name: any;
stage: string;
function: any;
apply(compiler: any): void;
}
export function createBuildEventsHook(options: any): BuildEventsHook;

View File

@@ -1,4 +1,4 @@
declare const PORT: 9100;
declare const PORT: 5000;
import HtmlWebPackPlugin = require("html-webpack-plugin");
export namespace entry {
const test: string;

View File

@@ -22,7 +22,6 @@
"babel": "^6.23.0",
"babel-loader": "^8.2.3",
"babel-runtime": "^6.26.0",
"bootswatch": "file:src/bootswatch",
"clean-webpack-plugin": "^4.0.0",
"commander": "^8.3.0",
"compression-webpack-plugin": "^9.2.0",
@@ -75,8 +74,10 @@
"@babel/runtime": "^7.16.7",
"async-mutex": "^0.3.2",
"bootstrap": "^5.1.3",
"bootswatch": "^5.3.2",
"jquery": "^3.6.0",
"npm": "^10.1.0",
"optipng-bin": "^9.0.0",
"popper.js": "^1.16.1",
"webpack-visualizer-plugin": "^0.1.11",
"webpack-visualizer-plugin2": "^1.0.0"

View File

@@ -99,7 +99,7 @@
<div class="tab-pane fade" id="tab-cfg-gen"></div>
<div class="tab-pane fade" id="tab-cfg-fw">
<div class="card text-white mb-3">
<div class="card mb-3">
<div class="card-header">Software Updates</div>
<div class="card-body">
<table class="table table-hover table-striped table-dark">
@@ -171,7 +171,7 @@
</div>
</div>
</div>
<div class="card text-white mb-3">
<div class="card mb-3">
<div class="card-header">Local Firmware Upload</div>
<div class="card-body">
<div id="uploaddiv" class="form-group row ">
@@ -216,7 +216,7 @@
</div>
<div class="tab-pane fade" id="tab-cfg-audio">
<div class="card text-white mb-3">
<div class="card mb-3">
<div class="card-header">Usage Templates</div>
<div class="card-body">
<fieldset class="form-group" id="output-tmpl">
@@ -355,7 +355,7 @@
</div>
</div>
<div class="tab-pane fade active show" id="tab-wifi">
<div class="card text-white mb-3">
<div class="card mb-3">
<div class="card-header">WiFi Status</div>
<div class="card-body if_eth" style="display: none">
@@ -527,7 +527,7 @@
</div>
<!-- syslog -->
<div class="tab-pane fade " id="tab-credits">
<div class="card text-white mb-3">
<div class="card mb-3">
<div class="card-header">Credits</div>
<div class="card-body">
<p><strong><a
@@ -554,7 +554,7 @@
</ul>
</div>
</div>
<div class="card text-white mb-3">
<div class="card mb-3">
<div class="card-header">Extras/Overrides</div>
<div class="card-body">
<fieldset>

View File

@@ -2097,7 +2097,7 @@ function getCommands() {
const isConfig = cmdParts[0] === 'cfg';
const targetDiv = '#tab-' + cmdParts[0] + '-' + cmdParts[1];
let innerhtml = '';
innerhtml += `<div class="card text-white mb-3"><div class="card-header">${command.help.encodeHTML().replace(/\n/g, '<br />')}</div><div class="card-body"><fieldset id="flds-${command.name}">`;
innerhtml += `<div class="card mb-3"><div class="card-header">${command.help.encodeHTML().replace(/\n/g, '<br />')}</div><div class="card-body"><fieldset id="flds-${command.name}">`;
if (command.argtable) {
command.argtable.forEach(function (arg) {
let placeholder = arg.datatype || '';

View File

@@ -1,5 +1,5 @@
@import "~bootswatch/dist/darkly/variables";
// @import "~bootswatch/dist/darkly/variables"; -- remove darkly until bootswatch color is resolved
@import "utils/variables";
@import "~bootstrap/scss/bootstrap";
// @import "~bootstrap/scss/functions";
@@ -38,5 +38,5 @@
// // Utilities
// @import "~bootstrap/scss/utilities/api";
@import "~bootswatch/dist/darkly/bootswatch";
// @import "~bootswatch/dist/darkly/bootswatch"; -- remove darkly until bootswatch color is resolved
@import "utils/style";

View File

@@ -1,5 +1,5 @@
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.1ab179394339385e0a02.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.4bbe29a78a667faa2b6f.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/favicon-32x32.png BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/index.html.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.b02584.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.b02584.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.0ba488.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.0ba488.bundle.js.gz BINARY)

View File

@@ -1,34 +1,34 @@
// Automatically generated. Do not edit manually!.
#include <inttypes.h>
extern const uint8_t _index_1ab179394339385e0a02_css_gz_start[] asm("_binary_index_1ab179394339385e0a02_css_gz_start");
extern const uint8_t _index_1ab179394339385e0a02_css_gz_end[] asm("_binary_index_1ab179394339385e0a02_css_gz_end");
extern const uint8_t _index_4bbe29a78a667faa2b6f_css_gz_start[] asm("_binary_index_4bbe29a78a667faa2b6f_css_gz_start");
extern const uint8_t _index_4bbe29a78a667faa2b6f_css_gz_end[] asm("_binary_index_4bbe29a78a667faa2b6f_css_gz_end");
extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_start");
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
extern const uint8_t _index_b02584_bundle_js_gz_start[] asm("_binary_index_b02584_bundle_js_gz_start");
extern const uint8_t _index_b02584_bundle_js_gz_end[] asm("_binary_index_b02584_bundle_js_gz_end");
extern const uint8_t _node_vendors_b02584_bundle_js_gz_start[] asm("_binary_node_vendors_b02584_bundle_js_gz_start");
extern const uint8_t _node_vendors_b02584_bundle_js_gz_end[] asm("_binary_node_vendors_b02584_bundle_js_gz_end");
extern const uint8_t _index_0ba488_bundle_js_gz_start[] asm("_binary_index_0ba488_bundle_js_gz_start");
extern const uint8_t _index_0ba488_bundle_js_gz_end[] asm("_binary_index_0ba488_bundle_js_gz_end");
extern const uint8_t _node_vendors_0ba488_bundle_js_gz_start[] asm("_binary_node_vendors_0ba488_bundle_js_gz_start");
extern const uint8_t _node_vendors_0ba488_bundle_js_gz_end[] asm("_binary_node_vendors_0ba488_bundle_js_gz_end");
const char * resource_lookups[] = {
"/css/index.1ab179394339385e0a02.css.gz",
"/css/index.4bbe29a78a667faa2b6f.css.gz",
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.b02584.bundle.js.gz",
"/js/node_vendors.b02584.bundle.js.gz",
"/js/index.0ba488.bundle.js.gz",
"/js/node_vendors.0ba488.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_index_1ab179394339385e0a02_css_gz_start,
_index_4bbe29a78a667faa2b6f_css_gz_start,
_favicon_32x32_png_start,
_index_html_gz_start,
_index_b02584_bundle_js_gz_start,
_node_vendors_b02584_bundle_js_gz_start
_index_0ba488_bundle_js_gz_start,
_node_vendors_0ba488_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_index_1ab179394339385e0a02_css_gz_end,
_index_4bbe29a78a667faa2b6f_css_gz_end,
_favicon_32x32_png_end,
_index_html_gz_end,
_index_b02584_bundle_js_gz_end,
_node_vendors_b02584_bundle_js_gz_end
_index_0ba488_bundle_js_gz_end,
_node_vendors_0ba488_bundle_js_gz_end
};

View File

@@ -1,6 +1,6 @@
/***********************************
webpack_headers
dist/css/index.1ab179394339385e0a02.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.b02584.bundle.js.gz,dist/js/node_vendors.b02584.bundle.js.gz
dist/css/index.4bbe29a78a667faa2b6f.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.0ba488.bundle.js.gz,dist/js/node_vendors.0ba488.bundle.js.gz
***********************************/
#pragma once
#include <inttypes.h>

View File

@@ -0,0 +1,117 @@
const path = require("path");
const fs = require('fs');
const zlib = require("zlib");
const glob = require('glob');
class BuildEventsHook {
constructor(name, fn, stage = 'afterEmit') {
this.name = name;
this.stage = stage;
this.function = fn;
}
apply(compiler) {
compiler.hooks[this.stage].tap(this.name, this.function);
}
}
function createBuildEventsHook(options){
return new BuildEventsHook('Update C App',
function (stats, arguments) {
if (options.mode !== "production") return;
let buildRootPath = path.join(process.cwd(), '..', '..', '..');
let wifiManagerPath = glob.sync(path.join(buildRootPath, 'components/**/wifi-manager*'))[0];
let buildCRootPath = glob.sync(buildRootPath)[0];
fs.appendFileSync('./dist/index.html.gz',
zlib.gzipSync(fs.readFileSync('./dist/index.html'),
{
chunckSize: 65536,
level: zlib.constants.Z_BEST_COMPRESSION
}));
let getDirectories = function getDirectories (src, callback) {
let searchPath = path.posix.join(src, '/**/*(*.gz|favicon-32x32.png)');
console.log(`Post build: Getting file list from ${searchPath}`);
glob(searchPath, callback);
};
let cleanUpPath = path.posix.join(buildCRootPath, '/build/*.S');
console.log(`Post build: Cleaning up previous builds in ${cleanUpPath}`);
glob(cleanUpPath, function (err, list) {
if (err) {
console.error('Error', err);
} else {
list.forEach(fileName => {
try {
console.log(`Post build: Purging old binary file ${fileName} from C project.`);
fs.unlinkSync(fileName)
//file removed
} catch (ferr) {
console.error(ferr)
}
});
}
},
'afterEmit'
);
console.log('Generating C include files from webpack build output');
getDirectories('./dist', function (err, list) {
console.log(`Post build: found ${list.length} files. Relative path: ${wifiManagerPath}.`);
if (err) {
console.log('Error', err);
} else {
let exportDefHead =
`/***********************************
webpack_headers
${arguments[1]}
***********************************/
#pragma once
#include <inttypes.h>
extern const char * resource_lookups[];
extern const uint8_t * resource_map_start[];
extern const uint8_t * resource_map_end[];`;
let exportDef = '// Automatically generated. Do not edit manually!.\n' +
'#include <inttypes.h>\n';
let lookupDef = 'const char * resource_lookups[] = {\n';
let lookupMapStart = 'const uint8_t * resource_map_start[] = {\n';
let lookupMapEnd = 'const uint8_t * resource_map_end[] = {\n';
let cMake = '';
list.forEach(foundFile => {
let exportName = path.basename(foundFile).replace(/[\. \-]/gm, '_');
//take the full path of the file and make it relative to the build directory
let cmakeFileName = path.posix.relative(wifiManagerPath, glob.sync(path.resolve(foundFile))[0]);
let httpRelativePath = path.posix.join('/', path.posix.relative('dist', foundFile));
exportDef += `extern const uint8_t _${exportName}_start[] asm("_binary_${exportName}_start");\nextern const uint8_t _${exportName}_end[] asm("_binary_${exportName}_end");\n`;
lookupDef += `\t"${httpRelativePath}",\n`;
lookupMapStart += '\t_' + exportName + '_start,\n';
lookupMapEnd += '\t_' + exportName + '_end,\n';
cMake += `target_add_binary_data( __idf_wifi-manager ${cmakeFileName} BINARY)\n`;
console.log(`Post build: adding cmake file reference to ${cmakeFileName} from C project, with web path ${httpRelativePath}.`);
});
lookupDef += '""\n};\n';
lookupMapStart = lookupMapStart.substring(0, lookupMapStart.length - 2) + '\n};\n';
lookupMapEnd = lookupMapEnd.substring(0, lookupMapEnd.length - 2) + '\n};\n';
try {
fs.writeFileSync('webapp.cmake', cMake);
fs.writeFileSync('webpack.c', exportDef + lookupDef + lookupMapStart + lookupMapEnd);
fs.writeFileSync('webpack.h', exportDefHead);
//file written successfully
} catch (e) {
console.error(e);
}
}
});
console.log('Post build completed.');
})
}
module.exports = {
BuildEventsHook,
createBuildEventsHook
}

View File

@@ -6,7 +6,7 @@ const HtmlWebPackPlugin = require('html-webpack-plugin');
const { Command } = require('commander');
let cmdLines= { };
var { parseArgsStringToArgv } = require('string-argv');
const PORT = 9100;
const PORT = 5000;
const data = {
messages: require("../mock/messages.json"),
@@ -159,7 +159,7 @@ module.exports ={
open: true,
compress: true,
port: PORT,
host: '127.0.0.1',//your ip address
host: '0.0.0.0',
allowedHosts: "all",
headers: {'Access-Control-Allow-Origin': '*', 'Accept-Encoding': 'identity'},
client: {

View File

@@ -0,0 +1,91 @@
param (
[Parameter(Position=0, Mandatory=$false)]
[ValidateSet("t", "u", "d")]
[string]$option
)
# Get the current directory
$currentDir = Get-Location
# Define target directories
$targetDir = "components\wifi-manager\webapp"
$distDir = "$targetDir\dist"
# Get list of files from the 'dist' directory
$fsFiles = Get-ChildItem -Recurse $distDir -File | ForEach-Object {
$_.FullName.Substring($currentDir.Path.Length + 1).Replace("\", "/")
}
# Define additional files to include
$additionalFiles = @("webpack.c", "webpack.h", "webapp.cmake")
# Check if additional files exist in $targetDir and format them
$additionalFilesFormatted = @()
Get-ChildItem $targetDir -File | ForEach-Object {
if ($additionalFiles -contains $_.Name) {
$formatted = $_.FullName.Substring($currentDir.Path.Length + 1).Replace("\", "/")
$additionalFilesFormatted += $formatted
Write-Host "Found $formatted"
}
}
# Get list of files from the Git index
$indexFiles = git ls-files -s $distDir | ForEach-Object {
($_ -split "\s+")[3]
}
# Combine and remove duplicates
$allFiles = $fsFiles + $additionalFilesFormatted + $indexFiles | Sort-Object -Unique
# ... (previous code remains unchanged)
# Apply the git command based on the option
$allFiles | ForEach-Object {
$relativePath = $_
$isInIndex = $indexFiles -contains $relativePath
if ($null -eq $option) {
$gitStatus = & git status --porcelain -- $relativePath
if ($gitStatus) {
$status = ($gitStatus -split "\s")[0]
Write-Host "$relativePath has Git status: $status"
} else {
Write-Host "$relativePath is not tracked"
}
}
elseif ($isInIndex) {
if ($option -eq "d") {
$resetResult = & git reset -- $relativePath 2>&1
if ($resetResult -match 'error:') {
Write-Host "Error resetting ${relativePath}: $resetResult"
continue
}
$checkoutResult = & git checkout -- $relativePath 2>&1
if ($checkoutResult -match 'error:') {
Write-Host "Error checking out ${relativePath}: $checkoutResult"
continue
}
Write-Host "Discarded changes in $relativePath"
}
# ... (rest of the code remains unchanged)
}
# else {
# # if ($option -eq "d") {
# # Remove-Item -Path $relativePath -Force
# # Write-Host "Removed untracked file $relativePath"
# # } else {
# # Write-Host "File $relativePath is not tracked."
# # }
# }
else {
if ($option -eq "t") {
git add $relativePath
git update-index --no-skip-worktree $relativePath
Write-Host "Started tracking changes in $relativePath"
} else {
Write-Host "File $relativePath is not tracked."
}
}
}

Some files were not shown because too many files have changed in this diff Show More