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
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”
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.
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.
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.
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()