16 Commits
10 changed files with 862 additions and 101 deletions
+1 -1
View File
@@ -1 +1 @@
2026.9.3
2026.11.0
+167 -77
View File
@@ -28,18 +28,18 @@
alias: TV Anti-Reflexion
description: Schaltet Wohnzimmer hinten Ambient Light aus, um Reflexionen im TV
zu verhindern.
trigger:
- platform: state
entity_id: media_player.lg_webos_smart_tv
triggers:
- entity_id: media_player.lg_webos_smart_tv
from: 'off'
to: 'on'
condition: []
action:
- service: light.turn_off
target:
device_id: 6dcbd87b459412144bddc42af3ae8b83
trigger: state
conditions: []
actions:
- target:
device_id: 5a0caeab0be8e10747c74dcf5dd9ced1
data:
transition: 2
action: light.turn_off
mode: single
- id: '1623526683767'
alias: Licht bei Sonnenaufgang ausschalten
@@ -56,14 +56,14 @@
area_id:
- schlafzimmer
device_id:
- 53a27edf581fcf40f3bc8b253b1ee369
- 4e8211f6c1ad515c79d7a26f3d7c9a27
- c64e7c3dcda7f1c23e456959f2c60f39
- 4edd9b9df7d1f6f2fe7dcc2e5c0eb968
- 6dcbd87b459412144bddc42af3ae8b83
- 98a9eef603f8c8ae6200c4834a3dbf4f
- 32d15eec63fe68d5770813d47da53657
- 9f42805af5b7e423023595390342b9ac
- f39e4bf25a3bee97f237b0d7ddd64493
- ca8b65e4a22153f953a88d7bb1efb36b
- 2b27a605524f6617ed1f71f98e0eb74d
- 2a5b866b0062c298950cb0bd03ec14eb
- 5a0caeab0be8e10747c74dcf5dd9ced1
- 23aeb31cedaf3ff0bf14d4a05fc779da
- fb581090b15a027cf071b99d0ea00874
- a7c56c0e328765addc33be2f375778bc
entity_id: light.kajplats_e14_cws_globe_806lm
data:
transition: 2
@@ -85,17 +85,39 @@
area_id:
- wohnzimmer
- kuche
- schlafzimmer
- kinderzimmer
entity_id:
- light.awtrix_528bd4_matrix
- light.awtrix_b8658c_matrix
- light.schlafzimmer_bett
- light.schlafzimmer_deckenlicht
data: {}
action: light.turn_off
- type: turn_off
device_id: 6d1be741876624a70ab5b01b54c6fd6f
device_id: 40ea5b499584568460396dc9b920beca
entity_id: switch.kuche_musik
domain: switch
- action: light.turn_on
metadata: {}
target:
entity_id: light.schlafzimmeruhr_display
data:
rgb_color:
- 224
- 27
- 36
brightness_pct: 5
- action: button.press
metadata: {}
target:
entity_id: button.schlafzimmeruhr_clear_digit_colors
data: {}
- action: select.select_option
metadata: {}
target:
entity_id: select.schlafzimmeruhr_effect
data:
option: None
- action: lock.lock
metadata: {}
target:
@@ -194,23 +216,23 @@
alias: TV Anti-Reflexion (undo)
description: Schaltet Wohnzimmer hinten Ambient Light wieder ein, wenn vorne auch
(noch) eingeschaltet ist.
trigger:
- platform: state
entity_id: media_player.lg_webos_smart_tv
triggers:
- entity_id: media_player.lg_webos_smart_tv
to: 'off'
from: 'on'
condition:
trigger: state
conditions:
- condition: device
type: is_on
device_id: 4edd9b9df7d1f6f2fe7dcc2e5c0eb968
device_id: 2a5b866b0062c298950cb0bd03ec14eb
entity_id: light.wohnzimmer_vorne
domain: light
action:
- service: light.turn_on
data:
actions:
- data:
transition: 2
target:
device_id: 6dcbd87b459412144bddc42af3ae8b83
device_id: 5a0caeab0be8e10747c74dcf5dd9ced1
action: light.turn_on
mode: single
- id: '1623941937228'
alias: Licht im Schlafzimmer zur Schlafenszeit einschalten
@@ -228,9 +250,24 @@
state: home
actions:
- target:
device_id: 68868390eda35e969ec60a13020f2407
device_id: ddf95b1e837e69c677460491a2600cd4
data: {}
action: light.turn_on
- action: select.select_option
metadata: {}
target:
entity_id: select.schlafzimmeruhr_effect
data:
option: None
- action: esphome.sleepingroom_clock_set_gradient
data:
red1: 255
green1: 40
blue1: 0
red2: 60
green2: 0
blue2: 120
angle: 0
- data:
cache: true
media_player_entity_id: media_player.home_assistant_voice_0a7c6b_media_player
@@ -276,7 +313,7 @@
seconds: 0
milliseconds: 0
- target:
device_id: 68868390eda35e969ec60a13020f2407
device_id: ddf95b1e837e69c677460491a2600cd4
data: {}
action: light.turn_on
- action: lock.unlock
@@ -284,6 +321,25 @@
target:
entity_id: lock.flur_wohnungstur
data: {}
- action: light.turn_on
metadata: {}
target:
entity_id: light.schlafzimmeruhr_display
data:
brightness_pct: 50
enabled: true
- action: select.select_option
metadata: {}
target:
entity_id: select.schlafzimmeruhr_effect
data:
option: Color Cycle
- action: select.select_option
metadata: {}
target:
entity_id: select.schlafzimmeruhr_effect_spread
data:
option: Per LED
mode: single
- id: '1624820688449'
alias: 'Anruf: Beim Klingeln grün blinken'
@@ -406,10 +462,9 @@
- id: '1628972104416'
alias: Raumduft nach einer Stunde wieder ausschalten
description: ''
trigger:
- platform: device
type: turned_on
device_id: 5a08ac4c3b3893b540a9934fa92dccfa
triggers:
- type: turned_on
device_id: b29693c2517f4d6538da9fe7e5e5a262
entity_id: switch.flur_raumduft
domain: switch
for:
@@ -417,10 +472,11 @@
minutes: 0
seconds: 0
milliseconds: 0
condition: []
action:
trigger: device
conditions: []
actions:
- type: turn_off
device_id: 5a08ac4c3b3893b540a9934fa92dccfa
device_id: b29693c2517f4d6538da9fe7e5e5a262
entity_id: switch.flur_raumduft
domain: switch
mode: single
@@ -439,7 +495,7 @@
state: home
actions:
- type: turn_on
device_id: 5a08ac4c3b3893b540a9934fa92dccfa
device_id: b29693c2517f4d6538da9fe7e5e5a262
entity_id: switch.flur_raumduft
domain: switch
- data: {}
@@ -472,20 +528,20 @@
data: {}
action: light.turn_off
- type: turn_off
device_id: 6d1be741876624a70ab5b01b54c6fd6f
device_id: 40ea5b499584568460396dc9b920beca
entity_id: switch.kuche_musik
domain: switch
- type: turn_off
device_id: 5a08ac4c3b3893b540a9934fa92dccfa
device_id: b29693c2517f4d6538da9fe7e5e5a262
entity_id: switch.flur_raumduft
domain: switch
- action: light.turn_off
metadata: {}
data: {}
target:
device_id:
- 231cd7ec37e32690faf126105b03ee02
- a6d24e7d93c114723bf525e9eac1c2df
entity_id:
- light.awtrix_b8658c_matrix
- light.awtrix_528bd4_matrix
- action: lock.lock
metadata: {}
target:
@@ -521,8 +577,8 @@
action: script.moodlight_orange_plasma
- target:
device_id:
- 53a27edf581fcf40f3bc8b253b1ee369
- 5abbb43e5d015ee75c7c900240b4329e
- f39e4bf25a3bee97f237b0d7ddd64493
data:
brightness_pct: 70
color_temp_kelvin: 2000
@@ -885,6 +941,7 @@
entity_id:
- light.awtrix_b8658c_matrix
- light.awtrix_528bd4_matrix
- light.schlafzimmeruhr_display
action: light.turn_on
mode: single
- id: '1700482951854'
@@ -905,8 +962,8 @@
action: script.moodlight_xmas
- target:
device_id:
- 53a27edf581fcf40f3bc8b253b1ee369
- 5abbb43e5d015ee75c7c900240b4329e
- f39e4bf25a3bee97f237b0d7ddd64493
data:
brightness_pct: 70
color_temp_kelvin: 2000
@@ -933,15 +990,15 @@
area_id:
- schlafzimmer
device_id:
- 53a27edf581fcf40f3bc8b253b1ee369
- 4e8211f6c1ad515c79d7a26f3d7c9a27
- c64e7c3dcda7f1c23e456959f2c60f39
- 4edd9b9df7d1f6f2fe7dcc2e5c0eb968
- 6dcbd87b459412144bddc42af3ae8b83
- 98a9eef603f8c8ae6200c4834a3dbf4f
- 32d15eec63fe68d5770813d47da53657
- 9f42805af5b7e423023595390342b9ac
- 5abbb43e5d015ee75c7c900240b4329e
- f39e4bf25a3bee97f237b0d7ddd64493
- ca8b65e4a22153f953a88d7bb1efb36b
- 2b27a605524f6617ed1f71f98e0eb74d
- 2a5b866b0062c298950cb0bd03ec14eb
- 5a0caeab0be8e10747c74dcf5dd9ced1
- 23aeb31cedaf3ff0bf14d4a05fc779da
- fb581090b15a027cf071b99d0ea00874
- a7c56c0e328765addc33be2f375778bc
data:
transition: 2
action: light.turn_off
@@ -965,15 +1022,15 @@
- data: {}
target:
device_id:
- 4edd9b9df7d1f6f2fe7dcc2e5c0eb968
- 6dcbd87b459412144bddc42af3ae8b83
- 2a5b866b0062c298950cb0bd03ec14eb
- 5a0caeab0be8e10747c74dcf5dd9ced1
action: light.turn_on
action_button_down_short:
- data: {}
target:
device_id:
- 6dcbd87b459412144bddc42af3ae8b83
- 4edd9b9df7d1f6f2fe7dcc2e5c0eb968
- 5a0caeab0be8e10747c74dcf5dd9ced1
- 2a5b866b0062c298950cb0bd03ec14eb
action: light.turn_off
action_button_left_short:
- data: {}
@@ -989,14 +1046,14 @@
data: {}
action_button_down_double:
- target:
device_id: 9f42805af5b7e423023595390342b9ac
device_id: a7c56c0e328765addc33be2f375778bc
data: {}
action: light.turn_off
action_button_up_double:
- metadata: {}
data: {}
target:
device_id: 9f42805af5b7e423023595390342b9ac
device_id: a7c56c0e328765addc33be2f375778bc
action: light.turn_on
button_up_double_press: true
button_down_double_press: true
@@ -1021,39 +1078,38 @@
remote: bd97db2ae9b0104d50dc6a343315608b
double_dot_single_press:
- target:
device_id:
- 68868390eda35e969ec60a13020f2407
device_id: ddf95b1e837e69c677460491a2600cd4
data: {}
action: light.turn_off
double_dot_double_press:
- metadata: {}
data: {}
target:
device_id: 3c86ddd39979139f29645308815c0271
device_id: 824bb838dd09391e75dd7c366a571c4c
action: light.turn_off
single_dot_single_press:
- metadata: {}
data: {}
target:
device_id: 68868390eda35e969ec60a13020f2407
device_id: ddf95b1e837e69c677460491a2600cd4
action: light.turn_on
single_dot_double_press:
- metadata: {}
data: {}
target:
device_id: 3c86ddd39979139f29645308815c0271
device_id: 824bb838dd09391e75dd7c366a571c4c
action: light.turn_on
double_dot_long_press:
- metadata: {}
data: {}
target:
device_id: b7c1c80b21406c5608e38aa0c7e7e439
device_id: 64daf8ee59e890acd2930fa91bcdeb9c
action: switch.turn_off
single_dot_long_press:
- metadata: {}
data: {}
target:
device_id: b7c1c80b21406c5608e38aa0c7e7e439
device_id: 64daf8ee59e890acd2930fa91bcdeb9c
action: switch.turn_on
- id: '1705488817426'
alias: Let's Encrypt
@@ -1071,17 +1127,17 @@
- id: '1705787842132'
alias: 'Luftbefeuchter: Zielwert'
description: Leicht oberhalb des Wohnungsdurchschnitts
trigger:
- platform: time_pattern
minutes: /10
condition: []
action:
- service: humidifier.set_humidity
metadata: {}
triggers:
- minutes: /10
trigger: time_pattern
conditions: []
actions:
- metadata: {}
data:
humidity: '{{ int(states(''sensor.wohnungsluftfeuchtigkeit'')) + 20 }}'
target:
entity_id: humidifier.badezimmer
entity_id: humidifier.badezimmer_badezimmer_ventilator_absaugung
action: humidifier.set_humidity
mode: single
- id: '1708188056710'
alias: IKEA STYRBAR - Moodlight Wohnzimmer
@@ -1112,14 +1168,14 @@
domain: switch
action_button_up_double:
- type: turn_on
device_id: 98a9eef603f8c8ae6200c4834a3dbf4f
device_id: 23aeb31cedaf3ff0bf14d4a05fc779da
entity_id: fdcff3852ecc17099c953da17eb4b8a3
domain: light
button_up_double_press: true
button_down_double_press: true
action_button_down_double:
- type: turn_off
device_id: 98a9eef603f8c8ae6200c4834a3dbf4f
device_id: 23aeb31cedaf3ff0bf14d4a05fc779da
entity_id: fdcff3852ecc17099c953da17eb4b8a3
domain: light
action_button_up_long: []
@@ -1390,7 +1446,7 @@
data:
volume_level: 0.47
- type: turn_off
device_id: 81d56cc472abaec2a02702def5f9cf66
device_id: f11b54371dd442568f1642fabf649116
entity_id: a57a56132635ff48a6d2ae8df322b385
domain: switch
mode: single
@@ -1403,7 +1459,7 @@
conditions: []
actions:
- type: turn_on
device_id: 81d56cc472abaec2a02702def5f9cf66
device_id: f11b54371dd442568f1642fabf649116
entity_id: a57a56132635ff48a6d2ae8df322b385
domain: switch
mode: single
@@ -1491,3 +1547,37 @@
entity_id: button.touchkio_raspberrypi_refresh
data: {}
mode: single
- id: '1791126911003'
alias: 'Schlafzimmeruhr: Außentemperatur anzeigen'
description: ''
triggers:
- trigger: state
entity_id:
- binary_sensor.schlafzimmeruhr_button_2
to:
- 'on'
conditions: []
actions:
- action: esphome.sleepingroom_clock_show_number
data:
value: '{{ states(''sensor.aussentemperatur'') | round(0) | int }}'
unit: C
duration: 10000
mode: single
- id: '1791127515667'
alias: 'Schlafzimmeruhr: Sekunden anzeigen'
description: ''
triggers:
- trigger: state
entity_id:
- binary_sensor.schlafzimmeruhr_button_1
to:
- 'on'
conditions: []
actions:
- action: button.press
metadata: {}
target:
entity_id: button.schlafzimmeruhr_show_seconds
data: {}
mode: single
+4
View File
@@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import time as time_
from esphome.components.esp32 import include_builtin_idf_component
from esphome.const import CONF_ID, CONF_NUMBER, PLATFORM_ESP32
CODEOWNERS = ["@misterblack1"]
@@ -64,6 +65,9 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config):
# The display scan uses GPTimer, which ESPHome excludes by default.
include_builtin_idf_component("esp_driver_gptimer")
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
@@ -0,0 +1,50 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor, i2c, sensor
from esphome.const import CONF_ID, ENTITY_CATEGORY_DIAGNOSTIC
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["sensor", "binary_sensor"]
ns = cg.esphome_ns.namespace("luxe_ip5306")
LuxeIP5306 = ns.class_("LuxeIP5306", cg.PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(LuxeIP5306),
cv.Required("available"): binary_sensor.binary_sensor_schema(
device_class="connectivity", entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Required("charging"): binary_sensor.binary_sensor_schema(
device_class="battery_charging"
),
cv.Required("full"): binary_sensor.binary_sensor_schema(),
cv.Required("charge_allowed"): binary_sensor.binary_sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Required("level"): sensor.sensor_schema(
unit_of_measurement="%", accuracy_decimals=0, device_class="battery",
state_class="measurement", entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Required("current_limit"): sensor.sensor_schema(
unit_of_measurement="mA", accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
}
)
.extend(cv.polling_component_schema("10s"))
.extend(i2c.i2c_device_schema(0x75))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
for key in ("available", "charging", "full", "charge_allowed"):
entity = await binary_sensor.new_binary_sensor(config[key])
cg.add(getattr(var, f"set_{key}")(entity))
for key in ("level", "current_limit"):
entity = await sensor.new_sensor(config[key])
cg.add(getattr(var, f"set_{key}")(entity))
@@ -0,0 +1,108 @@
#pragma once
#include <cmath>
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome::luxe_ip5306 {
// Optional IP5306-I2C on recent Luxe boards. Never changes power settings at boot.
class LuxeIP5306 : public PollingComponent, public i2c::I2CDevice {
public:
void set_available(binary_sensor::BinarySensor *s) { available_ = s; }
void set_charging(binary_sensor::BinarySensor *s) { charging_ = s; }
void set_full(binary_sensor::BinarySensor *s) { full_ = s; }
void set_charge_allowed(binary_sensor::BinarySensor *s) { charge_allowed_ = s; }
void set_level(sensor::Sensor *s) { level_ = s; }
void set_current_limit(sensor::Sensor *s) { current_limit_ = s; }
float get_setup_priority() const override { return setup_priority::DATA; }
void setup() override { update(); }
void update() override {
uint8_t control, charge, full, limit, gauge;
// Address-only ACK probe, then individual register reads. No assumption
// that the chip supports register auto-increment.
if (write(nullptr, 0) != i2c::ERROR_OK ||
!read_byte(0x00, &control) || !read_byte(0x70, &charge) ||
!read_byte(0x71, &full) || !read_byte(0x24, &limit)) {
unavailable_();
return;
}
valid_ = true;
charge_enabled_ = (control & 0x10) != 0;
available_->publish_state(true);
charge_allowed_->publish_state(charge_enabled_);
const bool full_flag = (full & 0x08) != 0;
full_->publish_state(full_flag);
charging_->publish_state(charge_enabled_ && (charge & 0x08) && !full_flag);
// Configured current, NOT measured current. Keep it separate from the
// charging state: 0x70 alone cannot establish USB presence in every mode.
current_limit_->publish_state(50 + 100 * (limit & 0x1F));
// Gauge is optional: undocumented on some revisions. Failure/unknown
// codes must never be presented as an empty battery.
float percent = NAN;
if (read_byte(0x78, &gauge)) {
switch (gauge >> 4) {
case 0x0: percent = 100; break;
case 0x8: percent = 75; break;
case 0xC: percent = 50; break;
case 0xE: percent = 25; break;
case 0xF: percent = 0; break;
}
}
level_->publish_state(percent);
}
optional<bool> charge_enabled() const {
if (!valid_) return {};
return charge_enabled_;
}
void set_charge_enabled(bool enabled) {
uint8_t before;
// Read afresh even if the last poll succeeded. Modify only bit 4, leaving
// boost, key behaviour and all reserved bits untouched.
if (write(nullptr, 0) != i2c::ERROR_OK || !read_byte(0x00, &before)) {
unavailable_();
ESP_LOGW("luxe_ip5306", "Recharge command rejected: circuit unavailable");
return;
}
const uint8_t after = enabled ? (before | 0x10) : (before & ~0x10);
if (after != before && !write_byte(0x00, after)) {
unavailable_();
ESP_LOGW("luxe_ip5306", "Recharge command failed");
return;
}
update(); // Read-back is authoritative; the switch is not optimistic.
if (valid_ && charge_enabled_ != enabled)
ESP_LOGW("luxe_ip5306", "Recharge command not confirmed by circuit");
}
protected:
void unavailable_() {
valid_ = false;
available_->publish_state(false);
charging_->invalidate_state();
full_->invalidate_state();
charge_allowed_->invalidate_state();
level_->publish_state(NAN);
current_limit_->publish_state(NAN);
// No mark_failed(): an older board must still play audio, and a device
// temporarily asleep or disconnected must be detected on the next poll.
}
binary_sensor::BinarySensor *available_{nullptr};
binary_sensor::BinarySensor *charging_{nullptr};
binary_sensor::BinarySensor *full_{nullptr};
binary_sensor::BinarySensor *charge_allowed_{nullptr};
sensor::Sensor *level_{nullptr};
sensor::Sensor *current_limit_{nullptr};
bool valid_{false};
bool charge_enabled_{false};
};
} // namespace esphome::luxe_ip5306
+487
View File
@@ -0,0 +1,487 @@
substitutions:
startup_sound_file: https://github.com/RASPIAUDIO/esphomeLuxe/raw/refs/heads/main/wav/sounds_timer_finished.wav
device_description: "Raspiaudio Muse Luxe Music player for Music Assistant using sendspin"
name: "muse-luxe-music"
friendly_name: "Raspiaudio Muse Luxe Music"
esphome:
comment: ${device_description}
name: ${name}
friendly_name: ${friendly_name}
min_version: 2026.8.2
name_add_mac_suffix: false
on_boot:
priority: -100
then:
# GPIO21 commande la mise en sourdine de l'amplificateur de la Luxe.
- output.turn_off: dac_mute
# Laisse le mixer terminer une annonce avant la reprise d'un flux MA.
- speaker_source.set_playlist_delay:
id: external_media_player
pipeline: announcement
delay: 500ms
- speaker_source.set_playlist_delay:
id: external_media_player
pipeline: media
delay: 1500ms
- script.execute: led_red
project:
name: raspiaudio.music-assistant
version: "2026.9.3"
esp32:
variant: esp32
framework:
type: esp-idf
advanced:
include_builtin_idf_components:
- esp_http_client
sram1_as_iram: true
logger:
level: warn
api:
encryption:
key: !secret apikey
# La lecture Sendspin doit continuer même sans client Home Assistant connecté.
reboot_timeout: 0s
on_client_connected:
- if:
condition:
lambda: return !id(boot_sound_played);
then:
- lambda: id(boot_sound_played) = true;
# Laisse MA restaurer son groupe avant de décider si l'annonce peut jouer.
- delay: 5s
- if:
condition:
lambda: |-
using media_player::MediaPlayerState;
return id(startup_sound_switch).state &&
id(external_media_player).state != MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING &&
id(external_media_player).state != MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING;
then:
- script.execute:
id: play_sound
priority: true
sound_file: ready_sound
wifi:
ssid: "Voltage-legacy"
password: !secret voltage_legacy_psk
#use_address: ${name}.home
power_save_mode: high
fast_connect: on
min_auth_mode: WPA2
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Raspiaudio Fallback Hotspot"
password: !secret fallback_psk
psram:
mode: quad
speed: 80MHz
ota:
- platform: esphome
id: ota_esphome
encryption:
- platform: http_request
id: ota_http_request
http_request:
captive_portal:
improv_serial:
external_components:
- source:
type: local
path: components
components: [luxe_ip5306]
# Initialisation spécifique du codec de la museLuxe (sorties différentielles
# et horloge MCLK sur GPIO0). Sendspin et la chaîne audio sont désormais natifs.
- source:
type: git
url: https://github.com/RASPIAUDIO/esphomeLuxe
ref: 192372ff59b560a74aa547dc26af609918fcc9d6
components: [es8388]
i2c:
sda: GPIO18
scl: GPIO23
frequency: 100kHz
id: bus_a
es8388:
i2s_audio:
- id: i2s_audio_bus
i2s_lrclk_pin: GPIO25
i2s_bclk_pin: GPIO5
#####################
# Internal Components
output:
- platform: gpio
id: dac_mute
pin:
number: GPIO21
inverted: true
mode:
output: true
speaker:
- platform: i2s_audio
id: box_speaker
i2s_audio_id: i2s_audio_bus
i2s_dout_pin: GPIO26
dac_type: external
channel: stereo
sample_rate: 48000
bits_per_sample: 16bit
buffer_duration: 300ms
mclk_multiple: 384
- platform: mixer
id: mixing_speaker
output_speaker: box_speaker
num_channels: 2
source_speakers:
- id: announcement_mixer_input
- id: media_mixer_input
- platform: resampler
id: media_resampling_input
output_speaker: media_mixer_input
sample_rate: 48000
- platform: resampler
id: announcement_resampling_input
output_speaker: announcement_mixer_input
sample_rate: 48000
sendspin:
id: sendspin_hub
audio_file:
- id: ready_sound
file: ${startup_sound_file}
media_source:
- platform: sendspin
id: sendspin_source
buffer_size: 2000000
- platform: audio_file
id: file_source
media_player:
- platform: sendspin
id: sendspin_media_player
name: museLuxe
volume_increment: 2%
- platform: speaker_source
name: museLuxe Audio
id: external_media_player
internal: true
announcement_pipeline:
speaker: announcement_resampling_input
sources:
- file_source
format: FLAC
num_channels: 1
sample_rate: 48000
media_pipeline:
speaker: media_resampling_input
sources:
- sendspin_source
format: FLAC
num_channels: 2
sample_rate: 48000
volume_increment: 0.05
volume_min: 0.05
volume_max: 0.95
on_state:
then:
- lambda: |-
using media_player::MediaPlayerState;
auto state = id(external_media_player).state;
if (
state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING ||
state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING
) {
id(led_green).execute();
} else {
id(led_red).execute();
}
script:
- id: update_battery_led
mode: queued
max_runs: 2
then:
- lambda: |-
const bool critical = id(battery_critical).state;
if (critical && (!id(led).remote_values.is_on() || id(led).get_effect_name() != "Battery alert")) {
auto call = id(led).turn_on();
call.set_effect("Battery alert");
call.set_brightness(1.0f);
call.perform();
} else if (!critical && id(led).get_effect_name() == "Battery alert") {
auto call = id(led).turn_on();
call.set_effect("None");
call.perform();
}
- id: play_sound
parameters:
priority: bool
sound_file: string
then:
- if:
condition:
lambda: return priority;
then:
- media_player.stop:
id: external_media_player
announcement: true
- lambda: |-
if ( (id(external_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) {
id(external_media_player)
->make_call()
.set_media_url("audio-file://" + sound_file)
.set_announcement(true)
.perform();
}
- id: led_red
then:
- light.turn_on:
id: led
red: 100%
green: 0%
blue: 0%
brightness: 100%
- id: led_green
then:
- light.turn_on:
id: led
red: 0%
green: 100%
blue: 0%
brightness: 100%
switch:
- platform: template
id: ip5306_charge_switch
name: "Allow charging"
entity_category: config
icon: mdi:battery-charging
restore_mode: DISABLED
optimistic: false
lambda: return id(battery_manager).charge_enabled();
turn_on_action:
- lambda: id(battery_manager).set_charge_enabled(true);
turn_off_action:
- lambda: id(battery_manager).set_charge_enabled(false);
- platform: template
id: startup_sound_switch
name: Startup sound
icon: "mdi:card-text-outline"
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
globals:
- id: boot_sound_played
type: bool
restore_value: no
initial_value: "false"
binary_sensor:
- platform: template
id: battery_critical
name: Battery low
device_class: battery
entity_category: diagnostic
lambda: |-
if (!id(battery_percent).has_state() || !std::isfinite(id(battery_percent).state)) return {};
const float percent = id(battery_percent).state;
const float threshold = id(battery_alert_threshold).state;
if (percent < threshold) return true;
if (percent >= threshold + 5.0f) return false;
return {};
on_state:
- script.execute: update_battery_led
- platform: gpio
pin:
number: GPIO12
inverted: true
mode:
input: true
output: false
open_drain: false
pullup: true
pulldown: false
drive_strength: 20.0
name: Play or Pause
on_press:
then:
- media_player.toggle: sendspin_media_player
- platform: gpio
pin:
number: GPIO19
inverted: true
mode:
input: true
pullup: true
name: Volume Up
on_click:
- media_player.volume_up:
id: sendspin_media_player
- platform: gpio
pin:
number: GPIO32
inverted: true
mode:
input: true
pullup: true
name: Volume Down
on_click:
- media_player.volume_down:
id: sendspin_media_player
light:
- platform: esp32_rmt_led_strip
id: led
name: LED light
disabled_by_default: false
entity_category: config
pin: GPIO22
default_transition_length: 0s
chipset: WS2812
num_leds: 1
channel_colors: GRB
# Conserve l'alerte après une commande HA ou un changement lecture/arrêt.
on_state:
- script.execute: update_battery_led
effects:
- addressable_lambda:
name: "Battery alert"
update_interval: 500ms
lambda: |-
static bool lit = false;
if (initial_run) lit = false;
lit = !lit;
it.all() = lit ? current_color : Color::BLACK;
- pulse:
name: "Slow Pulse"
transition_length: 250ms
update_interval: 250ms
min_brightness: 50%
max_brightness: 100%
- pulse:
name: "Fast Pulse"
transition_length: 100ms
update_interval: 100ms
min_brightness: 50%
max_brightness: 100%
sensor:
- platform: adc
pin: GPIO33
id: battery_voltage
name: Battery voltage
icon: "mdi:battery-outline"
update_interval: 15s
accuracy_decimals: 3
attenuation: 12db
raw: true
filters:
- multiply: 0.00173913 # 2300 -> 4, for attenuation 11db, based on Olivier's code
- exponential_moving_average:
alpha: 0.2
send_every: 2
- delta: 0.002
on_value:
- sensor.template.publish:
id: battery_percent
state: !lambda return x;
- platform: template
id: battery_percent
name: Battery level
device_class: battery
unit_of_measurement: "%"
accuracy_decimals: 0
state_class: measurement
entity_category: diagnostic
update_interval: never
# Même estimation tension/charge que la Radio, après calibration Luxe.
filters:
- calibrate_polynomial:
degree: 3
datapoints:
- 4.20 -> 100.0
- 4.13 -> 97.1
- 4.10 -> 94.2
- 4.07 -> 88.4
- 4.05 -> 82.7
- 4.04 -> 76.9
- 4.04 -> 71.1
- 4.01 -> 65.3
- 3.99 -> 59.5
- 3.95 -> 53.8
- 3.93 -> 48.0
- 3.91 -> 42.2
- 3.88 -> 36.4
- 3.86 -> 30.6
- 3.84 -> 24.9
- 3.82 -> 19.1
- 3.76 -> 13.3
- 3.73 -> 10.4
- 3.70 -> 7.5
- 3.64 -> 4.6
- 3.50 -> 1.7
- 3.00 -> 0.0
- lambda: return clamp(x, 0.0f, 100.0f);
- platform: wifi_signal
name: "museLuxe WiFi signal"
update_interval: 10s
# Paramètre logiciel : conserve une hystérésis fixe de 5 points.
number:
- platform: template
id: battery_alert_threshold
name: "Low battery threshold"
entity_category: config
unit_of_measurement: "%"
min_value: 5
max_value: 50
step: 1
initial_value: 20
restore_value: true
optimistic: true
mode: box
luxe_ip5306:
id: battery_manager
i2c_id: bus_a
address: 0x75
update_interval: 10s
available:
name: "IP5306 available"
id: ip5306_available
charging:
name: "Charging"
full:
name: "Battery full"
charge_allowed:
name: "Charging enabled (hardware)"
level:
name: "IP5306 battery level"
current_limit:
name: "IP5306 charge current limit"
+7 -1
View File
@@ -165,6 +165,12 @@ wifi:
fast_connect: on
min_auth_mode: WPA2
# The panel uses the upper colon dot as its Wi-Fi connection indicator.
on_connect:
- lambda: id(panel)->set_online(true);
on_disconnect:
- lambda: id(panel)->set_online(false);
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Schlafzimmeruhr Fallback Hotspot"
@@ -244,7 +250,7 @@ binary_sensor:
id: button_2
name: Button 2
pin:
number: GPIO5 # MCU socket pin 10, net S2, bottom button on the case
number: GPIO7 # MCU socket pin 10, net S2, bottom button on the case
mode:
input: true
pullup: true
+9
View File
@@ -1,3 +1,12 @@
button:
- name: Haustür öffnen
unique_id: gdoor_haustuer_oeffnen
icon: mdi:door-open
command_topic: gdoor/bus_tx
payload_press: '0200319A59A60000A1E2A1A494'
qos: 0
retain: false
sensor:
# MQTT/RTL_433 (outdoor) sensors
# Reliable sensor with good placement (good values) and good reception
+5 -5
View File
@@ -4,7 +4,7 @@
- 'time'
- 'date'
# SNMP (Juniper) router traffic sensor
# SNMP (VyOS) cumulative traffic counters; one octet is one byte.
- platform: snmp
name: snmp_wan_in
unique_id: '3303381540758'
@@ -14,7 +14,7 @@
# baseoid: .1.3.6.1.2.1.2.2.1.10.12
baseoid: .1.3.6.1.2.1.31.1.1.1.6.12 # IF-MIB::ifHCInOctets.12
version: 2c
unit_of_measurement: "Octets"
unit_of_measurement: "B"
- platform: snmp
name: snmp_wan_out
unique_id: '1573258703922'
@@ -24,17 +24,17 @@
# baseoid: .1.3.6.1.2.1.2.2.1.16.12
baseoid: .1.3.6.1.2.1.31.1.1.1.10.12 # IF-MIB::ifHCOutOctets.12
version: 2c
unit_of_measurement: "Octets"
unit_of_measurement: "B"
- platform: derivative
source: sensor.snmp_wan_in
unit_time: s
unit: B
unit: B/s
name: wan_in_derivative
- platform: derivative
source: sensor.snmp_wan_out
unit_time: s
unit: B
unit: B/s
name: wan_out_derivative
- platform: statistics
+20 -13
View File
@@ -1,40 +1,47 @@
# Internet traffic
# Internet traffic: difference cumulative byte counters, not derivative rates (B/s).
# Allow counters to reset when the router/interface restarts.
internet_usage_in_monthly:
source: sensor.wan_in_derivative
source: sensor.snmp_wan_in
name: Monthly internet traffic in
unique_id: monthly_internet_traffic_in
cycle: monthly
delta_values: True
delta_values: false
periodically_resetting: true
internet_usage_out_monthly:
source: sensor.wan_out_derivative
source: sensor.snmp_wan_out
name: Monthly internet traffic out
unique_id: monthly_internet_traffic_out
cycle: monthly
delta_values: True
delta_values: false
periodically_resetting: true
internet_usage_in_daily:
source: sensor.wan_in_derivative
source: sensor.snmp_wan_in
name: Daily internet traffic in
unique_id: daily_internet_traffic_in
cycle: daily
delta_values: True
delta_values: false
periodically_resetting: true
internet_usage_out_daily:
source: sensor.wan_out_derivative
source: sensor.snmp_wan_out
name: Daily internet traffic out
unique_id: daily_internet_traffic_out
cycle: daily
delta_values: True
delta_values: false
periodically_resetting: true
internet_usage_in_hourly:
source: sensor.wan_in_derivative
source: sensor.snmp_wan_in
name: Hourly internet traffic in
unique_id: hourly_internet_traffic_in
cycle: hourly
delta_values: True
delta_values: false
periodically_resetting: true
internet_usage_out_hourly:
source: sensor.wan_out_derivative
source: sensor.snmp_wan_out
name: Hourly internet traffic out
unique_id: hourly_internet_traffic_out
cycle: hourly
delta_values: True
delta_values: false
periodically_resetting: true
# Energy
energy_import_daily: