mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Mopeka Pro Check improvement to allow user to configure the sensor reporting for lower quality readings (#7475)
This commit is contained in:
		| @@ -17,6 +17,8 @@ void MopekaProCheck::dump_config() { | ||||
|   LOG_SENSOR("  ", "Temperature", this->temperature_); | ||||
|   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||
|   LOG_SENSOR("  ", "Reading Distance", this->distance_); | ||||
|   LOG_SENSOR("  ", "Read Quality", this->read_quality_); | ||||
|   LOG_SENSOR("  ", "Ignored Reads", this->ignored_reads_); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -66,34 +68,49 @@ bool MopekaProCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) | ||||
|     this->battery_level_->publish_state(level); | ||||
|   } | ||||
|  | ||||
|   // Get the quality value | ||||
|   SensorReadQuality quality_value = this->parse_read_quality_(manu_data.data); | ||||
|   if (this->read_quality_ != nullptr) { | ||||
|     this->read_quality_->publish_state(static_cast<int>(quality_value)); | ||||
|   } | ||||
|  | ||||
|   // Determine if we have a good enough quality of read to report level and distance | ||||
|   // sensors.  This sensor is reported regardless of distance or level sensors being enabled | ||||
|   if (quality_value < this->min_signal_quality_) { | ||||
|     ESP_LOGW(TAG, "Read Quality too low to report distance or level"); | ||||
|     this->ignored_read_count_++; | ||||
|   } else { | ||||
|     // reset to zero since read quality was sufficient | ||||
|     this->ignored_read_count_ = 0; | ||||
|   } | ||||
|   // Report number of contiguous ignored reads if sensor defined | ||||
|   if (this->ignored_reads_ != nullptr) { | ||||
|     this->ignored_reads_->publish_state(this->ignored_read_count_); | ||||
|   } | ||||
|  | ||||
|   // Get distance and level if either are sensors | ||||
|   if ((this->distance_ != nullptr) || (this->level_ != nullptr)) { | ||||
|     uint32_t distance_value = this->parse_distance_(manu_data.data); | ||||
|     SensorReadQuality quality_value = this->parse_read_quality_(manu_data.data); | ||||
|     ESP_LOGD(TAG, "Distance Sensor: Quality (0x%X) Distance (%" PRId32 "mm)", quality_value, distance_value); | ||||
|     if (quality_value < QUALITY_HIGH) { | ||||
|       ESP_LOGW(TAG, "Poor read quality."); | ||||
|     } | ||||
|     if (quality_value < QUALITY_MED) { | ||||
|       // if really bad reading set to 0 | ||||
|       ESP_LOGW(TAG, "Setting distance to 0"); | ||||
|       distance_value = 0; | ||||
|     } | ||||
|  | ||||
|     // update distance sensor | ||||
|     if (this->distance_ != nullptr) { | ||||
|       this->distance_->publish_state(distance_value); | ||||
|     } | ||||
|  | ||||
|     // update level sensor | ||||
|     if (this->level_ != nullptr) { | ||||
|       uint8_t tank_level = 0; | ||||
|       if (distance_value >= this->full_mm_) { | ||||
|         tank_level = 100;  // cap at 100% | ||||
|       } else if (distance_value > this->empty_mm_) { | ||||
|         tank_level = ((100.0f / (this->full_mm_ - this->empty_mm_)) * (distance_value - this->empty_mm_)); | ||||
|     // only update distance and level sensors if read quality was sufficient.  This can be determined by | ||||
|     // if the ignored_read_count is zero. | ||||
|     if (this->ignored_read_count_ == 0) { | ||||
|       // update distance sensor | ||||
|       if (this->distance_ != nullptr) { | ||||
|         this->distance_->publish_state(distance_value); | ||||
|       } | ||||
|  | ||||
|       // update level sensor | ||||
|       if (this->level_ != nullptr) { | ||||
|         uint8_t tank_level = 0; | ||||
|         if (distance_value >= this->full_mm_) { | ||||
|           tank_level = 100;  // cap at 100% | ||||
|         } else if (distance_value > this->empty_mm_) { | ||||
|           tank_level = ((100.0f / (this->full_mm_ - this->empty_mm_)) * (distance_value - this->empty_mm_)); | ||||
|         } | ||||
|         this->level_->publish_state(tank_level); | ||||
|       } | ||||
|       this->level_->publish_state(tank_level); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -131,6 +148,8 @@ uint32_t MopekaProCheck::parse_distance_(const std::vector<uint8_t> &message) { | ||||
| uint8_t MopekaProCheck::parse_temperature_(const std::vector<uint8_t> &message) { return (message[2] & 0x7F) - 40; } | ||||
|  | ||||
| SensorReadQuality MopekaProCheck::parse_read_quality_(const std::vector<uint8_t> &message) { | ||||
|   // Since a 8 bit value is being shifted and truncated to 2 bits all possible values are defined as enumeration | ||||
|   //  value and the static cast is safe. | ||||
|   return static_cast<SensorReadQuality>(message[4] >> 6); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -24,9 +24,9 @@ enum SensorType { | ||||
| }; | ||||
|  | ||||
| // Sensor read quality.  If sensor is poorly placed or tank level | ||||
| // gets too low the read quality will show and the distanace | ||||
| // gets too low the read quality will show and the distance | ||||
| // measurement may be inaccurate. | ||||
| enum SensorReadQuality { QUALITY_HIGH = 0x3, QUALITY_MED = 0x2, QUALITY_LOW = 0x1, QUALITY_NONE = 0x0 }; | ||||
| enum SensorReadQuality { QUALITY_HIGH = 0x3, QUALITY_MED = 0x2, QUALITY_LOW = 0x1, QUALITY_ZERO = 0x0 }; | ||||
|  | ||||
| class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceListener { | ||||
|  public: | ||||
| @@ -35,11 +35,14 @@ class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceLi | ||||
|   bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override { return setup_priority::DATA; } | ||||
|   void set_min_signal_quality(SensorReadQuality min) { this->min_signal_quality_ = min; }; | ||||
|  | ||||
|   void set_level(sensor::Sensor *level) { level_ = level; }; | ||||
|   void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }; | ||||
|   void set_battery_level(sensor::Sensor *bat) { battery_level_ = bat; }; | ||||
|   void set_distance(sensor::Sensor *distance) { distance_ = distance; }; | ||||
|   void set_signal_quality(sensor::Sensor *rq) { read_quality_ = rq; }; | ||||
|   void set_ignored_reads(sensor::Sensor *ir) { ignored_reads_ = ir; }; | ||||
|   void set_tank_full(float full) { full_mm_ = full; }; | ||||
|   void set_tank_empty(float empty) { empty_mm_ = empty; }; | ||||
|  | ||||
| @@ -49,9 +52,13 @@ class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceLi | ||||
|   sensor::Sensor *temperature_{nullptr}; | ||||
|   sensor::Sensor *distance_{nullptr}; | ||||
|   sensor::Sensor *battery_level_{nullptr}; | ||||
|   sensor::Sensor *read_quality_{nullptr}; | ||||
|   sensor::Sensor *ignored_reads_{nullptr}; | ||||
|  | ||||
|   uint32_t full_mm_; | ||||
|   uint32_t empty_mm_; | ||||
|   uint32_t ignored_read_count_ = 0; | ||||
|   SensorReadQuality min_signal_quality_ = QUALITY_MED; | ||||
|  | ||||
|   uint8_t parse_battery_level_(const std::vector<uint8_t> &message); | ||||
|   uint32_t parse_distance_(const std::vector<uint8_t> &message); | ||||
|   | ||||
| @@ -5,9 +5,12 @@ from esphome.const import ( | ||||
|     CONF_DISTANCE, | ||||
|     CONF_MAC_ADDRESS, | ||||
|     CONF_ID, | ||||
|     ICON_COUNTER, | ||||
|     ICON_THERMOMETER, | ||||
|     ICON_RULER, | ||||
|     ICON_SIGNAL, | ||||
|     UNIT_PERCENT, | ||||
|     UNIT_EMPTY, | ||||
|     CONF_LEVEL, | ||||
|     CONF_TEMPERATURE, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
| @@ -16,11 +19,15 @@ from esphome.const import ( | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     CONF_BATTERY_LEVEL, | ||||
|     DEVICE_CLASS_BATTERY, | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
| ) | ||||
|  | ||||
| CONF_TANK_TYPE = "tank_type" | ||||
| CONF_CUSTOM_DISTANCE_FULL = "custom_distance_full" | ||||
| CONF_CUSTOM_DISTANCE_EMPTY = "custom_distance_empty" | ||||
| CONF_SIGNAL_QUALITY = "signal_quality" | ||||
| CONF_MINIMUM_SIGNAL_QUALITY = "minimum_signal_quality" | ||||
| CONF_IGNORED_READS = "ignored_reads" | ||||
|  | ||||
| ICON_PROPANE_TANK = "mdi:propane-tank" | ||||
|  | ||||
| @@ -56,6 +63,14 @@ MopekaProCheck = mopeka_pro_check_ns.class_( | ||||
|     "MopekaProCheck", esp32_ble_tracker.ESPBTDeviceListener, cg.Component | ||||
| ) | ||||
|  | ||||
| SensorReadQuality = mopeka_pro_check_ns.enum("SensorReadQuality") | ||||
| SIGNAL_QUALITIES = { | ||||
|     "ZERO": SensorReadQuality.QUALITY_ZERO, | ||||
|     "LOW": SensorReadQuality.QUALITY_LOW, | ||||
|     "MEDIUM": SensorReadQuality.QUALITY_MED, | ||||
|     "HIGH": SensorReadQuality.QUALITY_HIGH, | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
| @@ -89,6 +104,21 @@ CONFIG_SCHEMA = ( | ||||
|                 device_class=DEVICE_CLASS_BATTERY, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_SIGNAL_QUALITY): sensor.sensor_schema( | ||||
|                 unit_of_measurement=UNIT_EMPTY, | ||||
|                 icon=ICON_SIGNAL, | ||||
|                 accuracy_decimals=0, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_IGNORED_READS): sensor.sensor_schema( | ||||
|                 unit_of_measurement=UNIT_EMPTY, | ||||
|                 icon=ICON_COUNTER, | ||||
|                 accuracy_decimals=0, | ||||
|                 entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|             ), | ||||
|             cv.Optional(CONF_MINIMUM_SIGNAL_QUALITY, default="MEDIUM"): cv.enum( | ||||
|                 SIGNAL_QUALITIES, upper=True | ||||
|             ), | ||||
|         } | ||||
|     ) | ||||
|     .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) | ||||
| @@ -119,6 +149,11 @@ async def to_code(config): | ||||
|         cg.add(var.set_tank_empty(CONF_SUPPORTED_TANKS_MAP[t][0])) | ||||
|         cg.add(var.set_tank_full(CONF_SUPPORTED_TANKS_MAP[t][1])) | ||||
|  | ||||
|     if ( | ||||
|         minimum_signal_quality := config.get(CONF_MINIMUM_SIGNAL_QUALITY, None) | ||||
|     ) is not None: | ||||
|         cg.add(var.set_min_signal_quality(minimum_signal_quality)) | ||||
|  | ||||
|     if CONF_TEMPERATURE in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) | ||||
|         cg.add(var.set_temperature(sens)) | ||||
| @@ -131,3 +166,9 @@ async def to_code(config): | ||||
|     if CONF_BATTERY_LEVEL in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL]) | ||||
|         cg.add(var.set_battery_level(sens)) | ||||
|     if CONF_SIGNAL_QUALITY in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_SIGNAL_QUALITY]) | ||||
|         cg.add(var.set_signal_quality(sens)) | ||||
|     if CONF_IGNORED_READS in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_IGNORED_READS]) | ||||
|         cg.add(var.set_ignored_reads(sens)) | ||||
|   | ||||
| @@ -14,3 +14,20 @@ sensor: | ||||
|       name: Propane test distance | ||||
|     battery_level: | ||||
|       name: Propane test battery level | ||||
|  | ||||
|   - platform: mopeka_pro_check | ||||
|     mac_address: AA:BB:CC:DD:EE:FF | ||||
|     tank_type: 20LB_V | ||||
|     temperature: | ||||
|       name: "Propane test2 temp" | ||||
|     level: | ||||
|       name: "Propane test2 level" | ||||
|     distance: | ||||
|       name: "Propane test2 distance" | ||||
|     battery_level: | ||||
|       name: "Propane test2 battery level" | ||||
|     signal_quality: | ||||
|       name: "propane test2 read quality" | ||||
|     ignored_reads: | ||||
|       name: "propane test2 ignored reads" | ||||
|     minimum_signal_quality: "LOW" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user