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