Browse Source

Multi touch events (#17)

Documentation and binary sensor code quality improvements.
pull/20/head
Maurice Makaay 3 years ago
committed by GitHub
parent
commit
b690c290cf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 213 additions and 168 deletions
  1. +37
    -19
      binary_sensor/__init__.py
  2. +11
    -16
      binary_sensor/touch_binary_sensor.h
  3. +141
    -109
      doc/configuration.md
  4. +2
    -2
      doc/example.yaml
  5. +22
    -22
      front_panel_hal.h

+ 37
- 19
binary_sensor/__init__.py View File

@ -1,7 +1,8 @@
import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_ID
from esphome.const import CONF_ID, CONF_FOR
from .. import (
bslamp2_ns, CODEOWNERS,
CONF_FRONT_PANEL_HAL_ID, FrontPanelHAL
@ -11,29 +12,46 @@ AUTO_LOAD = ["xiaomi_bslamp2"]
CONF_PART = "part"
# The identifier values match the bit values of the events as defined
# in ../front_panel_hal.h.
PARTS = {
"ANY" : 0,
"POWER_BUTTON" : 1,
"POWER" : 1,
"COLOR_BUTTON" : 2,
"COLOR" : 2,
"SLIDER" : 3,
"POWER_BUTTON" : 0b001 << 1,
"POWER" : 0b001 << 1,
"COLOR_BUTTON" : 0b010 << 1,
"COLOR" : 0b010 << 1,
"SLIDER" : 0b100 << 1,
}
def validate_part(value):
value = cv.string(value)
return cv.enum(PARTS, upper=True, space='_')(value)
XiaomiBslamp2TouchBinarySensor = bslamp2_ns.class_(
"XiaomiBslamp2TouchBinarySensor", binary_sensor.BinarySensor, cg.Component)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(XiaomiBslamp2TouchBinarySensor),
cv.GenerateID(CONF_FRONT_PANEL_HAL_ID): cv.use_id(FrontPanelHAL),
cv.Optional(CONF_PART, default="ANY"): validate_part,
}
).extend(cv.COMPONENT_SCHEMA)
def validate_for(value):
value = cv.string(value)
return cv.enum(PARTS, upper=True, space='_')(value)
def validate_binary_sensor(conf):
if CONF_PART in conf and CONF_FOR in conf:
raise cv.Invalid("Specify only one of [part] or [for]")
if CONF_PART in conf and not CONF_FOR in conf:
conf[CONF_FOR] = conf[CONF_PART]
if CONF_FOR not in conf:
raise cv.Invalid("'for' is a required option for [binary_sensor.xiaomi_bslamp2]")
return conf
CONFIG_SCHEMA = cv.All(
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(XiaomiBslamp2TouchBinarySensor),
cv.GenerateID(CONF_FRONT_PANEL_HAL_ID): cv.use_id(FrontPanelHAL),
# This option is not advertised in the documentation. It must be
# considered deprecated. I'm not announcing it as such yet. Not sure
# if it's useful to do so.
cv.Optional(CONF_PART): validate_for,
cv.Optional(CONF_FOR): validate_for,
}
).extend(cv.COMPONENT_SCHEMA),
validate_binary_sensor,
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
@ -42,4 +60,4 @@ 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))
cg.add(var.set_part(config[CONF_PART]))
cg.add(var.set_for(config[CONF_FOR]))

+ 11
- 16
binary_sensor/touch_binary_sensor.h View File

@ -18,38 +18,33 @@ public:
front_panel_ = front_panel;
}
void set_part(int part) {
part_ = part;
void set_for(int part) {
for_ = part;
}
void setup() {
front_panel_->add_on_event_callback(
[this](EVENT ev) {
// Filter events by part, when requested.
if (part_ > 0) {
if ((ev & FLAG_PART_MASK) != (part_ << FLAG_PART_SHIFT)) {
return;
}
auto part_in_event = ev & FLAG_PART_MASK;
if (for_ == 0 || part_in_event == for_) {
auto new_state = (ev & FLAG_TYPE_MASK) == FLAG_TYPE_TOUCH;
this->publish_state(new_state);
}
// Publish the new state, based on the touch/release status..
auto on_or_off = (ev & FLAG_TYPE_MASK) == FLAG_TYPE_TOUCH;
this->publish_state(on_or_off);
}
);
}
void dump_config() {
ESP_LOGCONFIG(TAG, "Front panel binary_sensor:");
ESP_LOGCONFIG(TAG, " Part: %s (id %d)",
(part_ == 1 ? "power button" :
part_ == 2 ? "color button" :
part_ == 3 ? "slider" : "any"),
part_);
ESP_LOGCONFIG(TAG, " For: %s",
for_ & FLAG_PART_POWER ? "power button" :
for_ & FLAG_PART_COLOR ? "color button" :
for_ & FLAG_PART_SLIDER ? "slider" : "ERR");
}
protected:
FrontPanelHAL *front_panel_;
EVENT part_;
EVENT for_ = 0;
};
} // namespace bslamp2


+ 141
- 109
doc/configuration.md View File

@ -2,14 +2,15 @@
# Configuration guide
I think, the best starting point for creating your own yaml configuration, is to
look at the [example.yaml](example.yaml) file from the project documentation.
This configuration was written with the functionality of the original firmware in mind
and it makes use of all available options. This configuration guide can be used to
fill in the blanks.
I think, the best starting point for creating your own yaml configuration,
is to look at the [example.yaml](example.yaml) file from the project
documentation. This configuration was written with the functionality of the
original firmware in mind and it makes use of all available options. This
configuration guide can be used to fill in the blanks.
The `xiaomi_bslamp2` platform provides various components that expose the core functionalities of the lamp.
In the following table, you can find what components are used for exposing what parts of the lamp.
The `xiaomi_bslamp2` platform provides various components that expose the
core functionalities of the lamp. In the following table, you can find what
components are used for exposing what physical components of the lamp.
| Part | Component(s) |
| -------------------------- |------------------------------------------------------------------|
@ -24,16 +25,17 @@ In the following table, you can find what components are used for exposing what
## Platform: xiaomi_bslamp2
At the core of the hardware support is the `xiaomi_bslamp2` platform, which provides two
hub-style hardware abstraction layer (HAL) components that are used by the other components:
one for driving the GPIO's for the RGBWW leds and one for the I2C communication between
the ESP32 and the front panel.
At the core of the hardware support is the `xiaomi_bslamp2` platform, which
provides two hub-style hardware abstraction layer (HAL) components that are
used by the other components: one for driving the GPIO's for the RGBWW leds
and one for the I2C communication between the ESP32 and the front panel.
I do mention it here for completeness sake, but generally you will not have to add the
following configuration option to your yaml file. It is loaded automatically by the
components that need it, and the GPIO + I2C configurations are fully prepared to work
for the Bedside Lamp 2 wiring out of the box.
Therefore, you will not find this piece of configuration in the [example.yaml](example.yaml).
I do mention the platform configuration here for completeness sake, but
generally you will not have to add the following configuration option to
your yaml file. It is loaded automatically by the components that need it,
and the GPIO + I2C configurations are fully prepared to work for the Bedside
Lamp 2 wiring out of the box. Therefore, you will not find this piece of
configuration in the [example.yaml](example.yaml).
Having said that, here are the configuration options:
@ -54,14 +56,14 @@ xiaomi_bslamp2:
trigger_pin: "GPIO16"
```
The only reason that I can think of for adding this platform configuration to your yaml
file, would be if you blew one or more or the ESP32 pins, and need to rewire functions
to different pins.
The only reason that I can think of for adding this platform configuration
to your yaml file, would be if you blew one or more or the ESP32 pins, and
need to rewire functionality. In other casis, simply omit the section.
## Component: light
The light component creates an RGBWW light. This means that it can do colored light and
cold/warm white light based on a color temperature.
The light component creates an RGBWW light. This means that it can do
colored light and cold/warm white light based on a color temperature.
```yaml
light:
@ -95,60 +97,68 @@ light:
### Configuration variables:
* **name** (**Required**, string): The name of the light.
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id,
you can reference the light from automation rules (e.g. to turn on the light when the power
button is tapped)
* **default_transition_length** (*Optional*, Time): The transition length to use when
no transition length is set in a light call. Defaults to 1s.
* **effects** (*Optional*, list): A list of [light effects](https://esphome.io/components/light/index.html#light-effects)
* **id** (*Optional*, ID): Manually specify the ID used for code generation.
By providing an id, you can reference the light from automation rules
(e.g. to turn on the light when the power button is tapped)
* **default_transition_length** (*Optional*, Time): The transition length to
use when no transition length is set in a light call. Defaults to 1s.
* **effects** (*Optional*, list): A list of
[light effects](https://esphome.io/components/light/index.html#light-effects)
to use for this light.
* **presets** (*Optional*, dict): Used to define presets, that can be used from automations.
See [below](#light-presets) for detailed information.
* **on_brightness** (*Optional*, Action): An automation to perform when the brightness of the light is modified.
* All other options from [the base Light implementation](https://esphome.io/components/light/index.html#config-light),
except for options that handle color correction options like `gamma_correct` and `color_correct`.
These options are superceded by the fact that the light component has a fully customized
light model, that closely follows the light model of the original lamp's firmware.
* **presets** (*Optional*, dict): Used to define presets, that can be used
from automations. See [below](#light-presets) for detailed information.
* **on_brightness** (*Optional*, Action): An automation to perform when the
brightness of the light is modified.
* All other options from [the base Light
implementation](https://esphome.io/components/light/index.html#config-light),
except for options that handle color correction options like
`gamma_correct` and `color_correct`. These options are superceded by the
fact that the light component has a fully customized light model, that
closely follows the light model of the original lamp's firmware.
### Light modes
The lamp supports multiple light modes. These are:
* **RGB light** (input: RGB + brightness)
* **White light** (input: Color Temperature + brightness)
* **Night light** (input: either RGB or Color Temperature + brightness at 1%)
* **RGB light** (input: RGB + brightness > 1%)
* **White light** (input: Color Temperature + brightness > 1%)
* **Night light** (input: RGB or White light + brightness at 1%)
In the original firmware + Yeelight Home Assistant integration, the night light feature is
implemented through a switch component. The switch can be turned on to activate the night
light mode. In this ESPHome firmware, setting the brightness to its lowest value triggers
the night light mode. This makes things a lot easier to control.
In the original firmware + Yeelight Home Assistant integration, the night
light feature is implemented through a switch component. The switch can be
turned on to activate the night light mode. In this ESPHome firmware,
setting the brightness to its lowest value triggers the night light mode.
This makes things a lot easier to control.
It is possible to control the night light mode separately. An example of this can be
found in the [example.yaml](example.yaml), in which holding the power button is bound
to activating the night light.
It is possible to control the night light mode separately. An example of
this can be found in the [example.yaml](example.yaml), in which holding the
power button is bound to activating the night light.
### Light presets
The presets functionality was written with the original lamp firemware functionality in mind:
the user has two groups of presets available: one for RGB light presets and one for white light
presets (based on color temperature). The color button (the top one on the front panel) can be
tapped to switch to the next preset within the active preset group. The same button can be
held for a little while, to switch to the other preset group.
The presets functionality was written with the original lamp firemware
functionality in mind: the user has two groups of presets available: one for
RGB light presets and one for white light presets (based on color
temperature). The color button (the top one on the front panel) can be
tapped to switch to the next preset within the active preset group. The same
button can be held for a little while, to switch to the other preset group.
In your light configuration, you can mimic this behavior (in fact: it is done so in the
[example.yaml](example.yaml)) by means of the presets system. This system consists of two
parts:
In your light configuration, you can mimic this behavior (in fact: it is
done so in the [example.yaml](example.yaml)) by means of the presets system.
This system consists of two parts:
* Defining presets
* Activating presets from automations
**Defining presets**
Presets can be configured in the `presets` option of the `light` configuration.
Presets can be configured in the `presets` option of the `light`
configuration.
Presets are arranged in groups. You can define as little or as many groups as you like.
The example configuration uses two groups, but that is only to mimic the original behavior.
If you only need one group, then create one group. If you need ten, go ahead and knock yourself out.
Presets are arranged in groups. You can define as little or as many groups
as you like. The example configuration uses two groups, but that is only to
mimic the original behavior. If you only need one group, then create one
group. If you need ten, go ahead and knock yourself out.
The general structure of the presets configuration is:
@ -166,9 +176,9 @@ light:
..
```
*Note: duplicate template names are ok, as long as they are within their own group.
If you use duplicate preset names within a single group, then the last preset will override the
earlier one(s).*
*Note: duplicate template names are ok, as long as they are within their own
group. If you use duplicate preset names within a single group, then the
last preset will override the earlier one(s).*
A preset can define one of the following:
@ -189,8 +199,9 @@ A preset can define one of the following:
**Activating presets from automations**
Once presets have been configured, they can be activated using the `preset.activate` action.
The following options are available for this action:
Once presets have been configured, they can be activated using the
`preset.activate` action. The following options are available for this
action:
* Switch to next preset group (and after the last, switch to the first):
```yaml
@ -198,7 +209,8 @@ preset.activate:
next: group
```
* Switch to next preset within currentl preset group (and after the last, switch to the first):
* Switch to next preset within currentl preset group (and after the last,
switch to the first):
```yaml
preset.activate:
next: preset
@ -228,57 +240,69 @@ preset.activate: white.warm
**Handling of invalid input**
When a group or template is specified that does not exist, or if next group/preset
is used while no presets have been defined at all, then the action will be ignored
and an error will be logged.
When a group or template is specified that does not exist, or if next
group/preset is used while no presets have been defined at all, then the
action will be ignored and an error will be logged.
*Note: This is validation at run time. It would be a lot better to
validate the names at compile time more strictly, so the firmware will not
even compile when invalid names are in use.
[Issue #15](https://github.com/mmakaay/esphome-xiaomi_bslamp2/issues/15)
was created for implementing this. However, a new feature in ESPHome is
required to be able to do this implementation. Good news is that this
is already well on its way.*
This is of course validation at run time. It would be better to validate the
names at compile time more strictly, so the firmware won't compile when invalid
names are in use. [Issue #15 was created for implementing this](https://github.com/mmakaay/esphome-xiaomi_bslamp2/issues/15).
## Component: binary_sensor
Binary sensors can be added to the configuration for handling touch/release events
for the front panel. On touch, a binary_sensor will publish `True`, on release it
will publish `False`. The configuration of a binary_sensor determines what part
or parts of the front panel are involved in the touch events.
Binary sensors can be added to the configuration for handling touch/release
events for the front panel. On touch, a binary_sensor will publish `True`,
on release it will publish `False`. The configuration of a binary_sensor
determines what part of the front panel is involved in the touch events.
```yaml
binary_sensor:
- platform: xiaomi_bslamp2
id: my_bedside_lamp_power_button
part: POWER_BUTTON
for: POWER_BUTTON
on_press:
then:
- light.toggle: my_bedside_lamp
```
For specifying specific parts of the front panel in the upcoming configuration variables,
the following identifiers are available:
For referencing the parts of the front panel, the following part identifiers
are available:
* POWER_BUTTON (or its alias: POWER)
* COLOR_BUTTON (or its alias: COLOR)
* SLIDER
If personal taste dictates so, you can use lower case characters and spaces
instead of underscores. This means that for example "Power Button" and
"power" would be valid identifiers for the power button.
### Configuration variables:
* **name** (*Optional*, string): The name of the binary sensor. Setting a name will expose the
binary sensor as an entity in Home Assistant. If you do not need this, you can omit the name.
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id,
you can reference the binary_sensor from automation rules (to retrieve the current state
of the binary_sensor).
* **part** (*Optional*, part identifier): This specifies what part of the front panel the binary sensor must
look at. Valid options are: "any" (the default) or one of the abovementioned part identifiers.
* All other options from [Binary Sensor](https://esphome.io/components/binary_sensor/index.html#config-binary-sensor).
* **name** (*Optional*, string): The name of the binary sensor. Setting a
name will expose the binary sensor as an entity in Home Assistant. If you
do not need this, you can omit the name.
* **id** (*Optional*, ID): Manually specify the ID used for code generation.
By providing an id, you can reference the binary_sensor from automation
rules (to retrieve the current state of the binary_sensor).
* **for** (*Mandatory*, part identifier): This specifies to for part of the
front panel the binary sensor must report touch events.
* All other options from
[Binary Sensor](https://esphome.io/components/binary_sensor/index.html#config-binary-sensor).
## Component: sensor
The sensor component publishes touch events for the front panel slider. The published value
represents the level at which the slider was touched.
The sensor component publishes touch events for the front panel slider. The
published value represents the level at which the slider was touched.
*Note: This sensor only reports the touched slider level. It cannot be used for detecting release
events. If you want to handle touch/release events for the slider, then you can make use of
the [binary_sensor](#component-binary_sensor) instead.*
*Note: This sensor only reports the touched slider level. It cannot be used
for detecting release events. If you want to handle touch/release events for
the slider, then you can make use of the
[binary_sensor](#component-binary_sensor) instead.*
```yaml
sensor:
@ -295,22 +319,27 @@ sensor:
### Configuration variables:
* **name** (*Optional*, string): The name of the sensor. Setting a name will expose the
sensor as an entity in Home Assistant. If you do not need this, you can omit the name.
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id,
you can reference the sensor from automation rules (e.g. to retrieve the current state
of the binary_sensor).
* **range_from** (*Optional*, float): By default, published values vary from the range 0.01 to 1.00,
in 20 steps. This option modifies the lower bound of the range.
* **range_to** (*Optional*, float): This option modifies the upper bound of the range.
* All other options from [Sensor](https://esphome.io/components/sensor/index.html#config-sensor).
* **name** (*Optional*, string): The name of the sensor. Setting a name will
expose the sensor as an entity in Home Assistant. If you do not need this,
you can omit the name.
* **id** (*Optional*, ID): Manually specify the ID used for code generation.
By providing an id, you can reference the sensor from automation rules
(e.g. to retrieve the current state of the binary_sensor).
* **range_from** (*Optional*, float): By default, published values vary from
the range 0.01 to 1.00, in 20 steps. This option modifies the lower bound
of the range.
* **range_to** (*Optional*, float): This option modifies the upper bound of
the range.
* All other options from
[Sensor](https://esphome.io/components/sensor/index.html#config-sensor).
## Component: output
The (float) output component is linked to the front panel illumination + level indicator.
Setting this output to value 0.0 will turn off the frontpanel illumination. Other values,
up to 1.0, will turn on the illumination and will set the level indicator to the requested
level (in 10 steps).
The (float) output component is linked to the front panel illumination +
level indicator. Setting this output to value 0.0 will turn off the
frontpanel illumination. Other values, up to 1.0, will turn on the
illumination and will set the level indicator to the requested level (in 10
steps).
```yaml
output:
@ -325,16 +354,19 @@ output:
## Component: text_sensor
The text sensor component publishes changes in the active [light mode](#light-modes).
Possible output values for this sensor are: "off", "rgb", "white" and "night".
The text sensor component publishes changes in the active
[light mode](#light-modes). Possible output values for this sensor are: "off",
"rgb", "white" and "night".
### Configuration variables:
* **name** (*Optional*, string): The name of the text sensor. Setting a name will expose the
text sensor as an entity in Home Assistant. If you do not need this, you can omit the name.
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id,
you can reference the text sensor from automation rules (to retrieve the current state
of the text_sensor).
* All other options from [Text Sensor](https://esphome.io/components/text_sensor/index.html)
* **name** (*Optional*, string): The name of the text sensor. Setting a name
will expose the text sensor as an entity in Home Assistant. If you do not
need this, you can omit the name.
* **id** (*Optional*, ID): Manually specify the ID used for code generation.
By providing an id, you can reference the text sensor from automation
rules (to retrieve the current state of the text_sensor).
* All other options from
[Text Sensor](https://esphome.io/components/text_sensor/index.html)
< [Installation guide](installation.md) | [Index](../README.md) | [Flashing guide](flashing.md) >

+ 2
- 2
doc/example.yaml View File

@ -64,8 +64,8 @@ esphome:
board: esp32doit-devkit-v1
platformio_options:
platform: espressif32@3.2.0
platform_packages: |-4
framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6
platform_packages: |-
framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6
# This component controls the LED lights of the lamp.
light:


+ 22
- 22
front_panel_hal.h View File

@ -37,39 +37,39 @@ using EVENT = uint16_t;
// BITS INDICATE PATTERN RESULT
// 1 status 0 parsing event failed
// 1 parsing event successful
// 2-3 part 00 part unknown
// 01 power button
// 10 color button
// 11 slider
// 4-5 type 00 type unknown
// 2-4 part 000 part unknown
// 001 power button
// 010 color button
// 100 slider
// 5-6 type 00 type unknown
// 01 touch
// 10 release
// 6-10 slider 00000 level known (or part is not "slider")
// 7-11 slider 00000 level known (or part is not "slider")
// level 00001 level 1
// ... up to
// 10101 level 21
//
static const EVENT FLAG_INIT = 0b0000000000;
static const EVENT FLAG_INIT = 0b00000000000;
static const EVENT FLAG_ERR = 0b0000000000;
static const EVENT FLAG_OK = 0b0000000001;
static const EVENT FLAG_ERR = 0b00000000000;
static const EVENT FLAG_OK = 0b00000000001;
static const EVENT FLAG_PART_SHIFT = 1;
static const EVENT FLAG_PART_MASK = 0b0000000110;
static const EVENT FLAG_PART_UNKNOWN = 0b0000000000;
static const EVENT FLAG_PART_POWER = 0b0000000010;
static const EVENT FLAG_PART_COLOR = 0b0000000100;
static const EVENT FLAG_PART_SLIDER = 0b0000000110;
static const EVENT FLAG_PART_MASK = 0b00000001110;
static const EVENT FLAG_PART_UNKNOWN = 0b00000000000;
static const EVENT FLAG_PART_POWER = 0b00000000010;
static const EVENT FLAG_PART_COLOR = 0b00000000100;
static const EVENT FLAG_PART_SLIDER = 0b00000001000;
static const EVENT FLAG_TYPE_SHIFT = 3;
static const EVENT FLAG_TYPE_MASK = 0b0000011000;
static const EVENT FLAG_TYPE_UNKNOWN = 0b0000000000;
static const EVENT FLAG_TYPE_TOUCH = 0b0000001000;
static const EVENT FLAG_TYPE_RELEASE = 0b0000010000;
static const EVENT FLAG_TYPE_SHIFT = 4;
static const EVENT FLAG_TYPE_MASK = 0b00000110000;
static const EVENT FLAG_TYPE_UNKNOWN = 0b00000000000;
static const EVENT FLAG_TYPE_TOUCH = 0b00000010000;
static const EVENT FLAG_TYPE_RELEASE = 0b00000100000;
static const EVENT FLAG_LEVEL_SHIFT = 5;
static const EVENT FLAG_LEVEL_MASK = 0b1111100000;
static const EVENT FLAG_LEVEL_UNKNOWN = 0b0000000000;
static const EVENT FLAG_LEVEL_SHIFT = 6;
static const EVENT FLAG_LEVEL_MASK = 0b11111000000;
static const EVENT FLAG_LEVEL_UNKNOWN = 0b00000000000;
/**
* This class implements a parser that translates event byte codes from the


Loading…
Cancel
Save