mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add Xiaomi Cleargrass Temperature and Humidity Sensor (#735)
* Add Xiaomi Cleargrass Temperature and Humidity Sensor * fix CI Travis * fix CI Travis 2 * Improve device detection (more accurate) Co-authored-by: t151602 <sergio.mayoralmartinez@telefonica.com>
This commit is contained in:
		
				
					committed by
					
						 Otto Winter
						Otto Winter
					
				
			
			
				
	
			
			
			
						parent
						
							e207c6ad84
						
					
				
				
					commit
					e30512931b
				
			| @@ -84,13 +84,14 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d | |||||||
|   bool is_mijia = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01; |   bool is_mijia = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01; | ||||||
|   bool is_miflora = (raw[1] & 0x20) == 0x20 && raw[2] == 0x98 && raw[3] == 0x00; |   bool is_miflora = (raw[1] & 0x20) == 0x20 && raw[2] == 0x98 && raw[3] == 0x00; | ||||||
|   bool is_lywsd02 = (raw[1] & 0x20) == 0x20 && raw[2] == 0x5b && raw[3] == 0x04; |   bool is_lywsd02 = (raw[1] & 0x20) == 0x20 && raw[2] == 0x5b && raw[3] == 0x04; | ||||||
|  |   bool is_cleargrass = (raw[1] & 0x30) == 0x30 && raw[2] == 0x47 && raw[3] == 0x03; | ||||||
|  |  | ||||||
|   if (!is_mijia && !is_miflora && !is_lywsd02) { |   if (!is_mijia && !is_miflora && !is_lywsd02 && !is_cleargrass) { | ||||||
|     // ESP_LOGVV(TAG, "Xiaomi no magic bytes"); |     // ESP_LOGVV(TAG, "Xiaomi no magic bytes"); | ||||||
|     return {}; |     return {}; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint8_t raw_offset = is_mijia ? 11 : 12; |   uint8_t raw_offset = is_mijia || is_cleargrass ? 11 : 12; | ||||||
|  |  | ||||||
|   const uint8_t raw_type = raw[raw_offset]; |   const uint8_t raw_type = raw[raw_offset]; | ||||||
|   const uint8_t data_length = raw[raw_offset + 2]; |   const uint8_t data_length = raw[raw_offset + 2]; | ||||||
| @@ -107,6 +108,8 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d | |||||||
|     result.type = XiaomiParseResult::TYPE_MIJIA; |     result.type = XiaomiParseResult::TYPE_MIJIA; | ||||||
|   } else if (is_lywsd02) { |   } else if (is_lywsd02) { | ||||||
|     result.type = XiaomiParseResult::TYPE_LYWSD02; |     result.type = XiaomiParseResult::TYPE_LYWSD02; | ||||||
|  |   } else if (is_cleargrass) { | ||||||
|  |     result.type = XiaomiParseResult::TYPE_CLEARGRASS; | ||||||
|   } |   } | ||||||
|   bool success = parse_xiaomi_data_byte(raw_type, data, data_length, result); |   bool success = parse_xiaomi_data_byte(raw_type, data, data_length, result); | ||||||
|   if (!success) |   if (!success) | ||||||
| @@ -124,6 +127,8 @@ bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) | |||||||
|     name = "Mi Jia"; |     name = "Mi Jia"; | ||||||
|   } else if (res->type == XiaomiParseResult::TYPE_LYWSD02) { |   } else if (res->type == XiaomiParseResult::TYPE_LYWSD02) { | ||||||
|     name = "LYWSD02"; |     name = "LYWSD02"; | ||||||
|  |   } else if (res->type == XiaomiParseResult::TYPE_CLEARGRASS) { | ||||||
|  |     name = "Cleargrass"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGD(TAG, "Got Xiaomi %s (%s):", name, device.address_str().c_str()); |   ESP_LOGD(TAG, "Got Xiaomi %s (%s):", name, device.address_str().c_str()); | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ namespace esphome { | |||||||
| namespace xiaomi_ble { | namespace xiaomi_ble { | ||||||
|  |  | ||||||
| struct XiaomiParseResult { | struct XiaomiParseResult { | ||||||
|   enum { TYPE_MIJIA, TYPE_MIFLORA, TYPE_LYWSD02 } type; |   enum { TYPE_MIJIA, TYPE_MIFLORA, TYPE_LYWSD02, TYPE_CLEARGRASS } type; | ||||||
|   optional<float> temperature; |   optional<float> temperature; | ||||||
|   optional<float> humidity; |   optional<float> humidity; | ||||||
|   optional<float> battery_level; |   optional<float> battery_level; | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/xiaomi_cleargrass/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/xiaomi_cleargrass/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										38
									
								
								esphome/components/xiaomi_cleargrass/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								esphome/components/xiaomi_cleargrass/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor, esp32_ble_tracker | ||||||
|  | from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ | ||||||
|  |     UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ['esp32_ble_tracker'] | ||||||
|  | AUTO_LOAD = ['xiaomi_ble'] | ||||||
|  |  | ||||||
|  | xiaomi_cleargrass_ns = cg.esphome_ns.namespace('xiaomi_cleargrass') | ||||||
|  | XiaomiCleargrass = xiaomi_cleargrass_ns.class_( | ||||||
|  |     'XiaomiCleargrass', esp32_ble_tracker.ESPBTDeviceListener, cg.Component) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(XiaomiCleargrass), | ||||||
|  |     cv.Required(CONF_MAC_ADDRESS): cv.mac_address, | ||||||
|  |     cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), | ||||||
|  |     cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), | ||||||
|  |     cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), | ||||||
|  | }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     yield cg.register_component(var, config) | ||||||
|  |     yield esp32_ble_tracker.register_ble_device(var, config) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) | ||||||
|  |  | ||||||
|  |     if CONF_TEMPERATURE in config: | ||||||
|  |         sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) | ||||||
|  |         cg.add(var.set_temperature(sens)) | ||||||
|  |     if CONF_HUMIDITY in config: | ||||||
|  |         sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) | ||||||
|  |         cg.add(var.set_humidity(sens)) | ||||||
|  |     if CONF_BATTERY_LEVEL in config: | ||||||
|  |         sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL]) | ||||||
|  |         cg.add(var.set_battery_level(sens)) | ||||||
							
								
								
									
										21
									
								
								esphome/components/xiaomi_cleargrass/xiaomi_cleargrass.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								esphome/components/xiaomi_cleargrass/xiaomi_cleargrass.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | #include "xiaomi_cleargrass.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace xiaomi_cleargrass { | ||||||
|  |  | ||||||
|  | static const char *TAG = "xiaomi_cleargrass"; | ||||||
|  |  | ||||||
|  | void XiaomiCleargrass::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Xiaomi Cleargrass"); | ||||||
|  |   LOG_SENSOR("  ", "Temperature", this->temperature_); | ||||||
|  |   LOG_SENSOR("  ", "Humidity", this->humidity_); | ||||||
|  |   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace xiaomi_cleargrass | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										50
									
								
								esphome/components/xiaomi_cleargrass/xiaomi_cleargrass.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								esphome/components/xiaomi_cleargrass/xiaomi_cleargrass.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
|  | #include "esphome/components/xiaomi_ble/xiaomi_ble.h" | ||||||
|  |  | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace xiaomi_cleargrass { | ||||||
|  |  | ||||||
|  | class XiaomiCleargrass : public Component, public esp32_ble_tracker::ESPBTDeviceListener { | ||||||
|  |  public: | ||||||
|  |   void set_address(uint64_t address) { address_ = address; } | ||||||
|  |  | ||||||
|  |   bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { | ||||||
|  |     if (device.address_uint64() != this->address_) | ||||||
|  |       return false; | ||||||
|  |  | ||||||
|  |     auto res = xiaomi_ble::parse_xiaomi(device); | ||||||
|  |     if (!res.has_value()) | ||||||
|  |       return false; | ||||||
|  |  | ||||||
|  |     if (res->temperature.has_value() && this->temperature_ != nullptr) | ||||||
|  |       this->temperature_->publish_state(*res->temperature); | ||||||
|  |     if (res->humidity.has_value() && this->humidity_ != nullptr) | ||||||
|  |       this->humidity_->publish_state(*res->humidity); | ||||||
|  |     if (res->battery_level.has_value() && this->battery_level_ != nullptr) | ||||||
|  |       this->battery_level_->publish_state(*res->battery_level); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |   void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } | ||||||
|  |   void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } | ||||||
|  |   void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint64_t address_; | ||||||
|  |   sensor::Sensor *temperature_{nullptr}; | ||||||
|  |   sensor::Sensor *humidity_{nullptr}; | ||||||
|  |   sensor::Sensor *battery_level_{nullptr}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace xiaomi_cleargrass | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
		Reference in New Issue
	
	Block a user