10 static const LogString *bedjet_button_to_string(
BedjetButton button) {
13 return LOG_STR(
"OFF");
15 return LOG_STR(
"COOL");
17 return LOG_STR(
"HEAT");
19 return LOG_STR(
"EXT HT");
21 return LOG_STR(
"TURBO");
23 return LOG_STR(
"DRY");
31 return LOG_STR(
"unknown");
42 ESP_LOGW(TAG,
"[%s] MAGIC_UPDATE button failed, status=%d", this->
get_name().c_str(),
status);
57 if (fan_speed_index > 19) {
58 ESP_LOGW(TAG,
"Invalid fan speed index %d, expecting 0-19.", fan_speed_index);
62 auto *pkt = this->
codec_->get_set_fan_speed_request(fan_speed_index);
66 ESP_LOGW(TAG,
"[%s] writing fan speed failed, status=%d", this->
get_name().c_str(),
status);
80 auto *pkt = this->
codec_->get_set_target_temp_request(temp_c);
84 ESP_LOGW(TAG,
"[%s] writing target temp failed, status=%d", this->
get_name().c_str(),
status);
91 auto *pkt = this->
codec_->get_set_runtime_remaining_request(hours, mins);
95 ESP_LOGW(TAG,
"[%s] writing remaining runtime failed, status=%d", this->
get_name().c_str(),
status);
101 auto *pkt = this->
codec_->get_button_request(button);
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);
108 ESP_LOGD(TAG,
"[%s] writing button %s success", this->
get_name().c_str(),
109 LOG_STR_ARG(bedjet_button_to_string(button)));
117 return status->time_remaining_secs +
status->time_remaining_mins * 60 +
status->time_remaining_hrs * 3600;
127 ESP_LOGI(TAG,
"[%s] Cannot write packet: Not connected, enabled=false", this->
get_name().c_str());
129 ESP_LOGW(TAG,
"[%s] Cannot write packet: Not connected", this->
get_name().c_str());
135 ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
146 ESP_LOGW(TAG,
"[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->
get_name().c_str(), status);
152 ESP_LOGW(TAG,
"[%s] esp_ble_gattc_unregister_for_notify failed, status=%d", this->
get_name().c_str(), status);
155 ESP_LOGV(TAG,
"[%s] set_notify: enable=%d; result=%d", this->
get_name().c_str(), enable, status);
165 if (chr ==
nullptr) {
166 ESP_LOGW(TAG,
"[%s] No control service found at device, not a BedJet..?", this->
get_name().c_str());
175 if (chr ==
nullptr) {
176 ESP_LOGW(TAG,
"[%s] No status service found at device, not a BedJet..?", this->
get_name().c_str());
188 if (descr ==
nullptr) {
189 ESP_LOGW(TAG,
"No config descriptor for status handle 0x%x. Will not be able to receive status notifications",
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());
204 if (chr ==
nullptr) {
205 ESP_LOGW(TAG,
"[%s] No name service found at device, not a BedJet..?", this->
get_name().c_str());
212 ESP_LOGI(TAG,
"[%s] Unable to read name characteristic: %d", this->
get_name().c_str(),
status);
217 ESP_LOGI(TAG,
"[%s] Discovered service characteristics: ", this->
get_name().c_str());
227 esp_ble_gattc_cb_param_t *param) {
229 case ESP_GATTC_DISCONNECT_EVT: {
230 ESP_LOGV(TAG,
"Disconnected: reason=%d", param->disconnect.reason);
235 case ESP_GATTC_SEARCH_CMPL_EVT: {
239 ESP_LOGD(TAG,
"[%s] Services complete: obtained char handles.", this->
get_name().c_str());
240 this->
node_state = espbt::ClientState::ESTABLISHED;
251 ESP_LOGW(TAG,
"[%s] Failed discovering service characteristics.", this->
get_name().c_str());
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) {
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);
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);
271 ESP_LOGD(TAG,
"[%s] Write to handle 0x%04x status=%d", this->
get_name().c_str(), param->write.handle,
272 param->write.status);
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);
280 if (param->write.handle == this->char_handle_cmd_) {
289 case ESP_GATTC_READ_CHAR_EVT: {
290 if (param->read.conn_id != this->parent_->get_conn_id())
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);
297 if (param->read.handle == this->char_handle_status_) {
299 this->
codec_->decode_extra(param->read.value, param->read.value_len);
301 }
else if (param->read.handle == this->char_handle_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());
311 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
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_);
330 case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
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_);
343 case ESP_GATTC_NOTIFY_EVT: {
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",
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(),
367 if (!this->
force_refresh_ && this->
codec_->compare(param->notify.value, param->notify.value_len)) {
370 ESP_LOGV(TAG,
"[%s] Incoming packet indicates a significant change.", this->
get_name().c_str());
376 ESP_LOGVV(TAG,
"[%s] Decoding packet: last=%d, delta=%d, force=%s", this->
get_name().c_str(),
378 bool needs_extra = this->
codec_->decode_notify(param->notify.value, param->notify.value_len);
386 ESP_LOGI(TAG,
"[%s] Unable to read extended status packet", this->
get_name().c_str());
395 ESP_LOGVV(TAG,
"[%s] gattc unhandled event: enum=%d", this->
get_name().c_str(), event);
426 uint16_t notify_en = enable ? 1 : 0;
428 sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP,
429 ESP_GATT_AUTH_REQ_NONE);
431 ESP_LOGW(TAG,
"esp_ble_gattc_write_char_descr error, status=%d",
status);
434 ESP_LOGD(TAG,
"[%s] wrote notify=%s to status config 0x%04x, for conn %d", this->
get_name().c_str(),
448 ESP_LOGD(TAG,
"Using time component to set BedJet clock: %d:%02d", now.
hour, now.
minute);
451 ESP_LOGI(TAG,
"`time_id` is not configured: will not sync BedJet clock.");
459 time_id->add_on_time_sync_callback([
this] { this->
send_local_time(); });
461 ESP_LOGI(TAG,
"`time_id` is not configured: will not sync BedJet clock.");
468 ESP_LOGV(TAG,
"[%s] Not connected, cannot send time.", this->
get_name().c_str());
475 ESP_LOGW(TAG,
"Failed setting BedJet clock: %d",
status);
477 ESP_LOGD(TAG,
"[%s] BedJet clock set to: %d:%02d", this->
get_name().c_str(), hour, minute);
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());
493 ESP_LOGCONFIG(TAG,
" - %s", child->describe().c_str());
499 child->on_bedjet_state(is_ready);
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(),
518 if (this->last_notify_ == 0) {
524 ESP_LOGI(TAG,
"[%s] Still waiting for first GATT notify event.", this->
get_name().c_str());
526 ESP_LOGW(TAG,
"[%s] Last GATT notify was %d seconds ago.", this->
get_name().c_str(), diff / 1000);
530 ESP_LOGW(TAG,
"[%s] Timed out after %d sec. Retrying...", this->
get_name().c_str(), this->
timeout_);
Enter Cool mode (fan only)
void set_name_(const std::string &name)
bool button_dry()
Press the DRY button.
Start the M2 biorhythm/preset program.
void dispatch_state_(bool is_ready)
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.
uint8_t write_bedjet_packet_(BedjetPacket *pkt)
Send the BedjetPacket to the device.
std::unique_ptr< BedjetCodec > codec_
uint8_t get_fan_index()
Return the fan speed index, in the range 0-19.
Enter Extended Heat mode (limited to 10 hours)
bool button_ext_heat()
Press the EXT HT button.
uint16_t char_handle_status_
uint8_t * get_remote_bda()
void send_local_time()
Attempts to sync the local time (via time_id) to the BedJet device.
static const uint32_t MIN_NOTIFY_THROTTLE
Enter Heat mode (limited to 4 hours)
optional< time::RealTimeClock * > time_id_
void upgrade_firmware()
Attempts to check for and apply firmware updates.
bool button_memory1()
Press the M1 (memory recall) button.
void set_parent(T *parent)
Set the parent of this object.
uint16_t get_conn_id() const
void dump_config() override
uint32_t IRAM_ATTR HOT millis()
BLEDescriptor * get_config_descriptor(uint16_t handle)
uint16_t char_handle_name_
uint8_t minute
minutes after the hour [0-59]
Enter Dry mode (high speed, no heat)
Start the M1 biorhythm/preset program.
uint8_t set_notify_(bool enable)
Configures the local ESP BLE client to register (true) or unregister (false) for status notifications...
void set_enabled(bool enabled)
bool button_heat()
Press the HEAT button.
A more user-friendly version of struct tm from time.h.
bool button_memory2()
Press the M2 (memory recall) button.
void set_state(espbt::ClientState state) override
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
bool button_memory3()
Press the M3 (memory recall) button.
void status_packet_ready_()
bool button_turbo()
Press the TURBO button.
std::vector< BedJetClient * > children_
Start the M3 biorhythm/preset program.
void status_set_warning()
bool set_time_remaining(uint8_t hours, uint8_t mins)
Set the operational runtime remaining.
bool set_target_temp(float temp_c)
Set the target temperature to temp_c in °C.
bool send_button(BedjetButton button)
Send the button.
uint16_t get_time_remaining()
Return the remaining runtime, in seconds.
void set_clock(uint8_t hour, uint8_t minute)
Attempt to set the BedJet device's clock to the specified time.
uint16_t config_descr_status_
uint16_t char_handle_cmd_
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.
uint8_t write_notify_config_descriptor_(bool enable)
Reimplementation of BLEClient.gattc_event_handler() for ESP_GATTC_REG_FOR_NOTIFY_EVT.
Enter Turbo mode (high heat, limited to 10 minutes)
bool discover_characteristics_()
bool button_cool()
Press the COOL button.
uint8_t hour
hours since midnight [0-23]
void register_child(BedJetClient *obj)
Register a BedJetClient child component.
bool button_off()
Press the OFF button.
Request a firmware update. This will also restart the Bedjet.
static const uint32_t NOTIFY_WARN_THRESHOLD
espbt::ClientState node_state