mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00: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:
		
				
					committed by
					
						 Otto Winter
						Otto Winter
					
				
			
			
				
	
			
			
			
						parent
						
							9e56318498
						
					
				
				
					commit
					d7a8c50c98
				
			
							
								
								
									
										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_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' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user