Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

homeassistant-core wyoming enabled voice assistant add-ons #481

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6623e18
fix(piper-tts): set PIPER_PHONEMIZE_DIR as ENV
ms1design Apr 18, 2024
79ce5b0
fix(wyoming): Add README.md
ms1design Apr 18, 2024
a4914ed
fix(wyoming-openwakeword): Config shell to be more descriptive in CI/…
ms1design Apr 18, 2024
e8d2b1b
fix(wyoming-openwakeword): Add missing config.py declaration to Docke…
ms1design Apr 18, 2024
0ded44f
fix(wyoming-whisper): Add wyoming wrapper for faster-whisper container
ms1design Apr 18, 2024
cbeedb8
fix(wyoming-piper): Add wyoming wrapper for piper-tts container
ms1design Apr 18, 2024
5dde8dc
feat(wyoming-assist-microphone): Introduce wyoming-assist-microphone …
ms1design Apr 18, 2024
74029e2
fix(wyoming-piper): Fixed buils.sh script
ms1design Apr 19, 2024
5a4b38c
feat(piper-tts): Add ESPEAK_NG_DATA_DIR env
ms1design Apr 20, 2024
4ba5dc4
feat(wyoming-piper): Enable CUDA, use ESPEAK_NG_DATA_DIR env
ms1design Apr 20, 2024
07d4e9e
feat(wyoming-assist-microphone): Refactor, independent rootfs, config.py
ms1design Apr 22, 2024
e014dfe
feat(wyoming-openwakeword): Refactor, independent rootfs, config.py
ms1design Apr 22, 2024
9d080f9
feat(wyoming-piper): Refactor, independent rootfs, config.py
ms1design Apr 22, 2024
7a42bac
feat(wyoming-whisper): Refactor, independent rootfs, config.py
ms1design Apr 22, 2024
6109ec7
feat(wyoming-assist-microphone): Cleanup
ms1design Apr 23, 2024
f338cd7
feat(wyoming-openwakeword): Cleanup
ms1design Apr 23, 2024
c4c01f3
feat(wyoming-piper): allow git branch name as version in config.py
ms1design Apr 23, 2024
c47c585
fix(wyoming-piper): add missing rootfs
ms1design Apr 23, 2024
baa10f6
feat(wyoming-piper): Cleanup
ms1design Apr 23, 2024
8205a90
feat(wyoming-whisper): Enable cuda, cleanup
ms1design Apr 23, 2024
473f90b
Merge remote-tracking branch 'origin/dev' into feature/homeassistant-…
ms1design Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions packages/audio/piper-tts/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ FROM ${BASE_IMAGE}

ARG PIPER_VERSION=master \
PIPER_PHONEMIZE_VERSION=master \
PIPER_PHONEMIZE_DIR=/usr/local \
ONNXRUNTIME_DIR=/usr/local

# make PIPER_CACHE a default mounted location available for downloading the models
# this still needs to be explicitly passed to piper.get_voices(), ensure_path_exists(), ect.
ENV PIPER_CACHE=/data/models/piper \
PIPER_PHONEMIZE_DIR=/usr/local \
ESPEAK_NG_DATA_DIR=/usr/local/share/espeak-ng-data

#ENV LD_LIBRARY_PATH=/usr/local/:$LD_LIBRARY_PATH

WORKDIR /opt
Expand All @@ -28,8 +33,8 @@ RUN git clone --branch ${PIPER_PHONEMIZE_VERSION} --depth 1 https://github.com/r
cmake --install build && \
CPPFLAGS="-I${ONNXRUNTIME_DIR}/include/onnxruntime/" \
pip3 install --no-cache-dir --verbose . && \
ln -s /usr/local/share/espeak-ng-data /usr/share/espeak-ng-data

ln -s ${ESPEAK_NG_DATA_DIR} /usr/share/espeak-ng-data
# Now we can build piper. In order to avoid it from downloading
# automatically piper-phonemize, we specify an installation dir.
RUN git clone --branch ${PIPER_VERSION} --depth 1 https://github.com/rhasspy/piper && \
Expand All @@ -42,8 +47,4 @@ RUN git clone --branch ${PIPER_VERSION} --depth 1 https://github.com/rhasspy/pip
pip3 install -r requirements_http.txt && \
pip3 install --no-cache-dir --verbose --no-deps .[gpu,http]

# make a default mounted location available for downloading the models
# this still needs to be explicitly passed to piper.get_voices(), ensure_path_exists(), ect.
ENV PIPER_CACHE=/data/models/piper

WORKDIR /
WORKDIR /
5 changes: 5 additions & 0 deletions packages/smart-home/wyoming/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Wyoming protocol

A peer-to-peer protocol for voice assistants (basically JSONL + PCM audio)

More details here: https://github.com/rhasspy/wyoming
51 changes: 51 additions & 0 deletions packages/smart-home/wyoming/assist-microphone/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#---
# name: wyoming-assist-microphone
# group: wyoming
# requires: '>=34.1.0'
# config: config.py
# depends: [homeassistant-base, python:3.11]
# test: [list_audio_devices.sh]
# notes: The `assist-microphone` using the `wyoming` protocol for usage with Home Assistant. Based on `https://github.com/home-assistant/addons/tree/master/assist_microphone`
#---
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ARG WYOMING_SATELLITE_VERSION

# Specify the shell with desired options
SHELL ["/bin/bash", "-euxo", "pipefail"]

ENV PIP_BREAK_SYSTEM_PACKAGES=1 \
WYOMING_SATELLITE_VERSION="$WYOMING_SATELLITE_VERSION" \
ASSIST_MICROPHONE_PORT="10700" \
ASSIST_MICROPHONE_SOUND_ENABLED="true" \
ASSIST_MICROPHONE_AWAKE_WAV="/usr/src/sounds/awake.wav" \
ASSIST_MICROPHONE_DONE_WAV="/usr/src/sounds/done.wav" \
ASSIST_MICROPHONE_DEBUG="true" \
\
WAKEWORD_SERVICE_URI="tcp://127.0.0.1:10400" \
WAKEWORD_NAME="ok_nabu" \
\
ASSIST_MICROPHONE_SND_VOLUME_MULTIPLIER="1.0" \
ASSIST_MICROPHONE_MIC_VOLUME_MULTIPLIER="1.0" \
ASSIST_MICROPHONE_MIC_AUTO_GAIN="0" \
ASSIST_MICROPHONE_MIC_NOISE_SUPPRESSION="0" \
\
AUDIO_DEVICE="plughw:CARD=S330,DEV=0"

COPY rootfs/ /
COPY build.sh /tmp/wyoming/build_wyoming-assist-microphone.sh

WORKDIR /usr/src

RUN /tmp/wyoming/build_wyoming-assist-microphone.sh

EXPOSE ${ASSIST_MICROPHONE_PORT}/tcp

HEALTHCHECK --start-period=10m \
CMD echo '{ "type": "describe" }' \
| nc -w 1 localhost ${ASSIST_MICROPHONE_PORT} \
| grep -q "assist" \
|| exit 1

ENTRYPOINT ["/init"]
29 changes: 29 additions & 0 deletions packages/smart-home/wyoming/assist-microphone/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# wyoming-assist-microphone
set -euxo pipefail

apt-get update
apt-get install -y --no-install-recommends \
netcat-traditional \
libasound2-plugins \
alsa-utils
apt-get clean
rm -rf /var/lib/apt/lists/*

pip3 install --no-cache-dir -U \
setuptools \
wheel \
webrtc-noise-gain==1.2.3 \
pysilero-vad==1.0.0
pip3 install --no-cache-dir \
"wyoming-satellite[webrtc] @ https://github.com/rhasspy/wyoming-satellite/archive/refs/tags/v${WYOMING_SATELLITE_VERSION}.tar.gz"

# Clone rootfs & config.aml
git clone --depth=1 https://github.com/home-assistant/addons /tmp/addons
git -C /tmp/addons sparse-checkout set --no-cone assist_microphone/ '!*/assist_microphone'

# Copy sounds
mkdir -p /usr/src/sounds
cp -r /tmp/addons/assist_microphone/sounds/* /usr/src/sounds/

rm -rf /tmp/addons
33 changes: 33 additions & 0 deletions packages/smart-home/wyoming/assist-microphone/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import requests


def get_latest_stable_version(fallback="1.2.0") -> str:
try:
response = requests.get('https://raw.githubusercontent.com/rhasspy/wyoming-satellite/master/wyoming_satellite/VERSION')
if response.status_code == 200:
return response.text.strip()
else:
print("Failed to fetch version information. Status code:", response.status_code)
return fallback
except Exception as e:
print("An error occurred:", e)
return fallback


def create_package(version, default=False) -> list:
pkg = package.copy()
wanted_version = get_latest_stable_version() if version == 'latest' else version
pkg['name'] = f'wyoming-assist-microphone:{version}'

pkg['build_args'] = {
'WYOMING_SATELLITE_VERSION': wanted_version,
}

if default:
pkg['alias'] = 'wyoming-assist-microphone'

return pkg

package = [
create_package("latest", default=True),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# wyoming-assist-microphone

echo "RECORDING DEVICES:"
arecord -L

echo "PLAYING DEVICES:"
aplay -L
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
# ==============================================================================
# Take down the S6 supervision tree when service fails
# s6-overlay docs: https://github.com/just-containers/s6-overlay
# ==============================================================================
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
readonly exit_code_service="${1}"
readonly exit_code_signal="${2}"

bashio::log.info \
"Service exited with code ${exit_code_service}" \
"(by signal ${exit_code_signal})"

if [[ "${exit_code_service}" -eq 256 ]]; then
if [[ "${exit_code_container}" -eq 0 ]]; then
echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
fi
[[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
elif [[ "${exit_code_service}" -ne 0 ]]; then
if [[ "${exit_code_container}" -eq 0 ]]; then
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
fi
exec /run/s6/basedir/bin/halt
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
# ==============================================================================
# Start service
# ==============================================================================
extra_args=()
if [[ "${ASSIST_MICROPHONE_DEBUG}" == "true" ]]; then
extra_args+=('--debug')
fi

if [[ "${ASSIST_MICROPHONE_SOUND_ENABLED}" == "true" ]]; then
extra_args+=("--snd-command" "aplay -D ${AUDIO_DEVICE} -r 16000 -c 1 -f S16_LE -t raw")
fi

exec python3 -m wyoming_satellite \
--name 'assist microphone' \
--uri "tcp://0.0.0.0:${ASSIST_MICROPHONE_PORT}" \
\
--snd-command-rate 16000 \
--snd-command-channels 1 \
--snd-volume-multiplier "${ASSIST_MICROPHONE_SND_VOLUME_MULTIPLIER}" \
\
--mic-command "arecord -D ${AUDIO_DEVICE} -r 16000 -c 1 -f S16_LE -t raw" \
--mic-command-rate 16000 \
--mic-command-channels 1 \
--mic-volume-multiplier "${ASSIST_MICROPHONE_MIC_VOLUME_MULTIPLIER}" \
--mic-auto-gain "${ASSIST_MICROPHONE_MIC_AUTO_GAIN}" \
--mic-noise-suppression "${ASSIST_MICROPHONE_MIC_NOISE_SUPPRESSION}" \
\
--wake-uri "${WAKEWORD_SERVICE_URI}" \
--wake-word-name "${WAKEWORD_NAME}" \
\
--awake-wav "${ASSIST_MICROPHONE_AWAKE_WAV}" \
--done-wav "${ASSIST_MICROPHONE_DONE_WAV}" \
--no-zeroconf "${extra_args[@]}"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
longrun
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
# ==============================================================================
# Sends discovery information to Home Assistant.
# ==============================================================================
declare config

# Wait for satellite to become available
bash -c \
"until
echo '{ \"type\": \"describe\" }'
> /dev/tcp/localhost/10700; do sleep 0.5;
done" > /dev/null 2>&1 || true;

config=$(\
bashio::var.json \
uri "tcp://$(hostname):10700" \
)

# if bashio::discovery "wyoming" "${config}" > /dev/null; then
# bashio::log.info "Successfully sent discovery information to Home Assistant."
# else
# bashio::log.error "Discovery message to Home Assistant failed!"
# fi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
oneshot
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/discovery/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# wyoming-assist-microphone

echo "Testing microphone recording..."

arecord -D plughw:CARD=S330,DEV=0 -r 16000 -c 1 -f S16_LE -t wav -d 5 /tmp/recording.wav

echo "microphone recording OK"

echo "Playing recording..."

aplay -D plughw:CARD=S330,DEV=0 /tmp/recording.wav

echo "Playing recording OK"

rm /tmp/recording.wav
22 changes: 17 additions & 5 deletions packages/smart-home/wyoming/openwakeword/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,39 @@
# group: wyoming
# requires: '>=34.1.0'
# docs: docs.md
# config: config.py
# depends: [homeassistant-base, python:3.11]
# notes: The `openWakeWord` using the `wyoming` protocol for usage with Home Assistant. Based on `https://github.com/home-assistant/addons/blob/master/openwakeword/Dockerfile`
#---
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ARG OPENWAKEWORD_VERSION
ARG WYOMING_OPENWAKEWORD_VERSION

# Specify the shell with desired options
SHELL ["/bin/bash", "-euxo", "pipefail"]

ENV PIP_BREAK_SYSTEM_PACKAGES=1 \
WYOMING_OPENWAKEWORD_VERSION="$WYOMING_OPENWAKEWORD_VERSION" \
OPENWAKEWORD_PORT=10400 \
OPENWAKEWORD_THRESHOLD=0.5 \
OPENWAKEWORD_TRIGGER_LEVEL=1 \
OPENWAKEWORD_PRELOAD_MODEL="ok_nabu" \
OPENWAKEWORD_CUSTOM_MODEL_DIR="/share/openwakeword" \
OPENWAKEWORD_DEBUG=true

WORKDIR /usr/src

COPY rootfs/ /
COPY build.sh /tmp/wyoming/build_openwakeword.sh
RUN cd /usr/src && /tmp/wyoming/build_openwakeword.sh

# FIXME: For some reason below HEALTHCHECK is not playing nicelly with my CI/CD env
# instead of just declaring itself it executes the CMD...
# HEALTHCHECK --start-period=10m CMD echo '{ "type": "describe" }' | nc -w 1 localhost ${OPENWAKEWORD_PORT} | grep -iq "openWakeWord" || exit 1
RUN /tmp/wyoming/build_openwakeword.sh

HEALTHCHECK --start-period=10m \
CMD echo '{ "type": "describe" }' \
| nc -w 1 localhost ${OPENWAKEWORD_PORT} \
| grep -iq "openWakeWord" \
|| exit 1

EXPOSE ${OPENWAKEWORD_PORT}/tcp

Expand Down
30 changes: 4 additions & 26 deletions packages/smart-home/wyoming/openwakeword/build.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# wyoming-openwakeword
set -ex
set -euxo pipefail

apt-get update
apt-get install -y --no-install-recommends \
Expand All @@ -13,33 +13,11 @@ pip3 install --no-cache-dir -U \
setuptools \
wheel

echo "wyoming-openwakeword: ${OPENWAKEWORD_VERSION}"
echo "wyoming-openwakeword: ${WYOMING_OPENWAKEWORD_VERSION}"

# TODO: This still uses tflite on CPU / Need to build from scratch on onnxruntime-gpu
pip3 install --no-cache-dir \
--extra-index-url https://www.piwheels.org/simple \
"wyoming-openwakeword @ https://github.com/rhasspy/wyoming-openwakeword/archive/refs/tags/v${OPENWAKEWORD_VERSION}.tar.gz"

git clone --depth=1 https://github.com/home-assistant/addons /tmp/addons
git -C /tmp/addons sparse-checkout set --no-cone openwakeword/ '!*/openwakeword'

# Fix: Adjust reading config data from config.yaml file when not using HA Supervisor
sed -i \
-e "s/bashio::config.true 'debug_logging'/[[ \"$OPENWAKEWORD_DEBUG\" == \"true\" ]]/g" \
-e "s/--threshold.*/--threshold \"$OPENWAKEWORD_THRESHOLD\" \\\\/g" \
-e "s/--trigger-level.*/--trigger-level \"$OPENWAKEWORD_TRIGGER_LEVEL\" \${flags[@]}/g" \
/tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/openwakeword/run

# Fix: Disable native Discovery handled by HA Supervisor
cat /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run
sed -i '$d' /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run
sed -i '$d' /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run
sed -i '$d' /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run
sed -i '$d' /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run
sed -i '$d' /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run
cat /tmp/addons/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/discovery/run

cp -r /tmp/addons/openwakeword/rootfs/* /
cp /tmp/addons/openwakeword/config.yaml /etc/s6-overlay/s6-rc.d/openwakeword/config.yaml
rm -rf /tmp/addons
"wyoming-openwakeword @ https://github.com/rhasspy/wyoming-openwakeword/archive/refs/tags/v${WYOMING_OPENWAKEWORD_VERSION}.tar.gz"

python3 -c 'import wyoming_openwakeword; print(wyoming_openwakeword.__version__);'
2 changes: 1 addition & 1 deletion packages/smart-home/wyoming/openwakeword/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def create_package(version, default=False) -> list:
pkg['name'] = f'wyoming-openwakeword:{version}'

pkg['build_args'] = {
'OPENWAKEWORD_VERSION': wanted_version,
'WYOMING_OPENWAKEWORD_VERSION': wanted_version,
}

if default:
Expand Down
Loading