mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 09:18:00 +01:00
Binary sensor map implementation (#551)
* add binary_sensor_map c code * add python file * fixed python and C++ code for new framework * renamed add_sensor to add_channel * travis * Updates - Use struct for channels_ array - heap allocation is not really necessary here. - any_active can also be written as mask != 0 - Update setup priority to DATA - Use shorter TAG (name is already long; not important) - Quotes around name - Add icon to sensor - Use new cv.typed_schema - Change CONF_CHANNEL to CONF_BINARY_SENSOR - makes it clearer that this option accepts a binary sensor (and not for example an int) - Add test Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
parent
9e56318498
commit
d7a8c50c98
6 changed files with 174 additions and 0 deletions
0
esphome/components/binary_sensor_map/__init__.py
Normal file
0
esphome/components/binary_sensor_map/__init__.py
Normal file
60
esphome/components/binary_sensor_map/binary_sensor_map.cpp
Normal file
60
esphome/components/binary_sensor_map/binary_sensor_map.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include "binary_sensor_map.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace binary_sensor_map {
|
||||||
|
|
||||||
|
static const char *TAG = "binary_sensor_map";
|
||||||
|
|
||||||
|
void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }
|
||||||
|
|
||||||
|
void BinarySensorMap::loop() {
|
||||||
|
switch (this->sensor_type_) {
|
||||||
|
case BINARY_SENSOR_MAP_TYPE_GROUP:
|
||||||
|
this->process_group_();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinarySensorMap::process_group_() {
|
||||||
|
float total_current_value = 0.0;
|
||||||
|
uint8_t num_active_sensors = 0;
|
||||||
|
uint64_t mask = 0x00;
|
||||||
|
// check all binary_sensors for its state. when active add its value to total_current_value.
|
||||||
|
// create a bitmask for the binary_sensor status on all channels
|
||||||
|
for (size_t i = 0; i < this->channels_.size(); i++) {
|
||||||
|
auto bs = this->channels_[i];
|
||||||
|
if (bs.binary_sensor->state) {
|
||||||
|
num_active_sensors++;
|
||||||
|
total_current_value += bs.sensor_value;
|
||||||
|
mask |= 1 << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if the sensor map was touched
|
||||||
|
if (mask != 0ULL) {
|
||||||
|
// did the bit_mask change or is it a new sensor touch
|
||||||
|
if (this->last_mask_ != mask) {
|
||||||
|
float publish_value = total_current_value / num_active_sensors;
|
||||||
|
ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
|
||||||
|
this->publish_state(publish_value);
|
||||||
|
}
|
||||||
|
} else if (this->last_mask_ != 0ULL) {
|
||||||
|
// is this a new sensor release
|
||||||
|
ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
|
||||||
|
this->publish_state(NAN);
|
||||||
|
}
|
||||||
|
this->last_mask_ = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float value) {
|
||||||
|
BinarySensorMapChannel sensor_channel{
|
||||||
|
.binary_sensor = sensor,
|
||||||
|
.sensor_value = value,
|
||||||
|
};
|
||||||
|
this->channels_.push_back(sensor_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinarySensorMap::set_sensor_type(BinarySensorMapType sensor_type) { this->sensor_type_ = sensor_type; }
|
||||||
|
|
||||||
|
} // namespace binary_sensor_map
|
||||||
|
} // namespace esphome
|
58
esphome/components/binary_sensor_map/binary_sensor_map.h
Normal file
58
esphome/components/binary_sensor_map/binary_sensor_map.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace binary_sensor_map {
|
||||||
|
|
||||||
|
enum BinarySensorMapType {
|
||||||
|
BINARY_SENSOR_MAP_TYPE_GROUP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BinarySensorMapChannel {
|
||||||
|
binary_sensor::BinarySensor *binary_sensor;
|
||||||
|
float sensor_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Class to group binary_sensors to one Sensor.
|
||||||
|
*
|
||||||
|
* Each binary sensor represents a float value in the group.
|
||||||
|
*/
|
||||||
|
class BinarySensorMap : public sensor::Sensor, public Component {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
/**
|
||||||
|
* The loop checks all binary_sensor states
|
||||||
|
* When the binary_sensor reports a true value for its state, then the float value it represents is added to the
|
||||||
|
* total_current_value
|
||||||
|
*
|
||||||
|
* Only when the total_current_value changed and at least one sensor reports an active state we publish the sensors
|
||||||
|
* average value. When the value changed and no sensors ar active we publish NAN.
|
||||||
|
* */
|
||||||
|
void loop() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
/** Add binary_sensors to the group.
|
||||||
|
* Each binary_sensor represents a float value when its state is true
|
||||||
|
*
|
||||||
|
* @param *sensor The binary sensor.
|
||||||
|
* @param value The value this binary_sensor represents
|
||||||
|
*/
|
||||||
|
void add_channel(binary_sensor::BinarySensor *sensor, float value);
|
||||||
|
void set_sensor_type(BinarySensorMapType sensor_type);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<BinarySensorMapChannel> channels_{};
|
||||||
|
BinarySensorMapType sensor_type_{BINARY_SENSOR_MAP_TYPE_GROUP};
|
||||||
|
// this gives max 64 channels per binary_sensor_map
|
||||||
|
uint64_t last_mask_{0x00};
|
||||||
|
/**
|
||||||
|
* methods to process the types of binary_sensor_maps
|
||||||
|
* GROUP: process_group_() just map to a value
|
||||||
|
* */
|
||||||
|
void process_group_();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace binary_sensor_map
|
||||||
|
} // namespace esphome
|
42
esphome/components/binary_sensor_map/sensor.py
Normal file
42
esphome/components/binary_sensor_map/sensor.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
|
from esphome.components import sensor, binary_sensor
|
||||||
|
from esphome.const import CONF_ID, CONF_CHANNELS, CONF_VALUE, CONF_TYPE, UNIT_EMPTY, \
|
||||||
|
ICON_CHECK_CIRCLE_OUTLINE, CONF_BINARY_SENSOR
|
||||||
|
|
||||||
|
DEPENDENCIES = ['binary_sensor']
|
||||||
|
|
||||||
|
binary_sensor_map_ns = cg.esphome_ns.namespace('binary_sensor_map')
|
||||||
|
BinarySensorMap = binary_sensor_map_ns.class_('BinarySensorMap', cg.Component, sensor.Sensor)
|
||||||
|
SensorMapType = binary_sensor_map_ns.enum('SensorMapType')
|
||||||
|
|
||||||
|
CONF_GROUP = 'group'
|
||||||
|
SENSOR_MAP_TYPES = {
|
||||||
|
CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP,
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
cv.Required(CONF_BINARY_SENSOR): cv.use_id(binary_sensor.BinarySensor),
|
||||||
|
cv.Required(CONF_VALUE): cv.float_,
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.typed_schema({
|
||||||
|
CONF_GROUP: sensor.sensor_schema(UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0).extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(BinarySensorMap),
|
||||||
|
cv.Required(CONF_CHANNELS): cv.All(cv.ensure_list(entry), cv.Length(min=1)),
|
||||||
|
}),
|
||||||
|
}, lower=True)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield sensor.register_sensor(var, config)
|
||||||
|
|
||||||
|
constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
|
||||||
|
cg.add(var.set_sensor_type(constant))
|
||||||
|
|
||||||
|
for ch in config[CONF_CHANNELS]:
|
||||||
|
input_var = yield cg.get_variable(ch[CONF_BINARY_SENSOR])
|
||||||
|
cg.add(var.add_channel(input_var, ch[CONF_VALUE]))
|
|
@ -458,6 +458,7 @@ ICON_BATTERY = 'mdi:battery'
|
||||||
ICON_BRIEFCASE_DOWNLOAD = 'mdi:briefcase-download'
|
ICON_BRIEFCASE_DOWNLOAD = 'mdi:briefcase-download'
|
||||||
ICON_BRIGHTNESS_5 = 'mdi:brightness-5'
|
ICON_BRIGHTNESS_5 = 'mdi:brightness-5'
|
||||||
ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon'
|
ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon'
|
||||||
|
ICON_CHECK_CIRCLE_OUTLINE = 'mdi:check-circle-outline'
|
||||||
ICON_EMPTY = ''
|
ICON_EMPTY = ''
|
||||||
ICON_FLASH = 'mdi:flash'
|
ICON_FLASH = 'mdi:flash'
|
||||||
ICON_FLOWER = 'mdi:flower'
|
ICON_FLOWER = 'mdi:flower'
|
||||||
|
|
|
@ -152,6 +152,16 @@ sensor:
|
||||||
sensors:
|
sensors:
|
||||||
- id: custom_sensor
|
- id: custom_sensor
|
||||||
name: Custom Sensor
|
name: Custom Sensor
|
||||||
|
- platform: binary_sensor_map
|
||||||
|
name: Binary Sensor Map
|
||||||
|
type: group
|
||||||
|
channels:
|
||||||
|
- binary_sensor: bin1
|
||||||
|
value: 10.0
|
||||||
|
- binary_sensor: bin2
|
||||||
|
value: 15.0
|
||||||
|
- binary_sensor: bin3
|
||||||
|
value: 100.0
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
|
@ -196,12 +206,15 @@ binary_sensor:
|
||||||
- platform: mpr121
|
- platform: mpr121
|
||||||
channel: 1
|
channel: 1
|
||||||
name: "touchkey1"
|
name: "touchkey1"
|
||||||
|
id: bin1
|
||||||
- platform: mpr121
|
- platform: mpr121
|
||||||
channel: 2
|
channel: 2
|
||||||
name: "touchkey2"
|
name: "touchkey2"
|
||||||
|
id: bin2
|
||||||
- platform: mpr121
|
- platform: mpr121
|
||||||
channel: 3
|
channel: 3
|
||||||
name: "touchkey3"
|
name: "touchkey3"
|
||||||
|
id: bin3
|
||||||
on_press:
|
on_press:
|
||||||
then:
|
then:
|
||||||
- switch.toggle: mpr121_toggle
|
- switch.toggle: mpr121_toggle
|
||||||
|
|
Loading…
Reference in a new issue