mirror of
https://github.com/esphome/esphome.git
synced 2025-04-15 07:10:33 +01:00
Add diesel heater BLE component
This commit is contained in:
parent
56e305f986
commit
45766a1a32
29
esphome/components/diesel_heater_ble/__init__.py
Normal file
29
esphome/components/diesel_heater_ble/__init__.py
Normal file
@ -0,0 +1,29 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import ble_client
|
||||
from esphome.components import sensor
|
||||
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@warehog"]
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_HEATER_ID = "diesel_heater_ble"
|
||||
|
||||
diesel_heater_ble_ns = cg.esphome_ns.namespace("diesel_heater_ble")
|
||||
DieselHeaterBLE = diesel_heater_ble_ns.class_("DieselHeaterBLE", sensor.Sensor, cg.Component, ble_client.BLEClientNode)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DieselHeaterBLE)
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
12
esphome/components/diesel_heater_ble/button.h
Normal file
12
esphome/components/diesel_heater_ble/button.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "heater.h"
|
||||
#include "messages.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
} // namespace diesel_heater_ble
|
||||
} // namespace esphome
|
24
esphome/components/diesel_heater_ble/button.py
Normal file
24
esphome/components/diesel_heater_ble/button.py
Normal file
@ -0,0 +1,24 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_POWER,
|
||||
ICON_POWER,
|
||||
DEVICE_CLASS_BUTTON
|
||||
)
|
||||
|
||||
from . import DieselHeaterBLE, CONF_HEATER_ID, diesel_heater_ble_ns
|
||||
|
||||
CODEOWNERS = ["@warehog"]
|
||||
DEPENDENCIES = ["diesel_heater_ble"]
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HEATER_ID): cv.use_id(DieselHeaterBLE),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_HEATER_ID])
|
219
esphome/components/diesel_heater_ble/heater.cpp
Normal file
219
esphome/components/diesel_heater_ble/heater.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
#include "heater.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
const char *TAG = "diesel_heater_ble";
|
||||
|
||||
void DieselHeaterBLE::loop() {
|
||||
if (this->node_state == esp32_ble_tracker::ClientState::ESTABLISHED) {
|
||||
if (this->last_request_ + 1000 < millis()) {
|
||||
this->last_request_ = millis();
|
||||
uint8_t data[8] = {0xaa, 0x55, 0x0c, 0x22, 0x01, 0x00, 0x00, 0x2f};
|
||||
this->ble_write_chr(this->parent()->get_gattc_if(), this->parent()->get_remote_bda(), this->handle_, data, sizeof(data));
|
||||
}
|
||||
this->update_sensors(this->state_);
|
||||
}
|
||||
}
|
||||
|
||||
void DieselHeaterBLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
ESP_LOGD(TAG, "GATT client opened.");
|
||||
break;
|
||||
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
ESP_LOGD(TAG, "GATT client disconnected.");
|
||||
this->node_state = esp32_ble_tracker::ClientState::DISCONNECTING;
|
||||
this->handle_ = 0;
|
||||
break;
|
||||
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT:
|
||||
if (param->search_cmpl.status != ESP_GATT_OK) {
|
||||
ESP_LOGD(TAG, "Service search failed, status: %d", param->search_cmpl.status);
|
||||
break;
|
||||
}
|
||||
this->ble_register_for_notify(this->parent()->get_gattc_if(), this->parent()->get_remote_bda());
|
||||
break;
|
||||
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT:
|
||||
if (param->reg_for_notify.status != ESP_GATT_OK) {
|
||||
ESP_LOGD(TAG, "Register for notify failed, status: %d", param->reg_for_notify.status);
|
||||
break;
|
||||
}
|
||||
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
||||
break;
|
||||
|
||||
case ESP_GATTC_NOTIFY_EVT:
|
||||
if (param->notify.conn_id != this->parent()->get_conn_id()) break;
|
||||
if (param->notify.handle == this->handle_) {
|
||||
std::vector<uint8_t> data(param->notify.value, param->notify.value + param->notify.value_len);
|
||||
this->on_notification_received(data);
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GATTC_READ_CHAR_EVT:
|
||||
case ESP_GATTC_WRITE_CHAR_EVT:
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "Unhandled GATT event: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DieselHeaterBLE::ble_write_chr(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, uint16_t handle, uint8_t *data, uint16_t len) {
|
||||
esp_err_t ret = esp_ble_gattc_write_char(gattc_if, 0, handle, len, data, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Write characteristic failed, status: %d", ret);
|
||||
return false;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Write characteristic success: %s", format_hex_pretty(std::vector<uint8_t>(data, data + len)).c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DieselHeaterBLE::ble_read_chr(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, uint16_t handle) {
|
||||
esp_err_t ret = esp_ble_gattc_read_char(gattc_if, 0, handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Read characteristic failed, status: %d", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DieselHeaterBLE::ble_register_for_notify(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda) {
|
||||
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->characteristic_uuid_);
|
||||
if (!chr) {
|
||||
ESP_LOGD(TAG, "Characteristic not found.");
|
||||
return false;
|
||||
}
|
||||
this->handle_ = chr->handle;
|
||||
esp_err_t ret = esp_ble_gattc_register_for_notify(gattc_if, remote_bda, this->handle_);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Register for notify failed, status: %d", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DieselHeaterBLE::on_notification_received(const std::vector<uint8_t> &data) {
|
||||
// ESP_LOGD(TAG, "Notification received: %s", format_hex_pretty(data).c_str());
|
||||
bool ret = ResponseParser::parse(data, this->state_);
|
||||
if (!ret) {
|
||||
ESP_LOGD(TAG, "Failed to parse response.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DieselHeaterBLE::update_sensors(const HeaterState &new_state) {
|
||||
if (running_state_ != nullptr && running_state_->state != new_state.runningstate) {
|
||||
if (this->power_switch_ != nullptr) {
|
||||
this->power_switch_->publish_state(new_state.runningstate);
|
||||
}
|
||||
running_state_->publish_state(new_state.runningstate);
|
||||
return;
|
||||
}
|
||||
if (error_code_ != nullptr && error_code_->state != new_state.errcode) {
|
||||
error_code_->publish_state(new_state.errcode);
|
||||
return;
|
||||
}
|
||||
if (running_step_ != nullptr && running_step_->state != new_state.runningstep) {
|
||||
running_step_->publish_state(new_state.runningstep);
|
||||
return;
|
||||
}
|
||||
if (altitude_ != nullptr && altitude_->state != new_state.altitude) {
|
||||
altitude_->publish_state(new_state.altitude);
|
||||
return;
|
||||
}
|
||||
if (running_mode_ != nullptr && running_mode_->state != new_state.runningmode) {
|
||||
running_mode_->publish_state(new_state.runningmode);
|
||||
return;
|
||||
}
|
||||
if (set_level_ != nullptr && set_level_->state != new_state.setlevel) {
|
||||
if (this->power_level_number_ != nullptr) {
|
||||
this->power_level_number_->publish_state(new_state.setlevel);
|
||||
}
|
||||
set_level_->publish_state(new_state.setlevel);
|
||||
return;
|
||||
}
|
||||
if (set_temp_ != nullptr && set_temp_->state != new_state.settemp) {
|
||||
if (this->set_temp_number_ != nullptr) {
|
||||
this->set_temp_number_->publish_state(new_state.settemp);
|
||||
}
|
||||
set_temp_->publish_state(new_state.settemp);
|
||||
return;
|
||||
}
|
||||
if (set_temp_number_ != nullptr && set_temp_number_->state != new_state.settemp) {
|
||||
set_temp_number_->publish_state(new_state.settemp);
|
||||
return;
|
||||
}
|
||||
if (supply_voltage_ != nullptr && supply_voltage_->state != new_state.supplyvoltage) {
|
||||
supply_voltage_->publish_state(new_state.supplyvoltage);
|
||||
return;
|
||||
}
|
||||
if (case_temp_ != nullptr && case_temp_->state != new_state.casetemp) {
|
||||
case_temp_->publish_state(new_state.casetemp);
|
||||
return;
|
||||
}
|
||||
if (cab_temp_ != nullptr && cab_temp_->state != new_state.cabtemp) {
|
||||
cab_temp_->publish_state(new_state.cabtemp);
|
||||
return;
|
||||
}
|
||||
if (start_time_ != nullptr && start_time_->state != new_state.sttime) {
|
||||
start_time_->publish_state(new_state.sttime);
|
||||
return;
|
||||
}
|
||||
if (auto_time_ != nullptr && auto_time_->state != new_state.autotime) {
|
||||
auto_time_->publish_state(new_state.autotime);
|
||||
return;
|
||||
}
|
||||
if (run_time_ != nullptr && run_time_->state != new_state.runtime) {
|
||||
run_time_->publish_state(new_state.runtime);
|
||||
return;
|
||||
}
|
||||
if (is_auto_ != nullptr && is_auto_->state != new_state.isauto) {
|
||||
is_auto_->publish_state(new_state.isauto);
|
||||
return;
|
||||
}
|
||||
// if (language_ != nullptr && language_->state != new_state.language) {
|
||||
// language_->publish_state(new_state.language);
|
||||
// return;
|
||||
// }
|
||||
if (temp_offset_ != nullptr && temp_offset_->state != new_state.tempoffset) {
|
||||
temp_offset_->publish_state(new_state.tempoffset);
|
||||
return;
|
||||
}
|
||||
if (tank_volume_ != nullptr && tank_volume_->state != new_state.tankvolume) {
|
||||
tank_volume_->publish_state(new_state.tankvolume);
|
||||
return;
|
||||
}
|
||||
if (oil_pump_type_ != nullptr && oil_pump_type_->state != new_state.oilpumptype) {
|
||||
oil_pump_type_->publish_state(new_state.oilpumptype);
|
||||
return;
|
||||
}
|
||||
if (rf433_on_off_ != nullptr && rf433_on_off_->state != static_cast<float>(new_state.rf433onoff)) {
|
||||
rf433_on_off_->publish_state(new_state.rf433onoff);
|
||||
return;
|
||||
}
|
||||
if (temp_unit_ != nullptr && temp_unit_->state != new_state.tempunit) {
|
||||
temp_unit_->publish_state(new_state.tempunit);
|
||||
return;
|
||||
}
|
||||
if (altitude_unit_ != nullptr && altitude_unit_->state != new_state.altiunit) {
|
||||
altitude_unit_->publish_state(new_state.altiunit);
|
||||
return;
|
||||
}
|
||||
if (automatic_heating_ != nullptr && automatic_heating_->state != new_state.automaticheating) {
|
||||
automatic_heating_->publish_state(new_state.automaticheating);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace diesel_heater_ble
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
120
esphome/components/diesel_heater_ble/heater.h
Normal file
120
esphome/components/diesel_heater_ble/heater.h
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "messages.h"
|
||||
#include "state.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
|
||||
class DieselHeaterBLE : public Component, public ble_client::BLEClientNode {
|
||||
public:
|
||||
DieselHeaterBLE() = default;
|
||||
|
||||
void loop() 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;
|
||||
|
||||
void sent_request(const std::vector<uint8_t> &data) {
|
||||
std::vector<uint8_t> data_(data);
|
||||
this->ble_write_chr(this->parent()->get_gattc_if(), this->parent()->get_remote_bda(), this->handle_, data_.data(), data.size());
|
||||
}
|
||||
|
||||
bool ble_write_chr(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, uint16_t handle, uint8_t *data, uint16_t len);
|
||||
bool ble_read_chr(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, uint16_t handle);
|
||||
bool ble_register_for_notify(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda);
|
||||
|
||||
void on_notification_received(const std::vector<uint8_t> &data);
|
||||
void update_sensors(const HeaterState &new_state);
|
||||
|
||||
// Sensor setters
|
||||
void set_running_state(sensor::Sensor *sensor) { running_state_ = sensor; }
|
||||
void set_error_code(sensor::Sensor *sensor) { error_code_ = sensor; }
|
||||
void set_running_step(sensor::Sensor *sensor) { running_step_ = sensor; }
|
||||
void set_altitude(sensor::Sensor *sensor) { altitude_ = sensor; }
|
||||
void set_running_mode(sensor::Sensor *sensor) { running_mode_ = sensor; }
|
||||
void set_set_level(sensor::Sensor *sensor) { set_level_ = sensor; }
|
||||
void set_set_temp(sensor::Sensor *sensor) { set_temp_ = sensor; }
|
||||
void set_supply_voltage(sensor::Sensor *sensor) { supply_voltage_ = sensor; }
|
||||
void set_case_temp(sensor::Sensor *sensor) { case_temp_ = sensor; }
|
||||
void set_cab_temp(sensor::Sensor *sensor) { cab_temp_ = sensor; }
|
||||
void set_start_time(sensor::Sensor *sensor) { start_time_ = sensor; }
|
||||
void set_auto_time(sensor::Sensor *sensor) { auto_time_ = sensor; }
|
||||
void set_run_time(sensor::Sensor *sensor) { run_time_ = sensor; }
|
||||
void set_is_auto(sensor::Sensor *sensor) { is_auto_ = sensor; }
|
||||
void set_language(sensor::Sensor *sensor) { language_ = sensor; }
|
||||
void set_temp_offset(sensor::Sensor *sensor) { temp_offset_ = sensor; }
|
||||
void set_tank_volume(sensor::Sensor *sensor) { tank_volume_ = sensor; }
|
||||
void set_oil_pump_type(sensor::Sensor *sensor) { oil_pump_type_ = sensor; }
|
||||
void set_rf433_on_off(sensor::Sensor *sensor) { rf433_on_off_ = sensor; }
|
||||
void set_temp_unit(sensor::Sensor *sensor) { temp_unit_ = sensor; }
|
||||
void set_altitude_unit(sensor::Sensor *sensor) { altitude_unit_ = sensor; }
|
||||
void set_automatic_heating(sensor::Sensor *sensor) { automatic_heating_ = sensor; }
|
||||
|
||||
void set_power_level_number(number::Number *number) { power_level_number_ = number; }
|
||||
void set_set_temp_number(number::Number *number) { set_temp_number_ = number; }
|
||||
|
||||
void set_power_switch(switch_::Switch *sw) { power_switch_ = sw; }
|
||||
|
||||
HeaterState get_state() {
|
||||
return this->state_;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint16_t handle_{0};
|
||||
esp32_ble_tracker::ESPBTUUID service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw("0000ffe0-0000-1000-8000-00805f9b34fb");
|
||||
esp32_ble_tracker::ESPBTUUID characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw("0000ffe1-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
HeaterState state_;
|
||||
|
||||
bool response_received_{false};
|
||||
uint32_t last_request_{0};
|
||||
uint32_t last_update_{0};
|
||||
|
||||
// Sensor fields
|
||||
sensor::Sensor *running_state_{};
|
||||
sensor::Sensor *error_code_{};
|
||||
sensor::Sensor *running_step_{};
|
||||
sensor::Sensor *altitude_{};
|
||||
sensor::Sensor *running_mode_{};
|
||||
sensor::Sensor *set_level_{};
|
||||
sensor::Sensor *set_temp_{};
|
||||
sensor::Sensor *supply_voltage_{};
|
||||
sensor::Sensor *case_temp_{};
|
||||
sensor::Sensor *cab_temp_{};
|
||||
sensor::Sensor *start_time_{};
|
||||
sensor::Sensor *auto_time_{};
|
||||
sensor::Sensor *run_time_{};
|
||||
sensor::Sensor *is_auto_{};
|
||||
sensor::Sensor *language_{};
|
||||
sensor::Sensor *temp_offset_{};
|
||||
sensor::Sensor *tank_volume_{};
|
||||
sensor::Sensor *oil_pump_type_{};
|
||||
sensor::Sensor *rf433_on_off_{};
|
||||
sensor::Sensor *temp_unit_{};
|
||||
sensor::Sensor *altitude_unit_{};
|
||||
sensor::Sensor *automatic_heating_{};
|
||||
|
||||
number::Number *power_level_number_{};
|
||||
number::Number *set_temp_number_{};
|
||||
|
||||
switch_::Switch *power_switch_{};
|
||||
};
|
||||
|
||||
} // namespace diesel_heater_ble
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
230
esphome/components/diesel_heater_ble/messages.h
Normal file
230
esphome/components/diesel_heater_ble/messages.h
Normal file
@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include "state.h"
|
||||
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
class ResponseParser {
|
||||
public:
|
||||
static std::vector<uint8_t> decrypt(const std::vector<uint8_t> &raw) {
|
||||
// decrypt only if raw data starts with [0xDA, 0x07]
|
||||
if (raw[0] == 0xAA) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> decrypted = raw;
|
||||
for (size_t i = 0; i < 48; i += 8) {
|
||||
decrypted[i] ^= 112; // "p" in ASCII
|
||||
decrypted[i + 1] ^= 97; // "a" in ASCII
|
||||
decrypted[i + 2] ^= 115; // "s" in ASCII
|
||||
decrypted[i + 3] ^= 115; // "s" in ASCII
|
||||
decrypted[i + 4] ^= 119; // "w" in ASCII
|
||||
decrypted[i + 5] ^= 111; // "o" in ASCII
|
||||
decrypted[i + 6] ^= 114; // "r" in ASCII
|
||||
decrypted[i + 7] ^= 100; // "d" in ASCII
|
||||
}
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
static HeaterClass detect_heater_class(const std::vector<uint8_t> &raw) {
|
||||
if(raw[0] == 0xAA) {
|
||||
return raw[1] == 0x55 ? HeaterClass::HEATER_AA_55 : HeaterClass::HEATER_AA_66;
|
||||
} else if (raw[0] == 0xDA) {
|
||||
std::vector<uint8_t> decrypted = ResponseParser::decrypt(raw);
|
||||
if (decrypted[1] == 0x55) {
|
||||
return HeaterClass::HEATER_AA_55_ENCRYPTED;
|
||||
} else if (decrypted[1] == 0x66) {
|
||||
return HeaterClass::HEATER_AA_66_ENCRYPTED;
|
||||
} else {
|
||||
return HeaterClass::HEATER_CLASS_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
return HeaterClass::HEATER_CLASS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse(const std::vector<uint8_t> &raw, HeaterState &state) {
|
||||
HeaterClass heater_class = detect_heater_class(raw);
|
||||
if (heater_class == HeaterClass::HEATER_CLASS_UNKNOWN) return false;
|
||||
|
||||
std::vector<uint8_t> decrypted = decrypt(raw);
|
||||
|
||||
state.heater_class = heater_class;
|
||||
state.rcv_cmd = decrypted[2];
|
||||
state.runningstate = decrypted[3];
|
||||
|
||||
if (heater_class == HeaterClass::HEATER_AA_55 || heater_class == HeaterClass::HEATER_AA_55_ENCRYPTED) {
|
||||
state.errcode = decrypted[4];
|
||||
} else if (heater_class == HeaterClass::HEATER_AA_66) {
|
||||
state.errcode = decrypted[17];
|
||||
} else if (heater_class == HeaterClass::HEATER_AA_66_ENCRYPTED) {
|
||||
state.errcode = decrypted[35];
|
||||
}
|
||||
|
||||
state.runningstep = decrypted[5];
|
||||
if (heater_class == HeaterClass::HEATER_AA_55 || heater_class == HeaterClass::HEATER_AA_66 ) {
|
||||
state.altitude = decrypted[6] + (decrypted[7] << 8);
|
||||
} else if (heater_class == HeaterClass::HEATER_AA_55_ENCRYPTED || heater_class == HeaterClass::HEATER_AA_66_ENCRYPTED) {
|
||||
state.altitude = (decrypted[7] + (decrypted[6] << 8)) / 10;
|
||||
}
|
||||
|
||||
state.runningmode = decrypted[8];
|
||||
|
||||
if (heater_class == HeaterClass::HEATER_AA_55 || heater_class == HeaterClass::HEATER_AA_66 ) {
|
||||
if (state.runningmode == 0x00) {
|
||||
state.setlevel = decrypted[10] + 1;
|
||||
} else if (state.runningmode == 0x01) {
|
||||
state.setlevel = decrypted[9];
|
||||
} else if (state.runningmode == 0x02) {
|
||||
state.settemp = decrypted[9];
|
||||
state.setlevel = decrypted[10] + 1;
|
||||
}
|
||||
} else if (heater_class == HeaterClass::HEATER_AA_55_ENCRYPTED || heater_class == HeaterClass::HEATER_AA_66_ENCRYPTED) {
|
||||
state.setlevel = decrypted[10];
|
||||
state.settemp = decrypted[9];
|
||||
}
|
||||
|
||||
if (heater_class == HeaterClass::HEATER_AA_55 || heater_class == HeaterClass::HEATER_AA_66 ) {
|
||||
state.supplyvoltage = (decrypted[11] + (decrypted[12] << 8)) / 10;
|
||||
} else if (heater_class == HeaterClass::HEATER_AA_55_ENCRYPTED || heater_class == HeaterClass::HEATER_AA_66_ENCRYPTED) {
|
||||
state.supplyvoltage = (decrypted[12] + (decrypted[11] << 8)) / 10;
|
||||
}
|
||||
|
||||
if (heater_class == HeaterClass::HEATER_AA_55 || heater_class == HeaterClass::HEATER_AA_66 ) {
|
||||
state.casetemp = (decrypted[13] + (decrypted[14] << 8));
|
||||
state.cabtemp = (decrypted[15] + (decrypted[16] << 8));
|
||||
} else if (heater_class == HeaterClass::HEATER_AA_55_ENCRYPTED || heater_class == HeaterClass::HEATER_AA_66_ENCRYPTED) {
|
||||
state.casetemp = (decrypted[14] + (decrypted[13] << 8));
|
||||
state.cabtemp = (decrypted[33] + (decrypted[32] << 8)) / 10;
|
||||
}
|
||||
|
||||
// encrypted types only
|
||||
if (heater_class == HeaterClass::HEATER_AA_55_ENCRYPTED || heater_class == HeaterClass::HEATER_AA_66_ENCRYPTED) {
|
||||
state.sttime = decrypted[20] + (decrypted[19] << 8);
|
||||
state.autotime = decrypted[22] + (decrypted[21] << 8);
|
||||
state.runtime = decrypted[24] + (decrypted[23] << 8);
|
||||
state.isauto = decrypted[25];
|
||||
state.language = decrypted[26];
|
||||
state.tempoffset = decrypted[34];
|
||||
state.tankvolume = decrypted[28];
|
||||
state.oilpumptype = decrypted[29];
|
||||
if (raw[29] == 20) {
|
||||
state.rf433onoff = false;
|
||||
} else if (raw[29] == 21) {
|
||||
state.rf433onoff = true;
|
||||
}
|
||||
state.tempunit = decrypted[27];
|
||||
state.altiunit = decrypted[30];
|
||||
state.automaticheating = decrypted[31];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Request {
|
||||
public:
|
||||
uint8_t header_1 = 0xAA;
|
||||
uint8_t header_2 = 0x55;
|
||||
uint8_t password_1 = 0x0C;
|
||||
uint8_t password_2 = 0x22;
|
||||
uint8_t command;
|
||||
uint8_t data_1;
|
||||
uint8_t data_2;
|
||||
uint8_t checksum;
|
||||
|
||||
Request(uint8_t command, uint8_t data1 = 0x00, uint8_t data2 = 0x00)
|
||||
: command(command), data_1(data1), data_2(data2) {
|
||||
calculateChecksum();
|
||||
}
|
||||
|
||||
void calculateChecksum() {
|
||||
checksum = (password_1 + password_2 + command + data_1 + data_2) % 256;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> toBytes() const {
|
||||
return {header_1, header_2, password_1, password_2, command, data_1, data_2, checksum};
|
||||
}
|
||||
};
|
||||
|
||||
// Specific Requests
|
||||
|
||||
class StatusRequest : public Request {
|
||||
public:
|
||||
StatusRequest() : Request(0x01) {}
|
||||
};
|
||||
|
||||
class SetPowerRequest : public Request {
|
||||
public:
|
||||
SetPowerRequest(bool enable)
|
||||
: Request(0x03, enable ? 0x01: 0x00, 0x00) {}
|
||||
};
|
||||
|
||||
class SetTemperatureRequest : public Request {
|
||||
public:
|
||||
SetTemperatureRequest(uint8_t temperature)
|
||||
: Request(0x04, temperature, 0x00) {}
|
||||
};
|
||||
|
||||
class SetLevelRequest : public Request {
|
||||
public:
|
||||
SetLevelRequest(uint8_t level)
|
||||
: Request(0x04, level - 1, 0x00) {}
|
||||
};
|
||||
|
||||
class SetRunningModeRequest : public Request {
|
||||
public:
|
||||
SetRunningModeRequest(uint8_t mode)
|
||||
: Request(0x02, mode, 0x00) {}
|
||||
};
|
||||
|
||||
class SetAutomaticStartStopRequest : public Request {
|
||||
public:
|
||||
SetAutomaticStartStopRequest(bool enable)
|
||||
: Request(0x13, enable ? 0x01 : 0x00, 0x00) {}
|
||||
};
|
||||
|
||||
class SetLanguageRequest : public Request {
|
||||
public:
|
||||
SetLanguageRequest(uint8_t languageCode)
|
||||
: Request(0x14, languageCode, 0x00) {}
|
||||
};
|
||||
|
||||
class SetTemperatureUnitRequest : public Request {
|
||||
public:
|
||||
SetTemperatureUnitRequest(bool isCelsius)
|
||||
: Request(0x15, isCelsius ? 0x01 : 0x00, 0x00) {}
|
||||
};
|
||||
|
||||
class SetAltitudeUnitRequest : public Request {
|
||||
public:
|
||||
SetAltitudeUnitRequest(bool isMeters)
|
||||
: Request(0x16, isMeters ? 0x01 : 0x00, 0x00) {}
|
||||
};
|
||||
|
||||
class SetTankVolumeRequest : public Request {
|
||||
public:
|
||||
SetTankVolumeRequest(uint8_t volume)
|
||||
: Request(0x17, volume, 0x00) {}
|
||||
};
|
||||
|
||||
class SetOilPumpTypeRequest : public Request {
|
||||
public:
|
||||
SetOilPumpTypeRequest(uint8_t type)
|
||||
: Request(0x18, type, 0x00) {}
|
||||
};
|
||||
|
||||
class SetTemperatureOffsetRequest : public Request {
|
||||
public:
|
||||
SetTemperatureOffsetRequest(uint8_t offset)
|
||||
: Request(0x20, offset, 0x00) {}
|
||||
};
|
||||
|
||||
} // namespace diesel_heater_ble
|
||||
} // namespace esphome
|
39
esphome/components/diesel_heater_ble/number.h
Normal file
39
esphome/components/diesel_heater_ble/number.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "heater.h"
|
||||
#include "messages.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
class PowerLevelNumber : public number::Number, public Parented<DieselHeaterBLE> {
|
||||
public:
|
||||
PowerLevelNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float value) override {
|
||||
if (this->parent_->get_state().runningmode == 2) {
|
||||
this->parent_->sent_request(SetRunningModeRequest(1).toBytes());
|
||||
}
|
||||
this->parent_->sent_request(SetLevelRequest(value + 1).toBytes());
|
||||
}
|
||||
};
|
||||
|
||||
class SetTempNumber : public number::Number, public Parented<DieselHeaterBLE> {
|
||||
public:
|
||||
SetTempNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float value) override {
|
||||
if (this->parent_->get_state().runningmode == 1) {
|
||||
this->parent_->sent_request(SetRunningModeRequest(2).toBytes());
|
||||
}
|
||||
this->parent_->sent_request(SetTemperatureRequest(value).toBytes());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace diesel_heater_ble
|
||||
} // namespace esphome
|
42
esphome/components/diesel_heater_ble/number.py
Normal file
42
esphome/components/diesel_heater_ble/number.py
Normal file
@ -0,0 +1,42 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import number
|
||||
from esphome.const import (
|
||||
CONF_TEMPERATURE
|
||||
)
|
||||
|
||||
from . import DieselHeaterBLE, CONF_HEATER_ID, diesel_heater_ble_ns
|
||||
|
||||
CODEOWNERS = ["@warehog"]
|
||||
DEPENDENCIES = ["diesel_heater_ble"]
|
||||
AUTO_LOAD = ["number"]
|
||||
|
||||
CONF_POWER_LEVEL = "power_level"
|
||||
PowerLevelNumber = diesel_heater_ble_ns.class_("PowerLevelNumber", number.Number)
|
||||
|
||||
CONF_SET_TEMP = "set_temp"
|
||||
SetTempNumber = diesel_heater_ble_ns.class_("SetTempNumber", number.Number)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HEATER_ID): cv.use_id(DieselHeaterBLE),
|
||||
cv.Optional(CONF_POWER_LEVEL): number.number_schema(PowerLevelNumber),
|
||||
cv.Optional(CONF_SET_TEMP): number.number_schema(SetTempNumber),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_HEATER_ID])
|
||||
|
||||
# CONF_POWER_LEVEL
|
||||
if conf := config.get(CONF_POWER_LEVEL):
|
||||
b = await number.new_number(conf, min_value=1, max_value=10, step=1)
|
||||
await cg.register_parented(b, parent)
|
||||
cg.add(getattr(parent, f"set_{CONF_POWER_LEVEL}_number")(b))
|
||||
if conf := config.get(CONF_SET_TEMP):
|
||||
b = await number.new_number(conf, min_value=8, max_value=36, step=1)
|
||||
await cg.register_parented(b, parent)
|
||||
cg.add(getattr(parent, f"set_{CONF_SET_TEMP}_number")(b))
|
217
esphome/components/diesel_heater_ble/sensor.py
Normal file
217
esphome/components/diesel_heater_ble/sensor.py
Normal file
@ -0,0 +1,217 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import ble_client
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
UNIT_VOLT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
UNIT_SECOND,
|
||||
UNIT_METER,
|
||||
UNIT_EMPTY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
DEVICE_CLASS_EMPTY
|
||||
)
|
||||
|
||||
from . import DieselHeaterBLE, CONF_HEATER_ID
|
||||
|
||||
CODEOWNERS = ["@warehog"]
|
||||
DEPENDENCIES = ["diesel_heater_ble"]
|
||||
AUTO_LOAD = ["sensor"]
|
||||
|
||||
# Sensor configurations
|
||||
CONF_RUNNING_STATE = "running_state"
|
||||
CONF_ERROR_CODE = "error_code"
|
||||
CONF_RUNNING_STEP = "running_step"
|
||||
CONF_ALTITUDE = "altitude"
|
||||
CONF_RUNNING_MODE = "running_mode"
|
||||
CONF_SET_LEVEL = "set_level"
|
||||
CONF_SET_TEMP = "set_temp"
|
||||
CONF_SUPPLY_VOLTAGE = "supply_voltage"
|
||||
CONF_CASE_TEMP = "case_temp"
|
||||
CONF_CAB_TEMP = "cab_temp"
|
||||
CONF_START_TIME = "start_time"
|
||||
CONF_AUTO_TIME = "auto_time"
|
||||
CONF_RUN_TIME = "run_time"
|
||||
CONF_IS_AUTO = "is_auto"
|
||||
CONF_LANGUAGE = "language"
|
||||
CONF_TEMP_OFFSET = "temp_offset"
|
||||
CONF_TANK_VOLUME = "tank_volume"
|
||||
CONF_OIL_PUMP_TYPE = "oil_pump_type"
|
||||
CONF_RF433_ON_OFF = "rf433_on_off"
|
||||
CONF_TEMP_UNIT = "temp_unit"
|
||||
CONF_ALTITUDE_UNIT = "altitude_unit"
|
||||
CONF_AUTOMATIC_HEATING = "automatic_heating"
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HEATER_ID): cv.use_id(DieselHeaterBLE),
|
||||
cv.Optional(CONF_RUNNING_STATE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ERROR_CODE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_RUNNING_STEP): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ALTITUDE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_RUNNING_MODE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_SET_LEVEL): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_SET_TEMP): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT
|
||||
),
|
||||
cv.Optional(CONF_SUPPLY_VOLTAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CASE_TEMP): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT
|
||||
),
|
||||
cv.Optional(CONF_CAB_TEMP): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_START_TIME): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_AUTO_TIME): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_RUN_TIME): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_IS_AUTO): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_LANGUAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMP_OFFSET): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT
|
||||
),
|
||||
cv.Optional(CONF_TANK_VOLUME): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_OIL_PUMP_TYPE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_RF433_ON_OFF): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMP_UNIT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ALTITUDE_UNIT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_AUTOMATIC_HEATING): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
}
|
||||
)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
async def to_code(config):
|
||||
var = await cg.get_variable(config[CONF_HEATER_ID])
|
||||
|
||||
for sensor_name in [
|
||||
CONF_RUNNING_STATE,
|
||||
CONF_ERROR_CODE,
|
||||
CONF_RUNNING_STEP,
|
||||
CONF_ALTITUDE,
|
||||
CONF_RUNNING_MODE,
|
||||
CONF_SET_LEVEL,
|
||||
CONF_SET_TEMP,
|
||||
CONF_SUPPLY_VOLTAGE,
|
||||
CONF_CASE_TEMP,
|
||||
CONF_CAB_TEMP,
|
||||
CONF_START_TIME,
|
||||
CONF_AUTO_TIME,
|
||||
CONF_RUN_TIME,
|
||||
CONF_IS_AUTO,
|
||||
CONF_LANGUAGE,
|
||||
CONF_TEMP_OFFSET,
|
||||
CONF_TANK_VOLUME,
|
||||
CONF_OIL_PUMP_TYPE,
|
||||
CONF_RF433_ON_OFF,
|
||||
CONF_TEMP_UNIT,
|
||||
CONF_ALTITUDE_UNIT,
|
||||
CONF_AUTOMATIC_HEATING]:
|
||||
if sensor_config := config.get(sensor_name):
|
||||
sens = await sensor.new_sensor(sensor_config)
|
||||
cg.add(getattr(var, f"set_{sensor_name}")(sens))
|
124
esphome/components/diesel_heater_ble/state.h
Normal file
124
esphome/components/diesel_heater_ble/state.h
Normal file
@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
enum class HeaterClass {
|
||||
HEATER_AA_55, // The one that sends responses with 0xAA 0x55 header
|
||||
HEATER_AA_66, // The one that sends responses with 0xAA 0x66 header
|
||||
HEATER_AA_55_ENCRYPTED, // The one that sends responses with 0xAA 0x55 header and encrypted data
|
||||
HEATER_AA_66_ENCRYPTED, // The one that sends responses with 0xAA 0x66 header and encrypted data
|
||||
HEATER_CLASS_UNKNOWN
|
||||
};
|
||||
|
||||
class HeaterState {
|
||||
public:
|
||||
HeaterClass heater_class;
|
||||
uint8_t rcv_cmd;
|
||||
uint8_t runningstate;
|
||||
uint8_t errcode;
|
||||
uint8_t runningstep;
|
||||
uint16_t altitude;
|
||||
uint8_t runningmode;
|
||||
uint8_t setlevel;
|
||||
uint8_t settemp;
|
||||
uint16_t supplyvoltage;
|
||||
uint16_t casetemp;
|
||||
uint16_t cabtemp;
|
||||
|
||||
// encoded types only
|
||||
uint16_t sttime;
|
||||
uint16_t autotime;
|
||||
uint16_t runtime;
|
||||
uint8_t isauto;
|
||||
uint8_t language;
|
||||
uint8_t tempoffset;
|
||||
uint8_t tankvolume;
|
||||
uint8_t oilpumptype;
|
||||
bool rf433onoff;
|
||||
uint8_t tempunit;
|
||||
uint8_t altiunit;
|
||||
uint8_t automaticheating;
|
||||
|
||||
// return table format of data with columnt names
|
||||
std::string to_string() {
|
||||
return "HeaterState: \n"
|
||||
" heater_class: " + std::to_string(static_cast<int>(heater_class)) + "\n"
|
||||
" rcv_cmd: " + std::to_string(rcv_cmd) + "\n"
|
||||
" runningstate: " + std::to_string(runningstate) + "\n"
|
||||
" errcode: " + std::to_string(errcode) + "\n"
|
||||
" runningstep: " + std::to_string(runningstep) + "\n"
|
||||
" altitude: " + std::to_string(altitude) + "\n"
|
||||
" runningmode: " + std::to_string(runningmode) + "\n"
|
||||
" setlevel: " + std::to_string(setlevel) + "\n"
|
||||
" settemp: " + std::to_string(settemp) + "\n"
|
||||
" supplyvoltage: " + std::to_string(supplyvoltage) + "\n"
|
||||
" casetemp: " + std::to_string(casetemp) + "\n"
|
||||
" cabtemp: " + std::to_string(cabtemp) + "\n"
|
||||
" sttime: " + std::to_string(sttime) + "\n"
|
||||
" autotime: " + std::to_string(autotime) + "\n"
|
||||
" runtime: " + std::to_string(runtime) + "\n"
|
||||
" isauto: " + std::to_string(isauto) + "\n";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace diesel_heater_ble
|
||||
} // namespace esphome
|
||||
|
||||
// // Represents the current system state
|
||||
// class HeaterState {
|
||||
// public:
|
||||
// // Decoded fields from the response
|
||||
// uint16_t deviceStatusFlags; // Device status flags (on/off, error states)
|
||||
// uint16_t tempSetpoint; // Temperature setpoint
|
||||
// uint16_t currentTemperature; // Current temperature
|
||||
// uint16_t altitude; // Altitude in meters or feet
|
||||
// uint8_t batteryVoltage; // Battery voltage (scaled)
|
||||
// uint8_t fuelLevel; // Remaining fuel level
|
||||
// uint8_t operatingMode; // Current operating mode
|
||||
// uint8_t errorCode; // Error code
|
||||
// uint32_t runtimeCounter; // Total runtime (in minutes or seconds)
|
||||
// uint8_t additionalStatus[10]; // Placeholder for additional statuses
|
||||
|
||||
// // Decode raw response into meaningful fields
|
||||
|
||||
// static std::vector<uint8_t> decode(const std::vector<uint8_t> &raw) {
|
||||
// if (raw.size() != 48 || raw[0] != 0xAA || raw[1] != 0x55) {
|
||||
// throw std::invalid_argument("Invalid packet format");
|
||||
// }
|
||||
|
||||
// std::vector<uint8_t> decoded;
|
||||
// decoded.push_back(raw[3] | (raw[4] << 8));
|
||||
// decoded.push_back(raw[5] | (raw[6] << 8));
|
||||
// decoded.push_back(raw[7] | (raw[8] << 8));
|
||||
// decoded.push_back(raw[9] | (raw[10] << 8));
|
||||
// decoded.push_back(raw[11]);
|
||||
// decoded.push_back(raw[12]);
|
||||
// decoded.push_back(raw[13]);
|
||||
// decoded.push_back(raw[14]);
|
||||
// decoded.push_back(raw[15] | (raw[16] << 8) | (raw[17] << 16) | (raw[18] << 24));
|
||||
// std::memcpy(&decoded[9], &raw[19], 10);
|
||||
// return decoded;
|
||||
// }
|
||||
// static HeaterState decompose(const std::vector<uint8_t> &raw) {
|
||||
// if (raw.size() != 48 || raw[0] != 0xAA || raw[1] != 0x55) {
|
||||
// throw std::invalid_argument("Invalid packet format");
|
||||
// }
|
||||
|
||||
// HeaterState state;
|
||||
// state.deviceStatusFlags = raw[3] | (raw[4] << 8);
|
||||
// state.tempSetpoint = raw[5] | (raw[6] << 8);
|
||||
// state.currentTemperature = raw[7] | (raw[8] << 8);
|
||||
// state.altitude = raw[9] | (raw[10] << 8);
|
||||
// state.batteryVoltage = raw[11];
|
||||
// state.fuelLevel = raw[12];
|
||||
// state.operatingMode = raw[13];
|
||||
// state.errorCode = raw[14];
|
||||
// state.runtimeCounter = raw[15] | (raw[16] << 8) | (raw[17] << 16) | (raw[18] << 24);
|
||||
// std::memcpy(state.additionalStatus, &raw[19], 10);
|
||||
// return state;
|
||||
// }
|
||||
// };
|
23
esphome/components/diesel_heater_ble/switch.h
Normal file
23
esphome/components/diesel_heater_ble/switch.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "heater.h"
|
||||
#include "messages.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace diesel_heater_ble {
|
||||
|
||||
class PowerSwitch : public switch_::Switch, public Parented<DieselHeaterBLE> {
|
||||
public:
|
||||
PowerSwitch() = default;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override {
|
||||
ESP_LOGD("diesel_heater_ble", "Setting power state to: %d", state);
|
||||
this->parent_->sent_request(SetPowerRequest(state).toBytes());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace diesel_heater
|
||||
} // namespace esphome
|
36
esphome/components/diesel_heater_ble/switch.py
Normal file
36
esphome/components/diesel_heater_ble/switch.py
Normal file
@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch
|
||||
|
||||
from . import DieselHeaterBLE, CONF_HEATER_ID, diesel_heater_ble_ns
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HEATER_ID): cv.use_id(DieselHeaterBLE),
|
||||
cv.Optional("power"): switch.switch_schema(
|
||||
diesel_heater_ble_ns.class_("PowerSwitch", switch.Switch),
|
||||
icon="mdi:power",
|
||||
),
|
||||
# cv.Optional("mode"): switch.switch_schema(
|
||||
# heater_ns.class_("ModeSwitch", switch.Switch),
|
||||
# icon="mdi:power",
|
||||
# ),
|
||||
# cv.Optional("alpine"): switch.switch_schema(
|
||||
# heater_ns.class_("AlpineSwitch", switch.Switch),
|
||||
# icon="mdi:power",
|
||||
# ),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_HEATER_ID])
|
||||
|
||||
for switch_type in ["power"]:
|
||||
if conf := config.get(switch_type):
|
||||
sw_var = await switch.new_switch(conf)
|
||||
await cg.register_parented(sw_var, parent)
|
||||
cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var))
|
Loading…
x
Reference in New Issue
Block a user