Compare commits

..

5 Commits

Author SHA1 Message Date
Otto winter
a81fc6e85d Add gai_strerror 2022-02-03 14:13:28 +01:00
Otto winter
c19d893e4e Lint 2022-02-03 09:23:48 +01:00
Otto winter
f4183778e3 simplify 2022-02-03 09:22:59 +01:00
Otto winter
11e8bd77e2 Lint 2022-02-03 09:16:46 +01:00
Otto winter
a39b2c4ac7 Add support for socket client mode and getaddrinfo 2022-02-02 22:38:27 +01:00
170 changed files with 1365 additions and 4096 deletions

View File

@@ -137,18 +137,18 @@ jobs:
--build-type "${{ matrix.build_type }}" \
manifest
deploy-ha-addon-repo:
deploy-hassio-repo:
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest
needs: [deploy-docker]
steps:
- env:
TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
curl \
-u ":$TOKEN" \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"

View File

@@ -89,12 +89,9 @@ esphome/components/json/* @OttoWinter
esphome/components/kalman_combinator/* @Cat-Ion
esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny
esphome/components/max7219digit/* @rspaargaren
esphome/components/max9611/* @mckaymatthew
esphome/components/mcp23008/* @jesserockz
esphome/components/mcp23017/* @jesserockz
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
@@ -111,12 +108,10 @@ esphome/components/mdns/* @esphome/core
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
esphome/components/mlx90393/* @functionpointer
esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras
esphome/components/modbus_controller/output/* @martgras
esphome/components/modbus_controller/select/* @martgras @stegm
esphome/components/modbus_controller/sensor/* @martgras
esphome/components/modbus_controller/switch/* @martgras
esphome/components/modbus_controller/text_sensor/* @martgras
@@ -143,8 +138,6 @@ esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/qr_code/* @wjtje
esphome/components/radon_eye_ble/* @jeffeb3
esphome/components/radon_eye_rd200/* @jeffeb3
esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet

View File

@@ -43,7 +43,7 @@ RUN \
# Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \
wheel==0.37.1 \
platformio==5.2.5 \
platformio==5.2.4 \
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_libraries_interval 1000000 \
@@ -102,7 +102,7 @@ RUN \
ARG BUILD_VERSION=dev
# Copy root filesystem
COPY docker/ha-addon-rootfs/ /
COPY docker/hassio-rootfs/ /
# First install requirements to leverage caching when requirements don't change
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /

View File

@@ -10,7 +10,7 @@ server {
ssl_certificate_key /ssl/%%keyfile%%;
# Clear Hass.io Ingress header
proxy_set_header X-HA-Ingress "";
proxy_set_header X-Hassio-Ingress "";
# Redirect http requests to https on the same port.
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/

View File

@@ -4,7 +4,7 @@ server {
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
# Clear Hass.io Ingress header
proxy_set_header X-HA-Ingress "";
proxy_set_header X-Hassio-Ingress "";
location / {
proxy_pass http://esphome;

View File

@@ -3,8 +3,8 @@ server {
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
# Set Home Assistant Ingress header
proxy_set_header X-HA-Ingress "YES";
# Set Hass.io Ingress header
proxy_set_header X-Hassio-Ingress "YES";
location / {
# Only allow from Hass.io supervisor

View File

@@ -4,7 +4,7 @@
# Runs the ESPHome dashboard
# ==============================================================================
export ESPHOME_IS_HA_ADDON=true
export ESPHOME_IS_HASSIO=true
if bashio::config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true
@@ -32,4 +32,4 @@ export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
bashio::log.info "Starting ESPHome dashboard..."
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio

View File

@@ -661,7 +661,7 @@ def parse_args(argv):
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
)
parser_dashboard.add_argument(
"--ha-addon", help=argparse.SUPPRESS, action="store_true"
"--hassio", help=argparse.SUPPRESS, action="store_true"
)
parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str

View File

@@ -63,7 +63,6 @@ from esphome.cpp_types import ( # noqa
uint32,
uint64,
int32,
int64,
const_char_ptr,
NAN,
esphome_ns,
@@ -82,5 +81,4 @@ from esphome.cpp_types import ( # noqa
InternalGPIOPin,
gpio_Flags,
EntityCategory,
Parented,
)

View File

@@ -76,8 +76,6 @@ async def to_code(config):
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])

View File

@@ -41,7 +41,6 @@ service APIConnection {
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
rpc lock_command (LockCommandRequest) returns (void) {}
}
@@ -529,7 +528,6 @@ message ListEntitiesSwitchResponse {
bool assumed_state = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
string device_class = 9;
}
message SwitchStateResponse {
option (id) = 26;
@@ -958,63 +956,6 @@ message SelectCommandRequest {
string state = 2;
}
// ==================== LOCK ====================
enum LockState {
LOCK_STATE_NONE = 0;
LOCK_STATE_LOCKED = 1;
LOCK_STATE_UNLOCKED = 2;
LOCK_STATE_JAMMED = 3;
LOCK_STATE_LOCKING = 4;
LOCK_STATE_UNLOCKING = 5;
}
enum LockCommand {
LOCK_UNLOCK = 0;
LOCK_LOCK = 1;
LOCK_OPEN = 2;
}
message ListEntitiesLockResponse {
option (id) = 58;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
bool assumed_state = 8;
bool supports_open = 9;
bool requires_code = 10;
# Not yet implemented:
string code_format = 11;
}
message LockStateResponse {
option (id) = 59;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
fixed32 key = 1;
LockState state = 2;
}
message LockCommandRequest {
option (id) = 60;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
fixed32 key = 1;
LockCommand command = 2;
# Not yet implemented:
bool has_code = 3;
string code = 4;
}
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
@@ -1039,4 +980,3 @@ message ButtonCommandRequest {
fixed32 key = 1;
}

View File

@@ -462,7 +462,6 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
msg.assumed_state = a_switch->assumed_state();
msg.disabled_by_default = a_switch->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
msg.device_class = a_switch->get_device_class();
return this->send_list_entities_switch_response(msg);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
@@ -701,49 +700,6 @@ void APIConnection::button_command(const ButtonCommandRequest &msg) {
}
#endif
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
if (!this->state_subscription_)
return false;
LockStateResponse resp{};
resp.key = a_lock->get_object_id_hash();
resp.state = static_cast<enums::LockState>(state);
return this->send_lock_state_response(resp);
}
bool APIConnection::send_lock_info(lock::Lock *a_lock) {
ListEntitiesLockResponse msg;
msg.key = a_lock->get_object_id_hash();
msg.object_id = a_lock->get_object_id();
msg.name = a_lock->get_name();
msg.unique_id = get_default_unique_id("lock", a_lock);
msg.icon = a_lock->get_icon();
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.disabled_by_default = a_lock->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
return this->send_list_entities_lock_response(msg);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
if (a_lock == nullptr)
return;
switch (msg.command) {
case enums::LOCK_UNLOCK:
a_lock->unlock();
break;
case enums::LOCK_LOCK:
a_lock->lock();
break;
case enums::LOCK_OPEN:
a_lock->open();
break;
}
}
#endif
#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)

View File

@@ -77,11 +77,6 @@ class APIConnection : public APIServerConnection {
#ifdef USE_BUTTON
bool send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
#endif
bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View File

@@ -278,36 +278,6 @@ template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LockState>(enums::LockState value) {
switch (value) {
case enums::LOCK_STATE_NONE:
return "LOCK_STATE_NONE";
case enums::LOCK_STATE_LOCKED:
return "LOCK_STATE_LOCKED";
case enums::LOCK_STATE_UNLOCKED:
return "LOCK_STATE_UNLOCKED";
case enums::LOCK_STATE_JAMMED:
return "LOCK_STATE_JAMMED";
case enums::LOCK_STATE_LOCKING:
return "LOCK_STATE_LOCKING";
case enums::LOCK_STATE_UNLOCKING:
return "LOCK_STATE_UNLOCKING";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LockCommand>(enums::LockCommand value) {
switch (value) {
case enums::LOCK_UNLOCK:
return "LOCK_UNLOCK";
case enums::LOCK_LOCK:
return "LOCK_LOCK";
case enums::LOCK_OPEN:
return "LOCK_OPEN";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@@ -2177,10 +2147,6 @@ bool ListEntitiesSwitchResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->icon = value.as_string();
return true;
}
case 9: {
this->device_class = value.as_string();
return true;
}
default:
return false;
}
@@ -2204,7 +2170,6 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->assumed_state);
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
buffer.encode_string(9, this->device_class);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
@@ -2242,10 +2207,6 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}");
}
#endif
@@ -4225,234 +4186,6 @@ void SelectCommandRequest::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 8: {
this->assumed_state = value.as_bool();
return true;
}
case 9: {
this->supports_open = value.as_bool();
return true;
}
case 10: {
this->requires_code = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesLockResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
case 11: {
this->code_format = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesLockResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_bool(8, this->assumed_state);
buffer.encode_bool(9, this->supports_open);
buffer.encode_bool(10, this->requires_code);
buffer.encode_string(11, this->code_format);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLockResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesLockResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" assumed_state: ");
out.append(YESNO(this->assumed_state));
out.append("\n");
out.append(" supports_open: ");
out.append(YESNO(this->supports_open));
out.append("\n");
out.append(" requires_code: ");
out.append(YESNO(this->requires_code));
out.append("\n");
out.append(" code_format: ");
out.append("'").append(this->code_format).append("'");
out.append("\n");
out.append("}");
}
#endif
bool LockStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->state = value.as_enum<enums::LockState>();
return true;
}
default:
return false;
}
}
bool LockStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void LockStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::LockState>(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LockStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("LockStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
out.append(proto_enum_to_string<enums::LockState>(this->state));
out.append("\n");
out.append("}");
}
#endif
bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->command = value.as_enum<enums::LockCommand>();
return true;
}
case 3: {
this->has_code = value.as_bool();
return true;
}
default:
return false;
}
}
bool LockCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 4: {
this->code = value.as_string();
return true;
}
default:
return false;
}
}
bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void LockCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::LockCommand>(2, this->command);
buffer.encode_bool(3, this->has_code);
buffer.encode_string(4, this->code);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LockCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("LockCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" command: ");
out.append(proto_enum_to_string<enums::LockCommand>(this->command));
out.append("\n");
out.append(" has_code: ");
out.append(YESNO(this->has_code));
out.append("\n");
out.append(" code: ");
out.append("'").append(this->code).append("'");
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
@@ -4515,7 +4248,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesButtonResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
char buffer[64];
out.append("ListEntitiesButtonResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -4565,7 +4298,7 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ButtonCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
char buffer[64];
out.append("ButtonCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);

View File

@@ -128,19 +128,6 @@ enum NumberMode : uint32_t {
NUMBER_MODE_BOX = 1,
NUMBER_MODE_SLIDER = 2,
};
enum LockState : uint32_t {
LOCK_STATE_NONE = 0,
LOCK_STATE_LOCKED = 1,
LOCK_STATE_UNLOCKED = 2,
LOCK_STATE_JAMMED = 3,
LOCK_STATE_LOCKING = 4,
LOCK_STATE_UNLOCKING = 5,
};
enum LockCommand : uint32_t {
LOCK_UNLOCK = 0,
LOCK_LOCK = 1,
LOCK_OPEN = 2,
};
} // namespace enums
@@ -580,7 +567,6 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
bool assumed_state{false};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -1063,58 +1049,6 @@ class SelectCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesLockResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
bool assumed_state{false};
bool supports_open{false};
bool requires_code{false};
std::string code_format{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class LockStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::LockState state{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class LockCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
enums::LockCommand command{};
bool has_code{false};
std::string code{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesButtonResponse : public ProtoMessage {
public:
std::string object_id{};

View File

@@ -282,24 +282,6 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
#endif
#ifdef USE_SELECT
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLockResponse>(msg, 58);
}
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LockStateResponse>(msg, 59);
}
#endif
#ifdef USE_LOCK
#endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -541,17 +523,6 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif
this->on_select_command_request(msg);
#endif
break;
}
case 60: {
#ifdef USE_LOCK
LockCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_lock_command_request: %s", msg.dump().c_str());
#endif
this->on_lock_command_request(msg);
#endif
break;
}
@@ -800,19 +771,6 @@ void APIServerConnection::on_button_command_request(const ButtonCommandRequest &
this->button_command(msg);
}
#endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->lock_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -130,15 +130,6 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_LOCK
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
#endif
#ifdef USE_LOCK
bool send_lock_state_response(const LockStateResponse &msg);
#endif
#ifdef USE_LOCK
virtual void on_lock_command_request(const LockCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
@@ -189,9 +180,6 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@@ -233,9 +221,6 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@@ -263,15 +263,6 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state)
}
#endif
#ifdef USE_LOCK
void APIServer::on_lock_update(lock::Lock *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_lock_state(obj, obj->state);
}
#endif
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; }
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -66,9 +66,6 @@ class APIServer : public Component, public Controller {
#endif
#ifdef USE_SELECT
void on_select_update(select::Select *obj, const std::string &state) override;
#endif
#ifdef USE_LOCK
void on_lock_update(lock::Lock *obj) override;
#endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }

View File

@@ -35,9 +35,6 @@ bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
return this->client_->send_text_sensor_info(text_sensor);
}
#endif
#ifdef USE_LOCK
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); }
#endif
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)

View File

@@ -48,9 +48,6 @@ class ListEntitiesIterator : public ComponentIterator {
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
bool on_end() override;

View File

@@ -47,9 +47,6 @@ bool InitialStateIterator::on_select(select::Select *select) {
return this->client_->send_select_state(select, select->state);
}
#endif
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
#endif
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {}

View File

@@ -45,9 +45,6 @@ class InitialStateIterator : public ComponentIterator {
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
protected:
APIConnection *client_;

View File

@@ -212,21 +212,6 @@ void ComponentIterator::advance() {
}
}
break;
#endif
#ifdef USE_LOCK
case IteratorState::LOCK:
if (this->at_ >= App.get_locks().size()) {
advance_platform = true;
} else {
auto *a_lock = App.get_locks()[this->at_];
if (a_lock->is_internal()) {
success = true;
break;
} else {
success = this->on_lock(a_lock);
}
}
break;
#endif
case IteratorState::MAX:
if (this->on_end()) {

View File

@@ -56,9 +56,6 @@ class ComponentIterator {
#endif
#ifdef USE_SELECT
virtual bool on_select(select::Select *select) = 0;
#endif
#ifdef USE_LOCK
virtual bool on_lock(lock::Lock *a_lock) = 0;
#endif
virtual bool on_end();
@@ -102,9 +99,6 @@ class ComponentIterator {
#endif
#ifdef USE_SELECT
SELECT,
#endif
#ifdef USE_LOCK
LOCK,
#endif
MAX,
} state_{IteratorState::NONE};

View File

@@ -395,19 +395,15 @@ BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
}
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) {
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
auto *client = this->service->client;
auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
write_type, ESP_GATT_AUTH_REQ_NONE);
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
}
}
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
}
} // namespace ble_client
} // namespace esphome

View File

@@ -60,7 +60,6 @@ class BLECharacteristic {
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid);
void write_value(uint8_t *new_val, int16_t new_val_size);
void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type);
BLEService *service;
};

View File

@@ -1,14 +1,13 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import ble_client, esp32_ble_tracker, output
from esphome.components import output, ble_client, esp32_ble_tracker
from esphome.const import CONF_ID, CONF_SERVICE_UUID
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_REQUIRE_RESPONSE = "require_response"
BLEBinaryOutput = ble_client_ns.class_(
"BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
@@ -20,7 +19,6 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_REQUIRE_RESPONSE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
@@ -63,7 +61,7 @@ def to_code(config):
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE]))
yield output.register_output(var, config)
yield ble_client.register_ble_node(var, config)
yield cg.register_component(var, config)

View File

@@ -63,11 +63,7 @@ void BLEBinaryOutput::write_state(bool state) {
uint8_t state_as_uint = (uint8_t) state;
ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint);
if (this->require_response_) {
chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_RSP);
} else {
chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_NO_RSP);
}
chr->write_value(&state_as_uint, sizeof(state_as_uint));
}
} // namespace ble_client

View File

@@ -25,11 +25,9 @@ class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, publi
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_require_response(bool response) { this->require_response_ = response; }
protected:
void write_state(bool state) override;
bool require_response_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
espbt::ClientState client_state_;

View File

@@ -1,6 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor, esp32_ble_tracker
from esphome.const import CONF_ID
DEPENDENCIES = ["esp32_ble_tracker"]
@@ -13,13 +14,18 @@ BLEScanner = ble_scanner_ns.class_(
)
CONFIG_SCHEMA = cv.All(
text_sensor.text_sensor_schema(klass=BLEScanner)
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BLEScanner),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)
await text_sensor.register_text_sensor(var, config)

View File

@@ -1,6 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_ICON
from . import BME680BSECComponent, CONF_BME680_BSEC_ID
DEPENDENCIES = ["bme680_bsec"]
@@ -13,8 +14,11 @@ TYPES = [CONF_IAQ_ACCURACY]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
cv.Optional(CONF_IAQ_ACCURACY): text_sensor.text_sensor_schema(
icon=ICON_ACCURACY
cv.Optional(CONF_IAQ_ACCURACY): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_ACCURACY): cv.icon,
}
),
}
)
@@ -23,7 +27,8 @@ CONFIG_SCHEMA = cv.Schema(
async def setup_conf(config, key, hub):
if key in config:
conf = config[key]
sens = await text_sensor.new_text_sensor(conf)
sens = cg.new_Pvariable(conf[CONF_ID])
await text_sensor.register_text_sensor(sens, conf)
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))

View File

@@ -14,14 +14,10 @@ CONF_BIT_RATE = "bit_rate"
CONF_ON_FRAME = "on_frame"
def validate_id(config):
if CONF_CAN_ID in config:
id_value = config[CONF_CAN_ID]
id_ext = config[CONF_USE_EXTENDED_ID]
if not id_ext:
if id_value > 0x7FF:
raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)")
return config
def validate_id(id_value, id_ext):
if not id_ext:
if id_value > 0x7FF:
raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)")
def validate_raw_data(value):
@@ -71,18 +67,23 @@ CANBUS_SCHEMA = cv.Schema(
cv.Optional(CONF_ON_FRAME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
},
validate_id,
cv.Optional(CONF_ON_FRAME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
}
),
}
),
},
}
).extend(cv.COMPONENT_SCHEMA)
CANBUS_SCHEMA.add_extra(validate_id)
async def setup_canbus_core_(var, config):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
await cg.register_component(var, config)
cg.add(var.set_can_id([config[CONF_CAN_ID]]))
cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]]))
@@ -91,6 +92,7 @@ async def setup_canbus_core_(var, config):
for conf in config.get(CONF_ON_FRAME, []):
can_id = conf[CONF_CAN_ID]
ext_id = conf[CONF_USE_EXTENDED_ID]
validate_id(can_id, ext_id)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
await cg.register_component(trigger, conf)
await automation.build_automation(
@@ -115,11 +117,11 @@ async def register_canbus(var, config):
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
},
validate_id,
key=CONF_DATA,
),
)
async def canbus_action_to_code(config, action_id, template_arg, args):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_CANBUS_ID])

View File

@@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor, text_sensor
from esphome.const import (
CONF_ICON,
CONF_ID,
ICON_RADIATOR,
ICON_RESTART,
@@ -46,8 +47,11 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(
icon=ICON_RESTART
cv.Optional(CONF_VERSION): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_RESTART): cv.icon,
}
),
cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
@@ -70,7 +74,8 @@ async def to_code(config):
cg.add(var.set_tvoc(sens))
if CONF_VERSION in config:
sens = await text_sensor.new_text_sensor(config[CONF_VERSION])
sens = cg.new_Pvariable(config[CONF_VERSION][CONF_ID])
await text_sensor.register_text_sensor(sens, config[CONF_VERSION])
cg.add(var.set_version(sens))
if CONF_BASELINE in config:

View File

@@ -7,7 +7,6 @@ from esphome.const import (
CONF_ID,
CONF_DEVICE_CLASS,
CONF_STATE,
CONF_ON_OPEN,
CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC,
CONF_POSITION_STATE_TOPIC,
@@ -75,6 +74,7 @@ CoverClosedTrigger = cover_ns.class_(
"CoverClosedTrigger", automation.Trigger.template()
)
CONF_ON_OPEN = "on_open"
CONF_ON_CLOSED = "on_closed"
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(

View File

@@ -11,7 +11,11 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(
text_sensor.text_sensor_schema()
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
)
),
}
)

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_STATUS
from esphome.const import CONF_ICON, CONF_ID, CONF_STATUS
from . import DalyBmsComponent, CONF_BMS_DALY_ID
ICON_CAR_BATTERY = "mdi:car-battery"
@@ -14,8 +14,11 @@ CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
cv.Optional(CONF_STATUS): text_sensor.text_sensor_schema(
icon=ICON_CAR_BATTERY
cv.Optional(CONF_STATUS): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_CAR_BATTERY): cv.icon,
}
),
}
).extend(cv.COMPONENT_SCHEMA)
@@ -25,7 +28,8 @@ CONFIG_SCHEMA = cv.All(
async def setup_conf(config, key, hub):
if key in config:
conf = config[key]
sens = await text_sensor.new_text_sensor(conf)
sens = cg.new_Pvariable(conf[CONF_ID])
await text_sensor.register_text_sensor(sens, conf)
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))

View File

@@ -1,5 +1,6 @@
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components import sensor, text_sensor
from esphome.const import (
CONF_ID,
CONF_DEVICE,
@@ -7,12 +8,16 @@ from esphome.const import (
CONF_FRAGMENTATION,
CONF_BLOCK,
CONF_LOOP_TIME,
UNIT_MILLISECOND,
UNIT_PERCENT,
UNIT_BYTES,
ICON_COUNTER,
ICON_TIMER,
)
CODEOWNERS = ["@OttoWinter"]
DEPENDENCIES = ["logger"]
CONF_DEBUG_ID = "debug_id"
debug_ns = cg.esphome_ns.namespace("debug")
DebugComponent = debug_ns.class_("DebugComponent", cg.PollingComponent)
@@ -20,20 +25,18 @@ DebugComponent = debug_ns.class_("DebugComponent", cg.PollingComponent)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(DebugComponent),
cv.Optional(CONF_DEVICE): cv.invalid(
"The 'device' option has been moved to the 'debug' text_sensor component"
cv.Optional(CONF_DEVICE): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{cv.GenerateID(): cv.declare_id(text_sensor.TextSensor)}
),
cv.Optional(CONF_FREE): cv.invalid(
"The 'free' option has been moved to the 'debug' sensor component"
cv.Optional(CONF_FREE): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0),
cv.Optional(CONF_BLOCK): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0),
cv.Optional(CONF_FRAGMENTATION): cv.All(
cv.only_on_esp8266,
cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)),
sensor.sensor_schema(UNIT_PERCENT, ICON_COUNTER, 1),
),
cv.Optional(CONF_BLOCK): cv.invalid(
"The 'block' option has been moved to the 'debug' sensor component"
),
cv.Optional(CONF_FRAGMENTATION): cv.invalid(
"The 'fragmentation' option has been moved to the 'debug' sensor component"
),
cv.Optional(CONF_LOOP_TIME): cv.invalid(
"The 'loop_time' option has been moved to the 'debug' sensor component"
cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(
UNIT_MILLISECOND, ICON_TIMER, 1
),
}
).extend(cv.polling_component_schema("60s"))
@@ -42,3 +45,24 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
if CONF_DEVICE in config:
sens = cg.new_Pvariable(config[CONF_DEVICE][CONF_ID])
await text_sensor.register_text_sensor(sens, config[CONF_DEVICE])
cg.add(var.set_device_info_sensor(sens))
if CONF_FREE in config:
sens = await sensor.new_sensor(config[CONF_FREE])
cg.add(var.set_free_sensor(sens))
if CONF_BLOCK in config:
sens = await sensor.new_sensor(config[CONF_BLOCK])
cg.add(var.set_block_sensor(sens))
if CONF_FRAGMENTATION in config:
sens = await sensor.new_sensor(config[CONF_FRAGMENTATION])
cg.add(var.set_fragmentation_sensor(sens))
if CONF_LOOP_TIME in config:
sens = await sensor.new_sensor(config[CONF_LOOP_TIME])
cg.add(var.set_loop_time_sensor(sens))

View File

@@ -47,16 +47,12 @@ void DebugComponent::dump_config() {
#endif
ESP_LOGCONFIG(TAG, "Debug component:");
#ifdef USE_TEXT_SENSOR
LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
#endif // USE_TEXT_SENSOR
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Free space on heap", this->free_sensor_);
LOG_SENSOR(" ", "Largest free heap block", this->block_sensor_);
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
LOG_SENSOR(" ", "Heap fragmentation", this->fragmentation_sensor_);
#endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#endif // USE_SENSOR
#endif
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
device_info += ESPHOME_VERSION;
@@ -272,13 +268,11 @@ void DebugComponent::dump_config() {
device_info += ESP.getResetInfo().c_str();
#endif
#ifdef USE_TEXT_SENSOR
if (this->device_info_ != nullptr) {
if (device_info.length() > 255)
device_info.resize(255);
this->device_info_->publish_state(device_info);
}
#endif // USE_TEXT_SENSOR
}
void DebugComponent::loop() {
@@ -290,7 +284,6 @@ void DebugComponent::loop() {
this->status_momentary_warning("heap", 1000);
}
#ifdef USE_SENSOR
// calculate loop time - from last call to this one
if (this->loop_time_sensor_ != nullptr) {
uint32_t now = millis();
@@ -298,11 +291,9 @@ void DebugComponent::loop() {
this->max_loop_time_ = std::max(this->max_loop_time_, loop_time);
this->last_loop_timetag_ = now;
}
#endif // USE_SENSOR
}
void DebugComponent::update() {
#ifdef USE_SENSOR
if (this->free_sensor_ != nullptr) {
this->free_sensor_->publish_state(get_free_heap());
}
@@ -316,7 +307,7 @@ void DebugComponent::update() {
#endif
}
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
if (this->fragmentation_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
@@ -327,7 +318,6 @@ void DebugComponent::update() {
this->loop_time_sensor_->publish_state(this->max_loop_time_);
this->max_loop_time_ = 0;
}
#endif // USE_SENSOR
}
float DebugComponent::get_setup_priority() const { return setup_priority::LATE; }

View File

@@ -1,16 +1,10 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
#include "esphome/core/helpers.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_TEXT_SENSOR
#include "esphome/components/text_sensor/text_sensor.h"
#endif
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace debug {
@@ -22,35 +16,27 @@ class DebugComponent : public PollingComponent {
float get_setup_priority() const override;
void dump_config() override;
#ifdef USE_TEXT_SENSOR
void set_device_info_sensor(text_sensor::TextSensor *device_info) { device_info_ = device_info; }
#endif // USE_TEXT_SENSOR
#ifdef USE_SENSOR
void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; }
void set_block_sensor(sensor::Sensor *block_sensor) { block_sensor_ = block_sensor; }
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; }
#endif
void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; }
#endif // USE_SENSOR
protected:
uint32_t free_heap_{};
#ifdef USE_SENSOR
uint32_t last_loop_timetag_{0};
uint32_t max_loop_time_{0};
text_sensor::TextSensor *device_info_{nullptr};
sensor::Sensor *free_sensor_{nullptr};
sensor::Sensor *block_sensor_{nullptr};
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
sensor::Sensor *fragmentation_sensor_{nullptr};
#endif
sensor::Sensor *loop_time_sensor_{nullptr};
#endif // USE_SENSOR
#ifdef USE_TEXT_SENSOR
text_sensor::TextSensor *device_info_{nullptr};
#endif // USE_TEXT_SENSOR
};
} // namespace debug

View File

@@ -1,70 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_FREE,
CONF_FRAGMENTATION,
CONF_BLOCK,
CONF_LOOP_TIME,
ENTITY_CATEGORY_DIAGNOSTIC,
UNIT_MILLISECOND,
UNIT_PERCENT,
UNIT_BYTES,
ICON_COUNTER,
ICON_TIMER,
)
from . import CONF_DEBUG_ID, DebugComponent
DEPENDENCIES = ["debug"]
CONFIG_SCHEMA = {
cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent),
cv.Optional(CONF_FREE): sensor.sensor_schema(
unit_of_measurement=UNIT_BYTES,
icon=ICON_COUNTER,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_BLOCK): sensor.sensor_schema(
unit_of_measurement=UNIT_BYTES,
icon=ICON_COUNTER,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_FRAGMENTATION): cv.All(
cv.only_on_esp8266,
cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)),
sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_COUNTER,
accuracy_decimals=1,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
),
cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(
unit_of_measurement=UNIT_MILLISECOND,
icon=ICON_TIMER,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
async def to_code(config):
debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if CONF_FREE in config:
sens = await sensor.new_sensor(config[CONF_FREE])
cg.add(debug_component.set_free_sensor(sens))
if CONF_BLOCK in config:
sens = await sensor.new_sensor(config[CONF_BLOCK])
cg.add(debug_component.set_block_sensor(sens))
if CONF_FRAGMENTATION in config:
sens = await sensor.new_sensor(config[CONF_FRAGMENTATION])
cg.add(debug_component.set_fragmentation_sensor(sens))
if CONF_LOOP_TIME in config:
sens = await sensor.new_sensor(config[CONF_LOOP_TIME])
cg.add(debug_component.set_loop_time_sensor(sens))

View File

@@ -1,26 +0,0 @@
from esphome.components import text_sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_DEVICE, ENTITY_CATEGORY_DIAGNOSTIC
from . import CONF_DEBUG_ID, DebugComponent
DEPENDENCIES = ["debug"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent),
cv.Optional(CONF_DEVICE): text_sensor.text_sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
}
)
async def to_code(config):
debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if CONF_DEVICE in config:
sens = await text_sensor.new_text_sensor(config[CONF_DEVICE])
cg.add(debug_component.set_device_info_sensor(sens))

View File

@@ -37,10 +37,12 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
ICON_BLUETOOTH,
ICON_BLUR,
ICON_EMPTY,
ICON_THERMOMETER,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_CELSIUS,
UNIT_EMPTY,
UNIT_PERCENT,
UNIT_WATT_HOURS,
)
@@ -337,7 +339,7 @@ CONFIG_SCHEMA = cv.Schema(
},
],
): [
sensor.sensor_schema(accuracy_decimals=0)
sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 0)
.extend(cv.polling_component_schema("60s"))
.extend(
{
@@ -376,8 +378,12 @@ CONFIG_SCHEMA = cv.Schema(
},
],
): [
text_sensor.text_sensor_schema(klass=DemoTextSensor).extend(
text_sensor.TEXT_SENSOR_SCHEMA.extend(
cv.polling_component_schema("60s")
).extend(
{
cv.GenerateID(): cv.declare_id(DemoTextSensor),
}
)
],
}
@@ -437,5 +443,6 @@ async def to_code(config):
await switch.register_switch(var, conf)
for conf in config[CONF_TEXT_SENSORS]:
var = await text_sensor.new_text_sensor(conf)
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await text_sensor.register_text_sensor(var, conf)

View File

@@ -1,7 +1,9 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import (
CONF_ID,
)
from . import Dsmr, CONF_DSMR_ID
AUTO_LOAD = ["dsmr"]
@@ -9,19 +11,71 @@ AUTO_LOAD = ["dsmr"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr),
cv.Optional("identification"): text_sensor.text_sensor_schema(),
cv.Optional("p1_version"): text_sensor.text_sensor_schema(),
cv.Optional("p1_version_be"): text_sensor.text_sensor_schema(),
cv.Optional("timestamp"): text_sensor.text_sensor_schema(),
cv.Optional("electricity_tariff"): text_sensor.text_sensor_schema(),
cv.Optional("electricity_failure_log"): text_sensor.text_sensor_schema(),
cv.Optional("message_short"): text_sensor.text_sensor_schema(),
cv.Optional("message_long"): text_sensor.text_sensor_schema(),
cv.Optional("gas_equipment_id"): text_sensor.text_sensor_schema(),
cv.Optional("thermal_equipment_id"): text_sensor.text_sensor_schema(),
cv.Optional("water_equipment_id"): text_sensor.text_sensor_schema(),
cv.Optional("sub_equipment_id"): text_sensor.text_sensor_schema(),
cv.Optional("gas_delivered_text"): text_sensor.text_sensor_schema(),
cv.Optional("identification"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("p1_version"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("p1_version_be"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("timestamp"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("electricity_tariff"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("electricity_failure_log"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("message_short"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("message_long"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("gas_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("thermal_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("water_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("sub_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("gas_delivered_text"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
@@ -35,7 +89,8 @@ async def to_code(config):
continue
id = conf.get("id")
if id and id.type == text_sensor.TextSensor:
var = await text_sensor.new_text_sensor(conf)
var = cg.new_Pvariable(conf[CONF_ID])
await text_sensor.register_text_sensor(var, conf)
cg.add(getattr(hub, f"set_{key}")(var))
text_sensors.append(f"F({key})")

View File

@@ -1,16 +1,13 @@
# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664
import esptool
from SCons.Script import ARGUMENTS
# pylint: disable=E0602
Import("env") # noqa
def esp32_create_combined_bin(source, target, env):
verbose = bool(int(ARGUMENTS.get("PIOVERBOSE", "0")))
if verbose:
print("Generating combined binary for serial flashing")
print("Generating combined binary for serial flashing")
app_offset = 0x10000
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin")
@@ -27,21 +24,18 @@ def esp32_create_combined_bin(source, target, env):
"--flash_size",
flash_size,
]
if verbose:
print(" Offset | File")
print(" Offset | File")
for section in sections:
sect_adr, sect_file = section.split(" ", 1)
if verbose:
print(f" - {sect_adr} | {sect_file}")
print(f" - {sect_adr} | {sect_file}")
cmd += [sect_adr, sect_file]
print(f" - {hex(app_offset)} | {firmware_name}")
cmd += [hex(app_offset), firmware_name]
if verbose:
print(f" - {hex(app_offset)} | {firmware_name}")
print()
print(f"Using esptool.py arguments: {' '.join(cmd)}")
print()
print()
print(f"Using esptool.py arguments: {' '.join(cmd)}")
print()
esptool.main(cmd)

View File

@@ -42,11 +42,6 @@ void BLEAdvertising::remove_service_uuid(ESPBTUUID uuid) {
this->advertising_uuids_.end());
}
void BLEAdvertising::set_manufacturer_data(uint8_t *data, uint16_t size) {
this->advertising_data_.p_manufacturer_data = data;
this->advertising_data_.manufacturer_len = size;
}
void BLEAdvertising::start() {
int num_services = this->advertising_uuids_.size();
if (num_services == 0) {

View File

@@ -20,7 +20,6 @@ class BLEAdvertising {
void remove_service_uuid(ESPBTUUID uuid);
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
void set_manufacturer_data(uint8_t *data, uint16_t size);
void start();
void stop();

View File

@@ -56,7 +56,7 @@ async def to_code(config):
cg.add(ble_server.register_service_component(var))
cg.add_define("USE_IMPROV")
cg.add_library("esphome/Improv", "1.2.1")
cg.add_library("esphome/Improv", "1.2.0")
cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION]))
cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION]))

View File

@@ -166,7 +166,6 @@ async def to_code(config):
cg.add_platformio_option("framework", "arduino")
cg.add_build_flag("-DUSE_ARDUINO")
cg.add_build_flag("-DUSE_ESP8266_FRAMEWORK_ARDUINO")
cg.add_build_flag("-Wno-nonnull-compare")
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
cg.add_platformio_option(
"platform_packages",

View File

@@ -49,12 +49,7 @@ CONFIG_SCHEMA = cv.All(
}
),
_validate,
cv.require_framework_version(
esp8266_arduino=cv.Version(2, 7, 4),
esp32_arduino=cv.Version(99, 0, 0),
max_version=True,
extra_message="Please see note on documentation for FastLED",
),
cv.only_with_arduino,
)

View File

@@ -33,12 +33,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_DATA_RATE): cv.frequency,
}
),
cv.require_framework_version(
esp8266_arduino=cv.Version(2, 7, 4),
esp32_arduino=cv.Version(99, 0, 0),
max_version=True,
extra_message="Please see note on documentation for FastLED",
),
cv.only_with_arduino,
)

View File

@@ -1,8 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID
from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_ID
from .. import homeassistant_ns
DEPENDENCIES = ["api"]
@@ -11,7 +10,7 @@ HomeassistantTextSensor = homeassistant_ns.class_(
"HomeassistantTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = text_sensor.text_sensor_schema().extend(
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(HomeassistantTextSensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
@@ -21,8 +20,9 @@ CONFIG_SCHEMA = text_sensor.text_sensor_schema().extend(
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
if CONF_ATTRIBUTE in config:

View File

@@ -30,4 +30,4 @@ FINAL_VALIDATE_SCHEMA = validate_logger_baud_rate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add_library("esphome/Improv", "1.2.1")
cg.add_library("esphome/Improv", "1.2.0")

View File

@@ -47,7 +47,6 @@ InkplateModel = inkplate6_ns.enum("InkplateModel")
MODELS = {
"inkplate_6": InkplateModel.INKPLATE_6,
"inkplate_10": InkplateModel.INKPLATE_10,
"inkplate_6_plus": InkplateModel.INKPLATE_6_PLUS,
}
CONFIG_SCHEMA = cv.All(

View File

@@ -13,11 +13,6 @@ namespace inkplate6 {
static const char *const TAG = "inkplate";
void Inkplate6::setup() {
for (uint32_t i = 0; i < 256; i++) {
this->pin_lut_[i] = ((i & 0b00000011) << 4) | (((i & 0b00001100) >> 2) << 18) | (((i & 0b00010000) >> 4) << 23) |
(((i & 0b11100000) >> 5) << 25);
}
this->initialize_();
this->vcom_pin_->setup();
@@ -43,21 +38,11 @@ void Inkplate6::setup() {
this->display_data_6_pin_->setup();
this->display_data_7_pin_->setup();
this->wakeup_pin_->digital_write(true);
delay(1);
this->write_bytes(0x09, {
0b00011011, // Power up seq.
0b00000000, // Power up delay (3mS per rail)
0b00011011, // Power down seq.
0b00000000, // Power down delay (6mS per rail)
});
delay(1);
this->wakeup_pin_->digital_write(false);
this->clean();
this->display();
}
void Inkplate6::initialize_() {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE);
uint32_t buffer_size = this->get_buffer_length_();
if (buffer_size == 0)
return;
@@ -68,10 +53,6 @@ void Inkplate6::initialize_() {
allocator.deallocate(this->partial_buffer_2_, buffer_size * 2);
if (this->buffer_ != nullptr)
allocator.deallocate(this->buffer_, buffer_size);
if (this->glut_ != nullptr)
allocator32.deallocate(this->glut_, 256 * (this->model_ == INKPLATE_6_PLUS ? 9 : 8));
if (this->glut2_ != nullptr)
allocator32.deallocate(this->glut2_, 256 * (this->model_ == INKPLATE_6_PLUS ? 9 : 8));
this->buffer_ = allocator.allocate(buffer_size);
if (this->buffer_ == nullptr) {
@@ -79,34 +60,7 @@ void Inkplate6::initialize_() {
this->mark_failed();
return;
}
if (this->greyscale_) {
uint8_t glut_size = (this->model_ == INKPLATE_6_PLUS ? 9 : 8);
this->glut_ = allocator32.allocate(256 * glut_size);
if (this->glut_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate glut!");
this->mark_failed();
return;
}
this->glut2_ = allocator32.allocate(256 * glut_size);
if (this->glut2_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate glut2!");
this->mark_failed();
return;
}
for (int i = 0; i < glut_size; i++) {
for (uint32_t j = 0; j < 256; j++) {
uint8_t z = (waveform3Bit[j & 0x07][i] << 2) | (waveform3Bit[(j >> 4) & 0x07][i]);
this->glut_[i * 256 + j] = ((z & 0b00000011) << 4) | (((z & 0b00001100) >> 2) << 18) |
(((z & 0b00010000) >> 4) << 23) | (((z & 0b11100000) >> 5) << 25);
z = ((waveform3Bit[j & 0x07][i] << 2) | (waveform3Bit[(j >> 4) & 0x07][i])) << 4;
this->glut2_[i * 256 + j] = ((z & 0b00000011) << 4) | (((z & 0b00001100) >> 2) << 18) |
(((z & 0b00010000) >> 4) << 23) | (((z & 0b11100000) >> 5) << 25);
}
}
} else {
if (!this->greyscale_) {
this->partial_buffer_ = allocator.allocate(buffer_size);
if (this->partial_buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate partial buffer for display!");
@@ -119,16 +73,13 @@ void Inkplate6::initialize_() {
this->mark_failed();
return;
}
memset(this->partial_buffer_, 0, buffer_size);
memset(this->partial_buffer_2_, 0, buffer_size * 2);
}
memset(this->buffer_, 0, buffer_size);
}
float Inkplate6::get_setup_priority() const { return setup_priority::PROCESSOR; }
size_t Inkplate6::get_buffer_length_() {
if (this->greyscale_) {
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 2u;
@@ -136,7 +87,6 @@ size_t Inkplate6::get_buffer_length_() {
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
}
}
void Inkplate6::update() {
this->do_update_();
@@ -146,7 +96,6 @@ void Inkplate6::update() {
this->display();
}
void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
return;
@@ -172,7 +121,6 @@ void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) {
this->partial_buffer_[pos] = (~pixelMaskLUT[x_sub] & current) | (color.is_on() ? 0 : pixelMaskLUT[x_sub]);
}
}
void Inkplate6::dump_config() {
LOG_DISPLAY("", "Inkplate", this);
ESP_LOGCONFIG(TAG, " Greyscale: %s", YESNO(this->greyscale_));
@@ -202,51 +150,44 @@ void Inkplate6::dump_config() {
LOG_UPDATE_INTERVAL(this);
}
void Inkplate6::eink_off_() {
ESP_LOGV(TAG, "Eink off called");
if (!panel_on_)
if (panel_on_ == 0)
return;
panel_on_ = false;
this->oe_pin_->digital_write(false);
panel_on_ = 0;
this->gmod_pin_->digital_write(false);
this->oe_pin_->digital_write(false);
GPIO.out &= ~(get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()) | (1 << this->le_pin_->get_pin()));
GPIO.out &= ~(this->get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()) | (1 << this->le_pin_->get_pin()));
this->ckv_pin_->digital_write(false);
this->sph_pin_->digital_write(false);
this->spv_pin_->digital_write(false);
this->powerup_pin_->digital_write(false);
this->wakeup_pin_->digital_write(false);
this->vcom_pin_->digital_write(false);
this->write_byte(0x01, 0x6F); // Put TPS65186 into standby mode
delay(100); // NOLINT
this->write_byte(0x01, 0x4f); // Disable 3V3 to the panel
if (this->model_ != INKPLATE_6_PLUS)
this->wakeup_pin_->digital_write(false);
pins_z_state_();
}
void Inkplate6::eink_on_() {
ESP_LOGV(TAG, "Eink on called");
if (panel_on_)
if (panel_on_ == 1)
return;
this->panel_on_ = true;
this->pins_as_outputs_();
panel_on_ = 1;
pins_as_outputs_();
this->wakeup_pin_->digital_write(true);
this->powerup_pin_->digital_write(true);
this->vcom_pin_->digital_write(true);
this->write_byte(0x01, 0x3F);
delay(40);
this->write_byte(0x0D, 0x80);
delay(2);
this->write_byte(0x01, 0b00101111); // Enable all rails
delay(1);
this->write_byte(0x01, 0b10101111); // Switch TPS65186 into active mode
this->read_register(0x00, nullptr, 0);
this->le_pin_->digital_write(false);
this->oe_pin_->digital_write(false);
@@ -255,33 +196,8 @@ void Inkplate6::eink_on_() {
this->gmod_pin_->digital_write(true);
this->spv_pin_->digital_write(true);
this->ckv_pin_->digital_write(false);
this->oe_pin_->digital_write(false);
uint32_t timer = millis();
do {
delay(1);
} while (!this->read_power_status_() && ((millis() - timer) < 250));
if ((millis() - timer) >= 250) {
ESP_LOGW(TAG, "Power supply not detected");
this->wakeup_pin_->digital_write(false);
this->vcom_pin_->digital_write(false);
this->powerup_pin_->digital_write(false);
this->panel_on_ = false;
return;
}
this->oe_pin_->digital_write(true);
}
bool Inkplate6::read_power_status_() {
uint8_t data;
auto err = this->read_register(0x0F, &data, 1);
if (err == i2c::ERROR_OK) {
return data == 0b11111010;
}
return false;
}
void Inkplate6::fill(Color color) {
ESP_LOGV(TAG, "Fill called");
uint32_t start_time = millis();
@@ -296,7 +212,6 @@ void Inkplate6::fill(Color color) {
ESP_LOGV(TAG, "Fill finished (%ums)", millis() - start_time);
}
void Inkplate6::display() {
ESP_LOGV(TAG, "Display called");
uint32_t start_time = millis();
@@ -312,185 +227,201 @@ void Inkplate6::display() {
}
ESP_LOGV(TAG, "Display finished (full) (%ums)", millis() - start_time);
}
void Inkplate6::display1b_() {
ESP_LOGV(TAG, "Display1b called");
uint32_t start_time = millis();
memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_());
uint32_t send;
uint8_t data;
uint8_t buffer_value;
const uint8_t *buffer_ptr;
eink_on_();
if (this->model_ == INKPLATE_6_PLUS) {
clean_fast_(0, 1);
clean_fast_(1, 15);
clean_fast_(2, 1);
clean_fast_(0, 5);
clean_fast_(2, 1);
clean_fast_(1, 15);
} else {
clean_fast_(0, 1);
clean_fast_(1, 21);
clean_fast_(2, 1);
clean_fast_(0, 12);
clean_fast_(2, 1);
clean_fast_(1, 21);
clean_fast_(2, 1);
clean_fast_(0, 12);
}
clean_fast_(0, 1);
clean_fast_(1, 5);
clean_fast_(2, 1);
clean_fast_(0, 5);
clean_fast_(2, 1);
clean_fast_(1, 12);
clean_fast_(2, 1);
clean_fast_(0, 11);
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t data_mask = this->get_data_pin_mask_();
ESP_LOGV(TAG, "Display1b start loops (%ums)", millis() - start_time);
for (int k = 0; k < 4; k++) {
for (int k = 0; k < 3; k++) {
buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
vscan_start_();
for (int i = 0, im = this->get_height_internal(); i < im; i++) {
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = *(buffer_ptr--);
data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value >> 4) & 0x0F] : LUTB[(buffer_value >> 4) & 0x0F];
hscan_start_(this->pin_lut_[data]);
data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value) & 0x0F] : LUTB[buffer_value & 0x0F];
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
data = LUTB[(buffer_value >> 4) & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25);
hscan_start_(send);
data = LUTB[buffer_value & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
buffer_value = *(buffer_ptr--);
data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value >> 4) & 0x0F] : LUTB[(buffer_value >> 4) & 0x0F];
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value) & 0x0F] : LUTB[buffer_value & 0x0F];
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
data = LUTB[(buffer_value >> 4) & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
data = LUTB[buffer_value & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = clock;
GPIO.out_w1tc = data_mask | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
}
ESP_LOGV(TAG, "Display1b first loop x %d (%ums)", 4, millis() - start_time);
ESP_LOGV(TAG, "Display1b first loop x %d (%ums)", 3, millis() - start_time);
buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
vscan_start_();
for (int i = 0, im = this->get_height_internal(); i < im; i++) {
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = *(buffer_ptr--);
data = this->model_ == INKPLATE_6_PLUS ? LUTB[(buffer_value >> 4) & 0x0F] : LUT2[(buffer_value >> 4) & 0x0F];
hscan_start_(this->pin_lut_[data] | clock);
data = this->model_ == INKPLATE_6_PLUS ? LUTB[buffer_value & 0x0F] : LUT2[buffer_value & 0x0F];
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
data = LUT2[(buffer_value >> 4) & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25);
hscan_start_(send);
data = LUT2[buffer_value & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
buffer_value = *(buffer_ptr--);
data = this->model_ == INKPLATE_6_PLUS ? LUTB[(buffer_value >> 4) & 0x0F] : LUT2[(buffer_value >> 4) & 0x0F];
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
data = this->model_ == INKPLATE_6_PLUS ? LUTB[buffer_value & 0x0F] : LUT2[buffer_value & 0x0F];
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
data = LUT2[(buffer_value >> 4) & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
data = LUT2[buffer_value & 0x0F];
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = clock;
GPIO.out_w1tc = data_mask | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Display1b second loop (%ums)", millis() - start_time);
if (this->model_ == INKPLATE_6_PLUS) {
clean_fast_(2, 2);
clean_fast_(3, 1);
} else {
uint32_t send = this->pin_lut_[0];
vscan_start_();
for (int i = 0, im = this->get_height_internal(); i < im; i++) {
hscan_start_(send);
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = data_mask | clock;
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = data_mask | clock;
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = data_mask | clock;
}
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = data_mask | clock;
vscan_end_();
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
data = 0b00000000;
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25);
hscan_start_(send);
send |= clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Display1b third loop (%ums)", millis() - start_time);
GPIO.out_w1ts = clock;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
ESP_LOGV(TAG, "Display1b third loop (%ums)", millis() - start_time);
vscan_start_();
eink_off_();
this->block_partial_ = false;
this->partial_updates_ = 0;
ESP_LOGV(TAG, "Display1b finished (%ums)", millis() - start_time);
}
void Inkplate6::display3b_() {
ESP_LOGV(TAG, "Display3b called");
uint32_t start_time = millis();
eink_on_();
if (this->model_ == INKPLATE_6_PLUS) {
clean_fast_(0, 1);
clean_fast_(1, 15);
clean_fast_(2, 1);
clean_fast_(0, 5);
clean_fast_(2, 1);
clean_fast_(1, 15);
} else {
clean_fast_(0, 1);
clean_fast_(1, 21);
clean_fast_(2, 1);
clean_fast_(0, 12);
clean_fast_(2, 1);
clean_fast_(1, 21);
clean_fast_(2, 1);
clean_fast_(0, 12);
}
clean_fast_(0, 1);
clean_fast_(1, 12);
clean_fast_(2, 1);
clean_fast_(0, 11);
clean_fast_(2, 1);
clean_fast_(1, 12);
clean_fast_(2, 1);
clean_fast_(0, 11);
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t data_mask = this->get_data_pin_mask_();
uint32_t pos;
uint32_t data;
uint8_t glut_size = this->model_ == INKPLATE_6_PLUS ? 9 : 8;
for (int k = 0; k < glut_size; k++) {
pos = this->get_buffer_length_();
for (int k = 0; k < 8; k++) {
const uint8_t *buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
uint32_t send;
uint8_t pix1;
uint8_t pix2;
uint8_t pix3;
uint8_t pix4;
uint8_t pixel;
uint8_t pixel2;
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
data = this->glut2_[k * 256 + this->buffer_[--pos]];
data |= this->glut_[k * 256 + this->buffer_[--pos]];
hscan_start_(data);
data = this->glut2_[k * 256 + this->buffer_[--pos]];
data |= this->glut_[k * 256 + this->buffer_[--pos]];
GPIO.out_w1ts = data | clock;
GPIO.out_w1tc = data_mask | clock;
pix1 = (*buffer_ptr--);
pix2 = (*buffer_ptr--);
pix3 = (*buffer_ptr--);
pix4 = (*buffer_ptr--);
pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
data = this->glut2_[k * 256 + this->buffer_[--pos]];
data |= this->glut_[k * 256 + this->buffer_[--pos]];
GPIO.out_w1ts = data | clock;
GPIO.out_w1tc = data_mask | clock;
data = this->glut2_[k * 256 + this->buffer_[--pos]];
data |= this->glut_[k * 256 + this->buffer_[--pos]];
GPIO.out_w1ts = data | clock;
GPIO.out_w1tc = data_mask | clock;
send = ((pixel & 0b00000011) << 4) | (((pixel & 0b00001100) >> 2) << 18) | (((pixel & 0b00010000) >> 4) << 23) |
(((pixel & 0b11100000) >> 5) << 25);
hscan_start_(send);
send = ((pixel2 & 0b00000011) << 4) | (((pixel2 & 0b00001100) >> 2) << 18) |
(((pixel2 & 0b00010000) >> 4) << 23) | (((pixel2 & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
pix1 = (*buffer_ptr--);
pix2 = (*buffer_ptr--);
pix3 = (*buffer_ptr--);
pix4 = (*buffer_ptr--);
pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
send = ((pixel & 0b00000011) << 4) | (((pixel & 0b00001100) >> 2) << 18) | (((pixel & 0b00010000) >> 4) << 23) |
(((pixel & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
send = ((pixel2 & 0b00000011) << 4) | (((pixel2 & 0b00001100) >> 2) << 18) |
(((pixel2 & 0b00010000) >> 4) << 23) | (((pixel2 & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = clock;
GPIO.out_w1tc = data_mask | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
}
clean_fast_(2, 1);
clean_fast_(3, 1);
vscan_start_();
eink_off_();
ESP_LOGV(TAG, "Display3b finished (%ums)", millis() - start_time);
}
bool Inkplate6::partial_update_() {
ESP_LOGV(TAG, "Partial update called");
uint32_t start_time = millis();
@@ -501,15 +432,16 @@ bool Inkplate6::partial_update_() {
this->partial_updates_++;
uint32_t pos = this->get_buffer_length_() - 1;
uint16_t pos = this->get_buffer_length_() - 1;
uint32_t send;
uint8_t data;
uint8_t diffw, diffb;
uint32_t n = (this->get_buffer_length_() * 2) - 1;
for (int i = 0, im = this->get_height_internal(); i < im; i++) {
for (int j = 0, jm = (this->get_width_internal() / 8); j < jm; j++) {
diffw = this->buffer_[pos] & ~(this->partial_buffer_[pos]);
diffb = ~(this->buffer_[pos]) & this->partial_buffer_[pos];
diffw = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & ~(this->partial_buffer_[pos]);
diffb = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & this->partial_buffer_[pos];
pos--;
this->partial_buffer_2_[n--] = LUTW[diffw >> 4] & LUTB[diffb >> 4];
this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F];
@@ -519,20 +451,23 @@ bool Inkplate6::partial_update_() {
eink_on_();
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t data_mask = this->get_data_pin_mask_();
for (int k = 0; k < 5; k++) {
vscan_start_();
const uint8_t *data_ptr = &this->partial_buffer_2_[(this->get_buffer_length_() * 2) - 1];
for (int i = 0; i < this->get_height_internal(); i++) {
data = *(data_ptr--);
hscan_start_(this->pin_lut_[data]);
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25);
hscan_start_(send);
for (int j = 0, jm = (this->get_width_internal() / 4) - 1; j < jm; j++) {
data = *(data_ptr--);
GPIO.out_w1ts = this->pin_lut_[data] | clock;
GPIO.out_w1tc = data_mask | clock;
send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = clock;
GPIO.out_w1tc = data_mask | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
@@ -547,7 +482,6 @@ bool Inkplate6::partial_update_() {
ESP_LOGV(TAG, "Partial update finished (%ums)", millis() - start_time);
return true;
}
void Inkplate6::vscan_start_() {
this->ckv_pin_->digital_write(true);
delayMicroseconds(7);
@@ -571,23 +505,30 @@ void Inkplate6::vscan_start_() {
delayMicroseconds(0);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::hscan_start_(uint32_t d) {
uint8_t clock = (1 << this->cl_pin_->get_pin());
this->sph_pin_->digital_write(false);
GPIO.out_w1ts = d | clock;
GPIO.out_w1tc = this->get_data_pin_mask_() | clock;
this->sph_pin_->digital_write(true);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::vscan_end_() {
void Inkplate6::vscan_write_() {
this->ckv_pin_->digital_write(false);
this->le_pin_->digital_write(true);
this->le_pin_->digital_write(false);
delayMicroseconds(0);
this->sph_pin_->digital_write(false);
this->cl_pin_->digital_write(true);
this->cl_pin_->digital_write(false);
this->sph_pin_->digital_write(true);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::hscan_start_(uint32_t d) {
this->sph_pin_->digital_write(false);
GPIO.out_w1ts = (d) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
this->sph_pin_->digital_write(true);
}
void Inkplate6::vscan_end_() {
this->ckv_pin_->digital_write(false);
this->le_pin_->digital_write(true);
this->le_pin_->digital_write(false);
delayMicroseconds(1);
this->ckv_pin_->digital_write(true);
}
void Inkplate6::clean() {
ESP_LOGV(TAG, "Clean called");
uint32_t start_time = millis();
@@ -601,7 +542,6 @@ void Inkplate6::clean() {
clean_fast_(1, 10); // White to White
ESP_LOGV(TAG, "Clean finished (%ums)", millis() - start_time);
}
void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep);
uint32_t start_time = millis();
@@ -628,14 +568,14 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
hscan_start_(send);
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = clock;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
for (int j = 0, jm = this->get_width_internal() / 8; j < jm; j++) {
GPIO.out_w1ts = clock;
GPIO.out_w1tc = clock;
GPIO.out_w1ts = clock;
GPIO.out_w1tc = clock;
}
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = clock;
GPIO.out_w1ts = clock;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
@@ -643,10 +583,7 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
}
ESP_LOGV(TAG, "Clean fast finished (%ums)", millis() - start_time);
}
void Inkplate6::pins_z_state_() {
this->cl_pin_->pin_mode(gpio::FLAG_INPUT);
this->le_pin_->pin_mode(gpio::FLAG_INPUT);
this->ckv_pin_->pin_mode(gpio::FLAG_INPUT);
this->sph_pin_->pin_mode(gpio::FLAG_INPUT);
@@ -663,10 +600,7 @@ void Inkplate6::pins_z_state_() {
this->display_data_6_pin_->pin_mode(gpio::FLAG_INPUT);
this->display_data_7_pin_->pin_mode(gpio::FLAG_INPUT);
}
void Inkplate6::pins_as_outputs_() {
this->cl_pin_->pin_mode(gpio::FLAG_OUTPUT);
this->le_pin_->pin_mode(gpio::FLAG_OUTPUT);
this->ckv_pin_->pin_mode(gpio::FLAG_OUTPUT);
this->sph_pin_->pin_mode(gpio::FLAG_OUTPUT);

View File

@@ -13,28 +13,32 @@ namespace inkplate6 {
enum InkplateModel : uint8_t {
INKPLATE_6 = 0,
INKPLATE_10 = 1,
INKPLATE_6_PLUS = 2,
};
class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public i2c::I2CDevice {
public:
const uint8_t LUT2[16] = {0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95,
0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55};
const uint8_t LUTW[16] = {0xFF, 0xFE, 0xFB, 0xFA, 0xEF, 0xEE, 0xEB, 0xEA,
0xBF, 0xBE, 0xBB, 0xBA, 0xAF, 0xAE, 0xAB, 0xAA};
const uint8_t LUTB[16] = {0xFF, 0xFD, 0xF7, 0xF5, 0xDF, 0xDD, 0xD7, 0xD5,
0x7F, 0x7D, 0x77, 0x75, 0x5F, 0x5D, 0x57, 0x55};
const uint8_t pixelMaskLUT[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
const uint8_t pixelMaskGLUT[2] = {0x0F, 0xF0};
const uint8_t waveform3Bit[8][8] = {{0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0}, {1, 1, 1, 2, 2, 1, 0, 0},
{0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0}, {2, 2, 1, 1, 2, 1, 2, 0},
const uint8_t LUT2[16] = {0b10101010, 0b10101001, 0b10100110, 0b10100101, 0b10011010, 0b10011001,
0b10010110, 0b10010101, 0b01101010, 0b01101001, 0b01100110, 0b01100101,
0b01011010, 0b01011001, 0b01010110, 0b01010101};
const uint8_t LUTW[16] = {0b11111111, 0b11111110, 0b11111011, 0b11111010, 0b11101111, 0b11101110,
0b11101011, 0b11101010, 0b10111111, 0b10111110, 0b10111011, 0b10111010,
0b10101111, 0b10101110, 0b10101011, 0b10101010};
const uint8_t LUTB[16] = {0b11111111, 0b11111101, 0b11110111, 0b11110101, 0b11011111, 0b11011101,
0b11010111, 0b11010101, 0b01111111, 0b01111101, 0b01110111, 0b01110101,
0b01011111, 0b01011101, 0b01010111, 0b01010101};
const uint8_t pixelMaskLUT[8] = {0b00000001, 0b00000010, 0b00000100, 0b00001000,
0b00010000, 0b00100000, 0b01000000, 0b10000000};
const uint8_t pixelMaskGLUT[2] = {0b00001111, 0b11110000};
const uint8_t waveform3Bit[8][8] = {{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0},
{0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0},
{1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0}};
const uint8_t waveform3Bit6Plus[8][9] = {{0, 0, 0, 0, 0, 2, 1, 1, 0}, {0, 0, 2, 1, 1, 1, 2, 1, 0},
{0, 2, 2, 2, 1, 1, 2, 1, 0}, {0, 0, 2, 2, 2, 1, 2, 1, 0},
{0, 0, 0, 0, 2, 2, 2, 1, 0}, {0, 0, 2, 1, 2, 1, 1, 2, 0},
{0, 0, 2, 2, 2, 1, 1, 2, 0}, {0, 0, 0, 0, 2, 2, 2, 2, 0}};
const uint32_t waveform[50] = {
0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868,
0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68,
0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595,
0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484,
0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9,
0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111};
void set_greyscale(bool greyscale) {
this->greyscale_ = greyscale;
@@ -84,8 +88,6 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public
bool get_partial_updating() { return this->partial_updating_; }
uint8_t get_temperature() { return this->temperature_; }
void block_partial() { this->block_partial_ = true; }
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void display1b_();
@@ -97,10 +99,10 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public
void hscan_start_(uint32_t d);
void vscan_end_();
void vscan_start_();
void vscan_write_();
void eink_off_();
void eink_on_();
bool read_power_status_();
void setup_pins_();
void pins_z_state_();
@@ -111,8 +113,6 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public
return 800;
} else if (this->model_ == INKPLATE_10) {
return 1200;
} else if (this->model_ == INKPLATE_6_PLUS) {
return 1024;
}
return 0;
}
@@ -122,8 +122,6 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public
return 600;
} else if (this->model_ == INKPLATE_10) {
return 825;
} else if (this->model_ == INKPLATE_6_PLUS) {
return 758;
}
return 0;
}
@@ -143,20 +141,16 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public
return data;
}
bool panel_on_{false};
uint8_t panel_on_ = 0;
uint8_t temperature_;
uint8_t *partial_buffer_{nullptr};
uint8_t *partial_buffer_2_{nullptr};
uint32_t *glut_{nullptr};
uint32_t *glut2_{nullptr};
uint32_t pin_lut_[256];
uint32_t full_update_every_;
uint32_t partial_updates_{0};
bool block_partial_{true};
bool block_partial_;
bool greyscale_;
bool partial_updating_;

View File

@@ -4,10 +4,9 @@
#include "esphome/core/helpers.h"
#undef ARDUINOJSON_ENABLE_STD_STRING
#define ARDUINOJSON_ENABLE_STD_STRING 1 // NOLINT
#define ARDUINOJSON_USE_LONG_LONG 1 // NOLINT
#include <ArduinoJson.h>
namespace esphome {

View File

@@ -1,3 +0,0 @@
import esphome.codegen as cg
lilygo_t5_47_ns = cg.esphome_ns.namespace("lilygo_t5_47")

View File

@@ -1,45 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import i2c, touchscreen
from esphome.const import CONF_ID
from .. import lilygo_t5_47_ns
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["i2c"]
LilygoT547Touchscreen = lilygo_t5_47_ns.class_(
"LilygoT547Touchscreen",
touchscreen.Touchscreen,
cg.Component,
i2c.I2CDevice,
)
CONF_LILYGO_T5_47_TOUCHSCREEN_ID = "lilygo_t5_47_touchscreen_id"
CONF_INTERRUPT_PIN = "interrupt_pin"
CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(LilygoT547Touchscreen),
cv.Required(CONF_INTERRUPT_PIN): cv.All(
pins.internal_gpio_input_pin_schema
),
}
)
.extend(i2c.i2c_device_schema(0x5A))
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
await touchscreen.register_touchscreen(var, config)
interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
cg.add(var.set_interrupt_pin(interrupt_pin))

View File

@@ -1,141 +0,0 @@
#include "lilygo_t5_47_touchscreen.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace lilygo_t5_47 {
static const char *const TAG = "lilygo_t5_47.touchscreen";
static const uint8_t POWER_REGISTER = 0xD6;
static const uint8_t TOUCH_REGISTER = 0xD0;
static const uint8_t WAKEUP_CMD[1] = {0x06};
static const uint8_t READ_FLAGS[1] = {0x00};
static const uint8_t CLEAR_FLAGS[2] = {0x00, 0xAB};
static const uint8_t READ_TOUCH[1] = {0x07};
#define ERROR_CHECK(err) \
if ((err) != i2c::ERROR_OK) { \
ESP_LOGE(TAG, "Failed to communicate!"); \
this->status_set_warning(); \
return; \
}
void Store::gpio_intr(Store *store) { store->touch = true; }
void LilygoT547Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up Lilygo T5 4.7 Touchscreen...");
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->interrupt_pin_->setup();
this->store_.pin = this->interrupt_pin_->to_isr();
this->interrupt_pin_->attach_interrupt(Store::gpio_intr, &this->store_, gpio::INTERRUPT_FALLING_EDGE);
if (this->write(nullptr, 0) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Failed to communicate!");
this->interrupt_pin_->detach_interrupt();
this->mark_failed();
return;
}
this->write_register(POWER_REGISTER, WAKEUP_CMD, 1);
}
void LilygoT547Touchscreen::loop() {
if (!this->store_.touch) {
for (auto *listener : this->touch_listeners_)
listener->release();
return;
}
this->store_.touch = false;
uint8_t point = 0;
uint8_t buffer[40] = {0};
uint32_t sum_l = 0, sum_h = 0;
i2c::ErrorCode err;
err = this->write_register(TOUCH_REGISTER, READ_FLAGS, 1);
ERROR_CHECK(err);
err = this->read(buffer, 7);
ERROR_CHECK(err);
if (buffer[0] == 0xAB) {
this->write_register(TOUCH_REGISTER, CLEAR_FLAGS, 2);
return;
}
point = buffer[5] & 0xF;
if (point == 0) {
for (auto *listener : this->touch_listeners_)
listener->release();
return;
} else if (point == 1) {
err = this->write_register(TOUCH_REGISTER, READ_TOUCH, 1);
ERROR_CHECK(err);
err = this->read(&buffer[5], 2);
ERROR_CHECK(err);
sum_l = buffer[5] << 8 | buffer[6];
} else if (point > 1) {
err = this->write_register(TOUCH_REGISTER, READ_TOUCH, 1);
ERROR_CHECK(err);
err = this->read(&buffer[5], 5 * (point - 1) + 3);
ERROR_CHECK(err);
sum_l = buffer[5 * point + 1] << 8 | buffer[5 * point + 2];
}
this->write_register(TOUCH_REGISTER, CLEAR_FLAGS, 2);
for (int i = 0; i < 5 * point; i++)
sum_h += buffer[i];
if (sum_l != sum_h)
point = 0;
if (point) {
uint8_t offset;
for (int i = 0; i < point; i++) {
if (i == 0) {
offset = 0;
} else {
offset = 4;
}
TouchPoint tp;
tp.id = (buffer[i * 5 + offset] >> 4) & 0x0F;
tp.state = buffer[i * 5 + offset] & 0x0F;
if (tp.state == 0x06)
tp.state = 0x07;
tp.y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
tp.x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
this->defer([this, tp]() { this->send_touch_(tp); });
}
} else {
TouchPoint tp;
tp.id = (buffer[0] >> 4) & 0x0F;
tp.state = 0x06;
tp.y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
tp.x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
this->defer([this, tp]() { this->send_touch_(tp); });
}
this->status_clear_warning();
}
void LilygoT547Touchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "Lilygo T5 47 Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
} // namespace lilygo_t5_47
} // namespace esphome

View File

@@ -1,35 +0,0 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/touchscreen/touchscreen.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace lilygo_t5_47 {
struct Store {
volatile bool touch;
ISRInternalGPIOPin pin;
static void gpio_intr(Store *store);
};
using namespace touchscreen;
class LilygoT547Touchscreen : public Touchscreen, public Component, public i2c::I2CDevice {
public:
void setup() override;
void loop() override;
void dump_config() override;
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
protected:
InternalGPIOPin *interrupt_pin_;
Store store_;
};
} // namespace lilygo_t5_47
} // namespace esphome

View File

@@ -1,102 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_ID,
CONF_ON_LOCK,
CONF_ON_UNLOCK,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
lock_ns = cg.esphome_ns.namespace("lock")
Lock = lock_ns.class_("Lock", cg.EntityBase)
LockPtr = Lock.operator("ptr")
LockCall = lock_ns.class_("LockCall")
UnlockAction = lock_ns.class_("UnlockAction", automation.Action)
LockAction = lock_ns.class_("LockAction", automation.Action)
OpenAction = lock_ns.class_("OpenAction", automation.Action)
LockPublishAction = lock_ns.class_("LockPublishAction", automation.Action)
LockCondition = lock_ns.class_("LockCondition", Condition)
LockLockTrigger = lock_ns.class_("LockLockTrigger", automation.Trigger.template())
LockUnlockTrigger = lock_ns.class_("LockUnlockTrigger", automation.Trigger.template())
LOCK_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTLockComponent),
cv.Optional(CONF_ON_LOCK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockLockTrigger),
}
),
cv.Optional(CONF_ON_UNLOCK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockUnlockTrigger),
}
),
}
)
async def setup_lock_core_(var, config):
await setup_entity(var, config)
for conf in config.get(CONF_ON_LOCK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_UNLOCK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
await mqtt.register_mqtt_component(mqtt_, config)
async def register_lock(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_lock(var))
await setup_lock_core_(var, config)
LOCK_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Lock),
}
)
@automation.register_action("lock.unlock", UnlockAction, LOCK_ACTION_SCHEMA)
@automation.register_action("lock.lock", LockAction, LOCK_ACTION_SCHEMA)
@automation.register_action("lock.open", OpenAction, LOCK_ACTION_SCHEMA)
async def lock_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_condition("lock.is_locked", LockCondition, LOCK_ACTION_SCHEMA)
async def lock_is_on_to_code(config, condition_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren, True)
@automation.register_condition("lock.is_unlocked", LockCondition, LOCK_ACTION_SCHEMA)
async def lock_is_off_to_code(config, condition_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren, False)
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_global(lock_ns.using)
cg.add_define("USE_LOCK")

View File

@@ -1,87 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/lock/lock.h"
namespace esphome {
namespace lock {
template<typename... Ts> class LockAction : public Action<Ts...> {
public:
explicit LockAction(Lock *a_lock) : lock_(a_lock) {}
void play(Ts... x) override { this->lock_->lock(); }
protected:
Lock *lock_;
};
template<typename... Ts> class UnlockAction : public Action<Ts...> {
public:
explicit UnlockAction(Lock *a_lock) : lock_(a_lock) {}
void play(Ts... x) override { this->lock_->unlock(); }
protected:
Lock *lock_;
};
template<typename... Ts> class OpenAction : public Action<Ts...> {
public:
explicit OpenAction(Lock *a_lock) : lock_(a_lock) {}
void play(Ts... x) override { this->lock_->open(); }
protected:
Lock *lock_;
};
template<typename... Ts> class LockCondition : public Condition<Ts...> {
public:
LockCondition(Lock *parent, bool state) : parent_(parent), state_(state) {}
bool check(Ts... x) override {
auto check_state = this->state_ ? LockState::LOCK_STATE_LOCKED : LockState::LOCK_STATE_UNLOCKED;
return this->parent_->state == check_state;
}
protected:
Lock *parent_;
bool state_;
};
class LockLockTrigger : public Trigger<> {
public:
LockLockTrigger(Lock *a_lock) {
a_lock->add_on_state_callback([this, a_lock]() {
if (a_lock->state == LockState::LOCK_STATE_LOCKED) {
this->trigger();
}
});
}
};
class LockUnlockTrigger : public Trigger<> {
public:
LockUnlockTrigger(Lock *a_lock) {
a_lock->add_on_state_callback([this, a_lock]() {
if (a_lock->state == LockState::LOCK_STATE_UNLOCKED) {
this->trigger();
}
});
}
};
template<typename... Ts> class LockPublishAction : public Action<Ts...> {
public:
LockPublishAction(Lock *a_lock) : lock_(a_lock) {}
TEMPLATABLE_VALUE(LockState, state)
void play(Ts... x) override { this->lock_->publish_state(this->state_.value(x...)); }
protected:
Lock *lock_;
};
} // namespace lock
} // namespace esphome

View File

@@ -1,109 +0,0 @@
#include "lock.h"
#include "esphome/core/log.h"
namespace esphome {
namespace lock {
static const char *const TAG = "lock";
const char *lock_state_to_string(LockState state) {
switch (state) {
case LOCK_STATE_LOCKED:
return "LOCKED";
case LOCK_STATE_UNLOCKED:
return "UNLOCKED";
case LOCK_STATE_JAMMED:
return "JAMMED";
case LOCK_STATE_LOCKING:
return "LOCKING";
case LOCK_STATE_UNLOCKING:
return "UNLOCKING";
case LOCK_STATE_NONE:
default:
return "UNKNOWN";
}
}
Lock::Lock(const std::string &name) : EntityBase(name), state(LOCK_STATE_NONE) {}
Lock::Lock() : Lock("") {}
LockCall Lock::make_call() { return LockCall(this); }
void Lock::lock() {
auto call = this->make_call();
call.set_state(LOCK_STATE_LOCKED);
this->control(call);
}
void Lock::unlock() {
auto call = this->make_call();
call.set_state(LOCK_STATE_UNLOCKED);
this->control(call);
}
void Lock::open() {
if (traits.get_supports_open()) {
ESP_LOGD(TAG, "'%s' Opening.", this->get_name().c_str());
this->open_latch();
} else {
ESP_LOGW(TAG, "'%s' Does not support Open.", this->get_name().c_str());
}
}
void Lock::publish_state(LockState state) {
if (!this->publish_dedup_.next(state))
return;
this->state = state;
this->rtc_.save(&this->state);
ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), lock_state_to_string(state));
this->state_callback_.call();
}
void Lock::add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
uint32_t Lock::hash_base() { return 856245656UL; }
void LockCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
this->validate_();
if (this->state_.has_value()) {
const char *state_s = lock_state_to_string(*this->state_);
ESP_LOGD(TAG, " State: %s", state_s);
}
this->parent_->control(*this);
}
void LockCall::validate_() {
if (this->state_.has_value()) {
auto state = *this->state_;
if (!this->parent_->traits.supports_state(state)) {
ESP_LOGW(TAG, " State %s is not supported by this device!", lock_state_to_string(*this->state_));
this->state_.reset();
}
}
}
LockCall &LockCall::set_state(LockState state) {
this->state_ = state;
return *this;
}
LockCall &LockCall::set_state(optional<LockState> state) {
this->state_ = state;
return *this;
}
LockCall &LockCall::set_state(const std::string &state) {
if (str_equals_case_insensitive(state, "LOCKED")) {
this->set_state(LOCK_STATE_LOCKED);
} else if (str_equals_case_insensitive(state, "UNLOCKED")) {
this->set_state(LOCK_STATE_UNLOCKED);
} else if (str_equals_case_insensitive(state, "JAMMED")) {
this->set_state(LOCK_STATE_JAMMED);
} else if (str_equals_case_insensitive(state, "LOCKING")) {
this->set_state(LOCK_STATE_LOCKING);
} else if (str_equals_case_insensitive(state, "UNLOCKING")) {
this->set_state(LOCK_STATE_UNLOCKING);
} else if (str_equals_case_insensitive(state, "NONE")) {
this->set_state(LOCK_STATE_NONE);
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized state %s", this->parent_->get_name().c_str(), state.c_str());
}
return *this;
}
const optional<LockState> &LockCall::get_state() const { return this->state_; }
} // namespace lock
} // namespace esphome

View File

@@ -1,178 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/preferences.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <set>
namespace esphome {
namespace lock {
class Lock;
#define LOG_LOCK(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
if ((obj)->traits.get_assumed_state()) { \
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
} \
}
/// Enum for all states a lock can be in.
enum LockState : uint8_t {
LOCK_STATE_NONE = 0,
LOCK_STATE_LOCKED = 1,
LOCK_STATE_UNLOCKED = 2,
LOCK_STATE_JAMMED = 3,
LOCK_STATE_LOCKING = 4,
LOCK_STATE_UNLOCKING = 5
};
const char *lock_state_to_string(LockState state);
class LockTraits {
public:
LockTraits() = default;
bool get_supports_open() const { return this->supports_open_; }
void set_supports_open(bool supports_open) { this->supports_open_ = supports_open; }
bool get_requires_code() const { return this->requires_code_; }
void set_requires_code(bool requires_code) { this->requires_code_ = requires_code; }
bool get_assumed_state() const { return this->assumed_state_; }
void set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
bool supports_state(LockState state) const { return supported_states_.count(state); }
std::set<LockState> get_supported_states() const { return supported_states_; }
void set_supported_states(std::set<LockState> states) { supported_states_ = std::move(states); }
void add_supported_state(LockState state) { supported_states_.insert(state); }
protected:
bool supports_open_{false};
bool requires_code_{false};
bool assumed_state_{false};
std::set<LockState> supported_states_ = {LOCK_STATE_NONE, LOCK_STATE_LOCKED, LOCK_STATE_UNLOCKED};
};
/** This class is used to encode all control actions on a lock device.
*
* It is supposed to be used by all code that wishes to control a lock device (mqtt, api, lambda etc).
* Create an instance of this class by calling `id(lock_device).make_call();`. Then set all attributes
* with the `set_x` methods. Finally, to apply the changes call `.perform();`.
*
* The integration that implements the lock device receives this instance with the `control` method.
* It should check all the properties it implements and apply them as needed. It should do so by
* getting all properties it controls with the getter methods in this class. If the optional value is
* set (check with `.has_value()`) that means the user wants to control this property. Get the value
* of the optional with the star operator (`*call.get_state()`) and apply it.
*/
class LockCall {
public:
LockCall(Lock *parent) : parent_(parent) {}
/// Set the state of the lock device.
LockCall &set_state(LockState state);
/// Set the state of the lock device.
LockCall &set_state(optional<LockState> state);
/// Set the state of the lock device based on a string.
LockCall &set_state(const std::string &state);
void perform();
const optional<LockState> &get_state() const;
protected:
void validate_();
Lock *const parent_;
optional<LockState> state_;
};
/** Base class for all locks.
*
* A lock is basically a switch with a combination of a binary sensor (for reporting lock values)
* and a write_state method that writes a state to the hardware. Locks can also have an "open"
* method to unlatch.
*
* For integrations: Integrations must implement the method control().
* Control will be called with the arguments supplied by the user and should be used
* to control all values of the lock.
*/
class Lock : public EntityBase {
public:
explicit Lock();
explicit Lock(const std::string &name);
/** Make a lock device control call, this is used to control the lock device, see the LockCall description
* for more info.
* @return A new LockCall instance targeting this lock device.
*/
LockCall make_call();
/** Publish a state to the front-end from the back-end.
*
* Then the internal value member is set and finally the callbacks are called.
*
* @param state The new state.
*/
void publish_state(LockState state);
/// The current reported state of the lock.
LockState state{LOCK_STATE_NONE};
LockTraits traits;
/** Turn this lock on. This is called by the front-end.
*
* For implementing locks, please override control.
*/
void lock();
/** Turn this lock off. This is called by the front-end.
*
* For implementing locks, please override control.
*/
void unlock();
/** Open (unlatch) this lock. This is called by the front-end.
*
* For implementing locks, please override control.
*/
void open();
/** Set callback for state changes.
*
* @param callback The void(bool) callback.
*/
void add_on_state_callback(std::function<void()> &&callback);
protected:
friend LockCall;
/** Perform the open latch action with hardware. This method is optional to implement
* when creating a new lock.
*
* In the implementation of this method, it is recommended you also call
* publish_state with "unlock" to acknowledge that the state was written to the hardware.
*/
virtual void open_latch() { unlock(); };
/** Control the lock device, this is a virtual method that each lock integration must implement.
*
* See more info in LockCall. The integration should check all of its values in this method and
* set them accordingly. At the end of the call, the integration must call `publish_state()` to
* notify the frontend of a changed state.
*
* @param call The LockCall instance encoding all attribute changes.
*/
virtual void control(const LockCall &call) = 0;
uint32_t hash_base() override;
CallbackManager<void()> state_callback_{};
Deduplicator<LockState> publish_dedup_;
ESPPreferenceObject rtc_;
};
} // namespace lock
} // namespace esphome

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@mckaymatthew"]

View File

@@ -1,93 +0,0 @@
#include "max9611.h"
#include "esphome/core/log.h"
#include "esphome/components/i2c/i2c_bus.h"
namespace esphome {
namespace max9611 {
using namespace esphome::i2c;
// Sign extend
// http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
template<typename T, unsigned B> inline T signextend(const T x) {
struct {
T x : B;
} s;
return s.x = x;
}
// Map the gain register to in uV/LSB
float gain_to_lsb(MAX9611Multiplexer gain) {
float lsb = 0.0;
if (gain == MAX9611_MULTIPLEXER_CSA_GAIN1) {
lsb = 107.50;
} else if (gain == MAX9611_MULTIPLEXER_CSA_GAIN4) {
lsb = 26.88;
} else if (gain == MAX9611_MULTIPLEXER_CSA_GAIN8) {
lsb = 13.44;
}
return lsb;
}
static const char *const TAG = "max9611";
static const uint8_t SETUP_DELAY = 4; // Wait 2 integration periods.
static const float VOUT_LSB = 14.0 / 1000.0; // 14mV/LSB
static const float TEMP_LSB = 0.48; // 0.48C/LSB
static const float MICRO_VOLTS_PER_VOLT = 1000000.0;
void MAX9611Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up max9611...");
// Perform dummy-read
uint8_t value;
this->read(&value, 1);
// Configuration Stage.
// First send an integration request with the specified gain
const uint8_t setup_dat[] = {CONTROL_REGISTER_1_ADRR, static_cast<uint8_t>(gain_)};
// Then send a request that samples all channels as fast as possible, using the last provided gain
const uint8_t fast_mode_dat[] = {CONTROL_REGISTER_1_ADRR, MAX9611Multiplexer::MAX9611_MULTIPLEXER_FAST_MODE};
if (this->write(reinterpret_cast<const uint8_t *>(&setup_dat), sizeof(setup_dat)) != ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Failed to setup Max9611 during GAIN SET");
return;
}
delay(SETUP_DELAY);
if (this->write(reinterpret_cast<const uint8_t *>(&fast_mode_dat), sizeof(fast_mode_dat)) != ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Failed to setup Max9611 during FAST MODE SET");
return;
}
}
void MAX9611Component::dump_config() {
ESP_LOGCONFIG(TAG, "Dump Config max9611...");
ESP_LOGCONFIG(TAG, " CSA Gain Register: %x", gain_);
LOG_I2C_DEVICE(this);
}
void MAX9611Component::update() {
// Setup read from 0x0 register base
const uint8_t reg_base = 0x0;
const ErrorCode write_result = this->write(&reg_base, 1);
// Just read the entire register map in a bulk read, faster than individually querying register.
const ErrorCode read_result = this->read(register_map_, sizeof(register_map_));
if (write_result != ErrorCode::ERROR_OK || read_result != ErrorCode::ERROR_OK) {
ESP_LOGW(TAG, "MAX9611 Update FAILED!");
return;
}
uint16_t csa_register = ((register_map_[CSA_DATA_BYTE_MSB_ADRR] << 8) | (register_map_[CSA_DATA_BYTE_LSB_ADRR])) >> 4;
uint16_t rs_register = ((register_map_[RS_DATA_BYTE_MSB_ADRR] << 8) | (register_map_[RS_DATA_BYTE_LSB_ADRR])) >> 4;
uint16_t t_register = ((register_map_[TEMP_DATA_BYTE_MSB_ADRR] << 8) | (register_map_[TEMP_DATA_BYTE_LSB_ADRR])) >> 7;
float voltage = rs_register * VOUT_LSB;
float shunt_voltage = (csa_register * gain_to_lsb(gain_)) / MICRO_VOLTS_PER_VOLT;
float temp = signextend<signed int, 9>(t_register) * TEMP_LSB;
float amps = shunt_voltage / current_resistor_;
float watts = amps * voltage;
if (voltage_sensor_ != nullptr) {
voltage_sensor_->publish_state(voltage);
}
if (current_sensor_ != nullptr) {
current_sensor_->publish_state(amps);
}
if (watt_sensor_ != nullptr) {
watt_sensor_->publish_state(watts);
}
if (temperature_sensor_ != nullptr) {
temperature_sensor_->publish_state(temp);
}
ESP_LOGD(TAG, "V: %f, A: %f, W: %f, Deg C: %f", voltage, amps, watts, temp);
}
} // namespace max9611
} // namespace esphome

View File

@@ -1,62 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace max9611 {
enum MAX9611Multiplexer {
MAX9611_MULTIPLEXER_CSA_GAIN1 = 0b000,
MAX9611_MULTIPLEXER_CSA_GAIN4 = 0b001,
MAX9611_MULTIPLEXER_CSA_GAIN8 = 0b010,
MAX9611_MULTIPLEXER_RS = 0b011,
MAX9611_MULTIPLEXER_OUT = 0b100,
MAX9611_MULTIPLEXER_SET = 0b101,
MAX9611_MULTIPLEXER_TEMP = 0b110,
MAX9611_MULTIPLEXER_FAST_MODE = 0b111,
};
enum MAX9611RegisterMap {
CSA_DATA_BYTE_MSB_ADRR = 0x00,
CSA_DATA_BYTE_LSB_ADRR = 0x01,
RS_DATA_BYTE_MSB_ADRR = 0x02,
RS_DATA_BYTE_LSB_ADRR = 0x03,
OUT_DATA_BYTE_MSB_ADRR = 0x04, // Unused Op-Amp
OUT_DATA_BYTE_LSB_ADRR = 0x05, // Unused Op-Amp
SET_DATA_BYTE_MSB_ADRR = 0x06, // Unused Op-Amp
SET_DATA_BYTE_LSB_ADRR = 0x07, // Unused Op-Amp
TEMP_DATA_BYTE_MSB_ADRR = 0x08,
TEMP_DATA_BYTE_LSB_ADRR = 0x09,
CONTROL_REGISTER_1_ADRR = 0x0A,
CONTROL_REGISTER_2_ADRR = 0x0B,
};
class MAX9611Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void update() override;
void set_voltage_sensor(sensor::Sensor *vs) { voltage_sensor_ = vs; }
void set_current_sensor(sensor::Sensor *cs) { current_sensor_ = cs; }
void set_watt_sensor(sensor::Sensor *ws) { watt_sensor_ = ws; }
void set_temp_sensor(sensor::Sensor *ts) { temperature_sensor_ = ts; }
void set_current_resistor(float r) { current_resistor_ = r; }
void set_gain(MAX9611Multiplexer g) { gain_ = g; }
protected:
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *watt_sensor_{nullptr};
sensor::Sensor *temperature_sensor_{nullptr};
float current_resistor_;
uint8_t register_map_[0x0C];
MAX9611Multiplexer gain_;
};
} // namespace max9611
} // namespace esphome

View File

@@ -1,92 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
CONF_SHUNT_RESISTANCE,
CONF_GAIN,
CONF_VOLTAGE,
CONF_CURRENT,
CONF_POWER,
CONF_TEMPERATURE,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_CELSIUS,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
)
DEPENDENCIES = ["i2c"]
max9611_ns = cg.esphome_ns.namespace("max9611")
max9611Gain = max9611_ns.enum("MAX9611Multiplexer")
MAX9611_GAIN = {
"8X": max9611Gain.MAX9611_MULTIPLEXER_CSA_GAIN8,
"4X": max9611Gain.MAX9611_MULTIPLEXER_CSA_GAIN4,
"1X": max9611Gain.MAX9611_MULTIPLEXER_CSA_GAIN1,
}
MAX9611Component = max9611_ns.class_(
"MAX9611Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MAX9611Component),
cv.Required(CONF_SHUNT_RESISTANCE): cv.resistance,
cv.Required(CONF_GAIN): cv.enum(MAX9611_GAIN, upper=True),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x70))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_current_resistor(config[CONF_SHUNT_RESISTANCE]))
cg.add(var.set_gain(config[CONF_GAIN]))
if CONF_VOLTAGE in config:
conf = config[CONF_VOLTAGE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_voltage_sensor(sens))
if CONF_CURRENT in config:
conf = config[CONF_CURRENT]
sens = await sensor.new_sensor(conf)
cg.add(var.set_current_sensor(sens))
if CONF_POWER in config:
conf = config[CONF_POWER]
sens = await sensor.new_sensor(conf)
cg.add(var.set_watt_sensor(sens))
if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_temp_sensor(sens))

View File

@@ -1,7 +1,7 @@
from esphome.const import CONF_ID
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.core import CORE, coroutine_with_priority
from esphome.core import CORE
CODEOWNERS = ["@esphome/core"]
DEPENDENCIES = ["network"]
@@ -29,7 +29,6 @@ CONFIG_SCHEMA = cv.All(
)
@coroutine_with_priority(55.0)
async def to_code(config):
if CORE.using_arduino:
if CORE.is_esp32:

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@functionpointer"]

View File

@@ -1,135 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
UNIT_MICROTESLA,
UNIT_CELSIUS,
STATE_CLASS_MEASUREMENT,
ICON_MAGNET,
ICON_THERMOMETER,
CONF_GAIN,
CONF_RESOLUTION,
CONF_OVERSAMPLING,
CONF_FILTER,
CONF_TEMPERATURE,
)
from esphome import pins
CODEOWNERS = ["@functionpointer"]
DEPENDENCIES = ["i2c"]
mlx90393_ns = cg.esphome_ns.namespace("mlx90393")
MLX90393Component = mlx90393_ns.class_(
"MLX90393Cls", cg.PollingComponent, i2c.I2CDevice
)
GAIN = {
"1X": 7,
"1_33X": 6,
"1_67X": 5,
"2X": 4,
"2_5X": 3,
"3X": 2,
"4X": 1,
"5X": 0,
}
RESOLUTION = {
"16BIT": 0,
"17BIT": 1,
"18BIT": 2,
"19BIT": 3,
}
CONF_X_AXIS = "x_axis"
CONF_Y_AXIS = "y_axis"
CONF_Z_AXIS = "z_axis"
CONF_DRDY_PIN = "drdy_pin"
def mlx90393_axis_schema(default_resolution: str):
return sensor.sensor_schema(
unit_of_measurement=UNIT_MICROTESLA,
accuracy_decimals=0,
icon=ICON_MAGNET,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
cv.Schema(
{
cv.Optional(CONF_RESOLUTION, default=default_resolution): cv.enum(
RESOLUTION, upper=True, space="_"
)
}
)
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MLX90393Component),
cv.Optional(CONF_GAIN, default="2_5X"): cv.enum(
GAIN, upper=True, space="_"
),
cv.Optional(CONF_DRDY_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_OVERSAMPLING, default=2): cv.int_range(min=0, max=3),
cv.Optional(CONF_FILTER, default=6): cv.int_range(min=0, max=7),
cv.Optional(CONF_X_AXIS): mlx90393_axis_schema("19BIT"),
cv.Optional(CONF_Y_AXIS): mlx90393_axis_schema("19BIT"),
cv.Optional(CONF_Z_AXIS): mlx90393_axis_schema("16BIT"),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
icon=ICON_THERMOMETER,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
cv.Schema(
{
cv.Optional(CONF_OVERSAMPLING, default=0): cv.int_range(
min=0, max=3
),
}
)
),
},
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x0C))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if CONF_DRDY_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_DRDY_PIN])
cg.add(var.set_drdy_pin(pin))
cg.add(var.set_gain(GAIN[config[CONF_GAIN]]))
cg.add(var.set_oversampling(config[CONF_OVERSAMPLING]))
cg.add(var.set_filter(config[CONF_FILTER]))
if CONF_X_AXIS in config:
sens = await sensor.new_sensor(config[CONF_X_AXIS])
cg.add(var.set_x_sensor(sens))
cg.add(var.set_resolution(0, RESOLUTION[config[CONF_X_AXIS][CONF_RESOLUTION]]))
if CONF_Y_AXIS in config:
sens = await sensor.new_sensor(config[CONF_Y_AXIS])
cg.add(var.set_y_sensor(sens))
cg.add(var.set_resolution(1, RESOLUTION[config[CONF_Y_AXIS][CONF_RESOLUTION]]))
if CONF_Z_AXIS in config:
sens = await sensor.new_sensor(config[CONF_Z_AXIS])
cg.add(var.set_z_sensor(sens))
cg.add(var.set_resolution(2, RESOLUTION[config[CONF_Z_AXIS][CONF_RESOLUTION]]))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_t_sensor(sens))
cg.add(var.set_t_oversampling(config[CONF_TEMPERATURE][CONF_OVERSAMPLING]))
if CONF_DRDY_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_DRDY_PIN])
cg.add(var.set_drdy_gpio(pin))
cg.add_library("functionpointer/arduino-MLX90393", "1.0.0")

View File

@@ -1,91 +0,0 @@
#include "sensor_mlx90393.h"
#include "esphome/core/log.h"
namespace esphome {
namespace mlx90393 {
static const char *const TAG = "mlx90393";
bool MLX90393Cls::transceive(const uint8_t *request, size_t request_size, uint8_t *response, size_t response_size) {
i2c::ErrorCode e = this->write(request, request_size);
if (e != i2c::ErrorCode::ERROR_OK) {
return false;
}
e = this->read(response, response_size);
return e == i2c::ErrorCode::ERROR_OK;
}
bool MLX90393Cls::has_drdy_pin() { return this->drdy_pin_ != nullptr; }
bool MLX90393Cls::read_drdy_pin() {
if (this->drdy_pin_ == nullptr) {
return false;
} else {
return this->drdy_pin_->digital_read();
}
}
void MLX90393Cls::sleep_millis(uint32_t millis) { delay(millis); }
void MLX90393Cls::sleep_micros(uint32_t micros) { delayMicroseconds(micros); }
void MLX90393Cls::setup() {
ESP_LOGCONFIG(TAG, "Setting up MLX90393...");
// note the two arguments A0 and A1 which are used to construct an i2c address
// we can hard-code these because we never actually use the constructed address
// see the transceive function above, which uses the address from I2CComponent
this->mlx_.begin_with_hal(this, 0, 0);
this->mlx_.setGainSel(this->gain_);
this->mlx_.setResolution(this->resolutions_[0], this->resolutions_[1], this->resolutions_[2]);
this->mlx_.setOverSampling(this->oversampling_);
this->mlx_.setDigitalFiltering(this->filter_);
this->mlx_.setTemperatureOverSampling(this->temperature_oversampling_);
}
void MLX90393Cls::dump_config() {
ESP_LOGCONFIG(TAG, "MLX90393:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with MLX90393 failed!");
return;
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "X Axis", this->x_sensor_);
LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
LOG_SENSOR(" ", "Temperature", this->t_sensor_);
}
float MLX90393Cls::get_setup_priority() const { return setup_priority::DATA; }
void MLX90393Cls::update() {
MLX90393::txyz data;
if (this->mlx_.readData(data) == MLX90393::STATUS_OK) {
ESP_LOGD(TAG, "received %f %f %f", data.x, data.y, data.z);
if (this->x_sensor_ != nullptr) {
this->x_sensor_->publish_state(data.x);
}
if (this->y_sensor_ != nullptr) {
this->y_sensor_->publish_state(data.y);
}
if (this->z_sensor_ != nullptr) {
this->z_sensor_->publish_state(data.z);
}
if (this->t_sensor_ != nullptr) {
this->t_sensor_->publish_state(data.t);
}
this->status_clear_warning();
} else {
ESP_LOGE(TAG, "failed to read data");
this->status_set_warning();
}
}
} // namespace mlx90393
} // namespace esphome

View File

@@ -1,59 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/hal.h"
#include <MLX90393.h>
#include <MLX90393Hal.h>
namespace esphome {
namespace mlx90393 {
class MLX90393Cls : public PollingComponent, public i2c::I2CDevice, public MLX90393Hal {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_drdy_gpio(GPIOPin *pin) { drdy_pin_ = pin; }
void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
void set_t_sensor(sensor::Sensor *t_sensor) { t_sensor_ = t_sensor; }
void set_oversampling(uint8_t osr) { oversampling_ = osr; }
void set_t_oversampling(uint8_t osr2) { temperature_oversampling_ = osr2; }
void set_resolution(uint8_t xyz, uint8_t res) { resolutions_[xyz] = res; }
void set_filter(uint8_t filter) { filter_ = filter; }
void set_gain(uint8_t gain_sel) { gain_ = gain_sel; }
// overrides for MLX library
// disable lint because it keeps suggesting const uint8_t *response.
// this->read() writes data into response, so it can't be const
bool transceive(const uint8_t *request, size_t request_size, uint8_t *response,
size_t response_size) override; // NOLINT
bool has_drdy_pin() override;
bool read_drdy_pin() override;
void sleep_millis(uint32_t millis) override;
void sleep_micros(uint32_t micros) override;
protected:
MLX90393 mlx_;
sensor::Sensor *x_sensor_{nullptr};
sensor::Sensor *y_sensor_{nullptr};
sensor::Sensor *z_sensor_{nullptr};
sensor::Sensor *t_sensor_{nullptr};
uint8_t gain_;
uint8_t oversampling_;
uint8_t temperature_oversampling_ = 0;
uint8_t filter_;
uint8_t resolutions_[3] = {0};
GPIOPin *drdy_pin_ = nullptr;
};
} // namespace mlx90393
} // namespace esphome

View File

@@ -110,8 +110,7 @@ void ModbusController::queue_command(const ModbusCommandItem &command) {
for (auto &item : command_queue_) {
if (item->register_address == command.register_address && item->register_count == command.register_count &&
item->register_type == command.register_type && item->function_code == command.function_code) {
ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u",
static_cast<uint8_t>(command.register_type), command.register_address, command.register_count);
ESP_LOGW(TAG, "Duplicate modbus command found");
// update the payload of the queued command
// replaces a previous command
item->payload = command.payload;
@@ -361,9 +360,8 @@ ModbusCommandItem ModbusCommandItem::create_write_multiple_command(ModbusControl
modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
};
for (auto v : values) {
auto decoded_value = decode_value(v);
cmd.payload.push_back(decoded_value[0]);
cmd.payload.push_back(decoded_value[1]);
cmd.payload.push_back((v / 256) & 0xFF);
cmd.payload.push_back(v & 0xFF);
}
return cmd;
}
@@ -418,7 +416,7 @@ ModbusCommandItem ModbusCommandItem::create_write_multiple_coils(ModbusControlle
}
ModbusCommandItem ModbusCommandItem::create_write_single_command(ModbusController *modbusdevice, uint16_t start_address,
uint16_t value) {
int16_t value) {
ModbusCommandItem cmd;
cmd.modbusdevice = modbusdevice;
cmd.register_type = ModbusRegisterType::HOLDING;
@@ -429,10 +427,8 @@ ModbusCommandItem ModbusCommandItem::create_write_single_command(ModbusControlle
const std::vector<uint8_t> &data) {
modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
};
auto decoded_value = decode_value(value);
cmd.payload.push_back(decoded_value[0]);
cmd.payload.push_back(decoded_value[1]);
cmd.payload.push_back((value / 256) & 0xFF);
cmd.payload.push_back((value % 256) & 0xFF);
return cmd;
}
@@ -467,70 +463,84 @@ bool ModbusCommandItem::send() {
return true;
}
void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
union {
float float_value;
uint32_t raw;
} raw_to_float;
std::vector<uint16_t> data;
int32_t val;
switch (value_type) {
case SensorValueType::U_WORD:
case SensorValueType::S_WORD:
data.push_back(value & 0xFFFF);
// cast truncates the float do some rounding here
data.push_back(lroundf(value) & 0xFFFF);
break;
case SensorValueType::U_DWORD:
case SensorValueType::S_DWORD:
case SensorValueType::FP32:
case SensorValueType::FP32_R:
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back(value & 0xFFFF);
val = lroundf(value);
data.push_back((val & 0xFFFF0000) >> 16);
data.push_back(val & 0xFFFF);
break;
case SensorValueType::U_DWORD_R:
case SensorValueType::S_DWORD_R:
data.push_back(value & 0xFFFF);
data.push_back((value & 0xFFFF0000) >> 16);
val = lroundf(value);
data.push_back(val & 0xFFFF);
data.push_back((val & 0xFFFF0000) >> 16);
break;
case SensorValueType::U_QWORD:
case SensorValueType::S_QWORD:
data.push_back((value & 0xFFFF000000000000) >> 48);
data.push_back((value & 0xFFFF00000000) >> 32);
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back(value & 0xFFFF);
case SensorValueType::FP32:
raw_to_float.float_value = value;
data.push_back((raw_to_float.raw & 0xFFFF0000) >> 16);
data.push_back(raw_to_float.raw & 0xFFFF);
break;
case SensorValueType::U_QWORD_R:
case SensorValueType::S_QWORD_R:
data.push_back(value & 0xFFFF);
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back((value & 0xFFFF00000000) >> 32);
data.push_back((value & 0xFFFF000000000000) >> 48);
case SensorValueType::FP32_R:
raw_to_float.float_value = value;
data.push_back(raw_to_float.raw & 0xFFFF);
data.push_back((raw_to_float.raw & 0xFFFF0000) >> 16);
break;
default:
ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversation: %d",
static_cast<uint16_t>(value_type));
ESP_LOGE(TAG, "Invalid data type for modbus float to payload conversation");
break;
}
return data;
}
int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask) {
float payload_to_float(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask) {
union {
float float_value;
uint32_t raw;
} raw_to_float;
int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
float result = NAN;
switch (sensor_value_type) {
case SensorValueType::U_WORD:
value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
result = static_cast<float>(value);
break;
case SensorValueType::U_DWORD:
case SensorValueType::FP32:
value = get_data<uint32_t>(data, offset);
value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
result = static_cast<float>(value);
break;
case SensorValueType::U_DWORD_R:
case SensorValueType::FP32_R:
value = get_data<uint32_t>(data, offset);
value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
result = static_cast<float>(value);
break;
case SensorValueType::S_WORD:
value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset),
bitmask); // default is 0xFFFF ;
result = static_cast<float>(value);
break;
case SensorValueType::S_DWORD:
value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
result = static_cast<float>(value);
break;
case SensorValueType::S_DWORD_R: {
value = get_data<uint32_t>(data, offset);
@@ -539,14 +549,18 @@ int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sens
uint32_t sign_bit = (value & 0x8000) << 16;
value = mask_and_shift_by_rightbit(
static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
result = static_cast<float>(value);
} break;
case SensorValueType::U_QWORD:
// Ignore bitmask for U_QWORD
value = get_data<uint64_t>(data, offset);
result = static_cast<float>(value);
break;
case SensorValueType::S_QWORD:
// Ignore bitmask for S_QWORD
value = get_data<int64_t>(data, offset);
result = static_cast<float>(value);
break;
case SensorValueType::U_QWORD_R:
// Ignore bitmask for U_QWORD
@@ -554,16 +568,32 @@ int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sens
value = static_cast<uint64_t>(value & 0xFFFF) << 48 | (value & 0xFFFF000000000000) >> 48 |
static_cast<uint64_t>(value & 0xFFFF0000) << 32 | (value & 0x0000FFFF00000000) >> 32 |
static_cast<uint64_t>(value & 0xFFFF00000000) << 16 | (value & 0x00000000FFFF0000) >> 16;
result = static_cast<float>(value);
break;
case SensorValueType::S_QWORD_R:
// Ignore bitmask for S_QWORD
value = get_data<int64_t>(data, offset);
result = static_cast<float>(value);
break;
case SensorValueType::FP32:
raw_to_float.raw = get_data<uint32_t>(data, offset);
ESP_LOGD(TAG, "FP32 = 0x%08X => %f", raw_to_float.raw, raw_to_float.float_value);
result = raw_to_float.float_value;
break;
case SensorValueType::FP32_R: {
auto tmp = get_data<uint32_t>(data, offset);
raw_to_float.raw = static_cast<uint32_t>(tmp & 0xFFFF) << 16 | (tmp & 0xFFFF0000) >> 16;
ESP_LOGD(TAG, "FP32_R = 0x%08X => %f", raw_to_float.raw, raw_to_float.float_value);
result = raw_to_float.float_value;
} break;
case SensorValueType::RAW:
result = NAN;
break;
default:
break;
}
return value;
return result;
}
} // namespace modbus_controller

View File

@@ -195,7 +195,7 @@ inline bool coil_from_vector(int coil, const std::vector<uint8_t> &data) {
*/
template<typename N> N mask_and_shift_by_rightbit(N data, uint32_t mask) {
auto result = (mask & data);
if (result == 0 || mask == 0xFFFFFFFF) {
if (result == 0) {
return result;
}
for (size_t pos = 0; pos < sizeof(N) << 3; pos++) {
@@ -205,23 +205,22 @@ template<typename N> N mask_and_shift_by_rightbit(N data, uint32_t mask) {
return 0;
}
/** Convert float value to vector<uint16_t> suitable for sending
* @param data target for payload
* @param value float value to convert
/** convert float value to vector<uint16_t> suitable for sending
* @param value float value to cconvert
* @param value_type defines if 16/32 or FP32 is used
* @return vector containing the modbus register words in correct order
*/
void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type);
std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type);
/** Convert vector<uint8_t> response payload to number.
* @param data payload with the data to convert
/** convert vector<uint8_t> response payload to float
* @param value float value to cconvert
* @param sensor_value_type defines if 16/32/64 bits or FP32 is used
* @param offset offset to the data in data
* @param bitmask bitmask used for masking and shifting
* @return 64-bit number of the payload
* @return float version of the input
*/
int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask);
float payload_to_float(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask);
class ModbusController;
@@ -349,11 +348,11 @@ class ModbusCommandItem {
* @param modbusdevice pointer to the device to execute the command
* @param start_address modbus address of the first register to read
* @param register_count number of registers to read
* @param value uint16_t single register value to write
* @param values uint16_t array to be written to the registers
* @return ModbusCommandItem with the prepared command
*/
static ModbusCommandItem create_write_single_command(ModbusController *modbusdevice, uint16_t start_address,
uint16_t value);
int16_t value);
/** Create modbus write single registers command
* Function 05 (05hex) Write Single Coil
* @param modbusdevice pointer to the device to execute the command
@@ -447,36 +446,13 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
uint16_t command_throttle_;
};
/** Convert vector<uint8_t> response payload to float.
* @param data payload with data
/** convert vector<uint8_t> response payload to float
* @param value float value to cconvert
* @param item SensorItem object
* @return float value of data
* @return float version of the input
*/
inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem &item) {
int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask);
float float_value;
if (item.sensor_value_type == SensorValueType::FP32 || item.sensor_value_type == SensorValueType::FP32_R) {
float_value = bit_cast<float>(static_cast<uint32_t>(number));
} else {
float_value = static_cast<float>(number);
}
return float_value;
}
inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
int64_t val;
if (value_type == SensorValueType::FP32 || value_type == SensorValueType::FP32_R) {
val = bit_cast<uint32_t>(value);
} else {
val = llroundf(value);
}
std::vector<uint16_t> data;
number_to_payload(data, val, value_type);
return data;
return payload_to_float(data, item.sensor_value_type, item.offset, item.bitmask);
}
} // namespace modbus_controller

View File

@@ -1,142 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import select
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA
from esphome.jsonschema import jschema_composite
from .. import (
SENSOR_VALUE_TYPE,
TYPE_REGISTER_MAP,
ModbusController,
SensorItem,
modbus_controller_ns,
)
from ..const import (
CONF_FORCE_NEW_RANGE,
CONF_MODBUS_CONTROLLER_ID,
CONF_REGISTER_COUNT,
CONF_SKIP_UPDATES,
CONF_USE_WRITE_MULTIPLE,
CONF_VALUE_TYPE,
CONF_WRITE_LAMBDA,
)
DEPENDENCIES = ["modbus_controller"]
CODEOWNERS = ["@martgras", "@stegm"]
CONF_OPTIONSMAP = "optionsmap"
ModbusSelect = modbus_controller_ns.class_(
"ModbusSelect", cg.Component, select.Select, SensorItem
)
@jschema_composite
def ensure_option_map():
def validator(value):
cv.check_not_templatable(value)
option = cv.All(cv.string_strict)
mapping = cv.All(cv.int_range(-(2**63), 2**63 - 1))
options_map_schema = cv.Schema({option: mapping})
value = options_map_schema(value)
all_values = list(value.values())
unique_values = set(value.values())
if len(all_values) != len(unique_values):
raise cv.Invalid("Mapping values must be unique.")
return value
return validator
def register_count_value_type_min(value):
reg_count = value.get(CONF_REGISTER_COUNT)
if reg_count is not None:
value_type = value[CONF_VALUE_TYPE]
min_register_count = TYPE_REGISTER_MAP[value_type]
if min_register_count > reg_count:
raise cv.Invalid(
f"Value type {value_type} needs at least {min_register_count} registers"
)
return value
INTEGER_SENSOR_VALUE_TYPE = {
key: value for key, value in SENSOR_VALUE_TYPE.items() if not key.startswith("FP")
}
CONFIG_SCHEMA = cv.All(
select.SELECT_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(ModbusSelect),
cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController),
cv.Required(CONF_ADDRESS): cv.positive_int,
cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(
INTEGER_SENSOR_VALUE_TYPE
),
cv.Optional(CONF_REGISTER_COUNT): cv.positive_int,
cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int,
cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean,
cv.Required(CONF_OPTIONSMAP): ensure_option_map(),
cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda,
},
),
register_count_value_type_min,
)
async def to_code(config):
value_type = config[CONF_VALUE_TYPE]
reg_count = config.get(CONF_REGISTER_COUNT)
if reg_count is None:
reg_count = TYPE_REGISTER_MAP[value_type]
options_map = config[CONF_OPTIONSMAP]
var = cg.new_Pvariable(
config[CONF_ID],
value_type,
config[CONF_ADDRESS],
reg_count,
config[CONF_SKIP_UPDATES],
config[CONF_FORCE_NEW_RANGE],
list(options_map.values()),
)
await cg.register_component(var, config)
await select.register_select(var, config, options=list(options_map.keys()))
parent = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID])
cg.add(parent.add_sensor_item(var))
cg.add(var.set_parent(parent))
cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE]))
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[
(ModbusSelect.operator("const_ptr"), "item"),
(cg.int64, "x"),
(
cg.std_vector.template(cg.uint8).operator("const").operator("ref"),
"data",
),
],
return_type=cg.optional.template(cg.std_string),
)
cg.add(var.set_template(template_))
if CONF_WRITE_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_WRITE_LAMBDA],
[
(ModbusSelect.operator("const_ptr"), "item"),
(cg.std_string.operator("const").operator("ref"), "x"),
(cg.int64, "value"),
(cg.std_vector.template(cg.uint16).operator("ref"), "payload"),
],
return_type=cg.optional.template(cg.int64),
)
cg.add(var.set_write_template(template_))

View File

@@ -1,86 +0,0 @@
#include "modbus_select.h"
#include "esphome/core/log.h"
namespace esphome {
namespace modbus_controller {
static const char *const TAG = "modbus_controller.select";
void ModbusSelect::dump_config() { LOG_SELECT(TAG, "Modbus Controller Select", this); }
void ModbusSelect::parse_and_publish(const std::vector<uint8_t> &data) {
int64_t value = payload_to_number(data, this->sensor_value_type, this->offset, this->bitmask);
ESP_LOGD(TAG, "New select value %lld from payload", value);
optional<std::string> new_state;
if (this->transform_func_.has_value()) {
auto val = (*this->transform_func_)(this, value, data);
if (val.has_value()) {
new_state = *val;
ESP_LOGV(TAG, "lambda returned option %s", new_state->c_str());
}
}
if (!new_state.has_value()) {
auto map_it = std::find(this->mapping_.cbegin(), this->mapping_.cend(), value);
if (map_it != this->mapping_.cend()) {
size_t idx = std::distance(this->mapping_.cbegin(), map_it);
new_state = this->traits.get_options()[idx];
ESP_LOGV(TAG, "Found option %s for value %lld", new_state->c_str(), value);
} else {
ESP_LOGE(TAG, "No option found for mapping %lld", value);
}
}
if (new_state.has_value()) {
this->publish_state(new_state.value());
}
}
void ModbusSelect::control(const std::string &value) {
auto options = this->traits.get_options();
auto opt_it = std::find(options.cbegin(), options.cend(), value);
size_t idx = std::distance(options.cbegin(), opt_it);
optional<int64_t> mapval = this->mapping_[idx];
ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, value.c_str());
std::vector<uint16_t> data;
if (this->write_transform_func_.has_value()) {
auto val = (*this->write_transform_func_)(this, value, *mapval, data);
if (val.has_value()) {
mapval = *val;
ESP_LOGV(TAG, "write_lambda returned mapping value %lld", *mapval);
} else {
ESP_LOGD(TAG, "Communication handled by write_lambda - exiting control");
return;
}
}
if (data.empty()) {
number_to_payload(data, *mapval, this->sensor_value_type);
} else {
ESP_LOGV(TAG, "Using payload from write lambda");
}
if (data.empty()) {
ESP_LOGW(TAG, "No payload was created for updating select");
return;
}
const uint16_t write_address = this->start_address + this->offset / 2;
ModbusCommandItem write_cmd;
if ((this->register_count == 1) && (!this->use_write_multiple_)) {
write_cmd = ModbusCommandItem::create_write_single_command(parent_, write_address, data[0]);
} else {
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, write_address, this->register_count, data);
}
parent_->queue_command(write_cmd);
}
} // namespace modbus_controller
} // namespace esphome

View File

@@ -1,51 +0,0 @@
#pragma once
#include <utility>
#include "esphome/components/modbus_controller/modbus_controller.h"
#include "esphome/components/select/select.h"
#include "esphome/core/component.h"
namespace esphome {
namespace modbus_controller {
class ModbusSelect : public Component, public select::Select, public SensorItem {
public:
ModbusSelect(SensorValueType sensor_value_type, uint16_t start_address, uint8_t register_count, uint8_t skip_updates,
bool force_new_range, std::vector<int64_t> mapping) {
this->register_type = ModbusRegisterType::HOLDING; // not configurable
this->sensor_value_type = sensor_value_type;
this->start_address = start_address;
this->offset = 0; // not configurable
this->bitmask = 0xFFFFFFFF; // not configurable
this->register_count = register_count;
this->response_bytes = 0; // not configurable
this->skip_updates = skip_updates;
this->force_new_range = force_new_range;
this->mapping_ = std::move(mapping);
}
using transform_func_t =
std::function<optional<std::string>(ModbusSelect *const, int64_t, const std::vector<uint8_t> &)>;
using write_transform_func_t =
std::function<optional<int64_t>(ModbusSelect *const, const std::string &, int64_t, std::vector<uint16_t> &)>;
void set_parent(ModbusController *const parent) { this->parent_ = parent; }
void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; }
void set_template(transform_func_t &&f) { this->transform_func_ = f; }
void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; }
void dump_config() override;
void parse_and_publish(const std::vector<uint8_t> &data) override;
void control(const std::string &value) override;
protected:
std::vector<int64_t> mapping_;
ModbusController *parent_;
bool use_write_multiple_{false};
optional<transform_func_t> transform_func_;
optional<write_transform_func_t> write_transform_func_;
};
} // namespace modbus_controller
} // namespace esphome

View File

@@ -40,8 +40,7 @@ RAW_ENCODING = {
}
CONFIG_SCHEMA = cv.All(
text_sensor.text_sensor_schema()
.extend(cv.COMPONENT_SCHEMA)
text_sensor.TEXT_SENSOR_SCHEMA.extend(cv.COMPONENT_SCHEMA)
.extend(ModbusItemBaseSchema)
.extend(
{

View File

@@ -16,7 +16,6 @@ from esphome.const import (
CONF_DISCOVERY_PREFIX,
CONF_DISCOVERY_RETAIN,
CONF_DISCOVERY_UNIQUE_ID_GENERATOR,
CONF_DISCOVERY_OBJECT_ID_GENERATOR,
CONF_ID,
CONF_KEEPALIVE,
CONF_LEVEL,
@@ -98,7 +97,6 @@ MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent)
MQTTLockComponent = mqtt_ns.class_("MQTTLockComponent", MQTTComponent)
MQTTDiscoveryUniqueIdGenerator = mqtt_ns.enum("MQTTDiscoveryUniqueIdGenerator")
MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS = {
@@ -106,12 +104,6 @@ MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS = {
"mac": MQTTDiscoveryUniqueIdGenerator.MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR,
}
MQTTDiscoveryObjectIdGenerator = mqtt_ns.enum("MQTTDiscoveryObjectIdGenerator")
MQTT_DISCOVERY_OBJECT_ID_GENERATOR_OPTIONS = {
"none": MQTTDiscoveryObjectIdGenerator.MQTT_NONE_OBJECT_ID_GENERATOR,
"device_name": MQTTDiscoveryObjectIdGenerator.MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR,
}
def validate_config(value):
# Populate default fields
@@ -173,9 +165,6 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_DISCOVERY_UNIQUE_ID_GENERATOR, default="legacy"): cv.enum(
MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS
),
cv.Optional(CONF_DISCOVERY_OBJECT_ID_GENERATOR, default="none"): cv.enum(
MQTT_DISCOVERY_OBJECT_ID_GENERATOR_OPTIONS
),
cv.Optional(CONF_USE_ABBREVIATIONS, default=True): cv.boolean,
cv.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
cv.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
@@ -255,27 +244,19 @@ async def to_code(config):
discovery_retain = config[CONF_DISCOVERY_RETAIN]
discovery_prefix = config[CONF_DISCOVERY_PREFIX]
discovery_unique_id_generator = config[CONF_DISCOVERY_UNIQUE_ID_GENERATOR]
discovery_object_id_generator = config[CONF_DISCOVERY_OBJECT_ID_GENERATOR]
if not discovery:
cg.add(var.disable_discovery())
elif discovery == "CLEAN":
cg.add(
var.set_discovery_info(
discovery_prefix,
discovery_unique_id_generator,
discovery_object_id_generator,
discovery_retain,
True,
discovery_prefix, discovery_unique_id_generator, discovery_retain, True
)
)
elif CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
cg.add(
var.set_discovery_info(
discovery_prefix,
discovery_unique_id_generator,
discovery_object_id_generator,
discovery_retain,
discovery_prefix, discovery_unique_id_generator, discovery_retain
)
)

View File

@@ -538,11 +538,9 @@ void MQTTClientComponent::set_birth_message(MQTTMessage &&message) {
void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->shutdown_message_ = std::move(message); }
void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain,
bool clean) {
bool retain, bool clean) {
this->discovery_info_.prefix = std::move(prefix);
this->discovery_info_.unique_id_generator = unique_id_generator;
this->discovery_info_.object_id_generator = object_id_generator;
this->discovery_info_.retain = retain;
this->discovery_info_.clean = clean;
}

View File

@@ -61,12 +61,6 @@ enum MQTTDiscoveryUniqueIdGenerator {
MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR,
};
/// available discovery object_id generators
enum MQTTDiscoveryObjectIdGenerator {
MQTT_NONE_OBJECT_ID_GENERATOR = 0,
MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR,
};
/** Internal struct for MQTT Home Assistant discovery
*
* See <a href="https://www.home-assistant.io/docs/mqtt/discovery/">MQTT Discovery</a>.
@@ -76,7 +70,6 @@ struct MQTTDiscoveryInfo {
bool retain; ///< Whether to retain discovery messages.
bool clean;
MQTTDiscoveryUniqueIdGenerator unique_id_generator;
MQTTDiscoveryObjectIdGenerator object_id_generator;
};
enum MQTTClientState {
@@ -113,11 +106,10 @@ class MQTTClientComponent : public Component {
* See <a href="https://www.home-assistant.io/docs/mqtt/discovery/">MQTT Discovery</a>.
* @param prefix The Home Assistant discovery prefix.
* @param unique_id_generator Controls how UniqueId is generated.
* @param object_id_generator Controls how ObjectId is generated.
* @param retain Whether to retain discovery messages.
*/
void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, bool clean = false);
void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator, bool retain,
bool clean = false);
/// Get Home Assistant discovery info.
const MQTTDiscoveryInfo &get_discovery_info() const;
/// Globally disable Home Assistant discovery.

View File

@@ -111,11 +111,12 @@ bool MQTTComponent::send_discovery_() {
root[MQTT_PAYLOAD_NOT_AVAILABLE] = this->availability_->payload_not_available;
}
const std::string &node_name = App.get_name();
std::string unique_id = this->unique_id();
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
if (!unique_id.empty()) {
root[MQTT_UNIQUE_ID] = unique_id;
} else {
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
char friendly_name_hash[9];
sprintf(friendly_name_hash, "%08x", fnv1_hash(this->friendly_name()));
@@ -128,10 +129,6 @@ bool MQTTComponent::send_discovery_() {
}
}
const std::string &node_name = App.get_name();
if (discovery_info.object_id_generator == MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR)
root[MQTT_OBJECT_ID] = node_name + "_" + this->get_default_object_id_();
JsonObject device_info = root.createNestedObject(MQTT_DEVICE);
device_info[MQTT_DEVICE_IDENTIFIERS] = get_mac_address();
device_info[MQTT_DEVICE_NAME] = node_name;

View File

@@ -107,7 +107,6 @@ constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_stat_t";
constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_stat_tpl";
constexpr const char *const MQTT_MODES = "modes";
constexpr const char *const MQTT_NAME = "name";
constexpr const char *const MQTT_OBJECT_ID = "obj_id";
constexpr const char *const MQTT_OFF_DELAY = "off_dly";
constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_cmd_type";
constexpr const char *const MQTT_OPTIONS = "ops";
@@ -361,7 +360,6 @@ constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_state_topic";
constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_state_template";
constexpr const char *const MQTT_MODES = "modes";
constexpr const char *const MQTT_NAME = "name";
constexpr const char *const MQTT_OBJECT_ID = "object_id";
constexpr const char *const MQTT_OFF_DELAY = "off_delay";
constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_command_type";
constexpr const char *const MQTT_OPTIONS = "options";

View File

@@ -1,55 +0,0 @@
#include "mqtt_lock.h"
#include "esphome/core/log.h"
#include "mqtt_const.h"
#ifdef USE_MQTT
#ifdef USE_LOCK
namespace esphome {
namespace mqtt {
static const char *const TAG = "mqtt.lock";
using namespace esphome::lock;
MQTTLockComponent::MQTTLockComponent(lock::Lock *a_lock) : lock_(a_lock) {}
void MQTTLockComponent::setup() {
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
if (strcasecmp(payload.c_str(), "LOCK") == 0) {
this->lock_->lock();
} else if (strcasecmp(payload.c_str(), "UNLOCK") == 0) {
this->lock_->unlock();
} else if (strcasecmp(payload.c_str(), "OPEN") == 0) {
this->lock_->open();
} else {
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str());
this->status_momentary_warning("state", 5000);
}
});
this->lock_->add_on_state_callback([this]() { this->defer("send", [this]() { this->publish_state(); }); });
}
void MQTTLockComponent::dump_config() {
ESP_LOGCONFIG(TAG, "MQTT Lock '%s': ", this->lock_->get_name().c_str());
LOG_MQTT_COMPONENT(true, true);
}
std::string MQTTLockComponent::component_type() const { return "lock"; }
const EntityBase *MQTTLockComponent::get_entity() const { return this->lock_; }
void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
if (this->lock_->traits.get_assumed_state())
root[MQTT_OPTIMISTIC] = true;
}
bool MQTTLockComponent::send_initial_state() { return this->publish_state(); }
bool MQTTLockComponent::publish_state() {
std::string payload = lock_state_to_string(this->lock_->state);
return this->publish(this->get_state_topic_(), payload);
}
} // namespace mqtt
} // namespace esphome
#endif
#endif // USE_MQTT

View File

@@ -1,41 +0,0 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_MQTT
#ifdef USE_LOCK
#include "esphome/components/lock/lock.h"
#include "mqtt_component.h"
namespace esphome {
namespace mqtt {
class MQTTLockComponent : public mqtt::MQTTComponent {
public:
explicit MQTTLockComponent(lock::Lock *a_lock);
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
void setup() override;
void dump_config() override;
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool publish_state();
protected:
/// "lock" component type.
std::string component_type() const override;
const EntityBase *get_entity() const override;
lock::Lock *lock_;
};
} // namespace mqtt
} // namespace esphome
#endif
#endif // USE_MQTT

View File

@@ -1,8 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor, mqtt
from esphome.const import CONF_QOS, CONF_TOPIC
from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC
from .. import mqtt_subscribe_ns
DEPENDENCIES = ["mqtt"]
@@ -12,23 +11,20 @@ MQTTSubscribeTextSensor = mqtt_subscribe_ns.class_(
"MQTTSubscribeTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = (
text_sensor.text_sensor_schema()
.extend(
{
cv.GenerateID(): cv.declare_id(MQTTSubscribeTextSensor),
cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent),
cv.Required(CONF_TOPIC): cv.subscribe_topic,
cv.Optional(CONF_QOS, default=0): cv.mqtt_qos,
}
)
.extend(cv.COMPONENT_SCHEMA)
)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MQTTSubscribeTextSensor),
cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent),
cv.Required(CONF_TOPIC): cv.subscribe_topic,
cv.Optional(CONF_QOS, default=0): cv.mqtt_qos,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
parent = await cg.get_variable(config[CONF_MQTT_PARENT_ID])
cg.add(var.set_parent(parent))

View File

@@ -17,7 +17,11 @@ NextionTextSensor = nextion_ns.class_(
)
CONFIG_SCHEMA = (
text_sensor.text_sensor_schema(klass=NextionTextSensor)
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(NextionTextSensor),
}
)
.extend(CONFIG_TEXT_COMPONENT_SCHEMA)
.extend(cv.polling_component_schema("never"))
)
@@ -26,8 +30,8 @@ CONFIG_SCHEMA = (
async def to_code(config):
hub = await cg.get_variable(config[CONF_NEXTION_ID])
var = cg.new_Pvariable(config[CONF_ID], hub)
await text_sensor.register_text_sensor(var, config)
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
cg.add(hub.register_textsensor_component(var))

View File

@@ -17,7 +17,7 @@ class ArduinoESP8266OTABackend : public OTABackend {
OTAResponseTypes write(uint8_t *data, size_t len) override;
OTAResponseTypes end() override;
void abort() override;
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
bool supports_compression() override { return true; }
#else
bool supports_compression() override { return false; }

View File

@@ -1,23 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output, lock
from esphome.const import CONF_ID, CONF_OUTPUT
from .. import output_ns
OutputLock = output_ns.class_("OutputLock", lock.Lock, cg.Component)
CONFIG_SCHEMA = lock.LOCK_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(OutputLock),
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await lock.register_lock(var, config)
output_ = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(output_))

View File

@@ -1,22 +0,0 @@
#include "output_lock.h"
#include "esphome/core/log.h"
namespace esphome {
namespace output {
static const char *const TAG = "output.lock";
void OutputLock::dump_config() { LOG_LOCK("", "Output Lock", this); }
void OutputLock::control(const lock::LockCall &call) {
auto state = *call.get_state();
if (state == lock::LOCK_STATE_LOCKED) {
this->output_->turn_on();
} else if (state == lock::LOCK_STATE_UNLOCKED) {
this->output_->turn_off();
}
this->publish_state(state);
}
} // namespace output
} // namespace esphome

View File

@@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/lock/lock.h"
#include "esphome/components/output/binary_output.h"
namespace esphome {
namespace output {
class OutputLock : public lock::Lock, public Component {
public:
void set_output(BinaryOutput *output) { output_ = output; }
float get_setup_priority() const override { return setup_priority::HARDWARE - 1.0f; }
void dump_config() override;
protected:
void control(const lock::LockCall &call) override;
output::BinaryOutput *output_;
};
} // namespace output
} // namespace esphome

View File

@@ -13,7 +13,7 @@ CONF_PIPSOLAR_ID = "pipsolar_id"
pipsolar_ns = cg.esphome_ns.namespace("pipsolar")
PipsolarComponent = pipsolar_ns.class_("Pipsolar", cg.Component)
PIPSOLAR_COMPONENT_SCHEMA = cv.Schema(
PIPSOLAR_COMPONENT_SCHEMA = cv.COMPONENT_SCHEMA.extend(
{
cv.Required(CONF_PIPSOLAR_ID): cv.use_id(PipsolarComponent),
}

View File

@@ -1,7 +1,8 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA
from esphome.const import CONF_ID
from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA, pipsolar_ns
DEPENDENCIES = ["uart"]
@@ -14,6 +15,10 @@ CONF_LAST_QPIWS = "last_qpiws"
CONF_LAST_QT = "last_qt"
CONF_LAST_QMN = "last_qmn"
PipsolarTextSensor = pipsolar_ns.class_(
"PipsolarTextSensor", text_sensor.TextSensor, cg.Component
)
TYPES = [
CONF_DEVICE_MODE,
CONF_LAST_QPIGS,
@@ -26,7 +31,12 @@ TYPES = [
]
CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend(
{cv.Optional(type): text_sensor.text_sensor_schema() for type in TYPES}
{
cv.Optional(type): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{cv.GenerateID(): cv.declare_id(PipsolarTextSensor)}
)
for type in TYPES
}
)
@@ -36,5 +46,7 @@ async def to_code(config):
for type in TYPES:
if type in config:
conf = config[type]
var = await text_sensor.new_text_sensor(conf)
var = cg.new_Pvariable(conf[CONF_ID])
await text_sensor.register_text_sensor(var, conf)
await cg.register_component(var, conf)
cg.add(getattr(paren, f"set_{type}")(var))

View File

@@ -0,0 +1,13 @@
#include "pipsolar_textsensor.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
namespace esphome {
namespace pipsolar {
static const char *const TAG = "pipsolar.text_sensor";
void PipsolarTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Pipsolar TextSensor", this); }
} // namespace pipsolar
} // namespace esphome

View File

@@ -0,0 +1,20 @@
#pragma once
#include "../pipsolar.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace pipsolar {
class Pipsolar;
class PipsolarTextSensor : public Component, public text_sensor::TextSensor {
public:
void set_parent(Pipsolar *parent) { this->parent_ = parent; };
void dump_config() override;
protected:
Pipsolar *parent_;
};
} // namespace pipsolar
} // namespace esphome

View File

@@ -45,12 +45,6 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) {
this->switch_row_(stream, obj);
#endif
#ifdef USE_LOCK
this->lock_type_(stream);
for (auto *obj : App.get_locks())
this->lock_row_(stream, obj);
#endif
req->send(stream);
}
@@ -316,30 +310,6 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch
}
#endif
#ifdef USE_LOCK
void PrometheusHandler::lock_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_lock_value GAUGE\n"));
stream->print(F("#TYPE esphome_lock_failed GAUGE\n"));
}
void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) {
if (obj->is_internal())
return;
stream->print(F("esphome_lock_failed{id=\""));
stream->print(obj->get_object_id().c_str());
stream->print(F("\",name=\""));
stream->print(obj->get_name().c_str());
stream->print(F("\"} 0\n"));
// Data itself
stream->print(F("esphome_lock_value{id=\""));
stream->print(obj->get_object_id().c_str());
stream->print(F("\",name=\""));
stream->print(obj->get_name().c_str());
stream->print(F("\"} "));
stream->print(obj->state);
stream->print('\n');
}
#endif
} // namespace prometheus
} // namespace esphome

View File

@@ -76,13 +76,6 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
void switch_row_(AsyncResponseStream *stream, switch_::Switch *obj);
#endif
#ifdef USE_LOCK
/// Return the type for prometheus
void lock_type_(AsyncResponseStream *stream);
/// Return the lock Values state as prometheus data point
void lock_row_(AsyncResponseStream *stream, lock::Lock *obj);
#endif
web_server_base::WebServerBase *base_;
};

View File

@@ -1,23 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker
from esphome.const import CONF_ID
DEPENDENCIES = ["esp32_ble_tracker"]
CODEOWNERS = ["@jeffeb3"]
radon_eye_ble_ns = cg.esphome_ns.namespace("radon_eye_ble")
RadonEyeListener = radon_eye_ble_ns.class_(
"RadonEyeListener", esp32_ble_tracker.ESPBTDeviceListener
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(RadonEyeListener),
}
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield esp32_ble_tracker.register_ble_device(var, config)

View File

@@ -1,25 +0,0 @@
#include "radon_eye_listener.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace radon_eye_ble {
static const char *const TAG = "radon_eye_ble";
bool RadonEyeListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (not device.get_name().empty()) {
if (device.get_name().rfind("FR:R20:SN", 0) == 0) {
// This is an RD200, I think
ESP_LOGD(TAG, "Found Radon Eye RD200 device Name: %s (MAC: %s)", device.get_name().c_str(),
device.address_str().c_str());
}
}
return false;
}
} // namespace radon_eye_ble
} // namespace esphome
#endif

View File

@@ -1,19 +0,0 @@
#pragma once
#ifdef USE_ESP32
#include "esphome/core/component.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
namespace esphome {
namespace radon_eye_ble {
class RadonEyeListener : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
} // namespace radon_eye_ble
} // namespace esphome
#endif

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@jeffeb3"]

View File

@@ -1,179 +0,0 @@
#include "radon_eye_rd200.h"
#ifdef USE_ESP32
namespace esphome {
namespace radon_eye_rd200 {
static const char *const TAG = "radon_eye_rd200";
void RadonEyeRD200::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->read_handle_ = 0;
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_read_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor read characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_read_characteristic_uuid_.to_string().c_str());
break;
}
this->read_handle_ = chr->handle;
// Write a 0x50 to the write characteristic.
auto *write_chr = this->parent()->get_characteristic(service_uuid_, sensors_write_characteristic_uuid_);
if (write_chr == nullptr) {
ESP_LOGW(TAG, "No sensor write characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_read_characteristic_uuid_.to_string().c_str());
break;
}
this->write_handle_ = write_chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
write_query_message_();
request_read_values_();
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->conn_id)
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
break;
}
if (param->read.handle == this->read_handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
void RadonEyeRD200::read_sensors_(uint8_t *value, uint16_t value_len) {
if (value_len < 20) {
ESP_LOGD(TAG, "Invalid read");
return;
}
// Example data
// [13:08:47][D][radon_eye_rd200:107]: result bytes: 5010 85EBB940 00000000 00000000 2200 2500 0000
ESP_LOGV(TAG, "result bytes: %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X %02X%02X %02X%02X",
value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], value[8], value[9],
value[10], value[11], value[12], value[13], value[14], value[15], value[16], value[17], value[18],
value[19]);
if (value[0] != 0x50) {
// This isn't a sensor reading.
return;
}
// Convert from pCi/L to Bq/m³
constexpr float convert_to_bwpm3 = 37.0;
RadonValue radon_value;
radon_value.chars[0] = value[2];
radon_value.chars[1] = value[3];
radon_value.chars[2] = value[4];
radon_value.chars[3] = value[5];
float radon_now = radon_value.number * convert_to_bwpm3;
if (is_valid_radon_value_(radon_now)) {
radon_sensor_->publish_state(radon_now);
}
radon_value.chars[0] = value[6];
radon_value.chars[1] = value[7];
radon_value.chars[2] = value[8];
radon_value.chars[3] = value[9];
float radon_day = radon_value.number * convert_to_bwpm3;
radon_value.chars[0] = value[10];
radon_value.chars[1] = value[11];
radon_value.chars[2] = value[12];
radon_value.chars[3] = value[13];
float radon_month = radon_value.number * convert_to_bwpm3;
if (is_valid_radon_value_(radon_month)) {
ESP_LOGV(TAG, "Radon Long Term based on month");
radon_long_term_sensor_->publish_state(radon_month);
} else if (is_valid_radon_value_(radon_day)) {
ESP_LOGV(TAG, "Radon Long Term based on day");
radon_long_term_sensor_->publish_state(radon_day);
}
ESP_LOGV(TAG, " Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f", radon_now, radon_day, radon_month);
ESP_LOGV(TAG, " Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f", radon_now / convert_to_bwpm3,
radon_day / convert_to_bwpm3, radon_month / convert_to_bwpm3);
// This instance must not stay connected
// so other clients can connect to it (e.g. the
// mobile app).
parent()->set_enabled(false);
}
bool RadonEyeRD200::is_valid_radon_value_(float radon) { return radon > 0.0 and radon < 37000; }
void RadonEyeRD200::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
void RadonEyeRD200::write_query_message_() {
ESP_LOGV(TAG, "writing 0x50 to write service");
int request = 0x50;
auto status = esp_ble_gattc_write_char_descr(this->parent()->gattc_if, this->parent()->conn_id, this->write_handle_,
sizeof(request), (uint8_t *) &request, ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending write request for sensor, status=%d", status);
}
}
void RadonEyeRD200::request_read_values_() {
auto status = esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->read_handle_,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
}
void RadonEyeRD200::dump_config() {
LOG_SENSOR(" ", "Radon", this->radon_sensor_);
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
}
RadonEyeRD200::RadonEyeRD200()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_write_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(WRITE_CHARACTERISTIC_UUID)),
sensors_read_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(READ_CHARACTERISTIC_UUID)) {}
} // namespace radon_eye_rd200
} // namespace esphome
#endif // USE_ESP32

View File

@@ -1,59 +0,0 @@
#pragma once
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace radon_eye_rd200 {
static const char *const SERVICE_UUID = "00001523-1212-efde-1523-785feabcd123";
static const char *const WRITE_CHARACTERISTIC_UUID = "00001524-1212-efde-1523-785feabcd123";
static const char *const READ_CHARACTERISTIC_UUID = "00001525-1212-efde-1523-785feabcd123";
class RadonEyeRD200 : public PollingComponent, public ble_client::BLEClientNode {
public:
RadonEyeRD200();
void dump_config() override;
void update() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
protected:
bool is_valid_radon_value_(float radon);
void read_sensors_(uint8_t *value, uint16_t value_len);
void write_query_message_();
void request_read_values_();
sensor::Sensor *radon_sensor_{nullptr};
sensor::Sensor *radon_long_term_sensor_{nullptr};
uint16_t read_handle_;
uint16_t write_handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_write_characteristic_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_read_characteristic_uuid_;
union RadonValue {
char chars[4];
float number;
};
};
} // namespace radon_eye_rd200
} // namespace esphome
#endif // USE_ESP32

View File

@@ -1,55 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import (
STATE_CLASS_MEASUREMENT,
UNIT_BECQUEREL_PER_CUBIC_METER,
CONF_ID,
CONF_RADON,
CONF_RADON_LONG_TERM,
ICON_RADIOACTIVE,
)
DEPENDENCIES = ["ble_client"]
radon_eye_rd200_ns = cg.esphome_ns.namespace("radon_eye_rd200")
RadonEyeRD200 = radon_eye_rd200_ns.class_(
"RadonEyeRD200", cg.PollingComponent, ble_client.BLEClientNode
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(RadonEyeRD200),
cv.Optional(CONF_RADON): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
if CONF_RADON in config:
sens = await sensor.new_sensor(config[CONF_RADON])
cg.add(var.set_radon(sens))
if CONF_RADON_LONG_TERM in config:
sens = await sensor.new_sensor(config[CONF_RADON_LONG_TERM])
cg.add(var.set_radon_long_term(sens))

View File

@@ -138,8 +138,6 @@ void RotaryEncoderSensor::setup() {
initial_value = 0;
break;
}
initial_value = clamp(initial_value, this->store_.min_value, this->store_.max_value);
this->store_.counter = initial_value;
this->store_.last_read = initial_value;

View File

@@ -1,5 +1,6 @@
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.core import CORE
CODEOWNERS = ["@esphome/core"]
@@ -26,3 +27,6 @@ async def to_code(config):
cg.add_define("USE_SOCKET_IMPL_LWIP_TCP")
elif impl == IMPLEMENTATION_BSD_SOCKETS:
cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS")
if CORE.target_platform in ["esp8266", "esp32"]:
cg.add_define("USE_SOCKET_HAS_LWIP")

View File

@@ -48,6 +48,43 @@ class BSDSocketImpl : public Socket {
return make_unique<BSDSocketImpl>(fd);
}
int bind(const struct sockaddr *addr, socklen_t addrlen) override { return ::bind(fd_, addr, addrlen); }
int connect(const struct sockaddr *addr, socklen_t addrlen) override { return ::connect(fd_, addr, addrlen); }
int connect_finished() override {
fd_set wfds;
struct timeval tv;
FD_ZERO(&wfds);
FD_SET(fd_, &wfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
int retval = ::select(fd_ + 1, nullptr, &wfds, nullptr, &tv);
if (retval == -1) {
// reuse errno
return -1;
}
if (retval == 0) {
// timeout, not writable yet
errno = EINPROGRESS;
return -1;
}
if (!FD_ISSET(fd_, &wfds)) {
errno = ECONNREFUSED;
return -1;
}
int so_error;
socklen_t len = sizeof(so_error);
int ret = this->getsockopt(SOL_SOCKET, SO_ERROR, &so_error, &len);
if (ret == -1) {
// reuse errno
return -1;
}
if (so_error == 0) {
return 0;
}
errno = ECONNREFUSED;
return -1;
}
int close() override {
int ret = ::close(fd_);
closed_ = true;

View File

@@ -0,0 +1,34 @@
#pragma once
#include <memory>
#include "headers.h"
namespace esphome {
namespace socket {
struct GetaddrinfoFuture {
public:
virtual ~GetaddrinfoFuture() = default;
// returns true when the request has completed (successfully or with an error)
virtual bool completed() = 0;
/**
* @brief Fetch the completed result into res.
*
* Should only be called after completed() returned true.
* Make sure to call freeaddrinfo() to free the addrinfo storage
* when it's no longer needed.
*
* @return See posix getaddrinfo() return values.
*/
virtual int fetch_result(struct addrinfo **res) = 0;
};
std::unique_ptr<GetaddrinfoFuture> getaddrinfo_async(const char *node, const char *service,
const struct addrinfo *hints);
} // namespace socket
} // namespace esphome
#ifdef USE_ESP8266
void freeaddrinfo(struct addrinfo *ai);
const char *gai_strerror(int errcode);
#endif

View File

@@ -8,6 +8,7 @@
#define LWIP_INTERNAL
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include <cerrno>
#include <cstdint>
#include <sys/types.h>
@@ -107,6 +108,34 @@ struct iovec {
#define ESPHOME_INADDR_NONE INADDR_NONE
#endif
#ifndef EAI_FAIL
#define EAI_BADFLAGS (-1)
#define EAI_NONAME (-2)
#define EAI_AGAIN (-3)
#define EAI_FAIL (-4)
#define EAI_FAMILY (-6)
#define EAI_SOCKTYPE (-7)
#define EAI_SERVICE (-8)
#define EAI_MEMORY (-10)
#define EAI_SYSTEM (-11)
#define EAI_OVERFLOW (-12)
#endif // !EAI_FAIL
#ifndef IPPROTO_UDP
#define IPPROTO_UDP 17
#endif
struct addrinfo { // NOLINT(readability-identifier-naming)
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
#endif // USE_SOCKET_IMPL_LWIP_TCP
#ifdef USE_SOCKET_IMPL_BSD_SOCKETS
@@ -118,6 +147,7 @@ struct iovec {
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <netdb.h>
#ifdef USE_ARDUINO
// arduino-esp32 declares a global var called INADDR_NONE which is replaced

View File

@@ -0,0 +1,197 @@
#include "getaddrinfo.h"
#include "esphome/core/defines.h"
#ifdef USE_SOCKET_HAS_LWIP
#include <utility>
#include "lwip/dns.h"
#include "lwip/ip_addr.h"
#include "lwip/netdb.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace socket {
static const char *const TAG = "socket.lwipgetaddrinfo";
struct LwipDNSResult {
bool completed;
bool error;
ip_addr_t ipaddr;
};
struct LwipDNSCallbackArg {
std::weak_ptr<LwipDNSResult> res;
};
void lwip_dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
LwipDNSCallbackArg *arg = reinterpret_cast<LwipDNSCallbackArg *>(callback_arg);
{
std::shared_ptr<LwipDNSResult> result = arg->res.lock();
if (result) {
if (ipaddr == nullptr) {
result->error = true;
} else {
result->error = false;
ip_addr_copy(result->ipaddr, *ipaddr);
}
result->completed = true;
}
}
delete arg; // NOLINT(cppcoreguidelines-owning-memory)
}
class LwipGetaddrinfoFuture : public GetaddrinfoFuture {
public:
LwipGetaddrinfoFuture(std::shared_ptr<LwipDNSResult> result, int hint_ai_socktype, int hint_ai_protocol,
uint16_t portno)
: result_(std::move(result)),
hint_ai_socktype_(hint_ai_socktype),
hint_ai_protocol_(hint_ai_protocol),
portno_(portno) {}
~LwipGetaddrinfoFuture() override = default;
bool completed() override { return result_->completed; }
int fetch_result(struct addrinfo **res) override {
if (res == nullptr)
return EAI_FAIL;
*res = nullptr;
if (!result_->completed)
return EAI_FAIL;
if (result_->error)
return EAI_FAIL;
size_t alloc_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
void *storage = malloc(alloc_size);
memset(storage, 0, alloc_size);
struct addrinfo *ai = reinterpret_cast<struct addrinfo *>(storage);
struct sockaddr_storage *sa = reinterpret_cast<struct sockaddr_storage *>(ai + 1);
bool isipv6 = IP_IS_V6(result_->ipaddr);
bool istcp = true;
if ((hint_ai_socktype_ != 0 && hint_ai_socktype_ == SOCK_DGRAM) ||
(hint_ai_protocol_ != 0 && hint_ai_protocol_ == IPPROTO_UDP)) {
istcp = false;
}
ai->ai_family = isipv6 ? AF_INET6 : AF_INET;
ai->ai_socktype = istcp ? SOCK_STREAM : SOCK_DGRAM;
ai->ai_protocol = istcp ? IPPROTO_TCP : IPPROTO_UDP;
if (isipv6) {
#if LWIP_IPV6
struct sockaddr_in6 *sa6 = reinterpret_cast<struct sockaddr_in6 *>(sa);
inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&result_->ipaddr)) sa6->sin6_family = AF_INET6;
sa6->sin6_len = sizeof(struct sockaddr_in6);
sa6->sin6_port = htons(portno_);
#endif // LWIP_IPV6
} else {
struct sockaddr_in *sa4 = reinterpret_cast<struct sockaddr_in *>(sa);
inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&result_->ipaddr));
sa4->sin_family = AF_INET;
sa4->sin_len = sizeof(struct sockaddr_in);
sa4->sin_port = htons(portno_);
}
ai->ai_addrlen = sizeof(struct sockaddr_storage);
ai->ai_addr = reinterpret_cast<struct sockaddr *>(sa);
*res = ai;
return 0;
}
protected:
std::shared_ptr<LwipDNSResult> result_;
int hint_ai_socktype_;
int hint_ai_protocol_;
uint16_t portno_;
};
std::unique_ptr<GetaddrinfoFuture> getaddrinfo_async(const char *node, const char *service,
const struct addrinfo *hints) {
std::shared_ptr<LwipDNSResult> result = std::make_shared<LwipDNSResult>();
result->completed = false;
uint16_t portno = 0;
if (service != nullptr) {
optional<uint16_t> i = parse_number<uint16_t>(service);
if (!i.has_value()) {
result->completed = true;
result->error = true;
return std::unique_ptr<GetaddrinfoFuture>{new LwipGetaddrinfoFuture(result, 0, 0, 0)};
}
portno = *i;
}
int hint_ai_socktype = 0, hint_ai_protocol = 0;
uint8_t dns_addrtype = LWIP_DNS_ADDRTYPE_DEFAULT;
if (hints != nullptr) {
hint_ai_socktype = hints->ai_socktype;
hint_ai_protocol = hints->ai_protocol;
if (hints->ai_family == AF_INET) {
dns_addrtype = LWIP_DNS_ADDRTYPE_IPV4;
} else if (hints->ai_family == AF_INET6) {
dns_addrtype = LWIP_DNS_ADDRTYPE_IPV6;
}
}
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
LwipDNSCallbackArg *callback_arg = new LwipDNSCallbackArg;
callback_arg->res = result;
ip_addr_t immediate_result;
err_t err = dns_gethostbyname_addrtype(node, &immediate_result, lwip_dns_callback, callback_arg, dns_addrtype);
if (err == ERR_OK) {
// immediate result
result->completed = true;
result->error = false;
ip_addr_copy(result->ipaddr, immediate_result);
// callback won't be called
delete callback_arg; // NOLINT(cppcoreguidelines-owning-memory)
} else if (err == ERR_INPROGRESS) {
// result notified via callback
} else {
// error
result->completed = true;
result->error = true;
// callback won't be called
delete callback_arg; // NOLINT(cppcoreguidelines-owning-memory)
}
return std::unique_ptr<GetaddrinfoFuture>{
new LwipGetaddrinfoFuture(result, hint_ai_socktype, hint_ai_protocol, portno)};
}
} // namespace socket
} // namespace esphome
#ifdef USE_ESP8266
void freeaddrinfo(struct addrinfo *ai) {
while (ai != nullptr) {
struct addrinfo *next = ai->ai_next;
delete ai; // NOLINT(cppcoreguidelines-owning-memory)
ai = next;
}
}
const char *gai_strerror(int errcode) {
switch (errcode) {
case EAI_BADFLAGS: return "badflags";
case EAI_NONAME: return "noname";
case EAI_AGAIN: return "again";
case EAI_FAMILY: return "family";
case EAI_SOCKTYPE: return "socktype";
case EAI_SERVICE: return "service";
case EAI_MEMORY: return "memory";
case EAI_SYSTEM: return "system";
case EAI_OVERFLOW: return "overflow";
default: return "unknown";
}
}
#endif
#endif // USE_SOCKET_HAS_LWIP

View File

@@ -69,7 +69,7 @@ class LWIPRawImpl : public Socket {
}
if (name == nullptr) {
errno = EINVAL;
return 0;
return -1;
}
ip_addr_t ip;
in_port_t port;
@@ -126,6 +126,76 @@ class LWIPRawImpl : public Socket {
}
return 0;
}
int connect(const struct sockaddr *addr, socklen_t addrlen) override {
if (pcb_ == nullptr) {
errno = EBADF;
return -1;
}
if (addr == nullptr) {
errno = EINVAL;
return -1;
}
if (connecting_) {
errno = EALREADY;
return -1;
}
ip_addr_t ipaddr;
uint16_t port;
if (addr->sa_family == AF_INET) {
const struct sockaddr_in *sa4 = reinterpret_cast<const struct sockaddr_in *>(addr);
inet_addr_to_ip4addr(ip_2_ip4(&ipaddr), &sa4->sin_addr);
#if LWIP_IPV4 && LWIP_IPV6
ipaddr.type = IPADDR_TYPE_V4;
#endif
port = ntohs(sa4->sin_port);
#if LWIP_IPV6
} else if (addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *sa6 = reinterpret_cast<const struct sockaddr_in6 *>(addr);
inet6_addr_to_ip6addr(ip_2_ip6(&ipaddr), &sa6->sin_addr);
ipaddr.type = IPADDR_TYPE_V6;
port = ntohs(sa6->sin_port);
#endif // LWIP_IPV6
} else {
errno = EAFNOSUPPORT;
return -1;
}
connecting_ = true;
connected_ = false;
connect_error_ = false;
LWIP_LOG("tcp_connect(%u)", port);
err_t err = tcp_connect(pcb_, &ipaddr, port, LWIPRawImpl::s_connected_fn);
if (err == ERR_VAL) {
errno = EINVAL;
return -1;
}
if (err != ERR_OK) {
errno = EIO;
return -1;
}
errno = EINPROGRESS;
return -1;
}
int connect_finished() override {
if (connected_) {
return 0;
}
if (connect_error_) {
errno = ECONNREFUSED;
return -1;
}
if (connecting_) {
errno = EINPROGRESS;
return -1;
}
// no connect started
errno = EALREADY;
return -1;
}
int close() override {
if (pcb_ == nullptr) {
errno = ECONNRESET;
@@ -369,9 +439,10 @@ class LWIPRawImpl : public Socket {
for (int i = 0; i < iovcnt; i++) {
ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
if (err == -1) {
if (ret != 0)
if (ret != 0) {
// if we already read some don't return an error
break;
}
return err;
}
ret += err;
@@ -433,9 +504,10 @@ class LWIPRawImpl : public Socket {
ssize_t written = internal_write(buf, len);
if (written == -1)
return -1;
if (written == 0)
if (written == 0) {
// no need to output if nothing written
return 0;
}
if (nodelay_) {
int err = internal_output();
if (err == -1)
@@ -448,18 +520,20 @@ class LWIPRawImpl : public Socket {
for (int i = 0; i < iovcnt; i++) {
ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
if (err == -1) {
if (written != 0)
if (written != 0) {
// if we already read some don't return an error
break;
}
return err;
}
written += err;
if ((size_t) err != iov[i].iov_len)
break;
}
if (written == 0)
if (written == 0) {
// no need to output if nothing written
return 0;
}
if (nodelay_) {
int err = internal_output();
if (err == -1)
@@ -524,6 +598,18 @@ class LWIPRawImpl : public Socket {
}
return ERR_OK;
}
err_t connected_fn(err_t err) {
LWIP_LOG("connected(err=%d)", err);
if (err != ERR_OK) {
connected_ = false;
connect_error_ = false;
} else {
connected_ = true;
connect_error_ = true;
}
connecting_ = false;
return ERR_OK;
}
static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
@@ -540,6 +626,11 @@ class LWIPRawImpl : public Socket {
return arg_this->recv_fn(pb, err);
}
static err_t s_connected_fn(void *arg, struct tcp_pcb *pcb, err_t err) {
LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
return arg_this->connected_fn(err);
}
protected:
int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
if (family_ == AF_INET) {
@@ -590,6 +681,9 @@ class LWIPRawImpl : public Socket {
// instead use it for determining whether to call lwip_output
bool nodelay_ = false;
sa_family_t family_ = 0;
bool connecting_ = false;
bool connected_ = false;
bool connect_error_ = false;
};
std::unique_ptr<Socket> socket(int domain, int type, int protocol) {

View File

@@ -7,7 +7,7 @@ namespace esphome {
namespace socket {
std::unique_ptr<Socket> socket_ip(int type, int protocol) {
#if LWIP_IPV6
#ifdef USE_SOCKET_IPV6
return socket(AF_INET6, type, protocol);
#else
return socket(AF_INET, type, protocol);
@@ -15,7 +15,7 @@ std::unique_ptr<Socket> socket_ip(int type, int protocol) {
}
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) {
#if LWIP_IPV6
#if USE_SOCKET_IPV6
if (addrlen < sizeof(sockaddr_in6)) {
errno = EINVAL;
return 0;

View File

@@ -5,6 +5,12 @@
#include "headers.h"
#include "esphome/core/optional.h"
#ifdef USE_SOCKET_IMPL_LWIP_TCP
#if LWIP_IPV6
#define USE_SOCKET_IPV6
#endif
#endif
namespace esphome {
namespace socket {
@@ -17,10 +23,17 @@ class Socket {
virtual std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) = 0;
virtual int bind(const struct sockaddr *addr, socklen_t addrlen) = 0;
virtual int connect(const struct sockaddr *addr, socklen_t addrlen) = 0;
/**
* @brief Helper to check if a socket connect() that was EINPROGRESS is now finished.
*
* If the connect finnished successfully, returns 0.
* If it's still in progress, returns -1 and sets errno to EINPROGRESS.
* Other errors result in return code -1 and errno like in blocking connect().
*/
virtual int connect_finished() = 0;
virtual int close() = 0;
// not supported yet:
// virtual int connect(const std::string &address) = 0;
// virtual int connect(const struct sockaddr *addr, socklen_t addrlen) = 0;
virtual int shutdown(int how) = 0;
virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;

View File

@@ -0,0 +1,99 @@
#include "getaddrinfo.h"
#include "esphome/core/defines.h"
#ifndef USE_SOCKET_HAS_LWIP
#include <thread>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace socket {
static const char *const TAG = "socket.threadgetaddrinfo";
struct ThreadGetaddrinfoResult {
bool completed;
int return_code;
struct addrinfo *res;
};
class ThreadGetaddrinfoFuture : public GetaddrinfoFuture {
public:
ThreadGetaddrinfoFuture(std::shared_ptr<ThreadGetaddrinfoResult> result) : result_(result) {}
~ThreadGetaddrinfoFuture() override = default;
bool completed() override { return result_->completed; }
int fetch_result(struct addrinfo **res) {
if (res == nullptr)
return EAI_FAIL;
*res = nullptr;
if (!result_->completed)
return EAI_FAIL;
if (result_->return_code != 0)
return result_->return_code;
*res = result_->res;
return 0;
}
protected:
std::shared_ptr<ThreadGetaddrinfoResult> result_;
};
void worker(std::shared_ptr<ThreadGetaddrinfoResult> result, const char *node, const char *service,
const struct addrinfo *hints) {
result->return_code = getaddrinfo(node, service, hints, &result->res);
result->completed = true;
if (hints != nullptr) {
delete hints->ai_addr;
delete hints->ai_canonname;
delete hints;
}
delete node;
delete service;
}
std::unique_ptr<GetaddrinfoFuture> getaddrinfo_async(const char *node, const char *service,
const struct addrinfo *hints) {
std::shared_ptr<ThreadGetaddrinfoResult> result = std::make_shared<ThreadGetaddrinfoResult>();
result->completed = false;
struct addrinfo *hints_copy = nullptr;
if (hints != nullptr) {
hints_copy = new struct addrinfo;
hints_copy->ai_flags = hints->ai_flags;
hints_copy->ai_family = hints->ai_family;
hints_copy->ai_socktype = hints->ai_socktype;
hints_copy->ai_protocol = hints->ai_protocol;
hints_copy->ai_addrlen = hints->ai_addrlen;
if (ai->ai_addr != nullptr) {
hints_copy->ai_addr = malloc(hints->ai_addrlen);
memcpy(hints_copy->ai_addr, hints->ai_addr, hints->ai_addrlen);
}
if (ai->ai_canonname != nullptr) {
hints_copy->ai_canonname = strdup(hints->ai_canonname);
}
hints_copy->ai_next = nullptr;
}
const char *node_copy = nullptr, *service_copy = nullptr;
if (node != nullptr)
node_copy = strdup(node);
if (service != nullptr)
service_copy = strdup(service);
std::thread thread(worker, result, node_copy, service_copy, hints_copy);
thread.detach();
return std::unique_ptr<GetaddrinfoFuture>{new ThreadGetaddrinfoFuture(result)};
}
} // namespace socket
} // namespace esphome
#endif // !USE_SOCKET_HAS_LWIP

View File

@@ -6,6 +6,7 @@ from esphome.const import (
ICON_WEATHER_SUNSET_DOWN,
ICON_WEATHER_SUNSET_UP,
CONF_TYPE,
CONF_ID,
CONF_FORMAT,
)
from .. import sun_ns, CONF_SUN_ID, Sun, CONF_ELEVATION, elevation, DEFAULT_ELEVATION
@@ -32,8 +33,7 @@ def validate_optional_icon(config):
CONFIG_SCHEMA = cv.All(
text_sensor.text_sensor_schema()
.extend(
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(SunTextSensor),
cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun),
@@ -41,15 +41,15 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation,
cv.Optional(CONF_FORMAT, default="%X"): cv.string_strict,
}
)
.extend(cv.polling_component_schema("60s")),
).extend(cv.polling_component_schema("60s")),
validate_optional_icon,
)
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
paren = await cg.get_variable(config[CONF_SUN_ID])
cg.add(var.set_parent(paren))

View File

@@ -4,27 +4,18 @@ from esphome import automation
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ID,
CONF_INVERTED,
CONF_MQTT_ID,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_TRIGGER_ID,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_OUTLET,
DEVICE_CLASS_SWITCH,
CONF_MQTT_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
DEVICE_CLASSES = [
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_OUTLET,
DEVICE_CLASS_SWITCH,
]
switch_ns = cg.esphome_ns.namespace("switch_")
Switch = switch_ns.class_("Switch", cg.EntityBase)
@@ -60,7 +51,6 @@ SWITCH_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).e
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOffTrigger),
}
),
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
}
)
@@ -81,9 +71,6 @@ async def setup_switch_core_(var, config):
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
await mqtt.register_mqtt_component(mqtt_, config)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
async def register_switch(var, config):
if not CORE.has_id(config[CONF_ID]):

View File

@@ -46,12 +46,5 @@ void Switch::set_inverted(bool inverted) { this->inverted_ = inverted; }
uint32_t Switch::hash_base() { return 3129890955UL; }
bool Switch::is_inverted() const { return this->inverted_; }
std::string Switch::get_device_class() {
if (this->device_class_.has_value())
return *this->device_class_;
return "";
}
void Switch::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
} // namespace switch_
} // namespace esphome

View File

@@ -20,9 +20,6 @@ namespace switch_ {
if ((obj)->is_inverted()) { \
ESP_LOGCONFIG(TAG, "%s Inverted: YES", prefix); \
} \
if (!(obj)->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
} \
}
/** Base class for all switches.
@@ -91,11 +88,6 @@ class Switch : public EntityBase {
bool is_inverted() const;
/// Get the device class for this switch.
std::string get_device_class();
/// Set the Home Assistant device class for this switch.
void set_device_class(const std::string &device_class);
protected:
/** Write the given state to hardware. You should implement this
* abstract method if you want to create your own switch.
@@ -113,7 +105,6 @@ class Switch : public EntityBase {
bool inverted_{false};
Deduplicator<bool> publish_dedup_;
ESPPreferenceObject rtc_;
optional<std::string> device_class_;
};
} // namespace switch_

View File

@@ -7,20 +7,9 @@ CODEOWNERS = ["@0hax"]
teleinfo_ns = cg.esphome_ns.namespace("teleinfo")
TeleInfo = teleinfo_ns.class_("TeleInfo", cg.PollingComponent, uart.UARTDevice)
CONF_TELEINFO_ID = "teleinfo_id"
CONF_TAG_NAME = "tag_name"
CONF_HISTORICAL_MODE = "historical_mode"
TELEINFO_LISTENER_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_TELEINFO_ID): cv.use_id(TeleInfo),
cv.Required(CONF_TAG_NAME): cv.string,
}
)
CONFIG_SCHEMA = (
cv.Schema(
{

View File

@@ -3,22 +3,20 @@ import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ID, ICON_FLASH, UNIT_WATT_HOURS
from .. import (
CONF_TAG_NAME,
TELEINFO_LISTENER_SCHEMA,
teleinfo_ns,
CONF_TELEINFO_ID,
)
from .. import teleinfo_ns, TeleInfo, CONF_TELEINFO_ID
CONF_TAG_NAME = "tag_name"
TeleInfoSensor = teleinfo_ns.class_("TeleInfoSensor", sensor.Sensor, cg.Component)
CONFIG_SCHEMA = (
sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS, icon=ICON_FLASH, accuracy_decimals=0
)
.extend({cv.GenerateID(): cv.declare_id(TeleInfoSensor)})
.extend(TELEINFO_LISTENER_SCHEMA)
CONFIG_SCHEMA = sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS, icon=ICON_FLASH, accuracy_decimals=0
).extend(
{
cv.GenerateID(): cv.declare_id(TeleInfoSensor),
cv.GenerateID(CONF_TELEINFO_ID): cv.use_id(TeleInfo),
cv.Required(CONF_TAG_NAME): cv.string,
}
)

View File

@@ -1,15 +1,22 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ID
from .. import CONF_TAG_NAME, TELEINFO_LISTENER_SCHEMA, teleinfo_ns, CONF_TELEINFO_ID
from .. import teleinfo_ns, TeleInfo, CONF_TELEINFO_ID
CONF_TAG_NAME = "tag_name"
TeleInfoTextSensor = teleinfo_ns.class_(
"TeleInfoTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = text_sensor.text_sensor_schema(klass=TeleInfoTextSensor).extend(
TELEINFO_LISTENER_SCHEMA
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TeleInfoTextSensor),
cv.GenerateID(CONF_TELEINFO_ID): cv.use_id(TeleInfo),
cv.Required(CONF_TAG_NAME): cv.string,
}
)

View File

@@ -1,103 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import lock
from esphome.const import (
CONF_ASSUMED_STATE,
CONF_ID,
CONF_LAMBDA,
CONF_LOCK_ACTION,
CONF_OPEN_ACTION,
CONF_OPTIMISTIC,
CONF_STATE,
CONF_UNLOCK_ACTION,
)
from .. import template_ns
TemplateLock = template_ns.class_("TemplateLock", lock.Lock, cg.Component)
LockState = lock.lock_ns.enum("LockState")
LOCK_STATES = {
"LOCKED": LockState.LOCK_STATE_LOCKED,
"UNLOCKED": LockState.LOCK_STATE_UNLOCKED,
"JAMMED": LockState.LOCK_STATE_JAMMED,
"LOCKING": LockState.LOCK_STATE_LOCKING,
"UNLOCKING": LockState.LOCK_STATE_UNLOCKING,
}
validate_lock_state = cv.enum(LOCK_STATES, upper=True)
def validate(config):
if not config[CONF_OPTIMISTIC] and (
CONF_LOCK_ACTION not in config or CONF_UNLOCK_ACTION not in config
):
raise cv.Invalid(
"Either optimistic mode must be enabled, or lock_action and unlock_action must be set, "
"to handle the lock being changed."
)
return config
CONFIG_SCHEMA = cv.All(
lock.LOCK_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TemplateLock),
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
cv.Optional(CONF_UNLOCK_ACTION): automation.validate_automation(
single=True
),
cv.Optional(CONF_LOCK_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
}
).extend(cv.COMPONENT_SCHEMA),
validate,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await lock.register_lock(var, config)
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.optional.template(LockState)
)
cg.add(var.set_state_lambda(template_))
if CONF_UNLOCK_ACTION in config:
await automation.build_automation(
var.get_unlock_trigger(), [], config[CONF_UNLOCK_ACTION]
)
if CONF_LOCK_ACTION in config:
await automation.build_automation(
var.get_lock_trigger(), [], config[CONF_LOCK_ACTION]
)
if CONF_OPEN_ACTION in config:
await automation.build_automation(
var.get_open_trigger(), [], config[CONF_OPEN_ACTION]
)
cg.add(var.traits.set_supports_open(CONF_OPEN_ACTION in config))
cg.add(var.traits.set_assumed_state(config[CONF_ASSUMED_STATE]))
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
@automation.register_action(
"lock.template.publish",
lock.LockPublishAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(lock.Lock),
cv.Required(CONF_STATE): cv.templatable(validate_lock_state),
}
),
)
async def lock_template_publish_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_STATE], args, LockState)
cg.add(var.set_state(template_))
return var

View File

@@ -1,59 +0,0 @@
#include "template_lock.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
using namespace esphome::lock;
static const char *const TAG = "template.lock";
TemplateLock::TemplateLock()
: lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {}
void TemplateLock::loop() {
if (!this->f_.has_value())
return;
auto val = (*this->f_)();
if (!val.has_value())
return;
this->publish_state(*val);
}
void TemplateLock::control(const lock::LockCall &call) {
if (this->prev_trigger_ != nullptr) {
this->prev_trigger_->stop_action();
}
auto state = *call.get_state();
if (state == LOCK_STATE_LOCKED) {
this->prev_trigger_ = this->lock_trigger_;
this->lock_trigger_->trigger();
} else if (state == LOCK_STATE_UNLOCKED) {
this->prev_trigger_ = this->unlock_trigger_;
this->unlock_trigger_->trigger();
}
if (this->optimistic_)
this->publish_state(state);
}
void TemplateLock::open_latch() {
if (this->prev_trigger_ != nullptr) {
this->prev_trigger_->stop_action();
}
this->prev_trigger_ = this->open_trigger_;
this->open_trigger_->trigger();
}
void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
void TemplateLock::set_state_lambda(std::function<optional<lock::LockState>()> &&f) { this->f_ = f; }
float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; }
Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; }
Trigger<> *TemplateLock::get_open_trigger() const { return this->open_trigger_; }
void TemplateLock::dump_config() {
LOG_LOCK("", "Template Lock", this);
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
}
} // namespace template_
} // namespace esphome

View File

@@ -1,38 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/lock/lock.h"
namespace esphome {
namespace template_ {
class TemplateLock : public lock::Lock, public Component {
public:
TemplateLock();
void dump_config() override;
void set_state_lambda(std::function<optional<lock::LockState>()> &&f);
Trigger<> *get_lock_trigger() const;
Trigger<> *get_unlock_trigger() const;
Trigger<> *get_open_trigger() const;
void set_optimistic(bool optimistic);
void loop() override;
float get_setup_priority() const override;
protected:
void control(const lock::LockCall &call) override;
void open_latch() override;
optional<std::function<optional<lock::LockState>()>> f_;
bool optimistic_{false};
Trigger<> *lock_trigger_;
Trigger<> *unlock_trigger_;
Trigger<> *open_trigger_;
Trigger<> *prev_trigger_{nullptr};
};
} // namespace template_
} // namespace esphome

View File

@@ -10,21 +10,18 @@ TemplateTextSensor = template_ns.class_(
"TemplateTextSensor", text_sensor.TextSensor, cg.PollingComponent
)
CONFIG_SCHEMA = (
text_sensor.text_sensor_schema()
.extend(
{
cv.GenerateID(): cv.declare_id(TemplateTextSensor),
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
}
)
.extend(cv.polling_component_schema("60s"))
)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TemplateTextSensor),
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
}
).extend(cv.polling_component_schema("60s"))
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(

View File

@@ -3,9 +3,7 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt
from esphome.const import (
CONF_ENTITY_CATEGORY,
CONF_FILTERS,
CONF_ICON,
CONF_ID,
CONF_ON_VALUE,
CONF_ON_RAW_VALUE,
@@ -16,7 +14,6 @@ from esphome.const import (
CONF_TO,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry
@@ -113,10 +110,12 @@ async def map_filter_to_code(config, filter_id):
)
icon = cv.icon
TEXT_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor),
cv.GenerateID(): cv.declare_id(TextSensor),
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
{
@@ -133,29 +132,6 @@ TEXT_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exte
}
)
_UNDEF = object()
def text_sensor_schema(
klass: MockObjClass = _UNDEF,
icon: str = _UNDEF,
entity_category: str = _UNDEF,
) -> cv.Schema:
schema = TEXT_SENSOR_SCHEMA
if klass is not _UNDEF:
schema = schema.extend({cv.GenerateID(): cv.declare_id(klass)})
if icon is not _UNDEF:
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
if entity_category is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_ENTITY_CATEGORY, default=entity_category
): cv.entity_category
}
)
return schema
async def build_filters(config):
return await cg.build_registry_list(FILTER_REGISTRY, config)
@@ -188,12 +164,6 @@ async def register_text_sensor(var, config):
await setup_text_sensor_core_(var, config)
async def new_text_sensor(config):
var = cg.new_Pvariable(config[CONF_ID])
await register_text_sensor(var, config)
return var
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_define("USE_TEXT_SENSOR")

View File

@@ -9,11 +9,7 @@ from .. import touchscreen_ns, CONF_TOUCHSCREEN_ID, Touchscreen, TouchListener
DEPENDENCIES = ["touchscreen"]
TouchscreenBinarySensor = touchscreen_ns.class_(
"TouchscreenBinarySensor",
binary_sensor.BinarySensor,
cg.Component,
TouchListener,
cg.Parented.template(Touchscreen),
"TouchscreenBinarySensor", binary_sensor.BinarySensor, TouchListener
)
CONF_X_MIN = "x_min"
@@ -43,7 +39,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_Y_MIN): cv.int_range(min=0, max=2000),
cv.Required(CONF_Y_MAX): cv.int_range(min=0, max=2000),
}
).extend(cv.COMPONENT_SCHEMA),
),
validate_coords,
)
@@ -51,9 +47,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await binary_sensor.register_binary_sensor(var, config)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_TOUCHSCREEN_ID])
hub = await cg.get_variable(config[CONF_TOUCHSCREEN_ID])
cg.add(
var.set_area(
config[CONF_X_MIN],
@@ -62,3 +56,4 @@ async def to_code(config):
config[CONF_Y_MAX],
)
)
cg.add(hub.register_listener(var))

View File

@@ -1,20 +1,13 @@
#pragma once
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/touchscreen/touchscreen.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace touchscreen {
class TouchscreenBinarySensor : public binary_sensor::BinarySensor,
public Component,
public TouchListener,
public Parented<Touchscreen> {
class TouchscreenBinarySensor : public binary_sensor::BinarySensor, public TouchListener {
public:
void setup() override { this->parent_->register_listener(this); }
/// Set the touch screen area where the button will detect the touch.
void set_area(int16_t x_min, int16_t x_max, int16_t y_min, int16_t y_max) {
this->x_min_ = x_min;

View File

@@ -1,7 +1,7 @@
from esphome.components import text_sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_SENSOR_DATAPOINT
from esphome.const import CONF_ID, CONF_SENSOR_DATAPOINT
from .. import tuya_ns, CONF_TUYA_ID, Tuya
DEPENDENCIES = ["tuya"]
@@ -9,22 +9,19 @@ CODEOWNERS = ["@dentra"]
TuyaTextSensor = tuya_ns.class_("TuyaTextSensor", text_sensor.TextSensor, cg.Component)
CONFIG_SCHEMA = (
text_sensor.text_sensor_schema()
.extend(
{
cv.GenerateID(): cv.declare_id(TuyaTextSensor),
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t,
}
)
.extend(cv.COMPONENT_SCHEMA)
)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TuyaTextSensor),
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
paren = await cg.get_variable(config[CONF_TUYA_ID])
cg.add(var.set_tuya_parent(paren))

View File

@@ -199,7 +199,7 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff
this->set_timeout("datapoint_dump", 1000, [this] { this->dump_config(); });
this->initialized_callback_.call();
}
this->handle_datapoints_(buffer, len);
this->handle_datapoint_(buffer, len);
break;
case TuyaCommandType::DATAPOINT_QUERY:
break;
@@ -224,111 +224,106 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff
}
}
void Tuya::handle_datapoints_(const uint8_t *buffer, size_t len) {
while (len >= 4) {
TuyaDatapoint datapoint{};
datapoint.id = buffer[0];
datapoint.type = (TuyaDatapointType) buffer[1];
datapoint.value_uint = 0;
void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) {
if (len < 2)
return;
size_t data_size = (buffer[2] << 8) + buffer[3];
const uint8_t *data = buffer + 4;
size_t data_len = len - 4;
if (data_size > data_len) {
ESP_LOGW(TAG, "Datapoint %u is truncated and cannot be parsed (%zu > %zu)", datapoint.id, data_size, data_len);
TuyaDatapoint datapoint{};
datapoint.id = buffer[0];
datapoint.type = (TuyaDatapointType) buffer[1];
datapoint.value_uint = 0;
// Drop update if datapoint is in ignore_mcu_datapoint_update list
for (uint8_t i : this->ignore_mcu_update_on_datapoints_) {
if (datapoint.id == i) {
ESP_LOGV(TAG, "Datapoint %u found in ignore_mcu_update_on_datapoints list, dropping MCU update", datapoint.id);
return;
}
}
datapoint.len = data_size;
size_t data_size = (buffer[2] << 8) + buffer[3];
const uint8_t *data = buffer + 4;
size_t data_len = len - 4;
if (data_size > data_len) {
ESP_LOGW(TAG, "Datapoint %u has extra bytes that will be ignored (%zu > %zu)", datapoint.id, data_size, data_len);
} else if (data_size < data_len) {
ESP_LOGW(TAG, "Datapoint %u is truncated and cannot be parsed (%zu < %zu)", datapoint.id, data_size, data_len);
return;
}
datapoint.len = data_len;
switch (datapoint.type) {
case TuyaDatapointType::RAW:
datapoint.value_raw = std::vector<uint8_t>(data, data + data_size);
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, format_hex_pretty(datapoint.value_raw).c_str());
break;
case TuyaDatapointType::BOOLEAN:
if (data_size != 1) {
ESP_LOGW(TAG, "Datapoint %u has bad boolean len %zu", datapoint.id, data_size);
return;
}
datapoint.value_bool = data[0];
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, ONOFF(datapoint.value_bool));
break;
case TuyaDatapointType::INTEGER:
if (data_size != 4) {
ESP_LOGW(TAG, "Datapoint %u has bad integer len %zu", datapoint.id, data_size);
return;
}
datapoint.value_uint = encode_uint32(data[0], data[1], data[2], data[3]);
ESP_LOGD(TAG, "Datapoint %u update to %d", datapoint.id, datapoint.value_int);
break;
case TuyaDatapointType::STRING:
datapoint.value_string = std::string(reinterpret_cast<const char *>(data), data_size);
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, datapoint.value_string.c_str());
break;
case TuyaDatapointType::ENUM:
if (data_size != 1) {
ESP_LOGW(TAG, "Datapoint %u has bad enum len %zu", datapoint.id, data_size);
return;
}
datapoint.value_enum = data[0];
ESP_LOGD(TAG, "Datapoint %u update to %d", datapoint.id, datapoint.value_enum);
break;
case TuyaDatapointType::BITMASK:
switch (data_size) {
case 1:
datapoint.value_bitmask = encode_uint32(0, 0, 0, data[0]);
break;
case 2:
datapoint.value_bitmask = encode_uint32(0, 0, data[0], data[1]);
break;
case 4:
datapoint.value_bitmask = encode_uint32(data[0], data[1], data[2], data[3]);
break;
default:
ESP_LOGW(TAG, "Datapoint %u has bad bitmask len %zu", datapoint.id, data_size);
return;
}
ESP_LOGD(TAG, "Datapoint %u update to %#08X", datapoint.id, datapoint.value_bitmask);
break;
default:
ESP_LOGW(TAG, "Datapoint %u has unknown type %#02hhX", datapoint.id, static_cast<uint8_t>(datapoint.type));
switch (datapoint.type) {
case TuyaDatapointType::RAW:
datapoint.value_raw = std::vector<uint8_t>(data, data + data_len);
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, format_hex_pretty(datapoint.value_raw).c_str());
break;
case TuyaDatapointType::BOOLEAN:
if (data_len != 1) {
ESP_LOGW(TAG, "Datapoint %u has bad boolean len %zu", datapoint.id, data_len);
return;
}
len -= data_size + 4;
buffer = data + data_size;
// drop update if datapoint is in ignore_mcu_datapoint_update list
bool skip = false;
for (auto i : this->ignore_mcu_update_on_datapoints_) {
if (datapoint.id == i) {
ESP_LOGV(TAG, "Datapoint %u found in ignore_mcu_update_on_datapoints list, dropping MCU update", datapoint.id);
skip = true;
break;
}
}
if (skip)
continue;
// Update internal datapoints
bool found = false;
for (auto &other : this->datapoints_) {
if (other.id == datapoint.id) {
other = datapoint;
found = true;
datapoint.value_bool = data[0];
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, ONOFF(datapoint.value_bool));
break;
case TuyaDatapointType::INTEGER:
if (data_len != 4) {
ESP_LOGW(TAG, "Datapoint %u has bad integer len %zu", datapoint.id, data_len);
return;
}
}
if (!found) {
this->datapoints_.push_back(datapoint);
}
datapoint.value_uint = encode_uint32(data[0], data[1], data[2], data[3]);
ESP_LOGD(TAG, "Datapoint %u update to %d", datapoint.id, datapoint.value_int);
break;
case TuyaDatapointType::STRING:
datapoint.value_string = std::string(reinterpret_cast<const char *>(data), data_len);
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, datapoint.value_string.c_str());
break;
case TuyaDatapointType::ENUM:
if (data_len != 1) {
ESP_LOGW(TAG, "Datapoint %u has bad enum len %zu", datapoint.id, data_len);
return;
}
datapoint.value_enum = data[0];
ESP_LOGD(TAG, "Datapoint %u update to %d", datapoint.id, datapoint.value_enum);
break;
case TuyaDatapointType::BITMASK:
switch (data_len) {
case 1:
datapoint.value_bitmask = encode_uint32(0, 0, 0, data[0]);
break;
case 2:
datapoint.value_bitmask = encode_uint32(0, 0, data[0], data[1]);
break;
case 4:
datapoint.value_bitmask = encode_uint32(data[0], data[1], data[2], data[3]);
break;
default:
ESP_LOGW(TAG, "Datapoint %u has bad bitmask len %zu", datapoint.id, data_len);
return;
}
ESP_LOGD(TAG, "Datapoint %u update to %#08X", datapoint.id, datapoint.value_bitmask);
break;
default:
ESP_LOGW(TAG, "Datapoint %u has unknown type %#02hhX", datapoint.id, static_cast<uint8_t>(datapoint.type));
return;
}
// Run through listeners
for (auto &listener : this->listeners_) {
if (listener.datapoint_id == datapoint.id)
listener.on_datapoint(datapoint);
// Update internal datapoints
bool found = false;
for (auto &other : this->datapoints_) {
if (other.id == datapoint.id) {
other = datapoint;
found = true;
}
}
if (!found) {
this->datapoints_.push_back(datapoint);
}
// Run through listeners
for (auto &listener : this->listeners_) {
if (listener.datapoint_id == datapoint.id)
listener.on_datapoint(datapoint);
}
}
void Tuya::send_raw_command_(TuyaCommand command) {

View File

@@ -101,7 +101,7 @@ class Tuya : public Component, public uart::UARTDevice {
protected:
void handle_char_(uint8_t c);
void handle_datapoints_(const uint8_t *buffer, size_t len);
void handle_datapoint_(const uint8_t *buffer, size_t len);
optional<TuyaDatapoint> get_datapoint_(uint8_t datapoint_id);
bool validate_message_();

View File

@@ -2,6 +2,9 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import (
CONF_ENTITY_CATEGORY,
CONF_ID,
CONF_ICON,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_NEW_BOX,
CONF_HIDE_TIMESTAMP,
@@ -12,22 +15,20 @@ VersionTextSensor = version_ns.class_(
"VersionTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = (
text_sensor.text_sensor_schema(
icon=ICON_NEW_BOX,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
)
.extend(
{
cv.GenerateID(): cv.declare_id(VersionTextSensor),
cv.Optional(CONF_HIDE_TIMESTAMP, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(VersionTextSensor),
cv.Optional(CONF_ICON, default=ICON_NEW_BOX): text_sensor.icon,
cv.Optional(CONF_HIDE_TIMESTAMP, default=False): cv.boolean,
cv.Optional(
CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC
): cv.entity_category,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = await text_sensor.new_text_sensor(config)
var = cg.new_Pvariable(config[CONF_ID])
await text_sensor.register_text_sensor(var, config)
await cg.register_component(var, config)
cg.add(var.set_hide_timestamp(config[CONF_HIDE_TIMESTAMP]))

View File

@@ -152,13 +152,6 @@ void WebServer::setup() {
client->send(this->select_json(obj, obj->state).c_str(), "state");
}
#endif
#ifdef USE_LOCK
for (auto *obj : App.get_locks()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->lock_json(obj, obj->state).c_str(), "state");
}
#endif
});
#ifdef USE_LOGGER
@@ -294,20 +287,6 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
}
#endif
#ifdef USE_LOCK
for (auto *obj : App.get_locks()) {
if (this->include_internal_ || !obj->is_internal()) {
write_row(stream, obj, "lock", "", [](AsyncResponseStream &stream, EntityBase *obj) {
lock::Lock *lock = (lock::Lock *) obj;
stream.print("<button>Lock</button><button>Unlock</button>");
if (lock->traits.get_supports_open()) {
stream.print("<button>Open</button>");
}
});
}
}
#endif
stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for "
"REST API documentation.</p>"));
if (this->allow_ota_) {
@@ -784,43 +763,6 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
}
#endif
#ifdef USE_LOCK
void WebServer::on_lock_update(lock::Lock *obj) {
this->events_.send(this->lock_json(obj, obj->state).c_str(), "state");
}
std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value) {
return json::build_json([obj, value](JsonObject root) {
root["id"] = "lock-" + obj->get_object_id();
root["state"] = lock::lock_state_to_string(value);
root["value"] = value;
});
}
void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (lock::Lock *obj : App.get_locks()) {
if (obj->get_object_id() != match.id)
continue;
if (request->method() == HTTP_GET) {
std::string data = this->lock_json(obj, obj->state);
request->send(200, "text/json", data.c_str());
} else if (match.method == "lock") {
this->defer([obj]() { obj->lock(); });
request->send(200);
} else if (match.method == "unlock") {
this->defer([obj]() { obj->unlock(); });
request->send(200);
} else if (match.method == "open") {
this->defer([obj]() { obj->open(); });
request->send(200);
} else {
request->send(404);
}
return;
}
request->send(404);
}
#endif
bool WebServer::canHandle(AsyncWebServerRequest *request) {
if (request->url() == "/")
return true;
@@ -888,11 +830,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
return true;
#endif
#ifdef USE_LOCK
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "lock")
return true;
#endif
return false;
}
void WebServer::handleRequest(AsyncWebServerRequest *request) {
@@ -985,13 +922,6 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
return;
}
#endif
#ifdef USE_LOCK
if (match.domain == "lock") {
this->handle_lock_request(request, match);
return;
}
#endif
}
bool WebServer::isRequestHandlerTrivial() { return false; }

View File

@@ -185,16 +185,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
std::string select_json(select::Select *obj, const std::string &value);
#endif
#ifdef USE_LOCK
void on_lock_update(lock::Lock *obj) override;
/// Handle a lock request under '/lock/<id>/</lock/unlock/open>'.
void handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match);
/// Dump the lock state with its value as a JSON string.
std::string lock_json(lock::Lock *obj, lock::LockState value);
#endif
/// Override the web handler's canHandle method.
bool canHandle(AsyncWebServerRequest *request) override;
/// Override the web handler's handleRequest method.

View File

@@ -161,8 +161,8 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t
wifi_config_t conf;
memset(&conf, 0, sizeof(conf));
strncpy(reinterpret_cast<char *>(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid));
strncpy(reinterpret_cast<char *>(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password));
strlcpy(reinterpret_cast<char *>(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid));
strlcpy(reinterpret_cast<char *>(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password));
// The weakest authmode to accept in the fast scan mode
if (ap.get_password().empty()) {
@@ -709,7 +709,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
wifi_config_t conf;
memset(&conf, 0, sizeof(conf));
strncpy(reinterpret_cast<char *>(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid));
strlcpy(reinterpret_cast<char *>(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid));
conf.ap.channel = ap.get_channel().value_or(1);
conf.ap.ssid_hidden = ap.get_ssid().size();
conf.ap.max_connection = 5;
@@ -720,7 +720,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
*conf.ap.password = 0;
} else {
conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
strncpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.ssid));
strlcpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.ssid));
}
#if ESP_IDF_VERSION_MAJOR >= 4

View File

@@ -3,6 +3,8 @@ import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import (
CONF_BSSID,
CONF_ENTITY_CATEGORY,
CONF_ID,
CONF_IP_ADDRESS,
CONF_SCAN_RESULTS,
CONF_SSID,
@@ -14,16 +16,14 @@ DEPENDENCIES = ["wifi"]
wifi_info_ns = cg.esphome_ns.namespace("wifi_info")
IPAddressWiFiInfo = wifi_info_ns.class_(
"IPAddressWiFiInfo", text_sensor.TextSensor, cg.PollingComponent
"IPAddressWiFiInfo", text_sensor.TextSensor, cg.Component
)
ScanResultsWiFiInfo = wifi_info_ns.class_(
"ScanResultsWiFiInfo", text_sensor.TextSensor, cg.PollingComponent
)
SSIDWiFiInfo = wifi_info_ns.class_(
"SSIDWiFiInfo", text_sensor.TextSensor, cg.PollingComponent
)
SSIDWiFiInfo = wifi_info_ns.class_("SSIDWiFiInfo", text_sensor.TextSensor, cg.Component)
BSSIDWiFiInfo = wifi_info_ns.class_(
"BSSIDWiFiInfo", text_sensor.TextSensor, cg.PollingComponent
"BSSIDWiFiInfo", text_sensor.TextSensor, cg.Component
)
MacAddressWifiInfo = wifi_info_ns.class_(
"MacAddressWifiInfo", text_sensor.TextSensor, cg.Component
@@ -31,20 +31,45 @@ MacAddressWifiInfo = wifi_info_ns.class_(
CONFIG_SCHEMA = cv.Schema(
{
cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema(
klass=IPAddressWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
).extend(cv.polling_component_schema("1s")),
cv.Optional(CONF_SCAN_RESULTS): text_sensor.text_sensor_schema(
klass=ScanResultsWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
cv.Optional(CONF_IP_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(IPAddressWiFiInfo),
cv.Optional(
CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC
): cv.entity_category,
}
),
cv.Optional(CONF_SCAN_RESULTS): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ScanResultsWiFiInfo),
cv.Optional(
CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC
): cv.entity_category,
}
).extend(cv.polling_component_schema("60s")),
cv.Optional(CONF_SSID): text_sensor.text_sensor_schema(
klass=SSIDWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
).extend(cv.polling_component_schema("1s")),
cv.Optional(CONF_BSSID): text_sensor.text_sensor_schema(
klass=BSSIDWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
).extend(cv.polling_component_schema("1s")),
cv.Optional(CONF_MAC_ADDRESS): text_sensor.text_sensor_schema(
klass=MacAddressWifiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
cv.Optional(CONF_SSID): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(SSIDWiFiInfo),
cv.Optional(
CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC
): cv.entity_category,
}
),
cv.Optional(CONF_BSSID): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BSSIDWiFiInfo),
cv.Optional(
CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC
): cv.entity_category,
}
),
cv.Optional(CONF_MAC_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MacAddressWifiInfo),
cv.Optional(
CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC
): cv.entity_category,
}
),
}
)
@@ -53,8 +78,9 @@ CONFIG_SCHEMA = cv.Schema(
async def setup_conf(config, key):
if key in config:
conf = config[key]
var = await text_sensor.new_text_sensor(conf)
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await text_sensor.register_text_sensor(var, conf)
async def to_code(config):

View File

@@ -7,9 +7,9 @@
namespace esphome {
namespace wifi_info {
class IPAddressWiFiInfo : public PollingComponent, public text_sensor::TextSensor {
class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor {
public:
void update() override {
void loop() override {
auto ip = wifi::global_wifi_component->wifi_sta_ip();
if (ip != this->last_ip_) {
this->last_ip_ = ip;
@@ -53,9 +53,9 @@ class ScanResultsWiFiInfo : public PollingComponent, public text_sensor::TextSen
std::string last_scan_results_;
};
class SSIDWiFiInfo : public PollingComponent, public text_sensor::TextSensor {
class SSIDWiFiInfo : public Component, public text_sensor::TextSensor {
public:
void update() override {
void loop() override {
std::string ssid = wifi::global_wifi_component->wifi_ssid();
if (this->last_ssid_ != ssid) {
this->last_ssid_ = ssid;
@@ -70,9 +70,9 @@ class SSIDWiFiInfo : public PollingComponent, public text_sensor::TextSensor {
std::string last_ssid_;
};
class BSSIDWiFiInfo : public PollingComponent, public text_sensor::TextSensor {
class BSSIDWiFiInfo : public Component, public text_sensor::TextSensor {
public:
void update() override {
void loop() override {
wifi::bssid_t bssid = wifi::global_wifi_component->wifi_bssid();
if (memcmp(bssid.data(), last_bssid_.data(), 6) != 0) {
std::copy(bssid.begin(), bssid.end(), last_bssid_.begin());

View File

@@ -1713,49 +1713,30 @@ def require_framework_version(
esp_idf=None,
esp32_arduino=None,
esp8266_arduino=None,
max_version=False,
extra_message=None,
):
def validator(value):
core_data = CORE.data[KEY_CORE]
framework = core_data[KEY_TARGET_FRAMEWORK]
if framework == "esp-idf":
if esp_idf is None:
msg = "This feature is incompatible with esp-idf"
if extra_message:
msg += f". {extra_message}"
raise Invalid(msg)
raise Invalid("This feature is incompatible with esp-idf")
required = esp_idf
elif CORE.is_esp32 and framework == "arduino":
if esp32_arduino is None:
msg = "This feature is incompatible with ESP32 using arduino framework"
if extra_message:
msg += f". {extra_message}"
raise Invalid(msg)
raise Invalid(
"This feature is incompatible with ESP32 using arduino framework"
)
required = esp32_arduino
elif CORE.is_esp8266 and framework == "arduino":
if esp8266_arduino is None:
msg = "This feature is incompatible with ESP8266"
if extra_message:
msg += f". {extra_message}"
raise Invalid(msg)
raise Invalid("This feature is incompatible with ESP8266")
required = esp8266_arduino
else:
raise NotImplementedError
if max_version:
if core_data[KEY_FRAMEWORK_VERSION] > required:
msg = f"This feature requires framework version {required} or lower"
if extra_message:
msg += f". {extra_message}"
raise Invalid(msg)
return value
if core_data[KEY_FRAMEWORK_VERSION] < required:
msg = f"This feature requires at least framework version {required}"
if extra_message:
msg += f". {extra_message}"
raise Invalid(msg)
raise Invalid(
f"This feature requires at least framework version {required}"
)
return value
return validator

View File

@@ -1,6 +1,6 @@
"""Constants used by esphome."""
__version__ = "2022.2.3"
__version__ = "2022.2.0-dev"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
@@ -168,7 +168,6 @@ CONF_DIRECTION = "direction"
CONF_DIRECTION_OUTPUT = "direction_output"
CONF_DISABLED_BY_DEFAULT = "disabled_by_default"
CONF_DISCOVERY = "discovery"
CONF_DISCOVERY_OBJECT_ID_GENERATOR = "discovery_object_id_generator"
CONF_DISCOVERY_PREFIX = "discovery_prefix"
CONF_DISCOVERY_RETAIN = "discovery_retain"
CONF_DISCOVERY_UNIQUE_ID_GENERATOR = "discovery_unique_id_generator"
@@ -335,7 +334,6 @@ CONF_LINE_THICKNESS = "line_thickness"
CONF_LINE_TYPE = "line_type"
CONF_LOADED_INTEGRATIONS = "loaded_integrations"
CONF_LOCAL = "local"
CONF_LOCK_ACTION = "lock_action"
CONF_LOG_TOPIC = "log_topic"
CONF_LOGGER = "logger"
CONF_LOGS = "logs"
@@ -428,11 +426,9 @@ CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan"
CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched"
CONF_ON_FINGER_SCAN_UNMATCHED = "on_finger_scan_unmatched"
CONF_ON_JSON_MESSAGE = "on_json_message"
CONF_ON_LOCK = "on_lock"
CONF_ON_LOOP = "on_loop"
CONF_ON_MESSAGE = "on_message"
CONF_ON_MULTI_CLICK = "on_multi_click"
CONF_ON_OPEN = "on_open"
CONF_ON_PRESS = "on_press"
CONF_ON_RAW_VALUE = "on_raw_value"
CONF_ON_RELEASE = "on_release"
@@ -446,7 +442,6 @@ CONF_ON_TIME_SYNC = "on_time_sync"
CONF_ON_TOUCH = "on_touch"
CONF_ON_TURN_OFF = "on_turn_off"
CONF_ON_TURN_ON = "on_turn_on"
CONF_ON_UNLOCK = "on_unlock"
CONF_ON_VALUE = "on_value"
CONF_ON_VALUE_RANGE = "on_value_range"
CONF_ONE = "one"
@@ -714,7 +709,6 @@ CONF_UART_ID = "uart_id"
CONF_UID = "uid"
CONF_UNIQUE = "unique"
CONF_UNIT_OF_MEASUREMENT = "unit_of_measurement"
CONF_UNLOCK_ACTION = "unlock_action"
CONF_UPDATE_INTERVAL = "update_interval"
CONF_UPDATE_ON_BOOT = "update_on_boot"
CONF_URL = "url"
@@ -921,9 +915,6 @@ DEVICE_CLASS_VOLTAGE = "voltage"
DEVICE_CLASS_UPDATE = "update"
# device classes of button component
DEVICE_CLASS_RESTART = "restart"
# device classes of switch component
DEVICE_CLASS_OUTLET = "outlet"
DEVICE_CLASS_SWITCH = "switch"
# state classes

View File

@@ -20,7 +20,7 @@ from esphome.coroutine import FakeEventLoop as _FakeEventLoop
# pylint: disable=unused-import
from esphome.coroutine import coroutine, coroutine_with_priority # noqa
from esphome.helpers import ensure_unique_string, is_ha_addon
from esphome.helpers import ensure_unique_string, is_hassio
from esphome.util import OrderedDict
if TYPE_CHECKING:
@@ -568,12 +568,12 @@ class EsphomeCore:
return self.relative_build_path("src", *path)
def relative_pioenvs_path(self, *path):
if is_ha_addon():
if is_hassio():
return os.path.join("/data", self.name, ".pioenvs", *path)
return self.relative_build_path(".pioenvs", *path)
def relative_piolibdeps_path(self, *path):
if is_ha_addon():
if is_hassio():
return os.path.join("/data", self.name, ".piolibdeps", *path)
return self.relative_build_path(".piolibdeps", *path)

View File

@@ -42,9 +42,6 @@
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#endif
#ifdef USE_LOCK
#include "esphome/components/lock/lock.h"
#endif
namespace esphome {
@@ -107,10 +104,6 @@ class Application {
void register_select(select::Select *select) { this->selects_.push_back(select); }
#endif
#ifdef USE_LOCK
void register_lock(lock::Lock *a_lock) { this->locks_.push_back(a_lock); }
#endif
/// Register the component in this Application instance.
template<class C> C *register_component(C *c) {
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
@@ -264,15 +257,6 @@ class Application {
return nullptr;
}
#endif
#ifdef USE_LOCK
const std::vector<lock::Lock *> &get_locks() { return this->locks_; }
lock::Lock *get_lock_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->locks_)
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj;
return nullptr;
}
#endif
Scheduler scheduler;
@@ -321,9 +305,6 @@ class Application {
#ifdef USE_SELECT
std::vector<select::Select *> selects_{};
#endif
#ifdef USE_LOCK
std::vector<lock::Lock *> locks_{};
#endif
std::string name_;
std::string compilation_time_;

View File

@@ -65,12 +65,6 @@ void Controller::setup_controller(bool include_internal) {
obj->add_on_state_callback([this, obj](const std::string &state) { this->on_select_update(obj, state); });
}
#endif
#ifdef USE_LOCK
for (auto *obj : App.get_locks()) {
if (include_internal || !obj->is_internal())
obj->add_on_state_callback([this, obj]() { this->on_lock_update(obj); });
}
#endif
}
} // namespace esphome

View File

@@ -34,9 +34,6 @@
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#endif
#ifdef USE_LOCK
#include "esphome/components/lock/lock.h"
#endif
namespace esphome {
@@ -73,9 +70,6 @@ class Controller {
#ifdef USE_SELECT
virtual void on_select_update(select::Select *obj, const std::string &state){};
#endif
#ifdef USE_LOCK
virtual void on_lock_update(lock::Lock *obj){};
#endif
};
} // namespace esphome

View File

@@ -26,7 +26,6 @@
#define USE_GRAPH
#define USE_HOMEASSISTANT_TIME
#define USE_LIGHT
#define USE_LOCK
#define USE_LOGGER
#define USE_MDNS
#define USE_NUMBER
@@ -71,6 +70,7 @@
#ifdef USE_ESP_IDF
#define USE_ARDUINO_VERSION_CODE VERSION_CODE(4, 3, 0)
#endif
#define USE_SOCKET_HAS_LWIP
#endif
// ESP8266-specific feature flags
@@ -80,6 +80,7 @@
#define USE_ESP8266_PREFERENCES_FLASH
#define USE_HTTP_REQUEST_ESP8266_HTTPS
#define USE_SOCKET_IMPL_LWIP_TCP
#define USE_SOCKET_HAS_LWIP
#endif
// Disabled feature flags

View File

@@ -773,11 +773,9 @@ class MockObj(Expression):
return MockObj(f"{self.base} &", "")
if name == "ptr":
return MockObj(f"{self.base} *", "")
if name == "const_ptr":
return MockObj(f"{self.base} *const", "")
if name == "const":
return MockObj(f"const {self.base}", "")
raise ValueError("Expected one of ref, ptr, const_ptr, const.")
raise ValueError("Expected one of ref, ptr, const.")
@property
def using(self) -> "MockObj":

View File

@@ -15,7 +15,6 @@ uint16 = global_ns.namespace("uint16_t")
uint32 = global_ns.namespace("uint32_t")
uint64 = global_ns.namespace("uint64_t")
int32 = global_ns.namespace("int32_t")
int64 = global_ns.namespace("int64_t")
const_char_ptr = global_ns.namespace("const char *")
NAN = global_ns.namespace("NAN")
esphome_ns = global_ns # using namespace esphome;
@@ -35,4 +34,3 @@ InternalGPIOPin = esphome_ns.class_("InternalGPIOPin", GPIOPin)
gpio_ns = esphome_ns.namespace("gpio")
gpio_Flags = gpio_ns.enum("Flags", is_class=True)
EntityCategory = esphome_ns.enum("EntityCategory")
Parented = esphome_ns.class_("Parented")

View File

@@ -55,13 +55,13 @@ class DashboardSettings:
self.password_hash = ""
self.username = ""
self.using_password = False
self.on_ha_addon = False
self.on_hassio = False
self.cookie_secret = None
def parse_args(self, args):
self.on_ha_addon = args.ha_addon
self.on_hassio = args.hassio
password = args.password or os.getenv("PASSWORD", "")
if not self.on_ha_addon:
if not self.on_hassio:
self.username = args.username or os.getenv("USERNAME", "")
self.using_password = bool(password)
if self.using_password:
@@ -77,14 +77,14 @@ class DashboardSettings:
return get_bool_env("ESPHOME_DASHBOARD_USE_PING")
@property
def using_ha_addon_auth(self):
if not self.on_ha_addon:
def using_hassio_auth(self):
if not self.on_hassio:
return False
return not get_bool_env("DISABLE_HA_AUTHENTICATION")
@property
def using_auth(self):
return self.using_password or self.using_ha_addon_auth
return self.using_password or self.using_hassio_auth
def check_password(self, username, password):
if not self.using_auth:
@@ -138,10 +138,10 @@ def authenticated(func):
def is_authenticated(request_handler):
if settings.on_ha_addon:
if settings.on_hassio:
# Handle ingress - disable auth on ingress port
# X-HA-Ingress is automatically stripped on the non-ingress server in nginx
header = request_handler.request.headers.get("X-HA-Ingress", "NO")
# X-Hassio-Ingress is automatically stripped on the non-ingress server in nginx
header = request_handler.request.headers.get("X-Hassio-Ingress", "NO")
if str(header) == "YES":
return True
if settings.using_auth:
@@ -792,23 +792,23 @@ class LoginHandler(BaseHandler):
self.render(
"login.template.html",
error=error,
ha_addon=settings.using_ha_addon_auth,
hassio=settings.using_hassio_auth,
has_username=bool(settings.username),
**template_args(),
)
def post_ha_addon_login(self):
def post_hassio_login(self):
import requests
headers = {
"Authentication": f"Bearer {os.getenv('SUPERVISOR_TOKEN')}",
"X-HASSIO-KEY": os.getenv("HASSIO_TOKEN"),
}
data = {
"username": self.get_argument("username", ""),
"password": self.get_argument("password", ""),
}
try:
req = requests.post("http://supervisor/auth", headers=headers, data=data)
req = requests.post("http://hassio/auth", headers=headers, data=data)
if req.status_code == 200:
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
self.redirect("/")
@@ -835,8 +835,8 @@ class LoginHandler(BaseHandler):
self.render_login_page(error=error_str)
def post(self):
if settings.using_ha_addon_auth:
self.post_ha_addon_login()
if settings.using_hassio_auth:
self.post_hassio_login()
else:
self.post_native_login()

View File

@@ -1,5 +1,4 @@
import codecs
from contextlib import suppress
import logging
import os
@@ -144,8 +143,8 @@ def get_bool_env(var, default=False):
return bool(os.getenv(var, default))
def is_ha_addon():
return get_bool_env("ESPHOME_IS_HA_ADDON")
def is_hassio():
return get_bool_env("ESPHOME_IS_HASSIO")
def walk_files(path):
@@ -234,20 +233,8 @@ def copy_file_if_changed(src: os.PathLike, dst: os.PathLike) -> None:
return
mkdir_p(os.path.dirname(dst))
try:
shutil.copyfile(src, dst)
shutil.copy(src, dst)
except OSError as err:
if isinstance(err, PermissionError):
# Older esphome versions copied over the src file permissions too.
# So when the dst file had 444 permissions, the dst file would have those
# too and subsequent writes would fail
# -> delete file (it would be overwritten anyway), and try again
# if that fails, use normal error handler
with suppress(OSError):
os.unlink(dst)
shutil.copyfile(src, dst)
return
from esphome.core import EsphomeError
raise EsphomeError(f"Error copying file {src} to {dst}: {err}") from err

View File

@@ -35,10 +35,9 @@ build_flags =
lib_deps =
esphome/noise-c@0.1.4 ; api
makuna/NeoPixelBus@2.6.9 ; neopixelbus
esphome/Improv@1.2.1 ; improv_serial / esp32_improv
esphome/Improv@1.2.0 ; improv_serial / esp32_improv
bblanchon/ArduinoJson@6.18.5 ; json
wjtje/qr-code-generator-library@1.7.0 ; qr_code
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
build_flags =
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
src_filter =
@@ -94,7 +93,6 @@ lib_deps =
DNSServer ; captive_portal (Arduino built-in)
build_flags =
${common:arduino.build_flags}
-Wno-nonnull-compare
-DUSE_ESP8266
-DUSE_ESP8266_FRAMEWORK_ARDUINO
extra_scripts = post:esphome/components/esp8266/post_build.py.script

View File

@@ -6,12 +6,12 @@ tornado==6.1
tzlocal==4.1 # from time
tzdata>=2021.1 # from time
pyserial==3.5
platformio==5.2.5 # When updating platformio, also update Dockerfile
platformio==5.2.4 # When updating platformio, also update Dockerfile
esptool==3.2
click==8.0.3
esphome-dashboard==20220209.0
aioesphomeapi==10.8.2
zeroconf==0.38.3
esphome-dashboard==20220116.0
aioesphomeapi==10.8.1
zeroconf==0.37.0
# esp-idf requires this, but doesn't bundle it by default
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24

View File

@@ -4,9 +4,9 @@ black==22.1.0
pre-commit
# Unit tests
pytest==7.0.0
pytest==6.2.5
pytest-cov==3.0.0
pytest-mock==3.7.0
pytest-asyncio==0.18.0
pytest-asyncio==0.17.2
asyncmock==0.4.2
hypothesis==5.49.0

View File

@@ -222,13 +222,7 @@ def lint_ext_check(fname):
@lint_file_check(
exclude=[
"**.sh",
"docker/ha-addon-rootfs/**",
"docker/*.py",
"script/*",
"setup.py",
]
exclude=["**.sh", "docker/hassio-rootfs/**", "docker/*.py", "script/*", "setup.py"]
)
def lint_executable_bit(fname):
ex = EXECUTABLE_BIT[fname]
@@ -604,7 +598,6 @@ def lint_inclusive_language(fname, match):
"esphome/components/display/display_buffer.h",
"esphome/components/fan/fan.h",
"esphome/components/i2c/i2c.h",
"esphome/components/lock/lock.h",
"esphome/components/mqtt/mqtt_component.h",
"esphome/components/number/number.h",
"esphome/components/output/binary_output.h",

View File

@@ -1077,21 +1077,7 @@ sensor:
cs_pin:
mcp23xxx: mcp23017_hub
number: 14
- platform: max9611
i2c_id: i2c_bus
shunt_resistance: 0.2 ohm
gain: '1X'
voltage:
name: Max9611 Voltage
current:
name: Max9611 Current
power:
name: Max9611 Watts
temperature:
name: Max9611 Temp
update_interval: 1s
esp32_touch:
setup_mode: False
iir_filter: 10ms
@@ -2051,10 +2037,6 @@ switch:
- platform: template
id: ble1_status
optimistic: true
- platform: template
id: outlet_switch
optimistic: true
device_class: outlet
fan:
- platform: binary
@@ -2600,28 +2582,3 @@ select:
qr_code:
- id: homepage_qr
value: https://esphome.io/index.html
lock:
- platform: template
id: test_lock1
name: "Template Switch"
lambda: |-
if (id(binary_sensor1).state) {
return LOCK_STATE_LOCKED;
}else{
return LOCK_STATE_UNLOCKED;
}
optimistic: true
assumed_state: no
on_unlock:
- lock.template.publish:
id: test_lock1
state: !lambda "return LOCK_STATE_UNLOCKED;"
on_lock:
- lock.template.publish:
id: test_lock1
state: !lambda "return LOCK_STATE_LOCKED;"
- platform: output
name: "Generic Output Lock"
id: test_lock2
output: pca_6

View File

@@ -324,13 +324,6 @@ sensor:
bus_voltage:
name: "INA260 Voltage"
update_interval: 60s
- platform: radon_eye_rd200
ble_client_id: radon_eye_ble_id
update_interval: 10min
radon:
name: "RD200 Radon"
radon_long_term:
name: "RD200 Radon Long Term"
time:
- platform: homeassistant
@@ -430,14 +423,10 @@ ble_client:
id: airthings01
- mac_address: 01:02:03:04:05:06
id: airthingsmini01
- mac_address: 01:02:03:04:05:06
id: radon_eye_ble_id
airthings_ble:
radon_eye_ble:
ruuvi_ble:
xiaomi_ble:

View File

@@ -692,20 +692,6 @@ sensor:
name: 'testwave'
component_id: 2
wave_channel_id: 1
- platform: mlx90393
oversampling: 1
filter: 0
gain: "3X"
x_axis:
name: "mlxxaxis"
y_axis:
name: "mlxyaxis"
z_axis:
name: "mlxzaxis"
resolution: 17BIT
temperature:
name: "mlxtemp"
oversampling: 2
time:
- platform: homeassistant

View File

@@ -328,7 +328,6 @@ binary_sensor:
number: 3
- platform: touchscreen
touchscreen_id: lilygo_touchscreen
id: touch_key1
x_min: 0
x_max: 100
@@ -562,12 +561,3 @@ touchscreen:
- logger.log:
format: Touch at (%d, %d)
args: ["touch.x", "touch.y"]
- platform: lilygo_t5_47
id: lilygo_touchscreen
interrupt_pin: GPIO36
display: inkplate_display
on_touch:
- logger.log:
format: Touch at (%d, %d)
args: ["touch.x", "touch.y"]

View File

@@ -124,13 +124,13 @@ def test_get_bool_env(monkeypatch, var, value, default, expected):
@pytest.mark.parametrize("value, expected", ((None, False), ("Yes", True)))
def test_is_ha_addon(monkeypatch, value, expected):
def test_is_hassio(monkeypatch, value, expected):
if value is None:
monkeypatch.delenv("ESPHOME_IS_HA_ADDON", raising=False)
monkeypatch.delenv("ESPHOME_IS_HASSIO", raising=False)
else:
monkeypatch.setenv("ESPHOME_IS_HA_ADDON", value)
monkeypatch.setenv("ESPHOME_IS_HASSIO", value)
actual = helpers.is_ha_addon()
actual = helpers.is_hassio()
assert actual == expected