mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Add support for Airthing Wave Mini (#2440)
This commit is contained in:
		| @@ -15,6 +15,7 @@ esphome/components/ac_dimmer/* @glmnet | |||||||
| esphome/components/adc/* @esphome/core | esphome/components/adc/* @esphome/core | ||||||
| esphome/components/addressable_light/* @justfalter | esphome/components/addressable_light/* @justfalter | ||||||
| esphome/components/airthings_ble/* @jeromelaban | esphome/components/airthings_ble/* @jeromelaban | ||||||
|  | esphome/components/airthings_wave_mini/* @ncareau | ||||||
| esphome/components/airthings_wave_plus/* @jeromelaban | esphome/components/airthings_wave_plus/* @jeromelaban | ||||||
| esphome/components/am43/* @buxtronix | esphome/components/am43/* @buxtronix | ||||||
| esphome/components/am43/cover/* @buxtronix | esphome/components/am43/cover/* @buxtronix | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								esphome/components/airthings_wave_mini/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/airthings_wave_mini/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | CODEOWNERS = ["@ncareau"] | ||||||
							
								
								
									
										120
									
								
								esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | #include "airthings_wave_mini.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32_FRAMEWORK_ARDUINO | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace airthings_wave_mini { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "airthings_wave_mini"; | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|  |                                             esp_ble_gattc_cb_param_t *param) { | ||||||
|  |   switch (event) { | ||||||
|  |     case ESP_GATTC_OPEN_EVT: { | ||||||
|  |       if (param->open.status == ESP_GATT_OK) { | ||||||
|  |         ESP_LOGI(TAG, "Connected successfully!"); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case ESP_GATTC_DISCONNECT_EVT: { | ||||||
|  |       ESP_LOGW(TAG, "Disconnected!"); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case ESP_GATTC_SEARCH_CMPL_EVT: { | ||||||
|  |       this->handle_ = 0; | ||||||
|  |       auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); | ||||||
|  |       if (chr == nullptr) { | ||||||
|  |         ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), | ||||||
|  |                  sensors_data_characteristic_uuid_.to_string().c_str()); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       this->handle_ = chr->handle; | ||||||
|  |       this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED; | ||||||
|  |  | ||||||
|  |       request_read_values_(); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case ESP_GATTC_READ_CHAR_EVT: { | ||||||
|  |       if (param->read.conn_id != this->parent()->conn_id) | ||||||
|  |         break; | ||||||
|  |       if (param->read.status != ESP_GATT_OK) { | ||||||
|  |         ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       if (param->read.handle == this->handle_) { | ||||||
|  |         read_sensors_(param->read.value, param->read.value_len); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) { | ||||||
|  |   auto value = (WaveMiniReadings *) raw_value; | ||||||
|  |  | ||||||
|  |   if (sizeof(WaveMiniReadings) <= value_len) { | ||||||
|  |     this->humidity_sensor_->publish_state(value->humidity / 100.0f); | ||||||
|  |     this->pressure_sensor_->publish_state(value->pressure / 50.0f); | ||||||
|  |     this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f); | ||||||
|  |     if (is_valid_voc_value_(value->voc)) { | ||||||
|  |       this->tvoc_sensor_->publish_state(value->voc); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // This instance must not stay connected | ||||||
|  |     // so other clients can connect to it (e.g. the | ||||||
|  |     // mobile app). | ||||||
|  |     parent()->set_enabled(false); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::loop() {} | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::update() { | ||||||
|  |   if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { | ||||||
|  |     if (!parent()->enabled) { | ||||||
|  |       ESP_LOGW(TAG, "Reconnecting to device"); | ||||||
|  |       parent()->set_enabled(true); | ||||||
|  |       parent()->connect(); | ||||||
|  |     } else { | ||||||
|  |       ESP_LOGW(TAG, "Connection in progress"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::request_read_values_() { | ||||||
|  |   auto status = | ||||||
|  |       esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE); | ||||||
|  |   if (status) { | ||||||
|  |     ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::dump_config() { | ||||||
|  |   LOG_SENSOR("  ", "Humidity", this->humidity_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Pressure", this->pressure_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "TVOC", this->tvoc_sensor_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AirthingsWaveMini::AirthingsWaveMini() : PollingComponent(10000) { | ||||||
|  |   auto service_bt = *BLEUUID::fromString(std::string("b42e3882-ade7-11e4-89d3-123b93f75cba")).getNative(); | ||||||
|  |   auto characteristic_bt = *BLEUUID::fromString(std::string("b42e3b98-ade7-11e4-89d3-123b93f75cba")).getNative(); | ||||||
|  |  | ||||||
|  |   service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(service_bt); | ||||||
|  |   sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(characteristic_bt); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AirthingsWaveMini::setup() {} | ||||||
|  |  | ||||||
|  | }  // namespace airthings_wave_mini | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif  // USE_ESP32_FRAMEWORK_ARDUINO | ||||||
							
								
								
									
										65
									
								
								esphome/components/airthings_wave_mini/airthings_wave_mini.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								esphome/components/airthings_wave_mini/airthings_wave_mini.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32_FRAMEWORK_ARDUINO | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <iterator> | ||||||
|  | #include <esp_gattc_api.h> | ||||||
|  | #include <BLEDevice.h> | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/components/ble_client/ble_client.h" | ||||||
|  | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace airthings_wave_mini { | ||||||
|  |  | ||||||
|  | class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode { | ||||||
|  |  public: | ||||||
|  |   AirthingsWaveMini(); | ||||||
|  |  | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   void update() override; | ||||||
|  |   void loop() override; | ||||||
|  |  | ||||||
|  |   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|  |                            esp_ble_gattc_cb_param_t *param) override; | ||||||
|  |  | ||||||
|  |   void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } | ||||||
|  |   void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } | ||||||
|  |   void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; } | ||||||
|  |   void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool is_valid_voc_value_(uint16_t voc); | ||||||
|  |  | ||||||
|  |   void read_sensors_(uint8_t *value, uint16_t value_len); | ||||||
|  |   void request_read_values_(); | ||||||
|  |  | ||||||
|  |   sensor::Sensor *temperature_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *humidity_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *pressure_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *tvoc_sensor_{nullptr}; | ||||||
|  |  | ||||||
|  |   uint16_t handle_; | ||||||
|  |   esp32_ble_tracker::ESPBTUUID service_uuid_; | ||||||
|  |   esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_; | ||||||
|  |  | ||||||
|  |   struct WaveMiniReadings { | ||||||
|  |     uint16_t unused01; | ||||||
|  |     uint16_t temperature; | ||||||
|  |     uint16_t pressure; | ||||||
|  |     uint16_t humidity; | ||||||
|  |     uint16_t voc; | ||||||
|  |     uint16_t unused02; | ||||||
|  |     uint32_t unused03; | ||||||
|  |     uint32_t unused04; | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace airthings_wave_mini | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif  // USE_ESP32_FRAMEWORK_ARDUINO | ||||||
							
								
								
									
										88
									
								
								esphome/components/airthings_wave_mini/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								esphome/components/airthings_wave_mini/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor, ble_client | ||||||
|  | from esphome.core import CORE | ||||||
|  |  | ||||||
|  | from esphome.const import ( | ||||||
|  |     DEVICE_CLASS_HUMIDITY, | ||||||
|  |     DEVICE_CLASS_TEMPERATURE, | ||||||
|  |     DEVICE_CLASS_PRESSURE, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_PERCENT, | ||||||
|  |     UNIT_CELSIUS, | ||||||
|  |     UNIT_HECTOPASCAL, | ||||||
|  |     CONF_ID, | ||||||
|  |     CONF_HUMIDITY, | ||||||
|  |     CONF_TVOC, | ||||||
|  |     CONF_PRESSURE, | ||||||
|  |     CONF_TEMPERATURE, | ||||||
|  |     UNIT_PARTS_PER_BILLION, | ||||||
|  |     ICON_RADIATOR, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["ble_client"] | ||||||
|  |  | ||||||
|  | airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini") | ||||||
|  | AirthingsWaveMini = airthings_wave_mini_ns.class_( | ||||||
|  |     "AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(AirthingsWaveMini), | ||||||
|  |             cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||||||
|  |                 unit_of_measurement=UNIT_PERCENT, | ||||||
|  |                 device_class=DEVICE_CLASS_HUMIDITY, | ||||||
|  |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |                 accuracy_decimals=2, | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||||
|  |                 unit_of_measurement=UNIT_CELSIUS, | ||||||
|  |                 accuracy_decimals=2, | ||||||
|  |                 device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|  |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||||
|  |                 unit_of_measurement=UNIT_HECTOPASCAL, | ||||||
|  |                 accuracy_decimals=2, | ||||||
|  |                 device_class=DEVICE_CLASS_PRESSURE, | ||||||
|  |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_TVOC): sensor.sensor_schema( | ||||||
|  |                 unit_of_measurement=UNIT_PARTS_PER_BILLION, | ||||||
|  |                 icon=ICON_RADIATOR, | ||||||
|  |                 accuracy_decimals=0, | ||||||
|  |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             ), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(cv.polling_component_schema("5mins")) | ||||||
|  |     .extend(ble_client.BLE_CLIENT_SCHEMA), | ||||||
|  |     # Until BLEUUID reference removed | ||||||
|  |     cv.only_with_arduino, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|  |     await ble_client.register_ble_node(var, config) | ||||||
|  |  | ||||||
|  |     if CONF_HUMIDITY in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_HUMIDITY]) | ||||||
|  |         cg.add(var.set_humidity(sens)) | ||||||
|  |     if CONF_TEMPERATURE in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) | ||||||
|  |         cg.add(var.set_temperature(sens)) | ||||||
|  |     if CONF_PRESSURE in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_PRESSURE]) | ||||||
|  |         cg.add(var.set_pressure(sens)) | ||||||
|  |     if CONF_TVOC in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_TVOC]) | ||||||
|  |         cg.add(var.set_tvoc(sens)) | ||||||
|  |  | ||||||
|  |     if CORE.is_esp32: | ||||||
|  |         cg.add_library("ESP32 BLE Arduino", None) | ||||||
| @@ -281,6 +281,17 @@ sensor: | |||||||
|       name: "Wave Plus CO2" |       name: "Wave Plus CO2" | ||||||
|     tvoc: |     tvoc: | ||||||
|       name: "Wave Plus VOC" |       name: "Wave Plus VOC" | ||||||
|  |   - platform: airthings_wave_mini | ||||||
|  |     ble_client_id: airthingsmini01 | ||||||
|  |     update_interval: 5min | ||||||
|  |     temperature: | ||||||
|  |       name: "Wave Mini Temperature" | ||||||
|  |     humidity: | ||||||
|  |       name: "Wave Mini Humidity" | ||||||
|  |     pressure: | ||||||
|  |       name: "Wave Mini Pressure" | ||||||
|  |     tvoc: | ||||||
|  |       name: "Wave Mini VOC" | ||||||
|  |  | ||||||
| time: | time: | ||||||
|   - platform: homeassistant |   - platform: homeassistant | ||||||
| @@ -378,6 +389,9 @@ esp32_ble_tracker: | |||||||
| ble_client: | ble_client: | ||||||
|   - mac_address: 01:02:03:04:05:06 |   - mac_address: 01:02:03:04:05:06 | ||||||
|     id: airthings01 |     id: airthings01 | ||||||
|  |   - mac_address: 01:02:03:04:05:06 | ||||||
|  |     id: airthingsmini01 | ||||||
|  |  | ||||||
|  |  | ||||||
| airthings_ble: | airthings_ble: | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user