ESPHome 2026.5.0
Loading...
Searching...
No Matches
anova.cpp
Go to the documentation of this file.
1#include "anova.h"
2#include "esphome/core/log.h"
3
4#ifdef USE_ESP32
5
6namespace esphome::anova {
7
8static const char *const TAG = "anova";
9
10using namespace esphome::climate;
11
12void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
13
15 this->codec_ = make_unique<AnovaCodec>();
16 this->current_request_ = 0;
17}
18
20 // Parent BLEClientNode has a loop() method, but this component uses
21 // polling via update() and BLE callbacks so loop isn't needed
22 this->disable_loop();
23}
24
25void Anova::control(const ClimateCall &call) {
26 auto mode_val = call.get_mode();
27 if (mode_val.has_value()) {
28 ClimateMode mode = *mode_val;
29 AnovaPacket *pkt;
30 switch (mode) {
32 pkt = this->codec_->get_stop_request();
33 break;
35 pkt = this->codec_->get_start_request();
36 break;
37 default:
38 ESP_LOGW(TAG, "Unsupported mode: %d", mode);
39 return;
40 }
41 auto status =
42 esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
43 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
44 if (status) {
45 ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str(), status);
46 }
47 }
48 auto target_temp = call.get_target_temperature();
49 if (target_temp.has_value()) {
50 auto *pkt = this->codec_->get_set_target_temp_request(*target_temp);
51 auto status =
52 esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
53 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
54 if (status) {
55 ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str(), status);
56 }
57 }
58}
59
60void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
61 switch (event) {
62 case ESP_GATTC_DISCONNECT_EVT: {
63 this->current_temperature = NAN;
64 this->target_temperature = NAN;
65 this->publish_state();
66 break;
67 }
68 case ESP_GATTC_SEARCH_CMPL_EVT: {
69 auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
70 if (chr == nullptr) {
71 ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
72 ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
73 break;
74 }
75 this->char_handle_ = chr->handle;
76
77 auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
78 chr->handle);
79 if (status) {
80 ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
81 }
82 break;
83 }
84 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
85 this->node_state = espbt::ClientState::ESTABLISHED;
86 this->current_request_ = 0;
87 this->update();
88 break;
89 }
90 case ESP_GATTC_NOTIFY_EVT: {
91 if (param->notify.handle != this->char_handle_)
92 break;
93 this->codec_->decode(param->notify.value, param->notify.value_len);
94 if (this->codec_->has_target_temp()) {
95 this->target_temperature = this->codec_->target_temp_;
96 }
97 if (this->codec_->has_current_temp()) {
98 this->current_temperature = this->codec_->current_temp_;
99 }
100 if (this->codec_->has_running()) {
102 }
103 if (this->codec_->has_unit()) {
104 this->fahrenheit_ = (this->codec_->unit_ == 'f');
105 ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celsius");
106 this->current_request_++;
107 }
108 this->publish_state();
109
110 if (this->current_request_ > 1) {
111 AnovaPacket *pkt = nullptr;
112 switch (this->current_request_++) {
113 case 2:
114 pkt = this->codec_->get_read_target_temp_request();
115 break;
116 case 3:
117 pkt = this->codec_->get_read_current_temp_request();
118 break;
119 default:
120 this->current_request_ = 1;
121 break;
122 }
123 if (pkt != nullptr) {
124 auto status =
125 esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
126 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
127 if (status) {
128 ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str(), status);
129 }
130 }
131 }
132 break;
133 }
134 default:
135 break;
136 }
137}
138
139void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
140
142 if (this->node_state != espbt::ClientState::ESTABLISHED)
143 return;
144
145 if (this->current_request_ < 2) {
146 AnovaPacket *pkt;
147 if (this->current_request_ == 0) {
148 pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
149 } else {
150 pkt = this->codec_->get_read_device_status_request();
151 }
152 auto status =
153 esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
154 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
155 if (status) {
156 ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str(), status);
157 }
158 this->current_request_++;
159 }
160}
161
162} // namespace esphome::anova
163
164#endif
uint8_t status
Definition bl0942.h:8
void disable_loop()
Disable this component's loop.
const StringRef & get_name() const
Definition entity_base.h:71
void dump_config() override
Definition anova.cpp:12
void update() override
Definition anova.cpp:141
void setup() override
Definition anova.cpp:14
void loop() override
Definition anova.cpp:19
std::unique_ptr< AnovaCodec > codec_
Definition anova.h:40
uint8_t current_request_
Definition anova.h:43
uint16_t char_handle_
Definition anova.h:42
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition anova.cpp:60
void control(const climate::ClimateCall &call) override
Definition anova.cpp:25
void set_unit_of_measurement(const char *unit)
Definition anova.cpp:139
This class is used to encode all control actions on a climate device.
Definition climate.h:34
ClimateMode mode
The active mode of the climate device.
Definition climate.h:293
float target_temperature
The target temperature of the climate device.
Definition climate.h:274
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition climate.h:267
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:437
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
ClimateMode
Enum for all modes a climate device can be in.
@ CLIMATE_MODE_HEAT
The climate device is set to heat to reach the target temperature.
@ CLIMATE_MODE_OFF
The climate device is off.