11static const char *
const TAG =
"mitsubishi_cn105.driver";
13static constexpr uint32_t RESPONSE_TIMEOUT_MS = 2000;
15static constexpr size_t REQUEST_PAYLOAD_LEN = 0x10;
16static constexpr size_t HEADER_LEN = 5;
17static constexpr uint8_t PREAMBLE = 0xFC;
18static constexpr uint8_t HEADER_BYTE_1 = 0x01;
19static constexpr uint8_t HEADER_BYTE_2 = 0x30;
21static constexpr uint8_t PACKET_TYPE_CONNECT_REQUEST = 0x5A;
22static constexpr uint8_t PACKET_TYPE_CONNECT_RESPONSE = 0x7A;
23static constexpr std::array<uint8_t, 2> CONNECT_REQUEST_PAYLOAD = {0xCA, 0x01};
25static constexpr uint8_t PACKET_TYPE_STATUS_REQUEST = 0x42;
26static constexpr uint8_t PACKET_TYPE_STATUS_RESPONSE = 0x62;
27static constexpr uint8_t STATUS_MSG_SETTINGS = 0x02;
28static constexpr uint8_t STATUS_MSG_TELEMETRY = 0x03;
30static constexpr uint8_t PACKET_TYPE_WRITE_SETTINGS_REQUEST = 0x41;
31static constexpr uint8_t PACKET_TYPE_WRITE_SETTINGS_RESPONSE = 0x61;
33static constexpr uint8_t
checksum(
const uint8_t *bytes,
size_t length) {
34 return static_cast<uint8_t
>(0xFC - std::accumulate(bytes, bytes +
length, uint8_t{0}));
37template<std::
size_t PayloadSize>
38static constexpr auto make_packet(uint8_t
type,
const std::array<uint8_t, PayloadSize> &payload) {
39 const size_t full_len = PayloadSize + HEADER_LEN + 1;
40 std::array<uint8_t, full_len> packet{PREAMBLE,
type, HEADER_BYTE_1, HEADER_BYTE_2,
static_cast<uint8_t
>(PayloadSize)};
41 std::copy_n(payload.begin(), PayloadSize, packet.begin() + HEADER_LEN);
42 packet.back() =
checksum(packet.data(), packet.size() - 1);
46static constexpr auto CONNECT_PACKET = make_packet(PACKET_TYPE_CONNECT_REQUEST, CONNECT_REQUEST_PAYLOAD);
218 this->
send_packet_(make_packet(PACKET_TYPE_STATUS_REQUEST, payload));
223 case PACKET_TYPE_CONNECT_RESPONSE:
227 case PACKET_TYPE_STATUS_RESPONSE:
230 case PACKET_TYPE_WRITE_SETTINGS_RESPONSE:
235 ESP_LOGVV(TAG,
"RX unknown packet type 0x%02X",
type);
242 ESP_LOGVV(TAG,
"RX status packet too short");
246 const auto previous = this->
status_;
247 const auto msg_type = payload[0];
271 case STATUS_MSG_SETTINGS:
272 if (!decoder.decode_settings(this->status_)) {
273 ESP_LOGVV(TAG,
"RX settings payload too short");
278 case STATUS_MSG_TELEMETRY:
279 if (!decoder.decode_room_temperature(this->status_)) {
280 ESP_LOGVV(TAG,
"RX telemetry payload too short");
287 ESP_LOGVV(TAG,
"RX unsupported status msg type 0x%02X", msg_type);
294 ESP_LOGD(TAG,
"Ignoring NaN remote temperature");
298 ESP_LOGD(TAG,
"Ignoring out-of-range remote temperature: %.1f",
temperature);
329 ESP_LOGD(TAG,
"Ignoring invalid mode: %u",
static_cast<uint8_t
>(
mode));
335 ESP_LOGD(TAG,
"Ignoring invalid fan mode: %u",
static_cast<uint8_t
>(
fan_mode));
341 ESP_LOGD(TAG,
"Ignoring invalid vane mode: %u",
static_cast<uint8_t
>(vane_mode));
347 ESP_LOGD(TAG,
"Ignoring invalid wide vane mode: %u",
static_cast<uint8_t
>(wide_vane_mode));
352 std::array<uint8_t, REQUEST_PAYLOAD_LEN> payload{};
359 encoder.encode_settings(this->
status_);
362 this->
send_packet_(make_packet(PACKET_TYPE_WRITE_SETTINGS_REQUEST, payload));
368 return LOG_STR(
"Not connected");
370 return LOG_STR(
"Connecting");
372 return LOG_STR(
"Connected");
374 return LOG_STR(
"UpdatingStatus");
376 return LOG_STR(
"StatusUpdated");
378 return LOG_STR(
"DeferredStatusRequest");
380 return LOG_STR(
"ScheduleNextStatusUpdate");
382 return LOG_STR(
"WaitingForScheduledStatusUpdate");
384 return LOG_STR(
"ApplyingSettings");
386 return LOG_STR(
"SettingsApplied");
388 return LOG_STR(
"ReadTimeout");
390 return LOG_STR(
"Unknown");
393template<
typename Callback>
395 uint8_t watchdog = 64;
396 while (device.
available() > 0 && watchdog-- > 0) {
397 uint8_t &value = this->read_buffer_[this->read_pos_];
399 ESP_LOGW(TAG,
"UART read failed while data available");
403 switch (++this->read_pos_) {
405 if (value != PREAMBLE) {
414 if (value != HEADER_BYTE_1) {
420 if (value != HEADER_BYTE_2) {
426 static_assert(READ_BUFFER_SIZE > HEADER_LEN);
427 if (this->read_buffer_[HEADER_LEN - 1] >= READ_BUFFER_SIZE - HEADER_LEN) {
436 const size_t len_without_checksum = HEADER_LEN +
static_cast<size_t>(this->read_buffer_[HEADER_LEN - 1]);
437 if (this->read_pos_ <= len_without_checksum) {
441 if (
checksum(this->read_buffer_, len_without_checksum) != value) {
447 const bool processed =
448 callback(this->read_buffer_[1], this->read_buffer_ + HEADER_LEN, len_without_checksum - HEADER_LEN);
457 dump_buffer_vv(prefix, this->read_buffer_, this->read_pos_);
462#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
BedjetMode mode
BedJet operating mode.
void reset_and_dump_buffer_(const char *prefix)
bool read_and_parse(uart::UARTDevice &device, Callback &&callback)
static void dump_buffer_vv(const char *prefix, const uint8_t *data, size_t len)
bool should_request_telemetry_() const
bool process_status_packet_(const uint8_t *payload, size_t len)
uint32_t operation_start_ms_
static bool should_transition(State from, State to)
uint8_t current_status_msg_type_
void set_state_(State new_state)
PropertyContext property_context_
void set_remote_temperature_half_deg_(uint8_t temperature_half_deg)
bool process_rx_packet_(uint8_t type, const uint8_t *payload, size_t len)
uint32_t status_update_wait_credit_ms_
bool is_status_initialized() const
bool is_telemetry_polling_enabled() const
void clear_remote_temperature()
uart::UARTDevice & device_
void did_transition_(State to)
void set_remote_temperature(float temperature)
bool has_timed_out_(uint32_t timeout) const
static constexpr uint8_t REMOTE_TEMPERATURE_DISABLED
uint32_t telemetry_request_min_interval_ms_
std::optional< uint32_t > last_telemetry_update_ms_
UpdateFlags pending_updates_
@ WAITING_FOR_SCHEDULED_STATUS_UPDATE
@ DEFERRED_STATUS_REQUEST
@ SCHEDULE_NEXT_STATUS_UPDATE
void send_packet_(std::span< const uint8_t > packet)
void set_power(bool power_on)
static const LogString * state_to_string(State state)
void set_wide_vane_mode(WideVaneMode mode)
void set_target_temperature(float target_temperature)
uint32_t update_interval_ms_
void set_fan_mode(FanMode fan_mode)
bool parse_status_payload_(uint8_t msg_type, const uint8_t *payload, size_t len)
uint8_t remote_temperature_half_deg_
FrameParser frame_parser_
void set_vane_mode(VaneMode vane_mode)
bool read_byte(uint8_t *data)
void write_array(const uint8_t *data, size_t len)
uint32_t get_loop_time_ms()
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Lightweight type-erased callback (8 bytes on 32-bit) that avoids std::function overhead.
WideVaneMode wide_vane_mode
bool contains_only(PropertyId id) const
static bool validate_and_set(Value value, Status &status, Mask &mask)