diff --git a/CODEOWNERS b/CODEOWNERS index 404ad35efc..b489c1ea3b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -301,6 +301,7 @@ esphome/components/nfc/* @jesserockz @kbx81 esphome/components/noblex/* @AGalfra esphome/components/npi19/* @bakerkj esphome/components/number/* @esphome/core +esphome/components/obd/* @abstractionnl esphome/components/one_wire/* @ssieb esphome/components/online_image/* @guillempages esphome/components/opentherm/* @olegtarasov diff --git a/esphome/components/obd/__init__.py b/esphome/components/obd/__init__.py index 8e6f28d83c..aa157861ac 100644 --- a/esphome/components/obd/__init__.py +++ b/esphome/components/obd/__init__.py @@ -1,13 +1,12 @@ -import esphome.config_validation as cv -import esphome.codegen as cg -import esphome.components.sensor as s -import esphome.components.binary_sensor as bs from esphome import automation -from esphome.const import ( - CONF_ID, - CONF_INTERVAL, - CONF_TRIGGER_ID -) +import esphome.codegen as cg +import esphome.components.binary_sensor as bs +import esphome.components.sensor as s +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERVAL, CONF_TIMEOUT, CONF_TRIGGER_ID + +CODEOWNERS = ["@abstractionnl"] +DEPENDENCIES = ["canbus"] obd_ns = cg.esphome_ns.namespace("obd") OBDComponent = obd_ns.class_("OBDComponent", cg.PollingComponent) @@ -15,48 +14,54 @@ OBDSensor = obd_ns.class_("OBDSensor", s.Sensor, cg.Component) OBDBinarySensor = obd_ns.class_("OBDBinarySensor", bs.BinarySensor, cg.Component) PIDRequest = obd_ns.class_("PIDRequest", cg.Component) OBDPidTrigger = obd_ns.class_( - "OBDPidTrigger", + "OBDPidTrigger", automation.Trigger.template(cg.std_vector.template(cg.uint8)), - cg.Component + cg.Component, ) -CONF_CANBUS_ID = 'canbus_id' -CONF_ENABLED_BY_DEFAULT = 'enabled_by_default' -CONF_OBD_ID = 'obd_id' +CONF_CANBUS_ID = "canbus_id" +CONF_ENABLED_BY_DEFAULT = "enabled_by_default" +CONF_OBD_ID = "obd_id" CONF_PID_ID = "pid_id" CONF_CAN_ID = "can_id" CONF_RESPONSE_CAN_ID = "response_can_id" CONF_USE_EXTENDED_ID = "use_extended_id" CONF_PIDS = "pids" CONF_PID = "pid" -CONF_TIMEOUT = "timeout" CONF_REPLY_LENGTH = "reply_length" CONF_ON_FRAME = "on_frame" CONF_MASK = "mask" CONF_SIGNED = "signed" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(OBDComponent), - cv.Required(CONF_CANBUS_ID): cv.use_id("CanbusComponent"), - cv.Optional(CONF_ENABLED_BY_DEFAULT, default=False): cv.boolean, - cv.Optional(CONF_PIDS): cv.ensure_list( - { - cv.GenerateID(CONF_ID): cv.declare_id(PIDRequest), - cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), - cv.Required(CONF_PID): cv.int_range(min=0, max=0x1FFFFFFF), - cv.Optional(CONF_RESPONSE_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), - cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, - cv.Optional(CONF_INTERVAL, default="5s"): cv.positive_time_period_milliseconds, - cv.Optional(CONF_TIMEOUT, default="500ms"): cv.positive_time_period_milliseconds, - cv.Optional(CONF_REPLY_LENGTH, default=8): cv.positive_int, - cv.Optional(CONF_ON_FRAME): automation.validate_automation( +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(OBDComponent), + cv.Required(CONF_CANBUS_ID): cv.use_id("CanbusComponent"), + cv.Optional(CONF_ENABLED_BY_DEFAULT, default=False): cv.boolean, + cv.Optional(CONF_PIDS): cv.ensure_list( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OBDPidTrigger), + cv.GenerateID(CONF_ID): cv.declare_id(PIDRequest), + cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), + cv.Required(CONF_PID): cv.int_range(min=0, max=0x1FFFFFFF), + cv.Optional(CONF_RESPONSE_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), + cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, + cv.Optional( + CONF_INTERVAL, default="5s" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_TIMEOUT, default="500ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_REPLY_LENGTH, default=8): cv.positive_int, + cv.Optional(CONF_ON_FRAME): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OBDPidTrigger), + } + ), } - ), - } - ) -}).extend(cv.COMPONENT_SCHEMA) + ), + } +).extend(cv.COMPONENT_SCHEMA) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -72,8 +77,8 @@ async def to_code(config): pid = pid_conf[CONF_PID] use_extended_id = pid_conf[CONF_USE_EXTENDED_ID] response_can_id = pid_conf.get(CONF_RESPONSE_CAN_ID) - - if (response_can_id is None): + + if response_can_id is None: response_can_id = can_id | 8 pid_request = cg.new_Pvariable( @@ -88,6 +93,8 @@ async def to_code(config): for trigger_conf in pid_conf.get(CONF_ON_FRAME, []): trigger = cg.new_Pvariable(trigger_conf[CONF_TRIGGER_ID], pid_request) await cg.register_component(trigger, trigger_conf) - await automation.build_automation(trigger, [(cg.std_vector.template(cg.uint8), "data")], trigger_conf) + await automation.build_automation( + trigger, [(cg.std_vector.template(cg.uint8), "data")], trigger_conf + ) return var diff --git a/esphome/components/obd/binary_sensor.py b/esphome/components/obd/binary_sensor.py index d4fdf53d22..7a008bd441 100644 --- a/esphome/components/obd/binary_sensor.py +++ b/esphome/components/obd/binary_sensor.py @@ -1,23 +1,15 @@ -import esphome.config_validation as cv import esphome.codegen as cg from esphome.components import binary_sensor +import esphome.config_validation as cv +from esphome.const import CONF_INDEX, CONF_LAMBDA from esphome.cpp_generator import LambdaExpression -from esphome.const import ( - CONF_LAMBDA, - CONF_INDEX, -) -from . import ( - CONF_PID_ID, - CONF_MASK, - OBDBinarySensor, - PIDRequest -) + +from . import CONF_MASK, CONF_PID_ID, OBDBinarySensor, PIDRequest CONFIG_SCHEMA = cv.All( binary_sensor.binary_sensor_schema( OBDBinarySensor, - ) - .extend( + ).extend( { cv.Required(CONF_PID_ID): cv.use_id(PIDRequest), cv.Optional(CONF_LAMBDA): cv.returning_lambda, @@ -26,24 +18,27 @@ CONFIG_SCHEMA = cv.All( } ), cv.has_exactly_one_key(CONF_LAMBDA, CONF_INDEX), - cv.has_none_or_all_keys(CONF_INDEX, CONF_MASK) + cv.has_none_or_all_keys(CONF_INDEX, CONF_MASK), ) + async def to_code(config): pid_request = await cg.get_variable(config[CONF_PID_ID]) var = await binary_sensor.new_binary_sensor(config, pid_request) await cg.register_component(var, config) if lambda_ := config.get(CONF_LAMBDA): - template = await cg.process_lambda(lambda_, [(cg.std_vector.template(cg.uint8), "data")], return_type=cg.bool_) + template = await cg.process_lambda( + lambda_, [(cg.std_vector.template(cg.uint8), "data")], return_type=cg.bool_ + ) cg.add(var.set_template(template)) - + else: index = config[CONF_INDEX] mask = config[CONF_MASK] template = LambdaExpression( - f"return (data[{index}] & {mask}) == {mask};", - [(cg.std_vector.template(cg.uint8), "data")], - return_type=cg.bool_ - ) - cg.add(var.set_template(template)) \ No newline at end of file + f"return (data[{index}] & {mask}) == {mask};", + [(cg.std_vector.template(cg.uint8), "data")], + return_type=cg.bool_, + ) + cg.add(var.set_template(template)) diff --git a/esphome/components/obd/obd_component.cpp b/esphome/components/obd/obd_component.cpp index e36896a708..0e66e5104b 100644 --- a/esphome/components/obd/obd_component.cpp +++ b/esphome/components/obd/obd_component.cpp @@ -10,170 +10,163 @@ static const char *const TAG = "obd"; // Skip setup from PollingComponent to stop polling from starting automatically void OBDComponent::call_setup() { - if (this->enabled_by_default_) { - this->start_poller(); - } + if (this->enabled_by_default_) { + this->start_poller(); + } } -void OBDComponent::dump_config() { - ESP_LOGCONFIG(TAG, "OBD Component"); -} +void OBDComponent::dump_config() { ESP_LOGCONFIG(TAG, "OBD Component"); } void OBDComponent::update() { - if (this->current_request_ == nullptr) { - - // No current request, find the next request to do - for (auto *request : this->pidrequests_) { - if (request->start()) { - this->current_request_ = request; - return; - } - } - - // No pid to be polled, exit loop + if (this->current_request_ == nullptr) { + // No current request, find the next request to do + for (auto *request : this->pidrequests_) { + if (request->start()) { + this->current_request_ = request; return; - } else { - if (this->current_request_->update()) { - this->current_request_ = nullptr; - } - } + } + } + + // No pid to be polled, exit loop + return; + } else { + if (this->current_request_->update()) { + this->current_request_ = nullptr; + } + } } void OBDComponent::send(std::uint32_t can_id, bool use_extended_id, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - std::vector data(8, 0xAA); - data[0] = a; - data[1] = b; - data[2] = c; - data[3] = d; + std::vector data(8, 0xAA); + data[0] = a; + data[1] = b; + data[2] = c; + data[3] = d; - this->canbus_->send_data(can_id, use_extended_id, data); + this->canbus_->send_data(can_id, use_extended_id, data); } void OBDComponent::send(std::uint32_t can_id, bool use_extended_id, uint8_t a, uint8_t b, uint8_t c) { - std::vector data(8, 0xAA); - data[0] = a; - data[1] = b; - data[2] = c; - - this->canbus_->send_data(can_id, use_extended_id, data); + std::vector data(8, 0xAA); + data[0] = a; + data[1] = b; + data[2] = c; + + this->canbus_->send_data(can_id, use_extended_id, data); } void OBDComponent::add_pidrequest(PIDRequest *request) { - ESP_LOGVV(TAG, "add request for canid=0x%03, pid=0x%06" PRIx32, request->can_id_, PRIx32, request->pid_); - this->pidrequests_.push_back(request); + ESP_LOGVV(TAG, "add request for canid=0x%03, pid=0x%06" PRIx32, request->can_id_, PRIx32, request->pid_); + this->pidrequests_.push_back(request); }; void PIDRequest::setup() { - this->parent_->add_pidrequest(this); + this->parent_->add_pidrequest(this); - auto trigger = new OBDCanbusTrigger(this); - trigger->setup(); + auto trigger = new OBDCanbusTrigger(this); + trigger->setup(); } bool PIDRequest::start() { - if (this->state_ != WAITING) - return false; + if (this->state_ != WAITING) + return false; - if ((this->last_polled_ + this->interval_) >= millis()) - return false; + if ((this->last_polled_ + this->interval_) >= millis()) + return false; - this->response_buffer_.clear(); - this->response_buffer_.reserve(this->reply_length_); - - auto can_id = this->can_id_; - auto pid = this->pid_; + this->response_buffer_.clear(); + this->response_buffer_.reserve(this->reply_length_); - ESP_LOGD(TAG, "polling can_id: 0x%03x for pid 0x%04x", can_id, pid); + auto can_id = this->can_id_; + auto pid = this->pid_; - if (pid > 0xFFFF) { - // 24 bit pid - this->parent_->send(can_id, this->use_extended_id_, 0x03, (pid >> 16) & 0xFF, (pid >> 8) & 0xFF, pid & 0xFF); - } else { - this->parent_->send(can_id, this->use_extended_id_, 0x02, (pid >> 8) & 0xFF, pid & 0xFF); - } - - this->last_polled_ = millis(); - this->state_ = POLLING; + ESP_LOGD(TAG, "polling can_id: 0x%03x for pid 0x%04x", can_id, pid); - return true; + if (pid > 0xFFFF) { + // 24 bit pid + this->parent_->send(can_id, this->use_extended_id_, 0x03, (pid >> 16) & 0xFF, (pid >> 8) & 0xFF, pid & 0xFF); + } else { + this->parent_->send(can_id, this->use_extended_id_, 0x02, (pid >> 8) & 0xFF, pid & 0xFF); + } + + this->last_polled_ = millis(); + this->state_ = POLLING; + + return true; } bool PIDRequest::update() { - if (this->state_ != POLLING) - return true; // Invalid state, update should not have been called here + if (this->state_ != POLLING) + return true; // Invalid state, update should not have been called here - if ((this->last_polled_ + this->timeout_) > millis()) { - return false; - } + if ((this->last_polled_ + this->timeout_) > millis()) { + return false; + } - if (this->response_buffer_.size() < this->reply_length_) { - ESP_LOGD(TAG, "timeout for polling can_id: 0x%03x for pid 0x%04x", this->can_id_, this->pid_); - this->state_ = WAITING; - return true; - } - - for (auto *trigger : this->triggers_) { - trigger->trigger(this->response_buffer_); - } - - for (auto *sensor : this->sensors_) { - sensor->update(this->response_buffer_); - } - - this->state_ = WAITING; + if (this->response_buffer_.size() < this->reply_length_) { + ESP_LOGD(TAG, "timeout for polling can_id: 0x%03x for pid 0x%04x", this->can_id_, this->pid_); + this->state_ = WAITING; return true; + } + + for (auto *trigger : this->triggers_) { + trigger->trigger(this->response_buffer_); + } + + for (auto *sensor : this->sensors_) { + sensor->update(this->response_buffer_); + } + + this->state_ = WAITING; + return true; } void PIDRequest::handle_incoming(std::vector &data) { - if (this->state_ != POLLING) - return; // Not our cup of tea here, some other pid might be polling on the same can_id - - ESP_LOGD(TAG, "recieved content for pid 0x%04x: %s", this->can_id_, this->pid_, format_hex_pretty(data).c_str()); + if (this->state_ != POLLING) + return; // Not our cup of tea here, some other pid might be polling on the same can_id - // Handle the data - if ((data[0] & 0xF0) == 0x10) { - // This is a flow control frame, we should ask for more - this->parent_->send(this->can_id_, this->use_extended_id_, 0x30, 0x0, 0x10); - } + ESP_LOGD(TAG, "recieved content for pid 0x%04x: %s", this->can_id_, this->pid_, format_hex_pretty(data).c_str()); - for (int i = 0; i < data.size(); i++) { - this->response_buffer_.push_back(data[i]); - } + // Handle the data + if ((data[0] & 0xF0) == 0x10) { + // This is a flow control frame, we should ask for more + this->parent_->send(this->can_id_, this->use_extended_id_, 0x30, 0x0, 0x10); + } + + for (int i = 0; i < data.size(); i++) { + this->response_buffer_.push_back(data[i]); + } } void PIDRequest::dump_config() { - ESP_LOGCONFIG(TAG, "PID Request 0x%03", this->pid_); + ESP_LOGCONFIG(TAG, "PID Request 0x%03", this->pid_); - if (this->use_extended_id_) { - ESP_LOGCONFIG(TAG, " Can extended id: 0x%08" PRIx32, this->can_id_); - ESP_LOGCONFIG(TAG, " Can response id: 0x%08" PRIx32, this->can_response_id_); - } else { - ESP_LOGCONFIG(TAG, " Can id: 0x%03" PRIx32, this->can_id_); - ESP_LOGCONFIG(TAG, " Can response id: 0x%03" PRIx32, this->can_response_id_); - } - - ESP_LOGCONFIG(TAG, " Pid: 0x%03" PRIx32, this->pid_); - ESP_LOGCONFIG(TAG, " Interval: %ims", this->interval_); - ESP_LOGCONFIG(TAG, " Timeout: %ims", this->timeout_); + if (this->use_extended_id_) { + ESP_LOGCONFIG(TAG, " Can extended id: 0x%08" PRIx32, this->can_id_); + ESP_LOGCONFIG(TAG, " Can response id: 0x%08" PRIx32, this->can_response_id_); + } else { + ESP_LOGCONFIG(TAG, " Can id: 0x%03" PRIx32, this->can_id_); + ESP_LOGCONFIG(TAG, " Can response id: 0x%03" PRIx32, this->can_response_id_); + } + + ESP_LOGCONFIG(TAG, " Pid: 0x%03" PRIx32, this->pid_); + ESP_LOGCONFIG(TAG, " Interval: %ims", this->interval_); + ESP_LOGCONFIG(TAG, " Timeout: %ims", this->timeout_); } void OBDSensor::update(const std::vector &data) { - auto value = this->data_to_value_func_(data); - this->publish_state(value); + auto value = this->data_to_value_func_(data); + this->publish_state(value); } -void OBDSensor::dump_config() { - LOG_SENSOR("", "OBD Sensor", this); -} +void OBDSensor::dump_config() { LOG_SENSOR("", "OBD Sensor", this); } void OBDBinarySensor::update(const std::vector &data) { - auto value = this->data_to_value_func_(data); - this->publish_state(value); + auto value = this->data_to_value_func_(data); + this->publish_state(value); } -void OBDBinarySensor::dump_config() { - LOG_BINARY_SENSOR("", "OBD Binary Sensor", this); -} +void OBDBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "OBD Binary Sensor", this); } -} -} \ No newline at end of file +} // namespace obd +} // namespace esphome diff --git a/esphome/components/obd/sensor.py b/esphome/components/obd/sensor.py index 26e53889f1..21442978cb 100644 --- a/esphome/components/obd/sensor.py +++ b/esphome/components/obd/sensor.py @@ -1,34 +1,26 @@ -import esphome.config_validation as cv import esphome.codegen as cg from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import CONF_INDEX, CONF_LAMBDA from esphome.cpp_generator import LambdaExpression -from esphome.const import ( - CONF_ID, - CONF_LAMBDA, - CONF_INDEX, -) -from . import ( - CONF_PID_ID, - CONF_SIGNED, - OBDSensor, - PIDRequest -) + +from . import CONF_PID_ID, CONF_SIGNED, OBDSensor, PIDRequest CONFIG_SCHEMA = cv.All( sensor.sensor_schema( OBDSensor, - ) - .extend( + ).extend( { cv.Required(CONF_PID_ID): cv.use_id(PIDRequest), cv.Optional(CONF_SIGNED, default=False): cv.boolean, cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.returning_lambda, - cv.Exclusive(CONF_INDEX, CONF_LAMBDA): cv.ensure_list(cv.positive_int) + cv.Exclusive(CONF_INDEX, CONF_LAMBDA): cv.ensure_list(cv.positive_int), } ), - cv.has_exactly_one_key(CONF_LAMBDA, CONF_INDEX) + cv.has_exactly_one_key(CONF_LAMBDA, CONF_INDEX), ) + async def to_code(config): pid_request = await cg.get_variable(config[CONF_PID_ID]) var = await sensor.new_sensor(config, pid_request) @@ -37,7 +29,9 @@ async def to_code(config): template = False if lambda_ := config.get(CONF_LAMBDA): - template = await cg.process_lambda(lambda_, [(cg.std_vector.template(cg.uint8), "data")], return_type=cg.float_) + template = await cg.process_lambda( + lambda_, [(cg.std_vector.template(cg.uint8), "data")], return_type=cg.float_ + ) cg.add(var.set_template(template)) else: indexes = config[CONF_INDEX] @@ -46,30 +40,29 @@ async def to_code(config): if len(indexes) == 4: template = LambdaExpression( f"return ({signed}data[{indexes[0]}] << 24) | (data[{indexes[1]}] << 16) | (data[{indexes[2]}] << 8) | data[{indexes[3]}];", - [(cg.std_vector.template(cg.uint8), "data")], - return_type=cg.float_ + [(cg.std_vector.template(cg.uint8), "data")], + return_type=cg.float_, ) if len(indexes) == 3: template = LambdaExpression( f"return ({signed}data[{indexes[0]}] << 16) | (data[{indexes[1]}] << 8) | data[{indexes[2]}];", - [(cg.std_vector.template(cg.uint8), "data")], - return_type=cg.float_ + [(cg.std_vector.template(cg.uint8), "data")], + return_type=cg.float_, ) if len(indexes) == 2: template = LambdaExpression( f"return ({signed}data[{indexes[0]}] << 8) | data[{indexes[1]}];", - [(cg.std_vector.template(cg.uint8), "data")], - return_type=cg.float_ - ) + [(cg.std_vector.template(cg.uint8), "data")], + return_type=cg.float_, + ) if len(indexes) == 1: template = LambdaExpression( f"return {signed}data[{indexes[0]}];", - [(cg.std_vector.template(cg.uint8), "data")], - return_type=cg.float_ - ) - - cg.add(var.set_template(template)) - \ No newline at end of file + [(cg.std_vector.template(cg.uint8), "data")], + return_type=cg.float_, + ) + + cg.add(var.set_template(template))