Files
Hitachi-IR-Climate-ESPHome-…/esphome/components/hitachi_rar6ne1/hitachi_rar6ne1.h
Francesco Zanin 1a9eb82d0f Initial commit: Hitachi RAR-6NE1 climate via ESP32/ESPHome
Reverse engineering del protocollo IR (HITACHI_AC 28 byte) del telecomando
RAR-6NE1 e custom component ESPHome bidirezionale (TX + RX) per Home Assistant.

- esphome/: custom component hitachi_rar6ne1 (climate_ir::ClimateIR) + config
- src/: firmware Arduino di cattura IR con web UI (strumento di diagnostica)
- README.md: documentazione completa (protocollo, decode, checksum, gotcha)
- Segreti esclusi dal versionamento (vedi *.example e .gitignore)
- Licenza GPL-3.0

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 15:50:26 +02:00

74 lines
3.2 KiB
C++

#pragma once
#include "esphome/components/climate_ir/climate_ir.h"
namespace esphome {
namespace hitachi_rar6ne1 {
// Timing del protocollo HITACHI_AC (28 byte), verificati su IRremoteESP8266
// (ir_Hitachi.cpp) e contro le catture reali del RAR-6NE1.
static const uint16_t kHeaderMark = 3300;
static const uint16_t kHeaderSpace = 1700;
static const uint16_t kBitMark = 400;
static const uint16_t kOneSpace = 1250;
static const uint16_t kZeroSpace = 500;
static const uint32_t kCarrierFrequency = 38000;
static const uint8_t kStateLength = 28;
// byte[9] = "codice tasto premuto" sul telecomando reale (0xE0 mode/idle,
// 0xC0 power, ...). L'unita' molto probabilmente lo ignora quando riceve uno
// stato completo, ma e' la PRIMA variabile da provare se i comandi sintetizzati
// non vengono accettati. Vedi README / nota Obsidian.
static const uint8_t kButtonCode = 0xE0;
// Limiti dell'entita' climate. RANGE CONSERVATIVO 18-30 scelto per sicurezza.
// FLAG: DA VERIFICARE contro il range realmente supportato dal RAR-6NE1 (alcuni
// Hitachi arrivano a 16 in Cool e/o 32, altri si fermano prima). Vedi "Blocchi
// attivi" nella nota Obsidian projects/hitachi-ir-esphome.
static const float kMinTempC = 18.0f;
static const float kMaxTempC = 30.0f;
static const float kTempStep = 1.0f;
class HitachiRar6ne1Climate : public climate_ir::ClimateIR {
public:
HitachiRar6ne1Climate()
: climate_ir::ClimateIR(
kMinTempC, kMaxTempC, kTempStep,
/*supports_dry=*/true, /*supports_fan_only=*/true,
// 4 velocita' + auto, mappate su modi standard HA:
// QUIET=1/4 LOW=2/4 MEDIUM=3/4 HIGH=4/4 AUTO
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_QUIET,
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH},
// Eco = Silent, Boost = Powerful
{climate::CLIMATE_PRESET_NONE, climate::CLIMATE_PRESET_ECO,
climate::CLIMATE_PRESET_BOOST}) {}
// Invia un frame OFF realmente catturato (deterministico) per il test
// self-loopback: richiamabile da un button template nello YAML.
void send_test_frame();
// Accoppia preset e ventola come il telecomando fisico (Silent=ventola minima,
// Powerful=massima, mutuamente esclusivi) prima di applicare il comando.
void control(const climate::ClimateCall &call) override;
protected:
// Costruisce il frame IR dallo stato climate corrente e lo trasmette.
void transmit_state() override;
// Decodifica un frame ricevuto (telecomando fisico) e aggiorna lo stato.
bool on_receive(remote_base::RemoteReceiveData data) override;
// Riempie i 28 byte a partire dallo stato climate corrente (checksum incluso).
void build_state_(uint8_t state[kStateLength]);
// Emette i 28 byte sul trasmettitore IR (header + bit MSB-first + footer).
void transmit_frame_(const uint8_t state[kStateLength]);
// Checksum identico a IRHitachiAc::calcChecksum().
static uint8_t calc_checksum_(const uint8_t state[kStateLength]);
};
} // namespace hitachi_rar6ne1
} // namespace esphome