Speak service

I created a simple speech service using pico tts to be able to announce some message.
I’m using home assistant on a raspberry pi with raspbian.
You need libttspico-utils to make it works

sudo apt install libttspico-utils

Content of /custom_components/speech.py

import tempfile
import subprocess
import os

DOMAIN = 'speech'

DEFAULT_TEXT = 'test'
DEFAULT_LANGUAGE = 'fr-FR'

def setup(hass, config):
    """Setup is called when Home Assistant is loading our component."""

    def handle_speak(call):
        text = call.data.get('text', DEFAULT_TEXT)
        lang = call.data.get('lang', DEFAULT_LANGUAGE)

        say(text,lang)

    hass.services.register(DOMAIN, 'speak', handle_speak)

    # Return boolean to indicate that initialization was successfully.
    return True

def say(text,lang):
    with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as f:
        fname = f.name
    cmd = ['pico2wave', '--wave', fname]
    cmd.extend(['-l', lang])
    cmd.append(text)
    with tempfile.TemporaryFile() as f:
        subprocess.call(cmd, stdout=f, stderr=f)
        f.seek(0)
        output = f.read()
    play(fname)
    os.remove(fname)
    
def play(filename):
    cmd = ['aplay', str(filename)]
    with tempfile.TemporaryFile() as f:
        subprocess.call(cmd, stdout=f, stderr=f)
        f.seek(0)
        output = f.read()

To use it, add the following in your configuration.yml
speech:

and just call speech.speak service with variable text and optionnally lang

3 Likes

This is amazing what does it play though

Please say sonos…

Very cool!!

Yes interested how to connect speakers to it. Supercool having Alexa dot answer :smiley:

But since Raspberry Pi has a jack audio output, you can connect any device :slight_smile:

Currently I use a USB speaker “Polycom communicator c100” (because my next step would record sound too)

Yes correct, beside also bluetooth

I just upgrade the code to follow the notify platform

Put this in [config]/custom_components/notify/speech.py

"""
Support for tts speech notification services.
"""
import logging
import subprocess
import tempfile

import voluptuous as vol

CONF_LANGUAGE = 'language'
from homeassistant.components.notify import (
    BaseNotificationService, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_LANGUAGE): cv.string,
})


def get_service(hass, config):
    """Get the tts notification service."""
    language = config[CONF_LANGUAGE]

    return TTSNotificationService(language)


class TTSNotificationService(BaseNotificationService):
    """Implement the notification service for the TTS service."""

    def __init__(self, language):
        """Initialize the service."""
        self.language = language or 'fr-FR'

    def send_message(self, message="", **kwargs):
        """Say a message using tts"""
        try:
            say(message,self.language)
        except:
            _LOGGER.error('Error trying to say: %s', message)

def say(text,lang):
    with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as f:
        fname = f.name
    cmd = ['pico2wave', '--wave', fname]
    cmd.extend(['-l', lang])
    cmd.append(text)
    with tempfile.TemporaryFile() as f:
        subprocess.call(cmd, stdout=f, stderr=f)
        f.seek(0)
        output = f.read()
    play(fname)
    os.remove(fname)
    
def play(filename):
    cmd = ['aplay', str(filename)]
    with tempfile.TemporaryFile() as f:
        subprocess.call(cmd, stdout=f, stderr=f)
        f.seek(0)
        output = f.read()

and add the following config

notify:
  - name: speak
    platform: speech
    language: fr-FR
2 Likes

Can you give me a real world example, and the steps to be done

Newb here

any time you use the notify service, you can use it.
Typically, I use it to inform about fire alarm, I have a z-wave smoke sensor , when smoke is detected, the system says something like “ALARM, Smoke detected in living”

you can use it to “talk” to intruders, etc, etc.

Very Nice. Anyway you can get this output to a Sonos system (not via the headphone jack but directly to Sonos) and/or Amazon Echo/Google Home?

@tyfoon this is a reciever not a transmitter.

this program does something with output from hass, it does not provide data so there is nothing to get to sonos or amazon echo.

see this program as the speaker, for your needs you need a microphone.

I think you misunderstood what @Tyfoon is trying to achieve here: he wants (I guess) to send the audio stream (or play the audio file created by pico2wave to player such as a Sonos system.

I don’t know anything about Sonos, if it can play remote wav file it’s possible

Sonos is able to play remote file using http, so it’s possible to use pico2wave to generate the wav file and serve this file over Home Assistant but currently the HA sonos component can’t ask sonos to play a file. I think adding the function is not a big deal.

i thought that with systems like amazon echo you just need to send data and that system translates that to speech.
i wouldnt think off using tts before the data is on the device which has to make the sound.

how it should be:

hass => data => network => device => tts => wav => speaker

not:

hass => data => tts => wav => network => device => speaker

the last option is only an option if there is no option to use tts on the device.
but because of the fact that amazon echo like devices also can read out newsfeeds, they must have a form off tts on board.

Sonos can play mp3 files. Is it possible to pass on an mp3 file to Sonos?

Thank you for creating this! I’ve been wanting a TTS notifier for a while now.

However, it doesn’t work for me. I’m calling the service from the Services web interface, sending the json data:

{"message":"'hello'"}

But I don’t hear anything. In the HASS log I get this:

Dec 08 22:16:22 raspberrypi hass[4464]: INFO:homeassistant.core:Bus:Handling <Event call_service[L]: domain=notify, service_call_id=1975881712-9, service_data=message='hello', service=speak>
Dec 08 22:16:23 raspberrypi hass[4464]: ERROR:custom_components.notify.speech:Error trying to say: 'hello'

But there’s no further information. Any ideas? I’m running Raspbian, not Hassbian.

did you also try:

{“message”:“hello”}

?

Alright, I fixed it. There were two problems: you didn’t import os (that caused the main error), and sudo is needed for playback. This may be specific to my installation.

Here’s the full code that works for me. You can see I added traceback info, that’s how I found the os error.

"""
Support for tts speech notification services.
"""
import logging
import subprocess
import tempfile
import traceback
import os

import voluptuous as vol

CONF_LANGUAGE = 'language'
from homeassistant.components.notify import (
    BaseNotificationService, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_LANGUAGE): cv.string,
})


def get_service(hass, config):
    """Get the tts notification service."""
    language = config[CONF_LANGUAGE]

    return TTSNotificationService(language)


class TTSNotificationService(BaseNotificationService):
    """Implement the notification service for the TTS service."""

    def __init__(self, language):
        """Initialize the service."""
        self.language = language or 'fr-FR'

    def send_message(self, message="", **kwargs):
        """Say a message using tts"""
        try:
            say(message,self.language)
        except:
            _LOGGER.error('Error trying to say: %s', message)
            _LOGGER.error(traceback.format_exc())

def say(text,lang):
    with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as f:
        fname = f.name
    cmd = ['pico2wave', '--wave', fname]
    cmd.extend(['-l', lang])
    cmd.append(text)
    with tempfile.TemporaryFile() as f:
        subprocess.call(cmd, stdout=f, stderr=f)
        f.seek(0)
        output = f.read()
    play(fname)
    os.remove(fname)
    
def play(filename):
    cmd = ['sudo','aplay', str(filename)]
    with tempfile.TemporaryFile() as f:
        subprocess.call(cmd, stdout=f, stderr=f)
        f.seek(0)
        output = f.read()
1 Like

I’m not having any luck. I used your script since I require sudo but I dont receive any error messages or audio playback.

how to use this script then in hass?