More work on the speech selector.

This commit is contained in:
Storm Dragon
2026-09-15 23:21:13 -04:00
parent 0df6d7b0fc
commit 4134a16ecd
5 changed files with 77 additions and 52 deletions
+8 -1
View File
@@ -104,6 +104,13 @@ EndSection
EOF
}
restart_fenrir_speech() {
if systemctl is-active --quiet fenrirscreenreader.service; then
killall speech-dispatcher 2>/dev/null || true
systemctl restart fenrirscreenreader.service
fi
}
set_timezone() {
# Get the list of timezones
mapfile -t regions < <(timedatectl --no-pager list-timezones | cut -d '/' -f1 | sort -u)
@@ -136,8 +143,8 @@ set_timezone() {
if read_yes_no "Would you like to switch Fenrir to laptop layout?"; then
sed -i 's/=desktop/=laptop/' /etc/fenrirscreenreader/settings/settings.conf
clear
systemctl restart fenrirscreenreader.service
fi
restart_fenrir_speech
if command -v i3 > /dev/null 2>&1 && systemctl list-unit-files nodm.service > /dev/null 2>&1; then
if read_yes_no "Will this Pi be connected to a physical screen?"; then
@@ -6,9 +6,11 @@ speechConfig="${STORMUX_SPEECH_CONFIG:-/etc/speech-dispatcher/speechd.conf}"
selectionFile=""
spdSayCommand="${STORMUX_SPD_SAY:-spd-say}"
systemctlCommand="${STORMUX_SYSTEMCTL:-systemctl}"
killallCommand="${STORMUX_KILLALL:-killall}"
socatCommand="${STORMUX_SOCAT:-socat}"
fenrirSocket="${STORMUX_FENRIR_SOCKET:-/tmp/fenrirscreenreader-daemon.sock}"
scriptPath="$(readlink -f "$0")"
runtimeDir="${STORMUX_RUNTIME_DIR:-/run/stormux}"
fenrirSpeechMuted=false
is_safe_module() {
[[ "$1" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]]
@@ -149,16 +151,30 @@ announce_failure() {
printf '%s\n' "$message" >&2
}
restart_speech() {
"${sudoCommand[@]}" "$killallCommand" speech-dispatcher
set_fenrir_speech() {
local enabled="$1"
printf 'setting set speech#enabled=%s\n' "$enabled" | \
"$socatCommand" - UNIX-CLIENT:"$fenrirSocket" >/dev/null 2>&1
}
restart_fenrir_if_active() {
if "$systemctlCommand" is-active --quiet fenrirscreenreader.service 2>/dev/null; then
"${sudoCommand[@]}" "$systemctlCommand" restart fenrirscreenreader.service
mute_fenrir_speech() {
if "$systemctlCommand" is-active --quiet fenrirscreenreader.service 2>/dev/null && \
set_fenrir_speech False; then
fenrirSpeechMuted=true
fi
}
restore_fenrir_speech() {
if [[ "$fenrirSpeechMuted" == true ]]; then
set_fenrir_speech True || true
fenrirSpeechMuted=false
fi
}
trap restore_fenrir_speech EXIT
trap 'exit 130' HUP INT TERM
discover_modules() {
local output line candidate
local skippedHeading=false
@@ -246,6 +262,7 @@ read_menu_key() {
declare -a modules=()
previousModule="$(configured_module)"
mute_fenrir_speech
if ! discover_modules; then
announce_failure "$previousModule"
exit 0
@@ -260,7 +277,7 @@ if ! selectedIndex="$(module_index "$previousModule")"; then
fi
draw_menu
"$spdSayCommand" "Speech engine selection. ${modules[$selectedIndex]} is highlighted. Use up and down to preview engines, Enter to select, or Escape to keep the current engine." >/dev/null 2>&1 || true
"$spdSayCommand" "Use up and down arrows to select the default voice for this computer. ${modules[$selectedIndex]} is highlighted. Press Enter to select, or Escape to keep the current voice." >/dev/null 2>&1 || true
while true; do
if ! keyName="$(read_menu_key)"; then
@@ -303,19 +320,9 @@ if ! "${sudoCommand[@]}" "$scriptPath" --apply-module "$selectedModule" "$speech
exit 0
fi
if ! restart_speech || ! restart_fenrir_if_active; then
"${sudoCommand[@]}" "$scriptPath" --restore-config "$speechConfig" "$backupFile" >/dev/null 2>&1 || true
restart_speech >/dev/null 2>&1 || true
restart_fenrir_if_active >/dev/null 2>&1 || true
announce_failure "$previousModule"
exit 0
fi
if [[ -n "$selectionFile" ]]; then
if ! "${sudoCommand[@]}" "$scriptPath" --write-selection "$selectedModule" "$selectionFile"; then
"${sudoCommand[@]}" "$scriptPath" --restore-config "$speechConfig" "$backupFile" >/dev/null 2>&1 || true
restart_speech >/dev/null 2>&1 || true
restart_fenrir_if_active >/dev/null 2>&1 || true
announce_failure "$previousModule"
exit 0
fi
+12 -16
View File
@@ -36,12 +36,10 @@ fi
exit 0
EOF
cat > "$mockBin/killall" <<'EOF'
cat > "$mockBin/socat" <<'EOF'
#!/usr/bin/env bash
printf 'killall %s\n' "$*" >> "$MOCK_LOG"
if [[ "${MOCK_KILLALL_FAIL:-no}" == "yes" ]]; then
exit 1
fi
read -r command
printf 'socat %s: %s\n' "$*" "$command" >> "$MOCK_LOG"
EOF
cat > "$mockBin/systemctl" <<'EOF'
@@ -104,13 +102,12 @@ run_selector() {
MOCK_LOG="$mockLog" \
MOCK_MODULES="$modulesFile" \
MOCK_DISCOVERY_STATUS="${MOCK_DISCOVERY_STATUS:-0}" \
MOCK_KILLALL_FAIL="${MOCK_KILLALL_FAIL:-no}" \
MOCK_FENRIR_ACTIVE="${MOCK_FENRIR_ACTIVE:-no}" \
MOCK_SUDO_FAIL_WRITE="${MOCK_SUDO_FAIL_WRITE:-no}" \
STORMUX_SPEECH_CONFIG="$configFile" \
STORMUX_RUNTIME_DIR="$caseDir/runtime" \
STORMUX_SPD_SAY=spd-say \
STORMUX_KILLALL=killall \
STORMUX_SOCAT=socat \
STORMUX_SYSTEMCTL=systemctl \
"$selector" --selection-file "$selectionFile" >/dev/null
}
@@ -122,7 +119,12 @@ MOCK_FENRIR_ACTIVE=yes run_selector '\n'
assert_equals 1 "$(grep -c '^DefaultModule ' "$configFile")" "initial selection should leave one active default"
assert_file_contains "$configFile" '^DefaultModule rhvoice$' "initial selection should retain rhvoice"
assert_equals rhvoice "$(< "$selectionFile")" "initial selection should be recorded"
assert_file_contains "$mockLog" '^systemctl restart fenrirscreenreader\.service$' "active Fenrir should restart"
assert_file_contains "$mockLog" 'speech#enabled=False' "active Fenrir speech should be muted"
assert_file_contains "$mockLog" 'speech#enabled=True' "active Fenrir speech should be restored"
assert_file_contains "$mockLog" 'Use up and down arrows to select the default voice for this computer\.' "the selector should voice its own instructions"
if grep -q '^systemctl restart ' "$mockLog"; then
fail "the selector should defer the Fenrir restart until after layout selection"
fi
# Down previews and selects the next module.
reset_case
@@ -163,16 +165,10 @@ fi
# A missing configuration simulates failed persistence and remains nonfatal.
reset_case
rm -f "$configFile"
run_selector '\n'
MOCK_FENRIR_ACTIVE=yes run_selector '\n'
assert_file_absent "$selectionFile" "failed persistence should not record a selection"
assert_file_contains "$mockLog" 'selection failed' "failed persistence should be announced"
# A restart failure restores the byte-for-byte original configuration.
reset_case
cp "$configFile" "$caseDir/original.conf"
MOCK_KILLALL_FAIL=yes run_selector '\033[B\n'
cmp -s "$configFile" "$caseDir/original.conf" || fail "restart failure should roll back the configuration"
assert_file_absent "$selectionFile" "restart failure should not record a selection"
assert_file_contains "$mockLog" 'speech#enabled=True' "failed persistence should restore Fenrir speech"
# Failure to record the live choice also rolls the system configuration back.
reset_case
@@ -37,6 +37,14 @@ set_timezone() {
ln -sf /usr/share/zoneinfo/"${region}"/"${city}" /etc/localtime
timedatectl set-ntp true
}
restart_fenrir_speech() {
if systemctl is-active --quiet fenrirscreenreader.service; then
killall speech-dispatcher 2>/dev/null || true
systemctl restart fenrirscreenreader.service
fi
}
# Offer to switch fenrir layout.
echo "Would you like to switch Fenrir to laptop layout?"
echo "Press y for yes or n for no followed by enter."
@@ -45,8 +53,8 @@ continue="${continue::1}"
if [[ "${continue,}" == "y" ]];then
sed -i 's/=desktop/=laptop/' /etc/fenrirscreenreader/settings/settings.conf
clear
systemctl restart fenrirscreenreader.service
fi
restart_fenrir_speech
if ! ping -c1 stormux.org &> /dev/null ; then
echo "No internet connection detected. Press enter to open NetworkManager."
@@ -6,9 +6,11 @@ speechConfig="${STORMUX_SPEECH_CONFIG:-/etc/speech-dispatcher/speechd.conf}"
selectionFile=""
spdSayCommand="${STORMUX_SPD_SAY:-spd-say}"
systemctlCommand="${STORMUX_SYSTEMCTL:-systemctl}"
killallCommand="${STORMUX_KILLALL:-killall}"
socatCommand="${STORMUX_SOCAT:-socat}"
fenrirSocket="${STORMUX_FENRIR_SOCKET:-/tmp/fenrirscreenreader-daemon.sock}"
scriptPath="$(readlink -f "$0")"
runtimeDir="${STORMUX_RUNTIME_DIR:-/run/stormux}"
fenrirSpeechMuted=false
is_safe_module() {
[[ "$1" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]]
@@ -149,16 +151,30 @@ announce_failure() {
printf '%s\n' "$message" >&2
}
restart_speech() {
"${sudoCommand[@]}" "$killallCommand" speech-dispatcher
set_fenrir_speech() {
local enabled="$1"
printf 'setting set speech#enabled=%s\n' "$enabled" | \
"$socatCommand" - UNIX-CLIENT:"$fenrirSocket" >/dev/null 2>&1
}
restart_fenrir_if_active() {
if "$systemctlCommand" is-active --quiet fenrirscreenreader.service 2>/dev/null; then
"${sudoCommand[@]}" "$systemctlCommand" restart fenrirscreenreader.service
mute_fenrir_speech() {
if "$systemctlCommand" is-active --quiet fenrirscreenreader.service 2>/dev/null && \
set_fenrir_speech False; then
fenrirSpeechMuted=true
fi
}
restore_fenrir_speech() {
if [[ "$fenrirSpeechMuted" == true ]]; then
set_fenrir_speech True || true
fenrirSpeechMuted=false
fi
}
trap restore_fenrir_speech EXIT
trap 'exit 130' HUP INT TERM
discover_modules() {
local output line candidate
local skippedHeading=false
@@ -246,6 +262,7 @@ read_menu_key() {
declare -a modules=()
previousModule="$(configured_module)"
mute_fenrir_speech
if ! discover_modules; then
announce_failure "$previousModule"
exit 0
@@ -260,7 +277,7 @@ if ! selectedIndex="$(module_index "$previousModule")"; then
fi
draw_menu
"$spdSayCommand" "Speech engine selection. ${modules[$selectedIndex]} is highlighted. Use up and down to preview engines, Enter to select, or Escape to keep the current engine." >/dev/null 2>&1 || true
"$spdSayCommand" "Use up and down arrows to select the default voice for this computer. ${modules[$selectedIndex]} is highlighted. Press Enter to select, or Escape to keep the current voice." >/dev/null 2>&1 || true
while true; do
if ! keyName="$(read_menu_key)"; then
@@ -303,19 +320,9 @@ if ! "${sudoCommand[@]}" "$scriptPath" --apply-module "$selectedModule" "$speech
exit 0
fi
if ! restart_speech || ! restart_fenrir_if_active; then
"${sudoCommand[@]}" "$scriptPath" --restore-config "$speechConfig" "$backupFile" >/dev/null 2>&1 || true
restart_speech >/dev/null 2>&1 || true
restart_fenrir_if_active >/dev/null 2>&1 || true
announce_failure "$previousModule"
exit 0
fi
if [[ -n "$selectionFile" ]]; then
if ! "${sudoCommand[@]}" "$scriptPath" --write-selection "$selectedModule" "$selectionFile"; then
"${sudoCommand[@]}" "$scriptPath" --restore-config "$speechConfig" "$backupFile" >/dev/null 2>&1 || true
restart_speech >/dev/null 2>&1 || true
restart_fenrir_if_active >/dev/null 2>&1 || true
announce_failure "$previousModule"
exit 0
fi