mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-31 21:59:43 +03:00
Compare commits
50 Commits
SqueezeAmp
...
I2S-4MFlas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ef63dc3e4 | ||
|
|
468c847499 | ||
|
|
8c0e766cd7 | ||
|
|
0f792d71ee | ||
|
|
1fc7675c14 | ||
|
|
7b439ae6ee | ||
|
|
c5d7fd521d | ||
|
|
a856b26181 | ||
|
|
a949ec2d24 | ||
|
|
5ec5236991 | ||
|
|
d4cd400cd9 | ||
|
|
64bb5f018b | ||
|
|
593927aac3 | ||
|
|
51761d0890 | ||
|
|
5c56abfe75 | ||
|
|
9ac7c5bbeb | ||
|
|
be28555a40 | ||
|
|
1d32479bc4 | ||
|
|
c83ddc4adc | ||
|
|
5a7cf9b8fe | ||
|
|
190326726c | ||
|
|
387276f2f3 | ||
|
|
87b8c46263 | ||
|
|
d7aef11856 | ||
|
|
be842d235d | ||
|
|
d49ad66177 | ||
|
|
ca28669b0a | ||
|
|
45f480d948 | ||
|
|
852e312879 | ||
|
|
c79cf5b58f | ||
|
|
1a4a8ba559 | ||
|
|
7ad39a02f5 | ||
|
|
f96d06912f | ||
|
|
36571d3dad | ||
|
|
b075bbaea3 | ||
|
|
ec7982dbfc | ||
|
|
afd76bdfbb | ||
|
|
89ecd19d32 | ||
|
|
8904ec1afd | ||
|
|
5120029643 | ||
|
|
4a529d6fbd | ||
|
|
afe697e4b1 | ||
|
|
879b1f9107 | ||
|
|
5a7d4fd535 | ||
|
|
d61c650f39 | ||
|
|
f096ee269e | ||
|
|
a105f7fd99 | ||
|
|
4444fed343 | ||
|
|
2b59c38b4b | ||
|
|
7457632990 |
7
.github/workflows/CrossBuild.yml
vendored
7
.github/workflows/CrossBuild.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
# This is a basic workflow to help you get started with Actions
|
# This is a basic workflow to help you get started with Actions
|
||||||
|
|
||||||
name: Cross-Build
|
name: Cross-Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -32,7 +31,7 @@ jobs:
|
|||||||
- name: Set target name
|
- name: Set target name
|
||||||
run: |
|
run: |
|
||||||
echo "TARGET_BUILD_NAME=${{ matrix.node }}" >> $GITHUB_ENV
|
echo "TARGET_BUILD_NAME=${{ matrix.node }}" >> $GITHUB_ENV
|
||||||
echo "build_version_prefix=V0." >> $GITHUB_ENV
|
echo "build_version_prefix=1." >> $GITHUB_ENV
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 15
|
fetch-depth: 15
|
||||||
@@ -58,7 +57,7 @@ jobs:
|
|||||||
echo "tag=${tag}" >> $GITHUB_ENV
|
echo "tag=${tag}" >> $GITHUB_ENV
|
||||||
last_commit="$(git log --pretty=format:'%s' --max-count=1)"
|
last_commit="$(git log --pretty=format:'%s' --max-count=1)"
|
||||||
if [[ "$last_commit" =~ .*"Release".* ]]; then echo "release_flag=1" >> $GITHUB_ENV; else echo "release_flag=0" >> $GITHUB_ENV; fi
|
if [[ "$last_commit" =~ .*"Release".* ]]; then echo "release_flag=1" >> $GITHUB_ENV; else echo "release_flag=0" >> $GITHUB_ENV; fi
|
||||||
name="dev.${BUILD_NUMBER}-${{matrix.depth}}#v4.0#${TARGET_BUILD_NAME}#${branch_name}"
|
name="1.${BUILD_NUMBER}-${{matrix.depth}}#v4.0#${TARGET_BUILD_NAME}#${branch_name}"
|
||||||
artifact_prefix="squeezelite-esp32-${branch_name}-${TARGET_BUILD_NAME}-${{matrix.depth}}-${build_version_prefix}${BUILD_NUMBER}"
|
artifact_prefix="squeezelite-esp32-${branch_name}-${TARGET_BUILD_NAME}-${{matrix.depth}}-${build_version_prefix}${BUILD_NUMBER}"
|
||||||
artifact_file_name="${artifact_prefix}.zip"
|
artifact_file_name="${artifact_prefix}.zip"
|
||||||
artifact_bin_file_name="${artifact_prefix}.bin"
|
artifact_bin_file_name="${artifact_prefix}.bin"
|
||||||
@@ -87,7 +86,7 @@ jobs:
|
|||||||
echo "${tag}" >version.txt
|
echo "${tag}" >version.txt
|
||||||
docker pull sle118/idf:release-v4.0
|
docker pull sle118/idf:release-v4.0
|
||||||
docker info
|
docker info
|
||||||
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project sle118/idf:release-v4.0 /bin/bash -c "cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig && idf.py build -DDEPTH=${{ matrix.depth }} -DBUILD_NUMBER=${BUILD_NUMBER}-${{ matrix.depth }} && zip -r build_output.zip build && zip build/${artifact_file_name} partitions*.csv build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt"
|
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project sle118/idf:release-v4.0 /bin/bash -c "cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig && idf.py build -DAAC_ENABLE_SBR=1 -DDEPTH=${{ matrix.depth }} -DBUILD_NUMBER=${BUILD_NUMBER}-${{ matrix.depth }} && zip -r build_output.zip build && zip build/${artifact_file_name} partitions*.csv build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt"
|
||||||
# - name: Build Mock firmware
|
# - name: Build Mock firmware
|
||||||
# run: |
|
# run: |
|
||||||
# mkdir -p build
|
# mkdir -p build
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -98,3 +98,5 @@ test/.vscode/
|
|||||||
|
|
||||||
node_modules/*
|
node_modules/*
|
||||||
esp-dsp/
|
esp-dsp/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<?eclipse-pydev version="1.0"?><pydev_project>
|
|
||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
|
||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
|
||||||
</pydev_project>
|
|
||||||
@@ -46,6 +46,8 @@ The esp32 must run at 240 MHz, with Quad-SPI I/O at 80 MHz and a clock of 40 Mhz
|
|||||||
In 16 bits mode, although 192 kHz is reported as max rate, it's highly recommended to limit reported sampling rate to 96k (-Z 96000). Note that some high-speed 24/96k on-line streams might stutter because of TCP/IP stack performances. It is usually due to the fact that the server sends small packets of data and the esp32 cannot receive encoded audio fast enough, regardless of task priority settings (I've tried to tweak that a fair bit). The best option in that case is to let LMS proxy the stream as it will provide larger chunks and a "smoother" stream that can then be handled.
|
In 16 bits mode, although 192 kHz is reported as max rate, it's highly recommended to limit reported sampling rate to 96k (-Z 96000). Note that some high-speed 24/96k on-line streams might stutter because of TCP/IP stack performances. It is usually due to the fact that the server sends small packets of data and the esp32 cannot receive encoded audio fast enough, regardless of task priority settings (I've tried to tweak that a fair bit). The best option in that case is to let LMS proxy the stream as it will provide larger chunks and a "smoother" stream that can then be handled.
|
||||||
## Supported Hardware
|
## Supported Hardware
|
||||||
Any esp32-based hardware with at least 4MB of flash and 4MB of PSRAM will be capable of running squeezelite-esp32 and there are various boards that include such chip. A few are mentionned below, but any should work. You can find various help & instructions [here](https://forums.slimdevices.com/showthread.php?112697-ANNOUNCE-Squeezelite-ESP32-(dedicated-thread))
|
Any esp32-based hardware with at least 4MB of flash and 4MB of PSRAM will be capable of running squeezelite-esp32 and there are various boards that include such chip. A few are mentionned below, but any should work. You can find various help & instructions [here](https://forums.slimdevices.com/showthread.php?112697-ANNOUNCE-Squeezelite-ESP32-(dedicated-thread))
|
||||||
|
|
||||||
|
**For the sake of clarity, WROOM modules DO NOT work as they don't include PSRAM. Some designs might add it externally, but it's (very) unlikely.**
|
||||||
### Raw WROVER module
|
### Raw WROVER module
|
||||||
Per above description, a [WROVER module](https://www.espressif.com/en/products/modules/esp32) is enough to run Squeezelite-esp32, but that requires a bit of tinkering to extend it to have analogue audio or hardware buttons (e.g.)
|
Per above description, a [WROVER module](https://www.espressif.com/en/products/modules/esp32) is enough to run Squeezelite-esp32, but that requires a bit of tinkering to extend it to have analogue audio or hardware buttons (e.g.)
|
||||||
|
|
||||||
@@ -123,13 +125,14 @@ The NVS parameter "i2c_config" set the i2c's gpio used for generic purpose (e.g.
|
|||||||
```
|
```
|
||||||
sda=<gpio>,scl=<gpio>[,port=0|1][,speed=<speed>]
|
sda=<gpio>,scl=<gpio>[,port=0|1][,speed=<speed>]
|
||||||
```
|
```
|
||||||
|
<strong>Please note that you can not use the same GPIO or port as the DAC</strong>
|
||||||
### SPI
|
### SPI
|
||||||
The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is
|
The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is
|
||||||
```
|
```
|
||||||
data=<gpio>,clk=<gpio>[,dc=<gpio>][,host=1|2]
|
data=<gpio>,clk=<gpio>[,dc=<gpio>][,host=1|2]
|
||||||
```
|
```
|
||||||
### DAC/I2S
|
### DAC/I2S
|
||||||
The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. If your DAC also requires i2c, then you must go the re-compile route. Syntax is
|
The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. Syntax is
|
||||||
```
|
```
|
||||||
bck=<gpio>,ws=<gpio>,do=<gpio>[,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
|
bck=<gpio>,ws=<gpio>,do=<gpio>[,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
|
||||||
```
|
```
|
||||||
@@ -144,6 +147,8 @@ The parameter "dac_controlset" allows definition of simple commands to be sent o
|
|||||||
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. **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
|
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. **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
|
||||||
|
|
||||||
NB: For well-known configuration, this is ignored
|
NB: For well-known configuration, this is ignored
|
||||||
|
|
||||||
|
<strong>Please note that you can not use the same GPIO or port as the I2C</strong>
|
||||||
### SPDIF
|
### SPDIF
|
||||||
The NVS parameter "spdif_config" sets the i2s's gpio needed for SPDIF.
|
The NVS parameter "spdif_config" sets the i2s's gpio needed for SPDIF.
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ CONFIG_SPDIF_DO_IO=-1
|
|||||||
CONFIG_DAC_CONFIG="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32"
|
CONFIG_DAC_CONFIG="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32"
|
||||||
CONFIG_MUTE_GPIO=-1
|
CONFIG_MUTE_GPIO=-1
|
||||||
CONFIG_MUTE_GPIO_LEVEL=-1
|
CONFIG_MUTE_GPIO_LEVEL=-1
|
||||||
|
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_PROJECT_NAME="Squeezelite ESP32-A1S"
|
||||||
|
CONFIG_FW_PLATFORM_NAME="ESP32-A1S"
|
||||||
CONFIG_IDF_TARGET_ESP32=y
|
CONFIG_IDF_TARGET_ESP32=y
|
||||||
CONFIG_IDF_TARGET="esp32"
|
CONFIG_IDF_TARGET="esp32"
|
||||||
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
|
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ CONFIG_SPDIF_WS_IO=-1
|
|||||||
CONFIG_SPDIF_DO_IO=-1
|
CONFIG_SPDIF_DO_IO=-1
|
||||||
CONFIG_MUTE_GPIO=-1
|
CONFIG_MUTE_GPIO=-1
|
||||||
CONFIG_MUTE_GPIO_LEVEL=-1
|
CONFIG_MUTE_GPIO_LEVEL=-1
|
||||||
|
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_PROJECT_NAME="Squeezelite-ESP32"
|
||||||
|
CONFIG_FW_PLATFORM_NAME="I2S-4MFlash"
|
||||||
#
|
#
|
||||||
# SDK tool configuration
|
# SDK tool configuration
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ CONFIG_SPDIF_NUM=0
|
|||||||
CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
|
CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
|
||||||
CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0"
|
CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0"
|
||||||
CONFIG_MUTE_GPIO_LEVEL=-1
|
CONFIG_MUTE_GPIO_LEVEL=-1
|
||||||
|
CONFIG_PROJECT_NAME="SqueezeAmp"
|
||||||
|
CONFIG_FW_PLATFORM_NAME="SqueezeAmp"
|
||||||
|
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
||||||
#
|
#
|
||||||
# SDK tool configuration
|
# SDK tool configuration
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -2,9 +2,14 @@ idf_component_register(
|
|||||||
INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis
|
INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (DEFINED AAC_ENABLE_SBR)
|
||||||
|
add_prebuilt_library(libhelix-aac lib/libhelix-aac-sbr.a )
|
||||||
|
else ()
|
||||||
|
add_prebuilt_library(libhelix-aac lib/libhelix-aac.a )
|
||||||
|
endif()
|
||||||
|
|
||||||
add_prebuilt_library(libmad lib/libmad.a)
|
add_prebuilt_library(libmad lib/libmad.a)
|
||||||
add_prebuilt_library(libFLAC lib/libFLAC.a )
|
add_prebuilt_library(libFLAC lib/libFLAC.a )
|
||||||
add_prebuilt_library(libhelix-aac lib/libhelix-aac.a )
|
|
||||||
add_prebuilt_library(libvorbisidec lib/libvorbisidec.a )
|
add_prebuilt_library(libvorbisidec lib/libvorbisidec.a )
|
||||||
add_prebuilt_library(libogg lib/libogg.a )
|
add_prebuilt_library(libogg lib/libogg.a )
|
||||||
add_prebuilt_library(libalac lib/libalac.a )
|
add_prebuilt_library(libalac lib/libalac.a )
|
||||||
|
|||||||
BIN
components/codecs/lib/libhelix-aac-sbr.a
Normal file
BIN
components/codecs/lib/libhelix-aac-sbr.a
Normal file
Binary file not shown.
Binary file not shown.
@@ -238,9 +238,9 @@ void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
|||||||
|
|
||||||
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
|
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
|
||||||
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
|
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
|
||||||
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
|
int GDS_GetWidth( struct GDS_Device* Device ) { return Device ? Device->Width : 0; }
|
||||||
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
|
int GDS_GetHeight( struct GDS_Device* Device ) { return Device ? Device->Height : 0; }
|
||||||
int GDS_GetDepth( struct GDS_Device* Device ) { return Device->Depth; }
|
int GDS_GetDepth( struct GDS_Device* Device ) { return Device ? Device->Depth : 0; }
|
||||||
int GDS_GetMode( struct GDS_Device* Device ) { return Device->Mode; }
|
int GDS_GetMode( struct GDS_Device* Device ) { return Device ? Device->Mode : 0; }
|
||||||
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
|
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
|
||||||
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }
|
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }
|
||||||
@@ -135,7 +135,7 @@ void display_init(char *welcome) {
|
|||||||
displayer.by = 2;
|
displayer.by = 2;
|
||||||
displayer.pause = 3600;
|
displayer.pause = 3600;
|
||||||
displayer.speed = 33;
|
displayer.speed = 33;
|
||||||
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "common_displayer", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
||||||
|
|
||||||
// set lines for "fixed" text mode
|
// set lines for "fixed" text mode
|
||||||
GDS_TextSetFontAuto(display, 1, GDS_FONT_LINE_1, -3);
|
GDS_TextSetFontAuto(display, 1, GDS_FONT_LINE_1, -3);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
|
|||||||
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
|
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
|
||||||
/* avrc TG event handler */
|
/* avrc TG event handler */
|
||||||
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
|
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
|
||||||
static void volume_set_by_local_host(uint8_t volume);
|
static void volume_set_by_local_host(int value, bool is_step);
|
||||||
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter);
|
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter);
|
||||||
|
|
||||||
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
|
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
|
||||||
@@ -70,7 +70,7 @@ static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"}
|
|||||||
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
|
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
|
||||||
|
|
||||||
static _lock_t s_volume_lock;
|
static _lock_t s_volume_lock;
|
||||||
static uint8_t s_volume = 0;
|
static int s_volume, abs_volume, sink_volume;
|
||||||
static bool s_volume_notify;
|
static bool s_volume_notify;
|
||||||
static enum { AUDIO_IDLE, AUDIO_CONNECTED, AUDIO_PLAYING } s_audio = AUDIO_IDLE;
|
static enum { AUDIO_IDLE, AUDIO_CONNECTED, AUDIO_PLAYING } s_audio = AUDIO_IDLE;
|
||||||
|
|
||||||
@@ -90,16 +90,14 @@ static EXT_RAM_ATTR struct {
|
|||||||
|
|
||||||
static void bt_volume_up(bool pressed) {
|
static void bt_volume_up(bool pressed) {
|
||||||
if (!pressed) return;
|
if (!pressed) return;
|
||||||
// volume UP/DOWN buttons are not supported by iPhone/Android
|
volume_set_by_local_host(+3, true);
|
||||||
volume_set_by_local_host(s_volume < 127-3 ? s_volume + 3 : 127);
|
|
||||||
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
|
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
|
||||||
ESP_LOGD(BT_AV_TAG, "BT volume up %u", s_volume);
|
ESP_LOGD(BT_AV_TAG, "BT volume up %u", s_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_volume_down(bool pressed) {
|
static void bt_volume_down(bool pressed) {
|
||||||
if (!pressed) return;
|
if (!pressed) return;
|
||||||
// volume UP/DOWN buttons are not supported by iPhone/Android
|
volume_set_by_local_host(-3, true);
|
||||||
volume_set_by_local_host(s_volume > 3 ? s_volume - 3 : 0);
|
|
||||||
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
|
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +282,8 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
|
|||||||
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
||||||
(*bt_app_a2d_cmd_cb)(BT_SINK_DISCONNECTED);
|
(*bt_app_a2d_cmd_cb)(BT_SINK_DISCONNECTED);
|
||||||
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
|
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
|
||||||
|
abs_volume = -1;
|
||||||
|
s_volume = sink_volume;
|
||||||
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
|
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
|
||||||
(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED);
|
(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED);
|
||||||
}
|
}
|
||||||
@@ -491,20 +491,29 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
|||||||
|
|
||||||
static void volume_set_by_controller(uint8_t volume)
|
static void volume_set_by_controller(uint8_t volume)
|
||||||
{
|
{
|
||||||
ESP_LOGD(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f);
|
// do not modified NVS volume
|
||||||
_lock_acquire(&s_volume_lock);
|
_lock_acquire(&s_volume_lock);
|
||||||
s_volume = volume;
|
s_volume = abs_volume = (volume * 100) / 127;
|
||||||
_lock_release(&s_volume_lock);
|
_lock_release(&s_volume_lock);
|
||||||
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, volume);
|
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void volume_set_by_local_host(uint8_t volume)
|
static void volume_set_by_local_host(int value, bool is_step)
|
||||||
{
|
{
|
||||||
ESP_LOGD(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f);
|
|
||||||
_lock_acquire(&s_volume_lock);
|
_lock_acquire(&s_volume_lock);
|
||||||
s_volume = volume;
|
s_volume = is_step ? s_volume + value : value;
|
||||||
|
if (s_volume > 127) s_volume = 127;
|
||||||
|
else if (s_volume < 0) s_volume = 0;
|
||||||
|
if (abs_volume >= 0) abs_volume = s_volume;
|
||||||
|
else sink_volume = s_volume;
|
||||||
_lock_release(&s_volume_lock);
|
_lock_release(&s_volume_lock);
|
||||||
|
|
||||||
|
// volume has been set by controller, do not store it in NVS
|
||||||
|
if (abs_volume < 0) {
|
||||||
|
char p[4];
|
||||||
|
config_set_value(NVS_TYPE_STR, "bt_sink_volume", itoa(s_volume, p, 10));
|
||||||
|
}
|
||||||
|
|
||||||
if (s_volume_notify) {
|
if (s_volume_notify) {
|
||||||
esp_avrc_rn_param_t rn_param;
|
esp_avrc_rn_param_t rn_param;
|
||||||
rn_param.volume = s_volume;
|
rn_param.volume = s_volume;
|
||||||
@@ -529,7 +538,7 @@ static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
|
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
|
||||||
ESP_LOGD(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f);
|
ESP_LOGD(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (rc->set_abs_vol.volume * 100) / 127);
|
||||||
volume_set_by_controller(rc->set_abs_vol.volume);
|
volume_set_by_controller(rc->set_abs_vol.volume);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -597,6 +606,10 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
|
|||||||
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
|
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char *item = config_alloc_get_default(NVS_TYPE_STR, "bt_sink_volume", "127", 0);
|
||||||
|
sink_volume = atol(item);
|
||||||
|
free(item);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set default parameters for Legacy Pairing
|
* Set default parameters for Legacy Pairing
|
||||||
*/
|
*/
|
||||||
@@ -630,6 +643,7 @@ void bt_sink_init(bt_cmd_vcb_t cmd_cb, bt_data_cb_t data_cb)
|
|||||||
esp_pin_code[3]='4';
|
esp_pin_code[3]='4';
|
||||||
}
|
}
|
||||||
esp_bt_gap_set_pin(pin_type, strlen(pin_code), esp_pin_code);
|
esp_bt_gap_set_pin(pin_type, strlen(pin_code), esp_pin_code);
|
||||||
|
free(pin_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_sink_deinit(void)
|
void bt_sink_deinit(void)
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ idf_component_register( SRC_DIRS .
|
|||||||
|
|
||||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
|
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
|
||||||
idf_build_get_property(project_ver PROJECT_VER)
|
idf_build_get_property(project_ver PROJECT_VER)
|
||||||
|
message("******************* ${project_ver}")
|
||||||
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
|
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
|
||||||
set_source_files_properties(recovery.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"recovery\"")
|
set_source_files_properties(recovery.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"")
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#define PROJECT_NAME "recovery"
|
#include "sdkconfig.h"
|
||||||
|
#ifndef CONFIG_PROJECT_NAME
|
||||||
|
#pragma message "Defaulting project name."
|
||||||
|
#define CONFIG_PROJECT_NAME "Squeezelite-ESP32"
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "application_name.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_app_format.h"
|
#include "esp_app_format.h"
|
||||||
|
|
||||||
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
|
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
|
||||||
|
|
||||||
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
||||||
.magic_word = ESP_APP_DESC_MAGIC_WORD,
|
.magic_word = ESP_APP_DESC_MAGIC_WORD,
|
||||||
.version = PROJECT_VER,
|
.version = PROJECT_VER,
|
||||||
.project_name = PROJECT_NAME,
|
.project_name = CONFIG_PROJECT_NAME,
|
||||||
.idf_ver = IDF_VER,
|
.idf_ver = IDF_VER,
|
||||||
|
|
||||||
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
|
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
idf_build_get_property(idf_path IDF_PATH)
|
idf_build_get_property(idf_path IDF_PATH)
|
||||||
idf_component_register( SRCS cmd_squeezelite.c
|
idf_component_register( SRCS cmd_squeezelite.c
|
||||||
INCLUDE_DIRS .
|
INCLUDE_DIRS .
|
||||||
PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display )
|
PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display )
|
||||||
@@ -13,7 +13,8 @@ target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=lround")
|
|||||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
|
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
|
||||||
|
|
||||||
idf_build_get_property(project_ver PROJECT_VER)
|
idf_build_get_property(project_ver PROJECT_VER)
|
||||||
|
message("******************* ${project_ver}")
|
||||||
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
|
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
|
||||||
set_source_files_properties(cmd_squeezelite.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"squeezelite\"")
|
set_source_files_properties(cmd_squeezelite.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\" ")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#define PROJECT_NAME "squeezelite"
|
#include "sdkconfig.h"
|
||||||
|
#ifndef CONFIG_PROJECT_NAME
|
||||||
|
#pragma message "Defaulting project name."
|
||||||
|
#define CONFIG_PROJECT_NAME "Squeezelite-ESP32"
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "application_name.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
#include "esp_pthread.h"
|
#include "esp_pthread.h"
|
||||||
@@ -22,7 +22,7 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
|||||||
|
|
||||||
.magic_word = ESP_APP_DESC_MAGIC_WORD,
|
.magic_word = ESP_APP_DESC_MAGIC_WORD,
|
||||||
.version = PROJECT_VER,
|
.version = PROJECT_VER,
|
||||||
.project_name = PROJECT_NAME,
|
.project_name = CONFIG_PROJECT_NAME,
|
||||||
.idf_ver = IDF_VER,
|
.idf_ver = IDF_VER,
|
||||||
|
|
||||||
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
|
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const char * desc_dac= "DAC Options";
|
|||||||
const char * desc_spdif= "SPDIF Options";
|
const char * desc_spdif= "SPDIF Options";
|
||||||
const char * desc_audio= "General Audio Options";
|
const char * desc_audio= "General Audio Options";
|
||||||
const char * desc_bt_source= "Bluetooth Audio Output Options";
|
const char * desc_bt_source= "Bluetooth Audio Output Options";
|
||||||
|
const char * desc_rotary= "Rotary Control";
|
||||||
|
|
||||||
|
|
||||||
#define CODECS_BASE "flac|pcm|mp3|ogg"
|
#define CODECS_BASE "flac|pcm|mp3|ogg"
|
||||||
@@ -85,6 +86,19 @@ static struct {
|
|||||||
struct arg_end *end;
|
struct arg_end *end;
|
||||||
} i2s_args;
|
} i2s_args;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
struct arg_rem * rem;
|
||||||
|
struct arg_int * A;
|
||||||
|
struct arg_int * B;
|
||||||
|
struct arg_int * SW;
|
||||||
|
struct arg_lit * volume_lock;
|
||||||
|
struct arg_lit * longpress;
|
||||||
|
struct arg_lit * knobonly;
|
||||||
|
struct arg_int * timer;
|
||||||
|
struct arg_lit * clear;
|
||||||
|
struct arg_end * end;
|
||||||
|
} rotary_args;
|
||||||
|
//config_rotary_get
|
||||||
static struct{
|
static struct{
|
||||||
struct arg_str *sink_name;
|
struct arg_str *sink_name;
|
||||||
struct arg_str *pin_code;
|
struct arg_str *pin_code;
|
||||||
@@ -142,7 +156,7 @@ static struct {
|
|||||||
struct arg_end *end;
|
struct arg_end *end;
|
||||||
} squeezelite_args;
|
} squeezelite_args;
|
||||||
|
|
||||||
int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory){
|
int is_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory, bool output){
|
||||||
int res = 0;
|
int res = 0;
|
||||||
const char * name = gpio->hdr.longopts?gpio->hdr.longopts:gpio->hdr.glossary;
|
const char * name = gpio->hdr.longopts?gpio->hdr.longopts:gpio->hdr.glossary;
|
||||||
*gpio_out=-1;
|
*gpio_out=-1;
|
||||||
@@ -152,7 +166,7 @@ int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandato
|
|||||||
fprintf(f,"Missing: %s\n", name);
|
fprintf(f,"Missing: %s\n", name);
|
||||||
res++;
|
res++;
|
||||||
}
|
}
|
||||||
} else if(!GPIO_IS_VALID_OUTPUT_GPIO(t_gpio)){
|
} else if((output && !GPIO_IS_VALID_OUTPUT_GPIO(t_gpio)) || (!GPIO_IS_VALID_GPIO(t_gpio))){
|
||||||
fprintf(f,"Invalid %s gpio: [%d] %s\n",name, t_gpio, GPIO_IS_VALID_GPIO(t_gpio)?NOT_OUTPUT:NOT_GPIO );
|
fprintf(f,"Invalid %s gpio: [%d] %s\n",name, t_gpio, GPIO_IS_VALID_GPIO(t_gpio)?NOT_OUTPUT:NOT_GPIO );
|
||||||
res++;
|
res++;
|
||||||
}
|
}
|
||||||
@@ -161,7 +175,9 @@ int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandato
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory){
|
||||||
|
return is_gpio(gpio,f,gpio_out,mandatory,true);
|
||||||
|
}
|
||||||
int check_missing_parm(struct arg_int * int_parm, FILE * f){
|
int check_missing_parm(struct arg_int * int_parm, FILE * f){
|
||||||
int res=0;
|
int res=0;
|
||||||
const char * name = int_parm->hdr.longopts?int_parm->hdr.longopts:int_parm->hdr.glossary;
|
const char * name = int_parm->hdr.longopts?int_parm->hdr.longopts:int_parm->hdr.glossary;
|
||||||
@@ -482,6 +498,60 @@ static int do_spdif_cmd(int argc, char **argv){
|
|||||||
return (nerrors==0 && err==ESP_OK)?0:1;
|
return (nerrors==0 && err==ESP_OK)?0:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_rotary_cmd(int argc, char **argv){
|
||||||
|
rotary_struct_t rotary={ .A = -1, .B = -1, .SW = -1, .longpress = 0, .knobonly=0,.volume_lock=false};
|
||||||
|
esp_err_t err=ESP_OK;
|
||||||
|
int nerrors = arg_parse(argc, argv,(void **)&rotary_args);
|
||||||
|
if (rotary_args.clear->count) {
|
||||||
|
cmd_send_messaging(argv[0],MESSAGING_WARNING,"rotary config cleared\n");
|
||||||
|
config_set_value(NVS_TYPE_STR, "rotary_config", "");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *buf = NULL;
|
||||||
|
size_t buf_size = 0;
|
||||||
|
FILE *f = open_memstream(&buf, &buf_size);
|
||||||
|
if (f == NULL) {
|
||||||
|
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(nerrors >0){
|
||||||
|
arg_print_errors(f,rotary_args.end,desc_rotary);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nerrors+=is_gpio(rotary_args.A, f, &rotary.A, true,false);
|
||||||
|
nerrors+=is_gpio(rotary_args.B, f, &rotary.B, true,false);
|
||||||
|
nerrors+=is_gpio(rotary_args.SW, f, &rotary.SW,false,false);
|
||||||
|
|
||||||
|
|
||||||
|
if(rotary_args.knobonly->count>0 && (rotary_args.volume_lock->count>0 || rotary_args.longpress->count>0)){
|
||||||
|
fprintf(f,"error: Cannot use volume lock or longpress option when knob only option selected\n");
|
||||||
|
nerrors++;
|
||||||
|
}
|
||||||
|
if(rotary_args.timer->count>0 && rotary_args.timer->ival[0]<0){
|
||||||
|
fprintf(f,"error: knob only timer should be greater than or equal to zero.\n");
|
||||||
|
nerrors++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rotary.timer = rotary_args.timer->count>0?rotary_args.timer->ival[0]:0;
|
||||||
|
}
|
||||||
|
rotary.knobonly = rotary_args.knobonly->count>0;
|
||||||
|
rotary.volume_lock= rotary_args.volume_lock->count>0;
|
||||||
|
rotary.longpress = rotary_args.longpress->count>0;
|
||||||
|
if(!nerrors ){
|
||||||
|
fprintf(f,"Storing rotary parameters.\n");
|
||||||
|
nerrors+=(config_rotary_set(&rotary )!=ESP_OK);
|
||||||
|
}
|
||||||
|
if(!nerrors ){
|
||||||
|
fprintf(f,"Done.\n");
|
||||||
|
}
|
||||||
|
fflush (f);
|
||||||
|
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
|
||||||
|
fclose(f);
|
||||||
|
FREE_AND_NULL(buf);
|
||||||
|
return (nerrors==0 && err==ESP_OK)?0:1;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_i2s_cmd(int argc, char **argv)
|
static int do_i2s_cmd(int argc, char **argv)
|
||||||
{
|
{
|
||||||
i2s_platform_config_t i2s_dac_pin = {
|
i2s_platform_config_t i2s_dac_pin = {
|
||||||
@@ -624,6 +694,23 @@ cJSON * spdif_cb(){
|
|||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
cJSON * rotary_cb(){
|
||||||
|
cJSON * values = cJSON_CreateObject();
|
||||||
|
const rotary_struct_t *rotary= config_rotary_get();
|
||||||
|
|
||||||
|
if(GPIO_IS_VALID_GPIO(rotary->A ) && rotary->A>=0 && GPIO_IS_VALID_GPIO(rotary->B) && rotary->B>=0){
|
||||||
|
cJSON_AddNumberToObject(values,"A",rotary->A);
|
||||||
|
cJSON_AddNumberToObject(values,"B",rotary->B);
|
||||||
|
if(GPIO_IS_VALID_GPIO(rotary->SW ) && rotary->SW>=0 ){
|
||||||
|
cJSON_AddNumberToObject(values,"SW",rotary->SW);
|
||||||
|
}
|
||||||
|
cJSON_AddBoolToObject(values,"volume_lock",rotary->volume_lock);
|
||||||
|
cJSON_AddBoolToObject(values,"longpress",rotary->longpress);
|
||||||
|
cJSON_AddBoolToObject(values,"knobonly",rotary->knobonly);
|
||||||
|
cJSON_AddNumberToObject(values,"timer",rotary->timer);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
cJSON * audio_cb(){
|
cJSON * audio_cb(){
|
||||||
cJSON * values = cJSON_CreateObject();
|
cJSON * values = cJSON_CreateObject();
|
||||||
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
|
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
|
||||||
@@ -815,6 +902,27 @@ static void register_bt_source_config(void){
|
|||||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void register_rotary_config(void){
|
||||||
|
rotary_args.rem = arg_rem("remark","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.\r\nEncoder is normally hard-coded to respectively knob left, right and push on LMS and to volume down/up/play toggle on BT and AirPlay.");
|
||||||
|
rotary_args.A = arg_int1(NULL,"A","gpio","A/DT gpio");
|
||||||
|
rotary_args.B = arg_int1(NULL,"B","gpio","B/CLK gpio");
|
||||||
|
rotary_args.SW = arg_int0(NULL,"SW","gpio","Switch gpio");
|
||||||
|
rotary_args.knobonly = arg_lit0(NULL,"knobonly","Single knob full navigation. Left, Right and Press is navigation, with Press always going to lower submenu item. Longpress is 'Play', Double press is 'Back', a quick left-right movement on the encoder is 'Pause'");
|
||||||
|
rotary_args.timer = arg_int0(NULL,"timer","ms","The speed of double click (or left-right) when knob only option is enabled. Be aware that the longer you set double click speed, the less responsive the interface will be. ");
|
||||||
|
rotary_args.volume_lock = arg_lit0(NULL,"volume_lock", "Force Volume down/up/play toggle all the time (even in LMS). ");
|
||||||
|
rotary_args.longpress = arg_lit0(NULL,"longpress","Enable alternate mode mode on long-press. In that mode, left is previous, right is next and press is toggle. Every long press on SW alternates between modes (the main mode actual behavior depends on 'volume').");
|
||||||
|
rotary_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||||
|
rotary_args.end = arg_end(3);
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = CFG_TYPE_HW("rotary"),
|
||||||
|
.help = desc_rotary,
|
||||||
|
.hint = NULL,
|
||||||
|
.func = &do_rotary_cmd,
|
||||||
|
.argtable = &rotary_args
|
||||||
|
};
|
||||||
|
cmd_to_json_with_cb(&cmd,&rotary_cb);
|
||||||
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
|
}
|
||||||
|
|
||||||
static void register_audio_config(void){
|
static void register_audio_config(void){
|
||||||
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
|
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
|
||||||
@@ -828,7 +936,9 @@ static void register_audio_config(void){
|
|||||||
};
|
};
|
||||||
cmd_to_json_with_cb(&cmd,&audio_cb);
|
cmd_to_json_with_cb(&cmd,&audio_cb);
|
||||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
}static void register_spdif_config(void){
|
}
|
||||||
|
|
||||||
|
static void register_spdif_config(void){
|
||||||
spdif_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
spdif_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||||
spdif_args.clock = arg_int1(NULL,"clock","<n>","Clock GPIO. e.g. 33");
|
spdif_args.clock = arg_int1(NULL,"clock","<n>","Clock GPIO. e.g. 33");
|
||||||
spdif_args.wordselect = arg_int1(NULL,"wordselect","<n>","Word Select GPIO. e.g. 25");
|
spdif_args.wordselect = arg_int1(NULL,"wordselect","<n>","Word Select GPIO. e.g. 25");
|
||||||
@@ -891,7 +1001,12 @@ void register_config_cmd(void){
|
|||||||
register_audio_config();
|
register_audio_config();
|
||||||
// register_squeezelite_config();
|
// register_squeezelite_config();
|
||||||
register_bt_source_config();
|
register_bt_source_config();
|
||||||
|
if(!is_dac_config_locked()){
|
||||||
register_i2s_config();
|
register_i2s_config();
|
||||||
|
}
|
||||||
|
if(!is_spdif_config_locked()){
|
||||||
register_spdif_config();
|
register_spdif_config();
|
||||||
|
}
|
||||||
|
register_rotary_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -906,7 +906,7 @@ static void register_i2c_set_display(){
|
|||||||
i2cdisp_args.cs = arg_int0("b", "cs", "<n>","SPI Only. CS GPIO (for SPI displays)");
|
i2cdisp_args.cs = arg_int0("b", "cs", "<n>","SPI Only. CS GPIO (for SPI displays)");
|
||||||
i2cdisp_args.speed = arg_int0("s", "speed", "<n>","SPI Only. Bus Speed (Default 8000000). SPI interface can work up to 26MHz~40MHz");
|
i2cdisp_args.speed = arg_int0("s", "speed", "<n>","SPI Only. Bus Speed (Default 8000000). SPI interface can work up to 26MHz~40MHz");
|
||||||
i2cdisp_args.back = arg_int0("b", "back", "<n>","Backlight GPIO (if applicable)");
|
i2cdisp_args.back = arg_int0("b", "back", "<n>","Backlight GPIO (if applicable)");
|
||||||
i2cdisp_args.depth = arg_int0("p", "depth", "1|4", "Bit Depth (only for SSD1326 displays)");
|
i2cdisp_args.depth = arg_int0("p", "depth", "-1|1|4", "Bit Depth (only for SSD1326 displays)");
|
||||||
i2cdisp_args.type = arg_str0("t", "type", "<I2C|SPI>", "Interface (default I2C)");
|
i2cdisp_args.type = arg_str0("t", "type", "<I2C|SPI>", "Interface (default I2C)");
|
||||||
i2cdisp_args.rotate = arg_lit0("r", "rotate", "Rotate 180 degrees");
|
i2cdisp_args.rotate = arg_lit0("r", "rotate", "Rotate 180 degrees");
|
||||||
i2cdisp_args.clear = arg_lit0(NULL, "clear", "clear configuration and return");
|
i2cdisp_args.clear = arg_lit0(NULL, "clear", "clear configuration and return");
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ cJSON * ParmsToJSON(struct arg_hdr * * argtable){
|
|||||||
ADD_TO_JSON(entry,table[tabindex],glossary);
|
ADD_TO_JSON(entry,table[tabindex],glossary);
|
||||||
ADD_TO_JSON(entry,table[tabindex],longopts);
|
ADD_TO_JSON(entry,table[tabindex],longopts);
|
||||||
ADD_TO_JSON(entry,table[tabindex],shortopts);
|
ADD_TO_JSON(entry,table[tabindex],shortopts);
|
||||||
cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0);
|
cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (table[tabindex]->longopts || table[tabindex]->shortopts) );
|
||||||
|
cJSON_AddBoolToObject(entry, "remark", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0 && (!table[tabindex]->longopts && !table[tabindex]->shortopts));
|
||||||
cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
|
cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
|
||||||
cJSON_AddNumberToObject(entry,"mincount",table[tabindex]->mincount);
|
cJSON_AddNumberToObject(entry,"mincount",table[tabindex]->mincount);
|
||||||
cJSON_AddNumberToObject(entry,"maxcount",table[tabindex]->maxcount);
|
cJSON_AddNumberToObject(entry,"maxcount",table[tabindex]->maxcount);
|
||||||
|
|||||||
@@ -183,6 +183,47 @@ esp_err_t config_i2c_set(const i2c_config_t * config, int port){
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
esp_err_t config_rotary_set(rotary_struct_t * config){
|
||||||
|
int buffer_size=512;
|
||||||
|
esp_err_t err=ESP_OK;
|
||||||
|
char * config_buffer=calloc(buffer_size,1);
|
||||||
|
char * config_buffer2=calloc(buffer_size,1);
|
||||||
|
if(config_buffer && config_buffer2) {
|
||||||
|
snprintf(config_buffer,buffer_size,"A=%i,B=%i",config->A, config->B);
|
||||||
|
if(config->SW >=0 ){
|
||||||
|
snprintf(config_buffer2,buffer_size,"%s,SW=%i",config_buffer,config->SW);
|
||||||
|
strcpy(config_buffer,config_buffer2);
|
||||||
|
}
|
||||||
|
if(config->knobonly){
|
||||||
|
strncat(config_buffer,",knobonly",buffer_size);
|
||||||
|
if(config->timer>0){
|
||||||
|
snprintf(config_buffer2,buffer_size,"%s=%i",config_buffer,config->timer);
|
||||||
|
strcpy(config_buffer,config_buffer2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(config->volume_lock){
|
||||||
|
strncat(config_buffer,",volume",buffer_size);
|
||||||
|
}
|
||||||
|
if(config->longpress){
|
||||||
|
strncat(config_buffer,",longpress",buffer_size);
|
||||||
|
}
|
||||||
|
log_send_messaging(MESSAGING_INFO,"Updating rotary configuration to %s",config_buffer);
|
||||||
|
err = config_set_value(NVS_TYPE_STR, "rotary_config", config_buffer);
|
||||||
|
if(err!=ESP_OK){
|
||||||
|
log_send_messaging(MESSAGING_ERROR,"Error: %s",esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
FREE_AND_NULL(config_buffer);
|
||||||
|
FREE_AND_NULL(config_buffer2);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -192,7 +233,7 @@ esp_err_t config_display_set(const display_config_t * config){
|
|||||||
char * config_buffer=calloc(buffer_size,1);
|
char * config_buffer=calloc(buffer_size,1);
|
||||||
char * config_buffer2=calloc(buffer_size,1);
|
char * config_buffer2=calloc(buffer_size,1);
|
||||||
if(config_buffer && config_buffer2) {
|
if(config_buffer && config_buffer2) {
|
||||||
snprintf(config_buffer,buffer_size,"%s:width=%i,height=%i",config->type,config->width,config->height);
|
snprintf(config_buffer,buffer_size,"%s,width=%i,height=%i",config->type,config->width,config->height);
|
||||||
if(strcasecmp("I2C",config->type)==0){
|
if(strcasecmp("I2C",config->type)==0){
|
||||||
if(config->address>0 ){
|
if(config->address>0 ){
|
||||||
snprintf(config_buffer2,buffer_size,"%s,address=%i",config_buffer,config->address);
|
snprintf(config_buffer2,buffer_size,"%s,address=%i",config_buffer,config->address);
|
||||||
@@ -510,6 +551,36 @@ void parse_set_GPIO(void (*cb)(int gpio, char *value)) {
|
|||||||
free(nvs_item);
|
free(nvs_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const rotary_struct_t * config_rotary_get() {
|
||||||
|
|
||||||
|
static rotary_struct_t rotary={ .A = -1, .B = -1, .SW = -1, .longpress = false, .knobonly=false,.timer=0,.volume_lock=false};
|
||||||
|
char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0);
|
||||||
|
if (config && *config) {
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
// parse config
|
||||||
|
if ((p = strcasestr(config, "A")) != NULL) rotary.A = atoi(strchr(p, '=') + 1);
|
||||||
|
if ((p = strcasestr(config, "B")) != NULL) rotary.B = atoi(strchr(p, '=') + 1);
|
||||||
|
if ((p = strcasestr(config, "SW")) != NULL) rotary.SW = atoi(strchr(p, '=') + 1);
|
||||||
|
if ((p = strcasestr(config, "knobonly")) != NULL) {
|
||||||
|
p = strchr(p, '=');
|
||||||
|
rotary.knobonly = true;
|
||||||
|
rotary.timer = p ? atoi(p + 1) : 350;
|
||||||
|
rotary.longpress = false;
|
||||||
|
} else {
|
||||||
|
rotary.knobonly = false;
|
||||||
|
rotary.timer = 0;
|
||||||
|
if ((p = strcasestr(config, "volume")) != NULL) rotary.volume_lock = true;
|
||||||
|
if ((p = strcasestr(config, "longpress")) != NULL) rotary.longpress = true;
|
||||||
|
}
|
||||||
|
free(config);
|
||||||
|
}
|
||||||
|
return &rotary;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -522,6 +593,17 @@ cJSON * get_gpio_entry(const char * name, const char * prefix, int gpio, bool fi
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
cJSON * add_gpio_for_value(cJSON * list,const char * name,int gpio, const char * prefix, bool fixed){
|
||||||
|
cJSON * llist = list?list:cJSON_CreateArray();
|
||||||
|
if(GPIO_IS_VALID_GPIO(gpio) && gpio>0){
|
||||||
|
cJSON_AddItemToArray(llist,get_gpio_entry(name,prefix,gpio,fixed));
|
||||||
|
}
|
||||||
|
return llist;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -657,13 +739,11 @@ cJSON * get_SPDIF_GPIO(cJSON * list, bool fixed){
|
|||||||
*/
|
*/
|
||||||
cJSON * get_Rotary_GPIO(cJSON * list){
|
cJSON * get_Rotary_GPIO(cJSON * list){
|
||||||
cJSON * llist = list?list:cJSON_CreateArray();
|
cJSON * llist = list?list:cJSON_CreateArray();
|
||||||
char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0);
|
|
||||||
if(config){
|
const rotary_struct_t *rotary= config_rotary_get();
|
||||||
llist = add_gpio_for_name(llist,config,"A", "rotary", false);
|
add_gpio_for_value(llist,"A",rotary->A, "rotary", false);
|
||||||
llist = add_gpio_for_name(llist,config,"B", "rotary", false);
|
add_gpio_for_value(llist,"B",rotary->B, "rotary", false);
|
||||||
llist = add_gpio_for_name(llist,config,"SW", "rotary", false);
|
add_gpio_for_value(llist,"SW",rotary->SW, "rotary", false);
|
||||||
free(config);
|
|
||||||
}
|
|
||||||
return llist;
|
return llist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,16 @@ typedef struct {
|
|||||||
gpio_with_level_t spkfault;
|
gpio_with_level_t spkfault;
|
||||||
} set_GPIO_struct_t;
|
} set_GPIO_struct_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int A;
|
||||||
|
int B;
|
||||||
|
int SW;
|
||||||
|
bool knobonly;
|
||||||
|
bool volume_lock;
|
||||||
|
bool longpress;
|
||||||
|
int timer;
|
||||||
|
} rotary_struct_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool fixed;
|
bool fixed;
|
||||||
char * name;
|
char * name;
|
||||||
@@ -82,3 +92,5 @@ gpio_entry_t * get_gpio_by_no(int gpionum, bool refresh);
|
|||||||
cJSON * get_gpio_list(bool refresh);
|
cJSON * get_gpio_list(bool refresh);
|
||||||
bool is_dac_config_locked();
|
bool is_dac_config_locked();
|
||||||
bool are_statistics_enabled();
|
bool are_statistics_enabled();
|
||||||
|
const rotary_struct_t * config_rotary_get();
|
||||||
|
esp_err_t config_rotary_set(rotary_struct_t * rotary);
|
||||||
@@ -38,6 +38,8 @@
|
|||||||
#include "gds_text.h"
|
#include "gds_text.h"
|
||||||
#include "gds_draw.h"
|
#include "gds_draw.h"
|
||||||
#include "platform_esp32.h"
|
#include "platform_esp32.h"
|
||||||
|
#include "lwip/sockets.h"
|
||||||
|
|
||||||
|
|
||||||
extern const char * get_certificate();
|
extern const char * get_certificate();
|
||||||
#define IF_DISPLAY(x) if(display) { x; }
|
#define IF_DISPLAY(x) if(display) { x; }
|
||||||
@@ -92,6 +94,7 @@ ota_status_t * ota_status;
|
|||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
static esp_http_client_config_t http_client_config;
|
static esp_http_client_config_t http_client_config;
|
||||||
|
|
||||||
|
|
||||||
void _printMemStats(){
|
void _printMemStats(){
|
||||||
ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
|
ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
|
||||||
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||||
@@ -276,8 +279,8 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
|||||||
// int data_len - data length of data
|
// int data_len - data length of data
|
||||||
// void *user_data -- user_data context, from esp_http_client_config_t user_data
|
// void *user_data -- user_data context, from esp_http_client_config_t user_data
|
||||||
|
|
||||||
// char *header_key For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header key
|
// char *header_key For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header key
|
||||||
// char *header_value For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header value
|
// char *header_value For HTTP_EVENT_ON_HEADER event_id, it<69>s store current http header value
|
||||||
// --------------
|
// --------------
|
||||||
switch (evt->event_id) {
|
switch (evt->event_id) {
|
||||||
case HTTP_EVENT_ERROR:
|
case HTTP_EVENT_ERROR:
|
||||||
@@ -630,7 +633,7 @@ void ota_task(void *pvParameter)
|
|||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
uint32_t elapsed_ms= (tv.tv_sec-ota_status->OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status->OTA_start.tv_usec)/1000;
|
uint32_t elapsed_ms= (tv.tv_sec-ota_status->OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status->OTA_start.tv_usec)/1000;
|
||||||
ESP_LOGI(TAG,"OTA progress : %d/%.0f (%d pct), %d KB/s", ota_status->actual_image_len, ota_status->total_image_len, ota_status->newpct, elapsed_ms>0?ota_status->actual_image_len*1000/elapsed_ms/1024:0);
|
ESP_LOGI(TAG,"OTA progress : %d/%.0f (%d pct), %d KB/s", ota_status->actual_image_len, ota_status->total_image_len, ota_status->newpct, elapsed_ms>0?ota_status->actual_image_len*1000/elapsed_ms/1024:0);
|
||||||
sendMessaging(MESSAGING_INFO,"Writing binary file %%%3d.",ota_status->newpct);
|
sendMessaging(MESSAGING_INFO,"Writing binary file %3d %%.",ota_status->newpct);
|
||||||
ota_status->lastpct=ota_status->newpct;
|
ota_status->lastpct=ota_status->newpct;
|
||||||
}
|
}
|
||||||
taskYIELD();
|
taskYIELD();
|
||||||
@@ -659,7 +662,7 @@ void ota_task(void *pvParameter)
|
|||||||
ESP_LOGI(TAG,"OTA Process completed successfully!");
|
ESP_LOGI(TAG,"OTA Process completed successfully!");
|
||||||
sendMessaging(MESSAGING_INFO,"Success!");
|
sendMessaging(MESSAGING_INFO,"Success!");
|
||||||
IF_DISPLAY(GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Success!"));
|
IF_DISPLAY(GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Success!"));
|
||||||
vTaskDelay(1500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh
|
vTaskDelay(3500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh
|
||||||
IF_DISPLAY(GDS_Clear(display,GDS_COLOR_BLACK));
|
IF_DISPLAY(GDS_Clear(display,GDS_COLOR_BLACK));
|
||||||
esp_restart();
|
esp_restart();
|
||||||
} else {
|
} else {
|
||||||
@@ -721,5 +724,66 @@ esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void set_lms_server_details(in_addr_t ip, u16_t hport, u16_t cport);
|
||||||
|
|
||||||
|
in_addr_t discover_ota_server(int max) {
|
||||||
|
struct sockaddr_in d;
|
||||||
|
struct sockaddr_in s;
|
||||||
|
char buf[32], port_d[] = "JSON", clip_d[] = "CLIP";
|
||||||
|
struct pollfd pollinfo;
|
||||||
|
uint8_t len;
|
||||||
|
uint16_t hport=9000;
|
||||||
|
uint16_t cport=9090;
|
||||||
|
|
||||||
|
int disc_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
|
||||||
|
socklen_t enable = 1;
|
||||||
|
setsockopt(disc_sock, SOL_SOCKET, SO_BROADCAST, (const void *)&enable, sizeof(enable));
|
||||||
|
|
||||||
|
len = sprintf(buf,"e%s%c%s", port_d, '\0', clip_d) + 1;
|
||||||
|
|
||||||
|
memset(&d, 0, sizeof(d));
|
||||||
|
d.sin_family = AF_INET;
|
||||||
|
d.sin_port = htons(3483);
|
||||||
|
d.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
||||||
|
|
||||||
|
pollinfo.fd = disc_sock;
|
||||||
|
pollinfo.events = POLLIN;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,"sending LMS discovery");
|
||||||
|
memset(&s, 0, sizeof(s));
|
||||||
|
|
||||||
|
if (sendto(disc_sock, buf, len, 0, (struct sockaddr *)&d, sizeof(d)) < 0) {
|
||||||
|
ESP_LOGE(TAG,"error sending discovery");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (poll(&pollinfo, 1, 5000) == 1) {
|
||||||
|
char readbuf[64], *p;
|
||||||
|
socklen_t slen = sizeof(s);
|
||||||
|
memset(readbuf, 0, sizeof(readbuf));
|
||||||
|
recvfrom(disc_sock, readbuf, sizeof(readbuf) - 1, 0, (struct sockaddr *)&s, &slen);
|
||||||
|
ESP_LOGI(TAG,"got response from: %s:%d - %s", inet_ntoa(s.sin_addr), ntohs(s.sin_port),readbuf);
|
||||||
|
|
||||||
|
if ((p = strstr(readbuf, port_d)) != NULL) {
|
||||||
|
p += strlen(port_d);
|
||||||
|
hport = atoi(p + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p = strstr(readbuf, clip_d)) != NULL) {
|
||||||
|
p += strlen(clip_d);
|
||||||
|
cport = atoi(p + 1);
|
||||||
|
}
|
||||||
|
server_notify(s.sin_addr.s_addr, hport, cport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (s.sin_addr.s_addr == 0 && (!max || --max));
|
||||||
|
|
||||||
|
closesocket(disc_sock);
|
||||||
|
|
||||||
|
return s.sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,4 +31,4 @@ const char * ota_get_status();
|
|||||||
uint8_t ota_get_pct_complete();
|
uint8_t ota_get_pct_complete();
|
||||||
|
|
||||||
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length);
|
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length);
|
||||||
|
in_addr_t discover_ota_server(int max);
|
||||||
|
|||||||
@@ -37,4 +37,8 @@ else()
|
|||||||
add_definitions(-DRESAMPLE16 -DBYTES_PER_FRAME=4)
|
add_definitions(-DRESAMPLE16 -DBYTES_PER_FRAME=4)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (DEFINED AAC_ENABLE_SBR)
|
||||||
|
add_definitions(-DAAC_ENABLE_SBR)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_compile_options (-O3 )
|
add_compile_options (-O3 )
|
||||||
|
|||||||
@@ -113,14 +113,23 @@ struct ANIC_header {
|
|||||||
u8_t mode;
|
u8_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dmxt_packet {
|
||||||
|
char opcode[4];
|
||||||
|
u16_t x;
|
||||||
|
u16_t length;
|
||||||
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
TaskHandle_t task;
|
TaskHandle_t task;
|
||||||
|
int wake;
|
||||||
|
bool owned;
|
||||||
|
struct {
|
||||||
SemaphoreHandle_t mutex;
|
SemaphoreHandle_t mutex;
|
||||||
int width, height;
|
int width, height;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
bool owned;
|
};
|
||||||
} displayer = { .dirty = true, .owned = true };
|
} displayer = { .dirty = true, .owned = true };
|
||||||
|
|
||||||
static uint32_t *grayMap;
|
static uint32_t *grayMap;
|
||||||
@@ -143,13 +152,13 @@ static uint32_t *grayMap;
|
|||||||
static struct scroller_s {
|
static struct scroller_s {
|
||||||
// copy of grfs content
|
// copy of grfs content
|
||||||
u8_t screen;
|
u8_t screen;
|
||||||
u32_t pause, speed;
|
u32_t pause;
|
||||||
int wake;
|
|
||||||
u16_t mode;
|
u16_t mode;
|
||||||
s16_t by;
|
s16_t by;
|
||||||
// scroller management & sharing between grfg and scrolling task
|
// scroller management & sharing between grfg and scrolling task
|
||||||
bool active, first, overflow;
|
bool active, first, overflow;
|
||||||
int scrolled;
|
int scrolled;
|
||||||
|
int speed, wake;
|
||||||
struct {
|
struct {
|
||||||
u8_t *frame;
|
u8_t *frame;
|
||||||
u32_t width;
|
u32_t width;
|
||||||
@@ -167,7 +176,7 @@ static struct {
|
|||||||
u8_t *data;
|
u8_t *data;
|
||||||
u32_t size;
|
u32_t size;
|
||||||
u16_t x, y;
|
u16_t x, y;
|
||||||
bool enable;
|
bool enable, full;
|
||||||
} artwork;
|
} artwork;
|
||||||
|
|
||||||
#define MAX_BARS 32
|
#define MAX_BARS 32
|
||||||
@@ -175,15 +184,13 @@ static struct {
|
|||||||
static EXT_RAM_ATTR struct {
|
static EXT_RAM_ATTR struct {
|
||||||
int bar_gap, bar_width, bar_border;
|
int bar_gap, bar_width, bar_border;
|
||||||
bool rotate;
|
bool rotate;
|
||||||
struct {
|
struct bar_s {
|
||||||
int current, max;
|
int current, max;
|
||||||
int limit;
|
int limit;
|
||||||
} bars[MAX_BARS];
|
} bars[MAX_BARS];
|
||||||
float spectrum_scale;
|
float spectrum_scale;
|
||||||
int n, col, row, height, width, border, style, max;
|
int n, col, row, height, width, border, style, max;
|
||||||
enum { VISU_BLANK, VISU_VUMETER, VISU_SPECTRUM, VISU_WAVEFORM } mode;
|
enum { VISU_BLANK, VISU_VUMETER = 0x01, VISU_SPECTRUM = 0x02, VISU_WAVEFORM } mode;
|
||||||
int speed, wake;
|
|
||||||
float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN];
|
|
||||||
struct {
|
struct {
|
||||||
u8_t *frame;
|
u8_t *frame;
|
||||||
int width;
|
int width;
|
||||||
@@ -191,6 +198,18 @@ static EXT_RAM_ATTR struct {
|
|||||||
} back;
|
} back;
|
||||||
} visu;
|
} visu;
|
||||||
|
|
||||||
|
static EXT_RAM_ATTR struct {
|
||||||
|
float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN];
|
||||||
|
int levels[2];
|
||||||
|
} meters;
|
||||||
|
|
||||||
|
static EXT_RAM_ATTR struct {
|
||||||
|
int mode;
|
||||||
|
int max;
|
||||||
|
u16_t config;
|
||||||
|
struct bar_s bars[MAX_BARS] ;
|
||||||
|
} led_visu;
|
||||||
|
|
||||||
extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start");
|
extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start");
|
||||||
|
|
||||||
#define ANIM_NONE 0x00
|
#define ANIM_NONE 0x00
|
||||||
@@ -211,7 +230,7 @@ static bool (*display_bus_chain)(void *from, enum display_bus_cmd_e cmd);
|
|||||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
static void server(in_addr_t ip, u16_t hport, u16_t cport);
|
static void server(in_addr_t ip, u16_t hport, u16_t cport);
|
||||||
static void sendSETD(u16_t width, u16_t height);
|
static void sendSETD(u16_t width, u16_t height, u16_t led_config);
|
||||||
static void sendANIC(u8_t code);
|
static void sendANIC(u8_t code);
|
||||||
static bool handler(u8_t *data, int len);
|
static bool handler(u8_t *data, int len);
|
||||||
static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd);
|
static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd);
|
||||||
@@ -222,8 +241,12 @@ static void grfs_handler(u8_t *data, int len);
|
|||||||
static void grfg_handler(u8_t *data, int len);
|
static void grfg_handler(u8_t *data, int len);
|
||||||
static void grfa_handler(u8_t *data, int len);
|
static void grfa_handler(u8_t *data, int len);
|
||||||
static void visu_handler(u8_t *data, int len);
|
static void visu_handler(u8_t *data, int len);
|
||||||
|
static void dmxt_handler(u8_t *data, int len);
|
||||||
static void displayer_task(void* arg);
|
static void displayer_task(void* arg);
|
||||||
|
|
||||||
|
// PLACEHOLDER
|
||||||
|
void *led_display = 0x1000;
|
||||||
|
|
||||||
/* scrolling undocumented information
|
/* scrolling undocumented information
|
||||||
grfs
|
grfs
|
||||||
B: screen number
|
B: screen number
|
||||||
@@ -281,19 +304,17 @@ static void displayer_task(void* arg);
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool sb_display_init(void) {
|
bool sb_displayer_init(void) {
|
||||||
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
|
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
|
||||||
static EXT_RAM_ATTR StackType_t xStack[SCROLL_STACK_SIZE] __attribute__ ((aligned (4)));
|
static EXT_RAM_ATTR StackType_t xStack[SCROLL_STACK_SIZE] __attribute__ ((aligned (4)));
|
||||||
|
|
||||||
// no display, just make sure we won't have requests
|
// no display, just make sure we won't have requests
|
||||||
if (!display || GDS_GetWidth(display) <= 0 || GDS_GetHeight(display) <= 0) {
|
if ((GDS_GetWidth(display) <= 0 || GDS_GetHeight(display) <= 0) && !led_display) {
|
||||||
LOG_INFO("no display for LMS");
|
LOG_INFO("no display or led visualizer for LMS");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// inform LMS of our screen dimensions
|
if (display) {
|
||||||
sendSETD(GDS_GetWidth(display), GDS_GetHeight(display));
|
|
||||||
|
|
||||||
// need to force height to 32 maximum
|
// need to force height to 32 maximum
|
||||||
displayer.width = GDS_GetWidth(display);
|
displayer.width = GDS_GetWidth(display);
|
||||||
displayer.height = min(GDS_GetHeight(display), SB_HEIGHT);
|
displayer.height = min(GDS_GetHeight(display), SB_HEIGHT);
|
||||||
@@ -306,14 +327,7 @@ bool sb_display_init(void) {
|
|||||||
|
|
||||||
// create visu configuration
|
// create visu configuration
|
||||||
visu.bar_gap = 1;
|
visu.bar_gap = 1;
|
||||||
visu.speed = 100;
|
|
||||||
visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8);
|
visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8);
|
||||||
dsps_fft2r_init_fc32(visu.fft, FFT_LEN);
|
|
||||||
dsps_wind_hann_f32(visu.hanning, FFT_LEN);
|
|
||||||
|
|
||||||
// create scroll management task
|
|
||||||
displayer.mutex = xSemaphoreCreateMutex();
|
|
||||||
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
|
||||||
|
|
||||||
// size scroller (width + current screen)
|
// size scroller (width + current screen)
|
||||||
scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1);
|
scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1);
|
||||||
@@ -321,6 +335,26 @@ bool sb_display_init(void) {
|
|||||||
scroller.back.frame = malloc(displayer.width * displayer.height / 8);
|
scroller.back.frame = malloc(displayer.width * displayer.height / 8);
|
||||||
scroller.frame = malloc(displayer.width * displayer.height / 8);
|
scroller.frame = malloc(displayer.width * displayer.height / 8);
|
||||||
|
|
||||||
|
// chain handlers
|
||||||
|
display_bus_chain = display_bus;
|
||||||
|
display_bus = display_bus_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (led_display) {
|
||||||
|
// PLACEHOLDER to init config
|
||||||
|
led_visu.mode = VISU_VUMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inform LMS of our screen/led dimensions
|
||||||
|
sendSETD(GDS_GetWidth(display), GDS_GetHeight(display), led_visu.config);
|
||||||
|
|
||||||
|
dsps_fft2r_init_fc32(meters.fft, FFT_LEN);
|
||||||
|
dsps_wind_hann_f32(meters.hanning, FFT_LEN);
|
||||||
|
|
||||||
|
// create displayer management task
|
||||||
|
displayer.mutex = xSemaphoreCreateMutex();
|
||||||
|
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "squeeze_displayer", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
||||||
|
|
||||||
// chain handlers
|
// chain handlers
|
||||||
slimp_handler_chain = slimp_handler;
|
slimp_handler_chain = slimp_handler;
|
||||||
slimp_handler = handler;
|
slimp_handler = handler;
|
||||||
@@ -328,10 +362,7 @@ bool sb_display_init(void) {
|
|||||||
notify_chain = server_notify;
|
notify_chain = server_notify;
|
||||||
server_notify = server;
|
server_notify = server;
|
||||||
|
|
||||||
display_bus_chain = display_bus;
|
return display != NULL;
|
||||||
display_bus = display_bus_handler;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
@@ -380,14 +411,14 @@ static void sendANIC(u8_t code) {
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Send SETD for width
|
* Send SETD for width
|
||||||
*/
|
*/
|
||||||
static void sendSETD(u16_t width, u16_t height) {
|
static void sendSETD(u16_t width, u16_t height, u16_t led_config) {
|
||||||
struct SETD_header pkt_header;
|
struct SETD_header pkt_header;
|
||||||
|
|
||||||
memset(&pkt_header, 0, sizeof(pkt_header));
|
memset(&pkt_header, 0, sizeof(pkt_header));
|
||||||
memcpy(&pkt_header.opcode, "SETD", 4);
|
memcpy(&pkt_header.opcode, "SETD", 4);
|
||||||
|
|
||||||
pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2
|
pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2
|
||||||
pkt_header.length = htonl(sizeof(pkt_header) + 4 - 8);
|
pkt_header.length = htonl(sizeof(pkt_header) + 6 - 8);
|
||||||
|
|
||||||
LOG_INFO("sending dimension %ux%u", width, height);
|
LOG_INFO("sending dimension %ux%u", width, height);
|
||||||
|
|
||||||
@@ -398,6 +429,7 @@ static void sendSETD(u16_t width, u16_t height) {
|
|||||||
send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
|
send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
|
||||||
send_packet((uint8_t *) &width, 2);
|
send_packet((uint8_t *) &width, 2);
|
||||||
send_packet((uint8_t *) &height, 2);
|
send_packet((uint8_t *) &height, 2);
|
||||||
|
send_packet((uint8_t *) &led_config, 2);
|
||||||
UNLOCK_P;
|
UNLOCK_P;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,13 +442,13 @@ static void server(in_addr_t ip, u16_t hport, u16_t cport) {
|
|||||||
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
||||||
|
|
||||||
sprintf(msg, "%s:%hu", inet_ntoa(ip), hport);
|
sprintf(msg, "%s:%hu", inet_ntoa(ip), hport);
|
||||||
if (displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg);
|
if (display && displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg);
|
||||||
displayer.dirty = true;
|
displayer.dirty = true;
|
||||||
|
|
||||||
xSemaphoreGive(displayer.mutex);
|
xSemaphoreGive(displayer.mutex);
|
||||||
|
|
||||||
// inform new LMS server of our capabilities
|
// inform new LMS server of our capabilities
|
||||||
sendSETD(displayer.width, GDS_GetHeight(display));
|
sendSETD(GDS_GetWidth(display), GDS_GetHeight(display), led_visu.config);
|
||||||
|
|
||||||
if (notify_chain) (*notify_chain)(ip, hport, cport);
|
if (notify_chain) (*notify_chain)(ip, hport, cport);
|
||||||
}
|
}
|
||||||
@@ -441,6 +473,8 @@ static bool handler(u8_t *data, int len){
|
|||||||
grfa_handler(data, len);
|
grfa_handler(data, len);
|
||||||
} else if (!strncmp((char*) data, "visu", 4)) {
|
} else if (!strncmp((char*) data, "visu", 4)) {
|
||||||
visu_handler(data, len);
|
visu_handler(data, len);
|
||||||
|
} else if (!strncmp((char*) data, "dmxt", 4)) {
|
||||||
|
dmxt_handler(data, len);
|
||||||
} else {
|
} else {
|
||||||
res = false;
|
res = false;
|
||||||
}
|
}
|
||||||
@@ -629,8 +663,7 @@ static void grfe_handler( u8_t *data, int len) {
|
|||||||
scroller.active = false;
|
scroller.active = false;
|
||||||
|
|
||||||
// full screen artwork or for small screen, full screen visu has priority
|
// full screen artwork or for small screen, full screen visu has priority
|
||||||
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) ||
|
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) || artwork.full) {
|
||||||
(artwork.enable && artwork.x == 0 && artwork.y == 0)) {
|
|
||||||
xSemaphoreGive(displayer.mutex);
|
xSemaphoreGive(displayer.mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -753,8 +786,7 @@ static void grfg_handler(u8_t *data, int len) {
|
|||||||
LOG_DEBUG("gfrg s:%hu w:%hu (len:%u)", htons(pkt->screen), htons(pkt->width), len);
|
LOG_DEBUG("gfrg s:%hu w:%hu (len:%u)", htons(pkt->screen), htons(pkt->width), len);
|
||||||
|
|
||||||
// full screen artwork or for small screen, visu has priority when full screen
|
// full screen artwork or for small screen, visu has priority when full screen
|
||||||
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) ||
|
if (((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) || artwork.full) {
|
||||||
(artwork.enable && artwork.x == 0 && artwork.y == 0)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,6 +840,7 @@ static void grfa_handler(u8_t *data, int len) {
|
|||||||
artwork.y = htons(pkt->y);
|
artwork.y = htons(pkt->y);
|
||||||
} else if (artwork.size) GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
|
} else if (artwork.size) GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
|
||||||
|
|
||||||
|
artwork.full = artwork.enable && artwork.x == 0 && artwork.y == 0;
|
||||||
LOG_INFO("gfra en:%u x:%hu, y:%hu", artwork.enable, artwork.x, artwork.y);
|
LOG_INFO("gfra en:%u x:%hu, y:%hu", artwork.enable, artwork.x, artwork.y);
|
||||||
|
|
||||||
// done in any case
|
// done in any case
|
||||||
@@ -825,6 +858,7 @@ static void grfa_handler(u8_t *data, int len) {
|
|||||||
// now use new parameters
|
// now use new parameters
|
||||||
artwork.x = htons(pkt->x);
|
artwork.x = htons(pkt->x);
|
||||||
artwork.y = htons(pkt->y);
|
artwork.y = htons(pkt->y);
|
||||||
|
artwork.full = artwork.enable && artwork.x == 0 && artwork.y == 0;
|
||||||
if (artwork.data) free(artwork.data);
|
if (artwork.data) free(artwork.data);
|
||||||
artwork.data = malloc(length);
|
artwork.data = malloc(length);
|
||||||
}
|
}
|
||||||
@@ -843,66 +877,17 @@ static void grfa_handler(u8_t *data, int len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Update visualization bars
|
* Fit spectrum into N bands and convert to dB
|
||||||
*/
|
*/
|
||||||
static void visu_update(void) {
|
void spectrum_scale(int n, struct bar_s *bars, int max, float *samples) {
|
||||||
// no update when artwork is full screen (but no need to protect against not owning the display as we are playing
|
|
||||||
if ((artwork.enable && artwork.x == 0 && artwork.y == 0) || pthread_mutex_trylock(&visu_export.mutex)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mode = visu.mode & ~VISU_ESP32;
|
|
||||||
|
|
||||||
// not enough frames
|
|
||||||
if (visu_export.level < (mode == VISU_VUMETER ? RMS_LEN : FFT_LEN) && visu_export.running) {
|
|
||||||
pthread_mutex_unlock(&visu_export.mutex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset bars for all cases first
|
|
||||||
for (int i = visu.n; --i >= 0;) visu.bars[i].current = 0;
|
|
||||||
|
|
||||||
if (visu_export.running) {
|
|
||||||
|
|
||||||
if (mode == VISU_VUMETER) {
|
|
||||||
s16_t *iptr = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
|
|
||||||
|
|
||||||
// calculate sum(L²+R²), try to not overflow at the expense of some precision
|
|
||||||
for (int i = RMS_LEN; --i >= 0;) {
|
|
||||||
visu.bars[0].current += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
|
|
||||||
iptr += BYTES_PER_FRAME / 4;
|
|
||||||
visu.bars[1].current += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
|
|
||||||
iptr += BYTES_PER_FRAME / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert to dB (1 bit remaining for getting X²/N, 60dB dynamic starting from 0dBFS = 3 bits back-off)
|
|
||||||
for (int i = visu.n; --i >= 0;) {
|
|
||||||
visu.bars[i].current = visu.max * (0.01667f*10*log10f(0.0000001f + (visu.bars[i].current >> (visu_export.gain == FIXED_ONE ? 8 : 1))) - 0.2543f);
|
|
||||||
if (visu.bars[i].current > visu.max) visu.bars[i].current = visu.max;
|
|
||||||
else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s16_t *iptr = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
|
|
||||||
// on xtensa/esp32 the floating point FFT takes 1/2 cycles of the fixed point
|
|
||||||
for (int i = 0 ; i < FFT_LEN ; i++) {
|
|
||||||
// don't normalize here, but we are due INT16_MAX and FFT_LEN / 2 / 2
|
|
||||||
visu.samples[i * 2 + 0] = (float) (*iptr + *(iptr+BYTES_PER_FRAME/4)) * visu.hanning[i];
|
|
||||||
visu.samples[i * 2 + 1] = 0;
|
|
||||||
iptr += 2 * BYTES_PER_FRAME / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// actual FFT that might be less cycle than all the crap below
|
|
||||||
dsps_fft2r_fc32_ae32(visu.samples, FFT_LEN);
|
|
||||||
dsps_bit_rev_fc32_ansi(visu.samples, FFT_LEN);
|
|
||||||
float rate = visu_export.rate;
|
float rate = visu_export.rate;
|
||||||
|
|
||||||
// now arrange the result with the number of bar and sampling rate (don't want DC)
|
// now arrange the result with the number of bar and sampling rate (don't want DC)
|
||||||
for (int i = 0, j = 1; i < visu.n && j < (FFT_LEN / 2); i++) {
|
for (int i = 0, j = 1; i < n && j < (FFT_LEN / 2); i++) {
|
||||||
float power, count;
|
float power, count;
|
||||||
|
|
||||||
// find the next point in FFT (this is real signal, so only half matters)
|
// find the next point in FFT (this is real signal, so only half matters)
|
||||||
for (count = 0, power = 0; j * visu_export.rate < visu.bars[i].limit * FFT_LEN && j < FFT_LEN / 2; j++, count += 1) {
|
for (count = 0, power = 0; j * visu_export.rate < bars[i].limit * FFT_LEN && j < FFT_LEN / 2; j++, count += 1) {
|
||||||
power += visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1];
|
power += samples[2*j] * samples[2*j] + samples[2*j+1] * samples[2*j+1];
|
||||||
}
|
}
|
||||||
// due to sample rate, we have reached the end of the available spectrum
|
// due to sample rate, we have reached the end of the available spectrum
|
||||||
if (j >= (FFT_LEN / 2)) {
|
if (j >= (FFT_LEN / 2)) {
|
||||||
@@ -910,28 +895,39 @@ static void visu_update(void) {
|
|||||||
if (count) power /= count * 2.;
|
if (count) power /= count * 2.;
|
||||||
} else if (count) {
|
} else if (count) {
|
||||||
// how much of what remains do we need to add
|
// how much of what remains do we need to add
|
||||||
float ratio = j - (visu.bars[i].limit * FFT_LEN) / rate;
|
float ratio = j - (bars[i].limit * FFT_LEN) / rate;
|
||||||
power += (visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1]) * ratio;
|
power += (samples[2*j] * samples[2*j] + samples[2*j+1] * samples[2*j+1]) * ratio;
|
||||||
|
|
||||||
// normalize accumulated data
|
// normalize accumulated data
|
||||||
power /= (count + ratio) * 2;
|
power /= (count + ratio) * 2;
|
||||||
} else {
|
} else {
|
||||||
// no data for that band (sampling rate too high), just assume same as previous one
|
// no data for that band (sampling rate too high), just assume same as previous one
|
||||||
power = (visu.samples[2*j] * visu.samples[2*j] + visu.samples[2*j+1] * visu.samples[2*j+1]) / 2.;
|
power = (samples[2*j] * samples[2*j] + samples[2*j+1] * samples[2*j+1]) / 2.;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert to dB and bars, same back-off
|
// convert to dB and bars, same back-off
|
||||||
if (power) visu.bars[i].current = visu.max * (0.01667f*10*(log10f(power) - log10f(FFT_LEN*(visu_export.gain == FIXED_ONE ? 256 : 2))) - 0.2543f);
|
bars[i].current = max * (0.01667f*10*(log10f(0.0000001f + power) - log10f(FFT_LEN*(visu_export.gain == FIXED_ONE ? 256 : 2))) - 0.2543f);
|
||||||
if (visu.bars[i].current > visu.max) visu.bars[i].current = visu.max;
|
if (bars[i].current > max) bars[i].current = max;
|
||||||
else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
|
else if (bars[i].current < 0) bars[i].current = 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we took what we want, we can release the buffer
|
/****************************************************************************************
|
||||||
visu_export.level = 0;
|
* Fit levels to max and convert to dB
|
||||||
pthread_mutex_unlock(&visu_export.mutex);
|
*/
|
||||||
|
void vu_scale(struct bar_s *bars, int max, int *levels) {
|
||||||
|
// convert to dB (1 bit remaining for getting X²/N, 60dB dynamic starting from 0dBFS = 3 bits back-off)
|
||||||
|
for (int i = 2; --i >= 0;) {
|
||||||
|
bars[i].current = max * (0.01667f*10*log10f(0.0000001f + (levels[i] >> (visu_export.gain == FIXED_ONE ? 8 : 1))) - 0.2543f);
|
||||||
|
if (bars[i].current > max) bars[i].current = max;
|
||||||
|
else if (bars[i].current < 0) bars[i].current = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* visu draw
|
||||||
|
*/
|
||||||
|
void visu_draw(void) {
|
||||||
// don't refresh screen if all max are 0 (we were are somewhat idle)
|
// don't refresh screen if all max are 0 (we were are somewhat idle)
|
||||||
int clear = 0;
|
int clear = 0;
|
||||||
for (int i = visu.n; --i >= 0;) clear = max(clear, visu.bars[i].max);
|
for (int i = visu.n; --i >= 0;) clear = max(clear, visu.bars[i].max);
|
||||||
@@ -942,9 +938,8 @@ static void visu_update(void) {
|
|||||||
GDS_DrawBitmapCBR(display, visu.back.frame, visu.back.width, displayer.height, GDS_COLOR_WHITE);
|
GDS_DrawBitmapCBR(display, visu.back.frame, visu.back.width, displayer.height, GDS_COLOR_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode != VISU_VUMETER || !visu.style) {
|
if ((visu.mode & ~VISU_ESP32) != VISU_VUMETER || !visu.style) {
|
||||||
// there is much more optimization to be done here, like not redrawing bars unless needed
|
// there is much more optimization to be done here, like not redrawing bars unless needed
|
||||||
|
|
||||||
for (int i = visu.n; --i >= 0;) {
|
for (int i = visu.n; --i >= 0;) {
|
||||||
// update maximum
|
// update maximum
|
||||||
if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
|
if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
|
||||||
@@ -988,6 +983,77 @@ static void visu_update(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Update displayer
|
||||||
|
*/
|
||||||
|
static void displayer_update(void) {
|
||||||
|
// no update when artwork is full screen and no led_strip (but no need to protect against not owning the display as we are playing
|
||||||
|
if ((artwork.full && !led_visu.mode) || pthread_mutex_trylock(&visu_export.mutex)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mode = (visu.mode & ~VISU_ESP32) | led_visu.mode;
|
||||||
|
|
||||||
|
// not enough frames
|
||||||
|
if (visu_export.level < (mode & VISU_SPECTRUM ? FFT_LEN : RMS_LEN) && visu_export.running) {
|
||||||
|
pthread_mutex_unlock(&visu_export.mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset all levels no matter what
|
||||||
|
meters.levels[0] = meters.levels[1] = 0;
|
||||||
|
memset(meters.samples, 0, sizeof(meters.samples));
|
||||||
|
|
||||||
|
if (visu_export.running) {
|
||||||
|
|
||||||
|
// calculate data for VU-meter
|
||||||
|
if (mode & VISU_VUMETER) {
|
||||||
|
s16_t *iptr = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
|
||||||
|
int *left = &meters.levels[0], *right = &meters.levels[1];
|
||||||
|
// calculate sum(L²+R²), try to not overflow at the expense of some precision
|
||||||
|
for (int i = RMS_LEN; --i >= 0;) {
|
||||||
|
*left += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
|
||||||
|
iptr += BYTES_PER_FRAME / 4;
|
||||||
|
*right += (*iptr * *iptr + (1 << (RMS_LEN_BIT - 2))) >> (RMS_LEN_BIT - 1);
|
||||||
|
iptr += BYTES_PER_FRAME / 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate data for spectrum
|
||||||
|
if (mode & VISU_SPECTRUM) {
|
||||||
|
s16_t *iptr = (s16_t*) visu_export.buffer + (BYTES_PER_FRAME / 4) - 1;
|
||||||
|
// on xtensa/esp32 the floating point FFT takes 1/2 cycles of the fixed point
|
||||||
|
for (int i = 0 ; i < FFT_LEN ; i++) {
|
||||||
|
// don't normalize here, but we are due INT16_MAX and FFT_LEN / 2 / 2
|
||||||
|
meters.samples[i * 2 + 0] = (float) (*iptr + *(iptr+BYTES_PER_FRAME/4)) * meters.hanning[i];
|
||||||
|
meters.samples[i * 2 + 1] = 0;
|
||||||
|
iptr += 2 * BYTES_PER_FRAME / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual FFT that might be less cycle than all the crap below
|
||||||
|
dsps_fft2r_fc32_ae32(meters.samples, FFT_LEN);
|
||||||
|
dsps_bit_rev_fc32_ansi(meters.samples, FFT_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// we took what we want, we can release the buffer
|
||||||
|
visu_export.level = 0;
|
||||||
|
pthread_mutex_unlock(&visu_export.mutex);
|
||||||
|
|
||||||
|
// actualize the display
|
||||||
|
if (visu.mode && !artwork.full) {
|
||||||
|
if (visu.mode & VISU_SPECTRUM) spectrum_scale(visu.n, visu.bars, visu.max, meters.samples);
|
||||||
|
else for (int i = 2; --i >= 0;) vu_scale(visu.bars, visu.max, meters.levels);
|
||||||
|
visu_draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// actualize led_vu
|
||||||
|
if (led_visu.mode) {
|
||||||
|
// PLACEHOLDER to handle led_display. you need potentially scaling of spectrum (X and Y)
|
||||||
|
// and scaling of levels (Y) and then call the
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Calculate spectrum spread
|
* Calculate spectrum spread
|
||||||
@@ -1129,7 +1195,7 @@ static void visu_handler( u8_t *data, int len) {
|
|||||||
if (visu.row < displayer.height) scroller.active = false;
|
if (visu.row < displayer.height) scroller.active = false;
|
||||||
vTaskResume(displayer.task);
|
vTaskResume(displayer.task);
|
||||||
}
|
}
|
||||||
visu.wake = 0;
|
displayer.wake = 0;
|
||||||
|
|
||||||
// reset bars maximum
|
// reset bars maximum
|
||||||
for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0;
|
for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0;
|
||||||
@@ -1144,6 +1210,25 @@ static void visu_handler( u8_t *data, int len) {
|
|||||||
xSemaphoreGive(displayer.mutex);
|
xSemaphoreGive(displayer.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Dmx style packet handler
|
||||||
|
* ToDo: make packet match dmx protocol format
|
||||||
|
*/
|
||||||
|
static void dmxt_handler( u8_t *data, int len) {
|
||||||
|
struct dmxt_packet *pkt = (struct dmxt_packet*) data;
|
||||||
|
uint16_t offset = htons(pkt->x);
|
||||||
|
uint16_t length = htons(pkt->length);
|
||||||
|
|
||||||
|
LOG_INFO("dmx packet len:%u offset:%u", length, offset);
|
||||||
|
|
||||||
|
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
||||||
|
|
||||||
|
// PLACEHOLDER
|
||||||
|
//led_vu_data(data + sizeof(struct dmxt_packet), offset, length);
|
||||||
|
|
||||||
|
xSemaphoreGive(displayer.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Scroll task
|
* Scroll task
|
||||||
* - with the addition of the visualizer, it's a bit a 2-headed beast not easy to
|
* - with the addition of the visualizer, it's a bit a 2-headed beast not easy to
|
||||||
@@ -1156,15 +1241,15 @@ static void displayer_task(void *args) {
|
|||||||
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
||||||
|
|
||||||
// suspend ourselves if nothing to do, grfg or visu will wake us up
|
// suspend ourselves if nothing to do, grfg or visu will wake us up
|
||||||
if (!scroller.active && !visu.mode) {
|
if (!scroller.active && !visu.mode && !led_visu.mode) {
|
||||||
xSemaphoreGive(displayer.mutex);
|
xSemaphoreGive(displayer.mutex);
|
||||||
vTaskSuspend(NULL);
|
vTaskSuspend(NULL);
|
||||||
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
||||||
scroller.wake = visu.wake = 0;
|
scroller.wake = displayer.wake = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// go for long sleep when either item is disabled
|
// go for long sleep when either item is disabled
|
||||||
if (!visu.mode) visu.wake = LONG_WAKE;
|
if (!visu.mode && !led_visu.mode) displayer.wake = LONG_WAKE;
|
||||||
if (!scroller.active) scroller.wake = LONG_WAKE;
|
if (!scroller.active) scroller.wake = LONG_WAKE;
|
||||||
|
|
||||||
// scroll required amount of columns (within the window)
|
// scroll required amount of columns (within the window)
|
||||||
@@ -1200,20 +1285,20 @@ static void displayer_task(void *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update visu if active
|
// update visu if active
|
||||||
if (visu.mode && visu.wake <= 0) {
|
if ((visu.mode || led_visu.mode) && displayer.wake <= 0) {
|
||||||
visu_update();
|
displayer_update();
|
||||||
visu.wake = 100;
|
displayer.wake = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to make sure we own display
|
// need to make sure we own display
|
||||||
if (displayer.owned) GDS_Update(display);
|
if (display && displayer.owned) GDS_Update(display);
|
||||||
|
|
||||||
// release semaphore and sleep what's needed
|
// release semaphore and sleep what's needed
|
||||||
xSemaphoreGive(displayer.mutex);
|
xSemaphoreGive(displayer.mutex);
|
||||||
|
|
||||||
sleep = min(visu.wake, scroller.wake);
|
sleep = min(displayer.wake, scroller.wake);
|
||||||
vTaskDelay(sleep / portTICK_PERIOD_MS);
|
vTaskDelay(sleep / portTICK_PERIOD_MS);
|
||||||
scroller.wake -= sleep;
|
scroller.wake -= sleep;
|
||||||
visu.wake -= sleep;
|
displayer.wake -= sleep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,14 +45,14 @@ uint32_t _gettime_ms_(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern void sb_controls_init(void);
|
extern void sb_controls_init(void);
|
||||||
extern bool sb_display_init(void);
|
extern bool sb_displayer_init(void);
|
||||||
|
|
||||||
u8_t custom_player_id = 12;
|
u8_t custom_player_id = 12;
|
||||||
|
|
||||||
void embedded_init(void) {
|
void embedded_init(void) {
|
||||||
mutex_create(slimp_mutex);
|
mutex_create(slimp_mutex);
|
||||||
sb_controls_init();
|
sb_controls_init();
|
||||||
custom_player_id = sb_display_init() ? 100 : 101;
|
custom_player_id = sb_displayer_init() ? 100 : 101;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16_t get_RSSI(void) {
|
u16_t get_RSSI(void) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
|
|||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
i2c_addr = adac_init(config, i2c_port_num);
|
i2c_addr = adac_init(config, i2c_port_num);
|
||||||
if (!i2c_addr) return false;
|
if (!i2c_addr) return true;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
|
ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
|
||||||
|
|
||||||
|
|||||||
@@ -152,20 +152,32 @@ static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int desc_len = mp4_desc_length(&ptr);
|
int desc_len = mp4_desc_length(&ptr);
|
||||||
info.profile = *ptr >> 3;
|
int AOT = *ptr >> 3;
|
||||||
|
info.profile = AAC_PROFILE_LC;
|
||||||
info.sampRateCore = (*ptr++ & 0x07) << 1;
|
info.sampRateCore = (*ptr++ & 0x07) << 1;
|
||||||
info.sampRateCore |= (*ptr >> 7) & 0x01;
|
info.sampRateCore |= (*ptr >> 7) & 0x01;
|
||||||
info.sampRateCore = rates[info.sampRateCore];
|
info.sampRateCore = rates[info.sampRateCore];
|
||||||
info.nChans = (*ptr++ & 0x7f) >> 3;
|
info.nChans = (*ptr & 0x7f) >> 3;
|
||||||
*channels_p = info.nChans;
|
*channels_p = info.nChans;
|
||||||
if (desc_len > 2 && ((ptr[0] << 3) | (ptr[1] >> 5)) == 0x2b7 && (ptr[1] & 0x1f) == 0x05 && (ptr[2] & 0x80)) {
|
// Note that 24 bits frequencies are not handled
|
||||||
*samplerate_p = rates[(ptr[2] & 0x78) >> 3];
|
#if AAC_ENABLE_SBR
|
||||||
LOG_WARN("AAC SBR mode activated => high CPU consumption expected, please use LMS proxy to mitigate");
|
if (AOT == 5 || AOT == 29) {
|
||||||
} else {
|
*samplerate_p = rates[((ptr[0] & 0x03) << 1) | (ptr[1] >> 7)];
|
||||||
|
LOG_WARN("AAC stream with SBR => high CPU required (use LMS proxied mode)");
|
||||||
|
} else if (desc_len > 2 && ((ptr[1] << 3) | (ptr[2] >> 5)) == 0x2b7 && (ptr[2] & 0x1f) == 0x05 && (ptr[3] & 0x80)) {
|
||||||
|
*samplerate_p = rates[(ptr[3] & 0x78) >> 3];
|
||||||
|
LOG_WARN("AAC stream with extended SBR => high CPU required (use LMS proxied mode)");
|
||||||
|
} else if (AOT == 2) {
|
||||||
*samplerate_p = info.sampRateCore;
|
*samplerate_p = info.sampRateCore;
|
||||||
|
} else {
|
||||||
|
*samplerate_p = 44100;
|
||||||
|
LOG_ERROR("AAC audio object type %d not handled", AOT);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
*samplerate_p = info.sampRateCore;
|
||||||
|
#endif
|
||||||
HAAC(a, SetRawBlockParams, a->hAac, 0, &info);
|
HAAC(a, SetRawBlockParams, a->hAac, 0, &info);
|
||||||
LOG_DEBUG("playable aac track: %u (p:%x, r:%d, c:%d)", trak, info.profile, info.sampRateCore, info.nChans);
|
LOG_DEBUG("playable aac track: %u (p:%x, r:%d, c:%d, desc_len:%d)", trak, AOT, info.sampRateCore, info.nChans, desc_len);
|
||||||
play = trak;
|
play = trak;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +442,7 @@ static decode_state helixaac_decode(void) {
|
|||||||
|
|
||||||
// not finished header parsing come back next time
|
// not finished header parsing come back next time
|
||||||
UNLOCK_S;
|
UNLOCK_S;
|
||||||
LOG_INFO("header not found yet");
|
LOG_DEBUG("header not found yet");
|
||||||
return DECODE_RUNNING;
|
return DECODE_RUNNING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,8 +357,8 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
|
|||||||
{
|
{
|
||||||
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
|
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
|
||||||
static DRAM_ATTR StackType_t xStack[OUTPUT_THREAD_STACK_SIZE] __attribute__ ((aligned (4)));
|
static DRAM_ATTR StackType_t xStack[OUTPUT_THREAD_STACK_SIZE] __attribute__ ((aligned (4)));
|
||||||
output_i2s_task = xTaskCreateStatic( (TaskFunction_t) output_thread_i2s, "output_i2s", OUTPUT_THREAD_STACK_SIZE,
|
output_i2s_task = xTaskCreateStaticPinnedToCore( (TaskFunction_t) output_thread_i2s, "output_i2s", OUTPUT_THREAD_STACK_SIZE,
|
||||||
NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, xStack, &xTaskBuffer );
|
NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, xStack, &xTaskBuffer, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// do we want stats
|
// do we want stats
|
||||||
@@ -419,7 +419,11 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
|
|||||||
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
|
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't update visu if we don't have enough data in buffer
|
||||||
|
if (silence || _buf_used(outputbuf) > outputbuf->size >> 2) {
|
||||||
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
oframes += out_frames;
|
oframes += out_frames;
|
||||||
|
|
||||||
return out_frames;
|
return out_frames;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#include "squeezelite.h"
|
#include "squeezelite.h"
|
||||||
|
|
||||||
#define VISUEXPORT_SIZE 2048
|
#define VISUEXPORT_SIZE 512
|
||||||
|
|
||||||
EXT_BSS struct visu_export_s visu_export;
|
EXT_BSS struct visu_export_s visu_export;
|
||||||
static struct visu_export_s *visu = &visu_export;
|
static struct visu_export_s *visu = &visu_export;
|
||||||
@@ -37,7 +37,7 @@ void output_visu_export(void *frames, frames_t out_frames, u32_t rate, bool sile
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not block, try to stuff data put wait for consumer to have used them
|
// do not block, try to stuff data but wait for consumer to have used them
|
||||||
if (!pthread_mutex_trylock(&visu->mutex)) {
|
if (!pthread_mutex_trylock(&visu->mutex)) {
|
||||||
// don't mix sample rates
|
// don't mix sample rates
|
||||||
if (visu->rate != rate) visu->level = 0;
|
if (visu->rate != rate) visu->level = 0;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
// make may define: PORTAUDIO, SELFPIPE, RESAMPLE, RESAMPLE_MP, VISEXPORT, GPIO, IR, DSD, LINKALL to influence build
|
// make may define: PORTAUDIO, SELFPIPE, RESAMPLE, RESAMPLE_MP, VISEXPORT, GPIO, IR, DSD, LINKALL to influence build
|
||||||
|
|
||||||
#define MAJOR_VERSION "0"
|
#define MAJOR_VERSION "1"
|
||||||
#define MINOR_VERSION "0"
|
#define MINOR_VERSION "0"
|
||||||
#define MICRO_VERSION ""
|
#define MICRO_VERSION ""
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ static const struct tas57xx_cmd_s tas57xx_cmd[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t tas57_addr;
|
static uint8_t tas57_addr;
|
||||||
int i2c_port_x;
|
|
||||||
|
|
||||||
static void dac_cmd(dac_cmd_e cmd, ...);
|
static void dac_cmd(dac_cmd_e cmd, ...);
|
||||||
static int tas57_detect(void);
|
static int tas57_detect(void);
|
||||||
@@ -69,9 +68,8 @@ static int tas57_detect(void);
|
|||||||
*/
|
*/
|
||||||
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
|
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
|
||||||
// find which TAS we are using (if any)
|
// find which TAS we are using (if any)
|
||||||
i2c_port_x = i2c_port;
|
tas57_addr = adac_init(config, i2c_port);
|
||||||
adac_init(config, i2c_port);
|
if (!tas57_addr) tas57_addr = tas57_detect();
|
||||||
tas57_addr = tas57_detect();
|
|
||||||
|
|
||||||
if (!tas57_addr) {
|
if (!tas57_addr) {
|
||||||
ESP_LOGW(TAG, "No TAS57xx detected");
|
ESP_LOGW(TAG, "No TAS57xx detected");
|
||||||
|
|||||||
@@ -33,3 +33,4 @@ extern void console_start();
|
|||||||
extern pthread_cond_t wifi_connect_suspend_cond;
|
extern pthread_cond_t wifi_connect_suspend_cond;
|
||||||
extern pthread_t wifi_connect_suspend_mutex;
|
extern pthread_t wifi_connect_suspend_mutex;
|
||||||
|
|
||||||
|
extern void (*server_notify)(in_addr_t ip, uint16_t hport, uint16_t cport);
|
||||||
@@ -1 +1 @@
|
|||||||
[{"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\test.js":"1","C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js":"2"},{"size":4775,"mtime":1608244817341,"results":"3","hashOfConfig":"4"},{"size":52524,"mtime":1608525668984,"results":"5","hashOfConfig":"4"},{"filePath":"6","messages":"7","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"8"},"1lj4yrw",{"filePath":"9","messages":"10","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\test.js",[],[],"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js",[]]
|
[{"/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js":"1"},{"size":59815,"mtime":1618633783112,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"15w6qa4","/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js",[]]
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
// {
|
|
||||||
// "label": "build",
|
|
||||||
// "command": "webpack --config webpack/webpack.prod.js",
|
|
||||||
// "type": "shell",
|
|
||||||
// "group": { "kind": "build", "isDefault": true },
|
|
||||||
// "isBackground": true
|
|
||||||
// },
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"type": "npm",
|
"type": "npm",
|
||||||
"label": "webpack: dev server",
|
"label": "webpack: dev server",
|
||||||
@@ -37,6 +29,35 @@
|
|||||||
"endsPattern": "Compiled successfully\\."
|
"endsPattern": "Compiled successfully\\."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"label": "webpack: build",
|
||||||
|
"script": "build",
|
||||||
|
"promptOnClose": true,
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "webpack",
|
||||||
|
"severity": "error",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "ERROR in (.*)",
|
||||||
|
"file": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regexp": "\\((\\d+),(\\d+)\\):(.*)",
|
||||||
|
"line": 1,
|
||||||
|
"column": 2,
|
||||||
|
"message": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": "Compiling\\.\\.\\.",
|
||||||
|
"endsPattern": "Compiled successfully\\."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,6 +338,11 @@
|
|||||||
"type": 33,
|
"type": 33,
|
||||||
"value": "D",
|
"value": "D",
|
||||||
"chg": false
|
"chg": false
|
||||||
|
},
|
||||||
|
"rel_api": {
|
||||||
|
"type": 33,
|
||||||
|
"value": "https://api.github.com/repos/sle118/squeezelite-esp32/releases",
|
||||||
|
"chg": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,14 @@
|
|||||||
"sent_time": 6245,
|
"sent_time": 6245,
|
||||||
"current_time": 6364
|
"current_time": 6364
|
||||||
}, {
|
}, {
|
||||||
"message": "[{\n\t\t\"name\":\t\"SMSL BT4.2\",\n\t\t\"rssi\":\t-129\n\t}]",
|
"message": "[{\n\t\t\"name\":\t\"BOSE BT\",\n\t\t\"rssi\":\t-129\n\t}]",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_BT",
|
||||||
|
"sent_time": 6259,
|
||||||
|
"current_time": 6364
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
"message": "[{\n\t\t\"name\":\t\"BOSE2 BT\",\n\t\t\"rssi\":\t-50\n\t}]",
|
||||||
"type": "MESSAGING_INFO",
|
"type": "MESSAGING_INFO",
|
||||||
"class": "MESSAGING_CLASS_BT",
|
"class": "MESSAGING_CLASS_BT",
|
||||||
"sent_time": 6259,
|
"sent_time": 6259,
|
||||||
|
|||||||
123
components/wifi-manager/webapp/mock/messages_ota.json
Normal file
123
components/wifi-manager/webapp/mock/messages_ota.json
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"message": "Save Success",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_SYSTEM",
|
||||||
|
"sent_time": 14084,
|
||||||
|
"current_time": 16958
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Initializing...\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 14110,
|
||||||
|
"current_time": 16958
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Starting OTA...\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 14146,
|
||||||
|
"current_time": 16958
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Downloading firmware\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 15208,
|
||||||
|
"current_time": 16958
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Download success\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 18825,
|
||||||
|
"current_time": 22219
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"New version is : mock version \",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 18849,
|
||||||
|
"current_time": 22220
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Formatting OTA partition\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 18850,
|
||||||
|
"current_time": 22220
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (1/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 22217,
|
||||||
|
"current_time": 22220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (5/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 29896,
|
||||||
|
"current_time": 29902
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (7/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 33395,
|
||||||
|
"current_time": 33408
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (9/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 36843,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash complete.\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 39463,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":5}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 41862,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":10}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 44003,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":40}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 56678,
|
||||||
|
"current_time": 65185
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":70}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 69407,
|
||||||
|
"current_time": 77858
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":95}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 80010,
|
||||||
|
"current_time": 82592
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":100}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 82084,
|
||||||
|
"current_time": 82592
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Success!\",\"ota_pct\":100}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 119255,
|
||||||
|
"current_time": 121838
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
21
components/wifi-manager/webapp/mock/messages_ota_fail.json
Normal file
21
components/wifi-manager/webapp/mock/messages_ota_fail.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[{
|
||||||
|
"message": "{\"ota_dsc\":\"Initializing...\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 119203,
|
||||||
|
"current_time": 121837
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Starting OTA...\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 119255,
|
||||||
|
"current_time": 121838
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Error: Failed to execute HTTP download. ERROR\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_ERROR",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 121954,
|
||||||
|
"current_time": 124911
|
||||||
|
}
|
||||||
|
]
|
||||||
117
components/wifi-manager/webapp/mock/messages_ota_flash.json
Normal file
117
components/wifi-manager/webapp/mock/messages_ota_flash.json
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Initializing...\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 14110,
|
||||||
|
"current_time": 16958
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Starting OTA...\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 14146,
|
||||||
|
"current_time": 16958
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"New version is : mock version \",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 18849,
|
||||||
|
"current_time": 22220
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Formatting OTA partition\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 18850,
|
||||||
|
"current_time": 22220
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (1/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 22217,
|
||||||
|
"current_time": 22220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (5/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 29896,
|
||||||
|
"current_time": 29902
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (7/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 33395,
|
||||||
|
"current_time": 33408
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash (9/11)\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 36843,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Erasing flash complete.\",\"ota_pct\":0}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 39463,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":5}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 41862,
|
||||||
|
"current_time": 54597
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":25}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 50307,
|
||||||
|
"current_time": 54598
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":30}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 52455,
|
||||||
|
"current_time": 54598
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":35}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 54603,
|
||||||
|
"current_time": 65184
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":55}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 63042,
|
||||||
|
"current_time": 65185
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":60}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 65190,
|
||||||
|
"current_time": 77858
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":95}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 80010,
|
||||||
|
"current_time": 82592
|
||||||
|
}, {
|
||||||
|
"message": "{\"ota_dsc\":\"Writing binary file\",\"ota_pct\":100}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 82084,
|
||||||
|
"current_time": 82592
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "{\"ota_dsc\":\"Success!\",\"ota_pct\":100}",
|
||||||
|
"type": "MESSAGING_INFO",
|
||||||
|
"class": "MESSAGING_CLASS_OTA",
|
||||||
|
"sent_time": 119255,
|
||||||
|
"current_time": 121838
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"project_name": "dev-server",
|
"project_name": "SqueezeAMP",
|
||||||
"version": "webpack",
|
"version": "local.500.cmake-master",
|
||||||
"recovery": 1,
|
"recovery": 1,
|
||||||
"Jack": "1",
|
"Jack": "1",
|
||||||
"Voltage": 0,
|
"Voltage": 0,
|
||||||
@@ -16,6 +16,11 @@
|
|||||||
"netmask": "255.255.255.0",
|
"netmask": "255.255.255.0",
|
||||||
"gw": "192.168.10.1",
|
"gw": "192.168.10.1",
|
||||||
"lms_cport": 9090,
|
"lms_cport": 9090,
|
||||||
"lms_port": 9000,
|
"lms_port": 9100,
|
||||||
"lms_ip": "127.0.0.1"
|
"lms_ip": "127.0.0.1",
|
||||||
|
"platform_name": "SqueezeAmp",
|
||||||
|
"mock_plugin_has_proxy": "x",
|
||||||
|
"mock_fail_fw_update":"",
|
||||||
|
"mock_fail_recovery":"",
|
||||||
|
"mock_old_recovery":""
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
"popper": "^1.0.1",
|
"popper": "^1.0.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"remixicon": "^2.5.0",
|
"remixicon": "^2.5.0",
|
||||||
|
"string-argv": "^0.3.1",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^20.0.0",
|
||||||
"svgo": "^1.3.2",
|
"svgo": "^1.3.2",
|
||||||
"webpack-icons-installer": "^2.0.0"
|
"webpack-icons-installer": "^2.0.0"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="d-flex flex-column">
|
||||||
<div style="display:none">
|
<div style="display:none">
|
||||||
<% if (htmlWebpackPlugin.files.sprites) { %>
|
<% if (htmlWebpackPlugin.files.sprites) { %>
|
||||||
<% for (var spriteFileName in htmlWebpackPlugin.files.sprites) { %>
|
<% for (var spriteFileName in htmlWebpackPlugin.files.sprites) { %>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-primary" id="mainnav">
|
<header class="navbar navbar-expand-sm navbar-dark bg-primary sticky-top border-bottom border-dark" id="mainnav">
|
||||||
<a class="navbar-brand" id="navtitle" href="#">SqueezeESP32</a>
|
<a class="navbar-brand" id="navtitle" href="#">SqueezeESP32</a>
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
|
||||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
@@ -26,11 +26,11 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
<ul class="nav navbar-nav mr-auto">
|
<ul class="nav navbar-nav mr-auto">
|
||||||
<li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#tab-wifi">WiFi</a></li>
|
<li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#tab-wifi">WiFi</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-syslog">Status<span
|
<li class="nav-item omsg"><a class="nav-link" data-toggle="tab" href="#tab-syslog">Status<span
|
||||||
class="badge badge-pill badge-success" id="msgcnt"></span></a></li>
|
class="badge badge-pill badge-success" id="msgcnt"></span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-audio">Audio</a></li>
|
<li class="nav-item orec"><a class="nav-link" data-toggle="tab" href="#tab-cfg-audio">Audio</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-syst">System</a></li>
|
<li class="nav-item orec"><a class="nav-link" data-toggle="tab" href="#tab-cfg-syst">System</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-hw">Hardware</a></li>
|
<li class="nav-item orec"><a class="nav-link" data-toggle="tab" href="#tab-cfg-hw">Hardware</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-fw">Updates</a></li>
|
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-cfg-fw">Updates</a></li>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-nvs">NVS Editor</a></li>
|
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-nvs">NVS Editor</a></li>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="info navbar-right" style="display: inline-flex;">
|
<div class="info navbar-right" style="display: inline-flex;">
|
||||||
<svg class="recovery_element bg-primary" style="fill:orange; width:1.5rem; height: 1.5rem;">
|
<svg class="recovery_element " style="fill:orange; width:1.5rem; height: 1.5rem;">
|
||||||
<use xlink:href="#device-recover-fill"></use>
|
<use xlink:href="#device-recover-fill"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
@@ -77,68 +77,135 @@
|
|||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</header>
|
||||||
<div id="message"></div>
|
<main role="main" class="flex-grow mt-1 mb-12" style="margin-bottom: 7rem;" id="content">
|
||||||
<div id="content">
|
<!-- Button trigger modal -->
|
||||||
<div id="myTabContent" class="tab-content mt-3">
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="otadiv" tabindex="-1" role="dialog" aria-labelledby="fwProgressLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="fwProgressLabel">Upgrade Progress</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<span id="flash-status"></span>
|
||||||
|
<div class="progress" id="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||||
|
0%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="myTabContent" class="tab-content">
|
||||||
|
|
||||||
<div class="tab-pane fade" id="tab-cfg-hw"></div>
|
<div class="tab-pane fade" id="tab-cfg-hw"></div>
|
||||||
<div class="tab-pane fade" id="tab-cfg-syst"></div>
|
<div class="tab-pane fade" id="tab-cfg-syst"></div>
|
||||||
<div class="tab-pane fade" id="tab-cfg-gen"></div>
|
<div class="tab-pane fade" id="tab-cfg-gen"></div>
|
||||||
<div class="tab-pane fade" id="tab-cfg-fw">
|
<div class="tab-pane fade" id="tab-cfg-fw">
|
||||||
<div id="boot-div">
|
|
||||||
<form id="boot-form" action="/recovery.json" method="post" target="dummyframe">
|
<div class="card text-white mb-3">
|
||||||
<button id="boot-button" type="submit" class="btn btn-primary">Recovery</button>
|
<div class="card-header">Software Updates</div>
|
||||||
</form>
|
<div class="card-body">
|
||||||
</div>
|
<table class="table table-hover table-striped table-dark">
|
||||||
<h1>Check for firmware upgrade</h1>
|
|
||||||
<div class="buttons">
|
|
||||||
<input type="button" id="fwcheck" class="btn btn-info" value="Check for updates" />
|
|
||||||
</div>
|
|
||||||
<div id="searchfw" class="form-group">
|
|
||||||
<select class="custom-select" id="fwbranch">
|
|
||||||
<option selected="">Choose FW branch</option>
|
|
||||||
</select>
|
|
||||||
<input class="form-control form-control-sm" id="searchinput" type="text"
|
|
||||||
placeholder="search releases" id="inputSmall">
|
|
||||||
</div>
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Firmware version</th>
|
<th class="border-bottom-0 pb-0" scope="col">Version</th>
|
||||||
<th scope="col">Release date/time</th>
|
<th class="border-bottom-0 pb-0" scope="col">Date/Time</th>
|
||||||
<th scope="col">HW platform</th>
|
<th class="border-bottom-0 pb-0" scope="col">Platform</th>
|
||||||
<th scope="col">IDF version</th>
|
<th class="border-bottom-0 pb-0" scope="col">Branch</th>
|
||||||
<th scope="col">Branch</th>
|
<th class="border-bottom-0 pb-0" scope="col">Bit Depth</th>
|
||||||
<th scope="col">Flash this FW</th>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="border-top-0 pt-0" scope="col"><input class="form-control-sm upSrch"
|
||||||
|
id="svrs" type="text" placeholder="search releases"></th>
|
||||||
|
<th class="border-top-0 pt-0" scope="col"></th>
|
||||||
|
<th class="border-top-0 pt-0" scope="col"><input class="form-control-sm upSrch"
|
||||||
|
id="splf" type="text" placeholder="search platform"></th>
|
||||||
|
<th class="border-top-0 pt-0" scope="col"><select class="form-control-sm upSrch"
|
||||||
|
id="fwbranch">
|
||||||
|
<option selected="">Choose FW branch</option>
|
||||||
|
</select>
|
||||||
|
<th class="border-top-0 pt-0" scope="col"><input class="form-control-sm upSrch"
|
||||||
|
id="bits" type="text" placeholder="search bit depth"></th>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="releaseTable">
|
<tbody id="rTable"></tbody>
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
<h2>Firmware URL:</h2>
|
|
||||||
<textarea id="fwurl" maxlength="1000"></textarea>
|
<div class="form-group row">
|
||||||
<div class="buttons">
|
<div class="col-auto">
|
||||||
<input type="button" id="flash" class="btn btn-danger" value="Flash!" /><span
|
<button type="button" id="chkUpdates" class="btn btn-info btn-sm">Check for
|
||||||
id="flash-status"></span>
|
updates</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="uploaddiv" class="recovery_element">
|
<label class="col-auto col-form-label" for="fw-url-input">Firmware URL</label>
|
||||||
<p>OR</p>
|
<div class="col">
|
||||||
<div class="form-group">
|
<input type="text" class="form-control"
|
||||||
|
placeholder="select entry from list or enter known url" id="fw-url-input">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="button" id='start-flash' data-toggle="modal" data-target="#uCnfrm"
|
||||||
|
class="btn btn-warning btn-sm" style="display: none;">Flash Firmware</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="btn-warning ota_element" type="submit" onclick="handleReboot('recovery');" >Recovery</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal" id="uCnfrm">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Firmware Flash</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Flash URL <span id="selectedFWURL" class="text-break"></span> to device?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer ">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-warning" data-dismiss="modal"
|
||||||
|
onclick="hFlash();">Ok</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card text-white mb-3" >
|
||||||
|
<div class="card-header">Local Firmware Upload</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="uploaddiv" class="form-group row">
|
||||||
|
<label for="flashfilename" class="col-auto col-form-label">Local File</label>
|
||||||
|
<div class="col">
|
||||||
<input type="file" class="form-control-file" id="flashfilename" aria-describedby="fileHelp">
|
<input type="file" class="form-control-file" id="flashfilename" aria-describedby="fileHelp">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="button" class="btn btn-danger" id="fwUpload">Upload!</button>
|
<button type="button" class="btn btn-danger" id="fwUpload">Upload!</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="otadiv" class="recovery_element">
|
|
||||||
<div class="progress" id="progress">
|
|
||||||
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100"
|
|
||||||
style="width:0%">
|
|
||||||
0%
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane fade" id="tab-nvs">
|
<div class="tab-pane fade" id="tab-nvs">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -151,11 +218,8 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div id="boot-div">
|
<button button id="reboot-button" class="btn btn-primary" style="float:right" type="submit" onclick="handleReboot('reboot');" >Reboot</button>
|
||||||
<form id="reboot-form" action="/reboot.json" method="post" target="dummyframe">
|
|
||||||
<button id="reboot-button" type="submit" class="btn btn-primary">Reboot</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<input id="save-nvs" type="button" class="btn btn-success" value="Commit">
|
<input id="save-nvs" type="button" class="btn btn-success" value="Commit">
|
||||||
<input id="save-as-nvs" type="button" class="btn btn-success" value="Download config">
|
<input id="save-as-nvs" type="button" class="btn btn-success" value="Download config">
|
||||||
<input id="load-nvs" type="button" class="btn btn-success" value="Load File">
|
<input id="load-nvs" type="button" class="btn btn-success" value="Load File">
|
||||||
@@ -165,7 +229,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="tab-cfg-audio">
|
<div class="tab-pane fade" id="tab-cfg-audio">
|
||||||
<div class="card text-white bg-primary mb-3">
|
<div class="card text-white mb-3">
|
||||||
<div class="card-header">Usage Templates</div>
|
<div class="card-header">Usage Templates</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -197,8 +261,8 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
<input class="form-check-input" type="checkbox" id="disable-squeezelite"
|
<input class="form-check-input" type="checkbox" id="disable-squeezelite" value=""
|
||||||
value="" checked="">
|
checked="">
|
||||||
Disable Squeezelite
|
Disable Squeezelite
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -219,7 +283,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade active show" id="tab-wifi">
|
<div class="tab-pane fade active show" id="tab-wifi">
|
||||||
<div class="card text-white bg-primary mb-3">
|
<div class="card text-white mb-3">
|
||||||
<div class="card-header">WiFi Status</div>
|
<div class="card-header">WiFi Status</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
@@ -246,7 +310,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Disconnect from network? After disconnecting, the system won't be accessible from the current address and will expose itself as access point name <span id="apName"></span> with password <span id="apPass"></span> </p>
|
<p>Disconnect from network? After disconnecting, the system won't be accessible from
|
||||||
|
the current address and will expose itself as access point name <span
|
||||||
|
id="apName"></span> with password <span id="apPass"></span> </p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer connecting-success connecting-status">
|
<div class="modal-footer connecting-success connecting-status">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
@@ -260,7 +326,8 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title connecting connecting-init connecting-fail">Connect to WiFi</h5>
|
<h5 class="modal-title connecting connecting-init connecting-fail">Connect to WiFi
|
||||||
|
</h5>
|
||||||
<h5 class="modal-title connecting-status connecting-success">Status</h5>
|
<h5 class="modal-title connecting-status connecting-success">Status</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
@@ -268,9 +335,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<fieldset class="connecting-init connecting-fail">
|
<fieldset class="connecting-init connecting-fail">
|
||||||
<div class="form-group"><label for="manual_ssid">Wifi Name</label><input
|
<div class="form-group"><label for="manual_ssid">Wifi Name</label><input type="text"
|
||||||
type="text" class="form-control" placeholder="Enter Name"
|
class="form-control" placeholder="Enter Name" id="manual_ssid"></div>
|
||||||
id="manual_ssid"></div>
|
|
||||||
<div class="form-group"><label for="manual_pwd">Password</label><input
|
<div class="form-group"><label for="manual_pwd">Password</label><input
|
||||||
type="password" class="form-control" placeholder="Enter Name"
|
type="password" class="form-control" placeholder="Enter Name"
|
||||||
id="manual_pwd"></div>
|
id="manual_pwd"></div>
|
||||||
@@ -292,24 +358,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="connect-fail" class="connecting-fail">
|
<div id="connect-fail" class="connecting-fail">
|
||||||
<h3 class="text-error">Connection failed</h3>
|
<h3 class="text-error">Connection failed</h3>
|
||||||
<p >Please double-check wifi password if any and make sure the access point has good signal.</p>
|
<p>Please double-check wifi password if any and make sure the access point has
|
||||||
|
good signal.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer ">
|
<div class="modal-footer ">
|
||||||
<button type="button" class="btn btn-secondary connecting-init connecting-fail connecting" data-dismiss="modal">Close</button>
|
<button type="button"
|
||||||
<button type="button" id="btnJoin" class="btn btn-primary connecting-init connecting-fail"
|
class="btn btn-secondary connecting-init connecting-fail connecting"
|
||||||
|
data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" id="btnJoin"
|
||||||
|
class="btn btn-primary connecting-init connecting-fail"
|
||||||
onclick="handleConnect();">Join</button>
|
onclick="handleConnect();">Join</button>
|
||||||
<button type="button" class="connecting btn btn-primary" disabled>
|
<button type="button" class="connecting btn btn-primary" disabled>
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status"
|
||||||
|
aria-hidden="true"></span>
|
||||||
<span class="sr-only">Connecting...</span></button>
|
<span class="sr-only">Connecting...</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer connecting-success connecting-status">
|
<div class="modal-footer connecting-success connecting-status justify-content-between" style=""><button type="button"
|
||||||
<button type="button" class="btn btn-warning" data-toggle="modal"
|
class="btn btn-primary" data-dismiss="modal">Ok</button><button type="button" class="btn btn-danger"
|
||||||
data-dismiss="modal" data-target="#WiFiDisconnectConfirm">Disconnect</button>
|
data-toggle="modal" data-dismiss="modal" data-target="#WiFiDisconnectConfirm">Disconnect</button>
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -339,7 +408,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card border-primary mb-3">
|
<div class="card border-primary mb-3" id="pins" style="display: none;">
|
||||||
<div class="card-header">Pin Assignments</div>
|
<div class="card-header">Pin Assignments</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
@@ -378,7 +447,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- syslog -->
|
<!-- syslog -->
|
||||||
<div class="tab-pane fade " id="tab-credits">
|
<div class="tab-pane fade " id="tab-credits">
|
||||||
<div class="card text-white bg-primary mb-3">
|
<div class="card text-white mb-3">
|
||||||
<div class="card-header">Credits</div>
|
<div class="card-header">Credits</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p><strong><a
|
<p><strong><a
|
||||||
@@ -404,7 +473,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card text-white bg-primary mb-3">
|
<div class="card text-white mb-3">
|
||||||
<div class="card-header">Extras/Overrides</div>
|
<div class="card-header">Extras/Overrides</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -424,16 +493,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- credits -->
|
<!-- credits -->
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<div class="fixed-bottom d-flex justify-content-between border-top border-dark p-3 bg-primary">
|
||||||
|
<span class="text-center" id="foot-fw"></span><button class="btn-warning ota_element " id="reboot_nav"
|
||||||
|
type="submit" onclick="handleReboot('reboot');" style="display: none;">Reboot</button>
|
||||||
|
<button class="btn-warning recovery_element" id="reboot_ota_nav" type="submit" onclick="handleReboot('reboot_ota');"
|
||||||
|
style="display: none;">Exit Recovery</button><span class="text-center" id="foot-wifi"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
<footer class="footer bg-primary text.primary">
|
|
||||||
<button class="btn-warning ota_element" id="reboot_nav" type="submit" onclick="handleReboot(false);"
|
|
||||||
style="display: none;">Reboot</button>
|
|
||||||
<button class="btn-danger recovery_element" id="reboot_ota_nav" type="submit" onclick="handleReboot(true);"
|
|
||||||
style="display: none;">Exit Recovery</button><br>
|
|
||||||
<span id="foot-fw"></span><span id="foot-wifi"></span>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -19,6 +19,6 @@ import '../node_modules/remixicon/icons/Media/play-circle-fill.svg';
|
|||||||
import '../node_modules/remixicon/icons/Media/pause-circle-fill.svg';
|
import '../node_modules/remixicon/icons/Media/pause-circle-fill.svg';
|
||||||
import '../node_modules/remixicon/icons/System/lock-fill.svg';
|
import '../node_modules/remixicon/icons/System/lock-fill.svg';
|
||||||
import '../node_modules/remixicon/icons/System/lock-unlock-fill.svg';
|
import '../node_modules/remixicon/icons/System/lock-unlock-fill.svg';
|
||||||
|
import './assets/images/favicon-32x32.png';
|
||||||
import './js/custom.js';
|
import './js/custom.js';
|
||||||
// <%= `<svg><use xlink:href="#${htmlWebpackPlugin.files.sprites.svg.defs.symbol[s].id}"></use></svg>` %>
|
// <%= `<svg><use xlink:href="#${htmlWebpackPlugin.files.sprites.svg.defs.symbol[s].id}"></use></svg>` %>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,34 @@
|
|||||||
.features:hover {
|
// .features:hover {
|
||||||
cursor: pointer;
|
// cursor: pointer;
|
||||||
animation: jello-horizontal 1.2s;
|
// animation: jello-horizontal 1.2s;
|
||||||
}
|
// }
|
||||||
|
|
||||||
@keyframes jello-horizontal {
|
// @keyframes jello-horizontal {
|
||||||
0% {
|
// 0% {
|
||||||
transform: scale3d(1, 1, 1);
|
// transform: scale3d(1, 1, 1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
30% {
|
// 30% {
|
||||||
transform: scale3d(1.25, .75, 1);
|
// transform: scale3d(1.25, .75, 1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
40% {
|
// 40% {
|
||||||
transform: scale3d(.75, 1.25, 1);
|
// transform: scale3d(.75, 1.25, 1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
50% {
|
// 50% {
|
||||||
transform: scale3d(1.15, .85, 1);
|
// transform: scale3d(1.15, .85, 1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
65% {
|
// 65% {
|
||||||
transform: scale3d(.95, 1.05, 1);
|
// transform: scale3d(.95, 1.05, 1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
75% {
|
// 75% {
|
||||||
transform: scale3d(1.05, .95, 1);
|
// transform: scale3d(1.05, .95, 1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
100% {
|
// 100% {
|
||||||
transform: scale3d(1, 1, 1);
|
// transform: scale3d(1, 1, 1);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,348 +1,348 @@
|
|||||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
// /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
/* Document
|
// /* Document
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Correct the line height in all browsers.
|
// * 1. Correct the line height in all browsers.
|
||||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
// * 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
html {
|
// html {
|
||||||
line-height: 1.15; /* 1 */
|
// line-height: 1.15; /* 1 */
|
||||||
-webkit-text-size-adjust: 100%; /* 2 */
|
// -webkit-text-size-adjust: 100%; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Sections
|
// /* Sections
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the margin in all browsers.
|
// * Remove the margin in all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
body {
|
// body {
|
||||||
margin: 0;
|
// margin: 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Render the `main` element consistently in IE.
|
// * Render the `main` element consistently in IE.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
main {
|
// main {
|
||||||
display: block;
|
// display: block;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Correct the font size and margin on `h1` elements within `section` and
|
// * Correct the font size and margin on `h1` elements within `section` and
|
||||||
* `article` contexts in Chrome, Firefox, and Safari.
|
// * `article` contexts in Chrome, Firefox, and Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
h1 {
|
// h1 {
|
||||||
font-size: 2em;
|
// font-size: 2em;
|
||||||
margin: .67em 0;
|
// margin: .67em 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Grouping content
|
// /* Grouping content
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Add the correct box sizing in Firefox.
|
// * 1. Add the correct box sizing in Firefox.
|
||||||
* 2. Show the overflow in Edge and IE.
|
// * 2. Show the overflow in Edge and IE.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
hr {
|
// hr {
|
||||||
box-sizing: content-box; /* 1 */
|
// box-sizing: content-box; /* 1 */
|
||||||
height: 0; /* 1 */
|
// height: 0; /* 1 */
|
||||||
overflow: visible; /* 2 */
|
// overflow: visible; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
// * 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
* 2. Correct the odd `em` font sizing in all browsers.
|
// * 2. Correct the odd `em` font sizing in all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
pre {
|
// pre {
|
||||||
font-family: monospace; /* 1 */
|
// font-family: monospace; /* 1 */
|
||||||
font-size: 1em; /* 2 */
|
// font-size: 1em; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Text-level semantics
|
// /* Text-level semantics
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the gray background on active links in IE 10.
|
// * Remove the gray background on active links in IE 10.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
a {
|
// a {
|
||||||
background-color: transparent;
|
// background-color: transparent;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Remove the bottom border in Chrome 57-
|
// * 1. Remove the bottom border in Chrome 57-
|
||||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
// * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
abbr[title] {
|
// abbr[title] {
|
||||||
border-bottom: none; /* 1 */
|
// border-bottom: none; /* 1 */
|
||||||
text-decoration: underline; /* 2 */
|
// text-decoration: underline; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
// * Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
b,
|
// b,
|
||||||
strong {
|
// strong {
|
||||||
font-weight: bolder;
|
// font-weight: bolder;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
// * 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
* 2. Correct the odd `em` font sizing in all browsers.
|
// * 2. Correct the odd `em` font sizing in all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
code,
|
// code,
|
||||||
kbd,
|
// kbd,
|
||||||
samp {
|
// samp {
|
||||||
font-family: monospace; /* 1 */
|
// font-family: monospace; /* 1 */
|
||||||
font-size: 1em; /* 2 */
|
// font-size: 1em; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Add the correct font size in all browsers.
|
// * Add the correct font size in all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
small {
|
// small {
|
||||||
font-size: 80%;
|
// font-size: 80%;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
// * Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
* all browsers.
|
// * all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
sub,
|
// sub,
|
||||||
sup {
|
// sup {
|
||||||
font-size: 75%;
|
// font-size: 75%;
|
||||||
line-height: 0;
|
// line-height: 0;
|
||||||
position: relative;
|
// position: relative;
|
||||||
vertical-align: baseline;
|
// vertical-align: baseline;
|
||||||
}
|
// }
|
||||||
|
|
||||||
sub {
|
// sub {
|
||||||
bottom: -.25em;
|
// bottom: -.25em;
|
||||||
}
|
// }
|
||||||
|
|
||||||
sup {
|
// sup {
|
||||||
top: -.5em;
|
// top: -.5em;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Embedded content
|
// /* Embedded content
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the border on images inside links in IE 10.
|
// * Remove the border on images inside links in IE 10.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
img {
|
// img {
|
||||||
border-style: none;
|
// border-style: none;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Forms
|
// /* Forms
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Change the font styles in all browsers.
|
// * 1. Change the font styles in all browsers.
|
||||||
* 2. Remove the margin in Firefox and Safari.
|
// * 2. Remove the margin in Firefox and Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
button,
|
// button,
|
||||||
input,
|
// input,
|
||||||
optgroup,
|
// optgroup,
|
||||||
select,
|
// select,
|
||||||
textarea {
|
// textarea {
|
||||||
font-family: inherit; /* 1 */
|
// font-family: inherit; /* 1 */
|
||||||
font-size: 100%; /* 1 */
|
// font-size: 100%; /* 1 */
|
||||||
line-height: 1.15; /* 1 */
|
// line-height: 1.15; /* 1 */
|
||||||
margin: 0; /* 2 */
|
// margin: 0; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Show the overflow in IE.
|
// * Show the overflow in IE.
|
||||||
* 1. Show the overflow in Edge.
|
// * 1. Show the overflow in Edge.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
button,
|
// button,
|
||||||
input { /* 1 */
|
// input { /* 1 */
|
||||||
overflow: visible;
|
// overflow: visible;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
// * Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
* 1. Remove the inheritance of text transform in Firefox.
|
// * 1. Remove the inheritance of text transform in Firefox.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
button,
|
// button,
|
||||||
select { /* 1 */
|
// select { /* 1 */
|
||||||
text-transform: none;
|
// text-transform: none;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Correct the inability to style clickable types in iOS and Safari.
|
// * Correct the inability to style clickable types in iOS and Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
button,
|
// button,
|
||||||
[type="button"],
|
// [type="button"],
|
||||||
[type="reset"],
|
// [type="reset"],
|
||||||
[type="submit"] {
|
// [type="submit"] {
|
||||||
-webkit-appearance: button;
|
// -webkit-appearance: button;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the inner border and padding in Firefox.
|
// * Remove the inner border and padding in Firefox.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
button::-moz-focus-inner,
|
// button::-moz-focus-inner,
|
||||||
[type="button"]::-moz-focus-inner,
|
// [type="button"]::-moz-focus-inner,
|
||||||
[type="reset"]::-moz-focus-inner,
|
// [type="reset"]::-moz-focus-inner,
|
||||||
[type="submit"]::-moz-focus-inner {
|
// [type="submit"]::-moz-focus-inner {
|
||||||
border-style: none;
|
// border-style: none;
|
||||||
padding: 0;
|
// padding: 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Restore the focus styles unset by the previous rule.
|
// * Restore the focus styles unset by the previous rule.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
button:-moz-focusring,
|
// button:-moz-focusring,
|
||||||
[type="button"]:-moz-focusring,
|
// [type="button"]:-moz-focusring,
|
||||||
[type="reset"]:-moz-focusring,
|
// [type="reset"]:-moz-focusring,
|
||||||
[type="submit"]:-moz-focusring {
|
// [type="submit"]:-moz-focusring {
|
||||||
outline: 1px dotted ButtonText;
|
// outline: 1px dotted ButtonText;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Correct the padding in Firefox.
|
// * Correct the padding in Firefox.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
fieldset {
|
// fieldset {
|
||||||
padding: .35em .75em .625em;
|
// padding: .35em .75em .625em;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Correct the text wrapping in Edge and IE.
|
// * 1. Correct the text wrapping in Edge and IE.
|
||||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
// * 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
* 3. Remove the padding so developers are not caught out when they zero out
|
// * 3. Remove the padding so developers are not caught out when they zero out
|
||||||
* `fieldset` elements in all browsers.
|
// * `fieldset` elements in all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
legend {
|
// legend {
|
||||||
box-sizing: border-box; /* 1 */
|
// box-sizing: border-box; /* 1 */
|
||||||
color: inherit; /* 2 */
|
// color: inherit; /* 2 */
|
||||||
display: table; /* 1 */
|
// display: table; /* 1 */
|
||||||
max-width: 100%; /* 1 */
|
// max-width: 100%; /* 1 */
|
||||||
padding: 0; /* 3 */
|
// padding: 0; /* 3 */
|
||||||
white-space: normal; /* 1 */
|
// white-space: normal; /* 1 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
// * Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
progress {
|
// progress {
|
||||||
vertical-align: baseline;
|
// vertical-align: baseline;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the default vertical scrollbar in IE 10+.
|
// * Remove the default vertical scrollbar in IE 10+.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
textarea {
|
// textarea {
|
||||||
overflow: auto;
|
// overflow: auto;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Add the correct box sizing in IE 10.
|
// * 1. Add the correct box sizing in IE 10.
|
||||||
* 2. Remove the padding in IE 10.
|
// * 2. Remove the padding in IE 10.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
[type="checkbox"],
|
// [type="checkbox"],
|
||||||
[type="radio"] {
|
// [type="radio"] {
|
||||||
box-sizing: border-box; /* 1 */
|
// box-sizing: border-box; /* 1 */
|
||||||
padding: 0; /* 2 */
|
// padding: 0; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
// * Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
[type="number"]::-webkit-inner-spin-button,
|
// [type="number"]::-webkit-inner-spin-button,
|
||||||
[type="number"]::-webkit-outer-spin-button {
|
// [type="number"]::-webkit-outer-spin-button {
|
||||||
height: auto;
|
// height: auto;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Correct the odd appearance in Chrome and Safari.
|
// * 1. Correct the odd appearance in Chrome and Safari.
|
||||||
* 2. Correct the outline style in Safari.
|
// * 2. Correct the outline style in Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
[type="search"] {
|
// [type="search"] {
|
||||||
-webkit-appearance: textfield; /* 1 */
|
// -webkit-appearance: textfield; /* 1 */
|
||||||
outline-offset: -2px; /* 2 */
|
// outline-offset: -2px; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Remove the inner padding in Chrome and Safari on macOS.
|
// * Remove the inner padding in Chrome and Safari on macOS.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
[type="search"]::-webkit-search-decoration {
|
// [type="search"]::-webkit-search-decoration {
|
||||||
-webkit-appearance: none;
|
// -webkit-appearance: none;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
// * 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
* 2. Change font properties to `inherit` in Safari.
|
// * 2. Change font properties to `inherit` in Safari.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
::-webkit-file-upload-button {
|
// ::-webkit-file-upload-button {
|
||||||
-webkit-appearance: button; /* 1 */
|
// -webkit-appearance: button; /* 1 */
|
||||||
font: inherit; /* 2 */
|
// font: inherit; /* 2 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Interactive
|
// /* Interactive
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/*
|
// /*
|
||||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
// * Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
details {
|
// details {
|
||||||
display: block;
|
// display: block;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
// /*
|
||||||
* Add the correct display in all browsers.
|
// * Add the correct display in all browsers.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
summary {
|
// summary {
|
||||||
display: list-item;
|
// display: list-item;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Misc
|
// /* Misc
|
||||||
========================================================================== */
|
// ========================================================================== */
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Add the correct display in IE 10+.
|
// * Add the correct display in IE 10+.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
template {
|
// template {
|
||||||
display: none;
|
// display: none;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Add the correct display in IE 10.
|
// * Add the correct display in IE 10.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
[hidden] {
|
// [hidden] {
|
||||||
display: none;
|
// display: none;
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
|
|
||||||
/* Device = Most of the Smartphones Mobiles (Portrait) */
|
// /* Device = Most of the Smartphones Mobiles (Portrait) */
|
||||||
$screen-xxs-min: 320px;
|
// $screen-xxs-min: 320px;
|
||||||
$screen-xxs-max: 480px;
|
// $screen-xxs-max: 480px;
|
||||||
|
|
||||||
/* Device = Low Resolution Tablets, Mobiles (Landscape) */
|
// /* Device = Low Resolution Tablets, Mobiles (Landscape) */
|
||||||
$screen-xs-min: 481px;
|
// $screen-xs-min: 481px;
|
||||||
$screen-xs-max: 767px;
|
// $screen-xs-max: 767px;
|
||||||
|
|
||||||
/* Device = Tablets, Ipads (portrait) */
|
// /* Device = Tablets, Ipads (portrait) */
|
||||||
$screen-sm-min: 768px;
|
// $screen-sm-min: 768px;
|
||||||
$screen-sm-max: 1024px;
|
// $screen-sm-max: 1024px;
|
||||||
|
|
||||||
/* Device = Laptops, Desktops */
|
// /* Device = Laptops, Desktops */
|
||||||
$screen-md-min: 1025px;
|
// $screen-md-min: 1025px;
|
||||||
$screen-md-max: 1280px;
|
// $screen-md-max: 1280px;
|
||||||
|
|
||||||
/* Device = Desktops */
|
// /* Device = Desktops */
|
||||||
$screen-lg-min: 1281px;
|
// $screen-lg-min: 1281px;
|
||||||
$screen-lg-max: 1440px;
|
// $screen-lg-max: 1440px;
|
||||||
|
|
||||||
/* Higher Resolution Screens */
|
// /* Higher Resolution Screens */
|
||||||
$screen-xlg-min: 1441px;
|
// $screen-xlg-min: 1441px;
|
||||||
$screen-xlg-max: 2560px;
|
// $screen-xlg-max: 2560px;
|
||||||
|
|||||||
@@ -1,4 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.border-bottom {
|
||||||
|
border-width:3px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-top {
|
||||||
|
border-width:3px !important;
|
||||||
|
}
|
||||||
|
tr.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.rebooting {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
td.value {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
#boot-div {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
/* body {
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom:50px;
|
margin-bottom:50px;
|
||||||
@@ -136,7 +160,7 @@ h3 {
|
|||||||
.fr {
|
.fr {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
} */
|
||||||
/* .w0 {
|
/* .w0 {
|
||||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABzUlEQVRIS+WUTShEURTH3zyRhjQ+8hWxmCJMoSzEwsbCgi1LZRYW9pONptiwka9iI81CWFpYaEqNMkVKmpWN1IhYKN9ZDL/z3p3mxZh5g9X4168799xz/vPefedeLeuVC+3gdTgc07CsmCQ2DI2gg21Jci30wSpGt/CeghickTsHPVACDkgqp67rPgpO4E0ZZMIj7OHhxSvPtEyomcVDeFXJv+EZNvEsNa01rZfAuSUhThR2wU+ObJkbyhRNMMDaDIThBqy1MdZ3wAPawqfFC2Lj0Ab5kpBGxdAJs9TeW72ITUhCPZMjFYwwbwXpnkwlDzOIx50yXwP5c0MeggHGanNqSDqqBqQ7/Kxvg2zHAfMN8IE8uZhYO6eBnBXGKnOakLWfaQZ9jMRjSPXhZUuC5A9JjVFpKkeNSVVA0Tq8KJN0yFl4gilqbW2tm+SQKoybXIG8jcT34RSsh1Byt6iVg2ZLlRCg6JpROqEDpFheXZ5S9rcLFsl5YJwHad+MVA5y13w5lRY5oRsKjdm/Vz/7LR86zG+5wr+9NX+iOowjEO+aELEic+lv1ILppeUPosRst6QduTANgnE2mC+BnYswI1VwfYzCCL9dZij7pWkf6UeSTYAuE/QAAAAASUVORK5CYII=') no-repeat left top;
|
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABzUlEQVRIS+WUTShEURTH3zyRhjQ+8hWxmCJMoSzEwsbCgi1LZRYW9pONptiwka9iI81CWFpYaEqNMkVKmpWN1IhYKN9ZDL/z3p3mxZh5g9X4168799xz/vPefedeLeuVC+3gdTgc07CsmCQ2DI2gg21Jci30wSpGt/CeghickTsHPVACDkgqp67rPgpO4E0ZZMIj7OHhxSvPtEyomcVDeFXJv+EZNvEsNa01rZfAuSUhThR2wU+ObJkbyhRNMMDaDIThBqy1MdZ3wAPawqfFC2Lj0Ab5kpBGxdAJs9TeW72ITUhCPZMjFYwwbwXpnkwlDzOIx50yXwP5c0MeggHGanNqSDqqBqQ7/Kxvg2zHAfMN8IE8uZhYO6eBnBXGKnOakLWfaQZ9jMRjSPXhZUuC5A9JjVFpKkeNSVVA0Tq8KJN0yFl4gilqbW2tm+SQKoybXIG8jcT34RSsh1Byt6iVg2ZLlRCg6JpROqEDpFheXZ5S9rcLFsl5YJwHad+MVA5y13w5lRY5oRsKjdm/Vz/7LR86zG+5wr+9NX+iOowjEO+aELEic+lv1ILppeUPosRst6QduTANgnE2mC+BnYswI1VwfYzCCL9dZij7pWkf6UeSTYAuE/QAAAAASUVORK5CYII=') no-repeat left top;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
@@ -158,7 +182,7 @@ h3 {
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
} */
|
} */
|
||||||
/* SpinKit is licensed under the MIT License. Copyright (c) 2015 Tobias Ahlin */
|
/* SpinKit is licensed under the MIT License. Copyright (c) 2015 Tobias Ahlin */
|
||||||
.spinner {
|
/* .spinner {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
|
||||||
@@ -198,11 +222,11 @@ h3 {
|
|||||||
transform: scale(1.0);
|
transform: scale(1.0);
|
||||||
-webkit-transform: scale(1.0);
|
-webkit-transform: scale(1.0);
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
/* end of SpinKit */
|
/* end of SpinKit */
|
||||||
|
|
||||||
/* daduke stuff */
|
/* daduke stuff */
|
||||||
input[type='text'], input[type='password'], textarea, select, option {
|
/* input[type='text'], input[type='password'], textarea, select, option {
|
||||||
background: #999;
|
background: #999;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
@@ -230,8 +254,8 @@ input[type='text'], input[type='password'], textarea, select, option {
|
|||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
background-color: #f00;
|
background-color: #f00;
|
||||||
}
|
} */
|
||||||
|
/*
|
||||||
.custom-switch .custom-control-label::after {
|
.custom-switch .custom-control-label::after {
|
||||||
top: calc(0.25rem + 2px);
|
top: calc(0.25rem + 2px);
|
||||||
left: calc(-2.25rem + 2px);
|
left: calc(-2.25rem + 2px);
|
||||||
@@ -247,8 +271,8 @@ input[type='text'], input[type='password'], textarea, select, option {
|
|||||||
.custom-switch .custom-control-label::after {
|
.custom-switch .custom-control-label::after {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
/*
|
||||||
.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
|
.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
|
||||||
background-color: #0f0;
|
background-color: #0f0;
|
||||||
}
|
}
|
||||||
@@ -308,7 +332,7 @@ input, textarea {
|
|||||||
span#flash-status {
|
span#flash-status {
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
}
|
} */
|
||||||
|
|
||||||
/* #info {
|
/* #info {
|
||||||
padding-top: 7px;
|
padding-top: 7px;
|
||||||
@@ -343,7 +367,7 @@ ul#navbar {
|
|||||||
border-top: 1px solid black;
|
border-top: 1px solid black;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.footer {
|
/* .footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -351,8 +375,8 @@ ul#navbar {
|
|||||||
background-color: #555;
|
background-color: #555;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
} */
|
||||||
|
/*
|
||||||
.sl {
|
.sl {
|
||||||
background-color: #053c1e;
|
background-color: #053c1e;
|
||||||
}
|
}
|
||||||
@@ -360,13 +384,9 @@ ul#navbar {
|
|||||||
background-color: #3c0505;
|
background-color: #3c0505;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.value {
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#boot-div {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
div#message {
|
div#message {
|
||||||
display: none;
|
display: none;
|
||||||
color: #000;
|
color: #000;
|
||||||
@@ -378,23 +398,21 @@ div#message {
|
|||||||
width:20em;
|
width:20em;
|
||||||
height:4em;
|
height:4em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-left: -10em; /*set to a negative number 1/2 of your width*/
|
margin-left: -10em;
|
||||||
margin-top: -2em; /*set to a negative number 1/2 of your height*/
|
margin-top: -2em;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0px 5px 2px -5px rgba(255, 255, 255, 0.5) inset, 0px 10px 20px -5px rgba(255, 255, 255, 0.1) inset, 0 0px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 1px rgba(0, 0, 0, 0.12), 0 1px 10px 0 rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 5px 2px -5px rgba(255, 255, 255, 0.5) inset, 0px 10px 20px -5px rgba(255, 255, 255, 0.1) inset, 0 0px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 1px rgba(0, 0, 0, 0.12), 0 1px 10px 0 rgba(0, 0, 0, 0.3);
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
} */
|
||||||
|
/*
|
||||||
tr.hide {
|
*/
|
||||||
display: none;
|
/*
|
||||||
}
|
|
||||||
|
|
||||||
#searchfw {
|
#searchfw {
|
||||||
float: right;
|
float: right;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
} */
|
||||||
|
|
||||||
button#updateAP {
|
/* button#updateAP {
|
||||||
float: right;
|
float: right;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
} */
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
<div id="allIcons"></div>
|
<div id="allIcons"></div>
|
||||||
|
|
||||||
<div class="card border-primary mb-3">
|
<div class="card border-primary mb-3">
|
||||||
<div class="card-header">Status Variables</div>
|
<div class="card-header">Status Variables</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/favicon-32x32.png BINARY)
|
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/favicon-32x32.png BINARY)
|
||||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/index.html.gz BINARY)
|
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/index.html.gz BINARY)
|
||||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.0b6890.bundle.js.gz BINARY)
|
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.df6830.bundle.js.gz BINARY)
|
||||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.0b6890.bundle.js.gz BINARY)
|
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.df6830.bundle.js.gz BINARY)
|
||||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.0b6890.bundle.js.gz BINARY)
|
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.df6830.bundle.js.gz BINARY)
|
||||||
|
|||||||
@@ -4,31 +4,31 @@ extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_s
|
|||||||
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
|
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_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_html_gz_end[] asm("_binary_index_html_gz_end");
|
||||||
extern const uint8_t _index_0b6890_bundle_js_gz_start[] asm("_binary_index_0b6890_bundle_js_gz_start");
|
extern const uint8_t _index_df6830_bundle_js_gz_start[] asm("_binary_index_df6830_bundle_js_gz_start");
|
||||||
extern const uint8_t _index_0b6890_bundle_js_gz_end[] asm("_binary_index_0b6890_bundle_js_gz_end");
|
extern const uint8_t _index_df6830_bundle_js_gz_end[] asm("_binary_index_df6830_bundle_js_gz_end");
|
||||||
extern const uint8_t _node_modules_0b6890_bundle_js_gz_start[] asm("_binary_node_modules_0b6890_bundle_js_gz_start");
|
extern const uint8_t _node_modules_df6830_bundle_js_gz_start[] asm("_binary_node_modules_df6830_bundle_js_gz_start");
|
||||||
extern const uint8_t _node_modules_0b6890_bundle_js_gz_end[] asm("_binary_node_modules_0b6890_bundle_js_gz_end");
|
extern const uint8_t _node_modules_df6830_bundle_js_gz_end[] asm("_binary_node_modules_df6830_bundle_js_gz_end");
|
||||||
extern const uint8_t _runtime_0b6890_bundle_js_gz_start[] asm("_binary_runtime_0b6890_bundle_js_gz_start");
|
extern const uint8_t _runtime_df6830_bundle_js_gz_start[] asm("_binary_runtime_df6830_bundle_js_gz_start");
|
||||||
extern const uint8_t _runtime_0b6890_bundle_js_gz_end[] asm("_binary_runtime_0b6890_bundle_js_gz_end");
|
extern const uint8_t _runtime_df6830_bundle_js_gz_end[] asm("_binary_runtime_df6830_bundle_js_gz_end");
|
||||||
const char * resource_lookups[] = {
|
const char * resource_lookups[] = {
|
||||||
"/dist/favicon-32x32.png",
|
"/favicon-32x32.png",
|
||||||
"/dist/index.html.gz",
|
"/index.html.gz",
|
||||||
"/js/index.0b6890.bundle.js.gz",
|
"/js/index.df6830.bundle.js.gz",
|
||||||
"/js/node-modules.0b6890.bundle.js.gz",
|
"/js/node-modules.df6830.bundle.js.gz",
|
||||||
"/js/runtime.0b6890.bundle.js.gz",
|
"/js/runtime.df6830.bundle.js.gz",
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
const uint8_t * resource_map_start[] = {
|
const uint8_t * resource_map_start[] = {
|
||||||
_favicon_32x32_png_start,
|
_favicon_32x32_png_start,
|
||||||
_index_html_gz_start,
|
_index_html_gz_start,
|
||||||
_index_0b6890_bundle_js_gz_start,
|
_index_df6830_bundle_js_gz_start,
|
||||||
_node_modules_0b6890_bundle_js_gz_start,
|
_node_modules_df6830_bundle_js_gz_start,
|
||||||
_runtime_0b6890_bundle_js_gz_start
|
_runtime_df6830_bundle_js_gz_start
|
||||||
};
|
};
|
||||||
const uint8_t * resource_map_end[] = {
|
const uint8_t * resource_map_end[] = {
|
||||||
_favicon_32x32_png_end,
|
_favicon_32x32_png_end,
|
||||||
_index_html_gz_end,
|
_index_html_gz_end,
|
||||||
_index_0b6890_bundle_js_gz_end,
|
_index_df6830_bundle_js_gz_end,
|
||||||
_node_modules_0b6890_bundle_js_gz_end,
|
_node_modules_df6830_bundle_js_gz_end,
|
||||||
_runtime_0b6890_bundle_js_gz_end
|
_runtime_df6830_bundle_js_gz_end
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
/***********************************
|
/***********************************
|
||||||
webpack_headers
|
webpack_headers
|
||||||
Hash: 0b6890f4337e767921f7
|
Hash: df683065b9a62ef5a0ce
|
||||||
Version: webpack 4.46.0
|
Version: webpack 4.46.0
|
||||||
Time: 273269ms
|
Time: 2739ms
|
||||||
Built at: 2021-04-03 1:28:56
|
Built at: 26.04.2021 07:00:49
|
||||||
Asset Size Chunks Chunk Names
|
Asset Size Chunks Chunk Names
|
||||||
./js/index.0b6890.bundle.js 231 KiB 0 [emitted] [immutable] index
|
./js/index.df6830.bundle.js 232 KiB 0 [emitted] [immutable] index
|
||||||
./js/index.0b6890.bundle.js.br 31.5 KiB [emitted]
|
./js/index.df6830.bundle.js.br 32.5 KiB [emitted]
|
||||||
./js/index.0b6890.bundle.js.gz 41.1 KiB [emitted]
|
./js/index.df6830.bundle.js.gz 41.9 KiB [emitted]
|
||||||
./js/node-modules.0b6890.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
|
./js/node-modules.df6830.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
|
||||||
./js/node-modules.0b6890.bundle.js.br 76.3 KiB [emitted]
|
./js/node-modules.df6830.bundle.js.br 76.3 KiB [emitted]
|
||||||
./js/node-modules.0b6890.bundle.js.gz 88.7 KiB [emitted]
|
./js/node-modules.df6830.bundle.js.gz 88.7 KiB [emitted]
|
||||||
./js/runtime.0b6890.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
|
./js/runtime.df6830.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
|
||||||
./js/runtime.0b6890.bundle.js.br 644 bytes [emitted]
|
./js/runtime.df6830.bundle.js.br 644 bytes [emitted]
|
||||||
./js/runtime.0b6890.bundle.js.gz 722 bytes [emitted]
|
./js/runtime.df6830.bundle.js.gz 722 bytes [emitted]
|
||||||
favicon-32x32.png 634 bytes [emitted]
|
favicon-32x32.png 634 bytes [emitted]
|
||||||
index.html 19.5 KiB [emitted]
|
index.html 21.7 KiB [emitted]
|
||||||
index.html.br 4.48 KiB [emitted]
|
index.html.br 4.74 KiB [emitted]
|
||||||
index.html.gz 5.46 KiB [emitted]
|
index.html.gz 5.75 KiB [emitted]
|
||||||
sprite.svg 4.4 KiB [emitted]
|
sprite.svg 4.4 KiB [emitted]
|
||||||
sprite.svg.br 912 bytes [emitted]
|
sprite.svg.br 898 bytes [emitted]
|
||||||
Entrypoint index [big] = ./js/runtime.0b6890.bundle.js ./js/node-modules.0b6890.bundle.js ./js/index.0b6890.bundle.js
|
Entrypoint index [big] = ./js/runtime.df6830.bundle.js ./js/node-modules.df6830.bundle.js ./js/index.df6830.bundle.js
|
||||||
[6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [built]
|
[6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [built]
|
||||||
[11] ./src/sass/main.scss 1.55 KiB {0} [built]
|
[11] ./src/sass/main.scss 1.55 KiB {0} [built]
|
||||||
[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 323 bytes {1} [built]
|
[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 323 bytes {1} [built]
|
||||||
@@ -35,22 +35,22 @@ Entrypoint index [big] = ./js/runtime.0b6890.bundle.js ./js/node-modules.0b6890.
|
|||||||
[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 329 bytes {1} [built]
|
[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 329 bytes {1} [built]
|
||||||
[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 319 bytes {1} [built]
|
[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 319 bytes {1} [built]
|
||||||
[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 335 bytes {1} [built]
|
[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 335 bytes {1} [built]
|
||||||
[37] ./src/index.ts + 1 modules 53.3 KiB {0} [built]
|
[38] ./src/index.ts + 1 modules 62.5 KiB {0} [built]
|
||||||
| ./src/index.ts 1.36 KiB [built]
|
| ./src/index.ts 1.4 KiB [built]
|
||||||
| ./src/js/custom.js 51.8 KiB [built]
|
| ./src/js/custom.js 61 KiB [built]
|
||||||
+ 23 hidden modules
|
+ 24 hidden modules
|
||||||
|
|
||||||
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
|
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
|
||||||
This can impact web performance.
|
This can impact web performance.
|
||||||
Assets:
|
Assets:
|
||||||
./js/node-modules.0b6890.bundle.js (266 KiB)
|
./js/node-modules.df6830.bundle.js (266 KiB)
|
||||||
|
|
||||||
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
|
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
|
||||||
Entrypoints:
|
Entrypoints:
|
||||||
index (499 KiB)
|
index (499 KiB)
|
||||||
./js/runtime.0b6890.bundle.js
|
./js/runtime.df6830.bundle.js
|
||||||
./js/node-modules.0b6890.bundle.js
|
./js/node-modules.df6830.bundle.js
|
||||||
./js/index.0b6890.bundle.js
|
./js/index.df6830.bundle.js
|
||||||
|
|
||||||
|
|
||||||
WARNING in webpack performance recommendations:
|
WARNING in webpack performance recommendations:
|
||||||
@@ -58,9 +58,9 @@ You can limit the size of your bundles by using import() or require.ensure to la
|
|||||||
For more info visit https://webpack.js.org/guides/code-splitting/
|
For more info visit https://webpack.js.org/guides/code-splitting/
|
||||||
Child html-webpack-plugin for "index.html":
|
Child html-webpack-plugin for "index.html":
|
||||||
Asset Size Chunks Chunk Names
|
Asset Size Chunks Chunk Names
|
||||||
index.html 556 KiB 0
|
index.html 559 KiB 0
|
||||||
Entrypoint undefined = index.html
|
Entrypoint undefined = index.html
|
||||||
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 20.3 KiB {0} [built]
|
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 22.9 KiB {0} [built]
|
||||||
[1] ./node_modules/lodash/lodash.js 531 KiB {0} [built]
|
[1] ./node_modules/lodash/lodash.js 531 KiB {0} [built]
|
||||||
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
|
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
|
||||||
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
|
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.br
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.br
vendored
Normal file
Binary file not shown.
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.gz
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.gz
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.df6830.bundle.js.br
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.df6830.bundle.js.br
vendored
Normal file
Binary file not shown.
BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.df6830.bundle.js.gz
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.df6830.bundle.js.gz
vendored
Normal file
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,8 @@ const ESLintPlugin = require('eslint-webpack-plugin');
|
|||||||
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
|
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
|
||||||
// Linting
|
// Linting
|
||||||
const TSLintPlugin = require('tslint-webpack-plugin');
|
const TSLintPlugin = require('tslint-webpack-plugin');
|
||||||
|
const ImageminPlugin = require('imagemin-webpack-plugin').default;
|
||||||
|
const imageminMozjpeg = require('imagemin-mozjpeg');
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -97,9 +99,10 @@ module.exports = {
|
|||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
options: {
|
options: {
|
||||||
// publicPath: '../',
|
// publicPath: '../',
|
||||||
name: './assets/images/' + '[name].[ext]',
|
//name: './assets/images/' + '[name].[ext]',
|
||||||
limit: 10000,
|
limit: 10000,
|
||||||
publicPath: '../'
|
//limit:false,
|
||||||
|
//publicPath: '../'
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -177,6 +180,22 @@ module.exports = {
|
|||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
|
new ImageminPlugin({
|
||||||
|
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||||
|
// lossLess gif compressor
|
||||||
|
gifsicle: {
|
||||||
|
optimizationLevel: 9
|
||||||
|
},
|
||||||
|
// lossy png compressor, remove for default lossLess
|
||||||
|
pngquant: ({
|
||||||
|
quality: '75'
|
||||||
|
}),
|
||||||
|
// lossy jpg compressor
|
||||||
|
plugins: [imageminMozjpeg({
|
||||||
|
quality: '75'
|
||||||
|
})],
|
||||||
|
destination: './webpack',
|
||||||
|
}),
|
||||||
new ESLintPlugin({
|
new ESLintPlugin({
|
||||||
cache: true,
|
cache: true,
|
||||||
ignore: true,
|
ignore: true,
|
||||||
@@ -203,6 +222,7 @@ module.exports = {
|
|||||||
useShortDoctype : true
|
useShortDoctype : true
|
||||||
},
|
},
|
||||||
favicon: "./src/assets/images/favicon-32x32.png",
|
favicon: "./src/assets/images/favicon-32x32.png",
|
||||||
|
|
||||||
excludeChunks: ['test'],
|
excludeChunks: ['test'],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,24 @@ const HtmlWebPackPlugin = require('html-webpack-plugin');
|
|||||||
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
|
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
|
||||||
const { Command } = require('commander');
|
const { Command } = require('commander');
|
||||||
let cmdLines= { };
|
let cmdLines= { };
|
||||||
|
var { parseArgsStringToArgv } = require('string-argv');
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
messages: require("../mock/messages.json"),
|
messages: require("../mock/messages.json"),
|
||||||
messagequeue: require("../mock/messages.json"),
|
messagequeue: require("../mock/messages.json"),
|
||||||
|
message_queue_sequence: [],
|
||||||
|
status_queue_sequence:[],
|
||||||
|
message_queue_sequence_post_empty: null,
|
||||||
|
status_queue_sequence_post_empty: null,
|
||||||
commands: require("../mock/commands.json"),
|
commands: require("../mock/commands.json"),
|
||||||
scan: require("../mock/scan.json"),
|
scan: require("../mock/scan.json"),
|
||||||
ap: require("../mock/ap.json"),
|
ap: require("../mock/ap.json"),
|
||||||
config: require("../mock/config.json"),
|
config: require("../mock/config.json"),
|
||||||
statusdefinition: require("../mock/statusdefinition.json"),
|
statusdefinition: require("../mock/statusdefinition.json"),
|
||||||
status: require("../mock/status.json")
|
status: require("../mock/status.json"),
|
||||||
|
messages_ota_fail: require("../mock/messages_ota_fail.json"),
|
||||||
|
messages_ota_flash: require("../mock/messages_ota_flash.json"),
|
||||||
|
messages_ota: require("../mock/messages_ota.json")
|
||||||
};
|
};
|
||||||
const messagingTypes= {
|
const messagingTypes= {
|
||||||
MESSAGING_INFO : 'MESSAGING_INFO',
|
MESSAGING_INFO : 'MESSAGING_INFO',
|
||||||
@@ -32,6 +41,7 @@ const messagingClass= {
|
|||||||
MESSAGING_CLASS_BT: 'MESSAGING_CLASS_BT'
|
MESSAGING_CLASS_BT: 'MESSAGING_CLASS_BT'
|
||||||
} ;
|
} ;
|
||||||
function requeueMessages(){
|
function requeueMessages(){
|
||||||
|
data.messagequeue = [];
|
||||||
data.messagequeue.push(...data.messages);
|
data.messagequeue.push(...data.messages);
|
||||||
console.log(`Re-queued ${data.messages.length} messages. Total queue length is: ${data.messagequeue.length}`);
|
console.log(`Re-queued ${data.messages.length} messages. Total queue length is: ${data.messagequeue.length}`);
|
||||||
}
|
}
|
||||||
@@ -70,7 +80,7 @@ for(const cmdIdx in data.commands.commands){
|
|||||||
cmd: new Command(),
|
cmd: new Command(),
|
||||||
};
|
};
|
||||||
cmdLines[cmd.name].cmd
|
cmdLines[cmd.name].cmd
|
||||||
.storeOptionsAsProperties(false)
|
.storeOptionsAsProperties(true)
|
||||||
.name(cmd.name)
|
.name(cmd.name)
|
||||||
.exitOverride();
|
.exitOverride();
|
||||||
for(const argIdx in cmd.argtable){
|
for(const argIdx in cmd.argtable){
|
||||||
@@ -107,6 +117,8 @@ module.exports = merge(common, {
|
|||||||
port: 9100,
|
port: 9100,
|
||||||
host: '127.0.0.1',//your ip address
|
host: '127.0.0.1',//your ip address
|
||||||
disableHostCheck: true,
|
disableHostCheck: true,
|
||||||
|
headers: {'Access-Control-Allow-Origin': '*',
|
||||||
|
'Accept-Encoding': 'identity'},
|
||||||
overlay: true,
|
overlay: true,
|
||||||
|
|
||||||
before: function(app) {
|
before: function(app) {
|
||||||
@@ -114,22 +126,96 @@ module.exports = merge(common, {
|
|||||||
app.use(bodyParser.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
|
app.use(bodyParser.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
|
||||||
app.get('/ap.json', function(req, res) { res.json( data.ap ); });
|
app.get('/ap.json', function(req, res) { res.json( data.ap ); });
|
||||||
app.get('/scan.json', function(req, res) { res.json( data.scan ); });
|
app.get('/scan.json', function(req, res) { res.json( data.scan ); });
|
||||||
app.get('/config.json', function(req, res) { res.json( data.config ); });
|
app.get('/config.json', function(req, res) {
|
||||||
app.get('/status.json', function(req, res) { res.json( data.status ); });
|
if(data.status.recovery==1 && (data.status.mock_old_recovery??'')!==''){
|
||||||
|
res.json( data.config.config );
|
||||||
|
console.log('Mock old recovery - return config structure without gpio');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.json( data.config );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/status.json', function(req, res) {
|
||||||
|
if(data.status_queue_sequence.length>0){
|
||||||
|
const curstatus = JSON.parse(data.status_queue_sequence_queue_sequence.shift());
|
||||||
|
data.status.ota_pct=curstatus.ota_pct??0;
|
||||||
|
data.status.ota_dsc=curstatus.ota_dsc??'';
|
||||||
|
console.log(`Mock firmware update @${data.status.ota_pct}%, ${data.status.ota_dsc}`)
|
||||||
|
}
|
||||||
|
else if (data.status_queue_sequence_post_empty){
|
||||||
|
data.status_queue_sequence_post_emptyy();
|
||||||
|
console.log(`Mock old firmware update: simulating a restart`);
|
||||||
|
data.status_queue_sequence_post_empty = null;
|
||||||
|
}
|
||||||
|
else if(data.status.ota_pct!=undefined || data.status.ota_dsc!=undefined) {
|
||||||
|
if(data.status.ota_pct!=undefined) delete data.status.ota_pct;
|
||||||
|
if(data.status.ota_dsc!=undefined) delete data.status.ota_dsc;
|
||||||
|
}
|
||||||
|
if(data.status.message) delete data.status.message;
|
||||||
|
if(data.status.recovery==1 && (data.status.mock_old_recovery??'')!==''){
|
||||||
|
if(data.message_queue_sequence.length>0){
|
||||||
|
|
||||||
|
const msgpayload = JSON.parse(data.message_queue_sequence.shift());
|
||||||
|
data.status.message = msgpayload.message??'';
|
||||||
|
console.log(`Mocking recovery, setting status message to ${data.status.message}`)
|
||||||
|
}
|
||||||
|
else if (data.message_queue_sequence_post_empty){
|
||||||
|
data.message_queue_sequence_post_empty();
|
||||||
|
data.message_queue_sequence_post_empty = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.json( data.status );
|
||||||
|
});
|
||||||
|
app.get('/plugins/SqueezeESP32/firmware/-99', function(req, res) {
|
||||||
|
let has_proxy= data.status.mock_plugin_has_proxy ?? 'n';
|
||||||
|
const statusCode='xy'.includes((has_proxy).toLowerCase())?200:500;
|
||||||
|
console.log(`Checking if plugin has proxy enabled with option mock_plugin_has_proxy = ${data.status.mock_plugin_has_proxy}. Returning status ${statusCode} `);
|
||||||
|
res.status(statusCode ).json();
|
||||||
|
});
|
||||||
app.get('/messages.json', function(req, res) {
|
app.get('/messages.json', function(req, res) {
|
||||||
|
if(data.status.recovery==1 && (data.status.mock_old_recovery??'')!==''){
|
||||||
|
console.log('Mocking old recovery, with no commands backend' );
|
||||||
|
res.status(404).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(data.message_queue_sequence.length>0){
|
||||||
|
data.messagequeue.push(data.message_queue_sequence.shift());
|
||||||
|
}
|
||||||
|
else if (data.message_queue_sequence_post_empty){
|
||||||
|
data.message_queue_sequence_post_empty();
|
||||||
|
data.message_queue_sequence_post_empty = null;
|
||||||
|
}
|
||||||
res.json( data.messagequeue ) ;
|
res.json( data.messagequeue ) ;
|
||||||
data.messagequeue=[];
|
data.messagequeue=[];
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/statusdefinition.json', function(req, res) { res.json( data.statusdefinition ); });
|
app.get('/statusdefinition.json', function(req, res) { res.json( data.statusdefinition ); });
|
||||||
app.get('/commands.json', function(req, res) { res.json( data.commands ); });
|
app.get('/commands.json', function(req, res) {
|
||||||
|
if(data.status.recovery==1 && (data.status.mock_old_recovery??'')!==''){
|
||||||
|
console.log('Mocking old recovery, with no commands backend' );
|
||||||
|
res.status(404).end();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.json( data.commands );
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
app.post('/commands.json', function(req, res) {
|
app.post('/commands.json', function(req, res) {
|
||||||
|
if(data.status.recovery==1 && (data.status.mock_old_recovery??'')!==''){
|
||||||
|
console.log('Mocking old recovery, with no commands backend' );
|
||||||
|
res.status(404).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log(req.body.command);
|
console.log(req.body.command);
|
||||||
try {
|
try {
|
||||||
const cmdName=req.body.command.split(" ")[0];
|
const cmdName=req.body.command.split(" ")[0];
|
||||||
const args=('node '+req.body.command).split(" ");
|
const args=parseArgsStringToArgv(req.body.command,'node ');
|
||||||
let cmd=cmdLines[cmdName].cmd;
|
let cmd=cmdLines[cmdName].cmd;
|
||||||
if(cmd){
|
if(cmd){
|
||||||
|
for (const property in cmd.opts()) {
|
||||||
|
delete cmd[property];
|
||||||
|
}
|
||||||
cmd.parse(args);
|
cmd.parse(args);
|
||||||
const msg=`Received Options: ${JSON.stringify(cmd.opts())}\n`;
|
const msg=`Received Options: ${JSON.stringify(cmd.opts())}\n`;
|
||||||
console.log('Options: ', cmd.opts());
|
console.log('Options: ', cmd.opts());
|
||||||
@@ -142,10 +228,15 @@ module.exports = merge(common, {
|
|||||||
res.json( { 'Result' : 'Success' } );
|
res.json( { 'Result' : 'Success' } );
|
||||||
});
|
});
|
||||||
app.post('/config.json', function(req, res) {
|
app.post('/config.json', function(req, res) {
|
||||||
console.log(req.body);
|
var fwurl='';
|
||||||
|
console.log(`Processing config.json with request body: ${req.body}`);
|
||||||
console.log(data.config);
|
console.log(data.config);
|
||||||
for (const property in req.body.config) {
|
for (const property in req.body.config) {
|
||||||
console.log(`${property}: ${req.body.config[property].value}`);
|
console.log(`${property}: ${req.body.config[property].value}`);
|
||||||
|
if(property=='fwurl'){
|
||||||
|
fwurl=req.body.config[property].value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
if(data.config[property]=== undefined){
|
if(data.config[property]=== undefined){
|
||||||
console.log(`Added config value ${property} [${req.body.config[property].value}]`);
|
console.log(`Added config value ${property} [${req.body.config[property].value}]`);
|
||||||
data.config[property] = {value: req.body.config[property].value};
|
data.config[property] = {value: req.body.config[property].value};
|
||||||
@@ -155,7 +246,35 @@ module.exports = merge(common, {
|
|||||||
data.config[property].value=req.body.config[property].value;
|
data.config[property].value=req.body.config[property].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
res.json( {} );
|
res.json( {} );
|
||||||
|
if(fwurl!=='' ){
|
||||||
|
const ota_msg_list= ((data.status.mock_fail_fw_update ?? '')!=='')?data.messages_ota_fail:data.messages_ota;
|
||||||
|
if(data.status.recovery!=1) {
|
||||||
|
// we're not yet in recovery. Simulate reboot to recovery
|
||||||
|
data.status.recovery=1;
|
||||||
|
if((data.status.mock_old_recovery??'')===''){
|
||||||
|
// older recovery partitions possibly aren't
|
||||||
|
// sending messages
|
||||||
|
requeueMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
var targetQueue='message_queue_sequence';
|
||||||
|
var targetPostEmpty='message_queue_sequence_post_empty';
|
||||||
|
if((data.status.mock_old_recovery??'')!==''){
|
||||||
|
console.log('Mocking old firmware flashing mechanism. Starting!');
|
||||||
|
targetQueue='status_queue_sequence';
|
||||||
|
targetPostEmpty='status_queue_sequence_post_empty';
|
||||||
|
}
|
||||||
|
console.log(`queuing ${ota_msg_list.length} ota messages `);
|
||||||
|
data[targetQueue].push(...ota_msg_list);
|
||||||
|
data[targetPostEmpty] = function(){
|
||||||
|
data.status.recovery=0;
|
||||||
|
requeueMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
app.post('/status.json', function(req, res) {
|
app.post('/status.json', function(req, res) {
|
||||||
|
|
||||||
@@ -199,14 +318,34 @@ module.exports = merge(common, {
|
|||||||
requeueMessages();
|
requeueMessages();
|
||||||
});
|
});
|
||||||
app.post('/recovery.json', function(req, res) {
|
app.post('/recovery.json', function(req, res) {
|
||||||
|
if((data.status.mock_fail_recovery ?? '')!==''){
|
||||||
|
res.status(404).end();
|
||||||
|
}
|
||||||
|
else {
|
||||||
data.status.recovery=1;
|
data.status.recovery=1;
|
||||||
requeueMessages();
|
requeueMessages();
|
||||||
res.json( { } );
|
res.json( { } );
|
||||||
|
}
|
||||||
});
|
});
|
||||||
app.post('/flash.json', function(req, res) {
|
app.post('/flash.json', function(req, res) {
|
||||||
|
|
||||||
if(data.status.recovery>0){
|
if(data.status.recovery>0){
|
||||||
|
if((data.status.mock_fail_fw_update ?? '')!=='' || (data.status.mock_old_recovery??'')!==''){
|
||||||
|
console.log('Old recovery mock, or fw fail requested' );
|
||||||
|
res.status(404).end();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`queuing ${data.messages_ota_flash.length} flash ota messages `);
|
||||||
|
data.message_queue_sequence.push(...data.messages_ota_flash);
|
||||||
|
data.message_queue_sequence_post_empty = function(){
|
||||||
|
data.status.recovery=0;
|
||||||
|
requeueMessages();
|
||||||
|
|
||||||
|
}
|
||||||
res.json({});
|
res.json({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).end();
|
res.status(404).end();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
const CompressionPlugin = require('compression-webpack-plugin');
|
const CompressionPlugin = require('compression-webpack-plugin');
|
||||||
const ImageminPlugin = require('imagemin-webpack-plugin').default;
|
|
||||||
const imageminMozjpeg = require('imagemin-mozjpeg');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
var WebpackOnBuildPlugin = require('on-build-webpack');
|
var WebpackOnBuildPlugin = require('on-build-webpack');
|
||||||
@@ -83,21 +82,7 @@ module.exports = merge(common, {
|
|||||||
threshold: 100,
|
threshold: 100,
|
||||||
minRatio: 0.8,
|
minRatio: 0.8,
|
||||||
}),
|
}),
|
||||||
new ImageminPlugin({
|
|
||||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
|
||||||
// lossLess gif compressor
|
|
||||||
gifsicle: {
|
|
||||||
optimizationLevel: 9
|
|
||||||
},
|
|
||||||
// lossy png compressor, remove for default lossLess
|
|
||||||
pngquant: ({
|
|
||||||
quality: '75'
|
|
||||||
}),
|
|
||||||
// lossy jpg compressor
|
|
||||||
plugins: [imageminMozjpeg({
|
|
||||||
quality: '75'
|
|
||||||
})]
|
|
||||||
}),
|
|
||||||
// new FaviconsWebpackPlugin({
|
// new FaviconsWebpackPlugin({
|
||||||
// // Your source logo
|
// // Your source logo
|
||||||
// logo: './src/assets/images/200px-ControllerAppIcon.png',
|
// logo: './src/assets/images/200px-ControllerAppIcon.png',
|
||||||
@@ -125,15 +110,34 @@ module.exports = merge(common, {
|
|||||||
// }
|
// }
|
||||||
// }),
|
// }),
|
||||||
new WebpackOnBuildPlugin(function(stats) {
|
new WebpackOnBuildPlugin(function(stats) {
|
||||||
|
|
||||||
var getDirectories = function (src, callback) {
|
var getDirectories = function (src, callback) {
|
||||||
glob(`${src}/**/*(*.gz|favicon-32x32.png)`, callback);
|
glob(`${src}/**/*(*.gz|favicon-32x32.png)`, callback);
|
||||||
};
|
};
|
||||||
|
console.log('Cleaning up previous builds');
|
||||||
|
glob(`../../../build/*.S`, function (err, list) {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error', err);
|
||||||
|
} else {
|
||||||
|
list.forEach(fileName=>{
|
||||||
|
try {
|
||||||
|
console.log(`Purging old binary file ${fileName} from C project.`);
|
||||||
|
fs.unlinkSync(fileName)
|
||||||
|
//file removed
|
||||||
|
} catch(ferr) {
|
||||||
|
console.error(ferr)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log('Generating C include files from webpack build output');
|
||||||
getDirectories('./webpack/dist', function (err, list) {
|
getDirectories('./webpack/dist', function (err, list) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Error', err);
|
console.log('Error', err);
|
||||||
} else {
|
} else {
|
||||||
const regex = /^(.*\/)([^\/]*)$/
|
const regex = /^(.*\/)([^\/]*)$/
|
||||||
const relativeRegex = /(\w+\/[^\/]*)$/
|
const relativeRegex = /((\w+(?<!dist)\/){0,1}[^\/]*)$/
|
||||||
const makePathRegex = /([^\.].*)$/
|
const makePathRegex = /([^\.].*)$/
|
||||||
let exportDefHead=
|
let exportDefHead=
|
||||||
'/***********************************\n'+
|
'/***********************************\n'+
|
||||||
@@ -175,6 +179,8 @@ module.exports = merge(common, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log('Post build completed.');
|
||||||
|
|
||||||
}),
|
}),
|
||||||
new BundleAnalyzerPlugin()
|
new BundleAnalyzerPlugin()
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -69,8 +69,10 @@ Contains the freeRTOS task and all necessary support
|
|||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
#include "globdefs.h"
|
#include "globdefs.h"
|
||||||
|
|
||||||
#ifndef SQUEEZELITE_ESP32_RELEASE_URL
|
|
||||||
#define SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
#ifndef CONFIG_SQUEEZELITE_ESP32_RELEASE_URL
|
||||||
|
#pragma message "Defaulting release url"
|
||||||
|
#define CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define STR_OR_BLANK(p) p==NULL?"":p
|
#define STR_OR_BLANK(p) p==NULL?"":p
|
||||||
@@ -90,11 +92,13 @@ char *ip_info_json = NULL;
|
|||||||
char * release_url=NULL;
|
char * release_url=NULL;
|
||||||
cJSON * ip_info_cjson=NULL;
|
cJSON * ip_info_cjson=NULL;
|
||||||
wifi_config_t* wifi_manager_config_sta = NULL;
|
wifi_config_t* wifi_manager_config_sta = NULL;
|
||||||
|
static void (*chained_notify)(in_addr_t, u16_t, u16_t);
|
||||||
|
|
||||||
static int32_t total_connected_time=0;
|
static int32_t total_connected_time=0;
|
||||||
static int64_t last_connected=0;
|
static int64_t last_connected=0;
|
||||||
static uint16_t num_disconnect=0;
|
static uint16_t num_disconnect=0;
|
||||||
|
static char lms_server_ip[IP4ADDR_STRLEN_MAX]={0};
|
||||||
|
static uint16_t lms_server_port=0;
|
||||||
|
static uint16_t lms_server_cport=0;
|
||||||
|
|
||||||
void (**cb_ptr_arr)(void*) = NULL;
|
void (**cb_ptr_arr)(void*) = NULL;
|
||||||
|
|
||||||
@@ -296,6 +300,22 @@ void wifi_manager_init_wifi(){
|
|||||||
taskYIELD();
|
taskYIELD();
|
||||||
ESP_LOGD(TAG, "Initializing wifi. done");
|
ESP_LOGD(TAG, "Initializing wifi. done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_lms_server_details(in_addr_t ip, u16_t hport, u16_t cport){
|
||||||
|
strncpy(lms_server_ip,inet_ntoa(ip),sizeof(lms_server_ip));
|
||||||
|
ESP_LOGI(TAG,"LMS IP: %s, hport: %d, cport: %d",lms_server_ip, hport, cport);
|
||||||
|
lms_server_port = hport;
|
||||||
|
lms_server_cport = cport;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void connect_notify(in_addr_t ip, u16_t hport, u16_t cport) {
|
||||||
|
set_lms_server_details(ip,hport,cport);
|
||||||
|
if (chained_notify) (*chained_notify)(ip, hport, cport);
|
||||||
|
wifi_manager_update_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void wifi_manager_start(){
|
void wifi_manager_start(){
|
||||||
|
|
||||||
|
|
||||||
@@ -330,14 +350,15 @@ void wifi_manager_start(){
|
|||||||
wifi_manager_safe_update_sta_ip_string(NULL);
|
wifi_manager_safe_update_sta_ip_string(NULL);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Getting release url ");
|
ESP_LOGD(TAG, "Getting release url ");
|
||||||
char * release_url = (char * )config_alloc_get_default(NVS_TYPE_STR, "release_url", QUOTE(SQUEEZELITE_ESP32_RELEASE_URL), 0);
|
char * release_url = (char * )config_alloc_get_default(NVS_TYPE_STR, "release_url", QUOTE(CONFIG_SQUEEZELITE_ESP32_RELEASE_URL), 0);
|
||||||
if(release_url == NULL){
|
if(release_url == NULL){
|
||||||
ESP_LOGE(TAG, "Unable to retrieve the release url from nvs");
|
ESP_LOGE(TAG, "Unable to retrieve the release url from nvs");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ESP_LOGD(TAG, "Found release url %s", release_url);
|
ESP_LOGD(TAG, "Found release url %s", release_url);
|
||||||
}
|
}
|
||||||
|
chained_notify = server_notify;
|
||||||
|
server_notify = connect_notify;
|
||||||
ESP_LOGD(TAG, "About to call init wifi");
|
ESP_LOGD(TAG, "About to call init wifi");
|
||||||
wifi_manager_init_wifi();
|
wifi_manager_init_wifi();
|
||||||
|
|
||||||
@@ -494,6 +515,35 @@ void wifi_manager_update_basic_info(){
|
|||||||
if(avg_conn_time){
|
if(avg_conn_time){
|
||||||
cJSON_SetNumberValue(avg_conn_time, num_disconnect>0?(total_connected_time/num_disconnect):0);
|
cJSON_SetNumberValue(avg_conn_time, num_disconnect>0?(total_connected_time/num_disconnect):0);
|
||||||
}
|
}
|
||||||
|
if(lms_server_cport>0){
|
||||||
|
cJSON * value = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "lms_cport");
|
||||||
|
if(value){
|
||||||
|
cJSON_SetNumberValue(value,lms_server_cport);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cJSON_AddNumberToObject(ip_info_cjson,"lms_cport",lms_server_cport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lms_server_port>0){
|
||||||
|
cJSON * value = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "lms_port");
|
||||||
|
if(value){
|
||||||
|
cJSON_SetNumberValue(value,lms_server_port);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cJSON_AddNumberToObject(ip_info_cjson,"lms_port",lms_server_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(strlen(lms_server_ip) >0){
|
||||||
|
cJSON * value = cJSON_GetObjectItemCaseSensitive(ip_info_cjson, "lms_ip");
|
||||||
|
if(!value){
|
||||||
|
// only create if it does not exist. Since we're creating a reference
|
||||||
|
// to a char buffer, updates to cJSON aren't needed
|
||||||
|
cJSON_AddItemToObject(ip_info_cjson, "lms_ip", cJSON_CreateStringReference(lms_server_ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
wifi_manager_unlock_json_buffer();
|
wifi_manager_unlock_json_buffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,6 +553,9 @@ cJSON * wifi_manager_get_basic_info(cJSON **old){
|
|||||||
ESP_LOGV(TAG, "wifi_manager_get_basic_info called");
|
ESP_LOGV(TAG, "wifi_manager_get_basic_info called");
|
||||||
cJSON *root = wifi_manager_get_new_json(old);
|
cJSON *root = wifi_manager_get_new_json(old);
|
||||||
cJSON_AddItemToObject(root, "project_name", cJSON_CreateString(desc->project_name));
|
cJSON_AddItemToObject(root, "project_name", cJSON_CreateString(desc->project_name));
|
||||||
|
#ifdef CONFIG_FW_PLATFORM_NAME
|
||||||
|
cJSON_AddItemToObject(root, "platform_name", cJSON_CreateString(CONFIG_FW_PLATFORM_NAME));
|
||||||
|
#endif
|
||||||
cJSON_AddItemToObject(root, "version", cJSON_CreateString(desc->version));
|
cJSON_AddItemToObject(root, "version", cJSON_CreateString(desc->version));
|
||||||
if(release_url !=NULL) cJSON_AddItemToObject(root, "release_url", cJSON_CreateString(release_url));
|
if(release_url !=NULL) cJSON_AddItemToObject(root, "release_url", cJSON_CreateString(release_url));
|
||||||
cJSON_AddNumberToObject(root,"recovery", is_recovery_running?1:0);
|
cJSON_AddNumberToObject(root,"recovery", is_recovery_running?1:0);
|
||||||
@@ -517,6 +570,7 @@ cJSON * wifi_manager_get_basic_info(cJSON **old){
|
|||||||
#else
|
#else
|
||||||
cJSON_AddFalseToObject(root, "is_i2c_locked");
|
cJSON_AddFalseToObject(root, "is_i2c_locked");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ESP_LOGV(TAG, "wifi_manager_get_basic_info done");
|
ESP_LOGV(TAG, "wifi_manager_get_basic_info done");
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ void register_common_handlers(httpd_handle_t server){
|
|||||||
httpd_register_uri_handler(server, &js_get);
|
httpd_register_uri_handler(server, &js_get);
|
||||||
httpd_uri_t icon_get = { .uri = "/icons*", .method = HTTP_GET, .handler = resource_filehandler, .user_ctx = rest_context };
|
httpd_uri_t icon_get = { .uri = "/icons*", .method = HTTP_GET, .handler = resource_filehandler, .user_ctx = rest_context };
|
||||||
httpd_register_uri_handler(server, &icon_get);
|
httpd_register_uri_handler(server, &icon_get);
|
||||||
|
httpd_uri_t png_get = { .uri = "/favicon*", .method = HTTP_GET, .handler = resource_filehandler, .user_ctx = rest_context };
|
||||||
|
httpd_register_uri_handler(server, &png_get);
|
||||||
|
|
||||||
}
|
}
|
||||||
void register_regular_handlers(httpd_handle_t server){
|
void register_regular_handlers(httpd_handle_t server){
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
# Wrapper to run make and preprocess any paths in the output from MSYS Unix-style paths
|
|
||||||
# to Windows paths, for Eclipse
|
|
||||||
from __future__ import print_function, division
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import os.path
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import glob
|
|
||||||
from test import test_cmd_line
|
|
||||||
|
|
||||||
#UNIX_PATH_RE = re.compile(r'(([a-zA-Z]{1}[:]{1}){0,1}[/\\][^\s\'\"\t\[\(]+)+')
|
|
||||||
UNIX_PATH_RE = re.compile(r'(([a-zA-Z]{1}[:]{1}){0,1}[/\\][^\s\'\"\t\[\(]+(?![^\\/\n]*$)[/\\]?)')
|
|
||||||
INCLUDE_PATH_RE = re.compile(r'-I[\s"]{0,}(.+?)["]{0,}(?=\s-\S)')
|
|
||||||
INCLUDE_PATH_ADJ_RE = re.compile(r'^([/]opt[/]esp-idf[/]){1}(.*)')
|
|
||||||
INCLUDE_PATH_ADJ2_RE = re.compile(r'^([/]c[/]){1}(.*)')
|
|
||||||
|
|
||||||
paths = {}
|
|
||||||
names = []
|
|
||||||
idf_path= os.environ.get('IDF_PATH').replace("/", "\\")
|
|
||||||
cwd_path= os.environ.get('CWD')
|
|
||||||
pwd_path= os.environ.get('PWD')
|
|
||||||
|
|
||||||
def check_path(path):
|
|
||||||
try:
|
|
||||||
return paths[path]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
paths[path] = path
|
|
||||||
winpath =path
|
|
||||||
|
|
||||||
if not os.path.exists(winpath):
|
|
||||||
# cache as failed, replace with success if it works
|
|
||||||
if re.match(INCLUDE_PATH_ADJ2_RE, path) is not None:
|
|
||||||
winpath = INCLUDE_PATH_ADJ2_RE.sub(r'c:/\2',path) #replace /c/
|
|
||||||
try:
|
|
||||||
winpath = subprocess.check_output(["cygpath", "-w", winpath]).strip()
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return path # something went wrong running cygpath, assume this is not a path!
|
|
||||||
if not os.path.exists(winpath):
|
|
||||||
if not os.path.exists(winpath):
|
|
||||||
winpath=idf_path + '\\' + re.sub(r'^[/\\]opt[/\\](esp-idf[/\\]){0,}', '', path, 1)
|
|
||||||
try:
|
|
||||||
winpath = subprocess.check_output(["cygpath", "-w", winpath]).strip()
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return path # something went wrong running cygpath, assume this is not a path!
|
|
||||||
if not os.path.exists(winpath):
|
|
||||||
return path # not actually a valid path
|
|
||||||
|
|
||||||
winpath = winpath.replace("/", "\\") # make consistent with forward-slashes used elsewhere
|
|
||||||
paths[path] = winpath
|
|
||||||
|
|
||||||
|
|
||||||
#print("In path: {0}, out path: {1}".format(path,winpath) )
|
|
||||||
return winpath
|
|
||||||
def fix_paths(filename):
|
|
||||||
if re.match(r'.*[\\](.*$)',filename) is not None:
|
|
||||||
filename = re.findall(r'.*[\\](.*$)',filename)[0].replace("\\", "/")
|
|
||||||
|
|
||||||
return filename.rstrip()
|
|
||||||
|
|
||||||
def print_paths(path_list, file_name, source_file):
|
|
||||||
|
|
||||||
new_path_list = list(set(path_list))
|
|
||||||
new_path_list.sort()
|
|
||||||
last_n = ''
|
|
||||||
cmd_line='xtensa-esp32-elf-gcc '
|
|
||||||
for n in new_path_list:
|
|
||||||
if re.match(INCLUDE_PATH_ADJ_RE, n) is not None:
|
|
||||||
n = INCLUDE_PATH_ADJ_RE.sub(idf_path+r"\2",n )
|
|
||||||
if re.match(INCLUDE_PATH_ADJ2_RE, n) is not None:
|
|
||||||
n = INCLUDE_PATH_ADJ2_RE.sub(r'c:/\2',n)
|
|
||||||
if last_n != n:
|
|
||||||
cmd_line = cmd_line + ' -I ' + n.rstrip()
|
|
||||||
last_n = n
|
|
||||||
if source_file:
|
|
||||||
cmd_line = cmd_line + ' -c ' + fix_paths(source_file)
|
|
||||||
cmd_line = cmd_line + ' -o ' + fix_paths(file_name)
|
|
||||||
print(cmd_line)
|
|
||||||
def extract_includes():
|
|
||||||
|
|
||||||
for filename in [y for x in os.walk('build') for y in glob.glob(os.path.join(x[0], '*.d'))]:
|
|
||||||
lines = []
|
|
||||||
source=''
|
|
||||||
with open(filename) as file_in:
|
|
||||||
for line in file_in:
|
|
||||||
if re.match(r'\S*(?=/[^/]*\.[h][p]?)',line) is not None:
|
|
||||||
lines.extend(re.findall(r'\S*(?=/[^/]*\.[h][p]?)/',line))
|
|
||||||
if re.match(r'\S*(?=\.[cC][pP]{0,})[^\\\s]*',line) is not None:
|
|
||||||
source = re.findall(r'\S*(?=\.[cC][pP]{0,})[^\\\s]*',line)[0]
|
|
||||||
|
|
||||||
print_paths(lines,filename,source )
|
|
||||||
|
|
||||||
def main():
|
|
||||||
cwd_path=check_path(os.getcwd())
|
|
||||||
os.environ['CWD']= cwd_path
|
|
||||||
os.environ['PWD']= cwd_path
|
|
||||||
idf_path= os.environ.get('IDF_PATH').replace("/", "\\")
|
|
||||||
cwd_path= os.environ.get('CWD')
|
|
||||||
pwd_path= os.environ.get('PWD')
|
|
||||||
print('Running custom script make in {}, IDF_PATH={}, CWD={}, PWD={}'.format(cwd_path,idf_path,cwd_path,pwd_path))
|
|
||||||
|
|
||||||
make = subprocess.Popen(["make"] + sys.argv[1:] + ["BATCH_BUILD=1"], stdout=subprocess.PIPE)
|
|
||||||
for line in iter(make.stdout.readline, ''):
|
|
||||||
line = re.sub(UNIX_PATH_RE, lambda m: check_path(m.group(0)), line)
|
|
||||||
names.extend(INCLUDE_PATH_RE.findall(line))
|
|
||||||
print(line.rstrip())
|
|
||||||
sys.exit(make.wait())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -21,7 +21,6 @@ menu "Squeezelite-ESP32"
|
|||||||
help
|
help
|
||||||
Set logging level info|debug|sdebug
|
Set logging level info|debug|sdebug
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
config JACK_LOCKED
|
config JACK_LOCKED
|
||||||
bool
|
bool
|
||||||
config BAT_LOCKED
|
config BAT_LOCKED
|
||||||
@@ -37,9 +36,58 @@ menu "Squeezelite-ESP32"
|
|||||||
config MUTE_GPIO_LEVEL
|
config MUTE_GPIO_LEVEL
|
||||||
int
|
int
|
||||||
default 0
|
default 0
|
||||||
|
menu "Target"
|
||||||
# AGGREGATES - begin
|
choice OUTPUT_TYPE
|
||||||
# these parameters are "aggregates" that take precedence. The must have a default value
|
prompt "Main system"
|
||||||
|
default BASIC_I2C_BT
|
||||||
|
help
|
||||||
|
Type of hardware platform
|
||||||
|
config SQUEEZEAMP
|
||||||
|
bool "SqueezeAMP"
|
||||||
|
select JACK_LOCKED
|
||||||
|
select BAT_LOCKED
|
||||||
|
select I2C_LOCKED
|
||||||
|
select LED_LOCKED
|
||||||
|
select SPKFAULT_LOCKED
|
||||||
|
config A1S
|
||||||
|
bool "ESP32-A1S module"
|
||||||
|
select I2C_LOCKED
|
||||||
|
config DAC32
|
||||||
|
bool "DAC32 module"
|
||||||
|
config TWATCH2020
|
||||||
|
bool "T-WATCH2020 by LilyGo"
|
||||||
|
select I2C_LOCKED
|
||||||
|
config BASIC_I2C_BT
|
||||||
|
bool "Generic I2S & Bluetooth"
|
||||||
|
endchoice
|
||||||
|
config RELEASE_API
|
||||||
|
string "Software update URL"
|
||||||
|
default "https://api.github.com/repos/sle118/squeezelite-esp32/releases" if !DAC32
|
||||||
|
default "https://yourdomain/api/releases" if DAC32
|
||||||
|
help
|
||||||
|
Set the URL of the API that the front-end UI will use to fetch software updates
|
||||||
|
config SQUEEZELITE_ESP32_RELEASE_URL
|
||||||
|
string "Release URL"
|
||||||
|
default "https://github.com/sle118/squeezelite-esp32/releases" if !DAC32
|
||||||
|
default "https://yourdomain/releases" if DAC32
|
||||||
|
help
|
||||||
|
Set the URL where users can see a list of releases
|
||||||
|
config PROJECT_NAME
|
||||||
|
string "Project Name"
|
||||||
|
default "Squeezelite-A1S" if A1S
|
||||||
|
default "SqueezeAMP" if SQUEEZEAMP
|
||||||
|
default "DAC32" if DAC32
|
||||||
|
default "Squeezelite-TWATCH" if TWATCH2020
|
||||||
|
default "Squeezelite-ESP32" if !A1S && !SQUEEZEAMP && !DAC32 && !TWATCH2020
|
||||||
|
config FW_PLATFORM_NAME
|
||||||
|
string "Hardware Platform Name"
|
||||||
|
default "SqueezeAmp" if SQUEEZEAMP
|
||||||
|
default "DAC32" if DAC32
|
||||||
|
default "ESP32-A1S" if A1S
|
||||||
|
default "TWATCH" if TWATCH2020
|
||||||
|
default "I2S-4MFlash" if !A1S && !SQUEEZEAMP && !DAC32 && !TWATCH2020
|
||||||
|
# AGGREGATES - begin
|
||||||
|
# these parameters are "aggregates" that take precedence. They must have a default value
|
||||||
config DAC_CONFIG
|
config DAC_CONFIG
|
||||||
string
|
string
|
||||||
default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
|
default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
|
||||||
@@ -62,30 +110,8 @@ menu "Squeezelite-ESP32"
|
|||||||
string
|
string
|
||||||
default "{ \"init\": [ {\"reg\":41, \"val\":128}, {\"reg\":18, \"val\":255} ], \"poweron\": [ {\"reg\":18, \"val\":64, \"mode\":\"or\"} ], \"poweroff\": [ {\"reg\":18, \"val\":191, \"mode\":\"and\"} ] }" if TWATCH2020
|
default "{ \"init\": [ {\"reg\":41, \"val\":128}, {\"reg\":18, \"val\":255} ], \"poweron\": [ {\"reg\":18, \"val\":64, \"mode\":\"or\"} ], \"poweroff\": [ {\"reg\":18, \"val\":191, \"mode\":\"and\"} ] }" if TWATCH2020
|
||||||
default ""
|
default ""
|
||||||
# AGGREGATES - end
|
# AGGREGATES - end
|
||||||
|
endmenu
|
||||||
choice OUTPUT_TYPE
|
|
||||||
prompt "Main system"
|
|
||||||
default BASIC_I2C_BT
|
|
||||||
help
|
|
||||||
Type of hardware platform
|
|
||||||
config SQUEEZEAMP
|
|
||||||
bool "SqueezeAMP"
|
|
||||||
select JACK_LOCKED
|
|
||||||
select BAT_LOCKED
|
|
||||||
select I2C_LOCKED
|
|
||||||
select LED_LOCKED
|
|
||||||
select SPKFAULT_LOCKED
|
|
||||||
config A1S
|
|
||||||
bool "ESP32-A1S module"
|
|
||||||
select I2C_LOCKED
|
|
||||||
config TWATCH2020
|
|
||||||
bool "T-WATCH2020 by LilyGo"
|
|
||||||
select I2C_LOCKED
|
|
||||||
config BASIC_I2C_BT
|
|
||||||
bool "Generic I2S & Bluetooth"
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
menu "Audio settings"
|
menu "Audio settings"
|
||||||
menu "DAC settings"
|
menu "DAC settings"
|
||||||
visible if BASIC_I2C_BT
|
visible if BASIC_I2C_BT
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_spi_flash.h"
|
#include "esp_spi_flash.h"
|
||||||
#include "esp_wifi.h"
|
#include "esp_wifi.h"
|
||||||
#include "esp_system.h"
|
|
||||||
#include <esp_event.h>
|
#include <esp_event.h>
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -86,11 +85,18 @@ void cb_connection_got_ip(void *pvParameter){
|
|||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
ip.addr = ipInfo.ip.addr;
|
ip.addr = ipInfo.ip.addr;
|
||||||
ESP_LOGI(TAG, "I have a connection!");
|
ESP_LOGI(TAG, "Wifi connected!");
|
||||||
messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Wifi connected");
|
messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Wifi connected");
|
||||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||||
bWifiConnected=true;
|
bWifiConnected=true;
|
||||||
led_unpush(LED_GREEN);
|
led_unpush(LED_GREEN);
|
||||||
|
if(is_recovery_running){
|
||||||
|
// when running in recovery, send a LMS discovery message
|
||||||
|
// to find a running instance. This is to enable using
|
||||||
|
// the plugin's proxy mode for FW download and avoid
|
||||||
|
// expired certificate issues.
|
||||||
|
discover_ota_server(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void cb_connection_sta_disconnected(void *pvParameter){
|
void cb_connection_sta_disconnected(void *pvParameter){
|
||||||
led_blink_pushed(LED_GREEN, 250, 250);
|
led_blink_pushed(LED_GREEN, 250, 250);
|
||||||
@@ -280,33 +286,43 @@ void register_default_nvs(){
|
|||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "autoexec", "1");
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "autoexec", "1");
|
||||||
config_set_default(NVS_TYPE_STR,"autoexec","1", 0);
|
config_set_default(NVS_TYPE_STR,"autoexec","1", 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "autoexec1",default_command_line);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "autoexec1",default_command_line);
|
||||||
config_set_default(NVS_TYPE_STR,"autoexec1",default_command_line,0);
|
config_set_default(NVS_TYPE_STR,"autoexec1",default_command_line,0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_sink_name", CONFIG_A2DP_SINK_NAME);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_sink_name", CONFIG_A2DP_SINK_NAME);
|
||||||
config_set_default(NVS_TYPE_STR, "a2dp_sink_name", CONFIG_A2DP_SINK_NAME, 0);
|
config_set_default(NVS_TYPE_STR, "a2dp_sink_name", CONFIG_A2DP_SINK_NAME, 0);
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_pin", STR(CONFIG_BT_SINK_PIN));
|
|
||||||
config_set_default(NVS_TYPE_STR, "a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS), 0);
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_pin", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS));
|
|
||||||
config_set_default(NVS_TYPE_STR, "a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS), 0);
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_pin", STR(CONFIG_A2DP_CONTROL_DELAY_MS));
|
|
||||||
|
|
||||||
config_set_default(NVS_TYPE_STR, "bt_sink_pin", STR(CONFIG_BT_SINK_PIN), 0);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_sink_name", STR(CONFIG_A2DP_SINK_NAME));
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "release_url", SQUEEZELITE_ESP32_RELEASE_URL);
|
config_set_default(NVS_TYPE_STR, "a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS), 0);
|
||||||
config_set_default(NVS_TYPE_STR, "release_url", SQUEEZELITE_ESP32_RELEASE_URL, 0);
|
|
||||||
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS));
|
||||||
|
config_set_default(NVS_TYPE_STR, "a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS), 0);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "release_url", CONFIG_SQUEEZELITE_ESP32_RELEASE_URL);
|
||||||
|
config_set_default(NVS_TYPE_STR, "release_url", CONFIG_SQUEEZELITE_ESP32_RELEASE_URL, 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s","ap_ip_address",CONFIG_DEFAULT_AP_IP );
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s","ap_ip_address",CONFIG_DEFAULT_AP_IP );
|
||||||
config_set_default(NVS_TYPE_STR, "ap_ip_address",CONFIG_DEFAULT_AP_IP , 0);
|
config_set_default(NVS_TYPE_STR, "ap_ip_address",CONFIG_DEFAULT_AP_IP , 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY );
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY );
|
||||||
config_set_default(NVS_TYPE_STR, "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY , 0);
|
config_set_default(NVS_TYPE_STR, "ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY , 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s","ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK );
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s","ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK );
|
||||||
config_set_default(NVS_TYPE_STR, "ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK , 0);
|
config_set_default(NVS_TYPE_STR, "ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK , 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL));
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL));
|
||||||
config_set_default(NVS_TYPE_STR, "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL) , 0);
|
config_set_default(NVS_TYPE_STR, "ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL) , 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_pwd", CONFIG_DEFAULT_AP_PASSWORD);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "ap_pwd", CONFIG_DEFAULT_AP_PASSWORD);
|
||||||
config_set_default(NVS_TYPE_STR, "ap_pwd", CONFIG_DEFAULT_AP_PASSWORD, 0);
|
config_set_default(NVS_TYPE_STR, "ap_pwd", CONFIG_DEFAULT_AP_PASSWORD, 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "airplay_port", CONFIG_AIRPLAY_PORT);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "airplay_port", CONFIG_AIRPLAY_PORT);
|
||||||
config_set_default(NVS_TYPE_STR, "airplay_port", CONFIG_AIRPLAY_PORT, 0);
|
config_set_default(NVS_TYPE_STR, "airplay_port", CONFIG_AIRPLAY_PORT, 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_dev_name", CONFIG_A2DP_DEV_NAME);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "a2dp_dev_name", CONFIG_A2DP_DEV_NAME);
|
||||||
config_set_default(NVS_TYPE_STR, "a2dp_dev_name", CONFIG_A2DP_DEV_NAME, 0);
|
config_set_default(NVS_TYPE_STR, "a2dp_dev_name", CONFIG_A2DP_DEV_NAME, 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bypass_wm", "0");
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bypass_wm", "0");
|
||||||
config_set_default(NVS_TYPE_STR, "bypass_wm", "0", 0);
|
config_set_default(NVS_TYPE_STR, "bypass_wm", "0", 0);
|
||||||
|
|
||||||
@@ -335,6 +351,12 @@ void register_default_nvs(){
|
|||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "enable_bt_sink", STR(CONFIG_BT_SINK));
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "enable_bt_sink", STR(CONFIG_BT_SINK));
|
||||||
config_set_default(NVS_TYPE_STR, "enable_bt_sink", STR(CONFIG_BT_SINK), 0);
|
config_set_default(NVS_TYPE_STR, "enable_bt_sink", STR(CONFIG_BT_SINK), 0);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_pin", STR(CONFIG_BT_SINK_PIN));
|
||||||
|
config_set_default(NVS_TYPE_STR, "bt_sink_pin", STR(CONFIG_BT_SINK_PIN), 0);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bt_sink_volume", "127");
|
||||||
|
config_set_default(NVS_TYPE_STR, "bt_sink_volume", "127", 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "enable_airplay", STR(CONFIG_AIRPLAY_SINK));
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "enable_airplay", STR(CONFIG_AIRPLAY_SINK));
|
||||||
config_set_default(NVS_TYPE_STR, "enable_airplay", STR(CONFIG_AIRPLAY_SINK), 0);
|
config_set_default(NVS_TYPE_STR, "enable_airplay", STR(CONFIG_AIRPLAY_SINK), 0);
|
||||||
|
|
||||||
@@ -382,6 +404,10 @@ void register_default_nvs(){
|
|||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s", "stats");
|
ESP_LOGD(TAG,"Registering default value for key %s", "stats");
|
||||||
config_set_default(NVS_TYPE_STR, "stats", "n", 0);
|
config_set_default(NVS_TYPE_STR, "stats", "n", 0);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,"Registering default value for key %s", "rel_api");
|
||||||
|
config_set_default(NVS_TYPE_STR, "rel_api", CONFIG_RELEASE_API, 0);
|
||||||
|
|
||||||
wait_for_commit();
|
wait_for_commit();
|
||||||
ESP_LOGD(TAG,"Done setting default values in nvs.");
|
ESP_LOGD(TAG,"Done setting default values in nvs.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esp_pthread.h"
|
#include "esp_pthread.h"
|
||||||
#ifndef SQUEEZELITE_ESP32_RELEASE_URL
|
#ifndef CONFIG_SQUEEZELITE_ESP32_RELEASE_URL
|
||||||
#define SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
#define CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern void run_command(char * line);
|
extern void run_command(char * line);
|
||||||
|
|||||||
Binary file not shown.
@@ -13,25 +13,75 @@ use constant FIRMWARE_POLL_INTERVAL => 3600 * (5 + rand());
|
|||||||
use constant GITHUB_RELEASES_URI => "https://api.github.com/repos/sle118/squeezelite-esp32/releases";
|
use constant GITHUB_RELEASES_URI => "https://api.github.com/repos/sle118/squeezelite-esp32/releases";
|
||||||
use constant GITHUB_ASSET_URI => GITHUB_RELEASES_URI . "/assets/";
|
use constant GITHUB_ASSET_URI => GITHUB_RELEASES_URI . "/assets/";
|
||||||
use constant GITHUB_DOWNLOAD_URI => "https://github.com/sle118/squeezelite-esp32/releases/download/";
|
use constant GITHUB_DOWNLOAD_URI => "https://github.com/sle118/squeezelite-esp32/releases/download/";
|
||||||
my $FW_DOWNLOAD_ID_REGEX = qr|plugins/SqueezeESP32/firmware/(-?\d+)|;
|
use constant ESP32_STATUS_URI => "http://%s/status.json";
|
||||||
my $FW_DOWNLOAD_REGEX = qr|plugins/SqueezeESP32/firmware/([-a-z0-9-/.]+\.bin)$|i;
|
use constant BASE_PATH => 'plugins/SqueezeESP32/firmware/';
|
||||||
|
|
||||||
|
my $FW_DOWNLOAD_REGEX = qr{plugins/SqueezeESP32/firmware/(-99|[-a-z0-9-/.]+\.bin)(?:\?.*)?$}i;
|
||||||
|
my $FW_CUSTOM_REGEX = qr/^((?:squeezelite-esp32-)?custom\.bin)$/;
|
||||||
my $FW_FILENAME_REGEX = qr/^squeezelite-esp32-.*\.bin(\.tmp)?$/;
|
my $FW_FILENAME_REGEX = qr/^squeezelite-esp32-.*\.bin(\.tmp)?$/;
|
||||||
my $FW_TAG_REGEX = qr/\/(ESP32-A1S|SqueezeAmp|I2S-4MFlash)\.(16|32)\.(\d+)\.(.*)\//;
|
my $FW_TAG_REGEX = qr/\b(ESP32-A1S|SqueezeAmp|I2S-4MFlash)\.(16|32)\.(\d+)\.([-a-zA-Z0-9]+)\b/;
|
||||||
|
|
||||||
|
use constant MAX_FW_IMAGE_SIZE => 10 * 1024 * 1024;
|
||||||
|
|
||||||
my $prefs = preferences('plugin.squeezeesp32');
|
my $prefs = preferences('plugin.squeezeesp32');
|
||||||
my $log = logger('plugin.squeezeesp32');
|
my $log = logger('plugin.squeezeesp32');
|
||||||
|
|
||||||
|
my $initialized;
|
||||||
|
|
||||||
sub init {
|
sub init {
|
||||||
Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_ID_REGEX, \&handleFirmwareDownload);
|
my ($client) = @_;
|
||||||
Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_REGEX, \&handleFirmwareDownloadDirect);
|
|
||||||
|
if (!$initialized) {
|
||||||
|
$initialized = 1;
|
||||||
|
Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_REGEX, \&handleFirmwareDownload);
|
||||||
|
Slim::Web::Pages->addRawFunction('plugins/SqueezeESP32/firmware/upload', \&handleFirmwareUpload);
|
||||||
|
}
|
||||||
|
|
||||||
# start checking for firmware updates
|
# start checking for firmware updates
|
||||||
Slim::Utils::Timers::setTimer(undef, Time::HiRes::time() + 30 + rand(30), \&prefetchFirmware);
|
Slim::Utils::Timers::setTimer($client, Time::HiRes::time() + 3.0 + rand(3.0), \&initFirmwareDownload);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub initFirmwareDownload {
|
||||||
|
my ($client, $cb) = @_;
|
||||||
|
|
||||||
|
Slim::Utils::Timers::killTimers($client, \&initFirmwareDownload);
|
||||||
|
|
||||||
|
return unless preferences('server')->get('checkVersion') || $cb;
|
||||||
|
|
||||||
|
Slim::Networking::SimpleAsyncHTTP->new(
|
||||||
|
sub {
|
||||||
|
my $http = shift;
|
||||||
|
my $content = eval { from_json( $http->content ) };
|
||||||
|
|
||||||
|
if ($content && ref $content) {
|
||||||
|
my $releaseInfo = getFirmwareTag($content->{version});
|
||||||
|
|
||||||
|
if ($releaseInfo && ref $releaseInfo) {
|
||||||
|
prefetchFirmware($releaseInfo, $cb);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$cb->() if $cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sub {
|
||||||
|
my ($http, $error) = @_;
|
||||||
|
$log->error("Failed to get releases from Github: $error");
|
||||||
|
|
||||||
|
$cb->() if $cb;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout => 10
|
||||||
|
}
|
||||||
|
)->get(sprintf(ESP32_STATUS_URI, $client->ip));
|
||||||
|
|
||||||
|
Slim::Utils::Timers::setTimer($client, Time::HiRes::time() + FIRMWARE_POLL_INTERVAL, \&initFirmwareDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub prefetchFirmware {
|
sub prefetchFirmware {
|
||||||
Slim::Utils::Timers::killTimers(undef, \&prefetchFirmware);
|
my ($releaseInfo, $cb) = @_;
|
||||||
my $releaseInfo = $prefs->get('lastReleaseTagUsed');
|
|
||||||
|
return unless $releaseInfo;
|
||||||
|
|
||||||
Slim::Networking::SimpleAsyncHTTP->new(
|
Slim::Networking::SimpleAsyncHTTP->new(
|
||||||
sub {
|
sub {
|
||||||
@@ -54,6 +104,9 @@ sub prefetchFirmware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $customFwUrl = _urlFromPath('custom.bin') if $cb && -f _customFirmwareFile();
|
||||||
|
|
||||||
|
if ( ($url && $url =~ /^https?/) || $customFwUrl ) {
|
||||||
downloadFirmwareFile(sub {
|
downloadFirmwareFile(sub {
|
||||||
main::INFOLOG && $log->is_info && $log->info("Pre-cached firmware file: " . $_[0]);
|
main::INFOLOG && $log->is_info && $log->info("Pre-cached firmware file: " . $_[0]);
|
||||||
}, sub {
|
}, sub {
|
||||||
@@ -62,8 +115,10 @@ sub prefetchFirmware {
|
|||||||
$url ||= ($http && $http->url) || 'no URL';
|
$url ||= ($http && $http->url) || 'no URL';
|
||||||
|
|
||||||
$log->error(sprintf("Failed to get firmware image from Github: %s (%s)", $error || $http->error, $url));
|
$log->error(sprintf("Failed to get firmware image from Github: %s (%s)", $error || $http->error, $url));
|
||||||
}, $url) if $url && $url =~ /^https?/;
|
}, $url) if $url;
|
||||||
|
|
||||||
|
$cb->($releaseInfo, _gh2lmsUrl($url), $customFwUrl) if $cb;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sub {
|
sub {
|
||||||
my ($http, $error) = @_;
|
my ($http, $error) = @_;
|
||||||
@@ -74,9 +129,23 @@ sub prefetchFirmware {
|
|||||||
cache => 1,
|
cache => 1,
|
||||||
expires => 3600
|
expires => 3600
|
||||||
}
|
}
|
||||||
)->get(GITHUB_RELEASES_URI) if $releaseInfo;
|
)->get(GITHUB_RELEASES_URI);
|
||||||
|
}
|
||||||
|
|
||||||
Slim::Utils::Timers::setTimer(undef, Time::HiRes::time() + FIRMWARE_POLL_INTERVAL, \&prefetchFirmware);
|
sub _gh2lmsUrl {
|
||||||
|
my ($url) = @_;
|
||||||
|
my $ghPrefix = GITHUB_DOWNLOAD_URI;
|
||||||
|
my $baseUrl = Slim::Utils::Network::serverURL();
|
||||||
|
$url =~ s/$ghPrefix/$baseUrl\/plugins\/SqueezeESP32\/firmware\//;
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _urlFromPath {
|
||||||
|
return sprintf('%s/%s%s', Slim::Utils::Network::serverURL(), BASE_PATH, basename(shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _customFirmwareFile {
|
||||||
|
return catfile(scalar Slim::Utils::OSDetect::dirsFor('updates'), 'squeezelite-esp32-custom.bin');
|
||||||
}
|
}
|
||||||
|
|
||||||
sub handleFirmwareDownload {
|
sub handleFirmwareDownload {
|
||||||
@@ -88,13 +157,13 @@ sub handleFirmwareDownload {
|
|||||||
_errorDownloading($httpClient, $response, @_);
|
_errorDownloading($httpClient, $response, @_);
|
||||||
};
|
};
|
||||||
|
|
||||||
my $id;
|
my $path;
|
||||||
if (!defined $request || !(($id) = $request->uri =~ $FW_DOWNLOAD_ID_REGEX)) {
|
if (!defined $request || !(($path) = $request->uri =~ $FW_DOWNLOAD_REGEX)) {
|
||||||
return $_errorDownloading->(undef, 'Invalid request', $request->uri, 400);
|
return $_errorDownloading->(undef, 'Invalid request', $request->uri, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
# this is the magic number used on the client to figure out whether the plugin does support download proxying
|
# this is the magic request used on the client to figure out whether the plugin does support download proxying
|
||||||
if ($id == -99) {
|
if ($path =~ /^(?:-99|-check.bin)$/) {
|
||||||
$response->code(204);
|
$response->code(204);
|
||||||
$response->header('Access-Control-Allow-Origin' => '*');
|
$response->header('Access-Control-Allow-Origin' => '*');
|
||||||
|
|
||||||
@@ -102,48 +171,20 @@ sub handleFirmwareDownload {
|
|||||||
return Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
return Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
Slim::Networking::SimpleAsyncHTTP->new(
|
if ($path =~ $FW_CUSTOM_REGEX) {
|
||||||
sub {
|
my $firmwareFile = _customFirmwareFile();
|
||||||
my $http = shift;
|
|
||||||
my $content = eval { from_json( $http->content ) };
|
|
||||||
|
|
||||||
if (!$content || !ref $content) {
|
if (! -f $firmwareFile) {
|
||||||
$@ && $log->error("Failed to parse response: $@");
|
main::INFOLOG && $log->is_info && $log->info("Failed to find custom firmware build: $firmwareFile");
|
||||||
return $_errorDownloading->($http);
|
$response->code(404);
|
||||||
}
|
$httpClient->send_response($response);
|
||||||
elsif (!$content->{browser_download_url} || !$content->{name}) {
|
return Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
||||||
return $_errorDownloading->($http, 'No download URL found');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFirmwareFile(sub {
|
main::INFOLOG && $log->is_info && $log->info("Getting custom firmware build");
|
||||||
my $firmwareFile = shift;
|
|
||||||
$response->code(200);
|
$response->code(200);
|
||||||
Slim::Web::HTTP::sendStreamingFile($httpClient, $response, 'application/octet-stream', $firmwareFile, undef, 1);
|
return Slim::Web::HTTP::sendStreamingFile($httpClient, $response, 'application/octet-stream', $firmwareFile, undef, 1);
|
||||||
}, $_errorDownloading, $content->{browser_download_url}, $content->{name});
|
|
||||||
},
|
|
||||||
$_errorDownloading,
|
|
||||||
{
|
|
||||||
timeout => 10,
|
|
||||||
cache => 1,
|
|
||||||
expires => 86400
|
|
||||||
}
|
|
||||||
)->get(GITHUB_ASSET_URI . $id);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub handleFirmwareDownloadDirect {
|
|
||||||
my ($httpClient, $response) = @_;
|
|
||||||
|
|
||||||
my $request = $response->request;
|
|
||||||
|
|
||||||
my $_errorDownloading = sub {
|
|
||||||
_errorDownloading($httpClient, $response, @_);
|
|
||||||
};
|
|
||||||
|
|
||||||
my $path;
|
|
||||||
if (!defined $request || !(($path) = $request->uri =~ $FW_DOWNLOAD_REGEX)) {
|
|
||||||
return $_errorDownloading->(undef, 'Invalid request', $request->uri, 400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main::INFOLOG && $log->is_info && $log->info("Requesting firmware from: $path");
|
main::INFOLOG && $log->is_info && $log->info("Requesting firmware from: $path");
|
||||||
@@ -159,7 +200,7 @@ sub downloadFirmwareFile {
|
|||||||
my ($cb, $ecb, $url, $name) = @_;
|
my ($cb, $ecb, $url, $name) = @_;
|
||||||
|
|
||||||
# keep track of the last firmware we requested, to prefetch it in the future
|
# keep track of the last firmware we requested, to prefetch it in the future
|
||||||
_getFirmwareTag($url);
|
my $releaseInfo = getFirmwareTag($url);
|
||||||
|
|
||||||
$name ||= basename($url);
|
$name ||= basename($url);
|
||||||
|
|
||||||
@@ -167,9 +208,21 @@ sub downloadFirmwareFile {
|
|||||||
return $ecb->(undef, 'Unexpected firmware image name: ' . $name, $url, 400);
|
return $ecb->(undef, 'Unexpected firmware image name: ' . $name, $url, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $updatesDir = Slim::Utils::OSDetect::dirsFor('updates');
|
my $updatesDir = _getTempDir();
|
||||||
my $firmwareFile = catfile($updatesDir, $name);
|
my $firmwareFile = catfile($updatesDir, $name);
|
||||||
Slim::Utils::Misc::deleteFiles($updatesDir, $FW_FILENAME_REGEX, $firmwareFile);
|
|
||||||
|
if (-f $firmwareFile) {
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("Found uploaded firmware file $name");
|
||||||
|
return $cb->($firmwareFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$updatesDir = Slim::Utils::OSDetect::dirsFor('updates');
|
||||||
|
$firmwareFile = catfile($updatesDir, $name);
|
||||||
|
|
||||||
|
if ($releaseInfo) {
|
||||||
|
my $fileMatchRegex = join('-', '', $releaseInfo->{branch}, $releaseInfo->{model}, $releaseInfo->{res});
|
||||||
|
Slim::Utils::Misc::deleteFiles($updatesDir, $fileMatchRegex, $firmwareFile);
|
||||||
|
}
|
||||||
|
|
||||||
if (-f $firmwareFile) {
|
if (-f $firmwareFile) {
|
||||||
main::INFOLOG && $log->is_info && $log->info("Found cached firmware file");
|
main::INFOLOG && $log->is_info && $log->info("Found cached firmware file");
|
||||||
@@ -188,7 +241,11 @@ sub downloadFirmwareFile {
|
|||||||
|
|
||||||
return $cb->($firmwareFile);
|
return $cb->($firmwareFile);
|
||||||
},
|
},
|
||||||
$ecb,
|
sub {
|
||||||
|
my ($http, $error) = @_;
|
||||||
|
$http->code(404) if $error =~ /\b404\b/;
|
||||||
|
$ecb->(@_);
|
||||||
|
},
|
||||||
{
|
{
|
||||||
saveAs => "$firmwareFile.tmp",
|
saveAs => "$firmwareFile.tmp",
|
||||||
}
|
}
|
||||||
@@ -197,10 +254,10 @@ sub downloadFirmwareFile {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _getFirmwareTag {
|
sub getFirmwareTag {
|
||||||
my ($url) = @_;
|
my ($info) = @_;
|
||||||
|
|
||||||
if (my ($model, $resolution, $version, $branch) = $url =~ $FW_TAG_REGEX) {
|
if (my ($model, $resolution, $version, $branch) = $info =~ $FW_TAG_REGEX) {
|
||||||
my $releaseInfo = {
|
my $releaseInfo = {
|
||||||
model => $model,
|
model => $model,
|
||||||
res => $resolution,
|
res => $resolution,
|
||||||
@@ -208,8 +265,6 @@ sub _getFirmwareTag {
|
|||||||
branch => $branch
|
branch => $branch
|
||||||
};
|
};
|
||||||
|
|
||||||
$prefs->set('lastReleaseTagUsed', $releaseInfo);
|
|
||||||
|
|
||||||
return $releaseInfo;
|
return $releaseInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,5 +288,123 @@ sub _errorDownloading {
|
|||||||
Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sub handleFirmwareUpload {
|
||||||
|
my ($httpClient, $response) = @_;
|
||||||
|
|
||||||
|
my $request = $response->request;
|
||||||
|
my $result = {};
|
||||||
|
|
||||||
|
my $t = Time::HiRes::time();
|
||||||
|
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("New firmware image to upload. Size: " . formatMB($request->content_length));
|
||||||
|
|
||||||
|
if ( $request->method !~ /HEAD|OPTIONS|POST/ ) {
|
||||||
|
$log->error("Invalid HTTP verb: " . $request->method);
|
||||||
|
$result = {
|
||||||
|
error => 'Invalid request.',
|
||||||
|
code => 400,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
elsif ( $request->content_length > MAX_FW_IMAGE_SIZE ) {
|
||||||
|
$log->error("Upload data is too large: " . $request->content_length);
|
||||||
|
$result = {
|
||||||
|
error => string('PLUGIN_DNDPLAY_FILE_TOO_LARGE', formatMB($request->content_length), formatMB(MAX_FW_IMAGE_SIZE)),
|
||||||
|
code => 413,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my $ct = $request->header('Content-Type');
|
||||||
|
my ($boundary) = $ct =~ /boundary=(.*)/;
|
||||||
|
|
||||||
|
my ($uploadedFwFh, $filename, $inUpload, $buf);
|
||||||
|
|
||||||
|
# open a pseudo-filehandle to the uploaded data ref for further processing
|
||||||
|
open TEMP, '<', $request->content_ref;
|
||||||
|
|
||||||
|
while (<TEMP>) {
|
||||||
|
if ( Time::HiRes::time - $t > 0.2 ) {
|
||||||
|
main::idleStreams();
|
||||||
|
$t = Time::HiRes::time();
|
||||||
|
}
|
||||||
|
|
||||||
|
# a new part starts - reset some variables
|
||||||
|
if ( /--\Q$boundary\E/i ) {
|
||||||
|
$filename = '';
|
||||||
|
|
||||||
|
if ($buf) {
|
||||||
|
$buf =~ s/\r\n$//;
|
||||||
|
print $uploadedFwFh $buf if $uploadedFwFh;
|
||||||
|
}
|
||||||
|
|
||||||
|
close $uploadedFwFh if $uploadedFwFh;
|
||||||
|
$inUpload = undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# write data to file handle
|
||||||
|
elsif ( $inUpload && $uploadedFwFh ) {
|
||||||
|
print $uploadedFwFh $buf if defined $buf;
|
||||||
|
$buf = $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
# we got an uploaded file name
|
||||||
|
elsif ( /filename="(.+?)"/i ) {
|
||||||
|
$filename = $1;
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("New file to upload: $filename")
|
||||||
|
}
|
||||||
|
|
||||||
|
# we got the separator after the upload file name: file data comes next. Open a file handle to write the data to.
|
||||||
|
elsif ( $filename && /^\s*$/ ) {
|
||||||
|
$inUpload = 1;
|
||||||
|
|
||||||
|
$uploadedFwFh = File::Temp->new(
|
||||||
|
DIR => _getTempDir(),
|
||||||
|
SUFFIX => '.bin',
|
||||||
|
TEMPLATE => 'squeezelite-esp32-upload-XXXXXX',
|
||||||
|
UNLINK => 0,
|
||||||
|
) or $log->warn("Failed to open file: $@");
|
||||||
|
|
||||||
|
binmode $uploadedFwFh;
|
||||||
|
|
||||||
|
# remove file after a few minutes
|
||||||
|
Slim::Utils::Timers::setTimer($uploadedFwFh->filename, Time::HiRes::time() + 15 * 60, sub { unlink shift });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close TEMP;
|
||||||
|
close $uploadedFwFh if $uploadedFwFh;
|
||||||
|
|
||||||
|
main::idleStreams();
|
||||||
|
|
||||||
|
if (!$result->{error}) {
|
||||||
|
$result->{url} = _urlFromPath($uploadedFwFh->filename);
|
||||||
|
$result->{size} = -s $uploadedFwFh->filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$log->error($result->{error}) if $result->{error};
|
||||||
|
|
||||||
|
my $content = to_json($result);
|
||||||
|
$response->header( 'Content-Length' => length($content) );
|
||||||
|
$response->code($result->{code} || 200);
|
||||||
|
$response->header('Connection' => 'close');
|
||||||
|
$response->content_type('application/json');
|
||||||
|
|
||||||
|
Slim::Web::HTTP::addHTTPResponse( $httpClient, $response, \$content );
|
||||||
|
}
|
||||||
|
|
||||||
|
my $tempDir;
|
||||||
|
sub _getTempDir {
|
||||||
|
return $tempDir if $tempDir;
|
||||||
|
|
||||||
|
eval { $tempDir = Slim::Utils::Misc::getTempDir() }; # LMS 8.2+ only
|
||||||
|
$tempDir ||= File::Temp::tempdir(CLEANUP => 1, DIR => preferences('server')->get('cachedir'));
|
||||||
|
|
||||||
|
return $tempDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub formatMB {
|
||||||
|
return Slim::Utils::Misc::delimitThousands(int($_[0] / 1024 / 1024)) . 'MB';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
@@ -1,5 +1,21 @@
|
|||||||
[% PROCESS settings/header.html %]
|
[% PROCESS settings/header.html %]
|
||||||
|
|
||||||
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_FIRMWARE" desc="" %]
|
||||||
|
<div><a href="http://[% player_ip %]" target="_blank">[% "PLUGIN_SQUEEZEESP32_PLAYERSETTINGS" | string %] ([% player_ip %])</a></div>
|
||||||
|
[% IF fwUpdateAvailable %]
|
||||||
|
<div>
|
||||||
|
<input type="submit" name="installUpdate" class="stdclick" value="[% "CONTROLPANEL_INSTALL_UPDATE" | string %]"/>
|
||||||
|
[% fwUpdateAvailable %]
|
||||||
|
</div>
|
||||||
|
[% END %]
|
||||||
|
[% IF fwCustomUpdateAvailable %]
|
||||||
|
<div>
|
||||||
|
<input type="submit" name="installCustomUpdate" class="stdclick" value="[% "CONTROLPANEL_INSTALL_UPDATE" | string %]"/>
|
||||||
|
[% fwCustomUpdateAvailable | string %]
|
||||||
|
</div>
|
||||||
|
[% END %]
|
||||||
|
[% END %]
|
||||||
|
|
||||||
[% IF prefs.pref_width %]
|
[% IF prefs.pref_width %]
|
||||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %]
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %]
|
||||||
<!--<input type="text" readonly class="stdedit" name="pref_width" id="width" value="[% prefs.pref_width %]" size="3">-->
|
<!--<input type="text" readonly class="stdedit" name="pref_width" id="width" value="[% prefs.pref_width %]" size="3">-->
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use List::Util qw(min);
|
|||||||
use Slim::Utils::Log;
|
use Slim::Utils::Log;
|
||||||
use Slim::Utils::Prefs;
|
use Slim::Utils::Prefs;
|
||||||
|
|
||||||
|
use Plugins::SqueezeESP32::FirmwareHelper;
|
||||||
|
|
||||||
my $sprefs = preferences('server');
|
my $sprefs = preferences('server');
|
||||||
my $prefs = preferences('plugin.squeezeesp32');
|
my $prefs = preferences('plugin.squeezeesp32');
|
||||||
my $log = logger('plugin.squeezeesp32');
|
my $log = logger('plugin.squeezeesp32');
|
||||||
@@ -95,6 +97,8 @@ sub init {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$client->SUPER::init(@_);
|
$client->SUPER::init(@_);
|
||||||
|
Plugins::SqueezeESP32::FirmwareHelper::init($client);
|
||||||
|
|
||||||
main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id);
|
main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +211,7 @@ sub update_artwork {
|
|||||||
my $cprefs = $prefs->client($client);
|
my $cprefs = $prefs->client($client);
|
||||||
|
|
||||||
my $artwork = $cprefs->get('artwork') || return;
|
my $artwork = $cprefs->get('artwork') || return;
|
||||||
return unless $artwork->{'enable'};
|
return unless $artwork->{'enable'} && $client->display->isa("Plugins::SqueezeESP32::Graphics");
|
||||||
|
|
||||||
my $header = pack('Nnn', $artwork->{'enable'}, $artwork->{'x'}, $artwork->{'y'});
|
my $header = pack('Nnn', $artwork->{'enable'}, $artwork->{'x'}, $artwork->{'y'});
|
||||||
$client->sendFrame( grfa => \$header );
|
$client->sendFrame( grfa => \$header );
|
||||||
@@ -282,7 +286,7 @@ sub reconnect {
|
|||||||
$client->SUPER::reconnect(@_);
|
$client->SUPER::reconnect(@_);
|
||||||
|
|
||||||
$client->pluginData('artwork_md5', '');
|
$client->pluginData('artwork_md5', '');
|
||||||
$client->config_artwork;
|
$client->config_artwork if $client->display->isa("Plugins::SqueezeESP32::Graphics");
|
||||||
$client->send_equalizer;
|
$client->send_equalizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package Plugins::SqueezeESP32::PlayerSettings;
|
|||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use base qw(Slim::Web::Settings);
|
use base qw(Slim::Web::Settings);
|
||||||
|
use JSON::XS::VersionOneAndTwo;
|
||||||
use List::Util qw(first);
|
use List::Util qw(first);
|
||||||
|
|
||||||
use Slim::Utils::Log;
|
use Slim::Utils::Log;
|
||||||
@@ -36,7 +37,7 @@ sub prefs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub handler {
|
sub handler {
|
||||||
my ($class, $client, $paramRef) = @_;
|
my ($class, $client, $paramRef, $callback, @args) = @_;
|
||||||
|
|
||||||
my ($cprefs, @prefs) = $class->prefs($client);
|
my ($cprefs, @prefs) = $class->prefs($client);
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ sub handler {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($client->depth == 16) {
|
if ($client->can('depth') && $client->depth == 16) {
|
||||||
my $equalizer = $cprefs->get('equalizer');
|
my $equalizer = $cprefs->get('equalizer');
|
||||||
for my $i (0 .. $#{$equalizer}) {
|
for my $i (0 .. $#{$equalizer}) {
|
||||||
$equalizer->[$i] = $paramRef->{"pref_equalizer.$i"} || 0;
|
$equalizer->[$i] = $paramRef->{"pref_equalizer.$i"} || 0;
|
||||||
@@ -93,9 +94,46 @@ sub handler {
|
|||||||
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
|
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
|
||||||
}
|
}
|
||||||
|
|
||||||
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->depth == 16;
|
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->can('depth') && $client->depth == 16;
|
||||||
|
$paramRef->{'player_ip'} = $client->ip;
|
||||||
|
|
||||||
return $class->SUPER::handler($client, $paramRef);
|
Plugins::SqueezeESP32::FirmwareHelper::initFirmwareDownload($client, sub {
|
||||||
|
my ($currentFWInfo, $newFWUrl, $customFwUrl) = @_;
|
||||||
|
|
||||||
|
$currentFWInfo ||= {};
|
||||||
|
my $newFWInfo = Plugins::SqueezeESP32::FirmwareHelper::getFirmwareTag($newFWUrl) || {};
|
||||||
|
|
||||||
|
if ($paramRef->{installUpdate} || $paramRef->{installCustomUpdate}) {
|
||||||
|
my $http = Slim::Networking::SimpleAsyncHTTP->new(sub {
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("Firmware update triggered");
|
||||||
|
}, sub {
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("Failed to trigger firmware update");
|
||||||
|
main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump(@_));
|
||||||
|
})->post(sprintf('http://%s/config.json', $client->ip), to_json({
|
||||||
|
timestamp => int(Time::HiRes::time() * 1000) * 1,
|
||||||
|
config => {
|
||||||
|
fwurl => {
|
||||||
|
value => $paramRef->{installCustomUpdate} ? $customFwUrl : $newFWUrl,
|
||||||
|
type => 33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($currentFWInfo->{version} && $newFWInfo->{version} && $currentFWInfo->{version} > $newFWInfo->{version}) {
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("There's an update for your SqueezeESP32 player: $newFWUrl");
|
||||||
|
$paramRef->{fwUpdateAvailable} = sprintf($client->string('PLUGIN_SQUEEZEESP32_FIRMWARE_AVAILABLE'), $newFWInfo->{version}, $currentFWInfo->{version});
|
||||||
|
}
|
||||||
|
if ($customFwUrl) {
|
||||||
|
main::INFOLOG && $log->is_info && $log->info("There's a custom firmware for your SqueezeESP32 player: $customFwUrl");
|
||||||
|
$paramRef->{fwCustomUpdateAvailable} = 'PLUGIN_SQUEEZEESP32_CUSTOM_FIRMWARE_AVAILABLE';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$callback->( $client, $paramRef, $class->SUPER::handler($client, $paramRef), @args );
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
@@ -8,8 +8,6 @@ use Slim::Utils::Prefs;
|
|||||||
use Slim::Utils::Log;
|
use Slim::Utils::Log;
|
||||||
use Slim::Web::ImageProxy;
|
use Slim::Web::ImageProxy;
|
||||||
|
|
||||||
use Plugins::SqueezeESP32::FirmwareHelper;
|
|
||||||
|
|
||||||
my $prefs = preferences('plugin.squeezeesp32');
|
my $prefs = preferences('plugin.squeezeesp32');
|
||||||
|
|
||||||
my $log = Slim::Utils::Log->addLogCategory({
|
my $log = Slim::Utils::Log->addLogCategory({
|
||||||
@@ -39,12 +37,13 @@ $prefs->setChange(sub {
|
|||||||
sub initPlugin {
|
sub initPlugin {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
|
|
||||||
|
# enable the following to test the firmware downloading code without a SqueezeliteESP32 player
|
||||||
|
# require Plugins::SqueezeESP32::FirmwareHelper;
|
||||||
|
# Plugins::SqueezeESP32::FirmwareHelper::init();
|
||||||
|
|
||||||
if ( main::WEBUI ) {
|
if ( main::WEBUI ) {
|
||||||
require Plugins::SqueezeESP32::PlayerSettings;
|
require Plugins::SqueezeESP32::PlayerSettings;
|
||||||
Plugins::SqueezeESP32::PlayerSettings->new;
|
Plugins::SqueezeESP32::PlayerSettings->new;
|
||||||
|
|
||||||
# require Plugins::SqueezeESP32::Settings;
|
|
||||||
# Plugins::SqueezeESP32::Settings->new;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$class->SUPER::initPlugin(@_);
|
$class->SUPER::initPlugin(@_);
|
||||||
@@ -60,8 +59,6 @@ sub initPlugin {
|
|||||||
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
|
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
|
||||||
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
|
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
|
||||||
Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
|
Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
|
||||||
|
|
||||||
Plugins::SqueezeESP32::FirmwareHelper->init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub onStopClear {
|
sub onStopClear {
|
||||||
|
|||||||
@@ -10,6 +10,6 @@
|
|||||||
<name>PLUGIN_SQUEEZEESP32</name>
|
<name>PLUGIN_SQUEEZEESP32</name>
|
||||||
<description>PLUGIN_SQUEEZEESP32_DESC</description>
|
<description>PLUGIN_SQUEEZEESP32_DESC</description>
|
||||||
<module>Plugins::SqueezeESP32::Plugin</module>
|
<module>Plugins::SqueezeESP32::Plugin</module>
|
||||||
<version>0.310</version>
|
<version>0.351</version>
|
||||||
<creator>Philippe</creator>
|
<creator>Philippe</creator>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ PLUGIN_SQUEEZEESP32_PLAYERSETTINGS
|
|||||||
DE ESP32 Einstellungen
|
DE ESP32 Einstellungen
|
||||||
EN ESP32 settings
|
EN ESP32 settings
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_FIRMWARE
|
||||||
|
EN Firmware
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_FIRMWARE_AVAILABLE
|
||||||
|
DE Es steht eine neue Firmware Version v%s zur Verfügung (aktuell installiert: v%s).
|
||||||
|
EN A new firmware version v%s is available (currently installed: v%s).
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_CUSTOM_FIRMWARE_AVAILABLE
|
||||||
|
DE Es steht eine benutzerdefinierte Firmware Version zur Verfügung.
|
||||||
|
EN A custom firmware image is available for installation.
|
||||||
|
|
||||||
PLUGIN_SQUEEZEESP32_WIDTH
|
PLUGIN_SQUEEZEESP32_WIDTH
|
||||||
DE Displaybreite
|
DE Displaybreite
|
||||||
EN Screen width
|
EN Screen width
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' standalone='yes'?>
|
<?xml version='1.0' standalone='yes'?>
|
||||||
<extensions>
|
<extensions>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin version="0.200" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
|
<plugin version="0.351" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
|
||||||
<link>https://github.com/sle118/squeezelite-esp32</link>
|
<link>https://github.com/sle118/squeezelite-esp32</link>
|
||||||
<creator>Philippe</creator>
|
<creator>Philippe</creator>
|
||||||
<sha>ab2d65f5ba8e73f0f78a1a8650af19ebb1e8e724</sha>
|
<sha>3209d93e2b02c1c9161572977f03c93938272b30</sha>
|
||||||
<email>philippe_44@outlook.com</email>
|
<email>philippe_44@outlook.com</email>
|
||||||
<desc lang="EN">SqueezeESP32 additional player id (100)</desc>
|
<desc lang="EN">SqueezeESP32 additional player id (100/101)</desc>
|
||||||
<url>http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip</url>
|
<url>http://github.com/sle118/squeezelite-esp32/raw/master-cmake/plugin/SqueezeESP32.zip</url>
|
||||||
<title lang="EN">SqueezeESP32</title>
|
<title lang="EN">SqueezeESP32</title>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|||||||
19
sdkconfig
19
sdkconfig
@@ -75,21 +75,26 @@ CONFIG_LOGGING_SLIMPROTO="info"
|
|||||||
CONFIG_LOGGING_STREAM="info"
|
CONFIG_LOGGING_STREAM="info"
|
||||||
CONFIG_LOGGING_DECODE="info"
|
CONFIG_LOGGING_DECODE="info"
|
||||||
CONFIG_LOGGING_OUTPUT="info"
|
CONFIG_LOGGING_OUTPUT="info"
|
||||||
|
CONFIG_I2C_LOCKED=y
|
||||||
CONFIG_MUTE_GPIO_LEVEL=0
|
CONFIG_MUTE_GPIO_LEVEL=0
|
||||||
CONFIG_DAC_CONFIG=""
|
# CONFIG_SQUEEZEAMP is not set
|
||||||
|
CONFIG_A1S=y
|
||||||
|
# CONFIG_DAC32 is not set
|
||||||
|
# CONFIG_TWATCH2020 is not set
|
||||||
|
# CONFIG_BASIC_I2C_BT is not set
|
||||||
|
CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_SQUEEZELITE_ESP32_RELEASE_URL="https://github.com/sle118/squeezelite-esp32/releases"
|
||||||
|
CONFIG_PROJECT_NAME="Squeezelite ESP32-A1S"
|
||||||
|
CONFIG_FW_PLATFORM_NAME="ESP32-A1S"
|
||||||
|
CONFIG_DAC_CONFIG="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32"
|
||||||
CONFIG_SPDIF_CONFIG=""
|
CONFIG_SPDIF_CONFIG=""
|
||||||
CONFIG_SPI_CONFIG=""
|
CONFIG_SPI_CONFIG=""
|
||||||
CONFIG_DISPLAY_CONFIG=""
|
CONFIG_DISPLAY_CONFIG=""
|
||||||
CONFIG_DAC_CONTROLSET=""
|
CONFIG_DAC_CONTROLSET=""
|
||||||
# CONFIG_SQUEEZEAMP is not set
|
|
||||||
# CONFIG_A1S is not set
|
|
||||||
# CONFIG_TWATCH2020 is not set
|
|
||||||
CONFIG_BASIC_I2C_BT=y
|
|
||||||
CONFIG_I2S_NUM=0
|
CONFIG_I2S_NUM=0
|
||||||
CONFIG_I2S_BCK_IO=-1
|
CONFIG_I2S_BCK_IO=-1
|
||||||
CONFIG_I2S_WS_IO=-1
|
CONFIG_I2S_WS_IO=-1
|
||||||
CONFIG_I2S_DO_IO=-1
|
CONFIG_I2S_DO_IO=-1
|
||||||
CONFIG_I2S_DI_IO=-1
|
|
||||||
CONFIG_I2C_SDA=-1
|
CONFIG_I2C_SDA=-1
|
||||||
CONFIG_I2C_SCL=-1
|
CONFIG_I2C_SCL=-1
|
||||||
CONFIG_MUTE_GPIO=-1
|
CONFIG_MUTE_GPIO=-1
|
||||||
@@ -612,7 +617,7 @@ CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
|||||||
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
|
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
|
||||||
CONFIG_LOG_DEFAULT_LEVEL=3
|
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||||
CONFIG_LOG_COLORS=y
|
CONFIG_LOG_COLORS=y
|
||||||
CONFIG_LWIP_LOCAL_HOSTNAME="squeezelite-esp32"
|
CONFIG_LWIP_LOCAL_HOSTNAME="esp32a1s"
|
||||||
# CONFIG_LWIP_L2_TO_L3_COPY is not set
|
# CONFIG_LWIP_L2_TO_L3_COPY is not set
|
||||||
# CONFIG_LWIP_IRAM_OPTIMIZATION is not set
|
# CONFIG_LWIP_IRAM_OPTIMIZATION is not set
|
||||||
CONFIG_LWIP_TIMERS_ONDEMAND=y
|
CONFIG_LWIP_TIMERS_ONDEMAND=y
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
custom.build
|
local.500.cmake-master
|
||||||
Reference in New Issue
Block a user