Browse Source

Added a text_sensor, which propagates the current light mode. This is one of "off", "rgb", "white" and "night".

pull/9/head
Maurice Makaay 3 years ago
parent
commit
d1d9ecea95
16 changed files with 148 additions and 20 deletions
  1. +3
    -2
      binary_sensor/__init__.py
  2. +3
    -3
      binary_sensor/touch_binary_sensor.h
  3. +9
    -0
      doc/example.yaml
  4. +2
    -1
      light/__init__.py
  5. +28
    -10
      light/automation.h
  6. +1
    -0
      light/color_instant_handler.h
  7. +2
    -0
      light/color_night_light.h
  8. +2
    -0
      light/color_off.h
  9. +2
    -0
      light/color_rgb_light.h
  10. +2
    -0
      light/color_transition_handler.h
  11. +2
    -0
      light/color_white_light.h
  12. +9
    -0
      light/gpio_outputs.h
  13. +13
    -4
      light/light_output.h
  14. +0
    -0
      sensor/slider_sensor.h
  15. +30
    -0
      text_sensor/__init__.py
  16. +40
    -0
      text_sensor/light_mode_text_sensor.h

+ 3
- 2
binary_sensor/__init__.py View File

@ -22,11 +22,12 @@ def validate_part(value):
value = cv.string(value)
return cv.enum(PARTS, upper=True, space='_')(value)
YeelightBS2Button = bs2_ns.class_("YeelightBS2Button", binary_sensor.BinarySensor, cg.Component)
YeelightBS2TouchBinarySensor = bs2_ns.class_(
"YeelightBS2TouchBinarySensor", binary_sensor.BinarySensor, cg.Component)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(YeelightBS2Button),
cv.GenerateID(): cv.declare_id(YeelightBS2TouchBinarySensor),
cv.GenerateID(CONF_FRONT_PANEL_HAL_ID): cv.use_id(FrontPanelHAL),
cv.Optional(CONF_PART, default="ANY"): validate_part,
}


binary_sensor/binary_sensor.h → binary_sensor/touch_binary_sensor.h View File

@ -9,10 +9,10 @@ namespace yeelight {
namespace bs2 {
/**
* This class implements a binary sensor for the buttons on the
* Yeelight Bedside Lamp 2.
* This class implements a binary sensor for the touch buttons
* on the Yeelight Bedside Lamp 2.
*/
class YeelightBS2Button : public binary_sensor::BinarySensor, public Component {
class YeelightBS2TouchBinarySensor : public binary_sensor::BinarySensor, public Component {
public:
void set_parent(FrontPanelHAL *front_panel) {
front_panel_ = front_panel;

+ 9
- 0
doc/example.yaml View File

@ -14,6 +14,7 @@ substitutions:
id_power_button: ${name}_power_button
id_color_button: ${name}_color_button
id_slider_level: ${name}_slider_level
id_light_mode: ${name}_light_mode
# --------------------------------------------------------------------------
# Use your own preferences for these components.
@ -100,6 +101,14 @@ light:
call.set_brightness(random_float());
call.perform();
# This text sensor propagates the currently active light mode.
# The possible light modes are: "off", "rgb", "white" and "night".
text_sensor:
- platform: yeelight_bs2
name: ${name} Light Mode
id: ${id_light_mode}
# This output controls the front panel illumination + level indication.
# Value 0.0 turns off the illumination.
# Other values (up to 1.0) turn on the illumination and set the level


+ 2
- 1
light/__init__.py View File

@ -4,7 +4,7 @@ from esphome.components import light
from esphome import automation
from esphome.const import (
CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE,
CONF_OUTPUT_ID, CONF_TRIGGER_ID
CONF_OUTPUT_ID, CONF_TRIGGER_ID, CONF_ID
)
from .. import bs2_ns, CODEOWNERS, CONF_LIGHT_HAL_ID, LightHAL
@ -17,6 +17,7 @@ CONF_ON_BRIGHTNESS = "on_brightness"
YeelightBS2LightState = bs2_ns.class_("YeelightBS2LightState", light.LightState)
YeelightBS2LightOutput = bs2_ns.class_("YeelightBS2LightOutput", light.LightOutput)
BrightnessTrigger = bs2_ns.class_("BrightnessTrigger", automation.Trigger.template())
LightModeTrigger = bs2_ns.class_("LightModeTrigger", automation.Trigger.template())
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend(
{


+ 28
- 10
light/automation.h View File

@ -12,22 +12,40 @@ namespace bs2 {
class BrightnessTrigger : public Trigger<float> {
public:
explicit BrightnessTrigger(YeelightBS2LightOutput *parent) {
parent->add_on_state_callback([this](light::LightColorValues values) {
auto new_brightness = values.get_brightness();
if (values.get_state() == 0) {
new_brightness = 0.0f;
parent->add_on_state_callback(
[this](light::LightColorValues values, std::string light_mode) {
auto new_brightness = values.get_brightness();
if (values.get_state() == 0) {
new_brightness = 0.0f;
}
new_brightness = roundf(new_brightness * 100.0f) / 100.0f;
if (last_brightness_ != new_brightness) {
trigger(new_brightness);
last_brightness_ = new_brightness;
}
}
new_brightness = roundf(new_brightness * 100.0f) / 100.0f;
if (last_brightness_ != new_brightness) {
trigger(new_brightness);
last_brightness_ = new_brightness;
}
});
);
}
protected:
float last_brightness_ = -1.0f;
};
class LightModeTrigger : public Trigger<std::string> {
public:
explicit LightModeTrigger(YeelightBS2LightOutput *parent) {
parent->add_on_state_callback(
[this](light::LightColorValues values, std::string light_mode) {
if (last_light_mode_ != light_mode) {
trigger(light_mode);
last_light_mode_ = light_mode;
}
}
);
}
protected:
std::string last_light_mode_ = LIGHT_MODE_UNKNOWN;
};
} // namespace yeelight_bs2
} // namespace yeelight
} // namespace bs2

+ 1
- 0
light/color_instant_handler.h View File

@ -50,6 +50,7 @@ public:
return true;
}
protected:
GPIOOutputs *off_light_ = new ColorOff();
GPIOOutputs *rgb_light_ = new ColorRGBLight();
GPIOOutputs *white_light_ = new ColorWhiteLight();


+ 2
- 0
light/color_night_light.h View File

@ -20,6 +20,8 @@ namespace bs2 {
class ColorNightLight : public GPIOOutputs {
public:
bool set_light_color_values(light::LightColorValues v) {
light_mode = LIGHT_MODE_NIGHT;
// Note: I do not check for a brightness at or below 0.01 (1%) here,
// because the lowest brightness setting from Home Assistant turns
// up as 0.011765 in here (which is 3/255 and not 1/100).


+ 2
- 0
light/color_off.h View File

@ -16,6 +16,8 @@ namespace bs2 {
class ColorOff : public GPIOOutputs {
public:
bool set_light_color_values(light::LightColorValues v) {
light_mode = LIGHT_MODE_OFF;
if (v.get_state() != 0.0f && v.get_brightness() != 0.0f) {
return false;
}


+ 2
- 0
light/color_rgb_light.h View File

@ -247,6 +247,8 @@ static const RGBCircle rgb_circle_ {{
class ColorRGBLight : public GPIOOutputs {
public:
bool set_light_color_values(light::LightColorValues v) {
light_mode = LIGHT_MODE_RGB;
if (v.get_white() > 0.0f) {
return false;
}


+ 2
- 0
light/color_transition_handler.h View File

@ -83,6 +83,8 @@ public:
end_->set_light_color_values(end_light_values_);
}
light_mode = end_->light_mode;
// Determine required GPIO outputs for current transition progress.
progress_ = transformer_->get_progress();
auto smoothed = light::LightTransitionTransformer::smoothed_progress(progress_);


+ 2
- 0
light/color_white_light.h View File

@ -75,6 +75,8 @@ static const RGBWLevelsTable rgbw_levels_100_ {{
class ColorWhiteLight : public GPIOOutputs {
public:
bool set_light_color_values(light::LightColorValues v) {
light_mode = LIGHT_MODE_WHITE;
if (v.get_white() == 0.0f) {
return false;
}


+ 9
- 0
light/gpio_outputs.h View File

@ -4,6 +4,13 @@ namespace esphome {
namespace yeelight {
namespace bs2 {
// Light modes that can be reported by implementations of GPIOOutputs.
static const std::string LIGHT_MODE_UNKNOWN { "unknown" };
static const std::string LIGHT_MODE_OFF { "off" };
static const std::string LIGHT_MODE_RGB { "rgb" };
static const std::string LIGHT_MODE_WHITE { "white" };
static const std::string LIGHT_MODE_NIGHT { "night" };
/**
* This abstract class is used for implementing classes that translate
* LightColorValues into the required GPIO PWM duty cycle levels to represent
@ -15,6 +22,7 @@ public:
float green = 0.0f;
float blue = 0.0f;
float white = 0.0f;
std::string light_mode = LIGHT_MODE_OFF;
/**
* Sets the red, green, blue, white fields to the PWM duty cycles
@ -33,6 +41,7 @@ public:
other->green = green;
other->blue = blue;
other->white = white;
other->light_mode = light_mode;
}
void log(const char *prefix) {


+ 13
- 4
light/light_output.h View File

@ -39,7 +39,7 @@ public:
return traits;
}
void add_on_state_callback(std::function<void(light::LightColorValues)> &&callback) {
void add_on_state_callback(std::function<void(light::LightColorValues, std::string)> &&callback) {
state_callback_.add(std::move(callback));
}
@ -57,13 +57,17 @@ public:
GPIOOutputs *delegate;
if (transition_handler_->set_light_color_values(values)) {
delegate = transition_handler_;
state_callback_.call(transition_handler_->get_end_values());
state_callback_.call(
transition_handler_->get_end_values(),
delegate->light_mode);
} else {
instant_handler_->set_light_color_values(values);
delegate = instant_handler_;
state_callback_.call(values);
state_callback_.call(values, delegate->light_mode);
}
light_mode_ = delegate->light_mode;
// Note: one might think that it is more logical to turn on the LED
// circuitry master switch after setting the individual channels,
// but this is the order that was used by the original firmware. I
@ -84,11 +88,16 @@ public:
light_->turn_off();
}
std::string get_light_mode() {
return light_mode_;
}
protected:
LightHAL *light_;
ColorTransitionHandler *transition_handler_;
ColorInstantHandler *instant_handler_ = new ColorInstantHandler();
CallbackManager<void(light::LightColorValues)> state_callback_{};
CallbackManager<void(light::LightColorValues, std::string)> state_callback_{};
std::string light_mode_;
friend class YeelightBS2LightState;


sensor/sensor.h → sensor/slider_sensor.h View File


+ 30
- 0
text_sensor/__init__.py View File

@ -0,0 +1,30 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_OUTPUT_ID
from .. import bs2_ns, CODEOWNERS
from ..light import YeelightBS2LightOutput
DEPENDENCIES = ["yeelight_bs2"]
CONF_LIGHT_ID = "light_id"
YeelightBS2LightModeTextSensor = bs2_ns.class_(
"YeelightBS2LightModeTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(YeelightBS2LightModeTextSensor),
cv.GenerateID(CONF_OUTPUT_ID): cv.use_id(YeelightBS2LightOutput),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield text_sensor.register_text_sensor(var, config)
parent_var = yield cg.get_variable(config[CONF_OUTPUT_ID])
cg.add(var.set_parent(parent_var))

+ 40
- 0
text_sensor/light_mode_text_sensor.h View File

@ -0,0 +1,40 @@
#pragma once
#include <cmath>
#include "../common.h"
#include "../front_panel_hal.h"
#include "esphome/components/text_sensor/text_sensor.h"
namespace esphome {
namespace yeelight {
namespace bs2 {
/**
* A text sensor, used for propagating the active light mode on the
* Yeelight Bedside Lamp 2.
*
* The possible light modes are "off", "rgb", "white" and "night".
*/
class YeelightBS2LightModeTextSensor : public text_sensor::TextSensor, public Component {
public:
void set_parent(YeelightBS2LightOutput *light) { light_ = light; }
void setup() {
light_->add_on_state_callback(
[this](light::LightColorValues values, std::string light_mode) {
if (last_light_mode_ != light_mode) {
publish_state(light_mode);
last_light_mode_ = light_mode;
}
}
);
}
protected:
YeelightBS2LightOutput *light_;
std::string last_light_mode_ = LIGHT_MODE_UNKNOWN;
};
} // namespace bs2
} // namespace yeelight
} // namespace esphome

Loading…
Cancel
Save