diff --git a/CODEOWNERS b/CODEOWNERS index 34ac0a7aa9..3592e448cf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -246,6 +246,7 @@ esphome/components/switch/* @esphome/core esphome/components/t6615/* @tylermenezes esphome/components/tca9548a/* @andreashergert1984 esphome/components/tcl112/* @glmnet +esphome/components/tee501/* @Stock-M esphome/components/teleinfo/* @0hax esphome/components/thermostat/* @kbx81 esphome/components/time/* @OttoWinter diff --git a/esphome/components/tee501/__init__.py b/esphome/components/tee501/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/tee501/sensor.py b/esphome/components/tee501/sensor.py new file mode 100644 index 0000000000..329fc724bd --- /dev/null +++ b/esphome/components/tee501/sensor.py @@ -0,0 +1,36 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +CODEOWNERS = ["@Stock-M"] + +DEPENDENCIES = ["i2c"] + +tee501_ns = cg.esphome_ns.namespace("tee501") + +TEE501Component = tee501_ns.class_( + "TEE501Component", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + TEE501Component, + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x48)) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) diff --git a/esphome/components/tee501/tee501.cpp b/esphome/components/tee501/tee501.cpp new file mode 100644 index 0000000000..22329d40cd --- /dev/null +++ b/esphome/components/tee501/tee501.cpp @@ -0,0 +1,85 @@ +#include "tee501.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tee501 { + +static const char *const TAG = "tee501"; + +void TEE501Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up TEE501..."); + uint8_t address[] = {0x70, 0x29}; + this->write(address, 2, false); + uint8_t identification[9]; + this->read(identification, 9); + if (identification[8] != calc_crc8_(identification, 0, 7)) { + this->error_code_ = CRC_CHECK_FAILED; + this->mark_failed(); + return; + } + ESP_LOGV(TAG, " Serial Number: 0x%s", format_hex(identification + 0, 7).c_str()); +} + +void TEE501Component::dump_config() { + ESP_LOGCONFIG(TAG, "TEE501:"); + LOG_I2C_DEVICE(this); + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGE(TAG, "Communication with TEE501 failed!"); + break; + case CRC_CHECK_FAILED: + ESP_LOGE(TAG, "The crc check failed"); + break; + case NONE: + default: + break; + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "TEE501", this); +} + +float TEE501Component::get_setup_priority() const { return setup_priority::DATA; } +void TEE501Component::update() { + uint8_t address_1[] = {0x2C, 0x1B}; + this->write(address_1, 2, true); + this->set_timeout(50, [this]() { + uint8_t i2c_response[3]; + this->read(i2c_response, 3); + if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1)) { + this->error_code_ = CRC_CHECK_FAILED; + this->status_set_warning(); + return; + } + float temperature = (float) encode_uint16(i2c_response[0], i2c_response[1]); + if (temperature > 55536) { + temperature = (temperature - 65536) / 100; + } else { + temperature = temperature / 100; + } + ESP_LOGD(TAG, "Got temperature=%.2f°C", temperature); + this->publish_state(temperature); + this->status_clear_warning(); + }); +} + +unsigned char TEE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) { + unsigned char crc_val = 0xFF; + unsigned char i = 0; + unsigned char j = 0; + for (i = from; i <= to; i++) { + int cur_val = buf[i]; + for (j = 0; j < 8; j++) { + if (((crc_val ^ cur_val) & 0x80) != 0) // If MSBs are not equal + { + crc_val = ((crc_val << 1) ^ 0x31); + } else { + crc_val = (crc_val << 1); + } + cur_val = cur_val << 1; + } + } + return crc_val; +} + +} // namespace tee501 +} // namespace esphome diff --git a/esphome/components/tee501/tee501.h b/esphome/components/tee501/tee501.h new file mode 100644 index 0000000000..fc655e58c9 --- /dev/null +++ b/esphome/components/tee501/tee501.h @@ -0,0 +1,25 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace tee501 { + +/// This class implements support for the tee501 of temperature i2c sensors. +class TEE501Component : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + + protected: + unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to); + + enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE}; +}; + +} // namespace tee501 +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index a26c24d7fa..0448093001 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1056,6 +1056,10 @@ sensor: name: tsl2591 calculated_lux id: tsl2591_cl i2c_id: i2c_bus + - platform: tee501 + name: Office Temperature 3 + address: 0x48 + i2c_id: i2c_bus - platform: ultrasonic trigger_pin: GPIO25 echo_pin: