From fbc65f6c914cd13b5913b8f911427f415e144e14 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Tue, 6 Jul 2021 16:52:02 +0200 Subject: [PATCH] Added Action output.set_leds for controlling the front panel LEDs individually. --- components/xiaomi_bslamp2/__init__.py | 3 +- components/xiaomi_bslamp2/front_panel_hal.h | 44 +++++++++++++------ components/xiaomi_bslamp2/output/__init__.py | 35 +++++++++++++-- components/xiaomi_bslamp2/output/automation.h | 29 ++++++++++++ components/xiaomi_bslamp2/output/output.h | 14 ++++-- 5 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 components/xiaomi_bslamp2/output/automation.h diff --git a/components/xiaomi_bslamp2/__init__.py b/components/xiaomi_bslamp2/__init__.py index a804686..ebc0dd5 100644 --- a/components/xiaomi_bslamp2/__init__.py +++ b/components/xiaomi_bslamp2/__init__.py @@ -13,7 +13,6 @@ from esphome.const import ( CODEOWNERS = ["@mmakaay"] -CONF_HUB_ID = "xiaomi_bslamp2_hub_id" CONF_RED_ID = "red_id" CONF_GREEN_ID = "green_id" CONF_BLUE_ID = "blue_id" @@ -25,8 +24,8 @@ CONF_MASTER2_ID = "master2_id" CONF_FP_I2C_ID = "front_panel_i2c_id" CONF_LIGHT_HAL_ID = "light_hal_id" CONF_FRONT_PANEL_HAL_ID = "front_panel_hal_id" - CONF_ON_BRIGHTNESS = "on_brightness" +CONF_LEDS = "leds" AUTO_LOAD = ["ledc", "output", "i2c"] diff --git a/components/xiaomi_bslamp2/front_panel_hal.h b/components/xiaomi_bslamp2/front_panel_hal.h index 5cebe80..f59ceb3 100644 --- a/components/xiaomi_bslamp2/front_panel_hal.h +++ b/components/xiaomi_bslamp2/front_panel_hal.h @@ -48,7 +48,6 @@ static const LED LED_LEVEL_10 = LED_POWER|LED_COLOR|LED_1|LED_2|LED_3|LED_4|L // Commands for the I2C interface. static const MSG READY_FOR_EV = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; -static const MSG SET_LEDS = {0x02, 0x03, 0x00, 0x00, 0x64, 0x00, 0x00}; using EVENT = uint16_t; @@ -233,23 +232,30 @@ class FrontPanelHAL : public Component, public i2c::I2CDevice { } } } - + /** - * Enables the LEDs according to the provided input. + * Turn on one or more LEDs (leaving the state of the other LEDs intact). * The input value is a bitwise OR-ed set of LED constants. - * E.g. LED_POWER|LED_1|LED2 + */ + void turn_on_leds(uint16_t leds) { + set_leds_(led_state_ | leds); + } + + /** + * Turn off one or more LEDs (leaving the state of the other LEDs intact). + * The input value is a bitwise OR-ed set of LED constants. + */ + void turn_off_leds(uint16_t leds) { + set_leds_(led_state_ & ~leds); + } + + /** + * Updates the state of the LEDs according to the provided input. + * The input value is a bitwise OR-ed set of LED constants, representing the + * LEDs that must be turned on. All other LEDs are turned off. */ void set_leds(uint16_t leds) { - MSG msg; - msg[0] = SET_LEDS[0]; - msg[1] = SET_LEDS[1]; - msg[2] = leds >> 8; - msg[3] = leds & 0xff; - msg[4] = SET_LEDS[4]; - msg[5] = SET_LEDS[5]; - msg[6] = SET_LEDS[6]; - - write_bytes_raw(msg, MSG_LEN); + set_leds_(leds); } /** @@ -294,6 +300,16 @@ class FrontPanelHAL : public Component, public i2c::I2CDevice { volatile int event_id_ = 0; int last_event_id_ = 0; CallbackManager event_callback_{}; + + MSG led_msg_ = {0x02, 0x03, 0x00, 0x00, 0x64, 0x00, 0x00}; + uint16_t led_state_ = 0; + + void set_leds_(uint16_t leds) { + led_state_ = 0b0000110000000000 | leds; + led_msg_[2] = led_state_ >> 8; + led_msg_[3] = led_state_ & 0xff; + write_bytes_raw(led_msg_, MSG_LEN); + } }; /** diff --git a/components/xiaomi_bslamp2/output/__init__.py b/components/xiaomi_bslamp2/output/__init__.py index 45dd168..ee69fc8 100644 --- a/components/xiaomi_bslamp2/output/__init__.py +++ b/components/xiaomi_bslamp2/output/__init__.py @@ -2,19 +2,21 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import output from esphome.const import CONF_ID +from esphome import automation from .. import ( bslamp2_ns, CODEOWNERS, - CONF_FRONT_PANEL_HAL_ID, FrontPanelHAL + CONF_FRONT_PANEL_HAL_ID, FrontPanelHAL, CONF_LEDS ) AUTO_LOAD = ["xiaomi_bslamp2"] -XiaomiBslamp2FrontPanelLight = bslamp2_ns.class_( - "XiaomiBslamp2FrontPanelLight", output.FloatOutput, cg.Component) +XiaomiBslamp2FrontPanelOutput = bslamp2_ns.class_( + "XiaomiBslamp2FrontPanelOutput", output.FloatOutput, cg.Component) +SetLEDsAction = bslamp2_ns.class_("SetLEDsAction", automation.Action) CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( { - cv.GenerateID(): cv.declare_id(XiaomiBslamp2FrontPanelLight), + cv.GenerateID(): cv.declare_id(XiaomiBslamp2FrontPanelOutput), cv.GenerateID(CONF_FRONT_PANEL_HAL_ID): cv.use_id(FrontPanelHAL), } ).extend(cv.COMPONENT_SCHEMA) @@ -26,3 +28,28 @@ def to_code(config): front_panel_hal_var = yield cg.get_variable(config[CONF_FRONT_PANEL_HAL_ID]) cg.add(var.set_parent(front_panel_hal_var)) + + +def maybe_simple_leds_value(schema): + def validator(value): + if isinstance(value, dict): + return schema(value) + return schema({ "leds": value }) + return validator + +@automation.register_action( + "output.set_leds", + SetLEDsAction, + cv.Schema( + maybe_simple_leds_value(cv.Schema({ + cv.GenerateID(CONF_ID): cv.use_id(XiaomiBslamp2FrontPanelOutput), + cv.Required(CONF_LEDS): cv.templatable(cv.uint16_t), + })) + ) +) +async def set_leds_to_code(config, action_id, template_arg, args): + output_var = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, output_var) + template_ = await cg.templatable(config[CONF_LEDS], args, cg.uint16) + cg.add(var.set_leds(template_)) + return var diff --git a/components/xiaomi_bslamp2/output/automation.h b/components/xiaomi_bslamp2/output/automation.h new file mode 100644 index 0000000..c68ad6e --- /dev/null +++ b/components/xiaomi_bslamp2/output/automation.h @@ -0,0 +1,29 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "output.h" +#include + +namespace esphome { +namespace xiaomi { +namespace bslamp2 { + +template class SetLEDsAction : public Action { + public: + explicit SetLEDsAction(XiaomiBslamp2FrontPanelOutput *parent) : parent_(parent) {} + + TEMPLATABLE_VALUE(uint16_t, leds) + + void play(Ts... x) override { + uint16_t value = this->leds_.value(x...); + parent_->set_leds(value); + } + + protected: + XiaomiBslamp2FrontPanelOutput *parent_; +}; + +} // namespace bslamp2 +} // namespace xiaomi +} // namespace esphome diff --git a/components/xiaomi_bslamp2/output/output.h b/components/xiaomi_bslamp2/output/output.h index bfb92a8..b58d926 100644 --- a/components/xiaomi_bslamp2/output/output.h +++ b/components/xiaomi_bslamp2/output/output.h @@ -13,11 +13,19 @@ namespace bslamp2 { * An output, used for controlling the front panel illumination and * level indicator on the Xiaomi Mijia Bedside Lamp 2 front panel. */ -class XiaomiBslamp2FrontPanelLight : public output::FloatOutput, public Component { +class XiaomiBslamp2FrontPanelOutput : public output::FloatOutput, public Component { public: - void set_parent(FrontPanelHAL *front_panel) { front_panel_ = front_panel; } + void set_parent(FrontPanelHAL *front_panel) { + front_panel_ = front_panel; + } - void write_state(float level) { front_panel_->set_light_level(level); } + void write_state(float level) { + front_panel_->set_light_level(level); + } + + void set_leds(uint16_t leds) { + front_panel_->set_leds(leds); + } protected: FrontPanelHAL *front_panel_;