From f7c37328ef8695709c4c579340e0c0d2b77fbb5d Mon Sep 17 00:00:00 2001 From: tetele Date: Tue, 14 Jan 2025 09:16:30 +0000 Subject: [PATCH] Basic config Lights, controls, BLE improv --- esphome/modular-onju.yaml | 13 ++ esphome/onju-modules/base.yaml | 225 +++++++++++++++++++++++++++ esphome/onju-modules/ble_improv.yaml | 18 +++ esphome/onju-modules/controls.yaml | 77 +++++++++ esphome/onju-modules/leds.yaml | 220 ++++++++++++++++++++++++++ 5 files changed, 553 insertions(+) create mode 100644 esphome/modular-onju.yaml create mode 100644 esphome/onju-modules/base.yaml create mode 100644 esphome/onju-modules/ble_improv.yaml create mode 100644 esphome/onju-modules/controls.yaml create mode 100644 esphome/onju-modules/leds.yaml diff --git a/esphome/modular-onju.yaml b/esphome/modular-onju.yaml new file mode 100644 index 0000000..c1dc3c4 --- /dev/null +++ b/esphome/modular-onju.yaml @@ -0,0 +1,13 @@ +substitutions: + name: "onju-voice" + friendly_name: "Onju Voice Satellite" + project_version: "2.0.0-b0" + device_description: "Onju Voice Satellite with ESPHome software and microWakeWord" + +packages: + onju_base: !include onju-modules/base.yaml + onju_ble_improv: !include onju-modules/ble_improv.yaml + onju_leds: !include onju-modules/leds.yaml + onju_controls: !include onju-modules/controls.yaml + # audio_gnumpi: !include onju-modules/audio-gnumpi.yaml + # onju_va_mww: !include onju-modules/micro_wake_word.yaml diff --git a/esphome/onju-modules/base.yaml b/esphome/onju-modules/base.yaml new file mode 100644 index 0000000..902f7ed --- /dev/null +++ b/esphome/onju-modules/base.yaml @@ -0,0 +1,225 @@ +substitutions: + # Phases of the Voice Assistant + # The voice assistant is ready to be triggered by a wake word + voice_assist_idle_phase_id: "1" + # The voice assistant is waiting for a voice command (after being triggered by the wake word) + voice_assist_waiting_for_command_phase_id: "2" + # The voice assistant is listening for a voice command + voice_assist_listening_for_command_phase_id: "3" + # The voice assistant is currently processing the command + voice_assist_thinking_phase_id: "4" + # The voice assistant is replying to the command + voice_assist_replying_phase_id: "5" + # The voice assistant is not ready + voice_assist_not_ready_phase_id: "10" + # The voice assistant encountered an error + voice_assist_error_phase_id: "11" + +esphome: + name: "${name}" + friendly_name: "${friendly_name}" + comment: "${device_description}" + name_add_mac_suffix: true + project: + name: tetele.onju_voice_satellite + version: "${project_version}" + min_version: 2024.12.2 + platformio_options: + board_build.flash_mode: dio + board_build.arduino.memory_type: qio_opi + on_boot: + priority: 375 + then: + - script.execute: control_led + # If after 10 minutes, the device is still initializing (It did not yet connect to Home Assistant), turn off the init_in_progress variable and run the script to refresh the LED status + - delay: 10min + - if: + condition: + lambda: return id(init_in_progress); + then: + - lambda: id(init_in_progress) = false; + - script.execute: control_led + +esp32: + board: esp32-s3-devkitc-1 + flash_size: 16MB + framework: + type: esp-idf + version: recommended + sdkconfig_options: + # need to set a s3 compatible board for the adf-sdk to compile + # board specific code is not used though + CONFIG_ESP32_S3_BOX_BOARD: "y" + +psram: + mode: octal + speed: 80MHz + +# Enable logging +logger: + +# Allow OTA updates +ota: + platform: esphome + +# Allow provisioning Wi-Fi via serial +improv_serial: + +wifi: + id: wifi_id + # Set up a wifi access point + ap: + ssid: "${friendly_name}" + on_connect: + - lambda: id(improv_ble_in_progress) = false; + - script.execute: control_led + on_disconnect: + - script.execute: control_led + +# In combination with the `ap` this allows the user +# to provision wifi credentials to the device via WiFi AP. +captive_portal: + +api: + # TODO + id: api_id + on_client_connected: + - script.execute: control_led + on_client_disconnected: + - script.execute: control_led + +globals: + - id: init_in_progress + type: bool + restore_value: no + initial_value: "true" + - id: improv_ble_in_progress + type: bool + restore_value: no + initial_value: "false" + - id: voice_assistant_phase + type: int + restore_value: no + initial_value: ${voice_assist_idle_phase_id} + - id: volume_touched + type: bool + restore_value: no + initial_value: "false" + +script: + - id: control_led + mode: restart + then: + - lambda: | + // TODO + // id(check_if_timers_active).execute(); + // if (id(is_timer_active)){ + // id(fetch_first_active_timer).execute(); + // } + if (id(improv_ble_in_progress)) { + ESP_LOGD("control_led", "BLE in progress"); + id(control_led_improv_ble_state).execute(); + } else if (id(init_in_progress)) { + ESP_LOGD("control_led", "Init in progress"); + id(control_led_init).execute(); + } else if (!id(wifi_id).is_connected() || !id(api_id).is_connected()){ + ESP_LOGD("control_led", "Wifi %sconnected, API %sconnected", (id(wifi_id).is_connected() ? "" : "not "), (id(api_id).is_connected() ? "" : "not ")); + id(control_led_no_ha_connection_state).execute(); + } else if (id(action).state) { + ESP_LOGD("control_led", "Top touchpad pressed"); + id(control_led_center_button_touched).execute(); + } else if (id(volume_touched)) { + ESP_LOGD("control_led", "Volume touchpad pressed"); + id(control_led_volume_touched).execute(); + + } else if (id(voice_assistant_phase) == ${voice_assist_waiting_for_command_phase_id}) { + ESP_LOGD("control_led", "VA: waiting for command"); + id(control_led_voice_assistant_waiting_for_command_phase).execute(); + } else if (id(voice_assistant_phase) == ${voice_assist_listening_for_command_phase_id}) { + ESP_LOGD("control_led", "VA: listening for command"); + id(control_led_voice_assistant_listening_for_command_phase).execute(); + } else if (id(voice_assistant_phase) == ${voice_assist_thinking_phase_id}) { + ESP_LOGD("control_led", "VA: thinking"); + id(control_led_voice_assistant_thinking_phase).execute(); + } else if (id(voice_assistant_phase) == ${voice_assist_replying_phase_id}) { + ESP_LOGD("control_led", "VA: replying"); + id(control_led_voice_assistant_replying_phase).execute(); + } else if (id(voice_assistant_phase) == ${voice_assist_error_phase_id}) { + ESP_LOGD("control_led", "VA: error"); + id(control_led_voice_assistant_error_phase).execute(); + } else if (id(voice_assistant_phase) == ${voice_assist_not_ready_phase_id}) { + ESP_LOGD("control_led", "VA: not ready"); + id(control_led_voice_assistant_not_ready_phase).execute(); + + } else if (id(voice_assistant_phase) == ${voice_assist_idle_phase_id}) { + ESP_LOGD("control_led", "VA: idle"); + id(control_led_voice_assistant_idle_phase).execute(); + + } else { + ESP_LOGD("control_led", "Device idle"); + id(control_led_idle).execute(); + } + # /* + # } else if (id(timer_ringing).state) { + # id(control_led_timer_ringing).execute(); + + # voice stuff + + # } else if (id(is_timer_active)) { + # id(control_led_timer_ticking).execute(); + # } else if (id(master_mute_switch).state) { + # id(control_led_muted_or_silent).execute(); + # } else if (id(nabu_media_player).volume == 0.0f || id(nabu_media_player).is_muted()) { + # id(control_led_muted_or_silent).execute(); + # } + # //*/ + + - id: control_led_init + then: [] + + - id: control_led_improv_ble_state + then: [] + + - id: control_led_no_ha_connection_state + then: [] + + - id: control_led_center_button_touched + then: [] + + - id: control_led_volume_touched + then: + - lambda: | + id(volume_touched) = false; + + - id: control_led_voice_assistant_waiting_for_command_phase + then: [] + + - id: control_led_voice_assistant_listening_for_command_phase + then: [] + + - id: control_led_voice_assistant_thinking_phase + then: [] + + - id: control_led_voice_assistant_replying_phase + then: [] + + - id: control_led_voice_assistant_error_phase + then: [] + + - id: control_led_voice_assistant_not_ready_phase + then: [] + + - id: control_led_voice_assistant_idle_phase + then: [] + + - id: control_led_idle + then: [] + +button: + - platform: factory_reset + id: factory_reset_button + name: "Factory Reset" + entity_category: diagnostic + + - platform: restart + name: "Restart" diff --git a/esphome/onju-modules/ble_improv.yaml b/esphome/onju-modules/ble_improv.yaml new file mode 100644 index 0000000..64a752c --- /dev/null +++ b/esphome/onju-modules/ble_improv.yaml @@ -0,0 +1,18 @@ +esp32_ble: + name: onju-voice + +esp32_improv: + authorizer: action + on_start: + - lambda: id(improv_ble_in_progress) = true; + - script.execute: control_led + on_provisioned: + - lambda: id(improv_ble_in_progress) = false; + - script.execute: control_led + on_stop: + - lambda: id(improv_ble_in_progress) = false; + - script.execute: control_led + +wifi: + on_disconnect: + - ble.enable: diff --git a/esphome/onju-modules/controls.yaml b/esphome/onju-modules/controls.yaml new file mode 100644 index 0000000..fcbb705 --- /dev/null +++ b/esphome/onju-modules/controls.yaml @@ -0,0 +1,77 @@ +external_components: + - source: github://pr#7802 # Self calibration for ESP32 touch controls https://github.com/esphome/esphome/pull/7802 + components: [esp32_touch] + # refresh: 0s + +esp32_touch: + setup_mode: false + sleep_duration: 2ms + measurement_duration: 800us + low_voltage_reference: 0.8V + high_voltage_reference: 2.4V + + filter_mode: IIR_16 + debounce_count: 2 + noise_threshold: 0 + jitter_step: 0 + smooth_mode: IIR_2 + + denoise_grade: BIT8 + denoise_cap_level: L0 + +binary_sensor: + - platform: esp32_touch + id: volume_down + pin: GPIO4 + threshold: + mode: dynamic + initial_value: 539000 + lookback_num_values: 10 + scan_interval: 1s + max_deviation: 0.5% + max_consecutive_anomalies: 10 + on_press: + then: + - light.turn_on: left_led + - lambda: | + id(volume_touched) = true; + on_release: + then: + - light.turn_off: left_led + + - platform: esp32_touch + id: volume_up + pin: GPIO2 + threshold: + mode: dynamic + initial_value: 580000 + lookback_num_values: 10 + scan_interval: 1s + max_deviation: 0.5% + max_consecutive_anomalies: 10 + on_press: + then: + - light.turn_on: right_led + - lambda: | + id(volume_touched) = true; + on_release: + then: + - light.turn_off: right_led + + - platform: esp32_touch + id: action + pin: GPIO3 + threshold: + mode: dynamic + initial_value: 751000 + lookback_num_values: 10 + scan_interval: 1s + max_deviation: 0.5% + max_consecutive_anomalies: 10 + + - platform: gpio + id: mute_switch + pin: + number: GPIO38 + mode: INPUT_PULLUP + name: Disable microphone diff --git a/esphome/onju-modules/leds.yaml b/esphome/onju-modules/leds.yaml new file mode 100644 index 0000000..d00cac4 --- /dev/null +++ b/esphome/onju-modules/leds.yaml @@ -0,0 +1,220 @@ +light: + - platform: esp32_rmt_led_strip + id: leds + pin: GPIO11 + chipset: SK6812 + num_leds: 6 + rgb_order: GRB + rmt_channel: 0 + default_transition_length: 0s + gamma_correct: 2.8 + + - platform: partition + id: left_led + segments: + - id: leds + from: 0 + to: 0 + default_transition_length: 100ms + + - platform: partition + id: top_led + segments: + - id: leds + from: 1 + to: 4 + default_transition_length: 100ms + effects: + - pulse: + name: pulse + transition_length: 250ms + update_interval: 250ms + - pulse: + name: slow_pulse + transition_length: 1s + update_interval: 2s + - addressable_twinkle: + name: slow_twinkle + twinkle_probability: 1% + - addressable_twinkle: + name: twinkle + twinkle_probability: 7% + - addressable_twinkle: + name: fast_twinkle + twinkle_probability: 45% + - addressable_scan: + name: processing + move_interval: 80ms + - addressable_flicker: + name: speaking + intensity: 35% + - addressable_random_twinkle: + name: random_twinkle + twinkle_probability: 45% + - addressable_color_wipe: + name: swipe_right + colors: + - red: 100% + green: 100% + blue: 100% + num_leds: 1 + gradient: true + - red: 0 + green: 0 + blue: 0 + num_leds: 2 + gradient: true + - red: 0 + green: 0 + blue: 0 + num_leds: 3 + gradient: false + add_led_interval: 100ms + reverse: false + - addressable_color_wipe: + name: swipe_right_fast + colors: + - red: 100% + green: 100% + blue: 100% + num_leds: 1 + gradient: true + - red: 0 + green: 0 + blue: 0 + num_leds: 2 + gradient: true + - red: 0 + green: 0 + blue: 0 + num_leds: 3 + gradient: false + add_led_interval: 50ms + reverse: false + - addressable_color_wipe: + name: swipe_left + colors: + - red: 100% + green: 100% + blue: 100% + num_leds: 1 + gradient: true + - red: 0 + green: 0 + blue: 0 + num_leds: 2 + gradient: true + - red: 0 + green: 0 + blue: 0 + num_leds: 3 + gradient: false + add_led_interval: 100ms + reverse: true + + - platform: partition + id: right_led + segments: + - id: leds + from: 5 + to: 5 + default_transition_length: 100ms + +script: + - id: !extend control_led_init + then: + - if: + condition: + wifi.connected: + then: + - light.turn_on: + id: top_led + brightness: 66% + red: 9.4% + green: 73.3% + blue: 94.9% + effect: twinkle + else: + - light.turn_on: + id: top_led + brightness: 66% + red: 100% + green: 89% + blue: 71% + effect: twinkle + + - id: !extend control_led_improv_ble_state + then: + - light.turn_on: + brightness: 66% + red: 100% + green: 89% + blue: 71% + id: top_led + effect: random_twinkle + + - id: !extend control_led_no_ha_connection_state + then: + - light.turn_on: + brightness: 66% + red: 1 + green: 0 + blue: 0 + id: top_led + effect: twinkle + + - id: !extend control_led_voice_assistant_idle_phase + then: + - light.turn_off: top_led + + - id: !extend control_led_voice_assistant_waiting_for_command_phase + then: + - light.turn_on: + brightness: 66% + id: top_led + effect: swipe_right + + - id: !extend control_led_voice_assistant_listening_for_command_phase + then: + - light.turn_on: + brightness: 66% + id: top_led + effect: swipe_right_fast + + - id: !extend control_led_voice_assistant_thinking_phase + then: + - light.turn_on: + brightness: 66% + id: top_led + effect: pulse + + - id: !extend control_led_voice_assistant_replying_phase + then: + - light.turn_on: + brightness: 66% + id: top_led + effect: swipe_left + + - id: !extend control_led_voice_assistant_error_phase + then: + - light.turn_on: + brightness: 33% + red: 1 + green: 0 + blue: 0 + id: top_led + effect: pulse + + - id: !extend control_led_voice_assistant_not_ready_phase + then: + - light.turn_on: + brightness: 33% + red: 1 + green: 0 + blue: 0 + id: top_led + effect: fast_twinkle + + - id: !extend control_led_idle + then: + - light.turn_off: top_led