mirror of
https://github.com/esphome/esphome.git
synced 2024-12-01 03:04:12 +01:00
Add support for Viessmann heating components via Optolink adapter
This commit is contained in:
parent
d642aeba0f
commit
112439b485
23 changed files with 1082 additions and 0 deletions
92
esphome/components/optolink/__init__.py
Normal file
92
esphome/components/optolink/__init__.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
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_ID,
|
||||||
|
CONF_LOGGER,
|
||||||
|
CONF_PROTOCOL,
|
||||||
|
CONF_RX_PIN,
|
||||||
|
CONF_STATE,
|
||||||
|
CONF_TX_PIN,
|
||||||
|
)
|
||||||
|
from esphome.core import CORE
|
||||||
|
|
||||||
|
DEPENDENCIES = []
|
||||||
|
AUTO_LOAD = ["sensor", "binary_sensor", "text_sensor", "number", "select", "switch"]
|
||||||
|
MULTI_CONF = False
|
||||||
|
CONF_DEVICE_INFO = "device_info"
|
||||||
|
|
||||||
|
optolink_ns = cg.esphome_ns.namespace("optolink")
|
||||||
|
OptolinkComponent = optolink_ns.class_("Optolink", cg.Component)
|
||||||
|
StateSensor = optolink_ns.class_(
|
||||||
|
"OptolinkStateSensor", ts.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
STATE_SENSOR_ID = "state_sensor_id"
|
||||||
|
DeviceInfoSensor = optolink_ns.class_(
|
||||||
|
"OptolinkDeviceInfoSensor", ts.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
DEVICE_INFO_SENSOR_ID = "device_info_sensor_id"
|
||||||
|
CONFIG_SCHEMA = 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_LOGGER, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_STATE): cv.string,
|
||||||
|
cv.Optional(CONF_DEVICE_INFO): cv.string,
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
if CORE.is_esp32:
|
||||||
|
CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
cg.add_library("VitoWiFi", "1.0.2")
|
||||||
|
|
||||||
|
cg.add_define(
|
||||||
|
"VITOWIFI_PROTOCOL", cg.RawExpression(f"Optolink{config[CONF_PROTOCOL]}")
|
||||||
|
)
|
||||||
|
|
||||||
|
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"]))
|
||||||
|
|
||||||
|
await cg.register_component(var, config)
|
31
esphome/components/optolink/binary_sensor.py
Normal file
31
esphome/components/optolink/binary_sensor.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
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 optolink_ns, OptolinkComponent
|
||||||
|
|
||||||
|
OptolinkBinarySensor = optolink_ns.class_(
|
||||||
|
"OptolinkBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
CONF_OPTOLINK_ID = "optolink_id"
|
||||||
|
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]))
|
59
esphome/components/optolink/number.py
Normal file
59
esphome/components/optolink/number.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
from esphome import core
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import number
|
||||||
|
from esphome.components.optolink.sensor import SENSOR_BASE_SCHEMA
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ADDRESS,
|
||||||
|
CONF_BYTES,
|
||||||
|
CONF_DIV_RATIO,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_MAX_VALUE,
|
||||||
|
CONF_MIN_VALUE,
|
||||||
|
CONF_STEP,
|
||||||
|
CONF_UPDATE_INTERVAL,
|
||||||
|
)
|
||||||
|
from . import OptolinkComponent, optolink_ns
|
||||||
|
|
||||||
|
OptolinkNumber = optolink_ns.class_(
|
||||||
|
"OptolinkNumber", number.Number, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_OPTOLINK_ID = "optolink_id"
|
||||||
|
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_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)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.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 number.register_number(
|
||||||
|
var,
|
||||||
|
config,
|
||||||
|
min_value=config[CONF_MIN_VALUE],
|
||||||
|
max_value=config[CONF_MAX_VALUE],
|
||||||
|
step=config[CONF_STEP],
|
||||||
|
)
|
||||||
|
|
||||||
|
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]))
|
72
esphome/components/optolink/optolink.cpp
Normal file
72
esphome/components/optolink/optolink.cpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/components/optolink/optolink.h"
|
||||||
|
#include <VitoWiFi.h>
|
||||||
|
|
||||||
|
VitoWiFiClass<VITOWIFI_PROTOCOL> VitoWiFi;
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace optolink {
|
||||||
|
|
||||||
|
void Optolink::_comm() {
|
||||||
|
ESP_LOGD("Optolink", "enter _comm");
|
||||||
|
VitoWiFi.readAll();
|
||||||
|
ESP_LOGD("Optolink", "exit _comm");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Optolink::setup() {
|
||||||
|
ESP_LOGI("Optolink", "setup");
|
||||||
|
|
||||||
|
if (logger_enabled_) {
|
||||||
|
VitoWiFi.setLogger(this);
|
||||||
|
VitoWiFi.enableLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(USE_ESP32)
|
||||||
|
VitoWiFi.setup(&Serial, rx_pin_, tx_pin_);
|
||||||
|
#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::set_error(const std::string &format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
char buffer[128];
|
||||||
|
size_t n = std::vsnprintf(buffer, sizeof(buffer), format.c_str(), args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
error_ = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Optolink::read_value(IDatapoint *datapoint) {
|
||||||
|
if (datapoint != nullptr) {
|
||||||
|
ESP_LOGI("Optolink", " read value of datapoint %s", datapoint->getName());
|
||||||
|
VitoWiFi.readDatapoint(*datapoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Optolink::write_value(IDatapoint *datapoint, DPValue dpValue) {
|
||||||
|
if (datapoint != nullptr) {
|
||||||
|
char buffer[64];
|
||||||
|
dpValue.getString(buffer, sizeof(buffer));
|
||||||
|
ESP_LOGI("Optolink", " write value %s of datapoint %s", buffer, datapoint->getName());
|
||||||
|
VitoWiFi.writeDatapoint(*datapoint, dpValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Optolink::write(uint8_t ch) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
ESP_LOGD("VitoWifi", "%s", log_buffer_.c_str());
|
||||||
|
log_buffer_.clear();
|
||||||
|
} else {
|
||||||
|
log_buffer_.push_back(ch);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
49
esphome/components/optolink/optolink.h
Executable file
49
esphome/components/optolink/optolink.h
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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>
|
||||||
|
|
||||||
|
using namespace esphome;
|
||||||
|
using namespace sensor;
|
||||||
|
using namespace binary_sensor;
|
||||||
|
using namespace text_sensor;
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace optolink {
|
||||||
|
|
||||||
|
// '00' ='WW' '01' ='RED' '02' ='NORM' '03' ='H+WW' '04' ='H+WW FS' '05' ='ABSCHALT'
|
||||||
|
|
||||||
|
//=====================================================================================================================
|
||||||
|
class Optolink : public esphome::Component, public Print {
|
||||||
|
protected:
|
||||||
|
std::string error_ = "OK";
|
||||||
|
std::string log_buffer_;
|
||||||
|
bool logger_enabled_ = false;
|
||||||
|
int rx_pin_;
|
||||||
|
int tx_pin_;
|
||||||
|
|
||||||
|
void _comm();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
size_t write(uint8_t ch) override;
|
||||||
|
|
||||||
|
void set_logger_enabled(bool logger_enabled) { logger_enabled_ = logger_enabled; }
|
||||||
|
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 dpValue);
|
||||||
|
void read_value(IDatapoint *datapoint);
|
||||||
|
|
||||||
|
void set_error(const std::string &format, ...);
|
||||||
|
std::string get_error() { return error_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
28
esphome/components/optolink/optolink_binary_sensor.h
Normal file
28
esphome/components/optolink/optolink_binary_sensor.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 std::string &get_sensor_name() override { return get_name(); }
|
||||||
|
void value_changed(float state) override { publish_state(state); };
|
||||||
|
};
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
43
esphome/components/optolink/optolink_device_info_sensor.h
Normal file
43
esphome/components/optolink/optolink_device_info_sensor.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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(std::string name, Optolink *optolink) {
|
||||||
|
optolink_ = optolink;
|
||||||
|
set_name(name);
|
||||||
|
set_update_interval(1800000);
|
||||||
|
set_entity_category(esphome::ENTITY_CATEGORY_DIAGNOSTIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setup() override {
|
||||||
|
datapoint_ = new Datapoint<conv4_1_UL>(get_name().c_str(), "optolink", 0x00f8, false);
|
||||||
|
datapoint_->setCallback([this](const IDatapoint &dp, DPValue dpValue) {
|
||||||
|
uint32_t value = dpValue.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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void update() override { optolink_->read_value(datapoint_); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Optolink *optolink_;
|
||||||
|
IDatapoint *datapoint_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
20
esphome/components/optolink/optolink_number.cpp
Normal file
20
esphome/components/optolink/optolink_number.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#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
|
26
esphome/components/optolink/optolink_number.h
Normal file
26
esphome/components/optolink/optolink_number.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 std::string &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
|
41
esphome/components/optolink/optolink_select.cpp
Normal file
41
esphome/components/optolink/optolink_select.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#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
|
39
esphome/components/optolink/optolink_select.h
Normal file
39
esphome/components/optolink/optolink_select.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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->begin(); it != mapping->end(); ++it) {
|
||||||
|
values.push_back(it->second);
|
||||||
|
}
|
||||||
|
traits.set_options(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setup() override { setup_datapoint(); }
|
||||||
|
void update() override { optolink_->read_value(datapoint_); }
|
||||||
|
|
||||||
|
const std::string &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
|
25
esphome/components/optolink/optolink_sensor.h
Normal file
25
esphome/components/optolink/optolink_sensor.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 std::string &get_sensor_name() override { return get_name(); }
|
||||||
|
void value_changed(float state) override { publish_state(state); };
|
||||||
|
};
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
163
esphome/components/optolink/optolink_sensor_base.cpp
Normal file
163
esphome/components/optolink/optolink_sensor_base.cpp
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
#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:
|
||||||
|
optolink_->write_value(datapoint_, DPValue((float) value));
|
||||||
|
break;
|
||||||
|
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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), dpValue.getU8());
|
||||||
|
value_changed(dpValue.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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dpValue.getFloat());
|
||||||
|
value_changed(dpValue.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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), dpValue.getU16());
|
||||||
|
value_changed(dpValue.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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dpValue.getFloat());
|
||||||
|
value_changed(dpValue.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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dpValue.getFloat());
|
||||||
|
value_changed(dpValue.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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %d", dp.getGroup(), dp.getName(), dpValue.getU32());
|
||||||
|
value_changed(dpValue.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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: %f", dp.getGroup(), dp.getName(), dpValue.getFloat());
|
||||||
|
value_changed(dpValue.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
|
47
esphome/components/optolink/optolink_sensor_base.h
Normal file
47
esphome/components/optolink/optolink_sensor_base.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/log.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 std::string &get_sensor_name() = 0;
|
||||||
|
virtual void value_changed(float state) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class conv2_100_F : public DPType {
|
||||||
|
public:
|
||||||
|
void encode(uint8_t *out, DPValue in);
|
||||||
|
DPValue decode(const uint8_t *in);
|
||||||
|
const size_t getLength() const { return 2; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
28
esphome/components/optolink/optolink_state_sensor.h
Normal file
28
esphome/components/optolink/optolink_state_sensor.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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);
|
||||||
|
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
|
20
esphome/components/optolink/optolink_switch.cpp
Normal file
20
esphome/components/optolink/optolink_switch.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#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
|
29
esphome/components/optolink/optolink_switch.h
Normal file
29
esphome/components/optolink/optolink_switch.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 std::string &get_sensor_name() override { return get_name(); }
|
||||||
|
void value_changed(float state) override { publish_state(state); };
|
||||||
|
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
25
esphome/components/optolink/optolink_text_sensor.cpp
Normal file
25
esphome/components/optolink/optolink_text_sensor.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#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 dpValue) {
|
||||||
|
ESP_LOGD("OptolinkSensorBase", "Datapoint %s - %s: <raw>", dp.getGroup(), dp.getName());
|
||||||
|
uint8_t buffer[bytes_ + 1];
|
||||||
|
dpValue.getRaw(buffer);
|
||||||
|
buffer[bytes_] = 0x0;
|
||||||
|
publish_state((char *) buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace optolink
|
||||||
|
} // namespace esphome
|
31
esphome/components/optolink/optolink_text_sensor.h
Normal file
31
esphome/components/optolink/optolink_text_sensor.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 std::string &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
|
80
esphome/components/optolink/select.py
Normal file
80
esphome/components/optolink/select.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
from esphome import core
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import select
|
||||||
|
from esphome.components.optolink.sensor import SENSOR_BASE_SCHEMA
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ADDRESS,
|
||||||
|
CONF_BYTES,
|
||||||
|
CONF_DIV_RATIO,
|
||||||
|
CONF_FROM,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_TO,
|
||||||
|
CONF_UPDATE_INTERVAL,
|
||||||
|
)
|
||||||
|
from . import OptolinkComponent, optolink_ns
|
||||||
|
|
||||||
|
OptolinkSelect = optolink_ns.class_(
|
||||||
|
"OptolinkSelect", select.Select, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_mapping(value):
|
||||||
|
if not isinstance(value, dict):
|
||||||
|
value = cv.string(value)
|
||||||
|
if "->" not in value:
|
||||||
|
raise cv.Invalid("Mapping must contain '->'")
|
||||||
|
a, b = value.split("->", 1)
|
||||||
|
value = {CONF_FROM: a.strip(), CONF_TO: b.strip()}
|
||||||
|
|
||||||
|
return cv.Schema(
|
||||||
|
{cv.Required(CONF_FROM): cv.string, cv.Required(CONF_TO): cv.string}
|
||||||
|
)(value)
|
||||||
|
|
||||||
|
|
||||||
|
CONF_OPTOLINK_ID = "optolink_id"
|
||||||
|
CONF_MAP = "map"
|
||||||
|
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)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.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 select.register_select(
|
||||||
|
var,
|
||||||
|
config,
|
||||||
|
options=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
map_type_ = cg.std_ns.class_("map").template(cg.std_string, cg.std_string)
|
||||||
|
map_var = cg.new_Pvariable(
|
||||||
|
config[MAP_ID],
|
||||||
|
map_type_([(item[CONF_FROM], item[CONF_TO]) for item in config[CONF_MAP]]),
|
||||||
|
)
|
||||||
|
|
||||||
|
cg.add(var.set_map(map_var))
|
||||||
|
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]))
|
51
esphome/components/optolink/sensor.py
Normal file
51
esphome/components/optolink/sensor.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
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]))
|
33
esphome/components/optolink/switch.py
Normal file
33
esphome/components/optolink/switch.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
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]))
|
50
esphome/components/optolink/text_sensor.py
Normal file
50
esphome/components/optolink/text_sensor.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from esphome import core
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components.optolink.sensor import SENSOR_BASE_SCHEMA
|
||||||
|
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
|
||||||
|
|
||||||
|
OptolinkTextSensor = optolink_ns.class_(
|
||||||
|
"OptolinkTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_OPTOLINK_ID = "optolink_id"
|
||||||
|
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]))
|
Loading…
Reference in a new issue