esphome icon indicating copy to clipboard operation
esphome copied to clipboard

Add basic snapclient component

Open luar123 opened this issue 10 months ago • 9 comments

What does this implement/fix?

Basic snapclient component based on the implementation from CarlosDerSeher. Does not provide any control functionality and will lock the i2s_audio device. So no other speaker/microphone components can share it.

If using a device without PSRAM make sure to reduce your snapserver buffer size to ~700ms.

Requires:

  • enable_ipv6: true

Notes:

  • if no server hostname is given, mdns is used to find the snapserver
  • Can use an audio_dac component or mute pin
  • If webserver_port is set, a webserver is started showing the equalizer, needs a spiffs partition (would benefit from #7682)
  • Other options like sample_rate or channel are ignored.
  • More features like stop/resume the player and releasing i2s would require more work on the upstream component but would allow to play from multiple sources.
  • Using a speaker component instead of i2s_audio might be possible but would require more work.

Types of changes

  • [ ] Bugfix (non-breaking change which fixes an issue)
  • [x] New feature (non-breaking change which adds functionality)
  • [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • [ ] Code quality improvements to existing code or addition of tests
  • [ ] Other

Related issue or feature (if applicable):

  • fixes esphome/feature-requests/issues/861

Pull request in esphome-docs with documentation (if applicable):

  • esphome/esphome-docs#<esphome-docs PR number goes here>

Test Environment

  • [ ] ESP32
  • [x] ESP32 IDF
  • [ ] ESP8266
  • [ ] RP2040
  • [ ] BK72xx
  • [ ] RTL87xx

Example entry for config.yaml:

# Example config.yaml
network:
  enable_ipv6: true

wifi:
  ssid: !secret ssid
  password: !secret password

# enable api for connecting to home assistant
#api:

ota:
  - platform: esphome

# Enable logging
logger:
  level: debug
  logs:
    wifi: info
    api: info
    mdns: info

i2s_audio:
  i2s_bclk_pin: 23
  i2s_lrclk_pin: 13

snapclient:
  hostname: !secret host
  # port: 1704  # default
  name: !secret name
  mute_pin: 18
  # audio_dac: audio_dac_id
  i2s_dout_pin: 14
  # webserver_port: 8080  # start webserver for equalizer

Checklist:

  • [x] The code change is tested and works locally.
  • [ ] Tests have been added to verify that the new code works (under tests/ folder).

If user exposed functionality or configuration variables are added/changed:

luar123 avatar Mar 01 '25 18:03 luar123

Hey there @luar123, Thanks for submitting this pull request! Can you add yourself as a codeowner for this integration? This way we can notify you if a bug report for this integration is reported. In __init__.py of the integration, please add:

CODEOWNERS = ["@luar123"]

And run script/build_codeowners.py

(message by NeedsCodeownersLabel)

probot-esphome[bot] avatar Mar 01 '25 18:03 probot-esphome[bot]

Codecov Report

:white_check_mark: All modified and coverable lines are covered by tests. :white_check_mark: Project coverage is 60.46%. Comparing base (84ffa42) to head (a3bd76c). :warning: Report is 396 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #8350      +/-   ##
==========================================
+ Coverage   60.11%   60.46%   +0.35%     
==========================================
  Files          51       51              
  Lines       10302    10422     +120     
  Branches     1371     1390      +19     
==========================================
+ Hits         6193     6302     +109     
- Misses       3754     3762       +8     
- Partials      355      358       +3     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov-commenter avatar Mar 01 '25 18:03 codecov-commenter

What's the minimal configuration for this to work? I tried it with Music Assistant - player was recognized, but is silent although MA is reporting playback. Hardware - ESP32-S3 Devkit-C N8R2. Do i need N16R8 for this?

formatBCE avatar Apr 03 '25 23:04 formatBCE

The example config should work as is, but there are limitations: My normal setup is an old esp32 without psram and pcm5102a DAC. This works fine as long as the snapserver config is setup properly: format: 44100/48000:16:2 codec: flac (opus/pcm should work as well, didn't test) chunk_ms: not too large, 20 ms works well buffer: not larger than 750 ms, maybe 700 ms to be save. (Default is normally 1000ms) Larger settings require PSRAM.

Not sure what settings are used by Musicassistant and how to change them.

I just tried with a ESP32S3 N16R8. Without activating PSRAM, same limitations apply. Adding:

psram:
  mode: octal

allows to increase the buffer, so this works as well. N8R2 should be enough, but you need to make sure PSRAM is working, or use the settings from above. Check the logs for:

[17:25:18]I (348) octal_psram: vendor id    : 0x0d (AP)
[17:25:18]I (352) octal_psram: dev id       : 0x02 (generation 3)
[17:25:18]I (358) octal_psram: density      : 0x03 (64 Mbit)
[17:25:18]I (364) octal_psram: good-die     : 0x01 (Pass)
[17:25:18]I (369) octal_psram: Latency      : 0x01 (Fixed)
[17:25:18]I (374) octal_psram: VCC          : 0x01 (3V)
[17:25:18]I (379) octal_psram: SRF          : 0x01 (Fast Refresh)
[17:25:18]I (385) octal_psram: BurstType    : 0x01 (Hybrid Wrap)
[17:25:18]I (391) octal_psram: BurstLen     : 0x01 (32 Byte)
[17:25:18]I (396) octal_psram: Readlatency  : 0x02 (10 cycles@Fixed)
[17:25:18]I (403) octal_psram: DriveStrength: 0x00 (1/1)
[17:25:18]I (408) esp_psram: Found 8MB PSRAM device
[17:25:18]I (412) esp_psram: Speed: 40MHz

luar123 avatar Apr 06 '25 15:04 luar123

What's the minimal configuration for this to work? I tried it with Music Assistant - player was recognized, but is silent although MA is reporting playback. Hardware - ESP32-S3 Devkit-C N8R2. Do i need N16R8 for this?

Are you 100% you've got the pin out right?

HarvsG avatar Apr 06 '25 16:04 HarvsG

What's the minimal configuration for this to work? I tried it with Music Assistant - player was recognized, but is silent although MA is reporting playback. Hardware - ESP32-S3 Devkit-C N8R2. Do i need N16R8 for this?

Are you 100% you've got the pin out right?

Checked several times... Probably DAC is faulty... Even with 700ms buffer I can't hear anything.

formatBCE avatar Apr 06 '25 16:04 formatBCE

Does it work with a different component, like media_player?

On Sun, 6 Apr 2025, 17:51 Andrii Mitnovych, @.***> wrote:

What's the minimal configuration for this to work? I tried it with Music Assistant - player was recognized, but is silent although MA is reporting playback. Hardware - ESP32-S3 Devkit-C N8R2. Do i need N16R8 for this?

Are you 100% you've got the pin out right?

Checked several times... Probably DAC is faulty... Even with 700ms buffer I can't hear anything.

— Reply to this email directly, view it on GitHub https://github.com/esphome/esphome/pull/8350#issuecomment-2781509221, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACXJC2QARJBLTU4ZM46KLZL2YFLRHAVCNFSM6AAAAABYELLXROVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDOOBRGUYDSMRSGE . You are receiving this because you are subscribed to this thread.Message ID: @.***> [image: formatBCE]formatBCE left a comment (esphome/esphome#8350) https://github.com/esphome/esphome/pull/8350#issuecomment-2781509221

What's the minimal configuration for this to work? I tried it with Music Assistant - player was recognized, but is silent although MA is reporting playback. Hardware - ESP32-S3 Devkit-C N8R2. Do i need N16R8 for this?

Are you 100% you've got the pin out right?

Checked several times... Probably DAC is faulty... Even with 700ms buffer I can't hear anything.

— Reply to this email directly, view it on GitHub https://github.com/esphome/esphome/pull/8350#issuecomment-2781509221, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACXJC2QARJBLTU4ZM46KLZL2YFLRHAVCNFSM6AAAAABYELLXROVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDOOBRGUYDSMRSGE . You are receiving this because you are subscribed to this thread.Message ID: @.***>

HarvsG avatar Apr 06 '25 17:04 HarvsG

Does it work with a different component, like media_player?

Thanks for suggestions. On different MAX98357 module it works!

formatBCE avatar Apr 06 '25 18:04 formatBCE

Hi @luar123 , thanks a lot for this component. To make it work with the louder-esp32 from @anabolyc with a tas5805m dac, I needed this small change:

--- snapclient.h.org    2025-05-30 20:52:55.390203882 +0200
+++ snapclient.h        2025-05-30 20:55:13.147828632 +0200
@@ -6,6 +6,9 @@
 #include "esphome/core/defines.h"
 #include "esphome/core/component.h"
 #include "esphome/components/i2s_audio/i2s_audio.h"
+#ifdef USE_AUDIO_DAC
+  #include "esphome/components/audio_dac/audio_dac.h"
+#endif
 #include "esphome/core/gpio.h"
 
 namespace esphome {

I hope this get's merged into esphome since it works great for me.

c-MM avatar May 30 '25 22:05 c-MM

today I tried to make a esphome project using this modification in esphome 2025.7.2 I get the compile error:

toolchain-xtensa-esp-elf @ 13.2.0+20240530 Reading CMake configuration... -- git rev-parse returned 'fatal: not a git repository (or any parent up to mount point /) Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).' -- Building ESP-IDF components for target esp32 Invalid field "dependencies:espressif__esp-dsp": Invalid component name -- Configuring incomplete, errors occurred! fatal: not a git repository (or any parent up to mount point /) Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). CMake Error at /data/cache/platformio/packages/framework-espidf/tools/cmake/build.cmake:552 (message): ERROR: Manifest is not valid

what change is needed to make it work on the current esphome release?

lordp1 avatar Jul 20 '25 19:07 lordp1

Yes, due to #9163 __init__.py needs to be adapted.

luar123 avatar Jul 20 '25 20:07 luar123

thanks @luar123 for the hint, it's a one line change to make it compile again:

--- ./esphome1/components/snapclient/__init__.py        2025-06-08 09:46:49.441672742 +0200
+++ ./esphome2/components/snapclient/__init__.py        2025-07-24 11:11:20.309887245 +0200
@@ -53,7 +53,7 @@

 async def to_code(config):
     add_idf_component(
-        name="espressif__esp-dsp",
+        name="espressif/esp-dsp",
         repo="https://github.com/espressif/esp-dsp.git",
     )
     add_idf_component(

c-MM avatar Jul 24 '25 19:07 c-MM

@luar123 Do you think it's ,much work to get rid of the IPv6 requirement? The reason is, that I have one louder-esp32 with snapclient in my kitchen and the microwave oven makes it reboot, when playing.

So I tried connecting it via W5500 ethernet module. But unfortunately that does not work with IPv6 enabled. It fails on boot with "Enable IPv6 link local failed". If I comment out that block in line 548 in ethernet_component.cpp it works fine and plays even while heating meals. :-)

c-MM avatar Jul 24 '25 19:07 c-MM

@c-MM does it compile for you (clean build)? I had to add

platformio_options:
    build_flags: "-Wno-incompatible-pointer-types"

luar123 avatar Jul 26 '25 16:07 luar123

To use the changes from this PR as an external component, add the following to your ESPHome configuration YAML file:

external_components:
  - source: github://pr#8350
    components: [snapclient]
    refresh: 1h

(Added by the PR bot)

github-actions[bot] avatar Jul 26 '25 16:07 github-actions[bot]

luar123 Do you think it's ,much work to get rid of the IPv6 requirement?

I think at least with the current state from upstream master branch it would be a lot of work if possible at all. I know that CarlosDerSeher is working on this in his dev branch, but didn't try.

luar123 avatar Jul 26 '25 16:07 luar123

@c-MM does it compile for you (clean build)? I had to add

platformio_options:
    build_flags: "-Wno-incompatible-pointer-types"

@luar123 Yes it compiles fine for me. I just did a:

esphome clean louder-esp-test.yaml
esphome compile louder-esp-test.yaml

I'm compiling from my repo clone of your pull request:

external_components:
  - source: github://mrtoy-me/esphome-tas5805m@main
    components: [ tas5805m ]
    refresh: 0s
  - source: github://c-MM/esphome-snapclient@main
    components: [ snapclient ]
    refresh: 0s

Edit: I switched from the dev branch to 2025.07.3 (to test the tas5805m from @mrtoy-me.

c-MM avatar Jul 26 '25 16:07 c-MM

I guess you are on 2025.7.x. I am running on the current dev environment which switched to idf5.4. So keep that in mind when 2025.8 is released.

luar123 avatar Jul 26 '25 16:07 luar123

ok got the yaml from 'raw.githubusercontent.com/mrtoy-me/esphome-tas5805m/refs/heads/main/components/tas5805m/Example YAML/esp32_louder_snap_client.yaml' and got a dependencie error (even 'clean build files').

@luar123 I also would (later) use the w5500 ethernet on my module instead of wifi, would simply including the 'dev' branche of c-MM/esphome-snapclient not be enough?

`Processing 10 dependencies: [1/10] dsp_processor (38d749e6cc305cddfc62241fc60ffcde8056cf55) [2/10] espressif/esp-dsp (3c12d059f96ec95ed00769fb32d41ca23e20cdf1) [3/10] espressif/mdns (1.8.2) [4/10] flac (38d749e6cc305cddfc62241fc60ffcde8056cf55) [5/10] libbuffer (38d749e6cc305cddfc62241fc60ffcde8056cf55) [6/10] libmedian (38d749e6cc305cddfc62241fc60ffcde8056cf55) [7/10] lightsnapcast (38d749e6cc305cddfc62241fc60ffcde8056cf55) [8/10] mdns (ffeee3e87a16c010953e597842a7278fdde8da73) [9/10] opus (38d749e6cc305cddfc62241fc60ffcde8056cf55) [10/10] idf (5.3.2) -- Configuring incomplete, errors occurred!

fatal: not a git repository (or any parent up to mount point /) Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). CMake Error at /data/cache/platformio/packages/framework-espidf/tools/cmake/component.cmake:251 (message): ERROR: Cannot process component requirements. Multiple candidates to satisfy project requirements:

requirement: "mdns" candidates: "espressif__mdns, mdns"

Call Stack (most recent call first): /data/cache/platformio/packages/framework-espidf/tools/cmake/build.cmake:586 (__component_get_requirements) /data/cache/platformio/packages/framework-espidf/tools/cmake/project.cmake:710 (idf_build_process) CMakeLists.txt:3 (project) `

lordp1 avatar Jul 27 '25 18:07 lordp1

Ah yes, please delete the whole .esphome/build/<name> folder. esphome clean is not enough.

luar123 avatar Jul 27 '25 20:07 luar123

Ah yes, please delete the whole .esphome/build/<name> folder. esphome clean is not enough.

made an new esp-home project, copied there the yaml and still got the same error. was searching for the esphome folder, using the terminal app. but didn't found the path.. where can i find it? in '/root/homeassistant/esphome' there are only the .yaml files no build folder

also got the warning "WARNING The refresh, components and submodules parameters in add_idf_component() are deprecated and will be removed in ESPHome 2026.1. If you are seeing this, report an issue to the external_component author and ask them to update it." could it have anything to do with this (2 methode for dependencies running next to each other?

lordp1 avatar Jul 27 '25 20:07 lordp1

Are you running esphome from the addon? See https://github.com/esphome/esphome/issues/9895

.Esphome folder is hidden and should be in the same folder as yaml file, but maybe this is different when running from addon.

luar123 avatar Jul 28 '25 06:07 luar123

@luar123 yes i running the addon. after mutiple restarts and cleans it still will give the same issue. (i see that esphome will make a new enviroment and get all the files) so it seems there is still a bug in this component (will not compile)

lordp1 avatar Aug 02 '25 13:08 lordp1

I tried it here with 2025.7.3 and dev, both are working fine if full .esphome folder is deleted. Can't test the addon.

What config are you using? Please try the config from this PR with

external_components:
  - source: github://pr#8350
    components: [snapclient]
    refresh: 1h

luar123 avatar Aug 02 '25 13:08 luar123

Thanks, changed external-source to this PR (was still on github://c-MM/esphome-snapclient@main) and now it compliles.

like c-MM i want to get rid of the ipv6 (because want to use w5500 on my louder board).

I think at least with the current state from upstream master branch it would be a lot of work if possible at all. I know that CarlosDerSeher is working on this in his dev branch, but didn't try.

is there maybe a way to change to this dev-branch in this pr ? so i can test it?

If I comment out that block in line 548 in ethernet_component.cpp it works fine and plays even while heating meals. :-)

or a way in the addon to overwrite this?

lordp1 avatar Aug 02 '25 14:08 lordp1

Basically there are two options:

  1. remove ipv6 config from yaml and try to fix build errors. If those are only in decoder.cpp it should be doable, but if there are some in the components loaded from snapclient repo it won't work. Or you would have to fork it and fix there. I tried this initially, but don't remember the details...settings ipv6 was easier.
  2. Change the ref of the snapclient components to the latest commit of the develop branch. But then you need to copy all relevant parts from Carlos main.c to decoder.cpp, adjust them a bit and make sure are dependencies are loaded as well. Is definitely a few hours work and might hit some blockers as well. -> I might look into this next winter.

luar123 avatar Aug 02 '25 14:08 luar123

yes it seems that the problem is in decoder.cpp with the ip4_addr changing u_addr to addr would maybe work..

src/esphome/components/snapclient/decoder.cpp: In function 'void esphome::snapclient::http_get_task(void*)': src/esphome/components/snapclient/decoder.cpp:529:58: error: 'ip_addr_t' {aka 'struct ip4_addr'} has no member named 'u_addr'; did you mean 'addr'? 529 | inet_pton(AF_INET, SNAPCAST_SERVER_HOST, &(remote_ip.u_addr.ip4.addr)); | ^~~~~~ | addr src/esphome/components/snapclient/decoder.cpp:530:15: error: 'ip_addr_t' {aka 'struct ip4_addr'} has no member named 'type' 530 | remote_ip.type = IPADDR_TYPE_V4; | ^~~~ src/esphome/components/snapclient/decoder.cpp:600:33: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings] 600 | hello_message.client_name = "libsnapcast"; | ^~~~~~~~~~~~~ src/esphome/components/snapclient/decoder.cpp:601:24: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings] 601 | hello_message.os = "esp32"; | ^~~~~~~ src/esphome/components/snapclient/decoder.cpp:602:26: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings] 602 | hello_message.arch = "xtensa"; | ^~~~~~~~ Compiling .pioenvs/louder-esp-keuken/src/esphome/components/socket/socket.cpp.o Compiling .pioenvs/louder-esp-keuken/src/esphome/components/switch/automation.cpp.o *** [.pioenvs/louder-esp-keuken/src/esphome/components/snapclient/decoder.cpp.o] Error 1

lordp1 avatar Aug 02 '25 14:08 lordp1

ok forked it and made some minnor changed (only needed in decoder.cpp) and compiles now with ethernet (without ipv6). still have to test it when i am home..

lordp1 avatar Aug 02 '25 15:08 lordp1

If it is working you can create a PR against my snapclient branch or simply post the diff here.

luar123 avatar Aug 02 '25 15:08 luar123

ok tested now and works.

yaml changes:

external_components:

source: github://mrtoy-me/esphome-tas5805m@main
components: [ tas5805m ]
refresh: 0s
source: github://lordp1/esphome@dev
components: [snapclient]
refresh: 0s

ethernet: type: W5500 clk_pin: GPIO18 mosi_pin: GPIO23 miso_pin: GPIO19 cs_pin: GPIO5 interrupt_pin: GPIO35 reset_pin: GPIO14 clock_speed: 20MHz

network: enable_ipv6: false

And comment out wifi component (ethernet and wifi can't run at the same time

changes in decoder.cpp:

// configure a failsafe snapserver according to CONFIG values

struct sockaddr_in servaddr;

servaddr.sin_family = AF_INET; inet_pton(AF_INET, SNAPCAST_SERVER_HOST, &(servaddr.sin_addr.s_addr)); servaddr.sin_port = htons(SNAPCAST_SERVER_PORT);

// Set up the remote IP address using ip_addr_t if (inet_pton(AF_INET, SNAPCAST_SERVER_HOST, &remote_ip) == 1) { // The IP address is now set in remote_ip // No need to set remote_ip.type; it's handled internally in ip_addr_t } else { // Handle error: invalid IP address }

// Set the remote port remotePort = SNAPCAST_SERVER_PORT;

ESP_LOGI(TAG, "try connecting to static configuration %s:%d", ipaddr_ntoa(&remote_ip), remotePort);

probably need to make a compiler-check if ipv6 is enabled to run old code, and only use this when ipv4 is enabled. but works stable for me now..

lordp1 avatar Aug 03 '25 12:08 lordp1