refact
This commit is contained in:
@@ -173,6 +173,64 @@ text_sensor:
|
|||||||
icon: mdi:alert-circle-outline
|
icon: mdi:alert-circle-outline
|
||||||
update_interval: never
|
update_interval: never
|
||||||
|
|
||||||
|
select:
|
||||||
|
- platform: template
|
||||||
|
name: "Тип напитка"
|
||||||
|
id: coffee_type
|
||||||
|
options:
|
||||||
|
- "Эспрессо"
|
||||||
|
- "Американо"
|
||||||
|
- "Капучино"
|
||||||
|
- "Латте"
|
||||||
|
- "Кофе с молоком"
|
||||||
|
- "Молочная пена"
|
||||||
|
- "Горячая вода"
|
||||||
|
initial_option: "Эспрессо"
|
||||||
|
optimistic: true
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Крепость"
|
||||||
|
id: coffee_strength
|
||||||
|
options:
|
||||||
|
- "Мягкий"
|
||||||
|
- "Обычный"
|
||||||
|
- "Крепкий"
|
||||||
|
- "Молотый"
|
||||||
|
initial_option: "Обычный"
|
||||||
|
optimistic: true
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Чашки"
|
||||||
|
id: coffee_cups
|
||||||
|
options:
|
||||||
|
- "1"
|
||||||
|
- "2"
|
||||||
|
- "3"
|
||||||
|
- "4"
|
||||||
|
initial_option: "1"
|
||||||
|
optimistic: true
|
||||||
|
|
||||||
|
number:
|
||||||
|
- platform: template
|
||||||
|
name: "Объем кофе (мл)"
|
||||||
|
id: coffee_volume
|
||||||
|
min_value: 20
|
||||||
|
max_value: 250
|
||||||
|
step: 10
|
||||||
|
initial_value: 100
|
||||||
|
unit_of_measurement: "ml"
|
||||||
|
optimistic: true
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Объем молока (мл)"
|
||||||
|
id: milk_volume
|
||||||
|
min_value: 0
|
||||||
|
max_value: 250
|
||||||
|
step: 10
|
||||||
|
initial_value: 0
|
||||||
|
unit_of_measurement: "ml"
|
||||||
|
optimistic: true
|
||||||
|
|
||||||
button:
|
button:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Американо"
|
name: "Американо"
|
||||||
@@ -185,6 +243,29 @@ button:
|
|||||||
"AA AA AA 90 0F 0A 00 02 00 03 02 00 B4 00 00 00 67 48 E4 4B 55",
|
"AA AA AA 90 0F 0A 00 02 00 03 02 00 B4 00 00 00 67 48 E4 4B 55",
|
||||||
"AA AA AA 91 10 01 03 56 9A 8C B8 55"
|
"AA AA AA 91 10 01 03 56 9A 8C B8 55"
|
||||||
}, 50);
|
}, 50);
|
||||||
|
- platform: template
|
||||||
|
name: "Выключение"
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
id(saeco_series_6000_component)->send_packets_to_mainboard(
|
||||||
|
std::vector<std::string>{
|
||||||
|
"AA AA AA FE 00 00 C8 87 1B 40 55"
|
||||||
|
}, 50);
|
||||||
|
- platform: template
|
||||||
|
name: "Приготовить напиток"
|
||||||
|
icon: mdi:coffee
|
||||||
|
on_press:
|
||||||
|
- lambda: |-
|
||||||
|
{
|
||||||
|
uint8_t type = id(coffee_type).index_of(id(coffee_type).state).value_or(0); // 0..6
|
||||||
|
uint8_t bean = id(coffee_strength).index_of(id(coffee_strength).state).value_or(0); // 0..3
|
||||||
|
uint8_t cups = id(coffee_cups).index_of(id(coffee_cups).state).value_or(0) + 1; // 1..4
|
||||||
|
uint16_t vol = (uint16_t) id(coffee_volume).state;
|
||||||
|
uint16_t milk = (uint16_t) id(milk_volume).state;
|
||||||
|
id(saeco_series_6000_component)->coffee_build(type, bean, cups, vol, milk);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[20:45:34][I][saeco_series_6000:079]: Packet from Display: AA AA AA 93 0A 01 01 57 15 3A ED 55
|
#[20:45:34][I][saeco_series_6000:079]: Packet from Display: AA AA AA 93 0A 01 01 57 15 3A ED 55
|
||||||
#[20:45:34][I][saeco_series_6000:079]: Packet from Display: AA AA AA 90 0B 0A 05 03 00 02 00 00 64 00 00 00 39 BF 5B 74 55
|
#[20:45:34][I][saeco_series_6000:079]: Packet from Display: AA AA AA 90 0B 0A 05 03 00 02 00 00 64 00 00 00 39 BF 5B 74 55
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Добавляем файл генерации рецептов
|
||||||
|
src_files(
|
||||||
|
saeco_series_6000.cpp
|
||||||
|
recipe.cpp
|
||||||
|
crc.cpp
|
||||||
|
)
|
||||||
Binary file not shown.
@@ -0,0 +1,43 @@
|
|||||||
|
#include "saeco_series_6000.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace saeco_series_6000 {
|
||||||
|
|
||||||
|
uint32_t SaecoSeries6000::calc_crc(const std::vector<uint8_t>& data, size_t start, size_t end) {
|
||||||
|
uint32_t crc = 0xFFFFFFFF;
|
||||||
|
const uint32_t POLY = 0x04C11DB7;
|
||||||
|
if (start >= end) return 0;
|
||||||
|
// start
|
||||||
|
uint8_t val = data[start];
|
||||||
|
((uint8_t*)(&crc))[3] ^= BitReverse[val];
|
||||||
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
if (crc & 0x80000000) {
|
||||||
|
crc = (crc << 1) ^ POLY;
|
||||||
|
} else {
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// остальные байты
|
||||||
|
for (size_t i = start + 1; i < end; i++) {
|
||||||
|
uint8_t v = data[i];
|
||||||
|
((uint8_t*)(&crc))[3] ^= BitReverse[v];
|
||||||
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
if (crc & 0x80000000) {
|
||||||
|
crc = (crc << 1) ^ POLY;
|
||||||
|
} else {
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t t32 = 0;
|
||||||
|
((uint8_t*)(&t32))[0] = BitReverse[((uint8_t*)(&crc))[3]];
|
||||||
|
((uint8_t*)(&t32))[1] = BitReverse[((uint8_t*)(&crc))[2]];
|
||||||
|
((uint8_t*)(&t32))[2] = BitReverse[((uint8_t*)(&crc))[1]];
|
||||||
|
((uint8_t*)(&t32))[3] = BitReverse[((uint8_t*)(&crc))[0]];
|
||||||
|
crc = t32 ^ 0xFFFFFFFF;
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace saeco_series_6000
|
||||||
|
} // namespace esphome
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include "saeco_series_6000.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace saeco_series_6000 {
|
||||||
|
|
||||||
|
std::vector<uint8_t> SaecoSeries6000::build_coffee_recipe(uint8_t type, uint8_t bean, uint8_t cups, uint16_t vol, uint16_t milk) {
|
||||||
|
static const uint8_t CoffeePattern[7][4] = {
|
||||||
|
{0,1,1,2}, // Espresso
|
||||||
|
{1,2,3,2}, // Americano
|
||||||
|
{2,2,1,2}, // Cappuccino
|
||||||
|
{3,2,2,2}, // Latte
|
||||||
|
{4,0,0,1}, // Coffee with milk
|
||||||
|
{5,1,0,1}, // Milk foam
|
||||||
|
{6,0,0,1} // Hot water
|
||||||
|
};
|
||||||
|
uint8_t rec[10] = {0};
|
||||||
|
if (type >= 7) type = 0;
|
||||||
|
rec[0] = CoffeePattern[type][0];
|
||||||
|
if (rec[0] == 5 || rec[0] == 4) {
|
||||||
|
rec[1] = 3; // всегда молотый для молока и milk foam
|
||||||
|
} else {
|
||||||
|
rec[1] = bean;
|
||||||
|
}
|
||||||
|
if (cups > CoffeePattern[type][3]) {
|
||||||
|
rec[2] = CoffeePattern[type][3];
|
||||||
|
} else {
|
||||||
|
rec[2] = cups;
|
||||||
|
}
|
||||||
|
rec[3] = CoffeePattern[type][1];
|
||||||
|
rec[4] = CoffeePattern[type][2];
|
||||||
|
if (rec[0] == 1) { // Americano
|
||||||
|
rec[6] = 40;
|
||||||
|
rec[7] = 0;
|
||||||
|
rec[8] = (uint8_t)vol;
|
||||||
|
rec[9] = vol / 0x100;
|
||||||
|
rec[5] = 0;
|
||||||
|
} else {
|
||||||
|
rec[6] = (uint8_t)vol;
|
||||||
|
rec[7] = vol / 0x100;
|
||||||
|
if (rec[0] == 0 || rec[0] == 1 || rec[0] == 4 || rec[0] == 5) {
|
||||||
|
if (rec[0] == 5) {
|
||||||
|
rec[5] = 2;
|
||||||
|
} else {
|
||||||
|
rec[5] = 0;
|
||||||
|
}
|
||||||
|
rec[8] = 0;
|
||||||
|
rec[9] = 0;
|
||||||
|
} else {
|
||||||
|
rec[5] = 2;
|
||||||
|
rec[8] = (uint8_t)milk;
|
||||||
|
rec[9] = milk / 0x100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::vector<uint8_t>(rec, rec + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace saeco_series_6000
|
||||||
|
} // namespace esphome
|
||||||
@@ -24,6 +24,7 @@ void SaecoSeries6000::loop() {
|
|||||||
uart_display_->write_byte(byte); // Mirror byte immediately
|
uart_display_->write_byte(byte); // Mirror byte immediately
|
||||||
process_mainboard_byte(byte); // Process the "sniffed" byte
|
process_mainboard_byte(byte); // Process the "sniffed" byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Контроль активности UART (выключение через 15 сек)
|
// Контроль активности UART (выключение через 15 сек)
|
||||||
static uint32_t last_check = 0;
|
static uint32_t last_check = 0;
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
@@ -232,41 +233,6 @@ const uint8_t SaecoSeries6000::BitReverse[256] = {
|
|||||||
0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
|
0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t SaecoSeries6000::calc_crc(const std::vector<uint8_t>& data, size_t start, size_t end) {
|
|
||||||
uint32_t crc = 0xFFFFFFFF;
|
|
||||||
const uint32_t POLY = 0x04C11DB7;
|
|
||||||
if (start >= end) return 0;
|
|
||||||
// start
|
|
||||||
uint8_t val = data[start];
|
|
||||||
((uint8_t*)(&crc))[3] ^= BitReverse[val];
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
if (crc & 0x80000000) {
|
|
||||||
crc = (crc << 1) ^ POLY;
|
|
||||||
} else {
|
|
||||||
crc <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// остальные байты
|
|
||||||
for (size_t i = start + 1; i < end; i++) {
|
|
||||||
uint8_t v = data[i];
|
|
||||||
((uint8_t*)(&crc))[3] ^= BitReverse[v];
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
if (crc & 0x80000000) {
|
|
||||||
crc = (crc << 1) ^ POLY;
|
|
||||||
} else {
|
|
||||||
crc <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint32_t t32 = 0;
|
|
||||||
((uint8_t*)(&t32))[0] = BitReverse[((uint8_t*)(&crc))[3]];
|
|
||||||
((uint8_t*)(&t32))[1] = BitReverse[((uint8_t*)(&crc))[2]];
|
|
||||||
((uint8_t*)(&t32))[2] = BitReverse[((uint8_t*)(&crc))[1]];
|
|
||||||
((uint8_t*)(&t32))[3] = BitReverse[((uint8_t*)(&crc))[0]];
|
|
||||||
crc = t32 ^ 0xFFFFFFFF;
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> SaecoSeries6000::parse_hex_string_to_bytes(const std::string& hex_string) {
|
std::vector<uint8_t> SaecoSeries6000::parse_hex_string_to_bytes(const std::string& hex_string) {
|
||||||
std::vector<uint8_t> pkt;
|
std::vector<uint8_t> pkt;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
@@ -339,5 +305,26 @@ void SaecoSeries6000::send_packets_to_mainboard(const std::vector<std::vector<ui
|
|||||||
|
|
||||||
// Реализация SendPacketsAction полностью в header (saeco_series_6000.h)
|
// Реализация SendPacketsAction полностью в header (saeco_series_6000.h)
|
||||||
|
|
||||||
|
void SaecoSeries6000::coffee_build(uint8_t type, uint8_t bean, uint8_t cups, uint16_t vol, uint16_t milk) {
|
||||||
|
// Формируем пакет для отправки (AA AA AA 90 ... CRC CRC CRC CRC 55)
|
||||||
|
std::vector<uint8_t> pkt = {0xAA, 0xAA, 0xAA, 0x90};
|
||||||
|
std::vector<uint8_t> rec = build_coffee_recipe(type, bean, cups, vol, milk);
|
||||||
|
pkt.insert(pkt.end(), rec.begin(), rec.end());
|
||||||
|
// Добавляем 4 байта CRC (заполним позже) и 0x55
|
||||||
|
pkt.resize(pkt.size() + 4 + 1, 0);
|
||||||
|
pkt[pkt.size() - 1] = 0x55;
|
||||||
|
// Пересчёт CRC
|
||||||
|
size_t preamble = 3;
|
||||||
|
size_t crc_start = preamble;
|
||||||
|
size_t crc_end = pkt.size() - 5;
|
||||||
|
uint32_t crc = calc_crc(pkt, crc_start, crc_end);
|
||||||
|
pkt[pkt.size()-2] = (crc >> 24) & 0xFF;
|
||||||
|
pkt[pkt.size()-3] = (crc >> 16) & 0xFF;
|
||||||
|
pkt[pkt.size()-4] = (crc >> 8) & 0xFF;
|
||||||
|
pkt[pkt.size()-5] = crc & 0xFF;
|
||||||
|
// Отправляем пакет
|
||||||
|
send_packets_to_mainboard(std::vector<std::vector<uint8_t>>{pkt});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace saeco_series_6000
|
} // namespace saeco_series_6000
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
@@ -26,6 +26,28 @@ class SaecoSeries6000 : public Component {
|
|||||||
void send_packets_to_mainboard(const std::vector<std::string>& hex_packets, uint32_t delay_ms = 10);
|
void send_packets_to_mainboard(const std::vector<std::string>& hex_packets, uint32_t delay_ms = 10);
|
||||||
void send_packets_to_mainboard(const std::string& hex_packet, uint32_t delay_ms = 10);
|
void send_packets_to_mainboard(const std::string& hex_packet, uint32_t delay_ms = 10);
|
||||||
|
|
||||||
|
// Типы напитков
|
||||||
|
enum CoffeeType {
|
||||||
|
ESPRESSO = 0,
|
||||||
|
AMERICANO = 1,
|
||||||
|
CAPPUCCINO = 2,
|
||||||
|
LATTE = 3,
|
||||||
|
COFFEE_WITH_MILK = 4,
|
||||||
|
MILK_FOAM = 5,
|
||||||
|
HOT_WATER = 6
|
||||||
|
};
|
||||||
|
// Крепость/помол
|
||||||
|
enum CoffeeStrength {
|
||||||
|
MILD = 0,
|
||||||
|
NORMAL = 1,
|
||||||
|
STRONG = 2,
|
||||||
|
GROUND = 3
|
||||||
|
};
|
||||||
|
// Построение и отправка пакета приготовления кофе
|
||||||
|
void coffee_build(uint8_t type, uint8_t bean, uint8_t cups, uint16_t vol, uint16_t milk);
|
||||||
|
// Генерация рецепта (только формирование данных)
|
||||||
|
static std::vector<uint8_t> build_coffee_recipe(uint8_t type, uint8_t bean, uint8_t cups, uint16_t vol, uint16_t milk);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uart::UARTComponent *uart_display_;
|
uart::UARTComponent *uart_display_;
|
||||||
uart::UARTComponent *uart_mainboard_;
|
uart::UARTComponent *uart_mainboard_;
|
||||||
@@ -57,7 +79,8 @@ class SaecoSeries6000 : public Component {
|
|||||||
void parse_b5_packet(const uint8_t* buffer, uint16_t size);
|
void parse_b5_packet(const uint8_t* buffer, uint16_t size);
|
||||||
void pub_status(uint8_t status);
|
void pub_status(uint8_t status);
|
||||||
static const uint8_t BitReverse[256];
|
static const uint8_t BitReverse[256];
|
||||||
uint32_t calc_crc(const std::vector<uint8_t>& data, size_t start, size_t end);
|
// CRC-32 для пакетов кофемашины
|
||||||
|
static uint32_t calc_crc(const std::vector<uint8_t>& data, size_t start, size_t end);
|
||||||
std::vector<uint8_t> parse_hex_string_to_bytes(const std::string& hex_string);
|
std::vector<uint8_t> parse_hex_string_to_bytes(const std::string& hex_string);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user