In this post, I’d like to show you how to run Piper on a external separate computer, outside your Home Assistant server.

In this post, I’ve included some screenshots with chinese localization. I hope it’s easy enough to follow in other languages.

Run a Wyoming protocol piper server on external server that anyone can connect to

There are two ways to go about starting the containers.

First, you could define a docker compose file with both services inside.

Second, you can execute each docker run command on its own, since the containers don’t need special configuration. We will go with the second option.

 

This tutorial explains how you can run a single-container text to speech service on your local machine using Docker.

GitHub - rhasspy/wyoming-piper: Wyoming protocol server for Piper

Prerequisites

  • use the docker image of the HA OS addon
  • Docker

Note that these libraries are only supported on x86 architectures.

 

The easier way is to use official docker image and you can run it on another host if you want, just specify the right IP address and ports when you will configure the integrations. You can use this working solution based on docker compose or run the containers manually

Quick Start

pull image

docker pull rhasspy/wyoming-piper 

change path and Rename container

change

-v /opt/whisper-piper/data:/data
to
-v /path/to/local/data:/data

run the container.

Whisper and piper are discoveryed by home assistant.

add wyoming integration

For host I selected localhost and for port 10200 and 10300

 

 

Step 1. found this  official  docker image

 docker search wyoming-piper

NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
rhasspy/wyoming-piper             Wyoming protocol server for piper text to sp…   22                   
dustynv/wyoming-piper             https://github.com/dusty-nv/jetson-container…   0                    
vioneta/wyoming-piper                                                             0                    
pcic/sandpiper                                                                    0                    
piperr/piperr                     Docker image used in bitbucket                  0                    
ppiper/cf-cli                     Cloud Foundry Command Line Client               4                    

 

what about wyoming-piper 

Home Assistant Add-on: Piper

Home Assistant add-on(https://github.com/home-assistant/addons/tree/master/piper) that uses dockerfile(https://github.com/home-assistant/addons/blob/master/piper/Dockerfile)  for text-to-speech.

The documentation within the repo only says:

Home Assistant add-on's dockerfile that uses 

 && pip3 install --no-cache-dir \
        "wyoming-piper @ https://github.com/rhasspy/wyoming-piper/archive/refs/tags/v${WYOMING_PIPER_VERSION}.tar.gz" \
    \
    && ARCH="${BUILD_ARCH}" \
    && if [[ "${BUILD_ARCH}" = "aarch64" ]]; then ARCH="arm64"; fi \
    && curl -L -s \
        "https://github.com/rhasspy/piper/releases/download/v${BINARY_PIPER_VERSION}/piper_${ARCH}.tar.gz" \
        | tar -zxvf - -C /usr/share \

 

This is a release of the official repo.

arm64:https://github.com/rhasspy/piper

amd64:https://github.com/rhasspy/wyoming-piper

 

for text-to-speech.

source:

https://github.com/home-assistant/addons

https://github.com/home-assistant/addons/tree/master/piper

detail:https://www.matterxiaomi.com/boards/topic/16775/how-to-manually-install-piper-and-whisper-on-home-assistant-os#23820

Step 2. docker pull wyoming-piper

docker pull rhasspy/wyoming-piper 
 

run the image in interactive mode:

mkdir /path/to/local/data
docker run -it -p 10200:10200 \
  -v /path/to/local/data:/data \
  rhasspy/wyoming-piper \
  --voice voice en_US-lessac-medium
or
 
docker run  -p 10200:10200 -v /path/to/local/data:/data rhasspy/wyoming-piper \
    --voice en_US-lessac-medium

custom voice model download

 
You can find the available models here:https://huggingface.co/rhasspy/piper-voices/tree/v1.0.0

Step 3. Starting piper

This will start the container in detached mode:

mkdir /path/to/local/data
docker run -d -p 10200:10200 \
  -v /root/piper-data:/data \
  rhasspy/wyoming-piper \
  --voice de_DE-kerstin-low
 

The build process uses configuration files from the chuck_var directory. The resulting image will serve two pretrained models (en-us-multimedia and fr-fr-multimedia) supporting English (en_US) and French (fr_FR).

Other models can be added to support other languages by updating the provided Dockerfile, as well as env_config.json and sessionPools.yaml in the chuck_var directory.

Step 4. Run the container to start the wyoming-piper text to speech service

 

The --voice argument can be the path to a custom voice file (<voice>.onnx). The voice config file must be named <voice>.onnx.json.

 

Run a piper server that anyone can connect to:

docker run -it -p 10200:10200 -v /path/to/local/data:/data rhasspy/wyoming-piper \
    --voice en_US-lessac-medium

This will automatically download voice files the first time they're used.

 

come from:https://github.com/rhasspy/wyoming-piper

Step 5. Query the Piper text to speech service

Open up another terminal to query the service. To start, get the language models available from the service:

docker ps -a
CONTAINER ID   IMAGE                              COMMAND                  CREATED       STATUS        PORTS                                                     NAMES
717fe1966138   yaming116/sherpa-onnx-asr:latest   "python app.py"          2 weeks ago   Up 7 days     5001/tcp, 0.0.0.0:10700->10700/tcp, :::10700->10700/tcp   sherpa-onnx-asr
1ee50dbb7a33   rhasspy/wyoming-piper              "bash /run.sh --voic…"   2 weeks ago   Up 7 days     0.0.0.0:10200->10200/tcp, :::10200->10200/tcp             nice_hofstadter
01b832193e61   rhasspy/wyoming-openwakeword       "bash /run.sh --prel…"   2 weeks ago   Up 7 days     0.0.0.0:10400->10400/tcp, :::10400->10400/tcp             gallant_engelbart
0389595d9f22   rhasspy/wyoming-whisper            "bash /run.sh --mode…"   2 weeks ago   Up 18 hours   0.0.0.0:10300->10300/tcp, :::10300->10300/tcp             nostalgic_jang
 
 

You will see output similar to the following:

"HostConfig": {
            "Binds": [
                "/path/to/local/data:/data"

 

docker inspect 1ee50dbb7a33
[
    {
        "Id": "1ee50dbb7a33db0324a5ba674829b4e9b4425aa961830fc343d97df953ed34ba",
        "Created": "2025-01-04T15:34:34.451109567Z",
        "Path": "bash",
        "Args": [
            "/run.sh",
            "--voice",
            "en_US-lessac-medium"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 1599,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2025-01-17T13:26:52.964878622Z",
            "FinishedAt": "2025-01-17T13:24:28.773341617Z"
        },
        "Image": "sha256:9e90dce8ae81a26e2e25d83a6b4a2a6ea7bb030dfe7f7c0f41078cf2e6771296",
        "ResolvConfPath": "/var/lib/docker/containers/1ee50dbb7a33db0324a5ba674829b4e9b4425aa961830fc343d97df953ed34ba/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/1ee50dbb7a33db0324a5ba674829b4e9b4425aa961830fc343d97df953ed34ba/hostname",
        "HostsPath": "/var/lib/docker/containers/1ee50dbb7a33db0324a5ba674829b4e9b4425aa961830fc343d97df953ed34ba/hosts",
        "LogPath": "/var/lib/docker/containers/1ee50dbb7a33db0324a5ba674829b4e9b4425aa961830fc343d97df953ed34ba/1ee50dbb7a33db0324a5ba674829b4e9b4425aa961830fc343d97df953ed34ba-json.log",
        "Name": "/nice_hofstadter",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "docker-default",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "/path/to/local/data:/data"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {
                "10200/tcp": [
                    {
                        "HostIp": "",
                        "HostPort": "10200"
                    }
                ]
            },
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "ConsoleSize": [
                24,
                151
            ],
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "private",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": [],
            "BlkioDeviceWriteBps": [],
            "BlkioDeviceReadIOps": [],
            "BlkioDeviceWriteIOps": [],
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": null,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware",
                "/sys/devices/virtual/powercap"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/949a5333206ccb8f8bd4b8a7a3e7e9e061a48914bea8cb1c7bffa2713e17b7ad-init/diff:/var/lib/docker/overlay2/20ba3514733e256976d5f671e0855f05e9443c31c56a7949577dbe901aaa5de8/diff:/var/lib/docker/overlay2/df017a73f80ce533538792b1fa4ff52d1e53d60210a488f5200f12c2ecead097/diff:/var/lib/docker/overlay2/1498bb473016d2ab72bc9724f8d54d9ebe63756c9bacd22f01c72b443046874c/diff:/var/lib/docker/overlay2/3a331ec2db7e6af14fad39b97511d4321b5a3481cf1ffbb1c5ea62f721b0d75b/diff",
                "MergedDir": "/var/lib/docker/overlay2/949a5333206ccb8f8bd4b8a7a3e7e9e061a48914bea8cb1c7bffa2713e17b7ad/merged",
                "UpperDir": "/var/lib/docker/overlay2/949a5333206ccb8f8bd4b8a7a3e7e9e061a48914bea8cb1c7bffa2713e17b7ad/diff",
                "WorkDir": "/var/lib/docker/overlay2/949a5333206ccb8f8bd4b8a7a3e7e9e061a48914bea8cb1c7bffa2713e17b7ad/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/path/to/local/data",
                "Destination": "/data",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
        "Config": {
            "Hostname": "1ee50dbb7a33",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "ExposedPorts": {
                "10200/tcp": {}
            },
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "--voice",
                "en_US-lessac-medium"
            ],
            "Image": "rhasspy/wyoming-piper",
            "Volumes": null,
            "WorkingDir": "/",
            "Entrypoint": [
                "bash",
                "/run.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "81914c7feb5dccfa2d1206b1cdd6b8d9ef093e57eb5eb7dad7bdfd60a7ffd504",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "10200/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "10200"
                    },
                    {
                        "HostIp": "::",
                        "HostPort": "10200"
                    }
                ]
            },
            "SandboxKey": "/var/run/docker/netns/81914c7feb5d",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "75914e44361ddd7d2fbb4a08c88f502d43ddf9fa29ae0872bf43fce4822033e4",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.3",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:03",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "00ceca59d76906efdaaa76c371b716ff489bed887cb9a5f023855fd076812873",
                    "EndpointID": "75914e44361ddd7d2fbb4a08c88f502d43ddf9fa29ae0872bf43fce4822033e4",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:03",
                    "DriverOpts": null
                }
            }
        }
    }
]
 

Next, try getting transcriptions from speech samples in the sample_dataset directory. For English audio samples, use the default Watson Speech to Text model, which is configured as en-US_Multimedia in env_config.json:

curl "http://localhost:10200“
 

For French audio samples, specify the model fr-FR_Multimedia:

@raspberrypi:/data/homeassistant202405/tts# ls -l
total 80
-rw-r--r-- 1 root root 56315 Nov 10 17:59 3caeb43ede21da438bb30ea4128096d7132655ae_en-us_c5ee8c087b_tts.piper.mp3
-rw-r--r-- 1 root root  8135 Nov 10 17:57 b802f384302cb24fbab0a44997e820bf2e8507bb_en-us_c5ee8c087b_tts.piper.mp3
-rw-r--r-- 1 root root 12545 Nov 10 17:58 f44761887c59dbbd377ca5960da9e15f3b7df652_en-us_c5ee8c087b_tts.piper.mp3

Step 6.Home Assistant configuration

Using it in Home Assistant

Home Assistant configuration
 
Settings->Integrations->Add Integration: Add the wyoming protocol and add the IP of wherever you are running Wyoming Piper Standalone. The port is 10200
 
Go to the Wyoming Protocol integration in Home Assistant and add a new service:
voice-piper-2.png
 
 
Enter your IP address and use 10200 as the port:
voice-piper-3.png
 

In both cases, transcriptions (in JSON format) are returned as standard output of the curl commands.

Summary

In this tutorial, you learned how to run a single-container speech-to-text service on Docker.

Take a look at more Embeddable AI content on IBM Developer, and learn how you can embed AI into your products to differentiate your solution.

 

 

Useful links

Run a single-container speech-to-text service on Docker

https://developer.ibm.com/tutorials/run-a-single-container-speech-to-text-service-on-docker/

Comments


Comments are closed