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