Hi all, I’ve got problems!
let me try to explain…
First af all a disclosure, the hardware and the software of the original product are certified homekit and it works with a custom mesh ble network to obtain what I want to replicate in esphome/home assistant.
but giving that I phisically make everything i can get the devices before the certification and let them open with a more friendly firmware for homeassistant.
The why i want I it to work with esphome and home assistant, is because I think that is a little thank you from myself to the opensource comunity, I would like to give something back by giving enthusiast a way to program my hardware with a firmware that suits better their needs.
with all this out of the way let me try to explain what I’m trying to replicate.
the hardware has abunch of ws2812 for halo and buttons animations, it comes with 3 custom made touch sensor a passive buzzer ( I think that could be changed with aptic but I haven’t had time to mess with it yet) and its all driven by an esp32 that controls also 3 triac for programmable dimmng.
I’ve made everithing to work, and I’m having a bit of trouble to get the dimming function right as the original product…
I’ve found this touch dimmer logic that works great, for multiple dimmer on the same device, I’ve made some test ( I’ll provide the modifed file at the bottom) because what i wanted to obtain was something in the ball park of
- touch event is propagated to home assistant ( got it working easy)
- touch logic ( touch_dimmer.h/ numeric_value_controller.h) resides only in HA ( kinda…almost there… i dunno)
- numeric value in HA syncs with all the dimmer that are “subscribed” on all the other devices
4)the value is used ( I would like almost realtime ) to change the value of the dimmer ( kinda works but really buggy and in a loop for latency)
this to obtain that I can map whatever touch of the device to whatever dim i want to control and also change the behaviour of the dimmer to act like a dumb switch for simple on-off. in this way I can also free the touch buttons to be alwais connected to just a light, i can use they touch event to trigger a scene or automation ,if I want to, without loosing a single dimmer.
the problem I’m having is that in theory it works but in the reality latency is getting in the way and the dimmer function start jumping all around .
I forgot, i’ve made the treeshold of the touch button to auto calibrate at statup and also available to automate in HA to mak ethe touch more responsive but able to read long press without changin the treeshold like other examples I’ve seen online… this is simple and it works amazingly with my hardware… kinda impressed by myself ( little tap on the shoulder ).
this is the code for Numeric_value_controller, if you create a number helper in HA ( in my case is the one named ha_dimmer_number) you can test part of the yaml with a simpre asp32 with touch configured ( a simple dev board works great)
#include "esphome/components/binary_sensor/automation.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/esp32_touch/esp32_touch.h"
#include "esphome/components/light/automation.h"
#include "esphome/components/script/script.h"
#include "esphome/core/application.h"
#include "esphome/core/automation.h"
#include "esphome/core/base_automation.h"
#include "esphome/core/log.h"
#include <string>
using namespace esphome;
using ScriptExecuteAction = script::ScriptExecuteAction<script::Script<>>;
using ScriptStopAction = script::ScriptStopAction<script::SingleScript<>>;
class NumericValueController {
static constexpr uint64_t dimStartDelayMs = 350;
static constexpr uint64_t dimPeriodMs = 10;
static constexpr auto LOGTAG = "NumericValueController";
std::string name;
number::Number *haNumber;
binary_sensor::BinarySensor *touchSensor;
script::SingleScript<> *scriptDimStart;
script::SingleScript<> *scriptDimStop;
float dimming_dir = 0;
float saved_value = 0.0f;
public:
NumericValueController(const std::string &name,
number::Number *haNumber,
binary_sensor::BinarySensor *touchSensor)
: name(name), haNumber(haNumber), touchSensor(touchSensor)
{
createDimStartScript();
createDimStopScript();
setupBinarySensor();
}
private:
void createDimStartScript()
{
auto *startDelayAction = new DelayAction<>();
startDelayAction->set_delay(dimStartDelayMs + 50);
App.register_component(startDelayAction);
LambdaAction<> *toggleDimDirLambda = new LambdaAction<>([&]() -> void {
const float cur_value = this->haNumber->state;
if (this->dimming_dir == 0 || cur_value <= 0.0f) {
this->dimming_dir = 1;
// Start at 0%.
this->haNumber->publish_state(0.0f);
} else if (cur_value >= 100.0f) {
this->dimming_dir = -1;
} else {
this->dimming_dir *= -1;
}
});
auto *dimDelayAction = new DelayAction<>();
dimDelayAction->set_delay(dimPeriodMs);
App.register_component(dimDelayAction);
LambdaAction<> *dimStepLambda = new LambdaAction<>([&]() -> void {
const float step = this->dimming_dir * 1.0f; // Dim step value
const float cur_value = this->haNumber->state;
const float new_value = clamp(cur_value + step, 0.0f, 100.0f);
if (new_value != cur_value) {
this->haNumber->publish_state(new_value);
if (new_value!= 0) {
this->saved_value = new_value;
}
}
if (new_value <= 0.0f || new_value >= 99.99f) {
this->scriptDimStop->execute();
}
});
auto *whileCondition = new binary_sensor::BinarySensorCondition<>(touchSensor, true);
auto *whileAction = new WhileAction<>(whileCondition);
whileAction->add_then({dimDelayAction, dimStepLambda});
scriptDimStart = new script::SingleScript<>();
scriptDimStart->set_name(name + "__script_dimming_start");
auto *dimStartAutom = new Automation<>(scriptDimStart);
dimStartAutom->add_actions({startDelayAction, toggleDimDirLambda, whileAction});
}
void createDimStopScript() {
auto *scriptStopAction = new ScriptStopAction(scriptDimStart);
scriptDimStop = new script::SingleScript<>();
scriptDimStop->set_name(name + "__script_dimming_stop");
auto *dimStopAutom = new Automation<>(scriptDimStop);
dimStopAutom->add_actions({scriptStopAction});
LambdaAction<> *saveCurrentValue = new LambdaAction<>([this]() {
if (this->haNumber->state!= 0) {
this->saved_value = this->haNumber->state;
}
});
dimStopAutom->add_actions({saveCurrentValue});
}
void toggle_value() {
if (this->haNumber->state == 0.0f) {
if (saved_value >0) {
float new_value = saved_value;
this->haNumber->publish_state(new_value);
}
} else {
saved_value = this->haNumber->state;
this->haNumber->publish_state(0.0f);
}
}
void setupBinarySensor() {
auto *pressTrigger = new binary_sensor::PressTrigger(touchSensor);
auto *pressScriptAction = new ScriptExecuteAction(scriptDimStart);
auto *pressAutomation = new Automation<>(pressTrigger);
pressAutomation->add_actions({pressScriptAction});
auto *releaseTrigger = new binary_sensor::ReleaseTrigger(touchSensor);
auto *releaseScriptAction = new ScriptExecuteAction(scriptDimStop);
auto *releaseAutomation = new Automation<>(releaseTrigger);
releaseAutomation->add_actions({releaseScriptAction});
auto *clickTrigger = new binary_sensor::ClickTrigger(touchSensor, 50, dimStartDelayMs);
auto *clickAutomation = new Automation<>(clickTrigger);
clickAutomation->add_actions({new LambdaAction<>([this]() { this->toggle_value(); })});
LambdaAction<> *setDimDirectionLambda = new LambdaAction<>([this]() -> void {
this->dimming_dir = this->haNumber->state > 50.0f ? -1 : 1;
});
clickAutomation->add_actions({setDimDirectionLambda});
}
};
this is the configuration yaml for the actual device ( sorry for the messy code…) the only part needed to test it are the ones relative to the touch sensor, the number with homeassistant template and the template to calibrate the touch… all the other things are for the complete device , maybe you can add a dimmer if you have one lying around and try the function live… but as for where I’m standing now I get stuck at the value looping for the latence…
esphome:
name: di-wioo
friendly_name: DI-WiOO
includes:
#- touch-dimming.h
- NumericValueController.h
on_boot:
priority: 500
then:
- delay: 3sec
- script.execute: calibrate_threshold
#- lambda: new NumericValueController("valore impostato da tasto 1", brt1, touch1);
- lambda: new NumericValueController("valore impostato da tasto 2", ha_dimmer_number, touch2);
#- lambda: new NumericValueController("valore impostato da tasto 3", brt3, touch3);
# - rtttl.play : "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6"
esp32:
board: esp32dev
framework:
type: arduino
logger:
api:
encryption:
key: "xxxxxxxx"
services:
- service: play_rtttl
variables:
song_str: string
then:
- rtttl.play:
rtttl: !lambda 'return song_str;'
- output.turn_off: buzzer_output
ota:
- platform: esphome
password: "xxxxxxxx"
wifi:
ssid: xxxxx
password: xxxxxx
ap:
ssid: "Hotspot configurazione DYWi-OO"
password: "12345678"
captive_portal:
light:
- platform: fastled_clockless
id: led
chipset: WS2811
pin: GPIO22
num_leds: 32
rgb_order: GRB
name: "LED"
effects:
!include effetti.yaml
- platform: partition
name: "Tasto 1"
segments:
- id: led
from: 0
to: 1
effects:
!include effetti.yaml
- platform: partition
name: "Tasto 2"
segments:
- id: led
from: 2
to: 3
effects:
!include effetti.yaml
- platform: partition
name: "Tasto 3"
segments:
- id: led
from: 4
to: 5
effects:
!include effetti.yaml
- platform: partition
name: "Halo"
segments:
- id: led
from: 6
to: 31
effects:
!include effetti.yaml
- platform: monochromatic
output: dimmer1
id: light1
name: Dimmer 1
- platform: monochromatic
output: dimmer2
id: light2
name: Dimmer 2
- platform: monochromatic
output: dimmer3
id: light3
name: Dimmer 3
binary_sensor:
- platform: esp32_touch
name: "Touch1"
id: touch1
pin: GPIO33
threshold: 100
filters:
- delayed_on_off: 80ms
- platform: esp32_touch
name: "Touch2"
id: touch2
pin: GPIO27
threshold: 100
filters:
- delayed_on_off: 80ms
- platform: esp32_touch
name: "Touch3"
id: touch3
pin: GPIO32
threshold: 100
filters:
- delayed_on_off: 80ms
number:
- platform: homeassistant
id: ha_dimmer_number
entity_id: input_number.lampadina_centrale
on_value:
then:
- homeassistant.service:
service: input_number.set_value
data:
entity_id: input_number.lampadina_centrale
value: !lambda 'return x;'
web_server:
port: 80
output:
- platform: ac_dimmer
id: dimmer1
gate_pin:
number: GPIO18
zero_cross_pin:
number: GPIO23
allow_other_uses : true
mode:
input: true
# Dimmer2 configuration
- platform : ac_dimmer
id: dimmer2
gate_pin:
number: GPIO19
zero_cross_pin :
number: GPIO23
allow_other_uses : true
mode:
input: true
# Dimmer3 configuration
- platform: ac_dimmer
id: dimmer3
gate_pin:
number: GPIO21
zero_cross_pin:
number: GPIO23
allow_other_uses : true
mode:
input: true
# Buzzer output configuration
- platform: ledc
pin: GPIO25
id: buzzer_output
inverted: False
zero_means_zero: True
rtttl:
output: buzzer_output
on_finished_playback:
- switch.turn_off: buzzer
switch:
- platform: output
name: 'Buzzer'
id: buzzer
output: buzzer_output
on_turn_on:
- rtttl.play: 'Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f'
on_turn_off:
- rtttl.stop
- platform: template
name: "Calibrate Touch Threshold"
turn_on_action:
- script.execute: calibrate_threshold
esp32_touch:
setup_mode: False
script:
- id: calibrate_threshold
mode: single
then:
- lambda: |-
float sum1 = 0.0;
float sum2 = 0.0;
float sum3 = 0.0;
for (int i = 0; i < 30; i++) {
sum1 += id(touch1).get_value();
sum2 += id(touch2).get_value();
sum3 += id(touch3).get_value();
}
float average1 = sum1 / 30.0;
float average2 = sum2/ 30.0;
float average3 = sum3 / 30.0;
id(touch1).set_threshold(average1 - 50);
id(touch2).set_threshold(average2 - 50);
id(touch3).set_threshold(average3 - 50);
thank you for any help you can give me! and don’t spare comment like " do you know that this code could have been written better by an epylectic monkey having a seizure?"