mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01:00
Feature/add seeed grove gmxxx multichannel gas support (#4304)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
81f6750211
commit
03a95ee05f
11 changed files with 245 additions and 0 deletions
|
@ -162,6 +162,7 @@ esphome/components/gps/* @coogle
|
||||||
esphome/components/graph/* @synco
|
esphome/components/graph/* @synco
|
||||||
esphome/components/graphical_display_menu/* @MrMDavidson
|
esphome/components/graphical_display_menu/* @MrMDavidson
|
||||||
esphome/components/gree/* @orestismers
|
esphome/components/gree/* @orestismers
|
||||||
|
esphome/components/grove_gas_mc_v2/* @YorkshireIoT
|
||||||
esphome/components/grove_tb6612fng/* @max246
|
esphome/components/grove_tb6612fng/* @max246
|
||||||
esphome/components/growatt_solar/* @leeuwte
|
esphome/components/growatt_solar/* @leeuwte
|
||||||
esphome/components/gt911/* @clydebarrow @jesserockz
|
esphome/components/gt911/* @clydebarrow @jesserockz
|
||||||
|
|
0
esphome/components/grove_gas_mc_v2/__init__.py
Normal file
0
esphome/components/grove_gas_mc_v2/__init__.py
Normal file
88
esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp
Normal file
88
esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include "grove_gas_mc_v2.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace grove_gas_mc_v2 {
|
||||||
|
|
||||||
|
static const char *const TAG = "grove_gas_mc_v2";
|
||||||
|
|
||||||
|
// I2C Commands for Grove Gas Multichannel V2 Sensor
|
||||||
|
// Taken from:
|
||||||
|
// https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/blob/master/src/Multichannel_Gas_GroveGasMultichannelV2.h
|
||||||
|
static const uint8_t GROVE_GAS_MC_V2_HEAT_ON = 0xFE;
|
||||||
|
static const uint8_t GROVE_GAS_MC_V2_HEAT_OFF = 0xFF;
|
||||||
|
static const uint8_t GROVE_GAS_MC_V2_READ_GM102B = 0x01;
|
||||||
|
static const uint8_t GROVE_GAS_MC_V2_READ_GM302B = 0x03;
|
||||||
|
static const uint8_t GROVE_GAS_MC_V2_READ_GM502B = 0x05;
|
||||||
|
static const uint8_t GROVE_GAS_MC_V2_READ_GM702B = 0x07;
|
||||||
|
|
||||||
|
bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sensor *sensor) {
|
||||||
|
if (sensor == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32_t value = 0;
|
||||||
|
if (!this->read_bytes(address, (uint8_t *) &value, 4)) {
|
||||||
|
ESP_LOGW(TAG, "Reading Grove Gas Sensor data failed!");
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->status_set_warning();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sensor->publish_state(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroveGasMultichannelV2Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up Grove Multichannel Gas Sensor V2...");
|
||||||
|
|
||||||
|
// Before reading sensor values, must preheat sensor
|
||||||
|
if (!(this->write_bytes(GROVE_GAS_MC_V2_HEAT_ON, {}))) {
|
||||||
|
this->mark_failed();
|
||||||
|
this->error_code_ = APP_START_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroveGasMultichannelV2Component::update() {
|
||||||
|
// Read from each of the gas sensors
|
||||||
|
if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM102B, this->nitrogen_dioxide_sensor_))
|
||||||
|
return;
|
||||||
|
if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM302B, this->ethanol_sensor_))
|
||||||
|
return;
|
||||||
|
if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM502B, this->tvoc_sensor_))
|
||||||
|
return;
|
||||||
|
if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM702B, this->carbon_monoxide_sensor_))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroveGasMultichannelV2Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2");
|
||||||
|
LOG_I2C_DEVICE(this)
|
||||||
|
LOG_UPDATE_INTERVAL(this)
|
||||||
|
LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_)
|
||||||
|
LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_)
|
||||||
|
LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_)
|
||||||
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_)
|
||||||
|
|
||||||
|
if (this->is_failed()) {
|
||||||
|
switch (this->error_code_) {
|
||||||
|
case COMMUNICATION_FAILED:
|
||||||
|
ESP_LOGW(TAG, "Communication failed! Is the sensor connected?");
|
||||||
|
break;
|
||||||
|
case APP_INVALID:
|
||||||
|
ESP_LOGW(TAG, "Sensor reported invalid APP installed.");
|
||||||
|
break;
|
||||||
|
case APP_START_FAILED:
|
||||||
|
ESP_LOGW(TAG, "Sensor reported APP start failed.");
|
||||||
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
default:
|
||||||
|
ESP_LOGW(TAG, "Unknown setup error!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace grove_gas_mc_v2
|
||||||
|
} // namespace esphome
|
39
esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h
Normal file
39
esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace grove_gas_mc_v2 {
|
||||||
|
|
||||||
|
class GroveGasMultichannelV2Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
SUB_SENSOR(tvoc)
|
||||||
|
SUB_SENSOR(carbon_monoxide)
|
||||||
|
SUB_SENSOR(nitrogen_dioxide)
|
||||||
|
SUB_SENSOR(ethanol)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Setup the sensor and test for a connection.
|
||||||
|
void setup() override;
|
||||||
|
/// Schedule temperature+pressure readings.
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum ErrorCode {
|
||||||
|
UNKNOWN,
|
||||||
|
COMMUNICATION_FAILED,
|
||||||
|
APP_INVALID,
|
||||||
|
APP_START_FAILED,
|
||||||
|
} error_code_{UNKNOWN};
|
||||||
|
|
||||||
|
bool read_sensor_(uint8_t address, sensor::Sensor *sensor);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace grove_gas_mc_v2
|
||||||
|
} // namespace esphome
|
77
esphome/components/grove_gas_mc_v2/sensor.py
Normal file
77
esphome/components/grove_gas_mc_v2/sensor.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_CARBON_MONOXIDE,
|
||||||
|
CONF_ETHANOL,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_NITROGEN_DIOXIDE,
|
||||||
|
CONF_TVOC,
|
||||||
|
DEVICE_CLASS_CARBON_MONOXIDE,
|
||||||
|
DEVICE_CLASS_NITROGEN_DIOXIDE,
|
||||||
|
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||||
|
ICON_AIR_FILTER,
|
||||||
|
ICON_FLASK_ROUND_BOTTOM,
|
||||||
|
ICON_GAS_CYLINDER,
|
||||||
|
ICON_MOLECULE_CO,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
UNIT_PARTS_PER_MILLION,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@YorkshireIoT"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
grove_gas_mc_v2_ns = cg.esphome_ns.namespace("grove_gas_mc_v2")
|
||||||
|
|
||||||
|
GroveGasMultichannelV2Component = grove_gas_mc_v2_ns.class_(
|
||||||
|
"GroveGasMultichannelV2Component", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(GroveGasMultichannelV2Component),
|
||||||
|
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
icon=ICON_AIR_FILTER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CARBON_MONOXIDE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||||
|
icon=ICON_MOLECULE_CO,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_CARBON_MONOXIDE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
icon=ICON_GAS_CYLINDER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_NITROGEN_DIOXIDE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ETHANOL): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||||
|
icon=ICON_FLASK_ROUND_BOTTOM,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x08))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
for key in [CONF_TVOC, CONF_CARBON_MONOXIDE, CONF_NITROGEN_DIOXIDE, CONF_ETHANOL]:
|
||||||
|
if sensor_config := config.get(key):
|
||||||
|
sensor_ = await sensor.new_sensor(sensor_config)
|
||||||
|
cg.add(getattr(var, f"set_{key}_sensor")(sensor_))
|
|
@ -962,6 +962,7 @@ ICON_ACCELERATION_Y = "mdi:axis-y-arrow"
|
||||||
ICON_ACCELERATION_Z = "mdi:axis-z-arrow"
|
ICON_ACCELERATION_Z = "mdi:axis-z-arrow"
|
||||||
ICON_ACCOUNT = "mdi:account"
|
ICON_ACCOUNT = "mdi:account"
|
||||||
ICON_ACCOUNT_CHECK = "mdi:account-check"
|
ICON_ACCOUNT_CHECK = "mdi:account-check"
|
||||||
|
ICON_AIR_FILTER = "mdi:air-filter"
|
||||||
ICON_ARROW_EXPAND_VERTICAL = "mdi:arrow-expand-vertical"
|
ICON_ARROW_EXPAND_VERTICAL = "mdi:arrow-expand-vertical"
|
||||||
ICON_BATTERY = "mdi:battery"
|
ICON_BATTERY = "mdi:battery"
|
||||||
ICON_BLUETOOTH = "mdi:bluetooth"
|
ICON_BLUETOOTH = "mdi:bluetooth"
|
||||||
|
@ -983,6 +984,7 @@ ICON_FINGERPRINT = "mdi:fingerprint"
|
||||||
ICON_FLASH = "mdi:flash"
|
ICON_FLASH = "mdi:flash"
|
||||||
ICON_FLASK = "mdi:flask"
|
ICON_FLASK = "mdi:flask"
|
||||||
ICON_FLASK_OUTLINE = "mdi:flask-outline"
|
ICON_FLASK_OUTLINE = "mdi:flask-outline"
|
||||||
|
ICON_FLASK_ROUND_BOTTOM = "mdi:flask-round-bottom"
|
||||||
ICON_FLOWER = "mdi:flower"
|
ICON_FLOWER = "mdi:flower"
|
||||||
ICON_GAS_CYLINDER = "mdi:gas-cylinder"
|
ICON_GAS_CYLINDER = "mdi:gas-cylinder"
|
||||||
ICON_GAUGE = "mdi:gauge"
|
ICON_GAUGE = "mdi:gauge"
|
||||||
|
@ -995,6 +997,7 @@ ICON_KEY_PLUS = "mdi:key-plus"
|
||||||
ICON_LIGHTBULB = "mdi:lightbulb"
|
ICON_LIGHTBULB = "mdi:lightbulb"
|
||||||
ICON_MAGNET = "mdi:magnet"
|
ICON_MAGNET = "mdi:magnet"
|
||||||
ICON_MEMORY = "mdi:memory"
|
ICON_MEMORY = "mdi:memory"
|
||||||
|
ICON_MOLECULE_CO = "mdi:molecule-co"
|
||||||
ICON_MOLECULE_CO2 = "mdi:molecule-co2"
|
ICON_MOLECULE_CO2 = "mdi:molecule-co2"
|
||||||
ICON_MOTION_SENSOR = "mdi:motion-sensor"
|
ICON_MOTION_SENSOR = "mdi:motion-sensor"
|
||||||
ICON_NEW_BOX = "mdi:new-box"
|
ICON_NEW_BOX = "mdi:new-box"
|
||||||
|
|
13
tests/components/grove_gas_mc_v2/common.yaml
Normal file
13
tests/components/grove_gas_mc_v2/common.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
sensor:
|
||||||
|
- platform: grove_gas_mc_v2
|
||||||
|
i2c_id: i2c_bus
|
||||||
|
nitrogen_dioxide:
|
||||||
|
name: "Nitrogen Dioxide"
|
||||||
|
ethanol:
|
||||||
|
name: "Ethanol"
|
||||||
|
carbon_monoxide:
|
||||||
|
name: "Carbon Monoxide"
|
||||||
|
tvoc:
|
||||||
|
name: "Volatile Organic Compounds"
|
||||||
|
update_interval: 30s
|
||||||
|
address: 0xAD
|
6
tests/components/grove_gas_mc_v2/test.esp32-ard.yaml
Normal file
6
tests/components/grove_gas_mc_v2/test.esp32-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
sda: 21
|
||||||
|
scl: 22
|
||||||
|
id: i2c_bus
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/grove_gas_mc_v2/test.esp32-idf.yaml
Normal file
6
tests/components/grove_gas_mc_v2/test.esp32-idf.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
sda: 21
|
||||||
|
scl: 22
|
||||||
|
id: i2c_bus
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml
Normal file
6
tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
sda: 4
|
||||||
|
scl: 5
|
||||||
|
id: i2c_bus
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml
Normal file
6
tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
sda: 21
|
||||||
|
scl: 22
|
||||||
|
id: i2c_bus
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
Loading…
Reference in a new issue