mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			56 Commits
		
	
	
		
			2024.8.0b3
			...
			power-supp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					49eba0a9bc | ||
| 
						 | 
					34cce0e920 | ||
| 
						 | 
					7e18a5c44f | ||
| 
						 | 
					5a707b558d | ||
| 
						 | 
					e10f8128c8 | ||
| 
						 | 
					0f2064193f | ||
| 
						 | 
					dc9c001056 | ||
| 
						 | 
					60fced53c2 | ||
| 
						 | 
					71d6bbc7e6 | ||
| 
						 | 
					caaae59ea9 | ||
| 
						 | 
					a01fea54a0 | ||
| 
						 | 
					43f8f2fd2e | ||
| 
						 | 
					3c65cabe1d | ||
| 
						 | 
					5cc8dbace4 | ||
| 
						 | 
					ab620acd4f | ||
| 
						 | 
					11e155d866 | ||
| 
						 | 
					68272c39c0 | ||
| 
						 | 
					da72bae94a | ||
| 
						 | 
					1f21e419aa | ||
| 
						 | 
					5d4bf5f8e5 | ||
| 
						 | 
					b5a6d3aa9d | ||
| 
						 | 
					813d517076 | ||
| 
						 | 
					4ed6a64869 | ||
| 
						 | 
					aaae8f4a87 | ||
| 
						 | 
					436c6282da | ||
| 
						 | 
					c043bbe598 | ||
| 
						 | 
					8fae609316 | ||
| 
						 | 
					848fd0442d | ||
| 
						 | 
					bd3d065a23 | ||
| 
						 | 
					fa497d06b0 | ||
| 
						 | 
					3cbdf63f56 | ||
| 
						 | 
					30414667d0 | ||
| 
						 | 
					1ffee9c4d2 | ||
| 
						 | 
					b425912a80 | ||
| 
						 | 
					10147d8e0e | ||
| 
						 | 
					baedd74c7a | ||
| 
						 | 
					8b6d6fe661 | ||
| 
						 | 
					ac9417d469 | ||
| 
						 | 
					56aa58780d | ||
| 
						 | 
					75899162b3 | ||
| 
						 | 
					a7167ec3bf | ||
| 
						 | 
					a0c54504cd | ||
| 
						 | 
					c3668b9a4d | ||
| 
						 | 
					9001d1c0d4 | ||
| 
						 | 
					abb2669f0f | ||
| 
						 | 
					9713458368 | ||
| 
						 | 
					5c31ab4060 | ||
| 
						 | 
					965141fad7 | ||
| 
						 | 
					ecd3d838c9 | ||
| 
						 | 
					ce7adbae99 | ||
| 
						 | 
					1bc3ccd969 | ||
| 
						 | 
					5646ec7f9c | ||
| 
						 | 
					80a0f13722 | ||
| 
						 | 
					fef592b6c6 | ||
| 
						 | 
					7133e08755 | ||
| 
						 | 
					350f17e48f | 
@@ -169,6 +169,7 @@ esphome/components/he60r/* @clydebarrow
 | 
			
		||||
esphome/components/heatpumpir/* @rob-deutsch
 | 
			
		||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
 | 
			
		||||
esphome/components/hm3301/* @freekode
 | 
			
		||||
esphome/components/hmac_md5/* @dwmw2
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter @esphome/core
 | 
			
		||||
esphome/components/homeassistant/number/* @landonr
 | 
			
		||||
esphome/components/homeassistant/switch/* @Links2004
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ from esphome.const import (
 | 
			
		||||
    SECRETS_FILES,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine
 | 
			
		||||
from esphome.helpers import indent, is_ip_address
 | 
			
		||||
from esphome.helpers import indent, is_ip_address, get_bool_env
 | 
			
		||||
from esphome.log import Fore, color, setup_log
 | 
			
		||||
from esphome.util import (
 | 
			
		||||
    get_serial_ports,
 | 
			
		||||
@@ -731,7 +731,11 @@ POST_CONFIG_ACTIONS = {
 | 
			
		||||
def parse_args(argv):
 | 
			
		||||
    options_parser = argparse.ArgumentParser(add_help=False)
 | 
			
		||||
    options_parser.add_argument(
 | 
			
		||||
        "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
 | 
			
		||||
        "-v",
 | 
			
		||||
        "--verbose",
 | 
			
		||||
        help="Enable verbose ESPHome logs.",
 | 
			
		||||
        action="store_true",
 | 
			
		||||
        default=get_bool_env("ESPHOME_VERBOSE"),
 | 
			
		||||
    )
 | 
			
		||||
    options_parser.add_argument(
 | 
			
		||||
        "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
 | 
			
		||||
 
 | 
			
		||||
@@ -1107,6 +1107,19 @@ enum MediaPlayerCommand {
 | 
			
		||||
  MEDIA_PLAYER_COMMAND_MUTE = 3;
 | 
			
		||||
  MEDIA_PLAYER_COMMAND_UNMUTE = 4;
 | 
			
		||||
}
 | 
			
		||||
enum MediaPlayerFormatPurpose {
 | 
			
		||||
  MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0;
 | 
			
		||||
  MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1;
 | 
			
		||||
}
 | 
			
		||||
message MediaPlayerSupportedFormat {
 | 
			
		||||
  option (id) = 119;
 | 
			
		||||
  option (ifdef) = "USE_MEDIA_PLAYER";
 | 
			
		||||
 | 
			
		||||
  string format = 1;
 | 
			
		||||
  uint32 sample_rate = 2;
 | 
			
		||||
  uint32 num_channels = 3;
 | 
			
		||||
  MediaPlayerFormatPurpose purpose = 4;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesMediaPlayerResponse {
 | 
			
		||||
  option (id) = 63;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
@@ -1122,6 +1135,8 @@ message ListEntitiesMediaPlayerResponse {
 | 
			
		||||
  EntityCategory entity_category = 7;
 | 
			
		||||
 | 
			
		||||
  bool supports_pause = 8;
 | 
			
		||||
 | 
			
		||||
  repeated MediaPlayerSupportedFormat supported_formats = 9;
 | 
			
		||||
}
 | 
			
		||||
message MediaPlayerStateResponse {
 | 
			
		||||
  option (id) = 64;
 | 
			
		||||
 
 | 
			
		||||
@@ -179,6 +179,7 @@ void APIConnection::loop() {
 | 
			
		||||
      SubscribeHomeAssistantStateResponse resp;
 | 
			
		||||
      resp.entity_id = it.entity_id;
 | 
			
		||||
      resp.attribute = it.attribute.value();
 | 
			
		||||
      resp.once = it.once;
 | 
			
		||||
      if (this->send_subscribe_home_assistant_state_response(resp)) {
 | 
			
		||||
        state_subs_at_++;
 | 
			
		||||
      }
 | 
			
		||||
@@ -1025,6 +1026,15 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play
 | 
			
		||||
  auto traits = media_player->get_traits();
 | 
			
		||||
  msg.supports_pause = traits.get_supports_pause();
 | 
			
		||||
 | 
			
		||||
  for (auto &supported_format : traits.get_supported_formats()) {
 | 
			
		||||
    MediaPlayerSupportedFormat media_format;
 | 
			
		||||
    media_format.format = supported_format.format;
 | 
			
		||||
    media_format.sample_rate = supported_format.sample_rate;
 | 
			
		||||
    media_format.num_channels = supported_format.num_channels;
 | 
			
		||||
    media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
 | 
			
		||||
    msg.supported_formats.push_back(media_format);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this->send_list_entities_media_player_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
 | 
			
		||||
 
 | 
			
		||||
@@ -387,6 +387,18 @@ template<> const char *proto_enum_to_string<enums::MediaPlayerCommand>(enums::Me
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::MediaPlayerFormatPurpose>(enums::MediaPlayerFormatPurpose value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT:
 | 
			
		||||
      return "MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT";
 | 
			
		||||
    case enums::MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT:
 | 
			
		||||
      return "MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<>
 | 
			
		||||
const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::BluetoothDeviceRequestType value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
@@ -5123,6 +5135,64 @@ void ButtonCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->sample_rate = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->num_channels = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->purpose = value.as_enum<enums::MediaPlayerFormatPurpose>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool MediaPlayerSupportedFormat::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->format = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->format);
 | 
			
		||||
  buffer.encode_uint32(2, this->sample_rate);
 | 
			
		||||
  buffer.encode_uint32(3, this->num_channels);
 | 
			
		||||
  buffer.encode_enum<enums::MediaPlayerFormatPurpose>(4, this->purpose);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void MediaPlayerSupportedFormat::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("MediaPlayerSupportedFormat {\n");
 | 
			
		||||
  out.append("  format: ");
 | 
			
		||||
  out.append("'").append(this->format).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  sample_rate: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->sample_rate);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  num_channels: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->num_channels);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  purpose: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::MediaPlayerFormatPurpose>(this->purpose));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
@@ -5159,6 +5229,10 @@ bool ListEntitiesMediaPlayerResponse::decode_length(uint32_t field_id, ProtoLeng
 | 
			
		||||
      this->icon = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 9: {
 | 
			
		||||
      this->supported_formats.push_back(value.as_message<MediaPlayerSupportedFormat>());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -5182,6 +5256,9 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_bool(6, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
 | 
			
		||||
  buffer.encode_bool(8, this->supports_pause);
 | 
			
		||||
  for (auto &it : this->supported_formats) {
 | 
			
		||||
    buffer.encode_message<MediaPlayerSupportedFormat>(9, it, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -5219,6 +5296,12 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  supports_pause: ");
 | 
			
		||||
  out.append(YESNO(this->supports_pause));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->supported_formats) {
 | 
			
		||||
    out.append("  supported_formats: ");
 | 
			
		||||
    it.dump_to(out);
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -156,6 +156,10 @@ enum MediaPlayerCommand : uint32_t {
 | 
			
		||||
  MEDIA_PLAYER_COMMAND_MUTE = 3,
 | 
			
		||||
  MEDIA_PLAYER_COMMAND_UNMUTE = 4,
 | 
			
		||||
};
 | 
			
		||||
enum MediaPlayerFormatPurpose : uint32_t {
 | 
			
		||||
  MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0,
 | 
			
		||||
  MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1,
 | 
			
		||||
};
 | 
			
		||||
enum BluetoothDeviceRequestType : uint32_t {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1,
 | 
			
		||||
@@ -1267,6 +1271,21 @@ class ButtonCommandRequest : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
};
 | 
			
		||||
class MediaPlayerSupportedFormat : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string format{};
 | 
			
		||||
  uint32_t sample_rate{0};
 | 
			
		||||
  uint32_t num_channels{0};
 | 
			
		||||
  enums::MediaPlayerFormatPurpose purpose{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
@@ -1277,6 +1296,7 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage {
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  bool supports_pause{false};
 | 
			
		||||
  std::vector<MediaPlayerSupportedFormat> supported_formats{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 
 | 
			
		||||
@@ -311,6 +311,14 @@ bool APIServerConnectionBase::send_list_entities_button_response(const ListEntit
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
bool APIServerConnectionBase::send_media_player_supported_format(const MediaPlayerSupportedFormat &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_media_player_supported_format: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<MediaPlayerSupportedFormat>(msg, 119);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str());
 | 
			
		||||
@@ -1135,6 +1143,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_update_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 119: {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
      MediaPlayerSupportedFormat msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_media_player_supported_format: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_media_player_supported_format(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -145,6 +145,10 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  virtual void on_button_command_request(const ButtonCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  bool send_media_player_supported_format(const MediaPlayerSupportedFormat &msg);
 | 
			
		||||
  virtual void on_media_player_supported_format(const MediaPlayerSupportedFormat &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +1,34 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, spi
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_REACTIVE_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_FORWARD_ACTIVE_ENERGY,
 | 
			
		||||
    CONF_FREQUENCY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LINE_FREQUENCY,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_POWER_FACTOR,
 | 
			
		||||
    CONF_FREQUENCY,
 | 
			
		||||
    CONF_FORWARD_ACTIVE_ENERGY,
 | 
			
		||||
    CONF_REACTIVE_POWER,
 | 
			
		||||
    CONF_REVERSE_ACTIVE_ENERGY,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_POWER_FACTOR,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ICON_LIGHTBULB,
 | 
			
		||||
    ICON_CURRENT_AC,
 | 
			
		||||
    ICON_LIGHTBULB,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    STATE_CLASS_TOTAL_INCREASING,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_HERTZ,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    UNIT_VOLT_AMPS_REACTIVE,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    UNIT_WATT_HOURS,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_LINE_FREQUENCY = "line_frequency"
 | 
			
		||||
CONF_METER_CONSTANT = "meter_constant"
 | 
			
		||||
CONF_PL_CONST = "pl_const"
 | 
			
		||||
CONF_GAIN_PGA = "gain_pga"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_FORWARD_ACTIVE_ENERGY,
 | 
			
		||||
    CONF_FREQUENCY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LINE_FREQUENCY,
 | 
			
		||||
    CONF_PHASE_A,
 | 
			
		||||
    CONF_PHASE_ANGLE,
 | 
			
		||||
    CONF_PHASE_B,
 | 
			
		||||
@@ -39,7 +40,6 @@ from esphome.const import (
 | 
			
		||||
 | 
			
		||||
from . import atm90e32_ns
 | 
			
		||||
 | 
			
		||||
CONF_LINE_FREQUENCY = "line_frequency"
 | 
			
		||||
CONF_CHIP_TEMPERATURE = "chip_temperature"
 | 
			
		||||
CONF_GAIN_PGA = "gain_pga"
 | 
			
		||||
CONF_CURRENT_PHASES = "current_phases"
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
// Datasheet: https://www.belling.com.cn/media/file_object/bel_product/BL0942/datasheet/BL0942_V1.06_en.pdf
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0942 {
 | 
			
		||||
 | 
			
		||||
@@ -12,33 +14,41 @@ static const uint8_t BL0942_FULL_PACKET = 0xAA;
 | 
			
		||||
static const uint8_t BL0942_PACKET_HEADER = 0x55;
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0942_WRITE_COMMAND = 0xA8;
 | 
			
		||||
static const uint8_t BL0942_REG_I_FAST_RMS_CTRL = 0x10;
 | 
			
		||||
static const uint8_t BL0942_REG_MODE = 0x18;
 | 
			
		||||
static const uint8_t BL0942_REG_SOFT_RESET = 0x19;
 | 
			
		||||
static const uint8_t BL0942_REG_USR_WRPROT = 0x1A;
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0942_REG_I_RMSOS = 0x12;
 | 
			
		||||
static const uint8_t BL0942_REG_WA_CREEP = 0x14;
 | 
			
		||||
static const uint8_t BL0942_REG_I_FAST_RMS_TH = 0x15;
 | 
			
		||||
static const uint8_t BL0942_REG_I_FAST_RMS_CYC = 0x16;
 | 
			
		||||
static const uint8_t BL0942_REG_FREQ_CYC = 0x17;
 | 
			
		||||
static const uint8_t BL0942_REG_OT_FUNX = 0x18;
 | 
			
		||||
static const uint8_t BL0942_REG_MODE = 0x19;
 | 
			
		||||
static const uint8_t BL0942_REG_SOFT_RESET = 0x1C;
 | 
			
		||||
static const uint8_t BL0942_REG_USR_WRPROT = 0x1D;
 | 
			
		||||
static const uint8_t BL0942_REG_TPS_CTRL = 0x1B;
 | 
			
		||||
 | 
			
		||||
// TODO: Confirm insialisation works as intended
 | 
			
		||||
const uint8_t BL0942_INIT[5][6] = {
 | 
			
		||||
    // Reset to default
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
 | 
			
		||||
    // Enable User Operation Write
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
 | 
			
		||||
    // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37},
 | 
			
		||||
    // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
 | 
			
		||||
    // 0x181C = Half cycle, Fast RMS threshold 6172
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_RESV = 0x03;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_CF_EN = 0x04;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_RMS_UPDATE_SEL = 0x08;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_FAST_RMS_SEL = 0x10;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_AC_FREQ_SEL = 0x20;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_CF_CNT_CLR_SEL = 0x40;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_CF_CNT_ADD_SEL = 0x80;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_UART_RATE_19200 = 0x200;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_UART_RATE_38400 = 0x300;
 | 
			
		||||
static const uint32_t BL0942_REG_MODE_DEFAULT =
 | 
			
		||||
    BL0942_REG_MODE_RESV | BL0942_REG_MODE_CF_EN | BL0942_REG_MODE_CF_CNT_ADD_SEL;
 | 
			
		||||
 | 
			
		||||
static const uint32_t BL0942_REG_SOFT_RESET_MAGIC = 0x5a5a5a;
 | 
			
		||||
static const uint32_t BL0942_REG_USR_WRPROT_MAGIC = 0x55;
 | 
			
		||||
 | 
			
		||||
void BL0942::loop() {
 | 
			
		||||
  DataPacket buffer;
 | 
			
		||||
  if (!this->available()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
 | 
			
		||||
    if (validate_checksum(&buffer)) {
 | 
			
		||||
      received_package_(&buffer);
 | 
			
		||||
  if (this->read_array((uint8_t *) &buffer, sizeof(buffer))) {
 | 
			
		||||
    if (this->validate_checksum_(&buffer)) {
 | 
			
		||||
      this->received_package_(&buffer);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
 | 
			
		||||
@@ -47,8 +57,8 @@ void BL0942::loop() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BL0942::validate_checksum(DataPacket *data) {
 | 
			
		||||
  uint8_t checksum = BL0942_READ_COMMAND;
 | 
			
		||||
bool BL0942::validate_checksum_(DataPacket *data) {
 | 
			
		||||
  uint8_t checksum = BL0942_READ_COMMAND | this->address_;
 | 
			
		||||
  // Whole package but checksum
 | 
			
		||||
  uint8_t *raw = (uint8_t *) data;
 | 
			
		||||
  for (uint32_t i = 0; i < sizeof(*data) - 1; i++) {
 | 
			
		||||
@@ -61,17 +71,58 @@ bool BL0942::validate_checksum(DataPacket *data) {
 | 
			
		||||
  return checksum == data->checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::update() {
 | 
			
		||||
void BL0942::write_reg_(uint8_t reg, uint32_t val) {
 | 
			
		||||
  uint8_t pkt[6];
 | 
			
		||||
 | 
			
		||||
  this->flush();
 | 
			
		||||
  this->write_byte(BL0942_READ_COMMAND);
 | 
			
		||||
  pkt[0] = BL0942_WRITE_COMMAND | this->address_;
 | 
			
		||||
  pkt[1] = reg;
 | 
			
		||||
  pkt[2] = (val & 0xff);
 | 
			
		||||
  pkt[3] = (val >> 8) & 0xff;
 | 
			
		||||
  pkt[4] = (val >> 16) & 0xff;
 | 
			
		||||
  pkt[5] = (pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4]) ^ 0xff;
 | 
			
		||||
  this->write_array(pkt, 6);
 | 
			
		||||
  delay(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BL0942::read_reg_(uint8_t reg) {
 | 
			
		||||
  union {
 | 
			
		||||
    uint8_t b[4];
 | 
			
		||||
    uint32_le_t le32;
 | 
			
		||||
  } resp;
 | 
			
		||||
 | 
			
		||||
  this->write_byte(BL0942_READ_COMMAND | this->address_);
 | 
			
		||||
  this->write_byte(reg);
 | 
			
		||||
  this->flush();
 | 
			
		||||
  if (this->read_array(resp.b, 4) &&
 | 
			
		||||
      resp.b[3] ==
 | 
			
		||||
          (uint8_t) ((BL0942_READ_COMMAND + this->address_ + reg + resp.b[0] + resp.b[1] + resp.b[2]) ^ 0xff)) {
 | 
			
		||||
    resp.b[3] = 0;
 | 
			
		||||
    return resp.le32;
 | 
			
		||||
  }
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::update() {
 | 
			
		||||
  this->write_byte(BL0942_READ_COMMAND | this->address_);
 | 
			
		||||
  this->write_byte(BL0942_FULL_PACKET);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::setup() {
 | 
			
		||||
  for (auto *i : BL0942_INIT) {
 | 
			
		||||
    this->write_array(i, 6);
 | 
			
		||||
    delay(1);
 | 
			
		||||
  }
 | 
			
		||||
  this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC);
 | 
			
		||||
  this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC);
 | 
			
		||||
 | 
			
		||||
  uint32_t mode = BL0942_REG_MODE_DEFAULT;
 | 
			
		||||
  mode |= BL0942_REG_MODE_RMS_UPDATE_SEL; /* 800ms refresh time */
 | 
			
		||||
  if (this->line_freq_ == LINE_FREQUENCY_60HZ)
 | 
			
		||||
    mode |= BL0942_REG_MODE_AC_FREQ_SEL;
 | 
			
		||||
  this->write_reg_(BL0942_REG_MODE, mode);
 | 
			
		||||
 | 
			
		||||
  this->write_reg_(BL0942_REG_USR_WRPROT, 0);
 | 
			
		||||
 | 
			
		||||
  if (this->read_reg_(BL0942_REG_MODE) != mode)
 | 
			
		||||
    this->status_set_warning("BL0942 setup failed!");
 | 
			
		||||
 | 
			
		||||
  this->flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -104,13 +155,15 @@ void BL0942::received_package_(DataPacket *data) {
 | 
			
		||||
  if (frequency_sensor_ != nullptr) {
 | 
			
		||||
    frequency_sensor_->publish_state(frequency);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
  ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms,
 | 
			
		||||
           watt, cf_cnt, total_energy_consumption, frequency, data->status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BL0942:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Address: %d", this->address_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Nominal line frequency: %d Hz", this->line_freq_);
 | 
			
		||||
  LOG_SENSOR("", "Voltage", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Current", this->current_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Power", this->power_sensor_);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,11 @@ struct DataPacket {
 | 
			
		||||
  uint8_t checksum;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
enum LineFrequency : uint8_t {
 | 
			
		||||
  LINE_FREQUENCY_50HZ = 50,
 | 
			
		||||
  LINE_FREQUENCY_60HZ = 60,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BL0942 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
@@ -35,9 +40,10 @@ class BL0942 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
  void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
 | 
			
		||||
  void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; }
 | 
			
		||||
  void set_address(uint8_t address) { this->address_ = address; }
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -59,9 +65,12 @@ class BL0942 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
  float current_reference_ = BL0942_IREF;
 | 
			
		||||
  // Divide by this to turn into kWh
 | 
			
		||||
  float energy_reference_ = BL0942_EREF;
 | 
			
		||||
  uint8_t address_ = 0;
 | 
			
		||||
  LineFrequency line_freq_ = LINE_FREQUENCY_50HZ;
 | 
			
		||||
 | 
			
		||||
  static bool validate_checksum(DataPacket *data);
 | 
			
		||||
 | 
			
		||||
  bool validate_checksum_(DataPacket *data);
 | 
			
		||||
  int read_reg_(uint8_t reg);
 | 
			
		||||
  void write_reg_(uint8_t reg, uint32_t val);
 | 
			
		||||
  void received_package_(DataPacket *data);
 | 
			
		||||
};
 | 
			
		||||
}  // namespace bl0942
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,27 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, uart
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_ENERGY,
 | 
			
		||||
    CONF_FREQUENCY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LINE_FREQUENCY,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    CONF_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    STATE_CLASS_TOTAL_INCREASING,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_HERTZ,
 | 
			
		||||
    UNIT_KILOWATT_HOURS,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    UNIT_HERTZ,
 | 
			
		||||
    STATE_CLASS_TOTAL_INCREASING,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
@@ -27,6 +29,12 @@ DEPENDENCIES = ["uart"]
 | 
			
		||||
bl0942_ns = cg.esphome_ns.namespace("bl0942")
 | 
			
		||||
BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice)
 | 
			
		||||
 | 
			
		||||
LineFrequency = bl0942_ns.enum("LineFrequency")
 | 
			
		||||
LINE_FREQS = {
 | 
			
		||||
    50: LineFrequency.LINE_FREQUENCY_50HZ,
 | 
			
		||||
    60: LineFrequency.LINE_FREQUENCY_60HZ,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
@@ -61,6 +69,14 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                device_class=DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_LINE_FREQUENCY, default="50HZ"): cv.All(
 | 
			
		||||
                cv.frequency,
 | 
			
		||||
                cv.enum(
 | 
			
		||||
                    LINE_FREQS,
 | 
			
		||||
                    int=True,
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ADDRESS, default=0): cv.int_range(min=0, max=3),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
@@ -88,3 +104,5 @@ async def to_code(config):
 | 
			
		||||
    if frequency_config := config.get(CONF_FREQUENCY):
 | 
			
		||||
        sens = await sensor.new_sensor(frequency_config)
 | 
			
		||||
        cg.add(var.set_frequency_sensor(sens))
 | 
			
		||||
    cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
 | 
			
		||||
    cg.add(var.set_address(config[CONF_ADDRESS]))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "captive_portal.h"
 | 
			
		||||
#ifdef USE_CAPTIVE_PORTAL
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/components/wifi/wifi_component.h"
 | 
			
		||||
@@ -91,3 +92,4 @@ CaptivePortal *global_captive_portal = nullptr;  // NOLINT(cppcoreguidelines-avo
 | 
			
		||||
 | 
			
		||||
}  // namespace captive_portal
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_CAPTIVE_PORTAL
 | 
			
		||||
#include <memory>
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
@@ -71,3 +72,4 @@ extern CaptivePortal *global_captive_portal;  // NOLINT(cppcoreguidelines-avoid-
 | 
			
		||||
 | 
			
		||||
}  // namespace captive_portal
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "e131.h"
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
#include "e131_addressable_light_effect.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
@@ -118,3 +119,4 @@ bool E131Component::process_(int universe, const E131Packet &packet) {
 | 
			
		||||
 | 
			
		||||
}  // namespace e131
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
@@ -53,3 +54,4 @@ class E131Component : public esphome::Component {
 | 
			
		||||
 | 
			
		||||
}  // namespace e131
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "e131_addressable_light_effect.h"
 | 
			
		||||
#include "e131.h"
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -90,3 +91,4 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
 | 
			
		||||
 | 
			
		||||
}  // namespace e131
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/light/addressable_light_effect.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace e131 {
 | 
			
		||||
 | 
			
		||||
@@ -42,3 +42,4 @@ class E131AddressableLightEffect : public light::AddressableLightEffect {
 | 
			
		||||
 | 
			
		||||
}  // namespace e131
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "e131.h"
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
#include "esphome/components/network/ip_address.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
@@ -137,3 +138,4 @@ bool E131Component::packet_(const std::vector<uint8_t> &data, int &universe, E13
 | 
			
		||||
 | 
			
		||||
}  // namespace e131
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -172,6 +172,19 @@ def add_idf_component(
 | 
			
		||||
            KEY_COMPONENTS: components,
 | 
			
		||||
            KEY_SUBMODULES: submodules,
 | 
			
		||||
        }
 | 
			
		||||
    else:
 | 
			
		||||
        component_config = CORE.data[KEY_ESP32][KEY_COMPONENTS][name]
 | 
			
		||||
        if components is not None:
 | 
			
		||||
            component_config[KEY_COMPONENTS] = list(
 | 
			
		||||
                set(component_config[KEY_COMPONENTS] + components)
 | 
			
		||||
            )
 | 
			
		||||
        if submodules is not None:
 | 
			
		||||
            if component_config[KEY_SUBMODULES] is None:
 | 
			
		||||
                component_config[KEY_SUBMODULES] = submodules
 | 
			
		||||
            else:
 | 
			
		||||
                component_config[KEY_SUBMODULES] = list(
 | 
			
		||||
                    set(component_config[KEY_SUBMODULES] + submodules)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_extra_script(stage: str, filename: str, path: str):
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,8 @@ void ESP32RMTLEDStripLightOutput::setup() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ExternalRAMAllocator<rmt_item32_t> rmt_allocator(ExternalRAMAllocator<rmt_item32_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8);  // 8 bits per byte, 1 rmt_item32_t per bit
 | 
			
		||||
  this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 +
 | 
			
		||||
                                          1);  // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset
 | 
			
		||||
 | 
			
		||||
  rmt_config_t config;
 | 
			
		||||
  memset(&config, 0, sizeof(config));
 | 
			
		||||
@@ -66,7 +67,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
 | 
			
		||||
                                                 uint32_t bit1_low) {
 | 
			
		||||
                                                 uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) {
 | 
			
		||||
  float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f;
 | 
			
		||||
 | 
			
		||||
  // 0-bit
 | 
			
		||||
@@ -79,6 +80,11 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi
 | 
			
		||||
  this->bit1_.level0 = 1;
 | 
			
		||||
  this->bit1_.duration1 = (uint32_t) (ratio * bit1_low);
 | 
			
		||||
  this->bit1_.level1 = 0;
 | 
			
		||||
  // reset
 | 
			
		||||
  this->reset_.duration0 = (uint32_t) (ratio * reset_time_high);
 | 
			
		||||
  this->reset_.level0 = 1;
 | 
			
		||||
  this->reset_.duration1 = (uint32_t) (ratio * reset_time_low);
 | 
			
		||||
  this->reset_.level1 = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
 | 
			
		||||
@@ -118,6 +124,12 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
 | 
			
		||||
    psrc++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->reset_.duration0 > 0 || this->reset_.duration1 > 0) {
 | 
			
		||||
    pdest->val = this->reset_.val;
 | 
			
		||||
    pdest++;
 | 
			
		||||
    len++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "RMT TX error");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,8 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
 | 
			
		||||
  /// Set a maximum refresh rate in µs as some lights do not like being updated too often.
 | 
			
		||||
  void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
 | 
			
		||||
 | 
			
		||||
  void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low);
 | 
			
		||||
  void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low,
 | 
			
		||||
                      uint32_t reset_time_high, uint32_t reset_time_low);
 | 
			
		||||
 | 
			
		||||
  void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
 | 
			
		||||
  void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; }
 | 
			
		||||
@@ -75,7 +76,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
 | 
			
		||||
  bool is_rgbw_;
 | 
			
		||||
  bool is_wrgb_;
 | 
			
		||||
 | 
			
		||||
  rmt_item32_t bit0_, bit1_;
 | 
			
		||||
  rmt_item32_t bit0_, bit1_, reset_;
 | 
			
		||||
  RGBOrder rgb_order_;
 | 
			
		||||
  rmt_channel_t channel_;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,13 +43,15 @@ class LEDStripTimings:
 | 
			
		||||
    bit0_low: int
 | 
			
		||||
    bit1_high: int
 | 
			
		||||
    bit1_low: int
 | 
			
		||||
    reset_high: int
 | 
			
		||||
    reset_low: int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CHIPSETS = {
 | 
			
		||||
    "WS2812": LEDStripTimings(400, 1000, 1000, 400),
 | 
			
		||||
    "SK6812": LEDStripTimings(300, 900, 600, 600),
 | 
			
		||||
    "APA106": LEDStripTimings(350, 1360, 1360, 350),
 | 
			
		||||
    "SM16703": LEDStripTimings(300, 900, 900, 300),
 | 
			
		||||
    "WS2812": LEDStripTimings(400, 1000, 1000, 400, 0, 0),
 | 
			
		||||
    "SK6812": LEDStripTimings(300, 900, 600, 600, 0, 0),
 | 
			
		||||
    "APA106": LEDStripTimings(350, 1360, 1360, 350, 0, 0),
 | 
			
		||||
    "SM16703": LEDStripTimings(300, 900, 900, 300, 0, 0),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -58,6 +60,8 @@ CONF_BIT0_HIGH = "bit0_high"
 | 
			
		||||
CONF_BIT0_LOW = "bit0_low"
 | 
			
		||||
CONF_BIT1_HIGH = "bit1_high"
 | 
			
		||||
CONF_BIT1_LOW = "bit1_low"
 | 
			
		||||
CONF_RESET_HIGH = "reset_high"
 | 
			
		||||
CONF_RESET_LOW = "reset_low"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
@@ -88,6 +92,14 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
                CONF_BIT1_LOW,
 | 
			
		||||
                "custom",
 | 
			
		||||
            ): cv.positive_time_period_nanoseconds,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_RESET_HIGH,
 | 
			
		||||
                default="0 us",
 | 
			
		||||
            ): cv.positive_time_period_nanoseconds,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_RESET_LOW,
 | 
			
		||||
                default="0 us",
 | 
			
		||||
            ): cv.positive_time_period_nanoseconds,
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
 | 
			
		||||
@@ -113,6 +125,8 @@ async def to_code(config):
 | 
			
		||||
                chipset.bit0_low,
 | 
			
		||||
                chipset.bit1_high,
 | 
			
		||||
                chipset.bit1_low,
 | 
			
		||||
                chipset.reset_high,
 | 
			
		||||
                chipset.reset_low,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
@@ -122,6 +136,8 @@ async def to_code(config):
 | 
			
		||||
                config[CONF_BIT0_LOW],
 | 
			
		||||
                config[CONF_BIT1_HIGH],
 | 
			
		||||
                config[CONF_BIT1_LOW],
 | 
			
		||||
                config[CONF_RESET_HIGH],
 | 
			
		||||
                config[CONF_RESET_LOW],
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include "ota_esphome.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_OTA
 | 
			
		||||
#include "esphome/components/md5/md5.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/components/ota/ota_backend.h"
 | 
			
		||||
@@ -410,3 +410,4 @@ float ESPHomeOTAComponent::get_setup_priority() const { return setup_priority::A
 | 
			
		||||
uint16_t ESPHomeOTAComponent::get_port() const { return this->port_; }
 | 
			
		||||
void ESPHomeOTAComponent::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_OTA
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
#include "esphome/components/ota/ota_backend.h"
 | 
			
		||||
@@ -41,3 +42,4 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								esphome/components/hmac_md5/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								esphome/components/hmac_md5/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
AUTO_LOAD = ["md5"]
 | 
			
		||||
CODEOWNERS = ["@dwmw2"]
 | 
			
		||||
							
								
								
									
										56
									
								
								esphome/components/hmac_md5/hmac_md5.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								esphome/components/hmac_md5/hmac_md5.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "hmac_md5.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace hmac_md5 {
 | 
			
		||||
void HmacMD5::init(const uint8_t *key, size_t len) {
 | 
			
		||||
  uint8_t ipad[64], opad[64];
 | 
			
		||||
 | 
			
		||||
  memset(ipad, 0, sizeof(ipad));
 | 
			
		||||
  if (len > 64) {
 | 
			
		||||
    md5::MD5Digest keymd5;
 | 
			
		||||
    keymd5.init();
 | 
			
		||||
    keymd5.add(key, len);
 | 
			
		||||
    keymd5.calculate();
 | 
			
		||||
    keymd5.get_bytes(ipad);
 | 
			
		||||
  } else {
 | 
			
		||||
    memcpy(ipad, key, len);
 | 
			
		||||
  }
 | 
			
		||||
  memcpy(opad, ipad, sizeof(opad));
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < 64; i++) {
 | 
			
		||||
    ipad[i] ^= 0x36;
 | 
			
		||||
    opad[i] ^= 0x5c;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->ihash_.init();
 | 
			
		||||
  this->ihash_.add(ipad, sizeof(ipad));
 | 
			
		||||
 | 
			
		||||
  this->ohash_.init();
 | 
			
		||||
  this->ohash_.add(opad, sizeof(opad));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HmacMD5::add(const uint8_t *data, size_t len) { this->ihash_.add(data, len); }
 | 
			
		||||
 | 
			
		||||
void HmacMD5::calculate() {
 | 
			
		||||
  uint8_t ibytes[16];
 | 
			
		||||
 | 
			
		||||
  this->ihash_.calculate();
 | 
			
		||||
  this->ihash_.get_bytes(ibytes);
 | 
			
		||||
 | 
			
		||||
  this->ohash_.add(ibytes, sizeof(ibytes));
 | 
			
		||||
  this->ohash_.calculate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HmacMD5::get_bytes(uint8_t *output) { this->ohash_.get_bytes(output); }
 | 
			
		||||
 | 
			
		||||
void HmacMD5::get_hex(char *output) { this->ohash_.get_hex(output); }
 | 
			
		||||
 | 
			
		||||
bool HmacMD5::equals_bytes(const uint8_t *expected) { return this->ohash_.equals_bytes(expected); }
 | 
			
		||||
 | 
			
		||||
bool HmacMD5::equals_hex(const char *expected) { return this->ohash_.equals_hex(expected); }
 | 
			
		||||
 | 
			
		||||
}  // namespace hmac_md5
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										48
									
								
								esphome/components/hmac_md5/hmac_md5.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								esphome/components/hmac_md5/hmac_md5.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/components/md5/md5.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace hmac_md5 {
 | 
			
		||||
 | 
			
		||||
class HmacMD5 {
 | 
			
		||||
 public:
 | 
			
		||||
  HmacMD5() = default;
 | 
			
		||||
  ~HmacMD5() = default;
 | 
			
		||||
 | 
			
		||||
  /// Initialize a new MD5 digest computation.
 | 
			
		||||
  void init(const uint8_t *key, size_t len);
 | 
			
		||||
  void init(const char *key, size_t len) { this->init((const uint8_t *) key, len); }
 | 
			
		||||
  void init(const std::string &key) { this->init(key.c_str(), key.length()); }
 | 
			
		||||
 | 
			
		||||
  /// Add bytes of data for the digest.
 | 
			
		||||
  void add(const uint8_t *data, size_t len);
 | 
			
		||||
  void add(const char *data, size_t len) { this->add((const uint8_t *) data, len); }
 | 
			
		||||
 | 
			
		||||
  /// Compute the digest, based on the provided data.
 | 
			
		||||
  void calculate();
 | 
			
		||||
 | 
			
		||||
  /// Retrieve the HMAC-MD5 digest as bytes.
 | 
			
		||||
  /// The output must be able to hold 16 bytes or more.
 | 
			
		||||
  void get_bytes(uint8_t *output);
 | 
			
		||||
 | 
			
		||||
  /// Retrieve the HMAC-MD5 digest as hex characters.
 | 
			
		||||
  /// The output must be able to hold 32 bytes or more.
 | 
			
		||||
  void get_hex(char *output);
 | 
			
		||||
 | 
			
		||||
  /// Compare the digest against a provided byte-encoded digest (16 bytes).
 | 
			
		||||
  bool equals_bytes(const uint8_t *expected);
 | 
			
		||||
 | 
			
		||||
  /// Compare the digest against a provided hex-encoded digest (32 bytes).
 | 
			
		||||
  bool equals_hex(const char *expected);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  md5::MD5Digest ihash_;
 | 
			
		||||
  md5::MD5Digest ohash_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace hmac_md5
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,31 +1,31 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import core, pins
 | 
			
		||||
from esphome.components import display, spi, font
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import display, font, spi
 | 
			
		||||
from esphome.components.display import validate_rotation
 | 
			
		||||
from esphome.core import CORE, HexInt
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_COLOR_ORDER,
 | 
			
		||||
    CONF_COLOR_PALETTE,
 | 
			
		||||
    CONF_DC_PIN,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LAMBDA,
 | 
			
		||||
    CONF_MODEL,
 | 
			
		||||
    CONF_RAW_DATA_ID,
 | 
			
		||||
    CONF_PAGES,
 | 
			
		||||
    CONF_RESET_PIN,
 | 
			
		||||
    CONF_DIMENSIONS,
 | 
			
		||||
    CONF_WIDTH,
 | 
			
		||||
    CONF_HEIGHT,
 | 
			
		||||
    CONF_ROTATION,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_INVERT_COLORS,
 | 
			
		||||
    CONF_LAMBDA,
 | 
			
		||||
    CONF_MIRROR_X,
 | 
			
		||||
    CONF_MIRROR_Y,
 | 
			
		||||
    CONF_SWAP_XY,
 | 
			
		||||
    CONF_COLOR_ORDER,
 | 
			
		||||
    CONF_MODEL,
 | 
			
		||||
    CONF_OFFSET_HEIGHT,
 | 
			
		||||
    CONF_OFFSET_WIDTH,
 | 
			
		||||
    CONF_PAGES,
 | 
			
		||||
    CONF_RAW_DATA_ID,
 | 
			
		||||
    CONF_RESET_PIN,
 | 
			
		||||
    CONF_ROTATION,
 | 
			
		||||
    CONF_SWAP_XY,
 | 
			
		||||
    CONF_TRANSFORM,
 | 
			
		||||
    CONF_INVERT_COLORS,
 | 
			
		||||
    CONF_WIDTH,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, HexInt
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["spi"]
 | 
			
		||||
 | 
			
		||||
@@ -177,7 +177,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_INVERT_DISPLAY): cv.invalid(
 | 
			
		||||
                "'invert_display' has been replaced by 'invert_colors'"
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_INVERT_COLORS): cv.boolean,
 | 
			
		||||
            cv.Required(CONF_INVERT_COLORS): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_COLOR_ORDER): cv.one_of(*COLOR_ORDERS.keys(), upper=True),
 | 
			
		||||
            cv.Exclusive(CONF_ROTATION, CONF_ROTATION): validate_rotation,
 | 
			
		||||
            cv.Exclusive(CONF_TRANSFORM, CONF_ROTATION): cv.Schema(
 | 
			
		||||
@@ -287,5 +287,4 @@ async def to_code(config):
 | 
			
		||||
        prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
 | 
			
		||||
        cg.add(var.set_palette(prog_arr))
 | 
			
		||||
 | 
			
		||||
    if CONF_INVERT_COLORS in config:
 | 
			
		||||
        cg.add(var.invert_colors(config[CONF_INVERT_COLORS]))
 | 
			
		||||
    cg.add(var.invert_colors(config[CONF_INVERT_COLORS]))
 | 
			
		||||
 
 | 
			
		||||
@@ -118,6 +118,7 @@ void ILI9XXXDisplay::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Swap_xy: %s", YESNO(this->swap_xy_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Mirror_x: %s", YESNO(this->mirror_x_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Mirror_y: %s", YESNO(this->mirror_y_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Invert colors: %s", YESNO(this->pre_invertcolors_));
 | 
			
		||||
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  => Failed to init Memory: YES!");
 | 
			
		||||
@@ -154,7 +155,6 @@ void ILI9XXXDisplay::fill(Color color) {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB);
 | 
			
		||||
      break;
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
 | 
			
		||||
                                             spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_40MHZ> {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXDisplay() = default;
 | 
			
		||||
  ILI9XXXDisplay(uint8_t const *init_sequence, int16_t width, int16_t height, bool invert_colors)
 | 
			
		||||
      : init_sequence_{init_sequence}, width_{width}, height_{height}, pre_invertcolors_{invert_colors} {
 | 
			
		||||
  ILI9XXXDisplay(uint8_t const *init_sequence, int16_t width, int16_t height)
 | 
			
		||||
      : init_sequence_{init_sequence}, width_{width}, height_{height} {
 | 
			
		||||
    uint8_t cmd, num_args, bits;
 | 
			
		||||
    const uint8_t *addr = init_sequence;
 | 
			
		||||
    while ((cmd = *addr++) != 0) {
 | 
			
		||||
@@ -144,7 +144,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
 | 
			
		||||
  bool need_update_ = false;
 | 
			
		||||
  bool is_18bitdisplay_ = false;
 | 
			
		||||
  PixelMode pixel_mode_{};
 | 
			
		||||
  bool pre_invertcolors_ = false;
 | 
			
		||||
  bool pre_invertcolors_{};
 | 
			
		||||
  display::ColorOrder color_order_{display::COLOR_ORDER_BGR};
 | 
			
		||||
  bool swap_xy_{};
 | 
			
		||||
  bool mirror_x_{};
 | 
			
		||||
@@ -154,54 +154,54 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
 | 
			
		||||
//-----------   M5Stack display --------------
 | 
			
		||||
class ILI9XXXM5Stack : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXM5Stack() : ILI9XXXDisplay(INITCMD_M5STACK, 320, 240, true) {}
 | 
			
		||||
  ILI9XXXM5Stack() : ILI9XXXDisplay(INITCMD_M5STACK, 320, 240) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   M5Stack display --------------
 | 
			
		||||
class ILI9XXXM5CORE : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXM5CORE() : ILI9XXXDisplay(INITCMD_M5CORE, 320, 240, true) {}
 | 
			
		||||
  ILI9XXXM5CORE() : ILI9XXXDisplay(INITCMD_M5CORE, 320, 240) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ST7789V display --------------
 | 
			
		||||
class ILI9XXXST7789V : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXST7789V() : ILI9XXXDisplay(INITCMD_ST7789V, 240, 320, false) {}
 | 
			
		||||
  ILI9XXXST7789V() : ILI9XXXDisplay(INITCMD_ST7789V, 240, 320) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_24_TFT display --------------
 | 
			
		||||
class ILI9XXXILI9341 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI9341() : ILI9XXXDisplay(INITCMD_ILI9341, 240, 320, false) {}
 | 
			
		||||
  ILI9XXXILI9341() : ILI9XXXDisplay(INITCMD_ILI9341, 240, 320) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_24_TFT rotated display --------------
 | 
			
		||||
class ILI9XXXILI9342 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI9342() : ILI9XXXDisplay(INITCMD_ILI9341, 320, 240, false) {}
 | 
			
		||||
  ILI9XXXILI9342() : ILI9XXXDisplay(INITCMD_ILI9341, 320, 240) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_??_TFT rotated display --------------
 | 
			
		||||
class ILI9XXXILI9481 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI9481() : ILI9XXXDisplay(INITCMD_ILI9481, 480, 320, false) {}
 | 
			
		||||
  ILI9XXXILI9481() : ILI9XXXDisplay(INITCMD_ILI9481, 480, 320) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9481 in 18 bit mode --------------
 | 
			
		||||
class ILI9XXXILI948118 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI948118() : ILI9XXXDisplay(INITCMD_ILI9481_18, 320, 480, true) {}
 | 
			
		||||
  ILI9XXXILI948118() : ILI9XXXDisplay(INITCMD_ILI9481_18, 320, 480) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_35_TFT rotated display --------------
 | 
			
		||||
class ILI9XXXILI9486 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320, false) {}
 | 
			
		||||
  ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ILI9XXXILI9488 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI9488(const uint8_t *seq = INITCMD_ILI9488) : ILI9XXXDisplay(seq, 480, 320, true) {}
 | 
			
		||||
  ILI9XXXILI9488(const uint8_t *seq = INITCMD_ILI9488) : ILI9XXXDisplay(seq, 480, 320) {}
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void set_madctl() override {
 | 
			
		||||
@@ -246,34 +246,34 @@ class WAVESHARERES35 : public ILI9XXXILI9488 {
 | 
			
		||||
//-----------   ILI9XXX_35_TFT origin colors rotated display --------------
 | 
			
		||||
class ILI9XXXILI9488A : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXILI9488A() : ILI9XXXDisplay(INITCMD_ILI9488_A, 480, 320, true) {}
 | 
			
		||||
  ILI9XXXILI9488A() : ILI9XXXDisplay(INITCMD_ILI9488_A, 480, 320) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_35_TFT rotated display --------------
 | 
			
		||||
class ILI9XXXST7796 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXST7796() : ILI9XXXDisplay(INITCMD_ST7796, 320, 480, false) {}
 | 
			
		||||
  ILI9XXXST7796() : ILI9XXXDisplay(INITCMD_ST7796, 320, 480) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ILI9XXXS3Box : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXS3Box() : ILI9XXXDisplay(INITCMD_S3BOX, 320, 240, false) {}
 | 
			
		||||
  ILI9XXXS3Box() : ILI9XXXDisplay(INITCMD_S3BOX, 320, 240) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ILI9XXXS3BoxLite : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240, true) {}
 | 
			
		||||
  ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ILI9XXXGC9A01A : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXGC9A01A() : ILI9XXXDisplay(INITCMD_GC9A01A, 240, 240, true) {}
 | 
			
		||||
  ILI9XXXGC9A01A() : ILI9XXXDisplay(INITCMD_GC9A01A, 240, 240) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_24_TFT display --------------
 | 
			
		||||
class ILI9XXXST7735 : public ILI9XXXDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  ILI9XXXST7735() : ILI9XXXDisplay(INITCMD_ST7735, 128, 160, false) {}
 | 
			
		||||
  ILI9XXXST7735() : ILI9XXXDisplay(INITCMD_ST7735, 128, 160) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ili9xxx
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,6 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = {
 | 
			
		||||
  ILI9XXX_MADCTL  , 1, MADCTL_MV | MADCTL_BGR,       // Memory Access Control
 | 
			
		||||
  ILI9XXX_CSCON , 1, 0x01,
 | 
			
		||||
  ILI9XXX_PIXFMT, 1, 0x55,  // 16 bit mode
 | 
			
		||||
  ILI9XXX_INVON, 0,
 | 
			
		||||
  ILI9XXX_DISPON, 0x80,     // Set display on
 | 
			
		||||
  0x00 // end
 | 
			
		||||
};
 | 
			
		||||
@@ -121,7 +120,6 @@ static const uint8_t PROGMEM INITCMD_ILI9481_18[] = {
 | 
			
		||||
    ILI9XXX_MADCTL  , 1, MADCTL_MX| MADCTL_BGR,       // Memory Access Control
 | 
			
		||||
    ILI9XXX_CSCON , 1, 0x01,
 | 
			
		||||
    ILI9XXX_PIXFMT, 1, 0x66,  // 18 bit mode
 | 
			
		||||
    ILI9XXX_INVON, 0,
 | 
			
		||||
    ILI9XXX_DISPON, 0x80,     // Set display on
 | 
			
		||||
    0x00 // end
 | 
			
		||||
};
 | 
			
		||||
@@ -204,7 +202,6 @@ static const uint8_t PROGMEM INITCMD_ILI9488_A[] = {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ILI9XXX_SLPOUT,  0x80,    // Exit sleep mode
 | 
			
		||||
  //ILI9XXX_INVON  , 0,
 | 
			
		||||
  ILI9XXX_DISPON,  0x80,    // Set display on
 | 
			
		||||
  0x00 // end
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include "improv_serial_component.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_WIFI
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
@@ -313,3 +313,4 @@ ImprovSerialComponent *global_improv_serial_component =  // NOLINT(cppcoreguidel
 | 
			
		||||
 | 
			
		||||
}  // namespace improv_serial
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_WIFI
 | 
			
		||||
#include <improv.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -78,3 +78,4 @@ extern ImprovSerialComponent
 | 
			
		||||
 | 
			
		||||
}  // namespace improv_serial
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@
 | 
			
		||||
#endif
 | 
			
		||||
#include <driver/ledc.h>
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
#define CLOCK_FREQUENCY 80e6f
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
@@ -115,20 +117,22 @@ void LEDCOutput::write_state(float state) {
 | 
			
		||||
  const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1;
 | 
			
		||||
  const float duty_rounded = roundf(state * max_duty);
 | 
			
		||||
  auto duty = static_cast<uint32_t>(duty_rounded);
 | 
			
		||||
  ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_);
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
  ESP_LOGV(TAG, "Setting duty: %u on channel %u", duty, this->channel_);
 | 
			
		||||
  ledcWrite(this->channel_, duty);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
  // ensure that 100% on is not 99.975% on
 | 
			
		||||
  if ((duty == max_duty) && (max_duty != 1)) {
 | 
			
		||||
    duty = max_duty + 1;
 | 
			
		||||
  }
 | 
			
		||||
  auto speed_mode = get_speed_mode(channel_);
 | 
			
		||||
  auto chan_num = static_cast<ledc_channel_t>(channel_ % 8);
 | 
			
		||||
  int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_);
 | 
			
		||||
  ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint);
 | 
			
		||||
  ledc_update_duty(speed_mode, chan_num);
 | 
			
		||||
  if (duty == max_duty) {
 | 
			
		||||
    ledc_stop(speed_mode, chan_num, 1);
 | 
			
		||||
  } else if (duty == 0) {
 | 
			
		||||
    ledc_stop(speed_mode, chan_num, 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint);
 | 
			
		||||
    ledc_update_duty(speed_mode, chan_num);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -266,7 +266,10 @@ async def to_code(config):
 | 
			
		||||
        await add_top_layer(config)
 | 
			
		||||
        await msgboxes_to_code(config)
 | 
			
		||||
        await disp_update(f"{lv_component}->get_disp()", config)
 | 
			
		||||
        Widget.set_completed()
 | 
			
		||||
    # At this point only the setup code should be generated
 | 
			
		||||
    assert LvContext.added_lambda_count == 1
 | 
			
		||||
    Widget.set_completed()
 | 
			
		||||
    async with LvContext(lv_component):
 | 
			
		||||
        await generate_triggers(lv_component)
 | 
			
		||||
        for conf in config.get(CONF_ON_IDLE, ()):
 | 
			
		||||
            templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ from esphome import automation
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_TIMEOUT
 | 
			
		||||
from esphome.cpp_generator import RawExpression
 | 
			
		||||
from esphome.cpp_types import nullptr
 | 
			
		||||
 | 
			
		||||
from .defines import (
 | 
			
		||||
@@ -26,6 +27,7 @@ from .lvcode import (
 | 
			
		||||
    add_line_marks,
 | 
			
		||||
    lv,
 | 
			
		||||
    lv_add,
 | 
			
		||||
    lv_expr,
 | 
			
		||||
    lv_obj,
 | 
			
		||||
    lvgl_comp,
 | 
			
		||||
)
 | 
			
		||||
@@ -38,7 +40,13 @@ from .types import (
 | 
			
		||||
    lv_disp_t,
 | 
			
		||||
    lv_obj_t,
 | 
			
		||||
)
 | 
			
		||||
from .widgets import Widget, get_widgets, lv_scr_act, set_obj_properties
 | 
			
		||||
from .widgets import (
 | 
			
		||||
    Widget,
 | 
			
		||||
    get_widgets,
 | 
			
		||||
    lv_scr_act,
 | 
			
		||||
    set_obj_properties,
 | 
			
		||||
    wait_for_widgets,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def action_to_code(
 | 
			
		||||
@@ -48,10 +56,12 @@ async def action_to_code(
 | 
			
		||||
    template_arg,
 | 
			
		||||
    args,
 | 
			
		||||
):
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext(parameters=args, where=action_id) as context:
 | 
			
		||||
        with LvConditional(lv_expr.is_pre_initialise()):
 | 
			
		||||
            context.add(RawExpression("return"))
 | 
			
		||||
        for widget in widgets:
 | 
			
		||||
            with LvConditional(widget.obj != nullptr):
 | 
			
		||||
                await action(widget)
 | 
			
		||||
            await action(widget)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
@@ -147,7 +157,7 @@ async def lvgl_update_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    widgets = await get_widgets(config)
 | 
			
		||||
    w = widgets[0]
 | 
			
		||||
    disp = f"{w.obj}->get_disp()"
 | 
			
		||||
    async with LambdaContext(parameters=args, where=action_id) as context:
 | 
			
		||||
    async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context:
 | 
			
		||||
        await disp_update(disp, config)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
 | 
			
		||||
    await cg.register_parented(var, w.var)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET
 | 
			
		||||
from ..lvcode import EVENT_ARG, LambdaContext, LvContext
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, lv_pseudo_button_t
 | 
			
		||||
from ..widgets import Widget, get_widgets
 | 
			
		||||
from ..widgets import Widget, get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    binary_sensor_schema(BinarySensor)
 | 
			
		||||
@@ -29,6 +29,7 @@ async def to_code(config):
 | 
			
		||||
    widget = await get_widgets(config, CONF_WIDGET)
 | 
			
		||||
    widget = widget[0]
 | 
			
		||||
    assert isinstance(widget, Widget)
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as pressed_ctx:
 | 
			
		||||
        pressed_ctx.add(sensor.publish_state(widget.is_pressed()))
 | 
			
		||||
    async with LvContext(paren) as ctx:
 | 
			
		||||
 
 | 
			
		||||
@@ -505,4 +505,10 @@ DEFAULT_ESPHOME_FONT = "esphome_lv_default_font"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def join_enums(enums, prefix=""):
 | 
			
		||||
    return literal("|".join(f"(int){prefix}{e.upper()}" for e in enums))
 | 
			
		||||
    enums = list(enums)
 | 
			
		||||
    enums.sort()
 | 
			
		||||
    # If a prefix is provided, prepend each constant with the prefix, and assume that all the constants are within the
 | 
			
		||||
    # same namespace, otherwise cast to int to avoid triggering warnings about mixing enum types.
 | 
			
		||||
    if prefix:
 | 
			
		||||
        return literal("|".join(f"{prefix}{e.upper()}" for e in enums))
 | 
			
		||||
    return literal("|".join(f"(int){e.upper()}" for e in enums))
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ from ..defines import CONF_LVGL_ID
 | 
			
		||||
from ..lvcode import LvContext
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LvType, lvgl_ns
 | 
			
		||||
from ..widgets import get_widgets
 | 
			
		||||
from ..widgets import get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
lv_led_t = LvType("lv_led_t")
 | 
			
		||||
LVLight = lvgl_ns.class_("LVLight", LightOutput)
 | 
			
		||||
@@ -28,5 +28,6 @@ async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_LVGL_ID])
 | 
			
		||||
    widget = await get_widgets(config, CONF_LED)
 | 
			
		||||
    widget = widget[0]
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LvContext(paren) as ctx:
 | 
			
		||||
        ctx.add(var.set_obj(widget.obj))
 | 
			
		||||
 
 | 
			
		||||
@@ -176,6 +176,8 @@ class LvContext(LambdaContext):
 | 
			
		||||
    Code generation into the LVGL initialisation code (called in `setup()`)
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    added_lambda_count = 0
 | 
			
		||||
 | 
			
		||||
    def __init__(self, lv_component, args=None):
 | 
			
		||||
        self.args = args or LVGL_COMP_ARG
 | 
			
		||||
        super().__init__(parameters=self.args)
 | 
			
		||||
@@ -183,6 +185,7 @@ class LvContext(LambdaContext):
 | 
			
		||||
 | 
			
		||||
    async def add_init_lambda(self):
 | 
			
		||||
        cg.add(self.lv_component.add_init_lambda(await self.get_lambda()))
 | 
			
		||||
        LvContext.added_lambda_count += 1
 | 
			
		||||
 | 
			
		||||
    async def __aexit__(self, exc_type, exc_val, exc_tb):
 | 
			
		||||
        await super().__aexit__(exc_type, exc_val, exc_tb)
 | 
			
		||||
 
 | 
			
		||||
@@ -294,6 +294,13 @@ void LvglComponent::loop() {
 | 
			
		||||
  }
 | 
			
		||||
  lv_timer_handler_run_in_period(5);
 | 
			
		||||
}
 | 
			
		||||
bool lv_is_pre_initialise() {
 | 
			
		||||
  if (!lv_is_initialized()) {
 | 
			
		||||
    ESP_LOGE(TAG, "LVGL call before component is initialised");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LVGL_IMAGE
 | 
			
		||||
lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) {
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ namespace lvgl {
 | 
			
		||||
 | 
			
		||||
extern lv_event_code_t lv_api_event;     // NOLINT
 | 
			
		||||
extern lv_event_code_t lv_update_event;  // NOLINT
 | 
			
		||||
extern bool lv_is_pre_initialise();
 | 
			
		||||
#ifdef USE_LVGL_COLOR
 | 
			
		||||
inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); }
 | 
			
		||||
#endif  // USE_LVGL_COLOR
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ from ..lvcode import (
 | 
			
		||||
)
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, LvNumber, lvgl_ns
 | 
			
		||||
from ..widgets import get_widgets
 | 
			
		||||
from ..widgets import get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number)
 | 
			
		||||
 | 
			
		||||
@@ -44,11 +44,13 @@ async def to_code(config):
 | 
			
		||||
        step=widget.get_step(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext([(cg.float_, "v")]) as control:
 | 
			
		||||
        await widget.set_property(
 | 
			
		||||
            "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED]
 | 
			
		||||
        )
 | 
			
		||||
        lv.event_send(widget.obj, API_EVENT, cg.nullptr)
 | 
			
		||||
        control.add(var.publish_state(widget.get_value()))
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as event:
 | 
			
		||||
        event.add(var.publish_state(widget.get_value()))
 | 
			
		||||
    event_code = (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/number/number.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
@@ -11,7 +13,7 @@ namespace lvgl {
 | 
			
		||||
class LVGLNumber : public number::Number {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_control_lambda(std::function<void(float)> control_lambda) {
 | 
			
		||||
    this->control_lambda_ = control_lambda;
 | 
			
		||||
    this->control_lambda_ = std::move(control_lambda);
 | 
			
		||||
    if (this->initial_state_.has_value()) {
 | 
			
		||||
      this->control_lambda_(this->initial_state_.value());
 | 
			
		||||
      this->initial_state_.reset();
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ from ..lvcode import (
 | 
			
		||||
)
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, LvSelect, lvgl_ns
 | 
			
		||||
from ..widgets import get_widgets
 | 
			
		||||
from ..widgets import get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select)
 | 
			
		||||
 | 
			
		||||
@@ -37,11 +37,13 @@ async def to_code(config):
 | 
			
		||||
    options = widget.config.get(CONF_OPTIONS, [])
 | 
			
		||||
    selector = await select.new_select(config, options=options)
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_LVGL_ID])
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as pub_ctx:
 | 
			
		||||
        pub_ctx.add(selector.publish_index(widget.get_value()))
 | 
			
		||||
    async with LambdaContext([(cg.uint16, "v")]) as control:
 | 
			
		||||
        await widget.set_property("selected", "v", animated=config[CONF_ANIMATED])
 | 
			
		||||
        lv.event_send(widget.obj, API_EVENT, cg.nullptr)
 | 
			
		||||
        control.add(selector.publish_index(widget.get_value()))
 | 
			
		||||
    async with LvContext(paren) as ctx:
 | 
			
		||||
        lv_add(selector.set_control_lambda(await control.get_lambda()))
 | 
			
		||||
        ctx.add(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/select/select.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
@@ -28,7 +30,7 @@ static std::vector<std::string> split_string(const std::string &str) {
 | 
			
		||||
class LVGLSelect : public select::Select {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_control_lambda(std::function<void(size_t)> lambda) {
 | 
			
		||||
    this->control_lambda_ = lambda;
 | 
			
		||||
    this->control_lambda_ = std::move(lambda);
 | 
			
		||||
    if (this->initial_state_.has_value()) {
 | 
			
		||||
      this->control(this->initial_state_.value());
 | 
			
		||||
      this->initial_state_.reset();
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ from ..lvcode import (
 | 
			
		||||
)
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, LvNumber
 | 
			
		||||
from ..widgets import Widget, get_widgets
 | 
			
		||||
from ..widgets import Widget, get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor_schema(Sensor)
 | 
			
		||||
@@ -33,6 +33,7 @@ async def to_code(config):
 | 
			
		||||
    widget = await get_widgets(config, CONF_WIDGET)
 | 
			
		||||
    widget = widget[0]
 | 
			
		||||
    assert isinstance(widget, Widget)
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as lamb:
 | 
			
		||||
        lv_add(sensor.publish_state(widget.get_value()))
 | 
			
		||||
    async with LvContext(paren, LVGL_COMP_ARG):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ from esphome.components.switch import Switch, new_switch, switch_schema
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.cpp_generator import MockObj
 | 
			
		||||
 | 
			
		||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET
 | 
			
		||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET, literal
 | 
			
		||||
from ..lvcode import (
 | 
			
		||||
    API_EVENT,
 | 
			
		||||
    EVENT_ARG,
 | 
			
		||||
@@ -16,7 +16,7 @@ from ..lvcode import (
 | 
			
		||||
)
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns
 | 
			
		||||
from ..widgets import get_widgets
 | 
			
		||||
from ..widgets import get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch)
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
@@ -35,6 +35,7 @@ async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_LVGL_ID])
 | 
			
		||||
    widget = await get_widgets(config, CONF_WIDGET)
 | 
			
		||||
    widget = widget[0]
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as checked_ctx:
 | 
			
		||||
        checked_ctx.add(switch.publish_state(widget.get_value()))
 | 
			
		||||
    async with LambdaContext([(cg.bool_, "v")]) as control:
 | 
			
		||||
@@ -43,6 +44,7 @@ async def to_code(config):
 | 
			
		||||
            cond.else_()
 | 
			
		||||
            widget.clear_state(LV_STATE.CHECKED)
 | 
			
		||||
        lv.event_send(widget.obj, API_EVENT, cg.nullptr)
 | 
			
		||||
        control.add(switch.publish_state(literal("v")))
 | 
			
		||||
    async with LvContext(paren) as ctx:
 | 
			
		||||
        lv_add(switch.set_control_lambda(await control.get_lambda()))
 | 
			
		||||
        ctx.add(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
@@ -11,7 +13,7 @@ namespace lvgl {
 | 
			
		||||
class LVGLSwitch : public switch_::Switch {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_control_lambda(std::function<void(bool)> state_lambda) {
 | 
			
		||||
    this->state_lambda_ = state_lambda;
 | 
			
		||||
    this->state_lambda_ = std::move(state_lambda);
 | 
			
		||||
    if (this->initial_state_.has_value()) {
 | 
			
		||||
      this->state_lambda_(this->initial_state_.value());
 | 
			
		||||
      this->initial_state_.reset();
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ from ..lvcode import (
 | 
			
		||||
)
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, LvText, lvgl_ns
 | 
			
		||||
from ..widgets import get_widgets
 | 
			
		||||
from ..widgets import get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
LVGLText = lvgl_ns.class_("LVGLText", text.Text)
 | 
			
		||||
 | 
			
		||||
@@ -32,9 +32,11 @@ async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_LVGL_ID])
 | 
			
		||||
    widget = await get_widgets(config, CONF_WIDGET)
 | 
			
		||||
    widget = widget[0]
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext([(cg.std_string, "text_value")]) as control:
 | 
			
		||||
        await widget.set_property("text", "text_value.c_str())")
 | 
			
		||||
        lv.event_send(widget.obj, API_EVENT, None)
 | 
			
		||||
        control.add(textvar.publish_state(widget.get_value()))
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as lamb:
 | 
			
		||||
        lv_add(textvar.publish_state(widget.get_value()))
 | 
			
		||||
    async with LvContext(paren):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/text/text.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
@@ -11,7 +13,7 @@ namespace lvgl {
 | 
			
		||||
class LVGLText : public text::Text {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_control_lambda(std::function<void(const std::string)> control_lambda) {
 | 
			
		||||
    this->control_lambda_ = control_lambda;
 | 
			
		||||
    this->control_lambda_ = std::move(control_lambda);
 | 
			
		||||
    if (this->initial_state_.has_value()) {
 | 
			
		||||
      this->control_lambda_(this->initial_state_.value());
 | 
			
		||||
      this->initial_state_.reset();
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET
 | 
			
		||||
from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext
 | 
			
		||||
from ..schemas import LVGL_SCHEMA
 | 
			
		||||
from ..types import LV_EVENT, LvText
 | 
			
		||||
from ..widgets import get_widgets
 | 
			
		||||
from ..widgets import get_widgets, wait_for_widgets
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    text_sensor_schema(TextSensor)
 | 
			
		||||
@@ -28,6 +28,7 @@ async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_LVGL_ID])
 | 
			
		||||
    widget = await get_widgets(config, CONF_WIDGET)
 | 
			
		||||
    widget = widget[0]
 | 
			
		||||
    await wait_for_widgets()
 | 
			
		||||
    async with LambdaContext(EVENT_ARG) as pressed_ctx:
 | 
			
		||||
        pressed_ctx.add(sensor.publish_state(widget.get_value()))
 | 
			
		||||
    async with LvContext(paren) as ctx:
 | 
			
		||||
 
 | 
			
		||||
@@ -118,7 +118,14 @@ class Widget:
 | 
			
		||||
    def clear_flag(self, flag):
 | 
			
		||||
        return lv_obj.clear_flag(self.obj, literal(flag))
 | 
			
		||||
 | 
			
		||||
    async def set_property(self, prop, value, animated: bool = None):
 | 
			
		||||
    async def set_property(self, prop, value, animated: bool = None, lv_name=None):
 | 
			
		||||
        """
 | 
			
		||||
        Set a property of the widget.
 | 
			
		||||
        :param prop:  The property name
 | 
			
		||||
        :param value:  The value
 | 
			
		||||
        :param animated:  If the change should be animated
 | 
			
		||||
        :param lv_name:  The base type of the widget e.g. "obj"
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(value, dict):
 | 
			
		||||
            value = value.get(prop)
 | 
			
		||||
            if isinstance(ALL_STYLES.get(prop), LValidator):
 | 
			
		||||
@@ -131,11 +138,12 @@ class Widget:
 | 
			
		||||
            value = value.total_milliseconds
 | 
			
		||||
        if isinstance(value, str):
 | 
			
		||||
            value = literal(value)
 | 
			
		||||
        lv_name = lv_name or self.type.lv_name
 | 
			
		||||
        if animated is None or self.type.animated is not True:
 | 
			
		||||
            lv.call(f"{self.type.lv_name}_set_{prop}", self.obj, value)
 | 
			
		||||
            lv.call(f"{lv_name}_set_{prop}", self.obj, value)
 | 
			
		||||
        else:
 | 
			
		||||
            lv.call(
 | 
			
		||||
                f"{self.type.lv_name}_set_{prop}",
 | 
			
		||||
                f"{lv_name}_set_{prop}",
 | 
			
		||||
                self.obj,
 | 
			
		||||
                value,
 | 
			
		||||
                literal("LV_ANIM_ON" if animated else "LV_ANIM_OFF"),
 | 
			
		||||
@@ -223,6 +231,19 @@ async def get_widget_(wid: Widget):
 | 
			
		||||
    return await FakeAwaitable(get_widget_generator(wid))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def widgets_wait_generator():
 | 
			
		||||
    while True:
 | 
			
		||||
        if Widget.widgets_completed:
 | 
			
		||||
            return
 | 
			
		||||
        yield
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def wait_for_widgets():
 | 
			
		||||
    if Widget.widgets_completed:
 | 
			
		||||
        return
 | 
			
		||||
    await FakeAwaitable(widgets_wait_generator())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]:
 | 
			
		||||
    if not config:
 | 
			
		||||
        return []
 | 
			
		||||
@@ -306,8 +327,15 @@ async def set_obj_properties(w: Widget, config):
 | 
			
		||||
            lv_obj.set_flex_align(w.obj, main, cross, track)
 | 
			
		||||
    parts = collect_parts(config)
 | 
			
		||||
    for part, states in parts.items():
 | 
			
		||||
        part = "LV_PART_" + part.upper()
 | 
			
		||||
        for state, props in states.items():
 | 
			
		||||
            lv_state = join_enums((f"LV_STATE_{state}", f"LV_PART_{part}"))
 | 
			
		||||
            state = "LV_STATE_" + state.upper()
 | 
			
		||||
            if state == "LV_STATE_DEFAULT":
 | 
			
		||||
                lv_state = literal(part)
 | 
			
		||||
            elif part == "LV_PART_MAIN":
 | 
			
		||||
                lv_state = literal(state)
 | 
			
		||||
            else:
 | 
			
		||||
                lv_state = join_enums((state, part))
 | 
			
		||||
            for style_id in props.get(CONF_STYLES, ()):
 | 
			
		||||
                lv_obj.add_style(w.obj, MockObj(style_id), lv_state)
 | 
			
		||||
            for prop, value in {
 | 
			
		||||
@@ -371,7 +399,7 @@ async def set_obj_properties(w: Widget, config):
 | 
			
		||||
                w.add_state(state)
 | 
			
		||||
                cond.else_()
 | 
			
		||||
                w.clear_state(state)
 | 
			
		||||
    await w.set_property(CONF_SCROLLBAR_MODE, config)
 | 
			
		||||
    await w.set_property(CONF_SCROLLBAR_MODE, config, lv_name="obj")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def add_widgets(parent: Widget, config: dict):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import functools
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
 | 
			
		||||
from ..defines import CONF_MAIN, literal
 | 
			
		||||
from ..defines import CONF_MAIN
 | 
			
		||||
from ..lvcode import lv
 | 
			
		||||
from ..types import LvType
 | 
			
		||||
from . import Widget, WidgetType
 | 
			
		||||
@@ -38,13 +38,15 @@ LINE_SCHEMA = {
 | 
			
		||||
 | 
			
		||||
class LineType(WidgetType):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super().__init__(CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA)
 | 
			
		||||
        super().__init__(
 | 
			
		||||
            CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    async def to_code(self, w: Widget, config):
 | 
			
		||||
        """For a line object, create and add the points"""
 | 
			
		||||
        data = literal(config[CONF_POINTS])
 | 
			
		||||
        points = cg.static_const_array(config[CONF_POINT_LIST_ID], data)
 | 
			
		||||
        lv.line_set_points(w.obj, points, len(data))
 | 
			
		||||
        if data := config.get(CONF_POINTS):
 | 
			
		||||
            points = cg.static_const_array(config[CONF_POINT_LIST_ID], data)
 | 
			
		||||
            lv.line_set_points(w.obj, points, len(data))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
line_spec = LineType()
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ from ..defines import (
 | 
			
		||||
    TYPE_FLEX,
 | 
			
		||||
    literal,
 | 
			
		||||
)
 | 
			
		||||
from ..helpers import add_lv_use
 | 
			
		||||
from ..helpers import add_lv_use, lvgl_components_required
 | 
			
		||||
from ..lv_validation import lv_bool, lv_pct, lv_text
 | 
			
		||||
from ..lvcode import (
 | 
			
		||||
    EVENT_ARG,
 | 
			
		||||
@@ -72,6 +72,7 @@ async def msgbox_to_code(conf):
 | 
			
		||||
        *buttonmatrix_spec.get_uses(),
 | 
			
		||||
        *button_spec.get_uses(),
 | 
			
		||||
    )
 | 
			
		||||
    lvgl_components_required.add("BUTTONMATRIX")
 | 
			
		||||
    messagebox_id = conf[CONF_ID]
 | 
			
		||||
    outer = lv_Pvariable(lv_obj_t, messagebox_id.id)
 | 
			
		||||
    buttonmatrix = new_Pvariable(
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,18 @@ enum MediaPlayerCommand : uint8_t {
 | 
			
		||||
};
 | 
			
		||||
const char *media_player_command_to_string(MediaPlayerCommand command);
 | 
			
		||||
 | 
			
		||||
enum class MediaPlayerFormatPurpose : uint8_t {
 | 
			
		||||
  PURPOSE_DEFAULT = 0,
 | 
			
		||||
  PURPOSE_ANNOUNCEMENT = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct MediaPlayerSupportedFormat {
 | 
			
		||||
  std::string format;
 | 
			
		||||
  uint32_t sample_rate;
 | 
			
		||||
  uint32_t num_channels;
 | 
			
		||||
  MediaPlayerFormatPurpose purpose;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class MediaPlayer;
 | 
			
		||||
 | 
			
		||||
class MediaPlayerTraits {
 | 
			
		||||
@@ -37,8 +49,11 @@ class MediaPlayerTraits {
 | 
			
		||||
 | 
			
		||||
  bool get_supports_pause() const { return this->supports_pause_; }
 | 
			
		||||
 | 
			
		||||
  std::vector<MediaPlayerSupportedFormat> &get_supported_formats() { return this->supported_formats_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool supports_pause_{false};
 | 
			
		||||
  std::vector<MediaPlayerSupportedFormat> supported_formats_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class MediaPlayerCall {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 
 | 
			
		||||
@@ -136,6 +136,9 @@ void Pipsolar::loop() {
 | 
			
		||||
        if (this->output_source_priority_battery_switch_) {
 | 
			
		||||
          this->output_source_priority_battery_switch_->publish_state(value_output_source_priority_ == 2);
 | 
			
		||||
        }
 | 
			
		||||
        if (this->output_source_priority_hybrid_switch_) {
 | 
			
		||||
          this->output_source_priority_hybrid_switch_->publish_state(value_output_source_priority_ == 3);
 | 
			
		||||
        }
 | 
			
		||||
        if (this->charger_source_priority_) {
 | 
			
		||||
          this->charger_source_priority_->publish_state(value_charger_source_priority_);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -174,6 +174,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent {
 | 
			
		||||
  PIPSOLAR_SWITCH(output_source_priority_utility_switch, QPIRI)
 | 
			
		||||
  PIPSOLAR_SWITCH(output_source_priority_solar_switch, QPIRI)
 | 
			
		||||
  PIPSOLAR_SWITCH(output_source_priority_battery_switch, QPIRI)
 | 
			
		||||
  PIPSOLAR_SWITCH(output_source_priority_hybrid_switch, QPIRI)
 | 
			
		||||
  PIPSOLAR_SWITCH(input_voltage_range_switch, QPIRI)
 | 
			
		||||
  PIPSOLAR_SWITCH(pv_ok_condition_for_parallel_switch, QPIRI)
 | 
			
		||||
  PIPSOLAR_SWITCH(pv_power_balance_switch, QPIRI)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ DEPENDENCIES = ["uart"]
 | 
			
		||||
CONF_OUTPUT_SOURCE_PRIORITY_UTILITY = "output_source_priority_utility"
 | 
			
		||||
CONF_OUTPUT_SOURCE_PRIORITY_SOLAR = "output_source_priority_solar"
 | 
			
		||||
CONF_OUTPUT_SOURCE_PRIORITY_BATTERY = "output_source_priority_battery"
 | 
			
		||||
CONF_OUTPUT_SOURCE_PRIORITY_HYBRID = "output_source_priority_hybrid"
 | 
			
		||||
CONF_INPUT_VOLTAGE_RANGE = "input_voltage_range"
 | 
			
		||||
CONF_PV_OK_CONDITION_FOR_PARALLEL = "pv_ok_condition_for_parallel"
 | 
			
		||||
CONF_PV_POWER_BALANCE = "pv_power_balance"
 | 
			
		||||
@@ -17,6 +18,7 @@ TYPES = {
 | 
			
		||||
    CONF_OUTPUT_SOURCE_PRIORITY_UTILITY: ("POP00", None),
 | 
			
		||||
    CONF_OUTPUT_SOURCE_PRIORITY_SOLAR: ("POP01", None),
 | 
			
		||||
    CONF_OUTPUT_SOURCE_PRIORITY_BATTERY: ("POP02", None),
 | 
			
		||||
    CONF_OUTPUT_SOURCE_PRIORITY_HYBRID: ("POP03", None),
 | 
			
		||||
    CONF_INPUT_VOLTAGE_RANGE: ("PGR01", "PGR00"),
 | 
			
		||||
    CONF_PV_OK_CONDITION_FOR_PARALLEL: ("PPVOKC1", "PPVOKC0"),
 | 
			
		||||
    CONF_PV_POWER_BALANCE: ("PSPB1", "PSPB0"),
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ void PowerSupply::unrequest_high_power() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->active_requests_--;
 | 
			
		||||
  ESP_LOGD(TAG, "Unrequesting high power, %d requests left.", this->active_requests_);
 | 
			
		||||
  if (this->active_requests_ == 0) {
 | 
			
		||||
    this->set_timeout("power-supply-off", this->keep_on_time_, [this]() {
 | 
			
		||||
      ESP_LOGD(TAG, "Disabling power supply.");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "prometheus_handler.h"
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -350,3 +351,4 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj)
 | 
			
		||||
 | 
			
		||||
}  // namespace prometheus
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_NETWORK
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
@@ -117,3 +118,4 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
 | 
			
		||||
 | 
			
		||||
}  // namespace prometheus
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,10 @@
 | 
			
		||||
 | 
			
		||||
#include <hardware/clocks.h>
 | 
			
		||||
#include <hardware/dma.h>
 | 
			
		||||
#include <hardware/irq.h>
 | 
			
		||||
#include <hardware/pio.h>
 | 
			
		||||
#include <pico/stdlib.h>
 | 
			
		||||
#include <pico/sem.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace rp2040_pio_led_strip {
 | 
			
		||||
@@ -23,6 +25,19 @@ static std::map<Chipset, bool> conf_count_ = {
 | 
			
		||||
    {CHIPSET_WS2812, false},  {CHIPSET_WS2812B, false}, {CHIPSET_SK6812, false},
 | 
			
		||||
    {CHIPSET_SM16703, false}, {CHIPSET_CUSTOM, false},
 | 
			
		||||
};
 | 
			
		||||
static bool dma_chan_active_[12];
 | 
			
		||||
static struct semaphore dma_write_complete_sem_[12];
 | 
			
		||||
 | 
			
		||||
// DMA interrupt service routine
 | 
			
		||||
void RP2040PIOLEDStripLightOutput::dma_write_complete_handler_() {
 | 
			
		||||
  uint32_t channel = dma_hw->ints0;
 | 
			
		||||
  for (uint dma_chan = 0; dma_chan < 12; ++dma_chan) {
 | 
			
		||||
    if (RP2040PIOLEDStripLightOutput::dma_chan_active_[dma_chan] && (channel & (1u << dma_chan))) {
 | 
			
		||||
      dma_hw->ints0 = (1u << dma_chan);                                               // Clear the interrupt
 | 
			
		||||
      sem_release(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[dma_chan]);  // Handle the interrupt
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RP2040PIOLEDStripLightOutput::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up RP2040 LED Strip...");
 | 
			
		||||
@@ -57,22 +72,22 @@ void RP2040PIOLEDStripLightOutput::setup() {
 | 
			
		||||
  // but there are only 4 state machines on each PIO so we can only have 4 strips per PIO
 | 
			
		||||
  uint offset = 0;
 | 
			
		||||
 | 
			
		||||
  if (num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) {
 | 
			
		||||
  if (RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) {
 | 
			
		||||
    ESP_LOGE(TAG, "Too many instances of PIO program");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // keep track of how many instances of the PIO program are running on each PIO
 | 
			
		||||
  num_instance_[this->pio_ == pio0 ? 0 : 1]++;
 | 
			
		||||
  RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1]++;
 | 
			
		||||
 | 
			
		||||
  // if there are multiple strips of the same chipset, we can reuse the same PIO program and save space
 | 
			
		||||
  if (this->conf_count_[this->chipset_]) {
 | 
			
		||||
    offset = chipset_offsets_[this->chipset_];
 | 
			
		||||
    offset = RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_];
 | 
			
		||||
  } else {
 | 
			
		||||
    // Load the assembled program into the PIO and get its location in the PIO's instruction memory and save it
 | 
			
		||||
    offset = pio_add_program(this->pio_, this->program_);
 | 
			
		||||
    chipset_offsets_[this->chipset_] = offset;
 | 
			
		||||
    conf_count_[this->chipset_] = true;
 | 
			
		||||
    RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_] = offset;
 | 
			
		||||
    RP2040PIOLEDStripLightOutput::conf_count_[this->chipset_] = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Configure the state machine's PIO, and start it
 | 
			
		||||
@@ -93,6 +108,9 @@ void RP2040PIOLEDStripLightOutput::setup() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Mark the DMA channel as active
 | 
			
		||||
  RP2040PIOLEDStripLightOutput::dma_chan_active_[this->dma_chan_] = true;
 | 
			
		||||
 | 
			
		||||
  this->dma_config_ = dma_channel_get_default_config(this->dma_chan_);
 | 
			
		||||
  channel_config_set_transfer_data_size(
 | 
			
		||||
      &this->dma_config_,
 | 
			
		||||
@@ -109,6 +127,13 @@ void RP2040PIOLEDStripLightOutput::setup() {
 | 
			
		||||
                        false                                            // don't start yet
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Initialize the semaphore for this DMA channel
 | 
			
		||||
  sem_init(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_], 1, 1);
 | 
			
		||||
 | 
			
		||||
  irq_set_exclusive_handler(DMA_IRQ_0, dma_write_complete_handler_);  // after DMA all data, raise an interrupt
 | 
			
		||||
  dma_channel_set_irq0_enabled(this->dma_chan_, true);                // map DMA channel to interrupt
 | 
			
		||||
  irq_set_enabled(DMA_IRQ_0, true);                                   // enable interrupt
 | 
			
		||||
 | 
			
		||||
  this->init_(this->pio_, this->sm_, offset, this->pin_, this->max_refresh_rate_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -126,6 +151,7 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // the bits are already in the correct order for the pio program so we can just copy the buffer using DMA
 | 
			
		||||
  sem_acquire_blocking(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_]);
 | 
			
		||||
  dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
#include <hardware/pio.h>
 | 
			
		||||
#include <hardware/structs/pio.h>
 | 
			
		||||
#include <pico/stdio.h>
 | 
			
		||||
#include <pico/sem.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -95,6 +96,8 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight {
 | 
			
		||||
 | 
			
		||||
  size_t get_buffer_size_() const { return this->num_leds_ * (3 + this->is_rgbw_); }
 | 
			
		||||
 | 
			
		||||
  static void dma_write_complete_handler_();
 | 
			
		||||
 | 
			
		||||
  uint8_t *buf_{nullptr};
 | 
			
		||||
  uint8_t *effect_data_{nullptr};
 | 
			
		||||
 | 
			
		||||
@@ -120,6 +123,8 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight {
 | 
			
		||||
  inline static int num_instance_[2];
 | 
			
		||||
  inline static std::map<Chipset, bool> conf_count_;
 | 
			
		||||
  inline static std::map<Chipset, int> chipset_offsets_;
 | 
			
		||||
  inline static bool dma_chan_active_[12];
 | 
			
		||||
  inline static struct semaphore dma_write_complete_sem_[12];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace rp2040_pio_led_strip
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ void Rtttl::play(std::string rtttl) {
 | 
			
		||||
  if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) {
 | 
			
		||||
    int pos = this->rtttl_.find(':');
 | 
			
		||||
    auto name = this->rtttl_.substr(0, pos);
 | 
			
		||||
    ESP_LOGW(TAG, "RTTL Component is already playing: %s", name.c_str());
 | 
			
		||||
    ESP_LOGW(TAG, "RTTTL Component is already playing: %s", name.c_str());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -122,6 +122,7 @@ void Rtttl::stop() {
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  if (this->output_ != nullptr) {
 | 
			
		||||
    this->output_->set_level(0.0);
 | 
			
		||||
    this->set_state_(STATE_STOPPED);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SPEAKER
 | 
			
		||||
@@ -129,10 +130,10 @@ void Rtttl::stop() {
 | 
			
		||||
    if (this->speaker_->is_running()) {
 | 
			
		||||
      this->speaker_->stop();
 | 
			
		||||
    }
 | 
			
		||||
    this->set_state_(STATE_STOPPING);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  this->note_duration_ = 0;
 | 
			
		||||
  this->set_state_(STATE_STOPPING);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Rtttl::loop() {
 | 
			
		||||
@@ -342,6 +343,7 @@ void Rtttl::finish_() {
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  if (this->output_ != nullptr) {
 | 
			
		||||
    this->output_->set_level(0.0);
 | 
			
		||||
    this->set_state_(State::STATE_STOPPED);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SPEAKER
 | 
			
		||||
@@ -354,9 +356,9 @@ void Rtttl::finish_() {
 | 
			
		||||
    this->speaker_->play((uint8_t *) (&sample), 8);
 | 
			
		||||
 | 
			
		||||
    this->speaker_->finish();
 | 
			
		||||
    this->set_state_(State::STATE_STOPPING);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  this->set_state_(State::STATE_STOPPING);
 | 
			
		||||
  this->note_duration_ = 0;
 | 
			
		||||
  this->on_finished_playback_callback_.call();
 | 
			
		||||
  ESP_LOGD(TAG, "Playback finished");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "socket.h"
 | 
			
		||||
#if defined(USE_SOCKET_IMPL_LWIP_TCP) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || defined(USE_SOCKET_IMPL_BSD_SOCKETS)
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -74,3 +75,4 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po
 | 
			
		||||
}
 | 
			
		||||
}  // namespace socket
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "esphome/core/optional.h"
 | 
			
		||||
#include "headers.h"
 | 
			
		||||
 | 
			
		||||
#if defined(USE_SOCKET_IMPL_LWIP_TCP) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || defined(USE_SOCKET_IMPL_BSD_SOCKETS)
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace socket {
 | 
			
		||||
 | 
			
		||||
@@ -57,3 +58,4 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po
 | 
			
		||||
 | 
			
		||||
}  // namespace socket
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ CONF_DATAPOINT_TYPE = "datapoint_type"
 | 
			
		||||
CONF_STATUS_PIN = "status_pin"
 | 
			
		||||
 | 
			
		||||
tuya_ns = cg.esphome_ns.namespace("tuya")
 | 
			
		||||
TuyaDatapointType = tuya_ns.enum("TuyaDatapointType", is_class=True)
 | 
			
		||||
Tuya = tuya_ns.class_("Tuya", cg.Component, uart.UARTDevice)
 | 
			
		||||
 | 
			
		||||
DPTYPE_ANY = "any"
 | 
			
		||||
 
 | 
			
		||||
@@ -8,18 +8,36 @@ from esphome.const import (
 | 
			
		||||
    CONF_MIN_VALUE,
 | 
			
		||||
    CONF_MULTIPLY,
 | 
			
		||||
    CONF_STEP,
 | 
			
		||||
    CONF_INITIAL_VALUE,
 | 
			
		||||
)
 | 
			
		||||
from .. import tuya_ns, CONF_TUYA_ID, Tuya
 | 
			
		||||
from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["tuya"]
 | 
			
		||||
CODEOWNERS = ["@frankiboy1"]
 | 
			
		||||
 | 
			
		||||
CONF_DATAPOINT_HIDDEN = "datapoint_hidden"
 | 
			
		||||
CONF_DATAPOINT_TYPE = "datapoint_type"
 | 
			
		||||
 | 
			
		||||
TuyaNumber = tuya_ns.class_("TuyaNumber", number.Number, cg.Component)
 | 
			
		||||
 | 
			
		||||
DATAPOINT_TYPES = {
 | 
			
		||||
    "int": TuyaDatapointType.INTEGER,
 | 
			
		||||
    "uint": TuyaDatapointType.INTEGER,
 | 
			
		||||
    "enum": TuyaDatapointType.ENUM,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_min_max(config):
 | 
			
		||||
    if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]:
 | 
			
		||||
    max_value = config[CONF_MAX_VALUE]
 | 
			
		||||
    min_value = config[CONF_MIN_VALUE]
 | 
			
		||||
    if max_value <= min_value:
 | 
			
		||||
        raise cv.Invalid("max_value must be greater than min_value")
 | 
			
		||||
    if hidden_config := config.get(CONF_DATAPOINT_HIDDEN):
 | 
			
		||||
        if (initial_value := hidden_config.get(CONF_INITIAL_VALUE, None)) is not None:
 | 
			
		||||
            if (initial_value > max_value) or (initial_value < min_value):
 | 
			
		||||
                raise cv.Invalid(
 | 
			
		||||
                    f"{CONF_INITIAL_VALUE} must be a value between {CONF_MAX_VALUE} and {CONF_MIN_VALUE}"
 | 
			
		||||
                )
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -33,6 +51,16 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Required(CONF_MIN_VALUE): cv.float_,
 | 
			
		||||
            cv.Required(CONF_STEP): cv.positive_float,
 | 
			
		||||
            cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_,
 | 
			
		||||
            cv.Optional(CONF_DATAPOINT_HIDDEN): cv.All(
 | 
			
		||||
                cv.Schema(
 | 
			
		||||
                    {
 | 
			
		||||
                        cv.Required(CONF_DATAPOINT_TYPE): cv.enum(
 | 
			
		||||
                            DATAPOINT_TYPES, lower=True
 | 
			
		||||
                        ),
 | 
			
		||||
                        cv.Optional(CONF_INITIAL_VALUE): cv.float_,
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
@@ -56,3 +84,9 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_tuya_parent(parent))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_number_id(config[CONF_NUMBER_DATAPOINT]))
 | 
			
		||||
    if hidden_config := config.get(CONF_DATAPOINT_HIDDEN):
 | 
			
		||||
        cg.add(var.set_datapoint_type(hidden_config[CONF_DATAPOINT_TYPE]))
 | 
			
		||||
        if (
 | 
			
		||||
            hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None)
 | 
			
		||||
        ) is not None:
 | 
			
		||||
            cg.add(var.set_datapoint_initial_value(hidden_init_value))
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,18 @@ void TuyaNumber::setup() {
 | 
			
		||||
      ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum);
 | 
			
		||||
      this->publish_state(datapoint.value_enum);
 | 
			
		||||
    }
 | 
			
		||||
    if ((this->type_) && (this->type_ != datapoint.type)) {
 | 
			
		||||
      ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast<int>(datapoint.type),
 | 
			
		||||
               static_cast<int>(*this->type_));
 | 
			
		||||
    }
 | 
			
		||||
    this->type_ = datapoint.type;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  this->parent_->add_on_initialized_callback([this] {
 | 
			
		||||
    if ((this->initial_value_) && (this->type_)) {
 | 
			
		||||
      this->control(*this->initial_value_);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TuyaNumber::control(float value) {
 | 
			
		||||
@@ -33,6 +43,15 @@ void TuyaNumber::control(float value) {
 | 
			
		||||
void TuyaNumber::dump_config() {
 | 
			
		||||
  LOG_NUMBER("", "Tuya Number", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Number has datapoint ID %u", this->number_id_);
 | 
			
		||||
  if (this->type_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Datapoint type is %d", static_cast<int>(*this->type_));
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Datapoint type is unknown");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->initial_value_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Initial Value: %f", *this->initial_value_);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace tuya
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/tuya/tuya.h"
 | 
			
		||||
#include "esphome/components/number/number.h"
 | 
			
		||||
#include "esphome/core/optional.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace tuya {
 | 
			
		||||
@@ -13,6 +14,8 @@ class TuyaNumber : public number::Number, public Component {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void set_number_id(uint8_t number_id) { this->number_id_ = number_id; }
 | 
			
		||||
  void set_write_multiply(float factor) { multiply_by_ = factor; }
 | 
			
		||||
  void set_datapoint_type(TuyaDatapointType type) { type_ = type; }
 | 
			
		||||
  void set_datapoint_initial_value(float value) { this->initial_value_ = value; }
 | 
			
		||||
 | 
			
		||||
  void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
 | 
			
		||||
 | 
			
		||||
@@ -22,7 +25,8 @@ class TuyaNumber : public number::Number, public Component {
 | 
			
		||||
  Tuya *parent_;
 | 
			
		||||
  uint8_t number_id_{0};
 | 
			
		||||
  float multiply_by_{1.0};
 | 
			
		||||
  TuyaDatapointType type_{};
 | 
			
		||||
  optional<TuyaDatapointType> type_{};
 | 
			
		||||
  optional<float> initial_value_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace tuya
 | 
			
		||||
 
 | 
			
		||||
@@ -480,7 +480,7 @@ void HOT WaveshareEPaperTypeA::display() {
 | 
			
		||||
  this->start_data_();
 | 
			
		||||
  switch (this->model_) {
 | 
			
		||||
    case TTGO_EPAPER_2_13_IN_B1: {  // block needed because of variable initializations
 | 
			
		||||
      int16_t wb = ((this->get_width_internal()) >> 3);
 | 
			
		||||
      int16_t wb = ((this->get_width_controller()) >> 3);
 | 
			
		||||
      for (int i = 0; i < this->get_height_internal(); i++) {
 | 
			
		||||
        for (int j = 0; j < wb; j++) {
 | 
			
		||||
          int idx = j + (this->get_height_internal() - 1 - i) * wb;
 | 
			
		||||
@@ -766,7 +766,7 @@ void WaveshareEPaper2P7InV2::initialize() {
 | 
			
		||||
  // XRAM_START_AND_END_POSITION
 | 
			
		||||
  this->command(0x44);
 | 
			
		||||
  this->data(0x00);
 | 
			
		||||
  this->data(((get_width_internal() - 1) >> 3) & 0xFF);
 | 
			
		||||
  this->data(((this->get_width_controller() - 1) >> 3) & 0xFF);
 | 
			
		||||
  // YRAM_START_AND_END_POSITION
 | 
			
		||||
  this->command(0x45);
 | 
			
		||||
  this->data(0x00);
 | 
			
		||||
@@ -928,8 +928,8 @@ void HOT WaveshareEPaper2P7InB::display() {
 | 
			
		||||
 | 
			
		||||
  // TCON_RESOLUTION
 | 
			
		||||
  this->command(0x61);
 | 
			
		||||
  this->data(this->get_width_internal() >> 8);
 | 
			
		||||
  this->data(this->get_width_internal() & 0xff);  // 176
 | 
			
		||||
  this->data(this->get_width_controller() >> 8);
 | 
			
		||||
  this->data(this->get_width_controller() & 0xff);  // 176
 | 
			
		||||
  this->data(this->get_height_internal() >> 8);
 | 
			
		||||
  this->data(this->get_height_internal() & 0xff);  // 264
 | 
			
		||||
 | 
			
		||||
@@ -994,7 +994,7 @@ void WaveshareEPaper2P7InBV2::initialize() {
 | 
			
		||||
  // self.SetWindows(0, 0, self.width-1, self.height-1)
 | 
			
		||||
  // SetWindows(self, Xstart, Ystart, Xend, Yend):
 | 
			
		||||
 | 
			
		||||
  uint32_t xend = this->get_width_internal() - 1;
 | 
			
		||||
  uint32_t xend = this->get_width_controller() - 1;
 | 
			
		||||
  uint32_t yend = this->get_height_internal() - 1;
 | 
			
		||||
  this->command(0x44);
 | 
			
		||||
  this->data(0x00);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,20 @@
 | 
			
		||||
import re
 | 
			
		||||
import ipaddress
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from esphome import automation
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import time
 | 
			
		||||
from esphome.components.esp32 import CORE, add_idf_sdkconfig_option
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_REBOOT_TIMEOUT,
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
    KEY_CORE,
 | 
			
		||||
    KEY_FRAMEWORK_VERSION,
 | 
			
		||||
)
 | 
			
		||||
from esphome.components.esp32 import CORE, add_idf_sdkconfig_option
 | 
			
		||||
from esphome.components import time
 | 
			
		||||
from esphome.core import TimePeriod
 | 
			
		||||
from esphome import automation
 | 
			
		||||
 | 
			
		||||
CONF_NETMASK = "netmask"
 | 
			
		||||
CONF_PRIVATE_KEY = "private_key"
 | 
			
		||||
@@ -91,6 +92,8 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_WIREGUARD")
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_address(str(config[CONF_ADDRESS])))
 | 
			
		||||
    cg.add(var.set_netmask(str(config[CONF_NETMASK])))
 | 
			
		||||
    cg.add(var.set_private_key(config[CONF_PRIVATE_KEY]))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include "wireguard.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_WIREGUARD
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <functional>
 | 
			
		||||
@@ -289,3 +289,4 @@ std::string mask_key(const std::string &key) { return (key.substr(0, 5) + "[...]
 | 
			
		||||
 | 
			
		||||
}  // namespace wireguard
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_WIREGUARD
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
@@ -170,3 +171,4 @@ template<typename... Ts> class WireguardDisableAction : public Action<Ts...>, pu
 | 
			
		||||
 | 
			
		||||
}  // namespace wireguard
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
"""Constants used by esphome."""
 | 
			
		||||
 | 
			
		||||
__version__ = "2024.8.0b3"
 | 
			
		||||
__version__ = "2024.9.0-dev"
 | 
			
		||||
 | 
			
		||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
			
		||||
VALID_SUBSTITUTIONS_CHARACTERS = (
 | 
			
		||||
@@ -431,6 +431,7 @@ CONF_LIGHT_ID = "light_id"
 | 
			
		||||
CONF_LIGHTNING_ENERGY = "lightning_energy"
 | 
			
		||||
CONF_LIGHTNING_THRESHOLD = "lightning_threshold"
 | 
			
		||||
CONF_LIMIT_MODE = "limit_mode"
 | 
			
		||||
CONF_LINE_FREQUENCY = "line_frequency"
 | 
			
		||||
CONF_LINE_THICKNESS = "line_thickness"
 | 
			
		||||
CONF_LINE_TYPE = "line_type"
 | 
			
		||||
CONF_LOADED_INTEGRATIONS = "loaded_integrations"
 | 
			
		||||
@@ -1042,6 +1043,7 @@ UNIT_KILOVOLT_AMPS_REACTIVE = "kVAR"
 | 
			
		||||
UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVARh"
 | 
			
		||||
UNIT_KILOWATT = "kW"
 | 
			
		||||
UNIT_KILOWATT_HOURS = "kWh"
 | 
			
		||||
UNIT_LITRE = "L"
 | 
			
		||||
UNIT_LUX = "lx"
 | 
			
		||||
UNIT_METER = "m"
 | 
			
		||||
UNIT_METER_PER_SECOND_SQUARED = "m/s²"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,64 @@
 | 
			
		||||
#include "bytebuffer.h"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::create(size_t capacity) {
 | 
			
		||||
  std::vector<uint8_t> data(capacity);
 | 
			
		||||
  return {data};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(uint8_t *ptr, size_t len) {
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(const uint8_t *ptr, size_t len, Endian endianness) {
 | 
			
		||||
  // there is a double copy happening here, could be optimized but at cost of clarity.
 | 
			
		||||
  std::vector<uint8_t> data(ptr, ptr + len);
 | 
			
		||||
  return {data};
 | 
			
		||||
  ByteBuffer buffer = {data};
 | 
			
		||||
  buffer.endianness_ = endianness;
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(std::vector<uint8_t> data) { return {std::move(data)}; }
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(std::vector<uint8_t> const &data, Endian endianness) {
 | 
			
		||||
  ByteBuffer buffer = {data};
 | 
			
		||||
  buffer.endianness_ = endianness;
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(uint8_t value) {
 | 
			
		||||
  ByteBuffer buffer = ByteBuffer(1);
 | 
			
		||||
  buffer.put_uint8(value);
 | 
			
		||||
  buffer.flip();
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(uint16_t value, Endian endianness) {
 | 
			
		||||
  ByteBuffer buffer = ByteBuffer(2, endianness);
 | 
			
		||||
  buffer.put_uint16(value);
 | 
			
		||||
  buffer.flip();
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(uint32_t value, Endian endianness) {
 | 
			
		||||
  ByteBuffer buffer = ByteBuffer(4, endianness);
 | 
			
		||||
  buffer.put_uint32(value);
 | 
			
		||||
  buffer.flip();
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(uint64_t value, Endian endianness) {
 | 
			
		||||
  ByteBuffer buffer = ByteBuffer(8, endianness);
 | 
			
		||||
  buffer.put_uint64(value);
 | 
			
		||||
  buffer.flip();
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(float value, Endian endianness) {
 | 
			
		||||
  ByteBuffer buffer = ByteBuffer(sizeof(float), endianness);
 | 
			
		||||
  buffer.put_float(value);
 | 
			
		||||
  buffer.flip();
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ByteBuffer ByteBuffer::wrap(double value, Endian endianness) {
 | 
			
		||||
  ByteBuffer buffer = ByteBuffer(sizeof(double), endianness);
 | 
			
		||||
  buffer.put_double(value);
 | 
			
		||||
  buffer.flip();
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ByteBuffer::set_limit(size_t limit) {
 | 
			
		||||
  assert(limit <= this->get_capacity());
 | 
			
		||||
@@ -27,108 +72,102 @@ void ByteBuffer::clear() {
 | 
			
		||||
  this->limit_ = this->get_capacity();
 | 
			
		||||
  this->position_ = 0;
 | 
			
		||||
}
 | 
			
		||||
uint16_t ByteBuffer::get_uint16() {
 | 
			
		||||
  assert(this->get_remaining() >= 2);
 | 
			
		||||
  uint16_t value;
 | 
			
		||||
  if (endianness_ == LITTLE) {
 | 
			
		||||
    value = this->data_[this->position_++];
 | 
			
		||||
    value |= this->data_[this->position_++] << 8;
 | 
			
		||||
  } else {
 | 
			
		||||
    value = this->data_[this->position_++] << 8;
 | 
			
		||||
    value |= this->data_[this->position_++];
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
void ByteBuffer::flip() {
 | 
			
		||||
  this->limit_ = this->position_;
 | 
			
		||||
  this->position_ = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t ByteBuffer::get_uint32() {
 | 
			
		||||
  assert(this->get_remaining() >= 4);
 | 
			
		||||
  uint32_t value;
 | 
			
		||||
  if (endianness_ == LITTLE) {
 | 
			
		||||
    value = this->data_[this->position_++];
 | 
			
		||||
    value |= this->data_[this->position_++] << 8;
 | 
			
		||||
    value |= this->data_[this->position_++] << 16;
 | 
			
		||||
    value |= this->data_[this->position_++] << 24;
 | 
			
		||||
  } else {
 | 
			
		||||
    value = this->data_[this->position_++] << 24;
 | 
			
		||||
    value |= this->data_[this->position_++] << 16;
 | 
			
		||||
    value |= this->data_[this->position_++] << 8;
 | 
			
		||||
    value |= this->data_[this->position_++];
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
uint32_t ByteBuffer::get_uint24() {
 | 
			
		||||
  assert(this->get_remaining() >= 3);
 | 
			
		||||
  uint32_t value;
 | 
			
		||||
  if (endianness_ == LITTLE) {
 | 
			
		||||
    value = this->data_[this->position_++];
 | 
			
		||||
    value |= this->data_[this->position_++] << 8;
 | 
			
		||||
    value |= this->data_[this->position_++] << 16;
 | 
			
		||||
  } else {
 | 
			
		||||
    value = this->data_[this->position_++] << 16;
 | 
			
		||||
    value |= this->data_[this->position_++] << 8;
 | 
			
		||||
    value |= this->data_[this->position_++];
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
uint32_t ByteBuffer::get_int24() {
 | 
			
		||||
  auto value = this->get_uint24();
 | 
			
		||||
  uint32_t mask = (~(uint32_t) 0) << 23;
 | 
			
		||||
  if ((value & mask) != 0)
 | 
			
		||||
    value |= mask;
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
/// Getters
 | 
			
		||||
uint8_t ByteBuffer::get_uint8() {
 | 
			
		||||
  assert(this->get_remaining() >= 1);
 | 
			
		||||
  return this->data_[this->position_++];
 | 
			
		||||
}
 | 
			
		||||
float ByteBuffer::get_float() {
 | 
			
		||||
  auto value = this->get_uint32();
 | 
			
		||||
  return *(float *) &value;
 | 
			
		||||
uint64_t ByteBuffer::get_uint(size_t length) {
 | 
			
		||||
  assert(this->get_remaining() >= length);
 | 
			
		||||
  uint64_t value = 0;
 | 
			
		||||
  if (this->endianness_ == LITTLE) {
 | 
			
		||||
    this->position_ += length;
 | 
			
		||||
    auto index = this->position_;
 | 
			
		||||
    while (length-- != 0) {
 | 
			
		||||
      value <<= 8;
 | 
			
		||||
      value |= this->data_[--index];
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    while (length-- != 0) {
 | 
			
		||||
      value <<= 8;
 | 
			
		||||
      value |= this->data_[this->position_++];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t ByteBuffer::get_int24() {
 | 
			
		||||
  auto value = this->get_uint24();
 | 
			
		||||
  uint32_t mask = (~static_cast<uint32_t>(0)) << 23;
 | 
			
		||||
  if ((value & mask) != 0)
 | 
			
		||||
    value |= mask;
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
float ByteBuffer::get_float() {
 | 
			
		||||
  assert(this->get_remaining() >= sizeof(float));
 | 
			
		||||
  auto ui_value = this->get_uint32();
 | 
			
		||||
  float value;
 | 
			
		||||
  memcpy(&value, &ui_value, sizeof(float));
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
double ByteBuffer::get_double() {
 | 
			
		||||
  assert(this->get_remaining() >= sizeof(double));
 | 
			
		||||
  auto ui_value = this->get_uint64();
 | 
			
		||||
  double value;
 | 
			
		||||
  memcpy(&value, &ui_value, sizeof(double));
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
std::vector<uint8_t> ByteBuffer::get_vector(size_t length) {
 | 
			
		||||
  assert(this->get_remaining() >= length);
 | 
			
		||||
  auto start = this->data_.begin() + this->position_;
 | 
			
		||||
  this->position_ += length;
 | 
			
		||||
  return {start, start + length};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Putters
 | 
			
		||||
void ByteBuffer::put_uint8(uint8_t value) {
 | 
			
		||||
  assert(this->get_remaining() >= 1);
 | 
			
		||||
  this->data_[this->position_++] = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ByteBuffer::put_uint16(uint16_t value) {
 | 
			
		||||
  assert(this->get_remaining() >= 2);
 | 
			
		||||
void ByteBuffer::put_uint(uint64_t value, size_t length) {
 | 
			
		||||
  assert(this->get_remaining() >= length);
 | 
			
		||||
  if (this->endianness_ == LITTLE) {
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) value;
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 8);
 | 
			
		||||
    while (length-- != 0) {
 | 
			
		||||
      this->data_[this->position_++] = static_cast<uint8_t>(value);
 | 
			
		||||
      value >>= 8;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 8);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) value;
 | 
			
		||||
    this->position_ += length;
 | 
			
		||||
    auto index = this->position_;
 | 
			
		||||
    while (length-- != 0) {
 | 
			
		||||
      this->data_[--index] = static_cast<uint8_t>(value);
 | 
			
		||||
      value >>= 8;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ByteBuffer::put_uint24(uint32_t value) {
 | 
			
		||||
  assert(this->get_remaining() >= 3);
 | 
			
		||||
  if (this->endianness_ == LITTLE) {
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) value;
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 8);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 16);
 | 
			
		||||
  } else {
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 16);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 8);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) value;
 | 
			
		||||
  }
 | 
			
		||||
void ByteBuffer::put_float(float value) {
 | 
			
		||||
  static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported");
 | 
			
		||||
  assert(this->get_remaining() >= sizeof(float));
 | 
			
		||||
  uint32_t ui_value;
 | 
			
		||||
  memcpy(&ui_value, &value, sizeof(float));  // this work-around required to silence compiler warnings
 | 
			
		||||
  this->put_uint32(ui_value);
 | 
			
		||||
}
 | 
			
		||||
void ByteBuffer::put_uint32(uint32_t value) {
 | 
			
		||||
  assert(this->get_remaining() >= 4);
 | 
			
		||||
  if (this->endianness_ == LITTLE) {
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) value;
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 8);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 16);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 24);
 | 
			
		||||
  } else {
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 24);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 16);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) (value >> 8);
 | 
			
		||||
    this->data_[this->position_++] = (uint8_t) value;
 | 
			
		||||
  }
 | 
			
		||||
void ByteBuffer::put_double(double value) {
 | 
			
		||||
  static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported");
 | 
			
		||||
  assert(this->get_remaining() >= sizeof(double));
 | 
			
		||||
  uint64_t ui_value;
 | 
			
		||||
  memcpy(&ui_value, &value, sizeof(double));
 | 
			
		||||
  this->put_uint64(ui_value);
 | 
			
		||||
}
 | 
			
		||||
void ByteBuffer::put_float(float value) { this->put_uint32(*(uint32_t *) &value); }
 | 
			
		||||
void ByteBuffer::flip() {
 | 
			
		||||
  this->limit_ = this->position_;
 | 
			
		||||
  this->position_ = 0;
 | 
			
		||||
void ByteBuffer::put_vector(const std::vector<uint8_t> &value) {
 | 
			
		||||
  assert(this->get_remaining() >= value.size());
 | 
			
		||||
  std::copy(value.begin(), value.end(), this->data_.begin() + this->position_);
 | 
			
		||||
  this->position_ += value.size();
 | 
			
		||||
}
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -15,55 +15,103 @@ enum Endian { LITTLE, BIG };
 | 
			
		||||
 *
 | 
			
		||||
 * There are three variables maintained pointing into the buffer:
 | 
			
		||||
 *
 | 
			
		||||
 * 0 <= position <= limit <= capacity
 | 
			
		||||
 *
 | 
			
		||||
 * capacity: the maximum amount of data that can be stored
 | 
			
		||||
 * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed
 | 
			
		||||
 * limit: the limit of the data currently available to get or put
 | 
			
		||||
 * position: the current insert or extract position
 | 
			
		||||
 *
 | 
			
		||||
 * 0 <= position <= limit <= capacity
 | 
			
		||||
 *
 | 
			
		||||
 * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore
 | 
			
		||||
 * the position to the mark.
 | 
			
		||||
 *
 | 
			
		||||
 * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order.
 | 
			
		||||
 *
 | 
			
		||||
 * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading
 | 
			
		||||
 * data from a buffer after it has been written.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class ByteBuffer {
 | 
			
		||||
 public:
 | 
			
		||||
  // Default constructor (compatibility with TEMPLATABLE_VALUE)
 | 
			
		||||
  ByteBuffer() : ByteBuffer(std::vector<uint8_t>()) {}
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new Bytebuffer with the given capacity
 | 
			
		||||
   */
 | 
			
		||||
  static ByteBuffer create(size_t capacity);
 | 
			
		||||
  ByteBuffer(size_t capacity, Endian endianness = LITTLE)
 | 
			
		||||
      : data_(std::vector<uint8_t>(capacity)), endianness_(endianness), limit_(capacity){};
 | 
			
		||||
  /**
 | 
			
		||||
   * Wrap an existing vector in a Bytebufffer
 | 
			
		||||
   * Wrap an existing vector in a ByteBufffer
 | 
			
		||||
   */
 | 
			
		||||
  static ByteBuffer wrap(std::vector<uint8_t> data);
 | 
			
		||||
  static ByteBuffer wrap(std::vector<uint8_t> const &data, Endian endianness = LITTLE);
 | 
			
		||||
  /**
 | 
			
		||||
   * Wrap an existing array in a Bytebufffer
 | 
			
		||||
   * Wrap an existing array in a ByteBuffer. Note that this will create a copy of the data.
 | 
			
		||||
   */
 | 
			
		||||
  static ByteBuffer wrap(uint8_t *ptr, size_t len);
 | 
			
		||||
  static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE);
 | 
			
		||||
  // Convenience functions to create a ByteBuffer from a value
 | 
			
		||||
  static ByteBuffer wrap(uint8_t value);
 | 
			
		||||
  static ByteBuffer wrap(uint16_t value, Endian endianness = LITTLE);
 | 
			
		||||
  static ByteBuffer wrap(uint32_t value, Endian endianness = LITTLE);
 | 
			
		||||
  static ByteBuffer wrap(uint64_t value, Endian endianness = LITTLE);
 | 
			
		||||
  static ByteBuffer wrap(int8_t value) { return wrap(static_cast<uint8_t>(value)); }
 | 
			
		||||
  static ByteBuffer wrap(int16_t value, Endian endianness = LITTLE) {
 | 
			
		||||
    return wrap(static_cast<uint16_t>(value), endianness);
 | 
			
		||||
  }
 | 
			
		||||
  static ByteBuffer wrap(int32_t value, Endian endianness = LITTLE) {
 | 
			
		||||
    return wrap(static_cast<uint32_t>(value), endianness);
 | 
			
		||||
  }
 | 
			
		||||
  static ByteBuffer wrap(int64_t value, Endian endianness = LITTLE) {
 | 
			
		||||
    return wrap(static_cast<uint64_t>(value), endianness);
 | 
			
		||||
  }
 | 
			
		||||
  static ByteBuffer wrap(float value, Endian endianness = LITTLE);
 | 
			
		||||
  static ByteBuffer wrap(double value, Endian endianness = LITTLE);
 | 
			
		||||
  static ByteBuffer wrap(bool value) { return wrap(static_cast<uint8_t>(value)); }
 | 
			
		||||
 | 
			
		||||
  // Get an integral value from the buffer, increment position by length
 | 
			
		||||
  uint64_t get_uint(size_t length);
 | 
			
		||||
  // Get one byte from the buffer, increment position by 1
 | 
			
		||||
  uint8_t get_uint8();
 | 
			
		||||
  // Get a 16 bit unsigned value, increment by 2
 | 
			
		||||
  uint16_t get_uint16();
 | 
			
		||||
  uint16_t get_uint16() { return static_cast<uint16_t>(this->get_uint(sizeof(uint16_t))); };
 | 
			
		||||
  // Get a 24 bit unsigned value, increment by 3
 | 
			
		||||
  uint32_t get_uint24();
 | 
			
		||||
  uint32_t get_uint24() { return static_cast<uint32_t>(this->get_uint(3)); };
 | 
			
		||||
  // Get a 32 bit unsigned value, increment by 4
 | 
			
		||||
  uint32_t get_uint32();
 | 
			
		||||
  // signed versions of the get functions
 | 
			
		||||
  uint8_t get_int8() { return (int8_t) this->get_uint8(); };
 | 
			
		||||
  int16_t get_int16() { return (int16_t) this->get_uint16(); }
 | 
			
		||||
  uint32_t get_uint32() { return static_cast<uint32_t>(this->get_uint(sizeof(uint32_t))); };
 | 
			
		||||
  // Get a 64 bit unsigned value, increment by 8
 | 
			
		||||
  uint64_t get_uint64() { return this->get_uint(sizeof(uint64_t)); };
 | 
			
		||||
  // Signed versions of the get functions
 | 
			
		||||
  uint8_t get_int8() { return static_cast<int8_t>(this->get_uint8()); };
 | 
			
		||||
  int16_t get_int16() { return static_cast<int16_t>(this->get_uint(sizeof(int16_t))); }
 | 
			
		||||
  uint32_t get_int24();
 | 
			
		||||
  int32_t get_int32() { return (int32_t) this->get_uint32(); }
 | 
			
		||||
  int32_t get_int32() { return static_cast<int32_t>(this->get_uint(sizeof(int32_t))); }
 | 
			
		||||
  int64_t get_int64() { return static_cast<int64_t>(this->get_uint(sizeof(int64_t))); }
 | 
			
		||||
  // Get a float value, increment by 4
 | 
			
		||||
  float get_float();
 | 
			
		||||
  // Get a double value, increment by 8
 | 
			
		||||
  double get_double();
 | 
			
		||||
  // Get a bool value, increment by 1
 | 
			
		||||
  bool get_bool() { return this->get_uint8(); }
 | 
			
		||||
  // Get vector of bytes, increment by length
 | 
			
		||||
  std::vector<uint8_t> get_vector(size_t length);
 | 
			
		||||
 | 
			
		||||
  // put values into the buffer, increment the position accordingly
 | 
			
		||||
  // Put values into the buffer, increment the position accordingly
 | 
			
		||||
  // put any integral value, length represents the number of bytes
 | 
			
		||||
  void put_uint(uint64_t value, size_t length);
 | 
			
		||||
  void put_uint8(uint8_t value);
 | 
			
		||||
  void put_uint16(uint16_t value);
 | 
			
		||||
  void put_uint24(uint32_t value);
 | 
			
		||||
  void put_uint32(uint32_t value);
 | 
			
		||||
  void put_uint16(uint16_t value) { this->put_uint(value, sizeof(uint16_t)); }
 | 
			
		||||
  void put_uint24(uint32_t value) { this->put_uint(value, 3); }
 | 
			
		||||
  void put_uint32(uint32_t value) { this->put_uint(value, sizeof(uint32_t)); }
 | 
			
		||||
  void put_uint64(uint64_t value) { this->put_uint(value, sizeof(uint64_t)); }
 | 
			
		||||
  // Signed versions of the put functions
 | 
			
		||||
  void put_int8(int8_t value) { this->put_uint8(static_cast<uint8_t>(value)); }
 | 
			
		||||
  void put_int16(int32_t value) { this->put_uint(static_cast<uint16_t>(value), sizeof(uint16_t)); }
 | 
			
		||||
  void put_int24(int32_t value) { this->put_uint(static_cast<uint32_t>(value), 3); }
 | 
			
		||||
  void put_int32(int32_t value) { this->put_uint(static_cast<uint32_t>(value), sizeof(uint32_t)); }
 | 
			
		||||
  void put_int64(int64_t value) { this->put_uint(static_cast<uint64_t>(value), sizeof(uint64_t)); }
 | 
			
		||||
  // Extra put functions
 | 
			
		||||
  void put_float(float value);
 | 
			
		||||
  void put_double(double value);
 | 
			
		||||
  void put_bool(bool value) { this->put_uint8(value); }
 | 
			
		||||
  void put_vector(const std::vector<uint8_t> &value);
 | 
			
		||||
 | 
			
		||||
  inline size_t get_capacity() const { return this->data_.size(); }
 | 
			
		||||
  inline size_t get_position() const { return this->position_; }
 | 
			
		||||
@@ -80,12 +128,12 @@ class ByteBuffer {
 | 
			
		||||
  // set limit to current position, postition to zero. Used when swapping from write to read operations.
 | 
			
		||||
  void flip();
 | 
			
		||||
  // retrieve a pointer to the underlying data.
 | 
			
		||||
  uint8_t *array() { return this->data_.data(); };
 | 
			
		||||
  std::vector<uint8_t> get_data() { return this->data_; };
 | 
			
		||||
  void rewind() { this->position_ = 0; }
 | 
			
		||||
  void reset() { this->position_ = this->mark_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  ByteBuffer(std::vector<uint8_t> data) : data_(std::move(data)) { this->limit_ = this->get_capacity(); }
 | 
			
		||||
  ByteBuffer(std::vector<uint8_t> const &data) : data_(data), limit_(data.size()) {}
 | 
			
		||||
  std::vector<uint8_t> data_;
 | 
			
		||||
  Endian endianness_{LITTLE};
 | 
			
		||||
  size_t position_{0};
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,7 @@
 | 
			
		||||
#define USE_VALVE
 | 
			
		||||
#define USE_WIFI
 | 
			
		||||
#define USE_WIFI_AP
 | 
			
		||||
#define USE_WIREGUARD
 | 
			
		||||
 | 
			
		||||
// Arduino-specific feature flags
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
 
 | 
			
		||||
@@ -106,6 +106,8 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool:
 | 
			
		||||
        return True
 | 
			
		||||
    if old.build_path != new.build_path:
 | 
			
		||||
        return True
 | 
			
		||||
    if old.loaded_integrations != new.loaded_integrations:
 | 
			
		||||
        return True
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -117,7 +119,9 @@ def update_storage_json():
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if storage_should_clean(old, new):
 | 
			
		||||
        _LOGGER.info("Core config or version changed, cleaning build files...")
 | 
			
		||||
        _LOGGER.info(
 | 
			
		||||
            "Core config, version or integrations changed, cleaning build files..."
 | 
			
		||||
        )
 | 
			
		||||
        clean_build()
 | 
			
		||||
 | 
			
		||||
    new.save(path)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								tests/components/bl0942/test.bk72xx-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/components/bl0942/test.bk72xx-ard.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
uart:
 | 
			
		||||
  - id: uart_bl0942
 | 
			
		||||
    tx_pin:
 | 
			
		||||
      number: TX1
 | 
			
		||||
    rx_pin:
 | 
			
		||||
      number: RX1
 | 
			
		||||
    baud_rate: 2400
 | 
			
		||||
 | 
			
		||||
sensor:
 | 
			
		||||
  - platform: bl0942
 | 
			
		||||
    address: 0
 | 
			
		||||
    line_frequency: 50Hz
 | 
			
		||||
    voltage:
 | 
			
		||||
      name: BL0942 Voltage
 | 
			
		||||
    current:
 | 
			
		||||
      name: BL0942 Current
 | 
			
		||||
    power:
 | 
			
		||||
      name: BL0942 Power
 | 
			
		||||
    energy:
 | 
			
		||||
      name: BL0942 Energy
 | 
			
		||||
    frequency:
 | 
			
		||||
      name: BL0942 Frequency
 | 
			
		||||
@@ -19,6 +19,7 @@ display:
 | 
			
		||||
    lambda: |-
 | 
			
		||||
      it.rectangle(0, 0, it.get_width(), it.get_height());
 | 
			
		||||
  - platform: ili9xxx
 | 
			
		||||
    invert_colors: false
 | 
			
		||||
    dimensions:
 | 
			
		||||
      width: 320
 | 
			
		||||
      height: 240
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ display:
 | 
			
		||||
    lambda: |-
 | 
			
		||||
      it.rectangle(0, 0, it.get_width(), it.get_height());
 | 
			
		||||
  - platform: ili9xxx
 | 
			
		||||
    invert_colors: false
 | 
			
		||||
    dimensions:
 | 
			
		||||
      width: 320
 | 
			
		||||
      height: 240
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ display:
 | 
			
		||||
    lambda: |-
 | 
			
		||||
      it.rectangle(0, 0, it.get_width(), it.get_height());
 | 
			
		||||
  - platform: ili9xxx
 | 
			
		||||
    invert_colors: false
 | 
			
		||||
    dimensions:
 | 
			
		||||
      width: 320
 | 
			
		||||
      height: 240
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ display:
 | 
			
		||||
    lambda: |-
 | 
			
		||||
      it.rectangle(0, 0, it.get_width(), it.get_height());
 | 
			
		||||
  - platform: ili9xxx
 | 
			
		||||
    invert_colors: false
 | 
			
		||||
    dimensions:
 | 
			
		||||
      width: 320
 | 
			
		||||
      height: 240
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ display:
 | 
			
		||||
    lambda: |-
 | 
			
		||||
      it.rectangle(0, 0, it.get_width(), it.get_height());
 | 
			
		||||
  - platform: ili9xxx
 | 
			
		||||
    invert_colors: false
 | 
			
		||||
    dimensions:
 | 
			
		||||
      width: 320
 | 
			
		||||
      height: 240
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
lvgl:
 | 
			
		||||
  log_level: TRACE
 | 
			
		||||
  bg_color: light_blue
 | 
			
		||||
  disp_bg_color: 0xffff00
 | 
			
		||||
  disp_bg_image: cat_image
 | 
			
		||||
  theme:
 | 
			
		||||
    obj:
 | 
			
		||||
      border_width: 1
 | 
			
		||||
@@ -78,6 +80,9 @@ lvgl:
 | 
			
		||||
            on_click:
 | 
			
		||||
              then:
 | 
			
		||||
                - lvgl.animimg.stop: anim_img
 | 
			
		||||
                - lvgl.update:
 | 
			
		||||
                    disp_bg_color: 0xffff00
 | 
			
		||||
                    disp_bg_image: cat_image
 | 
			
		||||
        - label:
 | 
			
		||||
            text: "Hello shiny day"
 | 
			
		||||
            text_color: 0xFFFFFF
 | 
			
		||||
@@ -304,6 +309,17 @@ lvgl:
 | 
			
		||||
            src: cat_image
 | 
			
		||||
            align: top_left
 | 
			
		||||
            y: 50
 | 
			
		||||
        - tileview:
 | 
			
		||||
            id: tileview_id
 | 
			
		||||
            scrollbar_mode: active
 | 
			
		||||
            tiles:
 | 
			
		||||
              - id: page_1
 | 
			
		||||
                row: 0
 | 
			
		||||
                column: 0
 | 
			
		||||
                dir: HOR
 | 
			
		||||
                widgets:
 | 
			
		||||
                  - obj:
 | 
			
		||||
                      bg_color: 0x000000
 | 
			
		||||
 | 
			
		||||
    - id: page2
 | 
			
		||||
      widgets:
 | 
			
		||||
@@ -379,6 +395,7 @@ lvgl:
 | 
			
		||||
                    format: "bar value %f"
 | 
			
		||||
                    args: [x]
 | 
			
		||||
        - line:
 | 
			
		||||
            id: lv_line_id
 | 
			
		||||
            align: center
 | 
			
		||||
            points:
 | 
			
		||||
              - 5, 5
 | 
			
		||||
@@ -387,7 +404,10 @@ lvgl:
 | 
			
		||||
              - 180, 60
 | 
			
		||||
              - 240, 10
 | 
			
		||||
            on_click:
 | 
			
		||||
              lvgl.page.next:
 | 
			
		||||
              - lvgl.widget.update:
 | 
			
		||||
                  id: lv_line_id
 | 
			
		||||
                  line_color: 0xFFFF
 | 
			
		||||
              - lvgl.page.next:
 | 
			
		||||
        - switch:
 | 
			
		||||
            align: right_mid
 | 
			
		||||
        - checkbox:
 | 
			
		||||
 
 | 
			
		||||
@@ -220,6 +220,8 @@ switch:
 | 
			
		||||
      name: inverter0_output_source_priority_solar
 | 
			
		||||
    output_source_priority_battery:
 | 
			
		||||
      name: inverter0_output_source_priority_battery
 | 
			
		||||
    output_source_priority_hybrid:
 | 
			
		||||
      name: inverter0_output_source_priority_hybrid
 | 
			
		||||
    input_voltage_range:
 | 
			
		||||
      name: inverter0_input_voltage_range
 | 
			
		||||
    pv_ok_condition_for_parallel:
 | 
			
		||||
 
 | 
			
		||||
@@ -220,6 +220,8 @@ switch:
 | 
			
		||||
      name: inverter0_output_source_priority_solar
 | 
			
		||||
    output_source_priority_battery:
 | 
			
		||||
      name: inverter0_output_source_priority_battery
 | 
			
		||||
    output_source_priority_hybrid:
 | 
			
		||||
      name: inverter0_output_source_priority_hybrid
 | 
			
		||||
    input_voltage_range:
 | 
			
		||||
      name: inverter0_input_voltage_range
 | 
			
		||||
    pv_ok_condition_for_parallel:
 | 
			
		||||
 
 | 
			
		||||
@@ -220,6 +220,8 @@ switch:
 | 
			
		||||
      name: inverter0_output_source_priority_solar
 | 
			
		||||
    output_source_priority_battery:
 | 
			
		||||
      name: inverter0_output_source_priority_battery
 | 
			
		||||
    output_source_priority_hybrid:
 | 
			
		||||
      name: inverter0_output_source_priority_hybrid
 | 
			
		||||
    input_voltage_range:
 | 
			
		||||
      name: inverter0_input_voltage_range
 | 
			
		||||
    pv_ok_condition_for_parallel:
 | 
			
		||||
 
 | 
			
		||||
@@ -220,6 +220,8 @@ switch:
 | 
			
		||||
      name: inverter0_output_source_priority_solar
 | 
			
		||||
    output_source_priority_battery:
 | 
			
		||||
      name: inverter0_output_source_priority_battery
 | 
			
		||||
    output_source_priority_hybrid:
 | 
			
		||||
      name: inverter0_output_source_priority_hybrid
 | 
			
		||||
    input_voltage_range:
 | 
			
		||||
      name: inverter0_input_voltage_range
 | 
			
		||||
    pv_ok_condition_for_parallel:
 | 
			
		||||
 
 | 
			
		||||
@@ -220,6 +220,8 @@ switch:
 | 
			
		||||
      name: inverter0_output_source_priority_solar
 | 
			
		||||
    output_source_priority_battery:
 | 
			
		||||
      name: inverter0_output_source_priority_battery
 | 
			
		||||
    output_source_priority_hybrid:
 | 
			
		||||
      name: inverter0_output_source_priority_hybrid
 | 
			
		||||
    input_voltage_range:
 | 
			
		||||
      name: inverter0_input_voltage_range
 | 
			
		||||
    pv_ok_condition_for_parallel:
 | 
			
		||||
 
 | 
			
		||||
@@ -220,6 +220,8 @@ switch:
 | 
			
		||||
      name: inverter0_output_source_priority_solar
 | 
			
		||||
    output_source_priority_battery:
 | 
			
		||||
      name: inverter0_output_source_priority_battery
 | 
			
		||||
    output_source_priority_hybrid:
 | 
			
		||||
      name: inverter0_output_source_priority_hybrid
 | 
			
		||||
    input_voltage_range:
 | 
			
		||||
      name: inverter0_input_voltage_range
 | 
			
		||||
    pv_ok_condition_for_parallel:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user