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>
74 lines
3.2 KiB
C++
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
|