From f1dbdf514d7d0a095710f51fb81f03dd2a3341df Mon Sep 17 00:00:00 2001 From: oskar Date: Wed, 22 Jul 2026 16:35:47 +0200 Subject: [PATCH] feat(gree-ir): wlasny komponent ESPHome dla klimy Gree Natywne komponenty gree/coolix nie dzialaly - zla suma kontrolna. Protokol zreverse-engineerowany z 22 ramek IR nagranych z pilota: - bajt0 = tryb+power: cool 0x19, heat 0x4C, fan 0x1B, off 0x44 - bajt1 = temperatura jako (temp-16) - checksum = (lo(b0..b3)+hi(b4..b6)+0x0A)&0xF w gornym nibblu b7 - timingi: hdr 9030/-4460, mark 677, space0 553, space1 1630, gap 19960 Termostat Off/Cool/Heat/Fan, zakres 16-30C, HW: Wemos D1 Mini + Grove IR. --- hardware/esp/ir-ac-ha-integration/.gitignore | 5 ++ .../components/gree_ir/__init__.py | 0 .../components/gree_ir/climate.py | 21 +++++ .../components/gree_ir/gree_ir.h | 85 +++++++++++++++++++ .../ir-ac-ha-integration/gree-ir-blaster.yaml | 24 +++--- 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 hardware/esp/ir-ac-ha-integration/.gitignore create mode 100644 hardware/esp/ir-ac-ha-integration/components/gree_ir/__init__.py create mode 100644 hardware/esp/ir-ac-ha-integration/components/gree_ir/climate.py create mode 100644 hardware/esp/ir-ac-ha-integration/components/gree_ir/gree_ir.h diff --git a/hardware/esp/ir-ac-ha-integration/.gitignore b/hardware/esp/ir-ac-ha-integration/.gitignore new file mode 100644 index 0000000..d8b4157 --- /dev/null +++ b/hardware/esp/ir-ac-ha-integration/.gitignore @@ -0,0 +1,5 @@ +# Gitignore settings for ESPHome +# This is an example and may include too much for your use-case. +# You can modify this file to suit your needs. +/.esphome/ +/secrets.yaml diff --git a/hardware/esp/ir-ac-ha-integration/components/gree_ir/__init__.py b/hardware/esp/ir-ac-ha-integration/components/gree_ir/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hardware/esp/ir-ac-ha-integration/components/gree_ir/climate.py b/hardware/esp/ir-ac-ha-integration/components/gree_ir/climate.py new file mode 100644 index 0000000..babcd90 --- /dev/null +++ b/hardware/esp/ir-ac-ha-integration/components/gree_ir/climate.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import climate, remote_transmitter + +CONF_TRANSMITTER_ID = "transmitter_id" + +gree_ir_ns = cg.esphome_ns.namespace("gree_ir") +GreeIR = gree_ir_ns.class_("GreeIR", climate.Climate, cg.Component) + +CONFIG_SCHEMA = climate.climate_schema(GreeIR).extend({ + cv.Required(CONF_TRANSMITTER_ID): cv.use_id( + remote_transmitter.RemoteTransmitterComponent + ), +}).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = await climate.new_climate(config) + await cg.register_component(var, config) + tx = await cg.get_variable(config[CONF_TRANSMITTER_ID]) + cg.add(var.set_transmitter(tx)) diff --git a/hardware/esp/ir-ac-ha-integration/components/gree_ir/gree_ir.h b/hardware/esp/ir-ac-ha-integration/components/gree_ir/gree_ir.h new file mode 100644 index 0000000..0c2edab --- /dev/null +++ b/hardware/esp/ir-ac-ha-integration/components/gree_ir/gree_ir.h @@ -0,0 +1,85 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/components/climate/climate.h" +#include "esphome/components/remote_transmitter/remote_transmitter.h" + +namespace esphome { +namespace gree_ir { + +class GreeIR : public climate::Climate, public Component { + public: + void set_transmitter(remote_transmitter::RemoteTransmitterComponent *tx) { + this->transmitter_ = tx; + } + + void setup() override { + this->mode = climate::CLIMATE_MODE_OFF; + this->target_temperature = 24; + this->publish_state(); + } + + climate::ClimateTraits traits() override { + auto traits = climate::ClimateTraits(); + traits.set_supported_modes({ + climate::CLIMATE_MODE_OFF, + climate::CLIMATE_MODE_COOL, + climate::CLIMATE_MODE_HEAT, + climate::CLIMATE_MODE_FAN_ONLY, + }); + traits.set_visual_min_temperature(16); + traits.set_visual_max_temperature(30); + traits.set_visual_temperature_step(1); + return traits; + } + + void control(const climate::ClimateCall &call) override { + if (call.get_mode().has_value()) + this->mode = *call.get_mode(); + if (call.get_target_temperature().has_value()) + this->target_temperature = *call.get_target_temperature(); + this->transmit_state_(); + this->publish_state(); + } + + protected: + remote_transmitter::RemoteTransmitterComponent *transmitter_{nullptr}; + + void transmit_state_() { + uint8_t b[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t mode_byte, b2, b1_0; + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: mode_byte = 0x19; b2 = 0x60; b1_0 = 0x06; break; + case climate::CLIMATE_MODE_HEAT: mode_byte = 0x4C; b2 = 0x40; b1_0 = 0x01; break; + case climate::CLIMATE_MODE_FAN_ONLY: mode_byte = 0x1B; b2 = 0x60; b1_0 = 0x02; break; + default: mode_byte = 0x44; b2 = 0x00; b1_0 = 0x01; break; + } + int temp = (int) this->target_temperature; + if (temp < 16) temp = 16; + if (temp > 30) temp = 30; + uint8_t t = (this->mode == climate::CLIMATE_MODE_OFF) ? 0x0A : (uint8_t)(temp - 16); + + b[0] = mode_byte; b[1] = t; b[2] = b2; b[3] = 0x50; + b[4] = b1_0; b[5] = 0x00; b[6] = 0x00; + uint8_t cs = ((b[0] & 0xF) + (b[1] & 0xF) + (b[2] & 0xF) + (b[3] & 0xF) + + ((b[4] >> 4) & 0xF) + ((b[5] >> 4) & 0xF) + ((b[6] >> 4) & 0xF) + 0x0A) & 0xF; + b[7] = (cs << 4) & 0xF0; + + auto transmit = this->transmitter_->transmit(); + auto *data = transmit.get_data(); + data->set_carrier_frequency(38000); + const int MK = 677, S0 = 553, S1 = 1630, GAP = 19960; + data->mark(9030); data->space(4460); + for (int by = 0; by < 4; by++) + for (int bit = 0; bit < 8; bit++) { data->mark(MK); data->space(((b[by] >> bit) & 1) ? S1 : S0); } + const int foot[3] = {0, 1, 0}; + for (int i = 0; i < 3; i++) { data->mark(MK); data->space(foot[i] ? S1 : S0); } + data->mark(MK); data->space(GAP); + for (int by = 4; by < 8; by++) + for (int bit = 0; bit < 8; bit++) { data->mark(MK); data->space(((b[by] >> bit) & 1) ? S1 : S0); } + data->mark(MK); + transmit.perform(); + } +}; + +} // namespace gree_ir +} // namespace diff --git a/hardware/esp/ir-ac-ha-integration/gree-ir-blaster.yaml b/hardware/esp/ir-ac-ha-integration/gree-ir-blaster.yaml index ac89f9a..892006c 100644 --- a/hardware/esp/ir-ac-ha-integration/gree-ir-blaster.yaml +++ b/hardware/esp/ir-ac-ha-integration/gree-ir-blaster.yaml @@ -1,31 +1,33 @@ esphome: name: gree-ir-blaster friendly_name: Gree IR Blaster + esp8266: board: d1_mini + +external_components: + - source: + type: local + path: components + wifi: ssid: !secret wifi_ssid password: !secret wifi_password ap: ssid: "Gree-IR Fallback" password: !secret fallback_password + logger: api: ota: platform: esphome + remote_transmitter: + id: tx pin: D5 carrier_duty_percent: 50% -remote_receiver: - pin: - number: D6 - inverted: true - mode: - input: true - pullup: true - dump: all + climate: - - platform: gree + - platform: gree_ir name: "Klimatyzacja" - id: gree_ac - model: yan + transmitter_id: tx