ESPHome  2024.11.1
bedjet_hub.cpp
Go to the documentation of this file.
1 #ifdef USE_ESP32
2 
3 #include "bedjet_hub.h"
4 #include "bedjet_child.h"
5 #include "bedjet_const.h"
6 #include <cinttypes>
7 
8 namespace esphome {
9 namespace bedjet {
10 
11 static const LogString *bedjet_button_to_string(BedjetButton button) {
12  switch (button) {
13  case BTN_OFF:
14  return LOG_STR("OFF");
15  case BTN_COOL:
16  return LOG_STR("COOL");
17  case BTN_HEAT:
18  return LOG_STR("HEAT");
19  case BTN_EXTHT:
20  return LOG_STR("EXT HT");
21  case BTN_TURBO:
22  return LOG_STR("TURBO");
23  case BTN_DRY:
24  return LOG_STR("DRY");
25  case BTN_M1:
26  return LOG_STR("M1");
27  case BTN_M2:
28  return LOG_STR("M2");
29  case BTN_M3:
30  return LOG_STR("M3");
31  default:
32  return LOG_STR("unknown");
33  }
34 }
35 
36 /* Public */
37 
39  auto *pkt = this->codec_->get_button_request(MAGIC_UPDATE);
40  auto status = this->write_bedjet_packet_(pkt);
41 
42  if (status) {
43  ESP_LOGW(TAG, "[%s] MAGIC_UPDATE button failed, status=%d", this->get_name().c_str(), status);
44  }
45 }
46 
47 bool BedJetHub::button_heat() { return this->send_button(BTN_HEAT); }
50 bool BedJetHub::button_cool() { return this->send_button(BTN_COOL); }
51 bool BedJetHub::button_dry() { return this->send_button(BTN_DRY); }
52 bool BedJetHub::button_off() { return this->send_button(BTN_OFF); }
53 bool BedJetHub::button_memory1() { return this->send_button(BTN_M1); }
54 bool BedJetHub::button_memory2() { return this->send_button(BTN_M2); }
55 bool BedJetHub::button_memory3() { return this->send_button(BTN_M3); }
56 
57 bool BedJetHub::set_fan_index(uint8_t fan_speed_index) {
58  if (fan_speed_index > 19) {
59  ESP_LOGW(TAG, "Invalid fan speed index %d, expecting 0-19.", fan_speed_index);
60  return false;
61  }
62 
63  auto *pkt = this->codec_->get_set_fan_speed_request(fan_speed_index);
64  auto status = this->write_bedjet_packet_(pkt);
65 
66  if (status) {
67  ESP_LOGW(TAG, "[%s] writing fan speed failed, status=%d", this->get_name().c_str(), status);
68  }
69  return status == 0;
70 }
71 
73  auto *status = this->codec_->get_status_packet();
74  if (status != nullptr) {
75  return status->fan_step;
76  }
77  return 0;
78 }
79 
80 bool BedJetHub::set_target_temp(float temp_c) {
81  auto *pkt = this->codec_->get_set_target_temp_request(temp_c);
82  auto status = this->write_bedjet_packet_(pkt);
83 
84  if (status) {
85  ESP_LOGW(TAG, "[%s] writing target temp failed, status=%d", this->get_name().c_str(), status);
86  }
87  return status == 0;
88 }
89 
90 bool BedJetHub::set_time_remaining(uint8_t hours, uint8_t mins) {
91  // FIXME: this may fail depending on current mode or other restrictions enforced by the unit.
92  auto *pkt = this->codec_->get_set_runtime_remaining_request(hours, mins);
93  auto status = this->write_bedjet_packet_(pkt);
94 
95  if (status) {
96  ESP_LOGW(TAG, "[%s] writing remaining runtime failed, status=%d", this->get_name().c_str(), status);
97  }
98  return status == 0;
99 }
100 
102  auto *pkt = this->codec_->get_button_request(button);
103  auto status = this->write_bedjet_packet_(pkt);
104 
105  if (status) {
106  ESP_LOGW(TAG, "[%s] writing button %s failed, status=%d", this->get_name().c_str(),
107  LOG_STR_ARG(bedjet_button_to_string(button)), status);
108  } else {
109  ESP_LOGD(TAG, "[%s] writing button %s success", this->get_name().c_str(),
110  LOG_STR_ARG(bedjet_button_to_string(button)));
111  }
112  return status == 0;
113 }
114 
116  auto *status = this->codec_->get_status_packet();
117  if (status != nullptr) {
118  return status->time_remaining_secs + status->time_remaining_mins * 60 + status->time_remaining_hrs * 3600;
119  }
120  return 0;
121 }
122 
123 /* Bluetooth/GATT */
124 
126  if (!this->is_connected()) {
127  if (!this->parent_->enabled) {
128  ESP_LOGI(TAG, "[%s] Cannot write packet: Not connected, enabled=false", this->get_name().c_str());
129  } else {
130  ESP_LOGW(TAG, "[%s] Cannot write packet: Not connected", this->get_name().c_str());
131  }
132  return -1;
133  }
134  auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
135  this->char_handle_cmd_, pkt->data_length + 1, (uint8_t *) &pkt->command,
136  ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
137  return status;
138 }
139 
141 uint8_t BedJetHub::set_notify_(const bool enable) {
142  uint8_t status;
143  if (enable) {
144  status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
145  this->char_handle_status_);
146  if (status) {
147  ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
148  }
149  } else {
150  status = esp_ble_gattc_unregister_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
151  this->char_handle_status_);
152  if (status) {
153  ESP_LOGW(TAG, "[%s] esp_ble_gattc_unregister_for_notify failed, status=%d", this->get_name().c_str(), status);
154  }
155  }
156  ESP_LOGV(TAG, "[%s] set_notify: enable=%d; result=%d", this->get_name().c_str(), enable, status);
157  return status;
158 }
159 
161  bool result = true;
163 
164  if (!this->char_handle_cmd_) {
165  chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_COMMAND_UUID);
166  if (chr == nullptr) {
167  ESP_LOGW(TAG, "[%s] No control service found at device, not a BedJet..?", this->get_name().c_str());
168  result = false;
169  } else {
170  this->char_handle_cmd_ = chr->handle;
171  }
172  }
173 
174  if (!this->char_handle_status_) {
175  chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_STATUS_UUID);
176  if (chr == nullptr) {
177  ESP_LOGW(TAG, "[%s] No status service found at device, not a BedJet..?", this->get_name().c_str());
178  result = false;
179  } else {
180  this->char_handle_status_ = chr->handle;
181  }
182  }
183 
184  if (!this->config_descr_status_) {
185  // We also need to obtain the config descriptor for this handle.
186  // Otherwise once we set node_state=Established, the parent will flush all handles/descriptors, and we won't be
187  // able to look it up.
188  auto *descr = this->parent_->get_config_descriptor(this->char_handle_status_);
189  if (descr == nullptr) {
190  ESP_LOGW(TAG, "No config descriptor for status handle 0x%x. Will not be able to receive status notifications",
191  this->char_handle_status_);
192  result = false;
193  } else if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 ||
194  descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) {
195  ESP_LOGW(TAG, "Config descriptor 0x%x (uuid %s) is not a client config char uuid", this->char_handle_status_,
196  descr->uuid.to_string().c_str());
197  result = false;
198  } else {
199  this->config_descr_status_ = descr->handle;
200  }
201  }
202 
203  if (!this->char_handle_name_) {
204  chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_NAME_UUID);
205  if (chr == nullptr) {
206  ESP_LOGW(TAG, "[%s] No name service found at device, not a BedJet..?", this->get_name().c_str());
207  result = false;
208  } else {
209  this->char_handle_name_ = chr->handle;
210  auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
211  this->char_handle_name_, ESP_GATT_AUTH_REQ_NONE);
212  if (status) {
213  ESP_LOGI(TAG, "[%s] Unable to read name characteristic: %d", this->get_name().c_str(), status);
214  }
215  }
216  }
217 
218  ESP_LOGI(TAG, "[%s] Discovered service characteristics: ", this->get_name().c_str());
219  ESP_LOGI(TAG, " - Command char: 0x%x", this->char_handle_cmd_);
220  ESP_LOGI(TAG, " - Status char: 0x%x", this->char_handle_status_);
221  ESP_LOGI(TAG, " - config descriptor: 0x%x", this->config_descr_status_);
222  ESP_LOGI(TAG, " - Name char: 0x%x", this->char_handle_name_);
223 
224  return result;
225 }
226 
227 void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
228  esp_ble_gattc_cb_param_t *param) {
229  switch (event) {
230  case ESP_GATTC_DISCONNECT_EVT: {
231  ESP_LOGV(TAG, "Disconnected: reason=%d", param->disconnect.reason);
232  this->status_set_warning();
233  this->dispatch_state_(false);
234  break;
235  }
236  case ESP_GATTC_SEARCH_CMPL_EVT: {
237  auto result = this->discover_characteristics_();
238 
239  if (result) {
240  ESP_LOGD(TAG, "[%s] Services complete: obtained char handles.", this->get_name().c_str());
241  this->node_state = espbt::ClientState::ESTABLISHED;
242  this->set_notify_(true);
243 
244 #ifdef USE_TIME
245  if (this->time_id_ != nullptr) {
246  this->send_local_time();
247  }
248 #endif
249 
250  this->dispatch_state_(true);
251  } else {
252  ESP_LOGW(TAG, "[%s] Failed discovering service characteristics.", this->get_name().c_str());
253  this->parent()->set_enabled(false);
254  this->status_set_warning();
255  this->dispatch_state_(false);
256  }
257  break;
258  }
259  case ESP_GATTC_WRITE_DESCR_EVT: {
260  if (param->write.status != ESP_GATT_OK) {
261  if (param->write.status == ESP_GATT_INVALID_ATTR_LEN) {
262  // This probably means that our hack for notify_en (8 bit vs 16 bit) didn't work right.
263  // Should we try to fall back to BLEClient's way?
264  ESP_LOGW(TAG, "[%s] Invalid attr length writing descr at handle 0x%04d, status=%d", this->get_name().c_str(),
265  param->write.handle, param->write.status);
266  } else {
267  ESP_LOGW(TAG, "[%s] Error writing descr at handle 0x%04d, status=%d", this->get_name().c_str(),
268  param->write.handle, param->write.status);
269  }
270  break;
271  }
272  ESP_LOGD(TAG, "[%s] Write to handle 0x%04x status=%d", this->get_name().c_str(), param->write.handle,
273  param->write.status);
274  break;
275  }
276  case ESP_GATTC_WRITE_CHAR_EVT: {
277  if (param->write.status != ESP_GATT_OK) {
278  ESP_LOGW(TAG, "Error writing char at handle 0x%04d, status=%d", param->write.handle, param->write.status);
279  break;
280  }
281  if (param->write.handle == this->char_handle_cmd_) {
282  if (this->force_refresh_) {
283  // Command write was successful. Publish the pending state, hoping that notify will kick in.
284  // FIXME: better to wait until we know the status has changed
285  this->dispatch_status_();
286  }
287  }
288  break;
289  }
290  case ESP_GATTC_READ_CHAR_EVT: {
291  if (param->read.conn_id != this->parent_->get_conn_id())
292  break;
293  if (param->read.status != ESP_GATT_OK) {
294  ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
295  break;
296  }
297 
298  if (param->read.handle == this->char_handle_status_) {
299  // This is the additional packet that doesn't fit in the notify packet.
300  this->codec_->decode_extra(param->read.value, param->read.value_len);
301  this->status_packet_ready_();
302  } else if (param->read.handle == this->char_handle_name_) {
303  // The data should represent the name.
304  if (param->read.status == ESP_GATT_OK && param->read.value_len > 0) {
305  std::string bedjet_name(reinterpret_cast<char const *>(param->read.value), param->read.value_len);
306  ESP_LOGV(TAG, "[%s] Got BedJet name: '%s'", this->get_name().c_str(), bedjet_name.c_str());
307  this->set_name_(bedjet_name);
308  }
309  }
310  break;
311  }
312  case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
313  // This event means that ESP received the request to enable notifications on the client side. But we also have to
314  // tell the server that we want it to send notifications. Normally BLEClient parent would handle this
315  // automatically, but as soon as we set our status to Established, the parent is going to purge all the
316  // service/char/descriptor handles, and then get_config_descriptor() won't work anymore. There's no way to disable
317  // the BLEClient parent behavior, so our only option is to write the handle anyway, and hope a double-write
318  // doesn't break anything.
319 
320  if (param->reg_for_notify.handle != this->char_handle_status_) {
321  ESP_LOGW(TAG, "[%s] Register for notify on unexpected handle 0x%04x, expecting 0x%04x",
322  this->get_name().c_str(), param->reg_for_notify.handle, this->char_handle_status_);
323  break;
324  }
325 
327  this->last_notify_ = 0;
328  this->force_refresh_ = true;
329  break;
330  }
331  case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
332  // This event is not handled by the parent BLEClient, so we need to do this either way.
333  if (param->unreg_for_notify.handle != this->char_handle_status_) {
334  ESP_LOGW(TAG, "[%s] Unregister for notify on unexpected handle 0x%04x, expecting 0x%04x",
335  this->get_name().c_str(), param->unreg_for_notify.handle, this->char_handle_status_);
336  break;
337  }
338 
339  this->write_notify_config_descriptor_(false);
340  this->last_notify_ = 0;
341  // Now we wait until the next update() poll to re-register notify...
342  break;
343  }
344  case ESP_GATTC_NOTIFY_EVT: {
345  if (this->processing_)
346  break;
347 
348  if (param->notify.conn_id != this->parent_->get_conn_id()) {
349  ESP_LOGW(TAG, "[%s] Received notify event for unexpected parent conn: expect %x, got %x",
350  this->get_name().c_str(), this->parent_->get_conn_id(), param->notify.conn_id);
351  // FIXME: bug in BLEClient holding wrong conn_id.
352  }
353 
354  if (param->notify.handle != this->char_handle_status_) {
355  ESP_LOGW(TAG, "[%s] Unexpected notify handle, wanted %04X, got %04X", this->get_name().c_str(),
356  this->char_handle_status_, param->notify.handle);
357  break;
358  }
359 
360  // FIXME: notify events come in every ~200-300 ms, which is too fast to be helpful. So we
361  // throttle the updates to once every MIN_NOTIFY_THROTTLE (5 seconds).
362  // Another idea would be to keep notify off by default, and use update() as an opportunity to turn on
363  // notify to get enough data to update status, then turn off notify again.
364 
365  uint32_t now = millis();
366  auto delta = now - this->last_notify_;
367 
368  if (!this->force_refresh_ && this->codec_->compare(param->notify.value, param->notify.value_len)) {
369  // If the packet is meaningfully different, trigger children as well
370  this->force_refresh_ = true;
371  ESP_LOGV(TAG, "[%s] Incoming packet indicates a significant change.", this->get_name().c_str());
372  }
373 
374  if (this->last_notify_ == 0 || delta > MIN_NOTIFY_THROTTLE || this->force_refresh_) {
375  // Set reentrant flag to prevent processing multiple packets.
376  this->processing_ = true;
377  ESP_LOGVV(TAG, "[%s] Decoding packet: last=%" PRId32 ", delta=%" PRId32 ", force=%s", this->get_name().c_str(),
378  this->last_notify_, delta, this->force_refresh_ ? "y" : "n");
379  bool needs_extra = this->codec_->decode_notify(param->notify.value, param->notify.value_len);
380 
381  if (needs_extra) {
382  // This means the packet was partial, so read the status characteristic to get the second part.
383  // Ideally this will complete quickly. We won't process additional notification events until it does.
384  auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
385  this->char_handle_status_, ESP_GATT_AUTH_REQ_NONE);
386  if (status) {
387  ESP_LOGI(TAG, "[%s] Unable to read extended status packet", this->get_name().c_str());
388  }
389  } else {
390  this->status_packet_ready_();
391  }
392  }
393  break;
394  }
395  default:
396  ESP_LOGVV(TAG, "[%s] gattc unhandled event: enum=%d", this->get_name().c_str(), event);
397  break;
398  }
399 }
400 
402  this->last_notify_ = millis();
403  this->processing_ = false;
404 
405  if (this->force_refresh_) {
406  // If we requested an immediate update, do that now.
407  this->update();
408  this->force_refresh_ = false;
409  }
410 }
411 
420  auto handle = this->config_descr_status_;
421  if (handle == 0) {
422  ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", this->char_handle_status_);
423  return -1;
424  }
425 
426  // NOTE: BLEClient uses `uint8_t*` of length 1, but BLE spec requires 16 bits.
427  uint16_t notify_en = enable ? 1 : 0;
428  auto status = esp_ble_gattc_write_char_descr(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), handle,
429  sizeof(notify_en), (uint8_t *) &notify_en, ESP_GATT_WRITE_TYPE_RSP,
430  ESP_GATT_AUTH_REQ_NONE);
431  if (status) {
432  ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
433  return status;
434  }
435  ESP_LOGD(TAG, "[%s] wrote notify=%s to status config 0x%04x, for conn %d", this->get_name().c_str(),
436  enable ? "true" : "false", handle, this->parent_->get_conn_id());
437  return ESP_GATT_OK;
438 }
439 
440 /* Time Component */
441 
442 #ifdef USE_TIME
444  if (this->time_id_ != nullptr) {
445  ESPTime now = this->time_id_->now();
446  if (now.is_valid()) {
447  this->set_clock(now.hour, now.minute);
448  ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
449  }
450  } else {
451  ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
452  }
453 }
454 
456  if (this->time_id_ != nullptr) {
457  this->send_local_time();
458  this->time_id_->add_on_time_sync_callback([this] { this->send_local_time(); });
459  } else {
460  ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
461  }
462 }
463 #endif
464 
465 void BedJetHub::set_clock(uint8_t hour, uint8_t minute) {
466  if (!this->is_connected()) {
467  ESP_LOGV(TAG, "[%s] Not connected, cannot send time.", this->get_name().c_str());
468  return;
469  }
470 
471  BedjetPacket *pkt = this->codec_->get_set_time_request(hour, minute);
472  auto status = this->write_bedjet_packet_(pkt);
473  if (status) {
474  ESP_LOGW(TAG, "Failed setting BedJet clock: %d", status);
475  } else {
476  ESP_LOGD(TAG, "[%s] BedJet clock set to: %d:%02d", this->get_name().c_str(), hour, minute);
477  }
478 }
479 
480 /* Internal */
481 
484 
486  ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str());
487  ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id);
488  ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id());
489  LOG_UPDATE_INTERVAL(this)
490  ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size());
491  for (auto *child : this->children_) {
492  ESP_LOGCONFIG(TAG, " - %s", child->describe().c_str());
493  }
494 }
495 
497  for (auto *child : this->children_) {
498  child->on_bedjet_state(is_ready);
499  }
500 }
501 
503  auto *status = this->codec_->get_status_packet();
504 
505  if (!this->is_connected()) {
506  ESP_LOGD(TAG, "[%s] Not connected, will not send status.", this->get_name().c_str());
507  } else if (status != nullptr) {
508  ESP_LOGD(TAG, "[%s] Notifying %d children of latest status @%p.", this->get_name().c_str(), this->children_.size(),
509  status);
510  for (auto *child : this->children_) {
511  child->on_status(status);
512  }
513  } else {
514  uint32_t now = millis();
515  uint32_t diff = now - this->last_notify_;
516 
517  if (this->last_notify_ == 0) {
518  // This means we're connected and haven't received a notification, so it likely means that the BedJet is off.
519  // However, it could also mean that it's running, but failing to send notifications.
520  // We can try to unregister for notifications now, and then re-register, hoping to clear it up...
521  // But how do we know for sure which state we're in, and how do we actually clear out the buggy state?
522 
523  ESP_LOGI(TAG, "[%s] Still waiting for first GATT notify event.", this->get_name().c_str());
524  } else if (diff > NOTIFY_WARN_THRESHOLD) {
525  ESP_LOGW(TAG, "[%s] Last GATT notify was %" PRId32 " seconds ago.", this->get_name().c_str(), diff / 1000);
526  }
527 
528  if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
529  ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_);
530  // set_enabled(false) will only close the connection if state != IDLE.
531  this->parent()->set_state(espbt::ClientState::CONNECTING);
532  this->parent()->set_enabled(false);
533  this->parent()->set_enabled(true);
534  }
535  }
536 }
537 
539  this->children_.push_back(obj);
540  obj->set_parent(this);
541 }
542 
543 } // namespace bedjet
544 } // namespace esphome
545 
546 #endif
Enter Cool mode (fan only)
Definition: bedjet_const.h:55
void set_name_(const std::string &name)
Definition: bedjet_hub.h:155
bool button_dry()
Press the DRY button.
Definition: bedjet_hub.cpp:51
ESPTime now()
Get the time in the currently defined timezone.
Start the M2 biorhythm/preset program.
Definition: bedjet_const.h:68
void dispatch_state_(bool is_ready)
Definition: bedjet_hub.cpp:496
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
void setup_time_()
Initializes time sync callbacks to support syncing current time to the BedJet.
Definition: bedjet_hub.cpp:455
uint8_t write_bedjet_packet_(BedjetPacket *pkt)
Send the BedjetPacket to the device.
Definition: bedjet_hub.cpp:125
std::unique_ptr< BedjetCodec > codec_
Definition: bedjet_hub.h:164
uint8_t get_fan_index()
Return the fan speed index, in the range 0-19.
Definition: bedjet_hub.cpp:72
Enter Extended Heat mode (limited to 10 hours)
Definition: bedjet_const.h:63
bool button_ext_heat()
Press the EXT HT button.
Definition: bedjet_hub.cpp:48
A more user-friendly version of struct tm from time.h.
Definition: time.h:17
void send_local_time()
Attempts to sync the local time (via time_id) to the BedJet device.
Definition: bedjet_hub.cpp:443
static const uint32_t MIN_NOTIFY_THROTTLE
Definition: bedjet_hub.h:148
void add_on_time_sync_callback(std::function< void()> callback)
Enter Heat mode (limited to 4 hours)
Definition: bedjet_const.h:57
void upgrade_firmware()
Attempts to check for and apply firmware updates.
Definition: bedjet_hub.cpp:38
Turn BedJet off.
Definition: bedjet_const.h:53
bool button_memory1()
Press the M1 (memory recall) button.
Definition: bedjet_hub.cpp:53
void set_parent(T *parent)
Set the parent of this object.
Definition: helpers.h:530
void dump_config() override
Definition: bedjet_hub.cpp:485
uint8_t minute
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
BLEDescriptor * get_config_descriptor(uint16_t handle)
bool is_ready() const
Definition: component.cpp:144
Enter Dry mode (high speed, no heat)
Definition: bedjet_const.h:61
Start the M1 biorhythm/preset program.
Definition: bedjet_const.h:66
uint8_t set_notify_(bool enable)
Configures the local ESP BLE client to register (true) or unregister (false) for status notifications...
Definition: bedjet_hub.cpp:141
void set_enabled(bool enabled)
Definition: ble_client.cpp:38
bool button_heat()
Press the HEAT button.
Definition: bedjet_hub.cpp:47
uint8_t hour
bool button_memory2()
Press the M2 (memory recall) button.
Definition: bedjet_hub.cpp:54
void set_state(espbt::ClientState state) override
Definition: ble_client.cpp:70
uint8_t minute
minutes after the hour [0-59]
Definition: time.h:23
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: bedjet_hub.cpp:227
bool button_memory3()
Press the M3 (memory recall) button.
Definition: bedjet_hub.cpp:55
bool button_turbo()
Press the TURBO button.
Definition: bedjet_hub.cpp:49
std::vector< BedJetClient * > children_
Definition: bedjet_hub.h:137
Start the M3 biorhythm/preset program.
Definition: bedjet_const.h:70
bool is_valid() const
Check if this ESPTime is valid (all fields in range and year is greater than 2018) ...
Definition: time.h:61
bool set_time_remaining(uint8_t hours, uint8_t mins)
Set the operational runtime remaining.
Definition: bedjet_hub.cpp:90
bool set_target_temp(float temp_c)
Set the target temperature to temp_c in °C.
Definition: bedjet_hub.cpp:80
bool send_button(BedjetButton button)
Send the button.
Definition: bedjet_hub.cpp:101
uint16_t get_time_remaining()
Return the remaining runtime, in seconds.
Definition: bedjet_hub.cpp:115
void set_clock(uint8_t hour, uint8_t minute)
Attempt to set the BedJet device&#39;s clock to the specified time.
Definition: bedjet_hub.cpp:465
uint8_t status
Definition: bl0942.h:74
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
bool set_fan_index(uint8_t fan_speed_index)
Set the fan speed to a stepped index in the range 0-19.
Definition: bedjet_hub.cpp:57
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
time::RealTimeClock * time_id_
Definition: bedjet_hub.h:144
uint8_t write_notify_config_descriptor_(bool enable)
Reimplementation of BLEClient.gattc_event_handler() for ESP_GATTC_REG_FOR_NOTIFY_EVT.
Definition: bedjet_hub.cpp:419
Enter Turbo mode (high heat, limited to 10 minutes)
Definition: bedjet_const.h:59
uint8_t hour
hours since midnight [0-23]
Definition: time.h:25
bool button_cool()
Press the COOL button.
Definition: bedjet_hub.cpp:50
void register_child(BedJetClient *obj)
Register a BedJetClient child component.
Definition: bedjet_hub.cpp:538
bool button_off()
Press the OFF button.
Definition: bedjet_hub.cpp:52
Request a firmware update. This will also restart the Bedjet.
Definition: bedjet_const.h:80
static const uint32_t NOTIFY_WARN_THRESHOLD
Definition: bedjet_hub.h:149
espbt::ClientState node_state
Definition: ble_client.h:38