Compare commits

...

35 Commits

Author SHA1 Message Date
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
99 changed files with 2787 additions and 5427 deletions

View File

@@ -1,3 +1,24 @@
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
- catchup with official cspot

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,7 +187,7 @@ 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"} ],
@@ -197,6 +197,8 @@ Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, head
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 '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
**Please note that you can not use the same GPIO or port as the I2C.**
@@ -275,7 +277,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 +321,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

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

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

View File

@@ -56,7 +56,7 @@ cJSON * config_alloc_get_cjson(const char *key);
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value);
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

@@ -43,11 +43,15 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
extern void register_audio_config(void);
extern void register_rotary_config(void);
extern void register_ledvu_config(void);
extern void register_nvs();
void register_optional_cmd(void) {
#if CONFIG_WITH_CONFIG_UI
register_rotary_config();
register_ledvu_config();
#endif
register_audio_config();
register_ledvu_config();
register_nvs();
}
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

@@ -64,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();
@@ -73,24 +75,42 @@ static void register_set_services();
static void register_tasks();
#endif
extern BaseType_t network_manager_task;
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size){
FILE *f = open_memstream(buf, buf_size);
if (f == NULL) {
cmd_send_messaging(cmdname,MESSAGING_ERROR,"Unable to open memory stream.");
}
return f;
}
void register_system()
{
register_free();
register_setdevicename();
register_set_services();
register_free();
register_heap();
register_dump_heap();
register_setdevicename();
register_version();
register_restart();
register_deep_sleep();
register_light_sleep();
register_factory_boot();
register_restart_ota();
#if WITH_TASKS_INFO
register_tasks();
#endif
#if CONFIG_WITH_CONFIG_UI
register_deep_sleep();
register_light_sleep();
#endif
}
void simple_restart()
{
log_send_messaging(MESSAGING_WARNING,"Rebooting.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
/* 'version' command */
static int get_version(int argc, char **argv)
{
@@ -128,36 +148,23 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
{
if(is_recovery_running){
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
// log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
simple_restart();
}
}
else {
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
// log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
simple_restart();
}
}
esp_err_t err = ESP_OK;
bool bFound=false;
log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
// log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
const esp_partition_t *partition;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
if(it == NULL){
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Cannot iterate through partitions");
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Partitions error");
}
else
{
@@ -166,15 +173,11 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
ESP_LOGD(TAG, "Releasing partition iterator");
esp_partition_iterator_release(it);
if(partition != NULL){
log_send_messaging(MESSAGING_INFO, "Found application partition %s sub type %u", partition->label,partition_subtype);
log_send_messaging(MESSAGING_INFO, "Rebooting to %s", partition->label);
err=esp_ota_set_boot_partition(partition);
if(err!=ESP_OK){
bFound=false;
log_send_messaging(MESSAGING_ERROR,"Unable to select partition for reboot: %s",esp_err_to_name(err));
}
else{
bFound=true;
}
}
else
{
@@ -183,13 +186,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
}
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
if(bFound) {
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration changes. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
simple_restart();
}
return ESP_OK;
@@ -197,46 +194,31 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
static int restart(int argc, char **argv)
{
log_send_messaging(MESSAGING_WARNING, "\n\nPerforming a simple restart to the currently active partition.");
if(!wait_for_commit()){
cmd_send_messaging(argv[0],MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
simple_restart();
return 0;
}
void simple_restart()
{
log_send_messaging(MESSAGING_WARNING,"System reboot requested.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
esp_err_t guided_restart_ota(){
log_send_messaging(MESSAGING_WARNING,"System reboot to Application requested");
log_send_messaging(MESSAGING_WARNING,"Booting to Squeezelite");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
esp_err_t guided_factory(){
log_send_messaging(MESSAGING_WARNING,"System reboot to recovery requested");
log_send_messaging(MESSAGING_WARNING,"Booting to recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
static int restart_factory(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into recovery");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return 0; // return fail. This should never return... we're rebooting!
}
static int restart_ota(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into ota app 0");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Squeezelite");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return 0; // return fail. This should never return... we're rebooting!
}
@@ -248,7 +230,9 @@ static void register_restart()
.hint = NULL,
.func = &restart,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_restart_ota()
@@ -259,7 +243,9 @@ static void register_restart_ota()
.hint = NULL,
.func = &restart_ota,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
@@ -271,7 +257,9 @@ static void register_factory_boot()
.hint = NULL,
.func = &restart_factory,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'free' command prints available heap memory */
@@ -287,11 +275,14 @@ static void register_free()
{
const esp_console_cmd_t cmd = {
.command = "free",
.help = "Get the current size of free heap memory",
.help = "Get free heap memory",
.hint = NULL,
.func = &free_mem,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static int dump_heap(int argc, char **argv)
@@ -303,16 +294,16 @@ static int dump_heap(int argc, char **argv)
/* 'heap' command prints minumum heap size */
static int heap_size(int argc, char **argv)
{
ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
heap_caps_get_free_size(MALLOC_CAP_DMA),
heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
// ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
// heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
// heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
// heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
// heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
// heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
// heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
// heap_caps_get_free_size(MALLOC_CAP_DMA),
// heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
// heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
cmd_send_messaging(argv[0],MESSAGING_INFO,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
@@ -457,9 +448,8 @@ static int setdevicename(int argc, char **argv)
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
nerrors+=setnamevar("a2dp_dev_name", f, name);
@@ -488,11 +478,13 @@ static void register_heap()
{
const esp_console_cmd_t heap_cmd = {
.command = "heap",
.help = "Get minimum size of free heap memory found during execution",
.help = "Get minimum size of free heap memory",
.hint = NULL,
.func = &heap_size,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&heap_cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
}
@@ -521,6 +513,7 @@ static void register_setdevicename()
.func = &setdevicename,
.argtable = &name_args
};
cmd_to_json_with_cb(&set_name,&setdevicename_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&set_name));
}
@@ -561,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;
@@ -618,15 +611,15 @@ static void register_deep_sleep()
const esp_console_cmd_t cmd = {
.command = "deep_sleep",
.help = "Enter deep sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"If no wakeup option is specified, will sleep indefinitely.",
.help = "Enter deep sleep mode. ",
.hint = NULL,
.func = &deep_sleep,
.argtable = &deep_sleep_args
};
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;
@@ -649,9 +642,8 @@ static int do_set_services(int argc, char **argv)
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
@@ -674,7 +666,7 @@ static int do_set_services(int argc, char **argv)
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting telnet service to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
fprintf(f,"Error setting telnet to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Telnet service changed to %s\n",set_services_args.telnet->sval[0]);
@@ -706,7 +698,6 @@ cJSON * set_services_cb(){
#if WITH_TASKS_INFO
console_set_bool_parameter(values,"stats",set_services_args.stats);
#endif
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
if(strcasestr("YX",p)!=NULL){
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Telnet Only");
@@ -717,7 +708,6 @@ cJSON * set_services_cb(){
else {
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Disabled");
}
FREE_AND_NULL(p);
}
@@ -745,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;
@@ -836,4 +828,4 @@ static void register_light_sleep()
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#endif

View File

@@ -17,6 +17,7 @@ void register_system();
esp_err_t guided_factory();
esp_err_t guided_restart_ota();
void simple_restart();
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size);
#ifdef __cplusplus
}

View File

@@ -204,8 +204,10 @@ void register_wifi_join()
void register_wifi()
{
#ifdef WIFI_CMDLINE
register_wifi_join();
if(bypass_network_manager){
initialise_wifi();
}
#endif
}

View File

@@ -329,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){
@@ -761,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;

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;
@@ -89,6 +89,7 @@ typedef struct {
char type[16];
int length;
int gpio;
int scale;
} ledvu_struct_t;
typedef struct {

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

@@ -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) {

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,18 @@ 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;
@@ -196,6 +208,12 @@ 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">
@@ -331,8 +331,7 @@
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="disable-squeezelite"
value="" checked="">
<input class="form-check-input" type="checkbox" id="disable-squeezelite" value="" >
Disable Squeezelite
</label>
</div>
@@ -356,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">
@@ -528,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
@@ -555,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

@@ -923,10 +923,10 @@ window.saveAutoexec1 = function (apply) {
};
data.config = {
autoexec1: { value: commandLine, type: 33 },
autoexec: {
value: $('#disable-squeezelite').prop('checked') ? '0' : '1',
type: 33,
},
// autoexec: {
// value: $('#disable-squeezelite').prop('checked') ? '0' : '1',
// type: 33,
// },
};
$.ajax({
@@ -1216,6 +1216,28 @@ $(document).ready(function () {
}
});
$('#disable-squeezelite').on('click', function () {
// this.checked = this.checked ? 1 : 0;
// $('#disable-squeezelite').prop('checked')
if (this.checked) {
// Store the current value before overwriting it
const currentValue = $('#cmd_opt_s').val();
$('#cmd_opt_s').data('originalValue', currentValue);
// Overwrite the value with '-disable'
$('#cmd_opt_s').val('-disable');
} else {
// Retrieve the original value
const originalValue = $('#cmd_opt_s').data('originalValue');
// Restore the original value if it exists, otherwise set it to an empty string
$('#cmd_opt_s').val(originalValue ? originalValue : '');
}
});
$('input#show-nvs').on('click', function () {
this.checked = this.checked ? 1 : 0;
Cookies.set("show-nvs", this.checked ? 'Y' : 'N');
@@ -2075,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 || '';
@@ -2199,13 +2221,7 @@ function getConfig() {
.sort()
.forEach(function (key) {
let val = data[key].value;
if (key === 'autoexec') {
if (data.autoexec.value === '0') {
$('#disable-squeezelite')[0].checked = true;
} else {
$('#disable-squeezelite')[0].checked = false;
}
} else if (key === 'autoexec1') {
if (key === 'autoexec1') {
/* call new function to parse the squeezelite options */
processSqueezeliteCommandLine(val);
} else if (key === 'host_name') {
@@ -2294,6 +2310,7 @@ function processSqueezeliteCommandLine(val) {
commandBTSinkName= parsed.otherOptions.btname;
}
handleTemplateTypeRadio('bt');
}
Object.keys(parsed.options).forEach(function (key) {
const option = parsed.options[key];
@@ -2312,6 +2329,17 @@ function processSqueezeliteCommandLine(val) {
$('#resample_i').prop('checked', true);
}
}
if (parsed.options.hasOwnProperty('s')) {
// parse -u v[:i] and check the appropriate radio button with id #resample_v
if(parsed.options.s === '-disable'){
$('#disable-squeezelite')[0].checked = true;
}
else {
$('#disable-squeezelite')[0].checked = false;
}
}
}

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.5fd84f.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.5fd84f.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_5fd84f_bundle_js_gz_start[] asm("_binary_index_5fd84f_bundle_js_gz_start");
extern const uint8_t _index_5fd84f_bundle_js_gz_end[] asm("_binary_index_5fd84f_bundle_js_gz_end");
extern const uint8_t _node_vendors_5fd84f_bundle_js_gz_start[] asm("_binary_node_vendors_5fd84f_bundle_js_gz_start");
extern const uint8_t _node_vendors_5fd84f_bundle_js_gz_end[] asm("_binary_node_vendors_5fd84f_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.5fd84f.bundle.js.gz",
"/js/node_vendors.5fd84f.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_5fd84f_bundle_js_gz_start,
_node_vendors_5fd84f_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_5fd84f_bundle_js_gz_end,
_node_vendors_5fd84f_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.5fd84f.bundle.js.gz,dist/js/node_vendors.5fd84f.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."
}
}
}

View File

@@ -68,7 +68,68 @@ bool cold_boot=true;
extern const char _ctype_[];
const char* __ctype_ptr__ = _ctype_;
#endif
typedef struct {
const char *key;
const char *value;
} DefaultStringVal;
typedef struct {
const char *key;
unsigned int uint_value;
bool is_signed;
} DefaultNumVal;
const DefaultNumVal defaultNumVals[] = {
{"ota_erase_blk", OTA_FLASH_ERASE_BLOCK, 0},
{"ota_stack", OTA_STACK_SIZE, 0},
{"ota_prio", OTA_TASK_PRIOTITY, 1}
};
const DefaultStringVal defaultStringVals[] = {
{"equalizer", ""},
{"loudness", "0"},
{"actrls_config", ""},
{"lms_ctrls_raw", "n"},
{"rotary_config", CONFIG_ROTARY_ENCODER},
{"display_config", CONFIG_DISPLAY_CONFIG},
{"eth_config", CONFIG_ETH_CONFIG},
{"i2c_config", CONFIG_I2C_CONFIG},
{"spi_config", CONFIG_SPI_CONFIG},
{"set_GPIO", CONFIG_SET_GPIO},
{"sleep_config", ""},
{"led_brightness", ""},
{"spdif_config", ""},
{"dac_config", ""},
{"dac_controlset", ""},
{"jack_mutes_amp", "n"},
{"gpio_exp_config", CONFIG_GPIO_EXP_CONFIG},
{"bat_config", ""},
{"metadata_config", ""},
{"telnet_enable", ""},
{"telnet_buffer", "40000"},
{"telnet_block", "500"},
{"stats", "n"},
{"rel_api", CONFIG_RELEASE_API},
{"pollmx", "600"},
{"pollmin", "15"},
{"ethtmout", "8"},
{"dhcp_tmout", "8"},
{"target", CONFIG_TARGET},
{"led_vu_config", ""},
#ifdef CONFIG_BT_SINK
{"bt_sink_pin", STR(CONFIG_BT_SINK_PIN)},
{"bt_sink_volume", "127"},
// Note: register_default_with_mac("bt_name", CONFIG_BT_NAME); is a special case
{"enable_bt_sink", STR(CONFIG_BT_SINK)},
{"a2dp_dev_name", CONFIG_A2DP_DEV_NAME},
{"a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS)},
{"a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS)},
{"a2dp_sink_name", CONFIG_A2DP_SINK_NAME},
{"autoexec", "1"},
#ifdef CONFIG_AIRPLAY_SINK
{"airplay_port", CONFIG_AIRPLAY_PORT},
{"enable_airplay", STR(CONFIG_AIRPLAY_SINK)}
#endif
#endif
};
static bool bNetworkConnected=false;
// as an exception _init function don't need include
@@ -80,7 +141,9 @@ extern void target_init(char *target);
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
bool is_recovery_running;
bool is_network_connected(){
return bNetworkConnected;
}
void cb_connection_got_ip(nm_state_t new_state, int sub_state){
const char *hostname;
static ip4_addr_t ip;
@@ -163,7 +226,7 @@ void set_log_level(char * tag, char * level){
}
#define DEFAULT_NAME_WITH_MAC(var,defval) char var[strlen(defval)+sizeof(macStr)]; strcpy(var,defval); strcat(var,macStr)
void register_default_string_val(const char * key, char * value){
void register_default_string_val(const char * key, const char * value){
char * existing =(char *)config_alloc_get(NVS_TYPE_STR,key );
ESP_LOGD(TAG,"Register default called with: %s= %s",key,value );
if(!existing) {
@@ -175,7 +238,15 @@ void register_default_string_val(const char * key, char * value){
}
FREE_AND_NULL(existing);
}
void register_single_default_num_val(const DefaultNumVal *entry) {
char number_buffer[101] = {};
if (entry->is_signed) {
snprintf(number_buffer, sizeof(number_buffer) - 1, "%d", entry->uint_value);
} else {
snprintf(number_buffer, sizeof(number_buffer) - 1, "%u", entry->uint_value);
}
register_default_string_val(entry->key, number_buffer);
}
char * alloc_get_string_with_mac(const char * val) {
uint8_t mac[6];
char macStr[LOCAL_MAC_SIZE + 1];
@@ -220,77 +291,25 @@ void register_default_nvs(){
else {
register_default_string_val("cspot_config", "");
}
}
#endif
#ifdef CONFIG_AIRPLAY_SINK
register_default_with_mac("airplay_name", CONFIG_AIRPLAY_NAME);
register_default_string_val("airplay_port", CONFIG_AIRPLAY_PORT);
register_default_string_val( "enable_airplay", STR(CONFIG_AIRPLAY_SINK));
#endif
#ifdef CONFIG_BT_SINK
register_default_string_val( "bt_sink_pin", STR(CONFIG_BT_SINK_PIN));
register_default_string_val( "bt_sink_volume", "127");
register_default_with_mac("bt_name", CONFIG_BT_NAME);
register_default_string_val( "enable_bt_sink", STR(CONFIG_BT_SINK));
register_default_string_val("a2dp_dev_name", CONFIG_A2DP_DEV_NAME);
register_default_string_val("a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS));
register_default_string_val("a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS));
register_default_string_val("a2dp_sink_name", CONFIG_A2DP_SINK_NAME);
#endif
register_default_with_mac("host_name", DEFAULT_HOST_NAME);
register_default_with_mac("ap_ssid", CONFIG_DEFAULT_AP_SSID);
register_default_string_val("autoexec","1");
register_default_with_mac("autoexec1",CONFIG_DEFAULT_COMMAND_LINE " -n " DEFAULT_HOST_NAME);
for (int i = 0; i < sizeof(defaultStringVals) / sizeof(DefaultStringVal); ++i) {
register_default_string_val(defaultStringVals[i].key, defaultStringVals[i].value);
}
for (int i = 0; i < sizeof(defaultNumVals) / sizeof(DefaultNumVal); ++i) {
register_single_default_num_val(&defaultNumVals[i]);
}
register_default_string_val("release_url", CONFIG_SQUEEZELITE_ESP32_RELEASE_URL);
register_default_string_val("ap_ip_address",CONFIG_DEFAULT_AP_IP);
register_default_string_val("ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY );
register_default_string_val("ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK);
register_default_string_val("ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL));
register_default_string_val("ap_pwd", CONFIG_DEFAULT_AP_PASSWORD);
register_default_string_val("bypass_wm", "0");
register_default_string_val("equalizer", "");
register_default_string_val("loudness", "0");
register_default_string_val("actrls_config", "");
register_default_string_val("lms_ctrls_raw", "n");
register_default_string_val("rotary_config", CONFIG_ROTARY_ENCODER);
char number_buffer[101] = {};
snprintf(number_buffer,sizeof(number_buffer)-1,"%u",OTA_FLASH_ERASE_BLOCK);
register_default_string_val( "ota_erase_blk", number_buffer);
snprintf(number_buffer,sizeof(number_buffer)-1,"%u",OTA_STACK_SIZE);
register_default_string_val( "ota_stack", number_buffer);
snprintf(number_buffer,sizeof(number_buffer)-1,"%d",OTA_TASK_PRIOTITY);
register_default_string_val( "ota_prio", number_buffer);
register_default_string_val( "display_config", CONFIG_DISPLAY_CONFIG);
register_default_string_val( "eth_config", CONFIG_ETH_CONFIG);
register_default_string_val( "i2c_config", CONFIG_I2C_CONFIG);
register_default_string_val( "spi_config", CONFIG_SPI_CONFIG);
register_default_string_val( "set_GPIO", CONFIG_SET_GPIO);
register_default_string_val( "sleep_config", "");
register_default_string_val( "led_brightness", "");
register_default_string_val( "spdif_config", "");
register_default_string_val( "dac_config", "");
register_default_string_val( "dac_controlset", "");
register_default_string_val( "jack_mutes_amp", "n");
register_default_string_val("gpio_exp_config", CONFIG_GPIO_EXP_CONFIG);
register_default_string_val( "bat_config", "");
register_default_string_val( "metadata_config", "");
register_default_string_val( "telnet_enable", "");
register_default_string_val( "telnet_buffer", "40000");
register_default_string_val( "telnet_block", "500");
register_default_string_val( "stats", "n");
register_default_string_val( "rel_api", CONFIG_RELEASE_API);
register_default_string_val("pollmx","600");
register_default_string_val("pollmin","15");
register_default_string_val("ethtmout","8");
register_default_string_val("dhcp_tmout","8");
register_default_string_val("target", CONFIG_TARGET);
register_default_string_val("led_vu_config", "");
wait_for_commit();
ESP_LOGD(TAG,"Done setting default values in nvs.");
}

Binary file not shown.

View File

@@ -64,6 +64,9 @@ sub initPlugin {
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
# Add menu item to extras
Slim::Buttons::Home::addSubMenu('PLUGINS', 'PLUGIN_SQUEEZEESP32', { 'useMode' => 'squeezeesp32_mode', });
}
sub onStopClear {

View File

@@ -174,6 +174,20 @@ sub setLEDVisu {
}
updateLED($client);
# display name
my $modes = ledVisualizerModes;
my $desc = $modes->[$visu]{'desc'};
my $name = '';
for (my $j = 0; $j < scalar @$desc; $j++) {
$name .= ' ' if ($j > 0);
$name .= $client->string(@{$desc}[$j]) || @{$desc}[$j];
}
$client->showBriefly( {
'line1' => $client->string('PLUGIN_SQUEEZEESP32_LED_VISUALIZER'),
'line2' => $name,
});
}
sub onNotification {
@@ -186,4 +200,62 @@ sub onNotification {
}
}
sub setMainMode {
my $client = shift;
my $method = shift;
if ($method eq 'pop') {
Slim::Buttons::Common::popMode($client);
$client->update();
return;
}
Slim::Buttons::Common::pushModeLeft($client, 'INPUT.Choice', {
'listRef' => [
{
name => string('PLUGIN_SQUEEZEESP32_LED_VISUALIZER'),
onPlay => sub { Slim::Control::Request::executeRequest($client, ['led_visual']); },
},
{
name => string('PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS'),
onPlay => sub { Slim::Buttons::Common::pushModeLeft($client, 'squeezeesp32_ledvu_bright'); },
},
],
'header' => string('PLUGIN_SQUEEZEESP32'),
'headerAddCount' => 1,
'overlayRef' => sub { return (undef, shift->symbols('rightarrow')) },
});
}
sub setLedvuBrightMode {
my $client = shift;
my $method = shift;
if ($method eq 'pop') {
Slim::Buttons::Common::popMode($client);
$client->update();
return;
}
my $bright = $prefs->client($client)->get('led_brightness');
Slim::Control::Request::executeRequest($client, ['led_visual',1,$bright]);
Slim::Buttons::Common::pushMode($client, 'INPUT.Bar', {
'header' => 'PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS',
'stringHeader' => 1,
'headerValue' => 'unscaled',
'min' => 1,
'max' => 255,
'increment' => 1,
'onChange' => sub {
my ($client, $value) = @_;
$bright = $bright + $value;
if ($bright > 0 && $bright <= 255) {
$prefs->client($client)->set('led_brightness', $bright);
updateLED($client);
}
},
'valueRef' => $bright,
});
}
1;

View File

@@ -10,6 +10,6 @@
<name>PLUGIN_SQUEEZEESP32</name>
<description>PLUGIN_SQUEEZEESP32_DESC</description>
<module>Plugins::SqueezeESP32::Plugin</module>
<version>0.501</version>
<version>0.600</version>
<creator>Philippe</creator>
</extensions>

View File

@@ -1,10 +1,10 @@
<?xml version='1.0' standalone='yes'?>
<extensions>
<plugins>
<plugin version="0.501" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
<plugin version="0.600" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
<link>https://github.com/sle118/squeezelite-esp32</link>
<creator>Philippe</creator>
<sha>842ee0f7b8ccaf2e6df4a741565068f9e4c7a27e</sha>
<sha>335b585e22eddbddacce00f34001d8bf2ee0f54f</sha>
<email>philippe_44@outlook.com</email>
<desc lang="EN">SqueezeESP32 additional player id (100/101)</desc>
<url>http://raw.githubusercontent.com/sle118/squeezelite-esp32/master-v4.3/plugin/SqueezeESP32.zip</url>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -144,18 +144,18 @@ LxoOkcXsNb/12jOV3iQSDfXDI41AgtFc694KCOjlg+UKizpemE53T5/cq37OqChP
qnlPyb6PYIhua/kgbH84ltba1xEDQ9i4UYfOMiJNZEzEdSfQ498=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIIXzCCB0egAwIBAgIQBKU3dDawNcfrgz6Dj4jGVjANBgkqhkiG9w0BAQsFADA8
MIIIOzCCByOgAwIBAgIQDMnYcxFdn1+HZt0ULgDEwTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAxMB4XDTIzMDcxMDAwMDAwMFoXDTI0MDYyMTIzNTk1OVowGzEZ
UlNBIDIwNDggTTAxMB4XDTIzMTAxMDAwMDAwMFoXDTI0MDcxMDIzNTk1OVowGzEZ
MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMhVo8GFFI8V3qJuBvo9Mcp9O2FkASu1No2kmnXcZn6rqV5Z9Rdz
DSTMBkgBz1UNxyOJ4HujSSjgqrDJ0Fv2uZ+xUfwh8wsZagpoMp6D53nOqzZxwmzi
rs7iGbBHZ0TvROel/zUEayjwm1CpEhM6fPGArITcAUcBZ0kaDLrStyijuqaQQasP
cnjcsTili+vWk4VhPNXKg9lkwQaWmcj/iUENx2l4Fqe6e93CmlqhYPMyf4lH2oiz
MX7WZdR1w2bMIP/+J1AAuoll2y0CF60/7w+NidmpGQyujOUV2YPFYhJc0mLRyY0c
btSo589TxMmhGbKwMywKQbXLueAC/CK0fUUCAwEAAaOCBXwwggV4MB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBSP/UslcNwoVtOJhOQj
scAlxpUVVjCCAqwGA1UdEQSCAqMwggKfghBzMy5hbWF6b25hd3MuY29tghIqLnMz
ADCCAQoCggEBANbrGvFbxrAM6TKussVOuHpCNsZX1V+jR6TAfzrO8VQWqiz+DCq+
LlGFT2is2kRxY+hv2NswgK+Ie4SUdypQDrJ+mKFadmM2UOqIcFepA4EIUESAXZHg
lHJAv5460i6gp9Lh6imN3jGjC1Ax0rgrfBZA+uzIy8dorBEptIj/YpE4wD4WPHnk
Y+wCDfMZdm6b6hqsrOkbqBWHH2zSH8TWfnYRguGe3NkLpM4pBNa2lTb1GBeZtm/W
TB3dEAaxcGCu+WQbicI6BxkbzklgxKTHZ2Lc3mL8P5Ca1yWQG3asdUxw0SIkCZVd
aCzQxqtLlXSxpe4xK2KjOfq2ZM+MZhq/tR0CAwEAAaOCBVgwggVUMB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBQi0miDsNb3pJw4EouH
AR3hoCAaqzCCAokGA1UdEQSCAoAwggJ8ghBzMy5hbWF6b25hd3MuY29tghIqLnMz
LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou
czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h
@@ -164,29 +164,28 @@ LWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLiouczMtY29udHJvbC5k
dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh
bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50
LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIhKi5zMy51cy1lYXN0LTEudnBj
ZS5hbWF6b25hd3MuY29tgicqLnMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpv
bmF3cy5jb22CJXMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22C
G3MzLWV4dGVybmFsLTEuYW1hem9uYXdzLmNvbYIdKi5zMy1leHRlcm5hbC0xLmFt
YXpvbmF3cy5jb22CG3MzLWV4dGVybmFsLTIuYW1hem9uYXdzLmNvbYIdKi5zMy1l
eHRlcm5hbC0yLmFtYXpvbmF3cy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v
Y3JsLnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jcmwwEwYDVR0gBAwwCjAI
BgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2Nz
cC5yMm0wMS5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQu
cjJtMDEuYW1hem9udHJ1c3QuY29tL3IybTAxLmNlcjAMBgNVHRMBAf8EAjAAMIIB
fgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwDuzdBk1dsazsVct520zROiModGfLzs
3sNRSFlGcR+1mwAAAYlCAMrQAAAEAwBIMEYCIQCJhWcUFTgBlQtPboTv0HorPNkz
l+/o8Fr1hFYfs+8bIQIhAOaCRLr5774k0iflsslqBMmubN3NC+mImezWIaMHPP26
AHUA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJQgDK8gAABAMA
RjBEAiBalCI6KSCFXMTY15CgLEHtqeE/vyihg8FWthX4JgPcFwIgWkI//yF+tqBi
Gs3sHuuGvG4v7rytkUM4Dli6sWAYXsYAdgA7U3d1Pi25gE6LMFsG/kA7Z9hPw/TH
vQANLXJv4frUFwAAAYlCAMsyAAAEAwBHMEUCIQDfn+j2sutaXvO5yZ86z6OFgq1u
WhNwM2MnEHfGhO+yCAIgLYi8SX6hZRGRZFxIrV1ekcRSk+9yN8oHNfOQOuGQQOEw
DQYJKoZIhvcNAQELBQADggEBAIprc7vYrAerkphosDZ2frlk9gYBnlgTYT0wJXLC
4ZTnlhXuMbL5gYmM8T1suPv5p2JphVFEsSS8PlmdQX45mPAlk8lHjHSZJRgL/r1Y
7wWYjKB3oHl5N/5UZTtDTtfxtjd/3Vts6citxzhVwGxH+fQspligvb1Sgy8/ojjV
lSz+QwpK2PrxRwvWPt0XImPj+5xPi8MOhRCh32Edtqy5fQkZiG/mYEvMMpq73kv0
uWbcjMFN2XbSOSYkjjseVoTBly65MAxFyAFAdg9HMJeJ5hbegpl4PARhgdL8sG3h
jEenWaFiCjwQ4iCPFeGk+vumNkL1c5OaLFZwhoZQ+KLkAQc=
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYInKi5zMy1kZXByZWNhdGVkLnVz
LWVhc3QtMS5hbWF6b25hd3MuY29tgiVzMy1kZXByZWNhdGVkLnVzLWVhc3QtMS5h
bWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0xLmFtYXpvbmF3cy5jb22CHSouczMt
ZXh0ZXJuYWwtMS5hbWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0yLmFtYXpvbmF3
cy5jb22CHSouczMtZXh0ZXJuYWwtMi5hbWF6b25hd3MuY29tMBMGA1UdIAQMMAow
CAYGZ4EMAQIBMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
KwYBBQUHAwIwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5yMm0wMS5hbWF6
b250cnVzdC5jb20vcjJtMDEuY3JsMHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcw
AYYhaHR0cDovL29jc3AucjJtMDEuYW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAC
hipodHRwOi8vY3J0LnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jZXIwDAYD
VR0TAQH/BAIwADCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFnAHYA7s3QZNXbGs7F
XLedtM0TojKHRny87N7DUUhZRnEftZsAAAGLG2S5PwAABAMARzBFAiEAxOJvK1tq
gzf5d6NPN4PZui5PhQePCg1bH/fVMd7T5ekCIHeOglhFW5BC0TNeHY+cfUOvDONT
DV4fAVcaWsBXErw9AHUASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMA
AAGLG2S5LwAABAMARjBEAiBYu16xOFIzUE9aMrB1vrFkTsMC3veucw9k0Hv4qO2v
XAIgTTMRV9sq+ytuTU53XdoKLF33qeUZHGqop7O7xoFEENMAdgDatr9rP7W2Ip+b
wrtca+hwkXFsu1GEhTS9pD0wSNf7qwAAAYsbZLlmAAAEAwBHMEUCIQCD6n2r6DVx
UMUgtfPRi3ieJdlObPzsrc0aGVhl6gCP5QIgbzkLqEOfJ7bozTgYzUO8ZDvAd0te
cdpZv182ZEQ974wwDQYJKoZIhvcNAQELBQADggEBAHEAZnHOPS7CoRXWTqWbQu5V
8x2uFNl5jjsbSH68xnviW24ShllvFyHH1iD2R3ov/3Ri8T8TJ0bA14u+3D/iPZk1
BgX2/YdL/0/CptgISLhBtugBk2+MsRry6i7hhbf4/pIyL1uWHUeDuYrW882Xw1ul
uD1wyC1z2CqXZWy0WL0zMV6Yfp/eFnCJ9tj0NoullHGhgk9RTU3qULwuAQmzMqS7
XlhmgdzVPDq3s8qCawHDU6yDdT5FAO3SJ23Dj2efM7enBzq0jryfEZn9vTCRJfKI
yCZT56FgMdEqvmQutAV5UCiL1acK8YU2zUQSxuWn+AyDivV4JSOYw8ho1yZrZZI=
-----END CERTIFICATE-----

BIN
server_certs/r2m01.cer.31 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.32 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.33 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.34 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.35 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.36 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.37 Normal file

Binary file not shown.

View File

@@ -1,16 +1,16 @@
-----BEGIN CERTIFICATE-----
MIIIXzCCB0egAwIBAgIQBKU3dDawNcfrgz6Dj4jGVjANBgkqhkiG9w0BAQsFADA8
MIIIOzCCByOgAwIBAgIQDMnYcxFdn1+HZt0ULgDEwTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAxMB4XDTIzMDcxMDAwMDAwMFoXDTI0MDYyMTIzNTk1OVowGzEZ
UlNBIDIwNDggTTAxMB4XDTIzMTAxMDAwMDAwMFoXDTI0MDcxMDIzNTk1OVowGzEZ
MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMhVo8GFFI8V3qJuBvo9Mcp9O2FkASu1No2kmnXcZn6rqV5Z9Rdz
DSTMBkgBz1UNxyOJ4HujSSjgqrDJ0Fv2uZ+xUfwh8wsZagpoMp6D53nOqzZxwmzi
rs7iGbBHZ0TvROel/zUEayjwm1CpEhM6fPGArITcAUcBZ0kaDLrStyijuqaQQasP
cnjcsTili+vWk4VhPNXKg9lkwQaWmcj/iUENx2l4Fqe6e93CmlqhYPMyf4lH2oiz
MX7WZdR1w2bMIP/+J1AAuoll2y0CF60/7w+NidmpGQyujOUV2YPFYhJc0mLRyY0c
btSo589TxMmhGbKwMywKQbXLueAC/CK0fUUCAwEAAaOCBXwwggV4MB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBSP/UslcNwoVtOJhOQj
scAlxpUVVjCCAqwGA1UdEQSCAqMwggKfghBzMy5hbWF6b25hd3MuY29tghIqLnMz
ADCCAQoCggEBANbrGvFbxrAM6TKussVOuHpCNsZX1V+jR6TAfzrO8VQWqiz+DCq+
LlGFT2is2kRxY+hv2NswgK+Ie4SUdypQDrJ+mKFadmM2UOqIcFepA4EIUESAXZHg
lHJAv5460i6gp9Lh6imN3jGjC1Ax0rgrfBZA+uzIy8dorBEptIj/YpE4wD4WPHnk
Y+wCDfMZdm6b6hqsrOkbqBWHH2zSH8TWfnYRguGe3NkLpM4pBNa2lTb1GBeZtm/W
TB3dEAaxcGCu+WQbicI6BxkbzklgxKTHZ2Lc3mL8P5Ca1yWQG3asdUxw0SIkCZVd
aCzQxqtLlXSxpe4xK2KjOfq2ZM+MZhq/tR0CAwEAAaOCBVgwggVUMB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBQi0miDsNb3pJw4EouH
AR3hoCAaqzCCAokGA1UdEQSCAoAwggJ8ghBzMy5hbWF6b25hd3MuY29tghIqLnMz
LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou
czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h
@@ -19,29 +19,28 @@ LWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLiouczMtY29udHJvbC5k
dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh
bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50
LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIhKi5zMy51cy1lYXN0LTEudnBj
ZS5hbWF6b25hd3MuY29tgicqLnMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpv
bmF3cy5jb22CJXMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22C
G3MzLWV4dGVybmFsLTEuYW1hem9uYXdzLmNvbYIdKi5zMy1leHRlcm5hbC0xLmFt
YXpvbmF3cy5jb22CG3MzLWV4dGVybmFsLTIuYW1hem9uYXdzLmNvbYIdKi5zMy1l
eHRlcm5hbC0yLmFtYXpvbmF3cy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v
Y3JsLnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jcmwwEwYDVR0gBAwwCjAI
BgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2Nz
cC5yMm0wMS5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQu
cjJtMDEuYW1hem9udHJ1c3QuY29tL3IybTAxLmNlcjAMBgNVHRMBAf8EAjAAMIIB
fgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwDuzdBk1dsazsVct520zROiModGfLzs
3sNRSFlGcR+1mwAAAYlCAMrQAAAEAwBIMEYCIQCJhWcUFTgBlQtPboTv0HorPNkz
l+/o8Fr1hFYfs+8bIQIhAOaCRLr5774k0iflsslqBMmubN3NC+mImezWIaMHPP26
AHUA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJQgDK8gAABAMA
RjBEAiBalCI6KSCFXMTY15CgLEHtqeE/vyihg8FWthX4JgPcFwIgWkI//yF+tqBi
Gs3sHuuGvG4v7rytkUM4Dli6sWAYXsYAdgA7U3d1Pi25gE6LMFsG/kA7Z9hPw/TH
vQANLXJv4frUFwAAAYlCAMsyAAAEAwBHMEUCIQDfn+j2sutaXvO5yZ86z6OFgq1u
WhNwM2MnEHfGhO+yCAIgLYi8SX6hZRGRZFxIrV1ekcRSk+9yN8oHNfOQOuGQQOEw
DQYJKoZIhvcNAQELBQADggEBAIprc7vYrAerkphosDZ2frlk9gYBnlgTYT0wJXLC
4ZTnlhXuMbL5gYmM8T1suPv5p2JphVFEsSS8PlmdQX45mPAlk8lHjHSZJRgL/r1Y
7wWYjKB3oHl5N/5UZTtDTtfxtjd/3Vts6citxzhVwGxH+fQspligvb1Sgy8/ojjV
lSz+QwpK2PrxRwvWPt0XImPj+5xPi8MOhRCh32Edtqy5fQkZiG/mYEvMMpq73kv0
uWbcjMFN2XbSOSYkjjseVoTBly65MAxFyAFAdg9HMJeJ5hbegpl4PARhgdL8sG3h
jEenWaFiCjwQ4iCPFeGk+vumNkL1c5OaLFZwhoZQ+KLkAQc=
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYInKi5zMy1kZXByZWNhdGVkLnVz
LWVhc3QtMS5hbWF6b25hd3MuY29tgiVzMy1kZXByZWNhdGVkLnVzLWVhc3QtMS5h
bWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0xLmFtYXpvbmF3cy5jb22CHSouczMt
ZXh0ZXJuYWwtMS5hbWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0yLmFtYXpvbmF3
cy5jb22CHSouczMtZXh0ZXJuYWwtMi5hbWF6b25hd3MuY29tMBMGA1UdIAQMMAow
CAYGZ4EMAQIBMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
KwYBBQUHAwIwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5yMm0wMS5hbWF6
b250cnVzdC5jb20vcjJtMDEuY3JsMHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcw
AYYhaHR0cDovL29jc3AucjJtMDEuYW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAC
hipodHRwOi8vY3J0LnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jZXIwDAYD
VR0TAQH/BAIwADCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFnAHYA7s3QZNXbGs7F
XLedtM0TojKHRny87N7DUUhZRnEftZsAAAGLG2S5PwAABAMARzBFAiEAxOJvK1tq
gzf5d6NPN4PZui5PhQePCg1bH/fVMd7T5ekCIHeOglhFW5BC0TNeHY+cfUOvDONT
DV4fAVcaWsBXErw9AHUASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMA
AAGLG2S5LwAABAMARjBEAiBYu16xOFIzUE9aMrB1vrFkTsMC3veucw9k0Hv4qO2v
XAIgTTMRV9sq+ytuTU53XdoKLF33qeUZHGqop7O7xoFEENMAdgDatr9rP7W2Ip+b
wrtca+hwkXFsu1GEhTS9pD0wSNf7qwAAAYsbZLlmAAAEAwBHMEUCIQCD6n2r6DVx
UMUgtfPRi3ieJdlObPzsrc0aGVhl6gCP5QIgbzkLqEOfJ7bozTgYzUO8ZDvAd0te
cdpZv182ZEQ974wwDQYJKoZIhvcNAQELBQADggEBAHEAZnHOPS7CoRXWTqWbQu5V
8x2uFNl5jjsbSH68xnviW24ShllvFyHH1iD2R3ov/3Ri8T8TJ0bA14u+3D/iPZk1
BgX2/YdL/0/CptgISLhBtugBk2+MsRry6i7hhbf4/pIyL1uWHUeDuYrW882Xw1ul
uD1wyC1z2CqXZWy0WL0zMV6Yfp/eFnCJ9tj0NoullHGhgk9RTU3qULwuAQmzMqS7
XlhmgdzVPDq3s8qCawHDU6yDdT5FAO3SJ23Dj2efM7enBzq0jryfEZn9vTCRJfKI
yCZT56FgMdEqvmQutAV5UCiL1acK8YU2zUQSxuWn+AyDivV4JSOYw8ho1yZrZZI=
-----END CERTIFICATE-----