Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support of Honeywell HumidIcon (I2C HIH series) Temperature & Humidity sensor #5730

Merged
merged 14 commits into from
Jan 18, 2024
Merged
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/hm3301/* @freekode
esphome/components/homeassistant/* @OttoWinter
esphome/components/honeywell_hih_i2c/* @Benichou34
esphome/components/honeywellabp/* @RubyBailey
esphome/components/honeywellabp2_i2c/* @jpfaff
esphome/components/host/* @esphome/core
Expand Down
2 changes: 2 additions & 0 deletions esphome/components/honeywell_hih_i2c/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""Support for Honeywell HumidIcon HIH"""
CODEOWNERS = ["@Benichou34"]
97 changes: 97 additions & 0 deletions esphome/components/honeywell_hih_i2c/honeywell_hih.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Honeywell HumidIcon I2C Sensors
// https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/humidity-with-temperature-sensors/common/documents/sps-siot-i2c-comms-humidicon-tn-009061-2-en-ciid-142171.pdf
//

#include "honeywell_hih.h"
#include "esphome/core/log.h"

namespace esphome {
namespace honeywell_hih_i2c {

static const char *const TAG = "honeywell_hih.i2c";

static const uint8_t REQUEST_CMD[1] = {0x00}; // Measurement Request Format
static const uint16_t MAX_COUNT = 0x3FFE; // 2^14 - 2

void HoneywellHIComponent::read_sensor_data_() {
uint8_t data[4];

if (this->read(data, sizeof(data)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with Honeywell HIH failed!");
this->mark_failed();
return;
}

const uint16_t raw_humidity = (static_cast<uint16_t>(data[0] & 0x3F) << 8) | data[1];
float humidity = (static_cast<float>(raw_humidity) / MAX_COUNT) * 100;

const uint16_t raw_temperature = (static_cast<uint16_t>(data[2]) << 6) | (data[3] >> 2);
float temperature = (static_cast<float>(raw_temperature) / MAX_COUNT) * 165 - 40;

ESP_LOGD(TAG, "Got temperature=%.2f°C humidity=%.2f%%", temperature, humidity);
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
if (this->humidity_sensor_ != nullptr)
this->humidity_sensor_->publish_state(humidity);
}

void HoneywellHIComponent::start_measurement_() {
if (this->write(REQUEST_CMD, sizeof(REQUEST_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with Honeywell HIH failed!");
this->mark_failed();
return;
}

this->measurement_running_ = true;
}

bool HoneywellHIComponent::is_measurement_ready_() {
uint8_t data[1];

if (this->read(data, sizeof(data)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with Honeywell HIH failed!");
this->mark_failed();
return false;
}

// Check status bits
return ((data[0] & 0xC0) == 0x00);
}

void HoneywellHIComponent::measurement_timeout_() {
ESP_LOGE(TAG, "Honeywell HIH Timeout!");
this->measurement_running_ = false;
this->mark_failed();
Benichou34 marked this conversation as resolved.
Show resolved Hide resolved
}

void HoneywellHIComponent::update() {
ESP_LOGV(TAG, "Update Honeywell HIH Sensor");

this->start_measurement_();
// The measurement cycle duration is typically 36.65 ms for temperature and humidity readings.
this->set_timeout("meas_timeout", 100, [this] { this->measurement_timeout_(); });
}

void HoneywellHIComponent::loop() {
if (this->measurement_running_ && this->is_measurement_ready_()) {
this->measurement_running_ = false;
this->cancel_timeout("meas_timeout");
this->read_sensor_data_();
}
}

void HoneywellHIComponent::dump_config() {
ESP_LOGD(TAG, "Honeywell HIH:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with Honeywell HIH failed!");
}
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
LOG_UPDATE_INTERVAL(this);
}

float HoneywellHIComponent::get_setup_priority() const { return setup_priority::DATA; }

} // namespace honeywell_hih_i2c
} // namespace esphome
34 changes: 34 additions & 0 deletions esphome/components/honeywell_hih_i2c/honeywell_hih.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Honeywell HumidIcon I2C Sensors
#pragma once

#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"

namespace esphome {
namespace honeywell_hih_i2c {

class HoneywellHIComponent : public PollingComponent, public i2c::I2CDevice {
public:
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
void update() override;

void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }

protected:
bool measurement_running_{false};
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};

private:
void read_sensor_data_();
void start_measurement_();
bool is_measurement_ready_();
void measurement_timeout_();
};

} // namespace honeywell_hih_i2c
} // namespace esphome
56 changes: 56 additions & 0 deletions esphome/components/honeywell_hih_i2c/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
)

DEPENDENCIES = ["i2c"]

honeywell_hih_ns = cg.esphome_ns.namespace("honeywell_hih_i2c")
HONEYWELLHIComponent = honeywell_hih_ns.class_(
"HoneywellHIComponent", cg.PollingComponent, i2c.I2CDevice
)

CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(HONEYWELLHIComponent),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x27))
)


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)

if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))

if humidity_config := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity_config)
cg.add(var.set_humidity_sensor(sens))
7 changes: 7 additions & 0 deletions tests/test1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,13 @@ sensor:
oversampling: 8x
update_interval: 15s
i2c_id: i2c_bus
- platform: honeywell_hih_i2c
temperature:
name: Living Room Temperature 7
humidity:
name: Living Room Humidity 7
update_interval: 15s
i2c_id: i2c_bus
- platform: honeywellabp
pressure:
name: Honeywell pressure
Expand Down