11 static const LogString *bedjet_button_to_string(
BedjetButton button) {
14 return LOG_STR(
"OFF");
16 return LOG_STR(
"COOL");
18 return LOG_STR(
"HEAT");
20 return LOG_STR(
"EXT HT");
22 return LOG_STR(
"TURBO");
24 return LOG_STR(
"DRY");
32 return LOG_STR(
"unknown");
43 ESP_LOGW(TAG,
"[%s] MAGIC_UPDATE button failed, status=%d", this->
get_name().c_str(),
status);
58 if (fan_speed_index > 19) {
59 ESP_LOGW(TAG,
"Invalid fan speed index %d, expecting 0-19.", fan_speed_index);
63 auto *pkt = this->
codec_->get_set_fan_speed_request(fan_speed_index);
67 ESP_LOGW(TAG,
"[%s] writing fan speed failed, status=%d", this->
get_name().c_str(),
status);
81 auto *pkt = this->
codec_->get_set_target_temp_request(temp_c);
85 ESP_LOGW(TAG,
"[%s] writing target temp failed, status=%d", this->
get_name().c_str(),
status);
92 auto *pkt = this->
codec_->get_set_runtime_remaining_request(hours, mins);
96 ESP_LOGW(TAG,
"[%s] writing remaining runtime failed, status=%d", this->
get_name().c_str(),
status);
102 auto *pkt = this->
codec_->get_button_request(button);
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);
109 ESP_LOGD(TAG,
"[%s] writing button %s success", this->
get_name().c_str(),
110 LOG_STR_ARG(bedjet_button_to_string(button)));
118 return status->time_remaining_secs +
status->time_remaining_mins * 60 +
status->time_remaining_hrs * 3600;
128 ESP_LOGI(TAG,
"[%s] Cannot write packet: Not connected, enabled=false", this->
get_name().c_str());
130 ESP_LOGW(TAG,
"[%s] Cannot write packet: Not connected", this->
get_name().c_str());
136 ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
147 ESP_LOGW(TAG,
"[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->
get_name().c_str(), status);
153 ESP_LOGW(TAG,
"[%s] esp_ble_gattc_unregister_for_notify failed, status=%d", this->
get_name().c_str(), status);
156 ESP_LOGV(TAG,
"[%s] set_notify: enable=%d; result=%d", this->
get_name().c_str(), enable, status);
166 if (chr ==
nullptr) {
167 ESP_LOGW(TAG,
"[%s] No control service found at device, not a BedJet..?", this->
get_name().c_str());
176 if (chr ==
nullptr) {
177 ESP_LOGW(TAG,
"[%s] No status service found at device, not a BedJet..?", this->
get_name().c_str());
189 if (descr ==
nullptr) {
190 ESP_LOGW(TAG,
"No config descriptor for status handle 0x%x. Will not be able to receive status notifications",
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());
205 if (chr ==
nullptr) {
206 ESP_LOGW(TAG,
"[%s] No name service found at device, not a BedJet..?", this->
get_name().c_str());
213 ESP_LOGI(TAG,
"[%s] Unable to read name characteristic: %d", this->
get_name().c_str(),
status);
218 ESP_LOGI(TAG,
"[%s] Discovered service characteristics: ", this->
get_name().c_str());
228 esp_ble_gattc_cb_param_t *param) {
230 case ESP_GATTC_DISCONNECT_EVT: {
231 ESP_LOGV(TAG,
"Disconnected: reason=%d", param->disconnect.reason);
236 case ESP_GATTC_SEARCH_CMPL_EVT: {
240 ESP_LOGD(TAG,
"[%s] Services complete: obtained char handles.", this->
get_name().c_str());
241 this->
node_state = espbt::ClientState::ESTABLISHED;
252 ESP_LOGW(TAG,
"[%s] Failed discovering service characteristics.", this->
get_name().c_str());
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) {
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);
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);
272 ESP_LOGD(TAG,
"[%s] Write to handle 0x%04x status=%d", this->
get_name().c_str(), param->write.handle,
273 param->write.status);
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);
281 if (param->write.handle == this->char_handle_cmd_) {
290 case ESP_GATTC_READ_CHAR_EVT: {
291 if (param->read.conn_id != this->parent_->get_conn_id())
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);
298 if (param->read.handle == this->char_handle_status_) {
300 this->
codec_->decode_extra(param->read.value, param->read.value_len);
302 }
else if (param->read.handle == this->char_handle_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());
312 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
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_);
331 case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
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_);
344 case ESP_GATTC_NOTIFY_EVT: {
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",
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(),
368 if (!this->
force_refresh_ && this->
codec_->compare(param->notify.value, param->notify.value_len)) {
371 ESP_LOGV(TAG,
"[%s] Incoming packet indicates a significant change.", this->
get_name().c_str());
377 ESP_LOGVV(TAG,
"[%s] Decoding packet: last=%" PRId32
", delta=%" PRId32
", force=%s", this->
get_name().c_str(),
379 bool needs_extra = this->
codec_->decode_notify(param->notify.value, param->notify.value_len);
387 ESP_LOGI(TAG,
"[%s] Unable to read extended status packet", this->
get_name().c_str());
396 ESP_LOGVV(TAG,
"[%s] gattc unhandled event: enum=%d", this->
get_name().c_str(), event);
427 uint16_t notify_en = enable ? 1 : 0;
429 sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP,
430 ESP_GATT_AUTH_REQ_NONE);
432 ESP_LOGW(TAG,
"esp_ble_gattc_write_char_descr error, status=%d",
status);
435 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.");
460 ESP_LOGI(TAG,
"`time_id` is not configured: will not sync BedJet clock.");
467 ESP_LOGV(TAG,
"[%s] Not connected, cannot send time.", this->
get_name().c_str());
474 ESP_LOGW(TAG,
"Failed setting BedJet clock: %d",
status);
476 ESP_LOGD(TAG,
"[%s] BedJet clock set to: %d:%02d", this->
get_name().c_str(), hour, minute);
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());
492 ESP_LOGCONFIG(TAG,
" - %s", child->describe().c_str());
498 child->on_bedjet_state(is_ready);
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(),
517 if (this->last_notify_ == 0) {
523 ESP_LOGI(TAG,
"[%s] Still waiting for first GATT notify event.", this->
get_name().c_str());
525 ESP_LOGW(TAG,
"[%s] Last GATT notify was %" PRId32
" seconds ago.", this->
get_name().c_str(), diff / 1000);
529 ESP_LOGW(TAG,
"[%s] Timed out after %" PRId32
" 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.
ESPTime now()
Get the time in the currently defined timezone.
Start the M2 biorhythm/preset program.
void dispatch_state_(bool is_ready)
void status_set_warning(const char *message="unspecified")
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_
A more user-friendly version of struct tm from time.h.
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
void add_on_time_sync_callback(std::function< void()> callback)
Enter Heat mode (limited to 4 hours)
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_
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.
bool button_memory2()
Press the M2 (memory recall) button.
void set_state(espbt::ClientState state) override
uint8_t minute
minutes after the hour [0-59]
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.
bool is_valid() const
Check if this ESPTime is valid (all fields in range and year is greater than 2018) ...
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.
Implementation of SPI Controller mode.
time::RealTimeClock * time_id_
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)
uint8_t hour
hours since midnight [0-23]
bool discover_characteristics_()
bool button_cool()
Press the COOL button.
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