diff --git a/.gitignore b/.gitignore index e9dad88..dc6c117 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ components/.DS_Store esp01-clim.yaml esp32-clim-chambres.yaml hp-sejour.yaml +Climate-only.yaml +esp32-clim-sejour+awox.yaml diff --git a/components/cn105/Globals.h b/components/cn105/Globals.h index 0183c63..8eabc43 100644 --- a/components/cn105/Globals.h +++ b/components/cn105/Globals.h @@ -12,7 +12,7 @@ #define MAX_DATA_BYTES 64 // max number of data bytes in incoming messages #define MAX_DELAY_RESPONSE_FACTOR 10 // update_interval*10 seconds max without response -#define TEST_MODE +//#define TEST_MODE static const char* LOG_ACTION_EVT_TAG = "EVT_SETS"; static const char* TAG = "CN105"; // Logging tag diff --git a/components/cn105/climate.py b/components/cn105/climate.py index d394049..e9b9871 100644 --- a/components/cn105/climate.py +++ b/components/cn105/climate.py @@ -12,6 +12,11 @@ CONF_FAN_MODE, CONF_SWING_MODE, CONF_UART_ID, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_TOTAL_INCREASING, + UNIT_SECOND, + ICON_TIMER, + DEVICE_CLASS_DURATION, ) from esphome.core import CORE, coroutine @@ -26,6 +31,7 @@ CONF_STAGE_SENSOR = "stage_sensor" CONF_SUB_MODE_SENSOR = "sub_mode_sensor" CONF_AUTO_SUB_MODE_SENSOR = "auto_sub_mode_sensor" +CONF_HP_UP_TIME_CONNECTION_SENSOR = "hp_uptime_connection_sensor" DEFAULT_CLIMATE_MODES = ["AUTO", "COOL", "HEAT", "DRY", "FAN_ONLY"] DEFAULT_FAN_MODES = ["AUTO", "MIDDLE", "QUIET", "LOW", "MEDIUM", "HIGH"] @@ -48,7 +54,14 @@ ISeeSensor = cg.global_ns.class_("ISeeSensor", binary_sensor.BinarySensor, cg.Component) StageSensor = cg.global_ns.class_("StageSensor", text_sensor.TextSensor, cg.Component) SubModSensor = cg.global_ns.class_("SubModSensor", text_sensor.TextSensor, cg.Component) -AutoSubModSensor = cg.global_ns.class_("AutoSubModSensor", text_sensor.TextSensor, cg.Component) +AutoSubModSensor = cg.global_ns.class_( + "AutoSubModSensor", text_sensor.TextSensor, cg.Component +) + +uptime_ns = cg.esphome_ns.namespace("uptime") +HpUpTimeConnectionSensor = uptime_ns.class_( + "HpUpTimeConnectionSensor", sensor.Sensor, cg.PollingComponent +) def valid_uart(uart): @@ -86,6 +99,17 @@ def valid_uart(uart): {cv.GenerateID(CONF_ID): cv.declare_id(AutoSubModSensor)} ) + +HP_UP_TIME_CONNECTION_SENSOR_SCHEMA = sensor.sensor_schema( + HpUpTimeConnectionSensor, + unit_of_measurement=UNIT_SECOND, + icon=ICON_TIMER, + accuracy_decimals=0, + state_class=STATE_CLASS_TOTAL_INCREASING, + device_class=DEVICE_CLASS_DURATION, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, +).extend(cv.polling_component_schema("60s")) + CONFIG_SCHEMA = climate.CLIMATE_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(CN105Climate), @@ -109,6 +133,9 @@ def valid_uart(uart): cv.update_interval ), cv.Optional(CONF_DEBOUNCE_DELAY, default="100ms"): cv.All(cv.update_interval), + cv.Optional( + CONF_HP_UP_TIME_CONNECTION_SENSOR + ): HP_UP_TIME_CONNECTION_SENSOR_SCHEMA, # Optionally override the supported ClimateTraits. cv.Optional(CONF_SUPPORTS, default={}): cv.Schema( { @@ -198,9 +225,16 @@ def to_code(config): yield cg.register_component(tsensor_, conf) cg.add(var.set_auto_sub_mode_sensor(tsensor_)) + if CONF_HP_UP_TIME_CONNECTION_SENSOR in config: + conf = config[CONF_HP_UP_TIME_CONNECTION_SENSOR] + hp_connection_sensor_ = yield sensor.new_sensor(conf) + yield cg.register_component(hp_connection_sensor_, conf) + cg.add(var.set_hp_uptime_connection_sensor(hp_connection_sensor_)) + yield cg.register_component(var, config) yield climate.register_climate(var, config) + ## cg.add_library( ## name="HeatPump", ## repository="https://github.com/SwiCago/HeatPump", diff --git a/components/cn105/cn105.cpp b/components/cn105/cn105.cpp index 33fd5d2..999263f 100644 --- a/components/cn105/cn105.cpp +++ b/components/cn105/cn105.cpp @@ -92,7 +92,7 @@ void CN105Climate::setupUART() { ESP_LOGI(TAG, "setupUART() with baudrate %d", this->parent_->get_baud_rate()); - this->isHeatpumpConnected_ = false; + this->setHeatpumpConnected(false); this->isUARTConnected_ = false; // just for debugging purpose, a way to use a button i, yaml to trigger a reconnect @@ -101,22 +101,33 @@ void CN105Climate::setupUART() { if (this->parent_->get_data_bits() == 8 && this->parent_->get_parity() == uart::UART_CONFIG_PARITY_EVEN && this->parent_->get_stop_bits() == 1) { - ESP_LOGD("CustomComponent", "UART est configuré en SERIAL_8E1"); + ESP_LOGD(TAG, "UART est configuré en SERIAL_8E1"); this->isUARTConnected_ = true; this->initBytePointer(); } else { - ESP_LOGW("CustomComponent", "UART n'est pas configuré en SERIAL_8E1"); + ESP_LOGW(TAG, "UART n'est pas configuré en SERIAL_8E1"); } } - +void CN105Climate::setHeatpumpConnected(bool state) { + this->isHeatpumpConnected_ = state; + if (this->hp_uptime_connection_sensor_ != nullptr) { + if (state) { + this->hp_uptime_connection_sensor_->start(); + ESP_LOGD(TAG, "starting hp_uptime_connection_sensor_ uptime chrono"); + } else { + this->hp_uptime_connection_sensor_->stop(); + ESP_LOGD(TAG, "stopping hp_uptime_connection_sensor_ uptime chrono"); + } + } +} void CN105Climate::disconnectUART() { ESP_LOGD(TAG, "disconnectUART()"); this->uart_setup_switch = false; - - this->isHeatpumpConnected_ = false; - this->isUARTConnected_ = false; + this->setHeatpumpConnected(false); + //this->isHeatpumpConnected_ = false; + //this->isUARTConnected_ = false; this->firstRun = true; this->publish_state(); @@ -144,6 +155,7 @@ void CN105Climate::reconnectIfConnectionLost() { } } + bool CN105Climate::isHeatpumpConnectionActive() { long lrTimeMs = CUSTOM_MILLIS - this->lastResponseMs; diff --git a/components/cn105/cn105.h b/components/cn105/cn105.h index 7590c2a..bef2487 100644 --- a/components/cn105/cn105.h +++ b/components/cn105/cn105.h @@ -2,6 +2,7 @@ #include "Globals.h" #include "heatpumpFunctions.h" #include "van_orientation_select.h" +#include "uptime_connection_sensor.h" #include "compressor_frequency_sensor.h" #include "auto_sub_mode_sensor.h" #include "isee_sensor.h" @@ -34,6 +35,7 @@ class CN105Climate : public climate::Climate, public Component, public uart::UAR void set_stage_sensor(esphome::text_sensor::TextSensor* Stage_sensor); void set_sub_mode_sensor(esphome::text_sensor::TextSensor* Sub_mode_sensor); void set_auto_sub_mode_sensor(esphome::text_sensor::TextSensor* Auto_sub_mode_sensor); + void set_hp_uptime_connection_sensor(uptime::HpUpTimeConnectionSensor* hp_up_connection_sensor); //sensor::Sensor* compressor_frequency_sensor; binary_sensor::BinarySensor* iSee_sensor_ = nullptr; @@ -51,6 +53,8 @@ class CN105Climate : public climate::Climate, public Component, public uart::UAR sensor::Sensor* compressor_frequency_sensor_ = nullptr; // Sensor to store compressor frequency + // sensor to monitor heatpump connection time + uptime::HpUpTimeConnectionSensor* hp_uptime_connection_sensor_ = nullptr; int get_compressor_frequency(); bool is_operating(); @@ -112,6 +116,14 @@ class CN105Climate : public climate::Climate, public Component, public uart::UAR /// le bouton de setup de l'UART bool uart_setup_switch; + bool isUARTConnected_ = false; + bool isHeatpumpConnected_ = false; + + unsigned long nbCompleteCycles_ = 0; + unsigned long nbCycles_ = 0; + unsigned int nbHeatpumpConnections_ = 0; + + void sendFirstConnectionPacket(); //bool can_proceed() override; @@ -189,6 +201,8 @@ class CN105Climate : public climate::Climate, public Component, public uart::UAR void setWideVaneSetting(const char* setting); void setFanSpeed(const char* setting); + void setHeatpumpConnected(bool state); + private: const char* lookupByteMapValue(const char* valuesMap[], const uint8_t byteMap[], int len, uint8_t byteValue, const char* debugInfo = "", const char* defaultValue = nullptr); int lookupByteMapValue(const int valuesMap[], const uint8_t byteMap[], int len, uint8_t byteValue, const char* debugInfo = ""); @@ -254,8 +268,7 @@ class CN105Climate : public climate::Climate, public Component, public uart::UAR int tx_pin_ = -1; int rx_pin_ = -1; - bool isUARTConnected_ = false; - bool isHeatpumpConnected_ = false; + //HardwareSerial* _HardSerial{ nullptr }; unsigned long lastSend; diff --git a/components/cn105/componentEntries.cpp b/components/cn105/componentEntries.cpp index ec8a910..2242044 100644 --- a/components/cn105/componentEntries.cpp +++ b/components/cn105/componentEntries.cpp @@ -20,6 +20,11 @@ void CN105Climate::setup() { this->initBytePointer(); this->lastResponseMs = CUSTOM_MILLIS; + // initialize diagnostic stats + this->nbCompleteCycles_ = 0; + this->nbCycles_ = 0; + this->nbHeatpumpConnections_ = 0; + ESP_LOGI(TAG, "tx_pin: %d rx_pin: %d", this->tx_pin_, this->rx_pin_); ESP_LOGI(TAG, "remote_temp_timeout is set to %d", this->remote_temp_timeout_); ESP_LOGI(TAG, "debounce_delay is set to %d", this->debounce_delay_); diff --git a/components/cn105/extraComponents.cpp b/components/cn105/extraComponents.cpp index 5f713c3..66fcf61 100644 --- a/components/cn105/extraComponents.cpp +++ b/components/cn105/extraComponents.cpp @@ -71,4 +71,8 @@ void CN105Climate::set_sub_mode_sensor(esphome::text_sensor::TextSensor* Sub_mod void CN105Climate::set_auto_sub_mode_sensor(esphome::text_sensor::TextSensor* Auto_sub_mode_sensor) { this->Auto_sub_mode_sensor_ = Auto_sub_mode_sensor; +} + +void CN105Climate::set_hp_uptime_connection_sensor(uptime::HpUpTimeConnectionSensor* hp_up_connection_sensor) { + this->hp_uptime_connection_sensor_ = hp_up_connection_sensor; } \ No newline at end of file diff --git a/components/cn105/hp_readings.cpp b/components/cn105/hp_readings.cpp index f2dd348..be0c80b 100644 --- a/components/cn105/hp_readings.cpp +++ b/components/cn105/hp_readings.cpp @@ -304,6 +304,14 @@ void CN105Climate::getDataFromResponsePacket() { this->getPowerFromResponsePacket(); //FC 62 01 30 10 09 00 00 00 02 02 00 00 00 00 00 00 00 00 00 00 50 this->loopCycle.cycleEnded(); + + if (this->hp_uptime_connection_sensor_ != nullptr) { + // if the uptime connection sensor is configured + // we trigger manual update at the end of a cycle. + this->hp_uptime_connection_sensor_->update(); + } + + this->nbCompleteCycles_++; break; case 0x10: @@ -353,7 +361,8 @@ void CN105Climate::processCommand() { break; case 0x7a: ESP_LOGI(TAG, "--> Heatpump did reply: connection success! <--"); - this->isHeatpumpConnected_ = true; + //this->isHeatpumpConnected_ = true; + this->setHeatpumpConnected(true); // let's say that the last complete cycle was over now this->loopCycle.lastCompleteCycleMs = CUSTOM_MILLIS; this->currentSettings.resetSettings(); // each time we connect, we need to reset current setting to force a complete sync with ha component state and receievdSettings diff --git a/components/cn105/hp_writings.cpp b/components/cn105/hp_writings.cpp index 8f54b36..491b3d1 100644 --- a/components/cn105/hp_writings.cpp +++ b/components/cn105/hp_writings.cpp @@ -14,8 +14,8 @@ uint8_t CN105Climate::checkSum(uint8_t bytes[], int len) { void CN105Climate::sendFirstConnectionPacket() { if (this->isUARTConnected_) { - this->isHeatpumpConnected_ = false; - + //this->isHeatpumpConnected_ = false; + this->setHeatpumpConnected(false); ESP_LOGD(TAG, "Envoi du packet de connexion..."); uint8_t packet[CONNECT_LEN]; memcpy(packet, CONNECT, CONNECT_LEN); @@ -25,6 +25,7 @@ void CN105Climate::sendFirstConnectionPacket() { this->lastSend = CUSTOM_MILLIS; this->lastConnectRqTimeMs = CUSTOM_MILLIS; + this->nbHeatpumpConnections_++; // we wait for a 10s timeout to check if the hp has replied to connection packet this->set_timeout("checkFirstConnection", 10000, [this]() { @@ -90,12 +91,13 @@ void CN105Climate::writePacket(uint8_t* packet, int length, bool checkIsActive) } else { ESP_LOGW(TAG, "could not write as asked, because UART is not connected"); + // this->disconnectUART(); - // this->setupUART(); - // this->sendFirstConnectionPacket(); + this->setupUART(); + this->sendFirstConnectionPacket(); ESP_LOGW(TAG, "delaying packet writing because we need to reconnect first..."); - this->set_timeout("write", 1000, [this, packet, length]() { this->writePacket(packet, length); }); + this->set_timeout("write", 4000, [this, packet, length]() { this->writePacket(packet, length); }); } } @@ -310,6 +312,7 @@ void CN105Climate::buildAndSendRequestsInfoPackets() { ESP_LOGV("CONTROL_WANTED_SETTINGS", "hasChanged is %s", wantedSettings.hasChanged ? "true" : "false"); ESP_LOGD(TAG, "sending a request for settings packet (0x02)"); this->loopCycle.cycleStarted(); + this->nbCycles_++; ESP_LOGD(LOG_CYCLE_TAG, "2a: Sending settings request (0x02)"); this->buildAndSendRequestPacket(RQST_PKT_SETTINGS); } else { diff --git a/components/cn105/uptime_connection_sensor.cpp b/components/cn105/uptime_connection_sensor.cpp new file mode 100644 index 0000000..fa22a26 --- /dev/null +++ b/components/cn105/uptime_connection_sensor.cpp @@ -0,0 +1,34 @@ +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" +#include "uptime_connection_sensor.h" + +namespace esphome { + namespace uptime { + + static const char* const TAG = "uptime.connection.sensor"; + void HpUpTimeConnectionSensor::update() { + if (connected_) { + UptimeSensor::update(); + } else { + this->uptime_ = 0; + this->publish_state(0); + } + } + + void HpUpTimeConnectionSensor::start() { + this->uptime_ = 0; + this->connected_ = true; + this->update(); + } + + void HpUpTimeConnectionSensor::stop() { + this->connected_ = false; + this->update(); + } + + std::string HpUpTimeConnectionSensor::unique_id() { return get_mac_address() + "-uptime-hp_connection"; } + void HpUpTimeConnectionSensor::dump_config() { LOG_SENSOR("", "Uptime Connection Sensor", this); } + + } // namespace uptime +} // namespace esphome \ No newline at end of file diff --git a/components/cn105/uptime_connection_sensor.h b/components/cn105/uptime_connection_sensor.h new file mode 100644 index 0000000..9314a32 --- /dev/null +++ b/components/cn105/uptime_connection_sensor.h @@ -0,0 +1,19 @@ +#pragma once +#include "esphome/components/uptime/uptime_sensor.h" + +namespace esphome { + namespace uptime { + + class HpUpTimeConnectionSensor : public UptimeSensor { + public: + void update() override; + std::string unique_id() override; + void dump_config() override; + + void start(); // connection established + void stop(); // connection lost + protected: + bool connected_{ false }; + }; + } // namespace uptime +} // namespace esphome \ No newline at end of file diff --git a/esp32-test.yaml b/esp32-test.yaml index 8944c4d..a1d33e5 100644 --- a/esp32-test.yaml +++ b/esp32-test.yaml @@ -14,7 +14,8 @@ esp32: mqtt: - broker: homeassistant.local + #broker: homeassistant.local + broker: home-io.local # if HA instance name is custom username: heatpumpSejour password: !secret mqtt_password discovery: true @@ -70,13 +71,13 @@ api: - service: use_internal_temperature then: - lambda: 'id(esp32_clim).set_remote_temperature(0);' - - service: test-mutex - then: - - logger.log: "Test de la fonction mutex..." - - lambda: |- - id(esp32_clim).testMutex(); + # - service: test-mutex + # then: + # - logger.log: "Test de la fonction mutex..." + # - lambda: |- + # id(esp32_clim).testMutex(); encryption: - key: !secret encryption_key + key: !secret encryption_key_sejour ota: password: !secret ota_pwd @@ -100,9 +101,8 @@ wifi: captive_portal: -# pas assez de place pour update ota -#web_server: -# port: 80 +web_server: + port: 80 time: - platform: homeassistant @@ -135,8 +135,62 @@ text_sensor: name: "Reset Reason" + + # Sensors with general information. sensor: + + - platform: template + name: "dg_uart_connected" + entity_category: DIAGNOSTIC + lambda: |- + return (bool) id(esp32_clim).isUARTConnected_; + update_interval: 30s + + - platform: template + name: "dg_hp_connected" + entity_category: DIAGNOSTIC + lambda: |- + return (bool) id(esp32_clim).isUARTConnected_; + update_interval: 30s + + - platform: template + name: "dg_complete_cycles" + entity_category: DIAGNOSTIC + accuracy_decimals: 0 + lambda: |- + return (unsigned long) id(esp32_clim).nbCompleteCycles_; + update_interval: 60s + + - platform: template + name: "dg_total_cycles" + accuracy_decimals: 0 + entity_category: DIAGNOSTIC + lambda: |- + return (unsigned long) id(esp32_clim).nbCycles_; + update_interval: 60s + - platform: template + name: "dg_nb_hp_connections" + accuracy_decimals: 0 + entity_category: DIAGNOSTIC + lambda: |- + return (unsigned int) id(esp32_clim).nbHeatpumpConnections_; + update_interval: 60s + + - platform: template + name: "dg_complete_cycles_percent" + unit_of_measurement: "%" + accuracy_decimals: 1 + entity_category: DIAGNOSTIC + lambda: |- + unsigned long nbCompleteCycles = id(esp32_clim).nbCompleteCycles_; + unsigned long nbCycles = id(esp32_clim).nbCycles_; + if (nbCycles == 0) { + return 0.0; + } + return (float) nbCompleteCycles / nbCycles * 100.0; + update_interval: 60s + - platform: debug free: name: "Heap Free" @@ -195,6 +249,9 @@ climate: remote_temperature_timeout: 15min update_interval: 2s # shouldn't be less than 1 second debounce_delay : 100ms # delay to prevent bouncing + hp_uptime_connection_sensor: + name: ${name} HP Uptime Connection + update_interval: 10s supports: mode: [COOL, HEAT, FAN_ONLY, DRY] fan_mode: [AUTO, QUIET, LOW, MEDIUM, HIGH] @@ -205,39 +262,3 @@ climate: temperature_step: 1.0 -# Configuration pour l'objet 'switch' (bouton) -# switch: -# - platform: template -# name: ${name} UART Setup Switch -# id: uart_setup_switch -# entity_category: DIAGNOSTIC -# #setup_priority : -100.0 ## late -# optimistic: true -# turn_on_action: -# - lambda: |- -# // Appelle la méthode 'setupUART' quand le switch est activé -# id(esp32_clim).setupUART(); -# turn_off_action: -# - lambda: |- -# // Appelle la méthode 'setupUART' quand le switch est activé -# id(esp32_clim).disconnectUART(); - -# - platform: template -# name: Send 1st packet to ${name} -# id: send_packet_switch -# entity_category: DIAGNOSTIC -# #setup_priority : -100.0 ## late -# optimistic: true -# turn_on_action: -# - lambda: |- -# id(esp32_clim).sendFirstConnectionPacket(); - -# - platform: template -# name: Requests ${name} Synchro -# id: build_and_send_requests_info_packets -# entity_category: DIAGNOSTIC -# #setup_priority : -100.0 ## late -# optimistic: true -# turn_on_action: -# - lambda: |- -# id(esp32_clim).buildAndSendRequestsInfoPackets(); \ No newline at end of file