mirror of
https://github.com/esphome/esphome.git
synced 2024-12-05 13:54:09 +01:00
Merge branch 'weekly_schedules' into optolink
This commit is contained in:
commit
7865a10d07
@ -1,35 +1,40 @@
|
||||
from esphome import core
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor as ts
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DIV_RATIO,
|
||||
CONF_ID,
|
||||
CONF_LOGGER,
|
||||
CONF_PROTOCOL,
|
||||
CONF_RX_PIN,
|
||||
CONF_STATE,
|
||||
CONF_TX_PIN,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
DEPENDENCIES = []
|
||||
AUTO_LOAD = ["sensor", "binary_sensor", "text_sensor", "number", "select", "switch"]
|
||||
AUTO_LOAD = []
|
||||
MULTI_CONF = False
|
||||
CONF_DEVICE_INFO = "device_info"
|
||||
|
||||
optolink_ns = cg.esphome_ns.namespace("optolink")
|
||||
CONF_OPTOLINK_ID = "optolink_id"
|
||||
|
||||
OptolinkComponent = optolink_ns.class_("Optolink", cg.Component)
|
||||
StateSensor = optolink_ns.class_(
|
||||
"OptolinkStateSensor", ts.TextSensor, cg.PollingComponent
|
||||
CONF_OPTOLINK_ID = "optolink_id"
|
||||
SENSOR_BASE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)),
|
||||
),
|
||||
cv.Optional(CONF_DIV_RATIO, default=1): cv.one_of(
|
||||
1, 10, 100, 1000, 3600, int=True
|
||||
),
|
||||
}
|
||||
)
|
||||
STATE_SENSOR_ID = "state_sensor_id"
|
||||
DeviceInfoSensor = optolink_ns.class_(
|
||||
"OptolinkDeviceInfoSensor", ts.TextSensor, cg.PollingComponent
|
||||
)
|
||||
DEVICE_INFO_SENSOR_ID = "device_info_sensor_id"
|
||||
|
||||
|
||||
def required_on_esp32(attribute):
|
||||
@ -47,8 +52,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(OptolinkComponent),
|
||||
cv.GenerateID(STATE_SENSOR_ID): cv.declare_id(StateSensor),
|
||||
cv.GenerateID(DEVICE_INFO_SENSOR_ID): cv.declare_id(DeviceInfoSensor),
|
||||
cv.Required(CONF_PROTOCOL): cv.one_of("P300", "KW"),
|
||||
cv.Optional(CONF_RX_PIN): cv.All(
|
||||
cv.only_on_esp32,
|
||||
@ -59,8 +62,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
pins.internal_gpio_output_pin_schema,
|
||||
),
|
||||
cv.Optional(CONF_LOGGER, default=False): cv.boolean,
|
||||
cv.Optional(CONF_STATE): cv.string,
|
||||
cv.Optional(CONF_DEVICE_INFO): cv.string,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_with_arduino,
|
||||
@ -71,7 +72,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_library("VitoWiFi", "1.0.2")
|
||||
cg.add_library("VitoWiFi", "1.1.2")
|
||||
|
||||
cg.add_define(
|
||||
"VITOWIFI_PROTOCOL", cg.RawExpression(f"Optolink{config[CONF_PROTOCOL]}")
|
||||
@ -80,32 +81,6 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_logger_enabled(config[CONF_LOGGER]))
|
||||
|
||||
if CONF_STATE in config:
|
||||
debugSensor = cg.new_Pvariable(config[STATE_SENSOR_ID], config[CONF_STATE], var)
|
||||
await ts.register_text_sensor(
|
||||
debugSensor,
|
||||
{
|
||||
"id": config[STATE_SENSOR_ID],
|
||||
"name": config[CONF_STATE],
|
||||
"disabled_by_default": "false",
|
||||
},
|
||||
)
|
||||
await cg.register_component(debugSensor, config)
|
||||
|
||||
if CONF_DEVICE_INFO in config:
|
||||
debugSensor = cg.new_Pvariable(
|
||||
config[DEVICE_INFO_SENSOR_ID], config[CONF_DEVICE_INFO], var
|
||||
)
|
||||
await ts.register_text_sensor(
|
||||
debugSensor,
|
||||
{
|
||||
"id": config[DEVICE_INFO_SENSOR_ID],
|
||||
"name": config[CONF_DEVICE_INFO],
|
||||
"disabled_by_default": "false",
|
||||
},
|
||||
)
|
||||
await cg.register_component(debugSensor, config)
|
||||
|
||||
if CORE.is_esp32:
|
||||
cg.add(var.set_rx_pin(config[CONF_RX_PIN]["number"]))
|
||||
cg.add(var.set_tx_pin(config[CONF_TX_PIN]["number"]))
|
||||
|
@ -1,30 +0,0 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ID, CONF_ADDRESS, CONF_UPDATE_INTERVAL
|
||||
from . import OptolinkComponent, optolink_ns, CONF_OPTOLINK_ID
|
||||
|
||||
OptolinkBinarySensor = optolink_ns.class_(
|
||||
"OptolinkBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
|
||||
)
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(OptolinkBinarySensor).extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
34
esphome/components/optolink/binary_sensor/__init__.py
Normal file
34
esphome/components/optolink/binary_sensor/__init__.py
Normal file
@ -0,0 +1,34 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID
|
||||
from .. import SENSOR_BASE_SCHEMA, optolink_ns, CONF_OPTOLINK_ID
|
||||
|
||||
DEPENDENCIES = ["optolink"]
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
|
||||
|
||||
OptolinkBinarySensor = optolink_ns.class_(
|
||||
"OptolinkBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(OptolinkBinarySensor),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(SENSOR_BASE_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkBinarySensor : public DatapointComponent,
|
||||
public esphome::binary_sensor::BinarySensor,
|
||||
public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkBinarySensor(Optolink *optolink) : DatapointComponent(optolink) {
|
||||
set_bytes(1);
|
||||
set_div_ratio(1);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint(); }
|
||||
void update() override { datapoint_read_request(); }
|
||||
|
||||
const StringRef &get_component_name() override { return get_name(); }
|
||||
void datapoint_value_changed(uint8_t state) override { publish_state(state); };
|
||||
};
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
316
esphome/components/optolink/datapoint_component.cpp
Normal file
316
esphome/components/optolink/datapoint_component.cpp
Normal file
@ -0,0 +1,316 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "datapoint_component.h"
|
||||
#include "optolink.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink.datapoint_component";
|
||||
static std::vector<HassSubscription> hass_subscriptions_;
|
||||
|
||||
void DatapointComponent::setup_datapoint() {
|
||||
switch (div_ratio_) {
|
||||
case 0:
|
||||
datapoint_ = new Datapoint<convRaw>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setLength(bytes_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
uint8_t buffer[bytes_];
|
||||
dp_value.getRaw(buffer);
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_INFO
|
||||
char print_buffer[bytes_ * 2 + 1];
|
||||
dp_value.getString(print_buffer, sizeof(print_buffer));
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %s", dp.getName(), print_buffer);
|
||||
#endif
|
||||
datapoint_value_changed((uint8_t *) buffer, bytes_);
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
switch (bytes_) {
|
||||
case 1:
|
||||
datapoint_ = new Datapoint<conv1_1_US>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %d", dp.getName(), dp_value.getU8());
|
||||
datapoint_value_changed(dp_value.getU8());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
datapoint_ = new Datapoint<conv2_1_US>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %d", dp.getName(), dp_value.getU16());
|
||||
datapoint_value_changed(dp_value.getU16());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
case 4:
|
||||
datapoint_ = new Datapoint<conv4_1_UL>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %d", dp.getName(), dp_value.getU32());
|
||||
datapoint_value_changed((uint32_t) dp_value.getU32());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
unfitting_value_type();
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (bytes_) {
|
||||
case 1:
|
||||
datapoint_ = new Datapoint<conv1_10_F>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat());
|
||||
datapoint_value_changed(dp_value.getFloat());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
datapoint_ = new Datapoint<conv2_10_F>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat());
|
||||
datapoint_value_changed(dp_value.getFloat());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
unfitting_value_type();
|
||||
}
|
||||
break;
|
||||
case 100:
|
||||
switch (bytes_) {
|
||||
case 2:
|
||||
datapoint_ = new Datapoint<conv2_100_F>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat());
|
||||
datapoint_value_changed(dp_value.getFloat());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
unfitting_value_type();
|
||||
}
|
||||
break;
|
||||
case 1000:
|
||||
switch (bytes_) {
|
||||
case 4:
|
||||
datapoint_ = new Datapoint<conv4_1000_F>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat());
|
||||
datapoint_value_changed(dp_value.getFloat());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3600:
|
||||
switch (bytes_) {
|
||||
case 4:
|
||||
datapoint_ = new Datapoint<conv4_3600_F>(get_component_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGI(TAG, "recieved data for datapoint %s: %f", dp.getName(), dp_value.getFloat());
|
||||
datapoint_value_changed(dp_value.getFloat());
|
||||
read_retries_ = 0;
|
||||
});
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unfitting_value_type();
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_read_request() {
|
||||
if (is_dp_value_writing_outstanding) {
|
||||
ESP_LOGI(TAG, "read request for %s deferred due to outstanding write request", get_component_name().c_str());
|
||||
datapoint_write_request(dp_value_outstanding);
|
||||
} else {
|
||||
if (read_retries_ == 0 || read_retries_ >= MAX_RETRIES_UNTIL_RESET) {
|
||||
if (optolink_->read_value(datapoint_)) {
|
||||
read_retries_ = 1;
|
||||
}
|
||||
} else {
|
||||
read_retries_++;
|
||||
ESP_LOGW(TAG, "%d. read request for %s rejected due to outstanding running request - increase update_interval!",
|
||||
read_retries_, get_component_name().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_value_changed(float value) {
|
||||
ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_value_changed(uint8_t value) {
|
||||
ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_value_changed(uint16_t value) {
|
||||
ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_value_changed(uint32_t value) {
|
||||
ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_value_changed(std::string value) {
|
||||
ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_value_changed(uint8_t *value, size_t length) {
|
||||
ESP_LOGW(TAG, "unused value update by sensor %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::datapoint_write_request(DPValue dp_value) {
|
||||
if (!writeable_) {
|
||||
optolink_->set_state("trying to control not writable datapoint %s", get_component_name().c_str());
|
||||
ESP_LOGE(TAG, "trying to control not writable datapoint %s", get_component_name().c_str());
|
||||
} else if (datapoint_ != nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_INFO
|
||||
char buffer[100];
|
||||
dp_value.getString(buffer, sizeof(buffer));
|
||||
ESP_LOGI(TAG, "trying to update datapoint %s value: %s", get_component_name().c_str(), buffer);
|
||||
#endif
|
||||
|
||||
dp_value_outstanding = dp_value;
|
||||
if (optolink_->write_value(datapoint_, dp_value_outstanding)) {
|
||||
is_dp_value_writing_outstanding = false;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "write request for %s rejected due to outstanding running request - increase update_interval!",
|
||||
get_component_name().c_str());
|
||||
is_dp_value_writing_outstanding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::write_datapoint_value(float value) {
|
||||
if (div_ratio_ > 1) {
|
||||
datapoint_write_request(DPValue(value));
|
||||
} else if (div_ratio_ == 1) {
|
||||
switch (bytes_) {
|
||||
case 1:
|
||||
datapoint_write_request(DPValue((uint8_t) value));
|
||||
break;
|
||||
case 2:
|
||||
datapoint_write_request(DPValue((uint16_t) value));
|
||||
break;
|
||||
case 4:
|
||||
datapoint_write_request(DPValue((uint32_t) value));
|
||||
break;
|
||||
default:
|
||||
unfitting_value_type();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
unfitting_value_type();
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::write_datapoint_value(uint8_t value) {
|
||||
if (bytes_ == 1 && div_ratio_ == 1) {
|
||||
datapoint_write_request(DPValue(value));
|
||||
} else {
|
||||
unfitting_value_type();
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::write_datapoint_value(uint16_t value) {
|
||||
if (bytes_ == 2 && div_ratio_ == 1) {
|
||||
datapoint_write_request(DPValue(value));
|
||||
} else {
|
||||
unfitting_value_type();
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::write_datapoint_value(uint32_t value) {
|
||||
if (bytes_ == 4 && div_ratio_ == 1) {
|
||||
datapoint_write_request(DPValue(value));
|
||||
} else {
|
||||
unfitting_value_type();
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::write_datapoint_value(uint8_t *value, size_t length) {
|
||||
if (bytes_ == length && div_ratio_ == 0) {
|
||||
datapoint_write_request(DPValue(value, length));
|
||||
} else {
|
||||
unfitting_value_type();
|
||||
}
|
||||
}
|
||||
|
||||
void DatapointComponent::unfitting_value_type() {
|
||||
optolink_->set_state("Unfitting byte/div_ratio combination for sensor/component %s", get_component_name().c_str());
|
||||
ESP_LOGE(TAG, "Unfitting byte/div_ratio combination for sensor/component %s", get_component_name().c_str());
|
||||
}
|
||||
|
||||
void DatapointComponent::set_optolink_state(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
char buffer[128];
|
||||
std::vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
va_end(args);
|
||||
|
||||
optolink_->set_state(buffer);
|
||||
}
|
||||
|
||||
std::string DatapointComponent::get_optolink_state() { return optolink_->get_state(); }
|
||||
|
||||
void DatapointComponent::subscribe_hass(std::string entity_id, std::function<void(std::string)> f) {
|
||||
for (auto &subscription : hass_subscriptions_) {
|
||||
if (subscription.entity_id == entity_id) {
|
||||
subscription.callbacks.push_back(f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
HassSubscription subscription{entity_id};
|
||||
subscription.callbacks.push_back(f);
|
||||
hass_subscriptions_.push_back(subscription);
|
||||
|
||||
api::global_api_server->subscribe_home_assistant_state(
|
||||
entity_id, optional<std::string>(), [this, entity_id](const std::string &state) {
|
||||
ESP_LOGD(TAG, "received schedule plan from HASS entity '%s': %s", entity_id.c_str(), state.c_str());
|
||||
for (auto &subscription : hass_subscriptions_) {
|
||||
if (subscription.last_state != state) {
|
||||
if (subscription.entity_id == entity_id) {
|
||||
subscription.last_state = state;
|
||||
for (auto callback : subscription.callbacks) {
|
||||
callback(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void conv2_100_F::encode(uint8_t *out, DPValue in) {
|
||||
int16_t tmp = floor((in.getFloat() * 100) + 0.5);
|
||||
out[1] = tmp >> 8;
|
||||
out[0] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
DPValue conv2_100_F::decode(const uint8_t *in) {
|
||||
int16_t tmp = in[1] << 8 | in[0];
|
||||
DPValue out(tmp / 100.0f);
|
||||
return out;
|
||||
}
|
||||
|
||||
void conv4_1000_F::encode(uint8_t *out, DPValue in) {
|
||||
int32_t tmp = floor((in.getFloat() * 1000) + 0.5);
|
||||
out[3] = tmp >> 24;
|
||||
out[2] = tmp >> 16;
|
||||
out[1] = tmp >> 8;
|
||||
out[0] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
DPValue conv4_1000_F::decode(const uint8_t *in) {
|
||||
int32_t tmp = in[3] << 24 | in[2] << 16 | in[1] << 8 | in[0];
|
||||
DPValue out(tmp / 1000.0f);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
95
esphome/components/optolink/datapoint_component.h
Normal file
95
esphome/components/optolink/datapoint_component.h
Normal file
@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class Optolink;
|
||||
|
||||
struct HassSubscription {
|
||||
std::string entity_id;
|
||||
std::string last_state;
|
||||
std::vector<std::function<void(std::string)>> callbacks;
|
||||
};
|
||||
|
||||
class DatapointComponent {
|
||||
public:
|
||||
DatapointComponent(Optolink *optolink, bool writeable = false) : dp_value_outstanding((uint8_t) 0) {
|
||||
optolink_ = optolink;
|
||||
writeable_ = writeable;
|
||||
}
|
||||
|
||||
void set_address(uint32_t address) { address_ = address; }
|
||||
void set_bytes(size_t bytes) { bytes_ = bytes; }
|
||||
void set_writeable(bool writeable) { writeable_ = writeable; }
|
||||
void set_div_ratio(size_t div_ratio) { div_ratio_ = div_ratio; }
|
||||
|
||||
protected:
|
||||
virtual const StringRef &get_component_name() = 0;
|
||||
|
||||
uint32_t get_address() { return address_; }
|
||||
|
||||
void setup_datapoint();
|
||||
|
||||
void datapoint_read_request();
|
||||
|
||||
virtual void datapoint_value_changed(float value);
|
||||
virtual void datapoint_value_changed(uint8_t value);
|
||||
virtual void datapoint_value_changed(uint16_t value);
|
||||
virtual void datapoint_value_changed(uint32_t value);
|
||||
virtual void datapoint_value_changed(std::string value);
|
||||
virtual void datapoint_value_changed(uint8_t *value, size_t length);
|
||||
|
||||
void write_datapoint_value(float value);
|
||||
void write_datapoint_value(uint8_t value);
|
||||
void write_datapoint_value(uint16_t value);
|
||||
void write_datapoint_value(uint32_t value);
|
||||
void write_datapoint_value(uint8_t *value, size_t length);
|
||||
|
||||
void unfitting_value_type();
|
||||
void set_optolink_state(const char *format, ...);
|
||||
std::string get_optolink_state();
|
||||
|
||||
void subscribe_hass(std::string entity_id, std::function<void(std::string)> f);
|
||||
|
||||
private:
|
||||
const size_t MAX_RETRIES_UNTIL_RESET = 10;
|
||||
Optolink *optolink_;
|
||||
IDatapoint *datapoint_ = nullptr;
|
||||
size_t read_retries_ = 0;
|
||||
size_t div_ratio_ = 0;
|
||||
size_t bytes_;
|
||||
uint32_t address_;
|
||||
bool writeable_;
|
||||
|
||||
bool is_dp_value_writing_outstanding = false;
|
||||
DPValue dp_value_outstanding;
|
||||
|
||||
void datapoint_write_request(DPValue dp_value);
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
class conv2_100_F : public DPType {
|
||||
public:
|
||||
void encode(uint8_t *out, DPValue in);
|
||||
DPValue decode(const uint8_t *in);
|
||||
size_t get_length() const { return 2; }
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
class conv4_1000_F : public DPType {
|
||||
public:
|
||||
void encode(uint8_t *out, DPValue in);
|
||||
DPValue decode(const uint8_t *in);
|
||||
const size_t getLength() const { return 4; }
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,4 +1,3 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import number
|
||||
@ -10,29 +9,27 @@ from esphome.const import (
|
||||
CONF_MAX_VALUE,
|
||||
CONF_MIN_VALUE,
|
||||
CONF_STEP,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from .sensor import SENSOR_BASE_SCHEMA
|
||||
from . import OptolinkComponent, optolink_ns, CONF_OPTOLINK_ID
|
||||
from .. import optolink_ns, CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA
|
||||
|
||||
|
||||
DEPENDENCIES = ["optolink"]
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
|
||||
OptolinkNumber = optolink_ns.class_(
|
||||
"OptolinkNumber", number.Number, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
number.NUMBER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.GenerateID(): cv.declare_id(OptolinkNumber),
|
||||
cv.Required(CONF_MAX_VALUE): cv.float_,
|
||||
cv.Required(CONF_MIN_VALUE): cv.float_range(min=0.0),
|
||||
cv.Required(CONF_MIN_VALUE): cv.float_range(),
|
||||
cv.Required(CONF_STEP): cv.float_,
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(
|
||||
min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)
|
||||
),
|
||||
),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Required(CONF_BYTES): cv.one_of(1, 2, 4, int=True),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
49
esphome/components/optolink/number/optolink_number.cpp
Normal file
49
esphome/components/optolink/number/optolink_number.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_number.h"
|
||||
#include "../optolink.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink.number";
|
||||
|
||||
void OptolinkNumber::control(float value) {
|
||||
if (value > traits.get_max_value() || value < traits.get_min_value()) {
|
||||
set_optolink_state("datapoint value of number %s not in allowed range", get_component_name().c_str());
|
||||
ESP_LOGE(TAG, "datapoint value of number %s not in allowed range", get_component_name().c_str());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "control of number %s to value %f", get_component_name().c_str(), value);
|
||||
write_datapoint_value(value);
|
||||
publish_state(value);
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkNumber::datapoint_value_changed(uint8_t state) {
|
||||
if (traits.get_min_value() >= 0) {
|
||||
publish_state(state);
|
||||
} else {
|
||||
publish_state((int8_t) state);
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkNumber::datapoint_value_changed(uint16_t state) {
|
||||
if (traits.get_min_value() >= 0) {
|
||||
publish_state(state);
|
||||
} else {
|
||||
publish_state((int16_t) state);
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkNumber::datapoint_value_changed(uint32_t state) {
|
||||
if (traits.get_min_value() >= 0) {
|
||||
publish_state(state);
|
||||
} else {
|
||||
publish_state((int32_t) state);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
31
esphome/components/optolink/number/optolink_number.h
Normal file
31
esphome/components/optolink/number/optolink_number.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkNumber : public DatapointComponent, public esphome::number::Number, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkNumber(Optolink *optolink) : DatapointComponent(optolink, true) {}
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint(); }
|
||||
void update() override { datapoint_read_request(); }
|
||||
void control(float value) override;
|
||||
|
||||
const StringRef &get_component_name() override { return get_name(); }
|
||||
void datapoint_value_changed(float state) override { publish_state(state); };
|
||||
void datapoint_value_changed(uint8_t state) override;
|
||||
void datapoint_value_changed(uint16_t state) override;
|
||||
void datapoint_value_changed(uint32_t state) override;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,8 +1,8 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
#if defined(VITOWIFI_PROTOCOL)
|
||||
// NOLINTNEXTLINE
|
||||
@ -15,14 +15,16 @@ VitoWiFiClass<P300> VitoWiFi; // this is not really a fallback but dedicated to
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink";
|
||||
|
||||
void Optolink::comm_() {
|
||||
ESP_LOGD("Optolink", "enter _comm");
|
||||
ESP_LOGD(TAG, "enter _comm");
|
||||
VitoWiFi.readAll();
|
||||
ESP_LOGD("Optolink", "exit _comm");
|
||||
ESP_LOGD(TAG, "exit _comm");
|
||||
}
|
||||
|
||||
void Optolink::setup() {
|
||||
ESP_LOGI("Optolink", "setup");
|
||||
ESP_LOGI(TAG, "setup");
|
||||
|
||||
if (logger_enabled_) {
|
||||
VitoWiFi.setLogger(this);
|
||||
@ -34,41 +36,56 @@ void Optolink::setup() {
|
||||
#elif defined(USE_ESP8266)
|
||||
VitoWiFi.setup(&Serial);
|
||||
#endif
|
||||
|
||||
// set_interval("Optolink_comm", 10000, std::bind(&Optolink::_comm, this));
|
||||
}
|
||||
|
||||
void Optolink::loop() { VitoWiFi.loop(); }
|
||||
void Optolink::loop() {
|
||||
// ESP_LOGD(TAG, "queue size: %d", VitoWiFi.queueSize());
|
||||
VitoWiFi.loop();
|
||||
}
|
||||
|
||||
void Optolink::set_error(const char *format, ...) {
|
||||
void Optolink::set_state(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
char buffer[128];
|
||||
std::vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
va_end(args);
|
||||
|
||||
error_ = buffer;
|
||||
state_ = buffer;
|
||||
}
|
||||
|
||||
void Optolink::read_value(IDatapoint *datapoint) {
|
||||
bool Optolink::read_value(IDatapoint *datapoint) {
|
||||
if (datapoint != nullptr) {
|
||||
ESP_LOGI("Optolink", " read value of datapoint %s", datapoint->getName());
|
||||
VitoWiFi.readDatapoint(*datapoint);
|
||||
ESP_LOGI(TAG, "requesting value of datapoint %s", datapoint->getName());
|
||||
if (!VitoWiFi.readDatapoint(*datapoint)) {
|
||||
ESP_LOGE(TAG, "read request rejected due to queue overload - queue size: %d", VitoWiFi.queueSize());
|
||||
for (auto dp : datapoint->getCollection()) {
|
||||
ESP_LOGD(TAG, "queued datapoint: %s", dp->getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Optolink::write_value(IDatapoint *datapoint, DPValue dp_value) {
|
||||
bool Optolink::write_value(IDatapoint *datapoint, DPValue dp_value) {
|
||||
if (datapoint != nullptr) {
|
||||
char buffer[64];
|
||||
dp_value.getString(buffer, sizeof(buffer));
|
||||
ESP_LOGI("Optolink", " write value %s of datapoint %s", buffer, datapoint->getName());
|
||||
VitoWiFi.writeDatapoint(*datapoint, dp_value);
|
||||
ESP_LOGI(TAG, "sending value %s of datapoint %s", buffer, datapoint->getName());
|
||||
if (!VitoWiFi.writeDatapoint(*datapoint, dp_value)) {
|
||||
ESP_LOGE(TAG, "write request rejected due to queue overload - queue size: %d", VitoWiFi.queueSize());
|
||||
for (auto dp : datapoint->getCollection()) {
|
||||
ESP_LOGE(TAG, "queued dp: %s", dp->getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Optolink::write(uint8_t ch) {
|
||||
if (ch == '\n') {
|
||||
ESP_LOGD("VitoWifi", "%s", log_buffer_.c_str());
|
||||
ESP_LOGD(TAG, "VitoWiFi: %s", log_buffer_.c_str());
|
||||
log_buffer_.clear();
|
||||
} else {
|
||||
log_buffer_.push_back(ch);
|
||||
|
@ -3,9 +3,6 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
@ -13,7 +10,7 @@ namespace optolink {
|
||||
|
||||
class Optolink : public esphome::Component, public Print {
|
||||
protected:
|
||||
std::string error_ = "OK";
|
||||
std::string state_ = "OK";
|
||||
std::string log_buffer_;
|
||||
bool logger_enabled_ = false;
|
||||
int rx_pin_;
|
||||
@ -32,11 +29,11 @@ class Optolink : public esphome::Component, public Print {
|
||||
void set_rx_pin(int rx_pin) { rx_pin_ = rx_pin; }
|
||||
void set_tx_pin(int tx_pin) { tx_pin_ = tx_pin; }
|
||||
|
||||
void write_value(IDatapoint *datapoint, DPValue dp_value);
|
||||
void read_value(IDatapoint *datapoint);
|
||||
bool write_value(IDatapoint *datapoint, DPValue dp_value);
|
||||
bool read_value(IDatapoint *datapoint);
|
||||
|
||||
void set_error(const char *format, ...);
|
||||
std::string get_error() { return error_; }
|
||||
void set_state(const char *format, ...);
|
||||
std::string get_state() { return state_; }
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "optolink.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkBinarySensor : public OptolinkSensorBase,
|
||||
public esphome::binary_sensor::BinarySensor,
|
||||
public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkBinarySensor(Optolink *optolink) : OptolinkSensorBase(optolink) {
|
||||
bytes_ = 1;
|
||||
div_ratio_ = 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint_(); }
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
const StringRef &get_sensor_name() override { return get_name(); }
|
||||
void value_changed(float state) override { publish_state(state); };
|
||||
};
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,26 +0,0 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "optolink_device_info_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
void OptolinkDeviceInfoSensor::setup() {
|
||||
datapoint_ = new Datapoint<conv4_1_UL>(get_name().c_str(), "optolink", 0x00f8, false);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
uint32_t value = dp_value.getU32();
|
||||
ESP_LOGD("OptolinkTextSensor", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), value);
|
||||
uint8_t *bytes = (uint8_t *) &value;
|
||||
uint16_t tmp = esphome::byteswap(*((uint16_t *) bytes));
|
||||
std::string geraetekennung = esphome::format_hex_pretty(&tmp, 1);
|
||||
std::string hardware_revision = esphome::format_hex_pretty((uint8_t *) bytes + 2, 1);
|
||||
std::string software_index = esphome::format_hex_pretty((uint8_t *) bytes + 3, 1);
|
||||
publish_state("Device ID: " + geraetekennung + "|Hardware Revision: " + hardware_revision +
|
||||
"|Software Index: " + software_index);
|
||||
});
|
||||
}
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "optolink.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkDeviceInfoSensor : public esphome::text_sensor::TextSensor, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkDeviceInfoSensor(const std::string &name, Optolink *optolink) {
|
||||
optolink_ = optolink;
|
||||
set_name(name.c_str());
|
||||
set_update_interval(1800000);
|
||||
set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
private:
|
||||
Optolink *optolink_;
|
||||
IDatapoint *datapoint_;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,24 +0,0 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_number.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
void OptolinkNumber::control(float value) {
|
||||
if (value > traits.get_max_value() || value < traits.get_min_value()) {
|
||||
optolink_->set_error("datapoint value of number %s not in allowed range", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkNumber", "datapoint value of number %s not in allowed range", get_sensor_name().c_str());
|
||||
} else {
|
||||
ESP_LOGI("OptolinkNumber", "control of number %s to value %f", get_sensor_name().c_str(), value);
|
||||
update_datapoint_(value);
|
||||
publish_state(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkNumber : public OptolinkSensorBase, public esphome::number::Number, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkNumber(Optolink *optolink) : OptolinkSensorBase(optolink, true) {}
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint_(); }
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
const StringRef &get_sensor_name() override { return get_name(); }
|
||||
void value_changed(float state) override { publish_state(state); };
|
||||
|
||||
void control(float value) override;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,45 +0,0 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_select.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
void OptolinkSelect::control(const std::string &value) {
|
||||
for (auto it = mapping_->begin(); it != mapping_->end(); ++it) {
|
||||
if (it->second == value) {
|
||||
ESP_LOGI("OptolinkSelect", "control of select %s to value %s", get_sensor_name().c_str(), it->first.c_str());
|
||||
update_datapoint_(std::stof(it->first));
|
||||
publish_state(it->second);
|
||||
break;
|
||||
}
|
||||
if (it == mapping_->end()) {
|
||||
optolink_->set_error("unknown value %s of select %s", value.c_str(), get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSelect", "unknown value %s of select %s", value.c_str(), get_sensor_name().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkSelect::value_changed(float state) {
|
||||
std::string key;
|
||||
if (div_ratio_ == 1) {
|
||||
key = std::to_string((int) state);
|
||||
} else {
|
||||
key = std::to_string(state);
|
||||
}
|
||||
auto pos = mapping_->find(key);
|
||||
if (pos == mapping_->end()) {
|
||||
optolink_->set_error("value %s not found in select %s", key.c_str(), get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSelect", "value %s not found in select %s", key.c_str(), get_sensor_name().c_str());
|
||||
} else {
|
||||
publish_state(pos->second);
|
||||
}
|
||||
//-----------------------------------------------publish_state(state);
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include <map>
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "optolink.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkSelect : public OptolinkSensorBase, public esphome::select::Select, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkSelect(Optolink *optolink) : OptolinkSensorBase(optolink, true) {}
|
||||
|
||||
void set_map(std::map<std::string, std::string> *mapping) {
|
||||
mapping_ = mapping;
|
||||
std::vector<std::string> values;
|
||||
for (auto &it : *mapping) {
|
||||
values.push_back(it.second);
|
||||
}
|
||||
traits.set_options(values);
|
||||
};
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint_(); }
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
const StringRef &get_sensor_name() override { return get_name(); }
|
||||
void value_changed(float state) override;
|
||||
|
||||
void control(const std::string &value) override;
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> *mapping_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "optolink.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkSensor : public OptolinkSensorBase, public esphome::sensor::Sensor, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkSensor(Optolink *optolink) : OptolinkSensorBase(optolink) {
|
||||
set_state_class(esphome::sensor::STATE_CLASS_MEASUREMENT);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() { setup_datapoint_(); }
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
const StringRef &get_sensor_name() override { return get_name(); }
|
||||
void value_changed(float state) override { publish_state(state); };
|
||||
};
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,166 +0,0 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "optolink.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
void OptolinkSensorBase::update_datapoint_(float value) {
|
||||
if (!writeable_) {
|
||||
optolink_->set_error("try to control not writable number %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "try to control not writable number %s", get_sensor_name().c_str());
|
||||
} else if (datapoint_ != nullptr) {
|
||||
switch (bytes_) {
|
||||
case 1:
|
||||
switch (div_ratio_) {
|
||||
case 1:
|
||||
optolink_->write_value(datapoint_, DPValue((uint8_t) value));
|
||||
break;
|
||||
case 10:
|
||||
optolink_->write_value(datapoint_, DPValue((float) value));
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte/div_ratio combination for number %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte/div_ratio combination for number %s",
|
||||
get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (div_ratio_) {
|
||||
case 1:
|
||||
optolink_->write_value(datapoint_, DPValue((uint16_t) value));
|
||||
break;
|
||||
case 10:
|
||||
case 100:
|
||||
optolink_->write_value(datapoint_, DPValue((float) value));
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte/div_ratio combination for number %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte/div_ratio combination for number %s",
|
||||
get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (div_ratio_) {
|
||||
case 1:
|
||||
optolink_->write_value(datapoint_, DPValue((uint32_t) value));
|
||||
break;
|
||||
case 3600:
|
||||
optolink_->write_value(datapoint_, DPValue((float) value));
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte/div_ratio combination for number %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte/div_ratio combination for number %s",
|
||||
get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte value for number %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte value for number %s", get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OptolinkSensorBase::setup_datapoint_() {
|
||||
switch (bytes_) {
|
||||
case 1:
|
||||
switch (div_ratio_) {
|
||||
case 1:
|
||||
datapoint_ = new Datapoint<conv1_1_US>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), dp_value.getU8());
|
||||
value_changed(dp_value.getU8());
|
||||
});
|
||||
break;
|
||||
case 10:
|
||||
datapoint_ = new Datapoint<conv1_10_F>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dp_value.getFloat());
|
||||
value_changed(dp_value.getFloat());
|
||||
});
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte/div_ratio combination for sensor %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte/div_ratio combination for sensor %s", get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (div_ratio_) {
|
||||
case 1:
|
||||
datapoint_ = new Datapoint<conv2_1_US>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), dp_value.getU16());
|
||||
value_changed(dp_value.getU16());
|
||||
});
|
||||
break;
|
||||
case 10:
|
||||
datapoint_ = new Datapoint<conv2_10_F>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dp_value.getFloat());
|
||||
value_changed(dp_value.getFloat());
|
||||
});
|
||||
break;
|
||||
case 100:
|
||||
datapoint_ = new Datapoint<conv2_100_F>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dp_value.getFloat());
|
||||
value_changed(dp_value.getFloat());
|
||||
});
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte/div_ratio combination for sensor %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte/div_ratio combination for sensor %s", get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (div_ratio_) {
|
||||
case 1:
|
||||
datapoint_ = new Datapoint<conv4_1_UL>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), dp_value.getU32());
|
||||
value_changed(dp_value.getU32());
|
||||
});
|
||||
break;
|
||||
case 3600:
|
||||
datapoint_ = new Datapoint<conv4_3600_F>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dp_value.getFloat());
|
||||
value_changed(dp_value.getFloat());
|
||||
});
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte/div_ratio combination for sensor %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte/div_ratio combination for sensor %s", get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
optolink_->set_error("Unknown byte value for sensor %s", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSensorBase", "Unknown byte value for sensor %s", get_sensor_name().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void conv2_100_F::encode(uint8_t *out, DPValue in) {
|
||||
int16_t tmp = floor((in.getFloat() * 100) + 0.5);
|
||||
out[1] = tmp >> 8;
|
||||
out[0] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
DPValue conv2_100_F::decode(const uint8_t *in) {
|
||||
int16_t tmp = in[1] << 8 | in[0];
|
||||
DPValue out(tmp / 100.0f);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class Optolink;
|
||||
|
||||
class OptolinkSensorBase {
|
||||
protected:
|
||||
Optolink *optolink_;
|
||||
bool writeable_;
|
||||
IDatapoint *datapoint_ = nullptr;
|
||||
uint32_t address_;
|
||||
int bytes_;
|
||||
int div_ratio_ = 1;
|
||||
|
||||
void setup_datapoint_();
|
||||
void update_datapoint_(float value);
|
||||
|
||||
public:
|
||||
OptolinkSensorBase(Optolink *optolink, bool writeable = false) {
|
||||
optolink_ = optolink;
|
||||
writeable_ = writeable;
|
||||
}
|
||||
|
||||
void set_address(uint32_t address) { address_ = address; }
|
||||
void set_bytes(int bytes) { bytes_ = bytes; }
|
||||
void set_div_ratio(int div_ratio) { div_ratio_ = div_ratio; }
|
||||
|
||||
protected:
|
||||
virtual const StringRef &get_sensor_name() = 0;
|
||||
virtual void value_changed(float state) = 0;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
class conv2_100_F : public DPType {
|
||||
public:
|
||||
void encode(uint8_t *out, DPValue in);
|
||||
DPValue decode(const uint8_t *in);
|
||||
size_t get_length() const { return 2; }
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "optolink.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkStateSensor : public esphome::text_sensor::TextSensor, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkStateSensor(std::string name, Optolink *optolink) {
|
||||
optolink_ = optolink;
|
||||
set_name(name.c_str());
|
||||
set_update_interval(1000);
|
||||
set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() override{};
|
||||
void update() override { publish_state(optolink_->get_error()); }
|
||||
|
||||
private:
|
||||
Optolink *optolink_;
|
||||
};
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,24 +0,0 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_switch.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
void OptolinkSwitch::write_state(bool value) {
|
||||
if (value != 0 && value != 1) {
|
||||
optolink_->set_error("datapoint value of switch %s not 0 or 1", get_sensor_name().c_str());
|
||||
ESP_LOGE("OptolinkSwitch", "datapoint value of switch %s not 0 or 1", get_sensor_name().c_str());
|
||||
} else {
|
||||
ESP_LOGI("OptolinkSwitch", "control of switch %s to value %d", get_sensor_name().c_str(), value);
|
||||
update_datapoint_(value);
|
||||
publish_state(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkSwitch : public OptolinkSensorBase, public esphome::switch_::Switch, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkSwitch(Optolink *optolink) : OptolinkSensorBase(optolink, true) {
|
||||
bytes_ = 1;
|
||||
div_ratio_ = 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint_(); }
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
const StringRef &get_sensor_name() override { return get_name(); }
|
||||
void value_changed(float state) override { publish_state(state); };
|
||||
|
||||
void write_state(bool value) override;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_text_sensor.h"
|
||||
#include "optolink.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
void OptolinkTextSensor::setup() {
|
||||
if (!raw_) {
|
||||
setup_datapoint_();
|
||||
} else {
|
||||
datapoint_ = new Datapoint<convRaw>(get_sensor_name().c_str(), "optolink", address_, writeable_);
|
||||
datapoint_->setLength(bytes_);
|
||||
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dp_value) {
|
||||
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: <raw>", dp.getGroup(), dp.getName());
|
||||
uint8_t buffer[bytes_ + 1];
|
||||
dp_value.getRaw(buffer);
|
||||
buffer[bytes_] = 0x0;
|
||||
publish_state((char *) buffer);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "optolink.h"
|
||||
#include "optolink_sensor_base.h"
|
||||
#include "VitoWiFi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkTextSensor : public OptolinkSensorBase,
|
||||
public esphome::text_sensor::TextSensor,
|
||||
public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkTextSensor(Optolink *optolink) : OptolinkSensorBase(optolink) {}
|
||||
|
||||
void set_raw(bool raw) { raw_ = raw; }
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
void update() override { optolink_->read_value(datapoint_); }
|
||||
|
||||
const StringRef &get_sensor_name() override { return get_name(); }
|
||||
void value_changed(float state) override { publish_state(std::to_string(state)); };
|
||||
|
||||
private:
|
||||
bool raw_ = false;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,4 +1,3 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import select
|
||||
@ -9,10 +8,12 @@ from esphome.const import (
|
||||
CONF_FROM,
|
||||
CONF_ID,
|
||||
CONF_TO,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from . import OptolinkComponent, optolink_ns, CONF_OPTOLINK_ID
|
||||
from .sensor import SENSOR_BASE_SCHEMA
|
||||
from .. import optolink_ns, CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA
|
||||
|
||||
DEPENDENCIES = ["optolink"]
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
|
||||
|
||||
OptolinkSelect = optolink_ns.class_(
|
||||
"OptolinkSelect", select.Select, cg.PollingComponent
|
||||
@ -37,18 +38,13 @@ MAP_ID = "mappings"
|
||||
CONFIG_SCHEMA = (
|
||||
select.SELECT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.GenerateID(): cv.declare_id(OptolinkSelect),
|
||||
cv.GenerateID(MAP_ID): cv.declare_id(
|
||||
cg.std_ns.class_("map").template(cg.std_string, cg.std_string)
|
||||
),
|
||||
cv.Required(CONF_MAP): cv.ensure_list(validate_mapping),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(
|
||||
min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)
|
||||
),
|
||||
),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Required(CONF_BYTES): cv.one_of(1, 2, 4, int=True),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
59
esphome/components/optolink/select/optolink_select.cpp
Normal file
59
esphome/components/optolink/select/optolink_select.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_select.h"
|
||||
#include "../optolink.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink.select";
|
||||
|
||||
void OptolinkSelect::control(const std::string &value) {
|
||||
for (auto it = mapping_->begin(); it != mapping_->end(); ++it) {
|
||||
if (it->second == value) {
|
||||
ESP_LOGI(TAG, "control of select %s to value %s", get_component_name().c_str(), it->first.c_str());
|
||||
write_datapoint_value(std::stof(it->first));
|
||||
publish_state(it->second);
|
||||
break;
|
||||
}
|
||||
if (it == mapping_->end()) {
|
||||
set_optolink_state("unknown value %s of select %s", value.c_str(), get_component_name().c_str());
|
||||
ESP_LOGE(TAG, "unknown value %s of select %s", value.c_str(), get_component_name().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkSelect::datapoint_value_changed(std::string key) {
|
||||
auto pos = mapping_->find(key);
|
||||
if (pos == mapping_->end()) {
|
||||
set_optolink_state("value %s not found in select %s", key.c_str(), get_component_name().c_str());
|
||||
ESP_LOGE(TAG, "value %s not found in select %s", key.c_str(), get_component_name().c_str());
|
||||
} else {
|
||||
publish_state(pos->second);
|
||||
}
|
||||
}
|
||||
|
||||
void OptolinkSelect::datapoint_value_changed(uint8_t state) {
|
||||
std::string key = std::to_string(state);
|
||||
datapoint_value_changed(key);
|
||||
}
|
||||
|
||||
void OptolinkSelect::datapoint_value_changed(uint16_t state) {
|
||||
std::string key = std::to_string(state);
|
||||
datapoint_value_changed(key);
|
||||
}
|
||||
|
||||
void OptolinkSelect::datapoint_value_changed(uint32_t state) {
|
||||
std::string key = std::to_string(state);
|
||||
datapoint_value_changed(key);
|
||||
}
|
||||
|
||||
void OptolinkSelect::datapoint_value_changed(float state) {
|
||||
std::string key = std::to_string(state);
|
||||
datapoint_value_changed(key);
|
||||
}
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
45
esphome/components/optolink/select/optolink_select.h
Normal file
45
esphome/components/optolink/select/optolink_select.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
#include <map>
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkSelect : public DatapointComponent, public esphome::select::Select, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkSelect(Optolink *optolink) : DatapointComponent(optolink, true) {}
|
||||
|
||||
void set_map(std::map<std::string, std::string> *mapping) {
|
||||
mapping_ = mapping;
|
||||
std::vector<std::string> values;
|
||||
for (auto &it : *mapping) {
|
||||
values.push_back(it.second);
|
||||
}
|
||||
traits.set_options(values);
|
||||
};
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint(); }
|
||||
void update() override { datapoint_read_request(); }
|
||||
void control(const std::string &value) override;
|
||||
|
||||
const StringRef &get_component_name() override { return get_name(); }
|
||||
void datapoint_value_changed(std::string state) override;
|
||||
void datapoint_value_changed(uint8_t state) override;
|
||||
void datapoint_value_changed(uint16_t state) override;
|
||||
void datapoint_value_changed(uint32_t state) override;
|
||||
void datapoint_value_changed(float state) override;
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> *mapping_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,51 +0,0 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_ADDRESS,
|
||||
CONF_BYTES,
|
||||
CONF_DIV_RATIO,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from . import optolink_ns, OptolinkComponent
|
||||
|
||||
OptolinkSensor = optolink_ns.class_(
|
||||
"OptolinkSensor", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
CONF_OPTOLINK_ID = "optolink_id"
|
||||
SENSOR_BASE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Required(CONF_BYTES): cv.one_of(1, 2, 4, int=True),
|
||||
cv.Optional(CONF_DIV_RATIO, default=1): cv.one_of(1, 10, 100, 3600, int=True),
|
||||
}
|
||||
)
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(OptolinkSensor)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(
|
||||
min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(SENSOR_BASE_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
||||
cg.add(var.set_bytes(config[CONF_BYTES]))
|
||||
cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
|
45
esphome/components/optolink/sensor/__init__.py
Normal file
45
esphome/components/optolink/sensor/__init__.py
Normal file
@ -0,0 +1,45 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_BYTES,
|
||||
CONF_DIV_RATIO,
|
||||
CONF_ID,
|
||||
CONF_MIN_VALUE,
|
||||
)
|
||||
from .. import CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA, optolink_ns
|
||||
|
||||
DEPENDENCIES = ["optolink"]
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
|
||||
|
||||
OptolinkSensor = optolink_ns.class_(
|
||||
"OptolinkSensor", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(OptolinkSensor),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Required(CONF_BYTES): cv.one_of(1, 2, 4, int=True),
|
||||
cv.Optional(CONF_MIN_VALUE): cv.float_,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(SENSOR_BASE_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
||||
cg.add(var.set_bytes(config[CONF_BYTES]))
|
||||
cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
|
||||
if CONF_MIN_VALUE in config:
|
||||
cg.add(var.set_min_value(config[CONF_MIN_VALUE]))
|
40
esphome/components/optolink/sensor/optolink_sensor.cpp
Normal file
40
esphome/components/optolink/sensor/optolink_sensor.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_sensor.h"
|
||||
#include "../optolink.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink.sensor";
|
||||
|
||||
void OptolinkSensor::set_min_value(float min_value) { min_value_ = -29.3; }
|
||||
|
||||
void OptolinkSensor::datapoint_value_changed(uint8_t state) {
|
||||
if (min_value_ >= 0.0) {
|
||||
publish_state(state);
|
||||
} else {
|
||||
publish_state((int8_t) state);
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkSensor::datapoint_value_changed(uint16_t state) {
|
||||
if (min_value_ >= 0.0) {
|
||||
publish_state(state);
|
||||
} else {
|
||||
publish_state((int16_t) state);
|
||||
}
|
||||
}
|
||||
|
||||
void OptolinkSensor::datapoint_value_changed(uint32_t state) {
|
||||
if (min_value_ >= 0.0) {
|
||||
publish_state(state);
|
||||
} else {
|
||||
publish_state((int32_t) state);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
37
esphome/components/optolink/sensor/optolink_sensor.h
Normal file
37
esphome/components/optolink/sensor/optolink_sensor.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
#include <cfloat>
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkSensor : public DatapointComponent, public esphome::sensor::Sensor, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkSensor(Optolink *optolink) : DatapointComponent(optolink) {
|
||||
set_state_class(esphome::sensor::STATE_CLASS_MEASUREMENT);
|
||||
}
|
||||
|
||||
void set_min_value(float min_value);
|
||||
|
||||
protected:
|
||||
void setup() { setup_datapoint(); }
|
||||
void update() override { datapoint_read_request(); }
|
||||
|
||||
const StringRef &get_component_name() override { return get_name(); }
|
||||
void datapoint_value_changed(float state) override { publish_state(state); };
|
||||
void datapoint_value_changed(uint8_t state) override;
|
||||
void datapoint_value_changed(uint16_t state) override;
|
||||
void datapoint_value_changed(uint32_t state) override;
|
||||
|
||||
private:
|
||||
float min_value_ = -FLT_MAX;
|
||||
};
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,33 +0,0 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_UPDATE_INTERVAL
|
||||
from . import OptolinkComponent, optolink_ns
|
||||
|
||||
OptolinkSwitch = optolink_ns.class_(
|
||||
"OptolinkSwitch", switch.Switch, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONF_OPTOLINK_ID = "optolink_id"
|
||||
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.GenerateID(): cv.declare_id(OptolinkSwitch),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)),
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await switch.register_switch(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
42
esphome/components/optolink/switch/__init__.py
Normal file
42
esphome/components/optolink/switch/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_UPDATE_INTERVAL
|
||||
from .. import SENSOR_BASE_SCHEMA, optolink_ns
|
||||
|
||||
DEPENDENCIES = ["optolink"]
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
|
||||
|
||||
OptolinkSwitch = optolink_ns.class_(
|
||||
"OptolinkSwitch", switch.Switch, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONF_OPTOLINK_ID = "optolink_id"
|
||||
CONFIG_SCHEMA = (
|
||||
switch.SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(OptolinkSwitch),
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(
|
||||
min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(SENSOR_BASE_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await switch.register_switch(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
25
esphome/components/optolink/switch/optolink_switch.cpp
Normal file
25
esphome/components/optolink/switch/optolink_switch.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "optolink_switch.h"
|
||||
#include "../optolink.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink.switch";
|
||||
|
||||
void OptolinkSwitch::write_state(bool value) {
|
||||
if (value != 0 && value != 1) {
|
||||
set_optolink_state("datapoint value of switch %s not 0 or 1", get_component_name().c_str());
|
||||
ESP_LOGE(TAG, "datapoint value of switch %s not 0 or 1", get_component_name().c_str());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "control of switch %s to value %d", get_component_name().c_str(), value);
|
||||
write_datapoint_value((uint8_t) value);
|
||||
publish_state(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
31
esphome/components/optolink/switch/optolink_switch.h
Normal file
31
esphome/components/optolink/switch/optolink_switch.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
class OptolinkSwitch : public DatapointComponent, public esphome::switch_::Switch, public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkSwitch(Optolink *optolink) : DatapointComponent(optolink, true) {
|
||||
set_bytes(1);
|
||||
set_div_ratio(1);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup() override { setup_datapoint(); }
|
||||
void update() override { datapoint_read_request(); }
|
||||
void write_state(bool value) override;
|
||||
|
||||
const StringRef &get_component_name() override { return get_name(); }
|
||||
void datapoint_value_changed(uint8_t state) override { publish_state(state); };
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -1,49 +0,0 @@
|
||||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_BYTES,
|
||||
CONF_DIV_RATIO,
|
||||
CONF_ID,
|
||||
CONF_RAW,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from . import optolink_ns, OptolinkComponent, CONF_OPTOLINK_ID
|
||||
from .sensor import SENSOR_BASE_SCHEMA
|
||||
|
||||
OptolinkTextSensor = optolink_ns.class_(
|
||||
"OptolinkTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
text_sensor.text_sensor_schema(OptolinkTextSensor)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OPTOLINK_ID): cv.use_id(OptolinkComponent),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="10s"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(
|
||||
min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_RAW, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(SENSOR_BASE_SCHEMA)
|
||||
.extend({cv.Required(CONF_BYTES): cv.int_}),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
|
||||
cg.add(var.set_raw(config[CONF_RAW]))
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
||||
cg.add(var.set_bytes(config[CONF_BYTES]))
|
||||
cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
|
174
esphome/components/optolink/text_sensor/__init__.py
Normal file
174
esphome/components/optolink/text_sensor/__init__.py
Normal file
@ -0,0 +1,174 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_BYTES,
|
||||
CONF_DIV_RATIO,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_ID,
|
||||
CONF_MODE,
|
||||
)
|
||||
from .. import optolink_ns, CONF_OPTOLINK_ID, SENSOR_BASE_SCHEMA
|
||||
|
||||
DEPENDENCIES = ["optolink", "api"]
|
||||
CODEOWNERS = ["@j0ta29"]
|
||||
|
||||
TextSensorMode = optolink_ns.enum("TextSensorMode")
|
||||
MODE = {
|
||||
"MAP": TextSensorMode.MAP,
|
||||
"RAW": TextSensorMode.RAW,
|
||||
"DAY_SCHEDULE": TextSensorMode.DAY_SCHEDULE,
|
||||
"DAY_SCHEDULE_SYNCHRONIZED": TextSensorMode.DAY_SCHEDULE_SYNCHRONIZED,
|
||||
"DEVICE_INFO": TextSensorMode.DEVICE_INFO,
|
||||
"STATE_INFO": TextSensorMode.STATE_INFO,
|
||||
}
|
||||
DAY_OF_WEEK = {
|
||||
"MONDAY": 0,
|
||||
"TUESDAY": 1,
|
||||
"WEDNESDAY": 2,
|
||||
"THURSDAY": 3,
|
||||
"FRIDAY": 4,
|
||||
"SATURDAY": 5,
|
||||
"SUNDAY": 6,
|
||||
}
|
||||
CONF_DAY_OF_WEEK = "day_of_week"
|
||||
|
||||
OptolinkTextSensor = optolink_ns.class_(
|
||||
"OptolinkTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
def check_address():
|
||||
def validator_(config):
|
||||
modes_address_needed = [
|
||||
"MAP",
|
||||
"RAW",
|
||||
"DAY_SCHEDULE",
|
||||
"DAY_SCHEDULE_SYNCHRONIZED",
|
||||
]
|
||||
address_needed = config[CONF_MODE] in modes_address_needed
|
||||
address_defined = CONF_ADDRESS in config
|
||||
if address_needed and not address_defined:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_ADDRESS} is required in this modes: {modes_address_needed}"
|
||||
)
|
||||
if not address_needed and address_defined:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_ADDRESS} is only allowed in this modes mode: {modes_address_needed}"
|
||||
)
|
||||
return config
|
||||
|
||||
return validator_
|
||||
|
||||
|
||||
def check_bytes():
|
||||
def validator_(config):
|
||||
modes_bytes_needed = ["MAP", "RAW", "DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"]
|
||||
bytes_needed = config[CONF_MODE] in modes_bytes_needed
|
||||
bytes_defined = CONF_BYTES in config
|
||||
if bytes_needed and not bytes_defined:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_BYTES} is required in this modes: {modes_bytes_needed}"
|
||||
)
|
||||
if not bytes_needed and bytes_defined:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_BYTES} is only allowed in this modes: {modes_bytes_needed}"
|
||||
)
|
||||
|
||||
modes_bytes_range_1_to_9 = ["MAP", "RAW"]
|
||||
if config[CONF_MODE] in modes_bytes_range_1_to_9 and config[
|
||||
CONF_BYTES
|
||||
] not in range(0, 10):
|
||||
raise cv.Invalid(
|
||||
f"{CONF_BYTES} must be between 1 and 9 for this modes: {modes_bytes_range_1_to_9}"
|
||||
)
|
||||
|
||||
modes_bytes_day_schedule = ["DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"]
|
||||
if config[CONF_MODE] in modes_bytes_day_schedule and config[CONF_BYTES] not in [
|
||||
56
|
||||
]:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_BYTES} must be 56 for this modes: {modes_bytes_day_schedule}"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
return validator_
|
||||
|
||||
|
||||
def check_dow():
|
||||
def validator_(config):
|
||||
modes_dow_needed = ["DAY_SCHEDULE", "DAY_SCHEDULE_SYNCHRONIZED"]
|
||||
if config[CONF_MODE] in modes_dow_needed and CONF_DAY_OF_WEEK not in config:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_DAY_OF_WEEK} is required in this modes: {modes_dow_needed}"
|
||||
)
|
||||
if config[CONF_MODE] not in modes_dow_needed and CONF_DAY_OF_WEEK in config:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_DAY_OF_WEEK} is only allowed in this modes: {modes_dow_needed}"
|
||||
)
|
||||
return config
|
||||
|
||||
return validator_
|
||||
|
||||
|
||||
def check_entity_id():
|
||||
def validator_(config):
|
||||
modes_entitiy_id_needed = ["DAY_SCHEDULE_SYNCHRONIZED"]
|
||||
if (
|
||||
config[CONF_MODE] in modes_entitiy_id_needed
|
||||
and CONF_ENTITY_ID not in config
|
||||
):
|
||||
raise cv.Invalid(
|
||||
f"{CONF_ENTITY_ID} is required in this modes: {modes_entitiy_id_needed}"
|
||||
)
|
||||
if (
|
||||
config[CONF_MODE] not in modes_entitiy_id_needed
|
||||
and CONF_ENTITY_ID in config
|
||||
):
|
||||
raise cv.Invalid(
|
||||
f"{CONF_ENTITY_ID} is only allowed in this modes: {modes_entitiy_id_needed}"
|
||||
)
|
||||
return config
|
||||
|
||||
return validator_
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
text_sensor.TEXT_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(OptolinkTextSensor),
|
||||
cv.Required(CONF_MODE): cv.enum(MODE, upper=True),
|
||||
cv.Optional(CONF_ADDRESS): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_BYTES): cv.uint8_t,
|
||||
cv.Optional(CONF_DAY_OF_WEEK): cv.enum(DAY_OF_WEEK, upper=True),
|
||||
cv.Optional(CONF_ENTITY_ID): cv.entity_id,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(SENSOR_BASE_SCHEMA),
|
||||
check_address(),
|
||||
check_bytes(),
|
||||
check_dow(),
|
||||
check_entity_id(),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
component = await cg.get_variable(config[CONF_OPTOLINK_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], component)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
|
||||
cg.add(var.set_mode(config[CONF_MODE]))
|
||||
if CONF_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
||||
cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
|
||||
if CONF_BYTES in config:
|
||||
cg.add(var.set_bytes(config[CONF_BYTES]))
|
||||
if CONF_DAY_OF_WEEK in config:
|
||||
cg.add(var.set_day_of_week(config[CONF_DAY_OF_WEEK]))
|
||||
if CONF_ENTITY_ID in config:
|
||||
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
|
176
esphome/components/optolink/text_sensor/optolink_text_sensor.cpp
Normal file
176
esphome/components/optolink/text_sensor/optolink_text_sensor.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "optolink_text_sensor.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
static const char *const TAG = "optolink.text_sensor";
|
||||
|
||||
struct Time {
|
||||
int hours;
|
||||
int minutes;
|
||||
};
|
||||
|
||||
bool check_time_sequence(const Time &t1, const Time &t2) {
|
||||
if (t2.hours > t1.hours || (t2.hours == t1.hours && t2.minutes >= t1.minutes)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_time_values(const Time &time) {
|
||||
return (time.hours >= 0 && time.hours <= 23) && (time.minutes >= 0 && time.minutes <= 59);
|
||||
}
|
||||
|
||||
uint8_t *encode_time_string(std::string input) {
|
||||
char buffer[49];
|
||||
strncpy(buffer, input.c_str(), sizeof(buffer));
|
||||
buffer[sizeof(buffer) - 1] = 0x00;
|
||||
Time time_values[8];
|
||||
Time prev_time = {0, 0};
|
||||
int time_count = 0;
|
||||
|
||||
char *token = strtok(buffer, " ");
|
||||
while (token && time_count < 8) {
|
||||
Time current_time;
|
||||
if (sscanf(token, "%d:%d", ¤t_time.hours, ¤t_time.minutes) == 2) {
|
||||
if (check_time_values(current_time) && check_time_sequence(prev_time, current_time)) {
|
||||
time_values[time_count++] = current_time;
|
||||
prev_time = current_time;
|
||||
} else {
|
||||
ESP_LOGE(
|
||||
TAG,
|
||||
"Time values should be in the format hh:mm and in increasing order within the range of 00:00 to 23:59");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid time format");
|
||||
return 0;
|
||||
}
|
||||
token = strtok(nullptr, " ");
|
||||
}
|
||||
|
||||
if (time_count % 2) {
|
||||
ESP_LOGE(TAG, "Number of time values must be even");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (time_count < 8) {
|
||||
time_values[time_count++] = {31, 70};
|
||||
}
|
||||
|
||||
static uint8_t data[8];
|
||||
// ESP_LOGD(TAG, "Parsed time values:");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Time time = time_values[i];
|
||||
data[i] = (time.hours << 3) + (time.minutes / 10);
|
||||
// ESP_LOGD(TAG, " %02d:%02d => %d", time.hours, time.minutes, data[i]);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void OptolinkTextSensor::setup() {
|
||||
switch (mode_) {
|
||||
case MAP:
|
||||
break;
|
||||
case RAW:
|
||||
set_div_ratio(0);
|
||||
break;
|
||||
case DAY_SCHEDULE:
|
||||
set_div_ratio(0);
|
||||
set_bytes(8);
|
||||
set_address(get_address() + 8 * dow_);
|
||||
break;
|
||||
case DAY_SCHEDULE_SYNCHRONIZED:
|
||||
set_writeable(true);
|
||||
set_div_ratio(0);
|
||||
set_bytes(8);
|
||||
set_address(get_address() + 8 * dow_);
|
||||
ESP_LOGI(TAG, "subscribing to schedule plan from HASS entity '%s' for component %s", this->entity_id_.c_str(),
|
||||
get_component_name().c_str());
|
||||
subscribe_hass(entity_id_, [this](const std::string &state) {
|
||||
ESP_LOGD(TAG, "update for schedule plan for component %s: %s", get_component_name().c_str(), state.c_str());
|
||||
uint8_t *data = encode_time_string(state);
|
||||
if (data) {
|
||||
write_datapoint_value(data, 8);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "not changing any value of datapoint %s", get_component_name().c_str());
|
||||
}
|
||||
});
|
||||
break;
|
||||
case DEVICE_INFO:
|
||||
set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC);
|
||||
set_bytes(4);
|
||||
set_address(0x00f8);
|
||||
break;
|
||||
case STATE_INFO:
|
||||
set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC);
|
||||
return; // no datapoint setup!
|
||||
}
|
||||
setup_datapoint();
|
||||
};
|
||||
|
||||
void OptolinkTextSensor::update() {
|
||||
if (mode_ == STATE_INFO) {
|
||||
publish_state(get_optolink_state());
|
||||
} else {
|
||||
datapoint_read_request();
|
||||
}
|
||||
}
|
||||
|
||||
void OptolinkTextSensor::datapoint_value_changed(uint8_t *value, size_t length) {
|
||||
switch (mode_) {
|
||||
case RAW:
|
||||
publish_state(std::string((const char *) value));
|
||||
break;
|
||||
case DAY_SCHEDULE:
|
||||
case DAY_SCHEDULE_SYNCHRONIZED:
|
||||
if (length == 8) {
|
||||
char buffer[6 * length + 1];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int hour = value[i] >> 3;
|
||||
int minute = (value[i] & 0b111) * 10;
|
||||
if (value[i] != 0xFF) {
|
||||
sprintf(buffer + i * 6, "%02d:%02d ", hour, minute);
|
||||
} else {
|
||||
sprintf(buffer + i * 6, " ");
|
||||
}
|
||||
}
|
||||
publish_state(buffer);
|
||||
} else {
|
||||
unfitting_value_type();
|
||||
}
|
||||
break;
|
||||
case DEVICE_INFO:
|
||||
case STATE_INFO:
|
||||
case MAP:
|
||||
unfitting_value_type();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void OptolinkTextSensor::datapoint_value_changed(uint32_t value) {
|
||||
switch (mode_) {
|
||||
case DEVICE_INFO: {
|
||||
uint8_t *bytes = (uint8_t *) &value;
|
||||
uint16_t tmp = esphome::byteswap(*((uint16_t *) bytes));
|
||||
std::string geraetekennung = esphome::format_hex_pretty(&tmp, 1);
|
||||
std::string hardware_revision = esphome::format_hex_pretty((uint8_t *) bytes + 2, 1);
|
||||
std::string software_index = esphome::format_hex_pretty((uint8_t *) bytes + 3, 1);
|
||||
publish_state("Device ID: " + geraetekennung + "|Hardware Revision: " + hardware_revision +
|
||||
"|Software Index: " + software_index);
|
||||
} break;
|
||||
default:
|
||||
publish_state(std::to_string(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "../optolink.h"
|
||||
#include "../datapoint_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace optolink {
|
||||
|
||||
enum TextSensorMode { MAP, RAW, DAY_SCHEDULE, DAY_SCHEDULE_SYNCHRONIZED, DEVICE_INFO, STATE_INFO };
|
||||
|
||||
class OptolinkTextSensor : public DatapointComponent,
|
||||
public esphome::text_sensor::TextSensor,
|
||||
public esphome::PollingComponent {
|
||||
public:
|
||||
OptolinkTextSensor(Optolink *optolink) : DatapointComponent(optolink) {}
|
||||
|
||||
void set_mode(TextSensorMode mode) { mode_ = mode; }
|
||||
void set_day_of_week(int dow) { dow_ = dow; }
|
||||
void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; }
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
|
||||
const StringRef &get_component_name() override { return get_name(); }
|
||||
void datapoint_value_changed(float state) override { publish_state(std::to_string(state)); };
|
||||
void datapoint_value_changed(uint8_t state) override { publish_state(std::to_string(state)); };
|
||||
void datapoint_value_changed(uint16_t state) override { publish_state(std::to_string(state)); };
|
||||
void datapoint_value_changed(uint32_t state) override;
|
||||
void datapoint_value_changed(uint8_t *state, size_t length) override;
|
||||
|
||||
private:
|
||||
TextSensorMode mode_ = MAP;
|
||||
int dow_ = 0;
|
||||
std::string entity_id_;
|
||||
};
|
||||
|
||||
} // namespace optolink
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
@ -66,7 +66,7 @@ lib_deps =
|
||||
rweather/Crypto@0.4.0 ; dsmr
|
||||
dudanov/MideaUART@1.1.8 ; midea
|
||||
tonia/HeatpumpIR@1.0.23 ; heatpumpir
|
||||
bertmelis/VitoWiFi@1.0.2 ; optolink
|
||||
bertmelis/VitoWiFi@1.1.2 ; optolink
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_ARDUINO
|
||||
|
@ -409,8 +409,6 @@ optolink:
|
||||
rx_pin: GPIO15
|
||||
tx_pin: GPIO16
|
||||
logger: true
|
||||
device_info: Device Info
|
||||
state: Component state
|
||||
|
||||
micronova:
|
||||
enable_rx_pin:
|
||||
@ -3909,11 +3907,6 @@ text_sensor:
|
||||
tag_name: OPTARIF
|
||||
name: optarif
|
||||
teleinfo_id: myteleinfo
|
||||
- platform: optolink
|
||||
name: Error history 1
|
||||
address: 0x7590
|
||||
bytes: 9
|
||||
raw: true
|
||||
- platform: ld2410
|
||||
version:
|
||||
name: "presenece sensor version"
|
||||
|
Loading…
Reference in New Issue
Block a user