Snapcast + TTS

Let me elaborate about the TTS named pipe. It has nothing to do with Mopidy, so it won’t be in the Mopidy configuration. It only exists so that ffmeg (or avconv for you) can play to it and the snapserver can read from it.

Well, it makes sense now…
Maybe you could share it with me ? here is a picture of me right now

If not i’m gonna have to have a cmd script playing the most recent TTS generated file ? (i do not know how to get the name of the file ?) but it’s not as classy as your solution !

I did understand that mopidy has nothing to do with it, i guess i was blind when i put that link in my post…
(i’ll remove it to make my topic clearer)

tried it, seems to work OK :

shell_command:
  lasttts: cd /tmp/tts && avconv -y -i $(ls -t | head -n1) -f u16le -acodec pcm_s16le -ac 2 -ar 48000 /tmp/snapcast_tts
1 Like

Here’s a diff of my hacky changes: http://pastebin.com/raw/mpgLMrFG

1 Like

Wow, thank you @happyleaves !

It works perfeclty, and as i saw from your code could probably do that PR !

One thing tho, is that out of the box you cannot do TTS to the whole house like this for example :

  - service: tts.google_say
    data_template:
      entity_id: 'media_player.mirror,media_player.livingroom,media_player.bedroom'
      message: 'Salut !' 

this is actually plays the TTS in the pipe multiples times (but i guess it’s more related to the way tts is implemented).

i had to do this script :

snapcast_tts:
  alias: 'Snapcast TTS'
  sequence:
    - service: media_player.select_source
      data_template:
        entity_id: 'media_player.livingroom,media_player.bedroom'
        source: 'TTS'
    - service: shell_command.alert_tone
    - service: tts.google_say
      data_template:
        entity_id: 'media_player.mirror'
        message: '{{ message }}'
    - delay: '00:00:05'
    - service: media_player.select_source
      data_template:
        entity_id: 'media_player.livingroom,media_player.bedroom'
        source: 'mopidy'

(mirror is always on TTS pipe, i did saw that your modification does an automatic pipe toogle, but mirror speakers are good only for TTS)

To get a tone before the TTS message i used this :

shell_command:
  alert_tone: avconv -y -i /home/homeassistant/.homeassistant/CTN-Delayed.mp3 -f u16le -acodec pcm_s16le -ac 2 -ar 48000 /tmp/snapcast_tts

It works really great !

One last thing missing : to be able to know how long the TTS message is and have a dynamic delay so my other speakers do not cut off too early (saw that on forum for sonos).
I think ffmeg/avconv should be a media_player too (or maybe use vlc ?).

Yep, these are the same problems I am struggling with right now. I’ve sketched out a lot of ideas, but I am not sure yet how to proceed. Some various thoughts:

  • A “coordinator” snapserver “media player”. tts to that media_player and it plays once, all the snapclients just have to be pointing to the same source.
  • A snapshot and restore service (like Sonos has) that saves source and volume. (I’ve already implemented this one!)
  • TTS message duration: This is a tough. You’re right, we could leverage ffmpeg/avconv/vlc as a media_player and make sure it has a attribute.duration property. Or snapcast's play_media could emit a media_done event when it is done playing. Then you have an automation that triggers on that message, and can switch the source back.

I agree about all theses ideas.

Here is something we should be able to do : SONOS TTS Script

Vlc integration offers something like this :

media_player:
  - platform: vlc
    name: speaker_1
    arguments: '--alsa-audio-device=hw:1,0'

I wonder if using the arguments parametter would not allow us to play on the snapcast pipe ?

agreend on the coordinator, but you should make sure to not prevent the use of multiple media_player on the same snapclients !
Maybe we should not consider the snapclients as media_players only ? (as they can’t play anything by themselves).

Another idea :
have a way to attach to the pipes(sources in HASS) a media_player(mpd for music, ffmpeg for TTS).
that way if we try to pause a snapclient it will actually pause the coresponding media_player.

this might be the way to go : https://home-assistant.io/components/media_player.universal/ ?

These are some good ideas. I like the thought of having a HASS media player that actually does the TTS. I think we can eliminate a snapcast “coordinator” that way. Just like you say, mpd for music, ____ for TTS.

I believe the best way to know when a TTS is done, is to look at the media player state. It should go from playing to idle. You’ll notice this behavior if you pause MPD, for example. The problem is that using avconv or ffmpeg, snapcast does not ever go back to idle, at least on my system. It isn’t a bug in HASS or my python-snapcast library - you can observe the same behavior in the Snapcast Android client. I filed an issue here: https://github.com/badaix/snapcast/issues/180

Also, we can probably use https://github.com/b-fitzpatrick/cpiped to direct audio to the named pipe. That way the source does not need the ability to write to a pipe (I think vlc does not, or at least I couldn’t find it).

Ok, I have an update on this.

I just completed a proof-of-concept media player that is Mopidy-like. That means HASS can play media files directly, and accept ALSA pipelines. So you can configure this media player with the same audio output string as Mopidy (to play to a named pipe), and then HASS can directly play TTS to the pipe. Eliminates a lot of complexity.

PR if anyone wants to try the aforementioned component: https://github.com/home-assistant/home-assistant/pull/5839

This sounds really cool but I’m having trouble getting the gstreamer component to load. I’m running HA 0.39.2 on a Raspberry Pi 3, setup with the AIO installer.

I installed all the system dependencies and symlinked the system Python’s gi module into my virtual environment:

sudo ln -s /usr/lib/python2.7/dist-packages/gi /srv/hass/hass_venv/lib/python3.4/site-packages/gi

but when I start HA it throws the following error:

Mar 03 11:30:03 raspberrypi2 hass[29489]: 17-03-03 11:30:03 ERROR (MainThread) [homeassistant.components.media_player] Error while setting up platform gstreamer
Mar 03 11:30:03 raspberrypi2 hass[29489]: Traceback (most recent call last):
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py", line 151, in _async_setup_platform
Mar 03 11:30:03 raspberrypi2 hass[29489]: entity_platform.add_entities, discovery_info
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/usr/lib/python3.4/asyncio/futures.py", line 388, in __iter__
Mar 03 11:30:03 raspberrypi2 hass[29489]: yield self  # This tells Task to wait for completion.
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/usr/lib/python3.4/asyncio/tasks.py", line 286, in _wakeup
Mar 03 11:30:03 raspberrypi2 hass[29489]: value = future.result()
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/usr/lib/python3.4/asyncio/futures.py", line 277, in result
Mar 03 11:30:03 raspberrypi2 hass[29489]: raise self._exception
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/usr/lib/python3.4/concurrent/futures/thread.py", line 54, in run
Mar 03 11:30:03 raspberrypi2 hass[29489]: result = self.fn(*self.args, **self.kwargs)
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/components/media_player/gstreamer.py", line 41, in setup_platform
Mar 03 11:30:03 raspberrypi2 hass[29489]: from gsp import GstreamerPlayer
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/home/hass/.homeassistant/deps/gsp/__init__.py", line 11, in <module>
Mar 03 11:30:03 raspberrypi2 hass[29489]: import gi  # pylint: disable=import-error
Mar 03 11:30:03 raspberrypi2 hass[29489]: File "/srv/hass/hass_venv/lib/python3.4/site-packages/gi/__init__.py", line 42, in <module>
Mar 03 11:30:03 raspberrypi2 hass[29489]: from . import _gi
Mar 03 11:30:03 raspberrypi2 hass[29489]: ImportError: /srv/hass/hass_venv/lib/python3.4/site-packages/gi/_gi.so: undefined symbol: _Py_ZeroStruct

Any thoughts?

You need to symlink the python 3 version of gi. You’ve got the python 2 version there :slight_smile:

Well sure, when you say it like that it seems obvious… :grin:

I didn’t actually see the Python3 version of gi until I installed it:

sudo pip3 install gi --upgrade

Then I symlinked that version:

sudo ln -s /usr/local/lib/python3.4/dist-packages/gi /srv/hass/hass_venv/lib/python3.4/site-packages

but I’m still getting a very similar error:

Mar 05 12:54:22 raspberrypi2 hass[28324]: 17-03-05 12:54:22 ERROR (MainThread) [homeassistant.components.media_player] Error while setting up platform gstreamer
Mar 05 12:54:22 raspberrypi2 hass[28324]: Traceback (most recent call last):
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py", line 151, in _async_setup_platform
Mar 05 12:54:22 raspberrypi2 hass[28324]: entity_platform.add_entities, discovery_info
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/usr/lib/python3.4/asyncio/futures.py", line 388, in __iter__
Mar 05 12:54:22 raspberrypi2 hass[28324]: yield self  # This tells Task to wait for completion.
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/usr/lib/python3.4/asyncio/tasks.py", line 286, in _wakeup
Mar 05 12:54:22 raspberrypi2 hass[28324]: value = future.result()
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/usr/lib/python3.4/asyncio/futures.py", line 277, in result
Mar 05 12:54:22 raspberrypi2 hass[28324]: raise self._exception
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/usr/lib/python3.4/concurrent/futures/thread.py", line 54, in run
Mar 05 12:54:22 raspberrypi2 hass[28324]: result = self.fn(*self.args, **self.kwargs)
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/components/media_player/gstreamer.py", line 41, in setup_platform
Mar 05 12:54:22 raspberrypi2 hass[28324]: from gsp import GstreamerPlayer
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/home/hass/.homeassistant/deps/gsp/__init__.py", line 11, in <module>
Mar 05 12:54:22 raspberrypi2 hass[28324]: import gi  # pylint: disable=import-error
Mar 05 12:54:22 raspberrypi2 hass[28324]: File "/srv/hass/hass_venv/lib/python3.4/site-packages/gi/__init__.py", line 39
Mar 05 12:54:22 raspberrypi2 hass[28324]: print url
Mar 05 12:54:22 raspberrypi2 hass[28324]: ^
Mar 05 12:54:22 raspberrypi2 hass[28324]: SyntaxError: Missing parentheses in call to 'print'

Something is wrong there … that’s python 2 syntax in your symlinked gi. How did you install gst?

I installed the required dependencies using the apt-get command provided in the Gstreamer component’s documentation. Even after uninstalling and reinstalling them the Python3 version of the gi module isn’t available:

$ python3 -c "import gi"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named 'gi'

By the way, I get the same syntax error if I create a fresh virtual environment, install gi and attempt to import it:

virtualenv -p /usr/bin/python3 venv
source venv/bin/activate
pip3 install gi
python3

then

Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import gi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/pi/gi-test/venv/lib/python3.4/site-packages/gi/__init__.py", line 39
    print url
            ^
SyntaxError: Missing parentheses in call to 'print'

Do you know if there’s any way to use the GStreamer component within the Docker image?

@happyleaves I found the problem: python-gst-1.0 installs the Python 2 version of gi. I believe the documentation should list python3-gst-1.0 instead.

Hmm, I’m having trouble setting up gstreamer, and I did use python3-gst-1.0

I also get

python3 -c "import gi" Traceback (most recent call last): File "", line 1, in File "/srv/homeassistant/homeassistant_venv/lib/python3.4/site-packages/gi/__init__.py", line 39 print url ^ SyntaxError: Missing parentheses in call to 'print'

Any suggestions?

Did you install python-gst-1.0 or python3-gst-1.0?

I used python3-gst-1.0 (as I referenced at the top of my post)

I followed the config guide at https://home-assistant.io/components/media_player.gstreamer/ which seems to have been updated to include the correct reference. (You know it’s updated, you updated it lol)

But it’s still throwing the syntax based error… [scratches head]

From the start of my attempts, I had used python3-gst-1.0. After I found this thread, I also ran sudo apt-get remove python-gst-1.0 and it was not installed. So I can be sure I only have the python3 version