From d7a8c50c989c298c9a7e1dd081f01c95703f278f Mon Sep 17 00:00:00 2001 From: Michiel van Turnhout Date: Tue, 28 May 2019 20:41:10 +0200 Subject: [PATCH] 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 --- .../components/binary_sensor_map/__init__.py | 0 .../binary_sensor_map/binary_sensor_map.cpp | 60 +++++++++++++++++++ .../binary_sensor_map/binary_sensor_map.h | 58 ++++++++++++++++++ .../components/binary_sensor_map/sensor.py | 42 +++++++++++++ esphome/const.py | 1 + tests/test3.yaml | 13 ++++ 6 files changed, 174 insertions(+) create mode 100644 esphome/components/binary_sensor_map/__init__.py create mode 100644 esphome/components/binary_sensor_map/binary_sensor_map.cpp create mode 100644 esphome/components/binary_sensor_map/binary_sensor_map.h create mode 100644 esphome/components/binary_sensor_map/sensor.py diff --git a/esphome/components/binary_sensor_map/__init__.py b/esphome/components/binary_sensor_map/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.cpp b/esphome/components/binary_sensor_map/binary_sensor_map.cpp new file mode 100644 index 0000000000..7a2eb66cf8 --- /dev/null +++ b/esphome/components/binary_sensor_map/binary_sensor_map.cpp @@ -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 diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.h b/esphome/components/binary_sensor_map/binary_sensor_map.h new file mode 100644 index 0000000000..e99b4e18d6 --- /dev/null +++ b/esphome/components/binary_sensor_map/binary_sensor_map.h @@ -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 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 diff --git a/esphome/components/binary_sensor_map/sensor.py b/esphome/components/binary_sensor_map/sensor.py new file mode 100644 index 0000000000..8b8cd8fc4e --- /dev/null +++ b/esphome/components/binary_sensor_map/sensor.py @@ -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])) diff --git a/esphome/const.py b/esphome/const.py index 19e138d506..63bfcf76d8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -458,6 +458,7 @@ ICON_BATTERY = 'mdi:battery' ICON_BRIEFCASE_DOWNLOAD = 'mdi:briefcase-download' ICON_BRIGHTNESS_5 = 'mdi:brightness-5' ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon' +ICON_CHECK_CIRCLE_OUTLINE = 'mdi:check-circle-outline' ICON_EMPTY = '' ICON_FLASH = 'mdi:flash' ICON_FLOWER = 'mdi:flower' diff --git a/tests/test3.yaml b/tests/test3.yaml index 1b75dac7b1..fb46043f34 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -152,6 +152,16 @@ sensor: sensors: - id: 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: - platform: homeassistant @@ -196,12 +206,15 @@ binary_sensor: - platform: mpr121 channel: 1 name: "touchkey1" + id: bin1 - platform: mpr121 channel: 2 name: "touchkey2" + id: bin2 - platform: mpr121 channel: 3 name: "touchkey3" + id: bin3 on_press: then: - switch.toggle: mpr121_toggle