Commit Graph
100 Commits
Author SHA1 Message Date
imonlinux 661e50921e Merge pull request #51 from imonlinux/refractory-rejection-loop_#48
Update audio_engine.py issue: 48
2025-12-20 13:36:48 -08:00
imonlinux 56572d4b33 LED color status in HA fixed
Bug was preventing HA from showing the correct LED color.
2025-12-11 11:39:21 -08:00
imonlinux 8d135dc51b Update audio_engine.py issue: 48
Behavioral changes:

Each wake word has its own timer.

Rapid repeats of the same phrase within the refractory window still get rejected (which is desirable).

A misfire of “Hey Jarvis” no longer locks out “Okay Nabu” immediately afterward.

If a user really wants to disable the gating, they can set:

"wake_word": {
  "refractory_seconds": 0
}


and this code will treat it as “no refractory” (max(..., 0.0) above).
2025-12-06 19:32:29 -08:00
imonlinux 186f33f1a1 Update config.json.example
Add clarity for using NeoPixel LEDs with Grove Port interface.
2025-11-30 12:31:34 -08:00
imonlinux 57b20a1ef1 Update button_controller.py
Fixed bug when running on a non-RPi board.
2025-11-30 00:49:59 -08:00
imonlinux 5ef680d734 Merge pull request #44 from imonlinux/gpio-button-suport
Summary

Add support for the ReSpeaker 2-Mic HAT momentary button as a first-class control surface for the Linux Voice Assistant. The button now behaves like:

Short press

If TTS or music is playing → stop playback (equivalent to the Stop wake word)

Otherwise → start a new conversation (equivalent to a wake word trigger)

Long press

Toggle microphone mute (wired through the existing set_mic_mute event, so MQTT state and LEDs stay in sync)

The implementation uses a polling-based GPIO loop (RPi.GPIO) instead of kernel edge-detection to avoid “Failed to add edge detection” issues on some HAT/overlay setups. Button behavior is fully configurable via config.json.
2025-11-30 00:38:37 -08:00
imonlinux 5773f3a35a Update lva-desktop.md
Update to include the additional dependencies in the .venv for Qt5.
2025-11-30 00:37:31 -08:00
imonlinux 6260bf1dca Update pyproject.toml
Fix regression bug introduced with the lva tray client.
2025-11-30 00:33:08 -08:00
imonlinux fa3889805e Update linux-voice-assistant-2mic-install.md
Update to include support of implementation of the GPIO button function.
2025-11-30 00:00:35 -08:00
imonlinux a9cf822136 Update satellite.py
Extended the satellite state machine with a manual wake entrypoint:

Added manual_wakeup(self, phrase: str = "button"):

Mirrors the semantics of wakeup(), but does not require a wake-word model instance.

Respects the current state: only triggers from STARTING or IDLE.

If a timer alarm is ringing, calls the existing _stop_timer_alarm(...) instead of starting a new conversation (consistent with wake-word behavior).

Otherwise calls a shared helper _start_conversation(phrase).

Introduced _start_conversation(wake_word_phrase: str) to encapsulate:

Sending VoiceAssistantRequest(start=True, wake_word_phrase=...),

Transitioning to LISTENING,

Setting _is_streaming_audio = True,

Playing the configured wakeup_sound via tts_player.

Kept prior timer/alarm logic intact (including auto stop and Stop wake word behavior), so the button integrates cleanly with existing timer and TTS flows.
2025-11-29 23:43:08 -08:00
imonlinux 0073dc6a23 Update __main__.py
Imported the new ButtonController.

In _init_controllers():

Instantiated ButtonController when config.button.enabled is True.

Passed loop, event_bus, state, and config.button.

Stored the instance on state.button_controller to keep it alive for the lifetime of the process.

Left the rest of startup orchestration unchanged (audio engine, MQTT, LEDs, etc.), so the button is just an additional input surface plugged into the existing architecture.
2025-11-29 23:42:24 -08:00
imonlinux dc9049e899 Add button_controller.py
Introduced a new ButtonController responsible for handling the ReSpeaker (or generic GPIO) button:

Uses RPi.GPIO with BCM numbering and internal pull-up (PUD_UP), assuming an active-low button.

Starts a dedicated polling thread that:

Reads the GPIO level at a configurable interval (default 10 ms / 100 Hz),

Detects press and release transitions,

Computes press duration to classify short vs long press.

Short press behavior:

If state.tts_player.is_playing or state.music_player.is_playing:

Schedules state.satellite.stop() on the asyncio loop,

Also stops music via state.music_player.stop().

Otherwise:

Schedules state.satellite.manual_wakeup("button") to start a new voice interaction (wake-like behavior without needing a model instance).

Long press behavior:

Toggles mic mute using the existing event path:

Publishes set_mic_mute on the EventBus with {"state": not state.mic_muted}.

This ensures MicMuteHandler remains the single writer for mic state, updates mic_muted_event, and syncs MQTT mute state/LEDs.

Designed to fail gracefully:

If ButtonConfig.enabled is False or RPi.GPIO is unavailable, logs a message and disables button support without affecting the rest of the system.
2025-11-29 23:41:24 -08:00
imonlinux edc66183c3 Update config.py
Added a new ButtonConfig dataclass:

enabled: bool = False

pin: int = 17 (BCM pin for the button)

long_press_seconds: float = 1.0

Extended the main Config dataclass with a button: ButtonConfig field.

Updated load_config_from_json() to construct button_config from the "button" section in config.json.

This allows enabling/disabling the button, changing the GPIO pin, and tuning the long-press threshold (and optional poll interval) without code changes.
2025-11-29 23:40:16 -08:00
imonlinux 93bdb60fc0 Update README.md
Updated to include support for the gpio button.
2025-11-29 23:39:13 -08:00
imonlinux 912468703a Update README.md
Updated to include the addition of the alarm duration MQTT setting.
2025-11-29 14:51:04 -08:00
imonlinux 17f1671f1b Merge pull request #43 from imonlinux/implement-alarm-play-duration
Added a new user preference: alarm_duration_seconds (0 = infinite / Stop-only, >0 = auto-stop after N seconds).

Exposed that preference via MQTT/HA as a number entity (Alarm Duration) in seconds.

Updated the satellite timer logic so the alarm:

loops as before,

stops on the Stop wake word or wake wake word (existing semantics),

and optionally auto-stops after alarm_duration_seconds if configured.

Ensured all paths that stop the alarm cleanly restore audio state and wake word activation.
2025-11-29 14:40:58 -08:00
imonlinux 0e928bd7b3 Update satellite.py
Added internal tracking for an alarm auto-stop:

self._timer_auto_stop_handle: Optional[asyncio.TimerHandle]

On VOICE_ASSISTANT_TIMER_FINISHED:

enables the Stop wake word,

starts the repeating timer_finished_sound alarm as before,

reads state.preferences.alarm_duration_seconds and, if > 0, schedules an auto-stop callback after that duration.

Centralized alarm stopping in helper methods:

_clear_timer_auto_stop() – cancels any pending auto-stop timer.

_stop_timer_alarm(reason: str) – stops the alarm sound, clears flags, unducks music, and removes the Stop wake word from active_wake_words.

_auto_stop_timer_alarm() – invoked by the scheduled timer, calls _stop_timer_alarm("auto_timeout").

Updated behavior paths:

wakeup() now stops a ringing alarm instead of starting a new conversation if a timer is active.

stop() (Stop wake word) stops a ringing alarm first; only falls back to stopping TTS if no alarm is active.

_play_timer_finished() continues to loop the sound until _timer_finished is cleared, then unducks and exits cleanly.
2025-11-29 14:39:46 -08:00
imonlinux 0025fc781f Update mqtt_controller.py
Added MQTT topics for the new alarm duration control:

lva/<device_id>/alarm_duration/set

lva/<device_id>/alarm_duration/state

Published a new Home Assistant number discovery config:

number.<device_id>_alarm_duration (0–3600 seconds, step 5, unit s).

On /alarm_duration/set:

validates and parses the payload as a non-negative integer,

publishes a set_alarm_duration event on the EventBus,

echoes the value back to /alarm_duration/state.

On connect, publishes the current alarm_duration_seconds from preferences to the state topic for retained startup behavior.
2025-11-29 14:38:52 -08:00
imonlinux 5da1dd4bc4 Update models.py
Updated Preferences dataclass with a new field:

alarm_duration_seconds: int = 0

Kept ServerState.save_preferences() unchanged so alarm_duration_seconds is automatically serialized into preferences.json.
2025-11-29 14:38:13 -08:00
imonlinux b43b036198 Update __main__.py
Extended Preferences loading to initialize and backfill a new alarm_duration_seconds field (default 0).

Added a set_alarm_duration event handler to MicMuteHandler which:

validates and normalizes the duration value,

updates state.preferences.alarm_duration_seconds,

persists the updated preferences to disk.

Kept the existing “single writer” pattern (MicMuteHandler) for preferences/state updates.
2025-11-29 14:37:18 -08:00
imonlinux 5931370c63 Update README.md 2025-11-28 14:10:45 -08:00
imonlinux 53d143fb35 Update README.md
Updated for additional supported Linux distros.
2025-11-28 14:10:18 -08:00
imonlinux be41c876b0 Update lva-desktop.md
Add command to chmod +x on the script/tray file.
2025-11-27 19:10:07 -08:00
imonlinux 48a6ec38a5 Add user mode unit file for the tray client 2025-11-27 19:08:09 -08:00
imonlinux b43cd95ec1 Update pyproject.toml
Add dependencies for the LVA Tray Client.
2025-11-26 20:08:48 -08:00
imonlinux e1fd1ccd4d Update lva-desktop.md
Update to include mpv "just in case"
2025-11-26 20:06:21 -08:00
imonlinux 5ce9085fd9 Merge pull request #42 from imonlinux/desktop-tray-client
Desktop tray client
2025-11-26 20:02:28 -08:00
imonlinux d0a74a2d3b Update README.md
Add info for tray client on Linux desktop.
2025-11-26 19:59:58 -08:00
imonlinux 85560b8126 Add tray script under the scripts folder. 2025-11-26 19:53:38 -08:00
imonlinux 0a2c227cad Add LVA Tray Client
Provide a simple tray client to show the status of the LVA running on a Linux desktop.
2025-11-26 19:51:17 -08:00
imonlinux 11c0e4fbb6 Create lva-desktop.md
Add tutorial for running LVA on a Linux desktop.
2025-11-26 19:49:39 -08:00
imonlinux e13cc31e5b Update satellite.py
Fix bug with no stt after wake word detection.
2025-11-24 15:14:15 -08:00
imonlinux e3f9757bee Update README.md
Clean up What's working section
2025-11-23 20:32:46 -08:00
imonlinux f38c89fb3b Update README.md
Typo
2025-11-23 20:12:02 -08:00
imonlinux c6c34b60e5 Update install-respeaker-drivers.sh
Added logic to support both Bookworm and Trixie version of Raspberry Pi OS.
2025-11-23 20:01:11 -08:00
imonlinux 62164e0142 Update linux-voice-assistant-2mic-install.md
Modified packages list in support of Trixie and soundcard.
2025-11-23 20:00:18 -08:00
imonlinux bd488f555e Update linux-voice-assistant-2mic-install.md
Add AEP tuning details.
2025-11-23 19:33:31 -08:00
imonlinux a9989f0c25 Update README.md
Confirmed working with Rasberry OS Trixie.
2025-11-23 19:25:11 -08:00
imonlinux baa6f9080f Update satellite.py
Fix wake word change bug
2025-11-22 16:11:16 -08:00
imonlinux f3ae02da0d Update util.py
Revert MAC address detection logic.
2025-11-21 17:17:56 -08:00
imonlinux 14d10b351f Merge pull request #41 from imonlinux/non-rpi_system_support
Make LED controller optional on non-RPi hosts and fix --list-output-devices exit code
2025-11-21 17:02:00 -08:00
imonlinux d2a133191b Update __main__.py
File: linux_voice_assistant/__main__.py

In _init_basics(), the --list-output-devices block is updated so that:

It exits with 0 on success.

It exits with 1 only if an exception occurs.
2025-11-21 17:00:25 -08:00
imonlinux 8d474acdab Update led_controller.py
Wrap the board import in a try/except and introduce a _BOARD_AVAILABLE flag.

If board (or Blinka/platformdetect) is not available or fails, log a clear warning and skip hardware LED initialization.

Keep the event subscriptions intact, but ensure all LED actions become no-ops when _is_ready is False.
2025-11-21 16:59:04 -08:00
imonlinux 18e9a49adf Update linux-voice-assistant-2mic-install.md
fix path typo in the new AEC section
2025-11-21 13:05:55 -08:00
imonlinux 61e02f9adb Merge pull request #40 from imonlinux/let_mpv_do_the_work
Simplify mpv audio backend selection and log effective backend
2025-11-21 11:01:07 -08:00
imonlinux 93ceffebae Update mpv_player.py
Removed the home-grown backend probing / priority logic (pipewire → pulse → alsa) when no device is explicitly configured.

Kept support for explicitly specifying an audio_output_device in the config/CLI; if provided, it is passed through to mpv.

Added debug logging after mpv initialization to show:

current ao

current audio-device

the full audio-device-list
2025-11-21 11:00:03 -08:00
imonlinux 9285337a5e Merge pull request #39 from imonlinux/stop-wakeword-audio-device-list
Improve stop wake word behavior and add audio device listing helpers
2025-11-20 20:45:43 -08:00
imonlinux c7af116904 Update linux-voice-assistant-2mic-install.md
Added section for implementing AEC.
2025-11-20 20:43:31 -08:00
imonlinux 25d341927c Update README.md
Updated to include AEC details.
2025-11-20 20:18:47 -08:00
imonlinux adbb37e731 Update __main__.py
New CLI helpers

    Added support for:

    script/run --list-input-devices
    script/run --list-output-devices

    These code paths:

        Initialize just enough of the audio stack to enumerate devices.

        Print a clean, human-readable list of input/output devices (including PipeWire / PulseAudio virtual devices like Echo-Cancel Source and pipewire/echo-cancel-sink).

        Exit cleanly with status 0 instead of trying to start the full LVA process afterward (fixing the previous CalledProcessError when --list-output-devices was used).

Behavior change

    __main__.py now treats the “list devices” modes as early-exit utility modes, not “run LVA and then also print devices”.

    This makes it practical to use these commands in setup docs and shell scripts.
2025-11-20 20:02:23 -08:00
imonlinux 0151b64810 Update audio_engine.py
Behavioral & logging changes

Improved the wake word processing loop to make the following clearly visible in DEBUG logs:

The current set of active wake word IDs (MWW/OWW).

When the Stop wake word:

is activated by the model,

is accepted (i.e. meets refractory timing + is currently allowed), and

is ignored because it is no longer in active_wake_words.

Ensured the special stop model:

Is only treated as “active” when state.stop_word.is_active is True.

Is flipped back to inactive immediately after satellite.stop() is called, so:

The first “Stop” that passes all checks cancels playback and alarm tones.

Subsequent detections in the same moment appear as “TRIGGERED but IGNORED” (expected and now clearly logged).

Why this matters

When the LVA is playing audio (TTS or timer alarm), you can now:

See exactly when the Stop model fires.

Confirm that playback is being canceled at the right time.

Understand why you might see multiple “Stop wake word ACTIVATED” messages even though the alarm already stopped (model still “hearing” the tail, but logically ignored).

This matches the behavior seen in your logs:

At lower volumes / with AEC, stop is reliably accepted and playback stops immediately.

At high volumes without AEC, the model sees far less “clean” speech, so activations are rare or absent.
2025-11-20 20:01:13 -08:00
imonlinux 8d928c6b02 Merge pull request #38 from imonlinux/media_player_persistence
Fix ESPHome media_player persistence and entity key stability
2025-11-20 13:05:45 -08:00
imonlinux 0b3c3e7b42 Update satellite.py
Fix ESPHome media_player persistence and entity key stability
2025-11-20 13:04:46 -08:00
imonlinux f34bc90edd Update satellite.py
Registration bug fix
2025-11-19 20:24:42 -08:00
imonlinux 196d5273f0 Update README.md 2025-11-19 19:06:22 -08:00
imonlinux d3d60e1c99 Merge pull request #37 from imonlinux/refactor-audio_engine
Core Refactor: Stability, Identity Persistence, and Audio Engine Optimization
2025-11-19 18:51:40 -08:00
imonlinux 4616645c40 Update __main__.py
Entry point. Clean imports, timestamp logging, integration of AudioEngine.
2025-11-19 18:49:43 -08:00
imonlinux 6eb3f46cbe Create audio_engine.py
Brand new file. Encapsulates audio logic, suppresses warnings, fixes busy wait.
2025-11-19 18:48:44 -08:00
imonlinux 6f5444237c Update satellite.py
Implements non-blocking downloads.
2025-11-19 18:47:37 -08:00
imonlinux cf82a3158d Update models.py
Added threading event for audio loop optimization.
2025-11-19 18:47:01 -08:00
imonlinux e63bda7e76 Update zeroconf.py
Uses the new util to ensure MDNS matches the device ID.
2025-11-19 18:46:05 -08:00
imonlinux 592d5eb0c3 Update util.py
New robust MAC address logic.
2025-11-19 18:45:16 -08:00
imonlinux 360fe06d96 Update satellite.py
Wrong file uploaded
2025-11-18 15:16:19 -08:00
imonlinux efe2b4ff4e Update api_server.py
Wrong file uploaded
2025-11-18 15:15:46 -08:00
imonlinux e3216d3e68 Update __main__.py
Wrong version uploaded.
2025-11-18 15:15:07 -08:00
imonlinux 5eb1489947 Update satellite.py
Uploaded the wrong version of the file.
2025-11-18 11:51:34 -08:00
imonlinux 2808877895 Update models.py
Uploaded wrong version of the file.
2025-11-18 11:51:04 -08:00
imonlinux 2fdef7d6ce Update __main__.py
Wrong version of file uploaded.
2025-11-18 11:48:04 -08:00
imonlinux 2ccda5ac4a Merge pull request #36 from imonlinux/decouple-controllers-breakup-main
Decouple controllers breakup main
2025-11-17 22:45:12 -08:00
imonlinux fd5cd13f72 Update __main__.py
(Major change) main() function completely refactored into helpers. _init_controllers now handles decoupled initialization. _process_audio loop logic was fixed to correctly initialize wake words on startup.

 mic.recorder() loop is now much more robust against OS buffering issue when the microphone is muted and wakewords were detected and processed after unmute.
2025-11-17 22:40:53 -08:00
imonlinux ada83bdfbb Update satellite.py
Re-added the self.state.satellite = self link, which is necessary for the main loop to be reachable from the audio thread.
2025-11-17 21:51:34 -08:00
imonlinux b62d07be7d Update event_bus.py
The EventHandler base class was modified to support the new decoupled __init__ pattern.
2025-11-17 21:50:53 -08:00
imonlinux f39df8a40a Update mqtt_controller.py
__init__ was completely rewritten to be decoupled. It now listens for mic_muted/mic_unmuted events to manage its own state instead of reading from ServerState.
2025-11-17 21:50:08 -08:00
imonlinux 37922351f6 Update led_controller.py
__init__ was completely rewritten to be decoupled. It now receives loop, event_bus, config, and preferences directly and no longer has any reference to ServerState.
2025-11-17 21:49:21 -08:00
imonlinux 64e2657824 Update models.py
ServerState is now a pure data container, no longer holding controller instances. The satellite reference was re-added, as it's required by the audio thread to call satellite.wakeup(). Fixed a TypeError by re-ordering non-default fields.
2025-11-17 21:48:08 -08:00
imonlinux 8c6df9b245 Update __main__.py
(Major change) main() function completely refactored into helpers. _init_controllers now handles decoupled initialization. _process_audio loop logic was fixed to correctly initialize wake words on startup.
2025-11-17 21:46:42 -08:00
imonlinux 52e5fb374e Update models.py
fixed wrong file uploaded
2025-11-17 20:37:46 -08:00
imonlinux fc6d750257 Merge pull request #35 from imonlinux/refactored-upstream-changes
Refactored upstream changes
2025-11-17 20:26:43 -08:00
imonlinux 4d8f5c97b7 Update util.py
Removed the unused is_arm function
2025-11-17 20:22:54 -08:00
imonlinux c9be5fc026 Update satellite.py
Added all the logic for downloading external wake words
2025-11-17 20:22:05 -08:00
imonlinux a4fa7b8631 Update __main__.py
Replaced sounddevice with soundcard, removed the audio Queue, added the new download directory to the search path, and added the device listing arguments
2025-11-17 20:21:24 -08:00
imonlinux 005900b2ae Update models.py
Replaced sounddevice with soundcard, removed the audio Queue, added the new download directory to the search path, and added the device listing arguments
2025-11-17 20:19:30 -08:00
imonlinux 324881ca8b Update models.py
Removed audio_queue, added download_dir, and updated AvailableWakeWord to use the new libraries
2025-11-17 20:18:38 -08:00
imonlinux f033595649 Update config.py
Added download_dir and removed old oww_ model paths
2025-11-17 20:17:44 -08:00
imonlinux d2a9057dde Update config.py
Updated for the new soundcard library
2025-11-17 20:17:08 -08:00
imonlinux d95c15bf40 Update mypy.ini
Updated for the new soundcard library
2025-11-17 20:16:07 -08:00
imonlinux 8d6b1849bd Update pyproject.toml
Added soundcard, pymicro-wakeword, pyopen-wakeword; removed sounddevice, pymicro-features
2025-11-17 20:14:54 -08:00
imonlinux 19677dfedd Update pyproject.toml 2025-11-17 20:14:11 -08:00
imonlinux e0bb8e7793 Update .gitignore 2025-11-17 20:13:31 -08:00
imonlinux 82c02e9db9 Delete linux_voice_assistant/openwakeword.py 2025-11-17 20:12:33 -08:00
imonlinux 9bc2830a2d Delete linux_voice_assistant/microwakeword.py 2025-11-17 20:12:18 -08:00
imonlinux 3507074698 Delete linux_voice_assistant/wakeword.py 2025-11-17 20:12:04 -08:00
imonlinux bf39055441 Delete wakewords/openWakeWord/melspectrogram.tflite 2025-11-17 20:11:42 -08:00
imonlinux 3587d8329f Delete wakewords/openWakeWord/embedding_model.tflite 2025-11-17 20:11:30 -08:00
imonlinux d07e1be653 Delete lib directory 2025-11-17 20:11:04 -08:00
imonlinux 28c4ade611 Update README.md
Refactor: Sync with Upstream Architectural Changes (Nov 2025)
Type: Refactor / Feat

Description
This PR ports several major architectural changes and new features from the upstream OHF-Voice/linux-voice-assistant repository. Because our codebase has been significantly refactored (config file, state machine), these changes were ported manually to integrate them cleanly with our new architecture.

This brings our fork up-to-date with the upstream's core logic and dependencies, ensuring we benefit from future bug fixes in those externalized libraries.

Key Changes
Audio Library Replaced: Replaced the sounddevice library with soundcard.

New Audio Capture Loop: The entire audio processing pipeline in __main__.py has been rewritten. It no longer uses a Queue and a sounddevice callback. Instead, the process_audio thread now runs a blocking mic.recorder() loop from the soundcard library, which is the new architecture adopted by the upstream project.

External Wake Word Libraries: All local wake word code (microwakeword.py, openwakeword.py, wakeword.py) and the lib/ directory have been deleted. They are replaced by new pip dependencies: pymicro-wakeword and pyopen-wakeword.

New Feature: External Wake Words: Ported the feature to allow Home Assistant to provide custom, external wake words. The LVA will now download models sent from HA to a new local/ directory.

New Feature: List Devices: Ported the --list-input-devices and --list-output-devices CLI helpers from upstream.

Dependency Updates: > * Bumped aioesphomeapi to 42.7.0 and updated api_server.py to use AuthenticationRequest (from ConnectRequest). > * Constrained zeroconf to <1. > * Updated dev dependencies (black, flake8, etc.)

Cleanup: Updated .gitignore to include the new /local/ download directory and mypy.ini for the new libraries.
2025-11-17 20:09:57 -08:00
imonlinux 90f37df4e8 Merge pull request #34 from imonlinux/centralized-configuration-file
Refactor: Centralize Configuration into config.json
Type: Refactor Issue: Closes #XYZ (if you have one)

Description
This pull request completes a major refactoring goal by moving all (20+) command-line arguments into a single, centralized config.json file.

The application is now launched with a single -c or --config flag pointing to this new file, dramatically simplifying startup scripts and configuration management.

Changes
NEW: linux_voice_assistant/config.py > * Adds new dataclasses (AppConfig, AudioConfig, LedConfig, etc.) to define the application's configuration structure. > * Includes a load_config_from_json() helper to parse the JSON file and provide default values for any omitted sections.

MODIFIED: linux_voice_assistant/__main__.py > * Removed all argparse.add_argument() calls, except for the new --config flag. > * The main() function now calls load_config_from_json() at startup. > * All internal logic now references the config object (e.g., config.app.name) instead of the old args object.
2025-11-17 18:40:01 -08:00
imonlinux 8e4480716a Update linux-voice-assistant-2mic-install.md
Updated the tutorial to reflect the change to config.json instead of command line arguements.
2025-11-17 18:38:01 -08:00
imonlinux 531e6500d0 Update README.md 2025-11-17 18:09:34 -08:00
imonlinux 70e51148bf Update satellite.py
Fixed LED startup state bug.
2025-11-17 18:05:06 -08:00