14static constexpr uint8_t FRAME_FLAGS_OFFSET = 1;
15static constexpr uint8_t FRAME_TYPE_OFFSET = 7;
16static constexpr uint8_t FRAME_PAYLOAD_OFFSET = 8;
17static constexpr uint8_t FRAME_FLAG_CRC = 0x40;
18static constexpr uint8_t FRAME_FLAG_ENCRYPTED = 0x80;
19static constexpr uint8_t MIN_FRAME_LENGTH = 8;
20static constexpr uint8_t MAX_FRAME_LENGTH = 0xF1;
23static constexpr size_t MAX_STORED_FRAME = 64;
24static constexpr size_t MAX_PAYLOAD = 4;
25static constexpr size_t MAX_WIRE_FRAME = 28;
83 }
else if (value == 0xFF) {
87 if (this->
size_ == 0 && (value < MIN_FRAME_LENGTH || value > MAX_FRAME_LENGTH)) {
91 const uint8_t index = this->
size_++;
92 if (index < MAX_STORED_FRAME) {
101 this->
crc_ =
crc16(&value, 1, this->
crc_, 0xA001,
false,
false);
102 }
else if (index ==
length) {
104 }
else if (index ==
length + 1) {
109 if (this->
size_ < 2) {
112 const bool crc = (this->
buffer_[FRAME_FLAGS_OFFSET] & FRAME_FLAG_CRC) != 0;
113 if (this->
size_ != length + 1 + (crc ? 2 : 0)) {
124 std::array<uint8_t, MAX_STORED_FRAME>
buffer_{};
138 if (
size > MAX_PAYLOAD) {
141 uint8_t core[FRAME_PAYLOAD_OFFSET + MAX_PAYLOAD] = {
static_cast<uint8_t
>(FRAME_PAYLOAD_OFFSET +
size)};
142 core[FRAME_TYPE_OFFSET] =
static_cast<uint8_t
>(
type);
143 for (
size_t i = 0; i <
size; i++) {
144 core[FRAME_PAYLOAD_OFFSET + i] = payload[i];
147 wire[
count++] = 0xFF;
148 wire[
count++] = 0xFF;
150 for (
size_t i = 0; i < FRAME_PAYLOAD_OFFSET +
size; i++) {
151 wire[
count++] = core[i];
153 if (core[i] == 0xFF) {
154 wire[
count++] = 0x55;
160 wire[
count++] = 0x55;
168static constexpr uint8_t TARGET_TEMPERATURE_OFFSET = 30;
169static constexpr uint8_t MAX_REPORTED_TEMPERATURE = 100;
184 const bool normal = (payload[38] & 0x10) && (payload[39] & 0x10);
185 const bool eco = (payload[41] & 0x04) != 0;
186 const bool bps = (payload[40] & 0x04) != 0;
187 if (
unsigned(normal) +
unsigned(eco) +
unsigned(bps) != 1) {
196static constexpr uint8_t STATUS_FRAME_LENGTH = 60;
203 if (
size <= STATUS_FRAME_LENGTH || frame[0] != STATUS_FRAME_LENGTH) {
206 const auto type =
static_cast<FrameType>(frame[FRAME_TYPE_OFFSET]);
210 const uint8_t *payload = frame + FRAME_PAYLOAD_OFFSET;
211 if (payload[0] != 0x6D || payload[1] != 0x01) {
214 status.current_temperature = payload[2];
215 status.target_temperature = payload[3] + TARGET_TEMPERATURE_OFFSET;
216 status.estimated_heating_time = payload[7];
217 status.secondary_temperature = payload[15];
218 status.powered = (payload[39] & 0x01) != 0;
219 status.heating = (payload[41] & 0x01) != 0;
221 status.available_water = payload[45];
222 return payload[2] <= MAX_REPORTED_TEMPERATURE && payload[3] <= MAX_REPORTED_TEMPERATURE - TARGET_TEMPERATURE_OFFSET;
232static constexpr uint8_t CONTROL_GROUP = 0x5D;
241 payload[0] = CONTROL_GROUP;
242 payload[1] =
static_cast<uint8_t
>(command.
parameter);
244 payload[3] = command.
value;
253 return status.target_temperature == command.
value + TARGET_TEMPERATURE_OFFSET;
uint8_t received_checksum_
bool feed(uint8_t value)
Returns true once a valid frame is complete. A complete but invalid frame leaves complete() set.
std::array< uint8_t, MAX_STORED_FRAME > buffer_
size_t size() const
Logical bytes received, including the checksum and CRC; may exceed MAX_STORED_FRAME.
const uint8_t * data() const
The stored head of the frame, at most MAX_STORED_FRAME bytes; valid until the next feed or reset.
size_t stored_size() const
bool decode_status(const uint8_t *frame, size_t size, Status &status)
Decodes a status report or a polled status; false for any other frame or an implausible one.
bool command_confirmed(const Command &command, const Status &status)
True when a status shows that the command took effect.
HonMode decode_mode(const uint8_t *payload)
size_t encode_frame(FrameType type, const uint8_t *payload, size_t size, uint8_t *wire)
Writes a clear-text frame with up to MAX_PAYLOAD bytes of payload into wire, which must hold MAX_WIRE...
@ PARAMETER_TARGET_TEMPERATURE
@ FRAME_TYPE_GET_NETWORK_STATUS
@ FRAME_TYPE_GET_DEVICE_ID
@ FRAME_TYPE_NETWORK_STATUS
void encode_control(const Command &command, uint8_t *payload)
Fills the four-byte control payload for a command.
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
uint8_t current_temperature
uint8_t secondary_temperature
uint8_t target_temperature
uint8_t estimated_heating_time