mirror of
https://github.com/esphome/esphome.git
synced 2025-01-06 19:08:23 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
3ae536ab2c
2
.github/workflows/sync-device-classes.yml
vendored
2
.github/workflows/sync-device-classes.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
python ./script/sync-device_class.py
|
||||
|
||||
- name: Commit changes
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
uses: peter-evans/create-pull-request@v6.0.1
|
||||
with:
|
||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||
committer: esphomebot <esphome@nabucasa.com>
|
||||
|
@ -21,4 +21,10 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||
|
||||
# If /build is mounted, use that as the build path
|
||||
# otherwise use path in /config (so that builds aren't lost on container restart)
|
||||
if [[ -d /build ]]; then
|
||||
export ESPHOME_BUILD_PATH=/build
|
||||
fi
|
||||
|
||||
exec esphome "$@"
|
||||
|
@ -600,6 +600,7 @@ message ListEntitiesTextSensorResponse {
|
||||
string icon = 5;
|
||||
bool disabled_by_default = 6;
|
||||
EntityCategory entity_category = 7;
|
||||
string device_class = 8;
|
||||
}
|
||||
message TextSensorStateResponse {
|
||||
option (id) = 27;
|
||||
|
@ -543,6 +543,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
|
||||
msg.icon = text_sensor->get_icon();
|
||||
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
||||
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
|
||||
msg.device_class = text_sensor->get_device_class();
|
||||
return this->send_list_entities_text_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
|
@ -2721,6 +2721,10 @@ bool ListEntitiesTextSensorResponse::decode_length(uint32_t field_id, ProtoLengt
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->device_class = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -2743,6 +2747,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||
buffer.encode_string(8, this->device_class);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||
@ -2776,6 +2781,10 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||
out.append(" entity_category: ");
|
||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" device_class: ");
|
||||
out.append("'").append(this->device_class).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
@ -713,6 +713,7 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
|
||||
std::string icon{};
|
||||
bool disabled_by_default{false};
|
||||
enums::EntityCategory entity_category{};
|
||||
std::string device_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
|
@ -22,7 +22,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
async def to_code(config):
|
||||
if CORE.is_esp32 or CORE.is_libretiny:
|
||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
||||
cg.add_library("esphome/AsyncTCP-esphome", "2.0.1")
|
||||
cg.add_library("esphome/AsyncTCP-esphome", "2.1.3")
|
||||
elif CORE.is_esp8266:
|
||||
# https://github.com/esphome/ESPAsyncTCP
|
||||
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
|
||||
|
@ -118,7 +118,7 @@ void CSE7766Component::parse_data_() {
|
||||
uint32_t power_coeff = this->get_24_bit_uint_(14);
|
||||
uint32_t power_cycle = this->get_24_bit_uint_(17);
|
||||
uint8_t adj = this->raw_data_[20];
|
||||
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
|
||||
uint16_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
|
||||
|
||||
bool have_power = adj & 0x10;
|
||||
bool have_current = adj & 0x20;
|
||||
@ -132,8 +132,19 @@ void CSE7766Component::parse_data_() {
|
||||
}
|
||||
}
|
||||
|
||||
float energy = 0.0;
|
||||
if (this->energy_sensor_ != nullptr) {
|
||||
if (this->cf_pulses_last_ == 0 && !this->energy_sensor_->has_state()) {
|
||||
this->cf_pulses_last_ = cf_pulses;
|
||||
}
|
||||
uint16_t cf_diff = cf_pulses - this->cf_pulses_last_;
|
||||
this->cf_pulses_total_ += cf_diff;
|
||||
this->cf_pulses_last_ = cf_pulses;
|
||||
energy = this->cf_pulses_total_ * float(power_coeff) / 1000000.0f / 3600.0f;
|
||||
this->energy_sensor_->publish_state(energy);
|
||||
}
|
||||
|
||||
float power = 0.0f;
|
||||
float energy = 0.0f;
|
||||
if (power_cycle_exceeds_range) {
|
||||
// Datasheet: power cycle exceeding range means active power is 0
|
||||
if (this->power_sensor_ != nullptr) {
|
||||
@ -144,27 +155,6 @@ void CSE7766Component::parse_data_() {
|
||||
if (this->power_sensor_ != nullptr) {
|
||||
this->power_sensor_->publish_state(power);
|
||||
}
|
||||
|
||||
// Add CF pulses to the total energy only if we have Power coefficient to multiply by
|
||||
|
||||
if (this->cf_pulses_last_ == 0) {
|
||||
this->cf_pulses_last_ = cf_pulses;
|
||||
}
|
||||
|
||||
uint32_t cf_diff;
|
||||
if (cf_pulses < this->cf_pulses_last_) {
|
||||
cf_diff = cf_pulses + (0x10000 - this->cf_pulses_last_);
|
||||
} else {
|
||||
cf_diff = cf_pulses - this->cf_pulses_last_;
|
||||
}
|
||||
this->cf_pulses_last_ = cf_pulses;
|
||||
|
||||
energy = cf_diff * float(power_coeff) / 1000000.0f / 3600.0f;
|
||||
this->energy_total_ += energy;
|
||||
if (this->energy_sensor_ != nullptr)
|
||||
this->energy_sensor_->publish_state(this->energy_total_);
|
||||
} else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) {
|
||||
this->energy_sensor_->publish_state(0);
|
||||
}
|
||||
|
||||
float current = 0.0f;
|
||||
@ -183,6 +173,32 @@ void CSE7766Component::parse_data_() {
|
||||
}
|
||||
}
|
||||
|
||||
if (have_voltage && have_current) {
|
||||
const float apparent_power = voltage * current;
|
||||
if (this->apparent_power_sensor_ != nullptr) {
|
||||
this->apparent_power_sensor_->publish_state(apparent_power);
|
||||
}
|
||||
if (this->power_factor_sensor_ != nullptr && (have_power || power_cycle_exceeds_range)) {
|
||||
float pf = NAN;
|
||||
if (apparent_power > 0) {
|
||||
pf = power / apparent_power;
|
||||
if (pf < 0 || pf > 1) {
|
||||
ESP_LOGD(TAG, "Impossible power factor: %.4f not in interval [0, 1]", pf);
|
||||
pf = NAN;
|
||||
}
|
||||
} else if (apparent_power == 0 && power == 0) {
|
||||
// No load, report ideal power factor
|
||||
pf = 1.0f;
|
||||
} else if (current == 0 && calculated_current <= 0.05f) {
|
||||
// Datasheet: minimum measured current is 50mA
|
||||
ESP_LOGV(TAG, "Can't calculate power factor (current below minimum for CSE7766)");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Can't calculate power factor from P = %.4f W, S = %.4f VA", power, apparent_power);
|
||||
}
|
||||
this->power_factor_sensor_->publish_state(pf);
|
||||
}
|
||||
}
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
{
|
||||
std::stringstream ss;
|
||||
@ -215,6 +231,8 @@ void CSE7766Component::dump_config() {
|
||||
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
|
||||
LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_);
|
||||
LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_);
|
||||
this->check_uart_settings(4800);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,10 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
||||
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||
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_apparent_power_sensor(sensor::Sensor *apparent_power_sensor) {
|
||||
apparent_power_sensor_ = apparent_power_sensor;
|
||||
}
|
||||
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
|
||||
|
||||
void loop() override;
|
||||
float get_setup_priority() const override;
|
||||
@ -30,8 +34,10 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
||||
sensor::Sensor *current_sensor_{nullptr};
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
sensor::Sensor *energy_sensor_{nullptr};
|
||||
float energy_total_{0.0f};
|
||||
uint32_t cf_pulses_last_{0};
|
||||
sensor::Sensor *apparent_power_sensor_{nullptr};
|
||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||
uint32_t cf_pulses_total_{0};
|
||||
uint16_t cf_pulses_last_{0};
|
||||
};
|
||||
|
||||
} // namespace cse7766
|
||||
|
@ -2,19 +2,24 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, uart
|
||||
from esphome.const import (
|
||||
CONF_APPARENT_POWER,
|
||||
CONF_CURRENT,
|
||||
CONF_ENERGY,
|
||||
CONF_ID,
|
||||
CONF_POWER,
|
||||
CONF_POWER_FACTOR,
|
||||
CONF_VOLTAGE,
|
||||
DEVICE_CLASS_APPARENT_POWER,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_POWER_FACTOR,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_VOLT,
|
||||
UNIT_VOLT_AMPS,
|
||||
UNIT_WATT,
|
||||
UNIT_WATT_HOURS,
|
||||
)
|
||||
@ -51,6 +56,17 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional(CONF_APPARENT_POWER): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT_AMPS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_APPARENT_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
).extend(uart.UART_DEVICE_SCHEMA)
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
@ -75,3 +91,9 @@ async def to_code(config):
|
||||
if energy_config := config.get(CONF_ENERGY):
|
||||
sens = await sensor.new_sensor(energy_config)
|
||||
cg.add(var.set_energy_sensor(sens))
|
||||
if apparent_power_config := config.get(CONF_APPARENT_POWER):
|
||||
sens = await sensor.new_sensor(apparent_power_config)
|
||||
cg.add(var.set_apparent_power_sensor(sens))
|
||||
if power_factor_config := config.get(CONF_POWER_FACTOR):
|
||||
sens = await sensor.new_sensor(power_factor_config)
|
||||
cg.add(var.set_power_factor_sensor(sens))
|
||||
|
@ -257,6 +257,67 @@ void Display::filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Co
|
||||
this->filled_flat_side_triangle_(x3, y3, x2, y2, x_temp, y_temp, color);
|
||||
}
|
||||
}
|
||||
void HOT Display::get_regular_polygon_vertex(int vertex_id, int *vertex_x, int *vertex_y, int center_x, int center_y,
|
||||
int radius, int edges, RegularPolygonVariation variation,
|
||||
float rotation_degrees) {
|
||||
if (edges >= 2) {
|
||||
// Given the orientation of the display component, an angle is measured clockwise from the x axis.
|
||||
// For a regular polygon, the human reference would be the top of the polygon,
|
||||
// hence we rotate the shape by 270° to orient the polygon up.
|
||||
rotation_degrees += ROTATION_270_DEGREES;
|
||||
// Convert the rotation to radians, easier to use in trigonometrical calculations
|
||||
float rotation_radians = rotation_degrees * PI / 180;
|
||||
// A pointy top variation means the first vertex of the polygon is at the top center of the shape, this requires no
|
||||
// additional rotation of the shape.
|
||||
// A flat top variation means the first point of the polygon has to be rotated so that the first edge is horizontal,
|
||||
// this requires to rotate the shape by π/edges radians counter-clockwise so that the first point is located on the
|
||||
// left side of the first horizontal edge.
|
||||
rotation_radians -= (variation == VARIATION_FLAT_TOP) ? PI / edges : 0.0;
|
||||
|
||||
float vertex_angle = ((float) vertex_id) / edges * 2 * PI + rotation_radians;
|
||||
*vertex_x = (int) round(cos(vertex_angle) * radius) + center_x;
|
||||
*vertex_y = (int) round(sin(vertex_angle) * radius) + center_y;
|
||||
}
|
||||
}
|
||||
|
||||
void HOT Display::regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation,
|
||||
float rotation_degrees, Color color, RegularPolygonDrawing drawing) {
|
||||
if (edges >= 2) {
|
||||
int previous_vertex_x, previous_vertex_y;
|
||||
for (int current_vertex_id = 0; current_vertex_id <= edges; current_vertex_id++) {
|
||||
int current_vertex_x, current_vertex_y;
|
||||
get_regular_polygon_vertex(current_vertex_id, ¤t_vertex_x, ¤t_vertex_y, x, y, radius, edges,
|
||||
variation, rotation_degrees);
|
||||
if (current_vertex_id > 0) { // Start drawing after the 2nd vertex coordinates has been calculated
|
||||
if (drawing == DRAWING_FILLED) {
|
||||
this->filled_triangle(x, y, previous_vertex_x, previous_vertex_y, current_vertex_x, current_vertex_y, color);
|
||||
} else if (drawing == DRAWING_OUTLINE) {
|
||||
this->line(previous_vertex_x, previous_vertex_y, current_vertex_x, current_vertex_y, color);
|
||||
}
|
||||
}
|
||||
previous_vertex_x = current_vertex_x;
|
||||
previous_vertex_y = current_vertex_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
void HOT Display::regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation, Color color,
|
||||
RegularPolygonDrawing drawing) {
|
||||
regular_polygon(x, y, radius, edges, variation, ROTATION_0_DEGREES, color, drawing);
|
||||
}
|
||||
void HOT Display::regular_polygon(int x, int y, int radius, int edges, Color color, RegularPolygonDrawing drawing) {
|
||||
regular_polygon(x, y, radius, edges, VARIATION_POINTY_TOP, ROTATION_0_DEGREES, color, drawing);
|
||||
}
|
||||
void Display::filled_regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation,
|
||||
float rotation_degrees, Color color) {
|
||||
regular_polygon(x, y, radius, edges, variation, rotation_degrees, color, DRAWING_FILLED);
|
||||
}
|
||||
void Display::filled_regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation,
|
||||
Color color) {
|
||||
regular_polygon(x, y, radius, edges, variation, ROTATION_0_DEGREES, color, DRAWING_FILLED);
|
||||
}
|
||||
void Display::filled_regular_polygon(int x, int y, int radius, int edges, Color color) {
|
||||
regular_polygon(x, y, radius, edges, VARIATION_POINTY_TOP, ROTATION_0_DEGREES, color, DRAWING_FILLED);
|
||||
}
|
||||
|
||||
void Display::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) {
|
||||
int x_start, y_start;
|
||||
|
@ -137,6 +137,42 @@ enum DisplayRotation {
|
||||
DISPLAY_ROTATION_270_DEGREES = 270,
|
||||
};
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
|
||||
const int EDGES_TRIGON = 3;
|
||||
const int EDGES_TRIANGLE = 3;
|
||||
const int EDGES_TETRAGON = 4;
|
||||
const int EDGES_QUADRILATERAL = 4;
|
||||
const int EDGES_PENTAGON = 5;
|
||||
const int EDGES_HEXAGON = 6;
|
||||
const int EDGES_HEPTAGON = 7;
|
||||
const int EDGES_OCTAGON = 8;
|
||||
const int EDGES_NONAGON = 9;
|
||||
const int EDGES_ENNEAGON = 9;
|
||||
const int EDGES_DECAGON = 10;
|
||||
const int EDGES_HENDECAGON = 11;
|
||||
const int EDGES_DODECAGON = 12;
|
||||
const int EDGES_TRIDECAGON = 13;
|
||||
const int EDGES_TETRADECAGON = 14;
|
||||
const int EDGES_PENTADECAGON = 15;
|
||||
const int EDGES_HEXADECAGON = 16;
|
||||
|
||||
const float ROTATION_0_DEGREES = 0.0;
|
||||
const float ROTATION_45_DEGREES = 45.0;
|
||||
const float ROTATION_90_DEGREES = 90.0;
|
||||
const float ROTATION_180_DEGREES = 180.0;
|
||||
const float ROTATION_270_DEGREES = 270.0;
|
||||
|
||||
enum RegularPolygonVariation {
|
||||
VARIATION_POINTY_TOP = 0,
|
||||
VARIATION_FLAT_TOP = 1,
|
||||
};
|
||||
|
||||
enum RegularPolygonDrawing {
|
||||
DRAWING_OUTLINE = 0,
|
||||
DRAWING_FILLED = 1,
|
||||
};
|
||||
|
||||
class Display;
|
||||
class DisplayPage;
|
||||
class DisplayOnPageChangeTrigger;
|
||||
@ -175,10 +211,15 @@ class Display : public PollingComponent {
|
||||
/// Clear the entire screen by filling it with OFF pixels.
|
||||
void clear();
|
||||
|
||||
/// Get the width of the image in pixels with rotation applied.
|
||||
virtual int get_width() = 0;
|
||||
/// Get the height of the image in pixels with rotation applied.
|
||||
virtual int get_height() = 0;
|
||||
/// Get the calculated width of the display in pixels with rotation applied.
|
||||
virtual int get_width() { return this->get_width_internal(); }
|
||||
/// Get the calculated height of the display in pixels with rotation applied.
|
||||
virtual int get_height() { return this->get_height_internal(); }
|
||||
|
||||
/// Get the native (original) width of the display in pixels.
|
||||
int get_native_width() { return this->get_width_internal(); }
|
||||
/// Get the native (original) height of the display in pixels.
|
||||
int get_native_height() { return this->get_height_internal(); }
|
||||
|
||||
/// Set a single pixel at the specified coordinates to default color.
|
||||
inline void draw_pixel_at(int x, int y) { this->draw_pixel_at(x, y, COLOR_ON); }
|
||||
@ -242,6 +283,42 @@ class Display : public PollingComponent {
|
||||
/// Fill a triangle contained between the points [x1,y1], [x2,y2] and [x3,y3] with the given color.
|
||||
void filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color = COLOR_ON);
|
||||
|
||||
/// Get the specified vertex (x,y) coordinates for the regular polygon inscribed in the circle centered on
|
||||
/// [center_x,center_y] with the given radius. Vertex id are 0-indexed and rotate clockwise. In a pointy-topped
|
||||
/// variation of a polygon with a 0° rotation, the vertex #0 is located at the top of the polygon. In a flat-topped
|
||||
/// variation of a polygon with a 0° rotation, the vertex #0 is located on the left-side of the horizontal top
|
||||
/// edge, and the vertex #1 is located on the right-side of the horizontal top edge.
|
||||
/// Use the edges constants (e.g.: EDGES_HEXAGON) or any integer to specify the number of edges of the polygon.
|
||||
/// Use the variation to switch between the flat-topped or the pointy-topped variation of the polygon.
|
||||
/// Use the rotation in degrees to rotate the shape clockwise.
|
||||
void get_regular_polygon_vertex(int vertex_id, int *vertex_x, int *vertex_y, int center_x, int center_y, int radius,
|
||||
int edges, RegularPolygonVariation variation = VARIATION_POINTY_TOP,
|
||||
float rotation_degrees = ROTATION_0_DEGREES);
|
||||
|
||||
/// Draw the outline of a regular polygon inscribed in the circle centered on [x,y] with the given
|
||||
/// radius and color.
|
||||
/// Use the edges constants (e.g.: EDGES_HEXAGON) or any integer to specify the number of edges of the polygon.
|
||||
/// Use the variation to switch between the flat-topped or the pointy-topped variation of the polygon.
|
||||
/// Use the rotation in degrees to rotate the shape clockwise.
|
||||
/// Use the drawing to switch between outlining or filling the polygon.
|
||||
void regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation = VARIATION_POINTY_TOP,
|
||||
float rotation_degrees = ROTATION_0_DEGREES, Color color = COLOR_ON,
|
||||
RegularPolygonDrawing drawing = DRAWING_OUTLINE);
|
||||
void regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation, Color color,
|
||||
RegularPolygonDrawing drawing = DRAWING_OUTLINE);
|
||||
void regular_polygon(int x, int y, int radius, int edges, Color color,
|
||||
RegularPolygonDrawing drawing = DRAWING_OUTLINE);
|
||||
|
||||
/// Fill a regular polygon inscribed in the circle centered on [x,y] with the given radius and color.
|
||||
/// Use the edges constants (e.g.: EDGES_HEXAGON) or any integer to specify the number of edges of the polygon.
|
||||
/// Use the variation to switch between the flat-topped or the pointy-topped variation of the polygon.
|
||||
/// Use the rotation in degrees to rotate the shape clockwise.
|
||||
void filled_regular_polygon(int x, int y, int radius, int edges,
|
||||
RegularPolygonVariation variation = VARIATION_POINTY_TOP,
|
||||
float rotation_degrees = ROTATION_0_DEGREES, Color color = COLOR_ON);
|
||||
void filled_regular_polygon(int x, int y, int radius, int edges, RegularPolygonVariation variation, Color color);
|
||||
void filled_regular_polygon(int x, int y, int radius, int edges, Color color);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
@ -538,6 +615,9 @@ class Display : public PollingComponent {
|
||||
void do_update_();
|
||||
void clear_clipping_();
|
||||
|
||||
virtual int get_height_internal() = 0;
|
||||
virtual int get_width_internal() = 0;
|
||||
|
||||
/**
|
||||
* This method fills a triangle using only integer variables by using a
|
||||
* modified bresenham algorithm.
|
||||
|
@ -22,9 +22,6 @@ class DisplayBuffer : public Display {
|
||||
/// Set a single pixel at the specified coordinates to the given color.
|
||||
void draw_pixel_at(int x, int y, Color color) override;
|
||||
|
||||
virtual int get_height_internal() = 0;
|
||||
virtual int get_width_internal() = 0;
|
||||
|
||||
protected:
|
||||
virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0;
|
||||
|
||||
|
@ -34,24 +34,27 @@ void EKTF2232Touchscreen::setup() {
|
||||
|
||||
// Get touch resolution
|
||||
uint8_t received[4];
|
||||
this->write(GET_X_RES, 4);
|
||||
if (this->read(received, 4)) {
|
||||
ESP_LOGE(TAG, "Failed to read X resolution!");
|
||||
this->interrupt_pin_->detach_interrupt();
|
||||
this->mark_failed();
|
||||
return;
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->write(GET_X_RES, 4);
|
||||
if (this->read(received, 4)) {
|
||||
ESP_LOGE(TAG, "Failed to read X resolution!");
|
||||
this->interrupt_pin_->detach_interrupt();
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->x_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
||||
}
|
||||
this->x_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
||||
|
||||
this->write(GET_Y_RES, 4);
|
||||
if (this->read(received, 4)) {
|
||||
ESP_LOGE(TAG, "Failed to read Y resolution!");
|
||||
this->interrupt_pin_->detach_interrupt();
|
||||
this->mark_failed();
|
||||
return;
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->write(GET_Y_RES, 4);
|
||||
if (this->read(received, 4)) {
|
||||
ESP_LOGE(TAG, "Failed to read Y resolution!");
|
||||
this->interrupt_pin_->detach_interrupt();
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->y_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
||||
}
|
||||
this->y_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
|
||||
|
||||
this->set_power_state(true);
|
||||
}
|
||||
|
||||
|
@ -141,9 +141,13 @@ void ESP32ImprovComponent::loop() {
|
||||
|
||||
std::vector<std::string> urls = {ESPHOME_MY_LINK};
|
||||
#ifdef USE_WEBSERVER
|
||||
auto ip = wifi::global_wifi_component->wifi_sta_ip();
|
||||
std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT);
|
||||
urls.push_back(webserver_url);
|
||||
for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) {
|
||||
if (ip.is_ip4()) {
|
||||
std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT);
|
||||
urls.push_back(webserver_url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls);
|
||||
this->send_response_(data);
|
||||
|
@ -1,6 +1,13 @@
|
||||
from esphome import pins
|
||||
import esphome.config_validation as cv
|
||||
import esphome.final_validate as fv
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
)
|
||||
from esphome.const import (
|
||||
CONF_DOMAIN,
|
||||
CONF_ID,
|
||||
@ -12,9 +19,17 @@ from esphome.const import (
|
||||
CONF_SUBNET,
|
||||
CONF_DNS1,
|
||||
CONF_DNS2,
|
||||
CONF_CLK_PIN,
|
||||
CONF_MISO_PIN,
|
||||
CONF_MOSI_PIN,
|
||||
CONF_CS_PIN,
|
||||
CONF_INTERRUPT_PIN,
|
||||
CONF_RESET_PIN,
|
||||
CONF_SPI,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.components.network import IPAddress
|
||||
from esphome.components.spi import get_spi_interface, CONF_INTERFACE_INDEX
|
||||
|
||||
CONFLICTS_WITH = ["wifi"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
@ -27,6 +42,8 @@ CONF_MDIO_PIN = "mdio_pin"
|
||||
CONF_CLK_MODE = "clk_mode"
|
||||
CONF_POWER_PIN = "power_pin"
|
||||
|
||||
CONF_CLOCK_SPEED = "clock_speed"
|
||||
|
||||
EthernetType = ethernet_ns.enum("EthernetType")
|
||||
ETHERNET_TYPES = {
|
||||
"LAN8720": EthernetType.ETHERNET_TYPE_LAN8720,
|
||||
@ -36,8 +53,11 @@ ETHERNET_TYPES = {
|
||||
"JL1101": EthernetType.ETHERNET_TYPE_JL1101,
|
||||
"KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
|
||||
"KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
|
||||
"W5500": EthernetType.ETHERNET_TYPE_W5500,
|
||||
}
|
||||
|
||||
SPI_ETHERNET_TYPES = ["W5500"]
|
||||
|
||||
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
|
||||
emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t")
|
||||
CLK_MODES = {
|
||||
@ -84,11 +104,22 @@ def _validate(config):
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
BASE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(EthernetComponent),
|
||||
cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA,
|
||||
cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name,
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
cv.Optional("enable_mdns"): cv.invalid(
|
||||
"This option has been removed. Please use the [disabled] option under the "
|
||||
"new mdns component instead."
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
RMII_SCHEMA = BASE_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(EthernetComponent),
|
||||
cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True),
|
||||
cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum(
|
||||
@ -96,19 +127,64 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
|
||||
cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA,
|
||||
cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name,
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
cv.Optional("enable_mdns"): cv.invalid(
|
||||
"This option has been removed. Please use the [disabled] option under the "
|
||||
"new mdns component instead."
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
SPI_SCHEMA = BASE_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(CONF_MISO_PIN): pins.internal_gpio_input_pin_number,
|
||||
cv.Required(CONF_MOSI_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(CONF_CS_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_number,
|
||||
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_CLOCK_SPEED, default="26.67MHz"): cv.All(
|
||||
cv.frequency, cv.int_range(int(8e6), int(80e6))
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
),
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.typed_schema(
|
||||
{
|
||||
"LAN8720": RMII_SCHEMA,
|
||||
"RTL8201": RMII_SCHEMA,
|
||||
"DP83848": RMII_SCHEMA,
|
||||
"IP101": RMII_SCHEMA,
|
||||
"JL1101": RMII_SCHEMA,
|
||||
"W5500": SPI_SCHEMA,
|
||||
},
|
||||
upper=True,
|
||||
),
|
||||
_validate,
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(config):
|
||||
if config[CONF_TYPE] not in SPI_ETHERNET_TYPES:
|
||||
return
|
||||
if spi_configs := fv.full_config.get().get(CONF_SPI):
|
||||
variant = get_esp32_variant()
|
||||
if variant in (VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3):
|
||||
spi_host = "SPI2_HOST"
|
||||
else:
|
||||
spi_host = "SPI3_HOST"
|
||||
for spi_conf in spi_configs:
|
||||
if (index := spi_conf.get(CONF_INTERFACE_INDEX)) is not None:
|
||||
interface = get_spi_interface(index)
|
||||
if interface == spi_host:
|
||||
raise cv.Invalid(
|
||||
f"`spi` component is using interface '{interface}'. "
|
||||
f"To use {config[CONF_TYPE]}, you must change the `interface` on the `spi` component.",
|
||||
)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
def manual_ip(config):
|
||||
return cg.StructInitializer(
|
||||
ManualIP,
|
||||
@ -125,15 +201,31 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
|
||||
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
|
||||
cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN]))
|
||||
cg.add(var.set_type(config[CONF_TYPE]))
|
||||
cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]]))
|
||||
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
||||
if config[CONF_TYPE] == "W5500":
|
||||
cg.add(var.set_clk_pin(config[CONF_CLK_PIN]))
|
||||
cg.add(var.set_miso_pin(config[CONF_MISO_PIN]))
|
||||
cg.add(var.set_mosi_pin(config[CONF_MOSI_PIN]))
|
||||
cg.add(var.set_cs_pin(config[CONF_CS_PIN]))
|
||||
if CONF_INTERRUPT_PIN in config:
|
||||
cg.add(var.set_interrupt_pin(config[CONF_INTERRUPT_PIN]))
|
||||
if CONF_RESET_PIN in config:
|
||||
cg.add(var.set_reset_pin(config[CONF_RESET_PIN]))
|
||||
cg.add(var.set_clock_speed(config[CONF_CLOCK_SPEED]))
|
||||
|
||||
if CONF_POWER_PIN in config:
|
||||
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
|
||||
cg.add_define("USE_ETHERNET_SPI")
|
||||
if CORE.using_esp_idf:
|
||||
add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True)
|
||||
add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True)
|
||||
else:
|
||||
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
|
||||
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
|
||||
cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN]))
|
||||
cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]]))
|
||||
if CONF_POWER_PIN in config:
|
||||
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
|
||||
|
||||
cg.add(var.set_type(ETHERNET_TYPES[config[CONF_TYPE]]))
|
||||
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
||||
|
||||
if CONF_MANUAL_IP in config:
|
||||
cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
|
||||
|
@ -9,6 +9,11 @@
|
||||
#include <lwip/dns.h>
|
||||
#include "esp_event.h"
|
||||
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_master.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace ethernet {
|
||||
|
||||
@ -33,6 +38,36 @@ void EthernetComponent::setup() {
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
// Install GPIO ISR handler to be able to service SPI Eth modules interrupts
|
||||
gpio_install_isr_service(0);
|
||||
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = this->mosi_pin_,
|
||||
.miso_io_num = this->miso_pin_,
|
||||
.sclk_io_num = this->clk_pin_,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.data4_io_num = -1,
|
||||
.data5_io_num = -1,
|
||||
.data6_io_num = -1,
|
||||
.data7_io_num = -1,
|
||||
.max_transfer_sz = 0,
|
||||
.flags = 0,
|
||||
.intr_flags = 0,
|
||||
};
|
||||
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
auto host = SPI2_HOST;
|
||||
#else
|
||||
auto host = SPI3_HOST;
|
||||
#endif
|
||||
|
||||
err = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO);
|
||||
ESPHL_ERROR_CHECK(err, "SPI bus initialize error");
|
||||
#endif
|
||||
|
||||
err = esp_netif_init();
|
||||
ESPHL_ERROR_CHECK(err, "ETH netif init error");
|
||||
err = esp_event_loop_create_default();
|
||||
@ -43,10 +78,40 @@ void EthernetComponent::setup() {
|
||||
|
||||
// Init MAC and PHY configs to default
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
|
||||
#ifdef USE_ETHERNET_SPI // Configure SPI interface and Ethernet driver for specific SPI module
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.command_bits = 16, // Actually it's the address phase in W5500 SPI frame
|
||||
.address_bits = 8, // Actually it's the control phase in W5500 SPI frame
|
||||
.dummy_bits = 0,
|
||||
.mode = 0,
|
||||
.duty_cycle_pos = 0,
|
||||
.cs_ena_pretrans = 0,
|
||||
.cs_ena_posttrans = 0,
|
||||
.clock_speed_hz = this->clock_speed_,
|
||||
.input_delay_ns = 0,
|
||||
.spics_io_num = this->cs_pin_,
|
||||
.flags = 0,
|
||||
.queue_size = 20,
|
||||
.pre_cb = nullptr,
|
||||
.post_cb = nullptr,
|
||||
};
|
||||
|
||||
spi_device_handle_t spi_handle = nullptr;
|
||||
err = spi_bus_add_device(host, &devcfg, &spi_handle);
|
||||
ESPHL_ERROR_CHECK(err, "SPI bus add device error");
|
||||
|
||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
|
||||
w5500_config.int_gpio_num = this->interrupt_pin_;
|
||||
phy_config.phy_addr = this->phy_addr_spi_;
|
||||
phy_config.reset_gpio_num = this->reset_pin_;
|
||||
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||
#else
|
||||
phy_config.phy_addr = this->phy_addr_;
|
||||
phy_config.reset_gpio_num = this->power_pin_;
|
||||
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
esp32_emac_config.smi_mdc_gpio_num = this->mdc_pin_;
|
||||
@ -62,9 +127,11 @@ void EthernetComponent::setup() {
|
||||
mac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
|
||||
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
switch (this->type_) {
|
||||
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||
case ETHERNET_TYPE_LAN8720: {
|
||||
this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
|
||||
break;
|
||||
@ -94,6 +161,13 @@ void EthernetComponent::setup() {
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
case ETHERNET_TYPE_W5500: {
|
||||
this->phy_ = esp_eth_phy_new_w5500(&phy_config);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
this->mark_failed();
|
||||
return;
|
||||
@ -105,10 +179,18 @@ void EthernetComponent::setup() {
|
||||
err = esp_eth_driver_install(ð_config, &this->eth_handle_);
|
||||
ESPHL_ERROR_CHECK(err, "ETH driver install error");
|
||||
|
||||
#ifndef USE_ETHERNET_SPI
|
||||
if (this->type_ == ETHERNET_TYPE_KSZ8081RNA && this->clk_mode_ == EMAC_CLK_OUT) {
|
||||
// KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
|
||||
this->ksz8081_set_clock_reference_(mac);
|
||||
}
|
||||
#endif
|
||||
|
||||
// use ESP internal eth mac
|
||||
uint8_t mac_addr[6];
|
||||
esp_read_mac(mac_addr, ESP_MAC_ETH);
|
||||
err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_S_MAC_ADDR, mac_addr);
|
||||
ESPHL_ERROR_CHECK(err, "set mac address error");
|
||||
|
||||
/* attach Ethernet driver to TCP/IP stack */
|
||||
err = esp_netif_attach(this->eth_netif_, esp_eth_new_netif_glue(this->eth_handle_));
|
||||
@ -119,10 +201,10 @@ void EthernetComponent::setup() {
|
||||
ESPHL_ERROR_CHECK(err, "ETH event handler register error");
|
||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &EthernetComponent::got_ip_event_handler, nullptr);
|
||||
ESPHL_ERROR_CHECK(err, "GOT IP event handler register error");
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &EthernetComponent::got_ip6_event_handler, nullptr);
|
||||
ESPHL_ERROR_CHECK(err, "GOT IP6 event handler register error");
|
||||
#endif /* ENABLE_IPV6 */
|
||||
ESPHL_ERROR_CHECK(err, "GOT IPv6 event handler register error");
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
/* start Ethernet driver state machine */
|
||||
err = esp_eth_start(this->eth_handle_);
|
||||
@ -165,20 +247,6 @@ void EthernetComponent::loop() {
|
||||
this->state_ = EthernetComponentState::CONNECTING;
|
||||
this->start_connect_();
|
||||
}
|
||||
#if ENABLE_IPV6
|
||||
else if (this->got_ipv6_) {
|
||||
esp_ip6_addr_t ip6_addr;
|
||||
if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 &&
|
||||
esp_netif_ip6_get_addr_type(&ip6_addr) == ESP_IP6_ADDR_IS_GLOBAL) {
|
||||
ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr));
|
||||
} else {
|
||||
esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr);
|
||||
ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(ip6_addr));
|
||||
}
|
||||
|
||||
this->got_ipv6_ = false;
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -214,6 +282,10 @@ void EthernetComponent::dump_config() {
|
||||
eth_type = "KSZ8081RNA";
|
||||
break;
|
||||
|
||||
case ETHERNET_TYPE_W5500:
|
||||
eth_type = "W5500";
|
||||
break;
|
||||
|
||||
default:
|
||||
eth_type = "Unknown";
|
||||
break;
|
||||
@ -221,23 +293,51 @@ void EthernetComponent::dump_config() {
|
||||
|
||||
ESP_LOGCONFIG(TAG, "Ethernet:");
|
||||
this->dump_connect_params_();
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
ESP_LOGCONFIG(TAG, " CLK Pin: %u", this->clk_pin_);
|
||||
ESP_LOGCONFIG(TAG, " MISO Pin: %u", this->miso_pin_);
|
||||
ESP_LOGCONFIG(TAG, " MOSI Pin: %u", this->mosi_pin_);
|
||||
ESP_LOGCONFIG(TAG, " CS Pin: %u", this->cs_pin_);
|
||||
ESP_LOGCONFIG(TAG, " IRQ Pin: %u", this->interrupt_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Reset Pin: %d", this->reset_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Clock Speed: %d MHz", this->clock_speed_ / 1000000);
|
||||
#else
|
||||
if (this->power_pin_ != -1) {
|
||||
ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_);
|
||||
ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Type: %s", eth_type);
|
||||
ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_);
|
||||
#endif
|
||||
ESP_LOGCONFIG(TAG, " Type: %s", eth_type);
|
||||
}
|
||||
|
||||
float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; }
|
||||
|
||||
bool EthernetComponent::can_proceed() { return this->is_connected(); }
|
||||
|
||||
network::IPAddress EthernetComponent::get_ip_address() {
|
||||
network::IPAddresses EthernetComponent::get_ip_addresses() {
|
||||
network::IPAddresses addresses;
|
||||
esp_netif_ip_info_t ip;
|
||||
esp_netif_get_ip_info(this->eth_netif_, &ip);
|
||||
return network::IPAddress(&ip.ip);
|
||||
esp_err_t err = esp_netif_get_ip_info(this->eth_netif_, &ip);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
|
||||
// TODO: do something smarter
|
||||
// return false;
|
||||
} else {
|
||||
addresses[0] = network::IPAddress(&ip.ip);
|
||||
}
|
||||
#if USE_NETWORK_IPV6
|
||||
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
||||
uint8_t count = 0;
|
||||
count = esp_netif_get_all_ip6(this->eth_netif_, if_ip6s);
|
||||
assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
|
||||
for (int i = 0; i < count; i++) {
|
||||
addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
|
||||
}
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) {
|
||||
@ -269,20 +369,33 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base
|
||||
|
||||
void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data) {
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||||
const esp_netif_ip_info_t *ip_info = &event->ip_info;
|
||||
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip));
|
||||
global_eth_component->got_ipv4_address_ = true;
|
||||
#if USE_NETWORK_IPV6
|
||||
global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT;
|
||||
#else
|
||||
global_eth_component->connected_ = true;
|
||||
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%" PRId32 ")", event_id);
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data) {
|
||||
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%" PRId32 ")", event_id);
|
||||
global_eth_component->got_ipv6_ = true;
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data;
|
||||
ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip));
|
||||
global_eth_component->ipv6_count_ += 1;
|
||||
global_eth_component->connected_ =
|
||||
global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT);
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
void EthernetComponent::start_connect_() {
|
||||
global_eth_component->got_ipv4_address_ = false;
|
||||
#if USE_NETWORK_IPV6
|
||||
global_eth_component->ipv6_count_ = 0;
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
this->connect_begin_ = millis();
|
||||
this->status_set_warning();
|
||||
|
||||
@ -334,12 +447,12 @@ void EthernetComponent::start_connect_() {
|
||||
if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
|
||||
ESPHL_ERROR_CHECK(err, "DHCPC start error");
|
||||
}
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
||||
if (err != ESP_OK) {
|
||||
ESPHL_ERROR_CHECK(err, "IPv6 local failed");
|
||||
ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed");
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
this->connect_begin_ = millis();
|
||||
@ -362,18 +475,15 @@ void EthernetComponent::dump_connect_params_() {
|
||||
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str());
|
||||
|
||||
#if ENABLE_IPV6
|
||||
if (this->ipv6_count_ > 0) {
|
||||
esp_ip6_addr_t ip6_addr;
|
||||
esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr);
|
||||
ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(ip6_addr));
|
||||
|
||||
if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 &&
|
||||
esp_netif_ip6_get_addr_type(&ip6_addr) == ESP_IP6_ADDR_IS_GLOBAL) {
|
||||
ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr));
|
||||
}
|
||||
#if USE_NETWORK_IPV6
|
||||
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
||||
uint8_t count = 0;
|
||||
count = esp_netif_get_all_ip6(this->eth_netif_, if_ip6s);
|
||||
assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
|
||||
for (int i = 0; i < count; i++) {
|
||||
ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(if_ip6s[i]));
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
@ -393,15 +503,25 @@ void EthernetComponent::dump_connect_params_() {
|
||||
ESP_LOGCONFIG(TAG, " Link Speed: %u", speed == ETH_SPEED_100M ? 100 : 10);
|
||||
}
|
||||
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
void EthernetComponent::set_clk_pin(uint8_t clk_pin) { this->clk_pin_ = clk_pin; }
|
||||
void EthernetComponent::set_miso_pin(uint8_t miso_pin) { this->miso_pin_ = miso_pin; }
|
||||
void EthernetComponent::set_mosi_pin(uint8_t mosi_pin) { this->mosi_pin_ = mosi_pin; }
|
||||
void EthernetComponent::set_cs_pin(uint8_t cs_pin) { this->cs_pin_ = cs_pin; }
|
||||
void EthernetComponent::set_interrupt_pin(uint8_t interrupt_pin) { this->interrupt_pin_ = interrupt_pin; }
|
||||
void EthernetComponent::set_reset_pin(uint8_t reset_pin) { this->reset_pin_ = reset_pin; }
|
||||
void EthernetComponent::set_clock_speed(int clock_speed) { this->clock_speed_ = clock_speed; }
|
||||
#else
|
||||
void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; }
|
||||
void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; }
|
||||
void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; }
|
||||
void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; }
|
||||
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
|
||||
void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio) {
|
||||
this->clk_mode_ = clk_mode;
|
||||
this->clk_gpio_ = clk_gpio;
|
||||
}
|
||||
#endif
|
||||
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
|
||||
void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; }
|
||||
|
||||
std::string EthernetComponent::get_use_address() const {
|
||||
@ -428,6 +548,7 @@ bool EthernetComponent::powerdown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef USE_ETHERNET_SPI
|
||||
void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
||||
#define KSZ80XX_PC2R_REG_ADDR (0x1F)
|
||||
|
||||
@ -458,6 +579,7 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
||||
|
||||
#undef KSZ80XX_PC2R_REG_ADDR
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ethernet
|
||||
} // namespace esphome
|
||||
|
@ -23,6 +23,7 @@ enum EthernetType {
|
||||
ETHERNET_TYPE_JL1101,
|
||||
ETHERNET_TYPE_KSZ8081,
|
||||
ETHERNET_TYPE_KSZ8081RNA,
|
||||
ETHERNET_TYPE_W5500,
|
||||
};
|
||||
|
||||
struct ManualIP {
|
||||
@ -50,15 +51,25 @@ class EthernetComponent : public Component {
|
||||
void on_shutdown() override { powerdown(); }
|
||||
bool is_connected();
|
||||
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
void set_clk_pin(uint8_t clk_pin);
|
||||
void set_miso_pin(uint8_t miso_pin);
|
||||
void set_mosi_pin(uint8_t mosi_pin);
|
||||
void set_cs_pin(uint8_t cs_pin);
|
||||
void set_interrupt_pin(uint8_t interrupt_pin);
|
||||
void set_reset_pin(uint8_t reset_pin);
|
||||
void set_clock_speed(int clock_speed);
|
||||
#else
|
||||
void set_phy_addr(uint8_t phy_addr);
|
||||
void set_power_pin(int power_pin);
|
||||
void set_mdc_pin(uint8_t mdc_pin);
|
||||
void set_mdio_pin(uint8_t mdio_pin);
|
||||
void set_type(EthernetType type);
|
||||
void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio);
|
||||
#endif
|
||||
void set_type(EthernetType type);
|
||||
void set_manual_ip(const ManualIP &manual_ip);
|
||||
|
||||
network::IPAddress get_ip_address();
|
||||
network::IPAddresses get_ip_addresses();
|
||||
std::string get_use_address() const;
|
||||
void set_use_address(const std::string &use_address);
|
||||
bool powerdown();
|
||||
@ -76,19 +87,30 @@ class EthernetComponent : public Component {
|
||||
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
|
||||
|
||||
std::string use_address_;
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
uint8_t clk_pin_;
|
||||
uint8_t miso_pin_;
|
||||
uint8_t mosi_pin_;
|
||||
uint8_t cs_pin_;
|
||||
uint8_t interrupt_pin_;
|
||||
int reset_pin_{-1};
|
||||
int phy_addr_spi_{-1};
|
||||
int clock_speed_;
|
||||
#else
|
||||
uint8_t phy_addr_{0};
|
||||
int power_pin_{-1};
|
||||
uint8_t mdc_pin_{23};
|
||||
uint8_t mdio_pin_{18};
|
||||
EthernetType type_{ETHERNET_TYPE_UNKNOWN};
|
||||
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
|
||||
emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO};
|
||||
#endif
|
||||
EthernetType type_{ETHERNET_TYPE_UNKNOWN};
|
||||
optional<ManualIP> manual_ip_{};
|
||||
|
||||
bool started_{false};
|
||||
bool connected_{false};
|
||||
bool got_ipv4_address_{false};
|
||||
#if LWIP_IPV6
|
||||
bool got_ipv6_{false};
|
||||
uint8_t ipv6_count_{0};
|
||||
#endif /* LWIP_IPV6 */
|
||||
EthernetComponentState state_{EthernetComponentState::STOPPED};
|
||||
|
@ -12,19 +12,30 @@ namespace ethernet_info {
|
||||
class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor {
|
||||
public:
|
||||
void update() override {
|
||||
auto ip = ethernet::global_eth_component->get_ip_address();
|
||||
if (ip != this->last_ip_) {
|
||||
this->last_ip_ = ip;
|
||||
this->publish_state(network::IPAddress(ip).str());
|
||||
auto ips = ethernet::global_eth_component->get_ip_addresses();
|
||||
if (ips != this->last_ips_) {
|
||||
this->last_ips_ = ips;
|
||||
this->publish_state(ips[0].str());
|
||||
uint8_t sensor = 0;
|
||||
for (auto &ip : ips) {
|
||||
if (ip.is_set()) {
|
||||
if (this->ip_sensors_[sensor] != nullptr) {
|
||||
this->ip_sensors_[sensor]->publish_state(ip.str());
|
||||
}
|
||||
sensor++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::ETHERNET; }
|
||||
std::string unique_id() override { return get_mac_address() + "-ethernetinfo"; }
|
||||
void dump_config() override;
|
||||
void add_ip_sensors(uint8_t index, text_sensor::TextSensor *s) { this->ip_sensors_[index] = s; }
|
||||
|
||||
protected:
|
||||
network::IPAddress last_ip_;
|
||||
network::IPAddresses last_ips_;
|
||||
std::array<text_sensor::TextSensor *, 5> ip_sensors_;
|
||||
};
|
||||
|
||||
} // namespace ethernet_info
|
||||
|
@ -18,17 +18,25 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema(
|
||||
IPAddressEsthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
)
|
||||
.extend(cv.polling_component_schema("1s"))
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(f"address_{x}"): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
for x in range(5)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def setup_conf(config, key):
|
||||
if key in config:
|
||||
conf = config[key]
|
||||
var = await text_sensor.new_text_sensor(conf)
|
||||
await cg.register_component(var, conf)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
await setup_conf(config, CONF_IP_ADDRESS)
|
||||
if conf := config.get(CONF_IP_ADDRESS):
|
||||
ip_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS])
|
||||
await cg.register_component(ip_info, config[CONF_IP_ADDRESS])
|
||||
for x in range(5):
|
||||
if sensor_conf := conf.get(f"address_{x}"):
|
||||
sens = await text_sensor.new_text_sensor(sensor_conf)
|
||||
cg.add(ip_info.add_ip_sensors(x, sens))
|
||||
|
@ -66,8 +66,14 @@ class FT5x06Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
|
||||
return;
|
||||
}
|
||||
// reading the chip registers to get max x/y does not seem to work.
|
||||
this->x_raw_max_ = this->display_->get_width();
|
||||
this->y_raw_max_ = this->display_->get_height();
|
||||
if (this->display_ != nullptr) {
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_width();
|
||||
}
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_height();
|
||||
}
|
||||
}
|
||||
esph_log_config(TAG, "FT5x06 Touchscreen setup complete");
|
||||
}
|
||||
|
||||
|
@ -12,21 +12,23 @@
|
||||
// Reference: https://focuslcds.com/content/FT6236.pdf
|
||||
namespace esphome {
|
||||
namespace ft63x6 {
|
||||
static const uint8_t FT6X36_ADDR_DEVICE_MODE = 0x00;
|
||||
|
||||
static const uint8_t FT63X6_ADDR_TD_STATUS = 0x02;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_STATE = 0x03;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_X = 0x03;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_Y = 0x05;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_WEIGHT = 0x07;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_MISC = 0x08;
|
||||
static const uint8_t FT6X36_ADDR_THRESHHOLD = 0x80;
|
||||
static const uint8_t FT6X36_ADDR_TOUCHRATE_ACTIVE = 0x88;
|
||||
static const uint8_t FT63X6_ADDR_CHIP_ID = 0xA3;
|
||||
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_STATE = 0x09;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_X = 0x09;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_ID = 0x0B;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_Y = 0x0B;
|
||||
|
||||
static const char *const TAG = "FT63X6Touchscreen";
|
||||
static const char *const TAG = "FT63X6";
|
||||
|
||||
void FT63X6Touchscreen::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up FT63X6Touchscreen Touchscreen...");
|
||||
ESP_LOGCONFIG(TAG, "Setting up FT63X6 Touchscreen...");
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||
this->interrupt_pin_->setup();
|
||||
@ -35,10 +37,9 @@ void FT63X6Touchscreen::setup() {
|
||||
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->hard_reset_();
|
||||
}
|
||||
|
||||
this->hard_reset_();
|
||||
|
||||
// Get touch resolution
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = 320;
|
||||
@ -46,6 +47,15 @@ void FT63X6Touchscreen::setup() {
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->y_raw_max_ = 480;
|
||||
}
|
||||
uint8_t chip_id = this->read_byte_(FT63X6_ADDR_CHIP_ID);
|
||||
if (chip_id != 0) {
|
||||
ESP_LOGI(TAG, "FT6336U touch driver started chipid: %d", chip_id);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "FT6336U touch driver failed to start");
|
||||
}
|
||||
this->write_byte(FT6X36_ADDR_DEVICE_MODE, 0x00);
|
||||
this->write_byte(FT6X36_ADDR_THRESHHOLD, this->threshold_);
|
||||
this->write_byte(FT6X36_ADDR_TOUCHRATE_ACTIVE, 0x0E);
|
||||
}
|
||||
|
||||
void FT63X6Touchscreen::hard_reset_() {
|
||||
@ -65,28 +75,61 @@ void FT63X6Touchscreen::dump_config() {
|
||||
}
|
||||
|
||||
void FT63X6Touchscreen::update_touches() {
|
||||
uint8_t data[15];
|
||||
uint16_t touch_id, x, y;
|
||||
|
||||
if (!this->read_bytes(0x00, (uint8_t *) data, 15)) {
|
||||
ESP_LOGE(TAG, "Failed to read touch data");
|
||||
this->skip_update_ = true;
|
||||
uint8_t touches = this->read_touch_number_();
|
||||
if ((touches == 0x00) || (touches == 0xff)) {
|
||||
// ESP_LOGD(TAG, "No touches detected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (((data[FT63X6_ADDR_TOUCH1_STATE] >> 6) & 0x01) == 0) {
|
||||
touch_id = data[FT63X6_ADDR_TOUCH1_ID] >> 4; // id1 = 0 or 1
|
||||
x = encode_uint16(data[FT63X6_ADDR_TOUCH1_X] & 0x0F, data[FT63X6_ADDR_TOUCH1_X + 1]);
|
||||
y = encode_uint16(data[FT63X6_ADDR_TOUCH1_Y] & 0x0F, data[FT63X6_ADDR_TOUCH1_Y + 1]);
|
||||
this->add_raw_touch_position_(touch_id, x, y);
|
||||
}
|
||||
if (((data[FT63X6_ADDR_TOUCH2_STATE] >> 6) & 0x01) == 0) {
|
||||
touch_id = data[FT63X6_ADDR_TOUCH2_ID] >> 4; // id1 = 0 or 1
|
||||
x = encode_uint16(data[FT63X6_ADDR_TOUCH2_X] & 0x0F, data[FT63X6_ADDR_TOUCH2_X + 1]);
|
||||
y = encode_uint16(data[FT63X6_ADDR_TOUCH2_Y] & 0x0F, data[FT63X6_ADDR_TOUCH2_Y + 1]);
|
||||
this->add_raw_touch_position_(touch_id, x, y);
|
||||
ESP_LOGV(TAG, "Touches found: %d", touches);
|
||||
|
||||
for (auto point = 0; point < touches; point++) {
|
||||
if (((this->read_touch_event_(point)) & 0x01) == 0) { // checking event flag bit 6 if it is null
|
||||
touch_id = this->read_touch_id_(point); // id1 = 0 or 1
|
||||
x = this->read_touch_x_(point);
|
||||
y = this->read_touch_y_(point);
|
||||
if ((x == 0) && (y == 0)) {
|
||||
ESP_LOGW(TAG, "Reporting a (0,0) touch on %d", touch_id);
|
||||
}
|
||||
this->add_raw_touch_position_(touch_id, x, y, this->read_touch_weight_(point));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t FT63X6Touchscreen::read_touch_number_() { return this->read_byte_(FT63X6_ADDR_TD_STATUS) & 0x0F; }
|
||||
// Touch 1 functions
|
||||
uint16_t FT63X6Touchscreen::read_touch_x_(uint8_t touch) {
|
||||
uint8_t read_buf[2];
|
||||
read_buf[0] = this->read_byte_(FT63X6_ADDR_TOUCH1_X + (touch * 6));
|
||||
read_buf[1] = this->read_byte_(FT63X6_ADDR_TOUCH1_X + 1 + (touch * 6));
|
||||
return ((read_buf[0] & 0x0f) << 8) | read_buf[1];
|
||||
}
|
||||
uint16_t FT63X6Touchscreen::read_touch_y_(uint8_t touch) {
|
||||
uint8_t read_buf[2];
|
||||
read_buf[0] = this->read_byte_(FT63X6_ADDR_TOUCH1_Y + (touch * 6));
|
||||
read_buf[1] = this->read_byte_(FT63X6_ADDR_TOUCH1_Y + 1 + (touch * 6));
|
||||
return ((read_buf[0] & 0x0f) << 8) | read_buf[1];
|
||||
}
|
||||
uint8_t FT63X6Touchscreen::read_touch_event_(uint8_t touch) {
|
||||
return this->read_byte_(FT63X6_ADDR_TOUCH1_X + (touch * 6)) >> 6;
|
||||
}
|
||||
uint8_t FT63X6Touchscreen::read_touch_id_(uint8_t touch) {
|
||||
return this->read_byte_(FT63X6_ADDR_TOUCH1_ID + (touch * 6)) >> 4;
|
||||
}
|
||||
uint8_t FT63X6Touchscreen::read_touch_weight_(uint8_t touch) {
|
||||
return this->read_byte_(FT63X6_ADDR_TOUCH1_WEIGHT + (touch * 6));
|
||||
}
|
||||
uint8_t FT63X6Touchscreen::read_touch_misc_(uint8_t touch) {
|
||||
return this->read_byte_(FT63X6_ADDR_TOUCH1_MISC + (touch * 6)) >> 4;
|
||||
}
|
||||
|
||||
uint8_t FT63X6Touchscreen::read_byte_(uint8_t addr) {
|
||||
uint8_t byte = 0;
|
||||
this->read_byte(addr, &byte);
|
||||
return byte;
|
||||
}
|
||||
|
||||
} // namespace ft63x6
|
||||
} // namespace esphome
|
||||
|
@ -16,6 +16,8 @@ namespace ft63x6 {
|
||||
|
||||
using namespace touchscreen;
|
||||
|
||||
static const uint8_t FT6X36_DEFAULT_THRESHOLD = 22;
|
||||
|
||||
class FT63X6Touchscreen : public Touchscreen, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
@ -23,18 +25,26 @@ class FT63X6Touchscreen : public Touchscreen, public i2c::I2CDevice {
|
||||
|
||||
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
|
||||
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
|
||||
void set_threshold(uint8_t threshold) { this->threshold_ = threshold; }
|
||||
|
||||
protected:
|
||||
void hard_reset_();
|
||||
uint8_t read_byte_(uint8_t addr);
|
||||
void update_touches() override;
|
||||
|
||||
InternalGPIOPin *interrupt_pin_{nullptr};
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
uint8_t threshold_{FT6X36_DEFAULT_THRESHOLD};
|
||||
|
||||
uint8_t read_touch_count_();
|
||||
uint16_t read_touch_coordinate_(uint8_t coordinate);
|
||||
uint8_t read_touch_id_(uint8_t id_address);
|
||||
uint8_t read_touch_number_();
|
||||
|
||||
uint16_t read_touch_x_(uint8_t touch);
|
||||
uint16_t read_touch_y_(uint8_t touch);
|
||||
uint8_t read_touch_event_(uint8_t touch);
|
||||
uint8_t read_touch_id_(uint8_t touch);
|
||||
uint8_t read_touch_weight_(uint8_t touch);
|
||||
uint8_t read_touch_misc_(uint8_t touch);
|
||||
|
||||
uint8_t read_byte_(uint8_t addr);
|
||||
};
|
||||
|
||||
} // namespace ft63x6
|
||||
|
@ -3,7 +3,7 @@ import esphome.config_validation as cv
|
||||
|
||||
from esphome import pins
|
||||
from esphome.components import i2c, touchscreen
|
||||
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN
|
||||
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN, CONF_THRESHOLD
|
||||
|
||||
CODEOWNERS = ["@gpambrozio"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@ -26,6 +26,7 @@ CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
|
||||
pins.internal_gpio_input_pin_schema
|
||||
),
|
||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_THRESHOLD): cv.uint8_t,
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x38))
|
||||
)
|
||||
|
@ -48,9 +48,13 @@ void GT911Touchscreen::setup() {
|
||||
if (err == i2c::ERROR_OK) {
|
||||
err = this->read(data, sizeof(data));
|
||||
if (err == i2c::ERROR_OK) {
|
||||
this->x_raw_max_ = encode_uint16(data[1], data[0]);
|
||||
this->y_raw_max_ = encode_uint16(data[3], data[2]);
|
||||
esph_log_d(TAG, "Read max_x/max_y %d/%d", this->x_raw_max_, this->y_raw_max_);
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = encode_uint16(data[1], data[0]);
|
||||
}
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->y_raw_max_ = encode_uint16(data[3], data[2]);
|
||||
}
|
||||
esph_log_d(TAG, "calibration max_x/max_y %d/%d", this->x_raw_max_, this->y_raw_max_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,13 @@ std::string ImprovBase::get_formatted_next_url_() {
|
||||
// Ip address
|
||||
pos = this->next_url_.find("{{ip_address}}");
|
||||
if (pos != std::string::npos) {
|
||||
std::string ip = network::get_ip_address().str();
|
||||
copy.replace(pos, 14, ip);
|
||||
for (auto &ip : network::get_ip_addresses()) {
|
||||
if (ip.is_ip4()) {
|
||||
std::string ipa = ip.str();
|
||||
copy.replace(pos, 14, ipa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
|
@ -155,9 +155,13 @@ std::vector<uint8_t> ImprovSerialComponent::build_rpc_settings_response_(improv:
|
||||
urls.push_back(this->get_formatted_next_url_());
|
||||
}
|
||||
#ifdef USE_WEBSERVER
|
||||
auto ip = wifi::global_wifi_component->wifi_sta_ip();
|
||||
std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT);
|
||||
urls.push_back(webserver_url);
|
||||
for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) {
|
||||
if (ip.is_ip4()) {
|
||||
std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT);
|
||||
urls.push_back(webserver_url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(command, urls, false);
|
||||
return data;
|
||||
|
@ -38,9 +38,14 @@ void LilygoT547Touchscreen::setup() {
|
||||
}
|
||||
|
||||
this->write_register(POWER_REGISTER, WAKEUP_CMD, 1);
|
||||
|
||||
this->x_raw_max_ = this->get_width_();
|
||||
this->y_raw_max_ = this->get_height_();
|
||||
if (this->display_ != nullptr) {
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_width();
|
||||
}
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_height();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LilygoT547Touchscreen::update_touches() {
|
||||
|
@ -91,8 +91,13 @@ void MQTTClientComponent::send_device_info_() {
|
||||
this->publish_json(
|
||||
topic,
|
||||
[](JsonObject root) {
|
||||
auto ip = network::get_ip_address();
|
||||
root["ip"] = ip.str();
|
||||
uint8_t index = 0;
|
||||
for (auto &ip : network::get_ip_addresses()) {
|
||||
if (ip.is_set()) {
|
||||
root["ip" + (index == 0 ? "" : esphome::to_string(index))] = ip.str();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
root["name"] = App.get_name();
|
||||
#ifdef USE_API
|
||||
root["port"] = api::global_api_server->get_port();
|
||||
@ -159,14 +164,13 @@ void MQTTClientComponent::start_dnslookup_() {
|
||||
this->dns_resolve_error_ = false;
|
||||
this->dns_resolved_ = false;
|
||||
ip_addr_t addr;
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
#if USE_NETWORK_IPV6
|
||||
err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
|
||||
MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
|
||||
#else
|
||||
err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
|
||||
MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4);
|
||||
#endif
|
||||
#ifdef USE_ESP8266
|
||||
err_t err = dns_gethostbyname(this->credentials_.address.c_str(), &addr,
|
||||
esphome::mqtt::MQTTClientComponent::dns_found_callback, this);
|
||||
#endif
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
switch (err) {
|
||||
case ERR_OK: {
|
||||
// Got IP immediately
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "mqtt_text_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
||||
@ -13,6 +15,8 @@ using namespace esphome::text_sensor;
|
||||
|
||||
MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {}
|
||||
void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
if (!this->sensor_->get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
||||
config.command_topic = false;
|
||||
}
|
||||
void MQTTTextSensor::setup() {
|
||||
|
@ -5,6 +5,7 @@ from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ENABLE_IPV6,
|
||||
CONF_MIN_IPV6_ADDR_COUNT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@ -16,12 +17,14 @@ IPAddress = network_ns.class_("IPAddress")
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_ENABLE_IPV6, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_define("ENABLE_IPV6", config[CONF_ENABLE_IPV6])
|
||||
cg.add_define("USE_NETWORK_IPV6", config[CONF_ENABLE_IPV6])
|
||||
cg.add_define("USE_NETWORK_MIN_IPV6_ADDR_COUNT", config[CONF_MIN_IPV6_ADDR_COUNT])
|
||||
if CORE.using_esp_idf:
|
||||
add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", config[CONF_ENABLE_IPV6])
|
||||
add_idf_sdkconfig_option(
|
||||
|
@ -77,6 +77,13 @@ struct IPAddress {
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
IPAddress(esp_ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t)); }
|
||||
IPAddress(esp_ip_addr_t *other_ip) {
|
||||
#if LWIP_IPV6
|
||||
memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip_addr_));
|
||||
#else
|
||||
memcpy((void *) &ip_addr_, (void *) &other_ip->u_addr.ip4, sizeof(ip_addr_));
|
||||
#endif
|
||||
}
|
||||
operator esp_ip_addr_t() const {
|
||||
esp_ip_addr_t tmp;
|
||||
#if LWIP_IPV6
|
||||
@ -128,5 +135,7 @@ struct IPAddress {
|
||||
ip_addr_t ip_addr_;
|
||||
};
|
||||
|
||||
using IPAddresses = std::array<IPAddress, 5>;
|
||||
|
||||
} // namespace network
|
||||
} // namespace esphome
|
||||
|
@ -37,14 +37,14 @@ bool is_disabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
network::IPAddress get_ip_address() {
|
||||
network::IPAddresses get_ip_addresses() {
|
||||
#ifdef USE_ETHERNET
|
||||
if (ethernet::global_eth_component != nullptr)
|
||||
return ethernet::global_eth_component->get_ip_address();
|
||||
return ethernet::global_eth_component->get_ip_addresses();
|
||||
#endif
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr)
|
||||
return wifi::global_wifi_component->get_ip_address();
|
||||
return wifi::global_wifi_component->get_ip_addresses();
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ bool is_connected();
|
||||
bool is_disabled();
|
||||
/// Get the active network hostname
|
||||
std::string get_use_address();
|
||||
IPAddress get_ip_address();
|
||||
IPAddresses get_ip_addresses();
|
||||
|
||||
} // namespace network
|
||||
} // namespace esphome
|
||||
|
@ -12,6 +12,7 @@ from esphome.const import (
|
||||
CONF_PLATFORM,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SPEAKER,
|
||||
CONF_GAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -38,6 +39,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(Rtttl),
|
||||
cv.Optional(CONF_OUTPUT): cv.use_id(FloatOutput),
|
||||
cv.Optional(CONF_SPEAKER): cv.use_id(Speaker),
|
||||
cv.Optional(CONF_GAIN, default="0.6"): cv.percentage,
|
||||
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
@ -98,6 +100,8 @@ async def to_code(config):
|
||||
out = await cg.get_variable(config[CONF_SPEAKER])
|
||||
cg.add(var.set_speaker(out))
|
||||
|
||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||
|
||||
for conf in config.get(CONF_ON_FINISHED_PLAYBACK, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
@ -16,7 +16,7 @@ static const uint16_t NOTES[] = {0, 262, 277, 294, 311, 330, 349, 370,
|
||||
1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217,
|
||||
2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951};
|
||||
|
||||
static const uint16_t I2S_SPEED = 1600;
|
||||
static const uint16_t I2S_SPEED = 1000;
|
||||
|
||||
#undef HALF_PI
|
||||
static const double HALF_PI = 1.5707963267948966192313216916398;
|
||||
@ -136,7 +136,7 @@ void Rtttl::loop() {
|
||||
if (this->samples_per_wave_ != 0 && this->samples_sent_ >= this->samples_gap_) { // Play note//
|
||||
rem = ((this->samples_sent_ << 10) % this->samples_per_wave_) * (360.0 / this->samples_per_wave_);
|
||||
|
||||
int16_t val = 8192 * sin(deg2rad(rem));
|
||||
int16_t val = (49152 * this->gain_) * sin(deg2rad(rem));
|
||||
|
||||
sample[x].left = val;
|
||||
sample[x].right = val;
|
||||
@ -269,7 +269,7 @@ void Rtttl::loop() {
|
||||
}
|
||||
if (this->output_freq_ != 0) {
|
||||
this->output_->update_frequency(this->output_freq_);
|
||||
this->output_->set_level(0.5);
|
||||
this->output_->set_level(this->gain_);
|
||||
} else {
|
||||
this->output_->set_level(0.0);
|
||||
}
|
||||
@ -278,18 +278,23 @@ void Rtttl::loop() {
|
||||
#ifdef USE_SPEAKER
|
||||
if (this->speaker_ != nullptr) {
|
||||
this->samples_sent_ = 0;
|
||||
this->samples_count_ = (this->sample_rate_ * this->note_duration_) / I2S_SPEED;
|
||||
// Convert from frequency in Hz to high and low samples in fixed point
|
||||
this->samples_gap_ = 0;
|
||||
this->samples_per_wave_ = 0;
|
||||
this->samples_count_ = (this->sample_rate_ * this->note_duration_) / 1600; //(ms);
|
||||
if (need_note_gap) {
|
||||
this->samples_gap_ = (this->sample_rate_ * DOUBLE_NOTE_GAP_MS) / 1600; //(ms);
|
||||
}
|
||||
if (this->output_freq_ != 0) {
|
||||
this->samples_per_wave_ = (this->sample_rate_ << 10) / this->output_freq_;
|
||||
} else {
|
||||
this->samples_per_wave_ = 0;
|
||||
}
|
||||
if (need_note_gap) {
|
||||
this->samples_gap_ = (this->sample_rate_ * DOUBLE_NOTE_GAP_MS) / I2S_SPEED;
|
||||
} else {
|
||||
this->samples_gap_ = 0;
|
||||
|
||||
// make sure there is enough samples to add a full last sinus.
|
||||
uint16_t division = ((this->samples_count_ << 10) / this->samples_per_wave_) + 1;
|
||||
uint16_t x = this->samples_count_;
|
||||
this->samples_count_ = (division * this->samples_per_wave_);
|
||||
ESP_LOGD(TAG, "play time old: %d div: %d new: %d %d", x, division, this->samples_count_, this->samples_per_wave_);
|
||||
this->samples_count_ = this->samples_count_ >> 10;
|
||||
}
|
||||
// Convert from frequency in Hz to high and low samples in fixed point
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace esphome {
|
||||
namespace rtttl {
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
static const size_t SAMPLE_BUFFER_SIZE = 256;
|
||||
static const size_t SAMPLE_BUFFER_SIZE = 512;
|
||||
|
||||
struct SpeakerSample {
|
||||
int16_t left{0};
|
||||
@ -31,6 +31,13 @@ class Rtttl : public Component {
|
||||
#ifdef USE_SPEAKER
|
||||
void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; }
|
||||
#endif
|
||||
void set_gain(float gain) {
|
||||
if (gain < 0.1f)
|
||||
gain = 0.1f;
|
||||
if (gain > 1.0f)
|
||||
gain = 1.0f;
|
||||
this->gain_ = gain;
|
||||
}
|
||||
void play(std::string rtttl);
|
||||
void stop();
|
||||
void dump_config() override;
|
||||
@ -60,6 +67,7 @@ class Rtttl : public Component {
|
||||
uint16_t note_duration_;
|
||||
|
||||
uint32_t output_freq_;
|
||||
float gain_{0.6f};
|
||||
|
||||
#ifdef USE_OUTPUT
|
||||
output::FloatOutput *output_;
|
||||
@ -68,13 +76,13 @@ class Rtttl : public Component {
|
||||
void play_output_();
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
speaker::Speaker *speaker_;
|
||||
void play_speaker_();
|
||||
speaker::Speaker *speaker_{nullptr};
|
||||
int sample_rate_{16000};
|
||||
int samples_per_wave_{0};
|
||||
int samples_sent_{0};
|
||||
int samples_count_{0};
|
||||
int samples_gap_{0};
|
||||
|
||||
#endif
|
||||
|
||||
CallbackManager<void()> on_finished_playback_callback_;
|
||||
|
@ -252,7 +252,9 @@ ThrottleAverageFilter::ThrottleAverageFilter(uint32_t time_period) : time_period
|
||||
|
||||
optional<float> ThrottleAverageFilter::new_value(float value) {
|
||||
ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::new_value(value=%f)", this, value);
|
||||
if (!std::isnan(value)) {
|
||||
if (std::isnan(value)) {
|
||||
this->have_nan_ = true;
|
||||
} else {
|
||||
this->sum_ += value;
|
||||
this->n_++;
|
||||
}
|
||||
@ -262,12 +264,14 @@ void ThrottleAverageFilter::setup() {
|
||||
this->set_interval("throttle_average", this->time_period_, [this]() {
|
||||
ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::interval(sum=%f, n=%i)", this, this->sum_, this->n_);
|
||||
if (this->n_ == 0) {
|
||||
this->output(NAN);
|
||||
if (this->have_nan_)
|
||||
this->output(NAN);
|
||||
} else {
|
||||
this->output(this->sum_ / this->n_);
|
||||
this->sum_ = 0.0f;
|
||||
this->n_ = 0;
|
||||
}
|
||||
this->have_nan_ = false;
|
||||
});
|
||||
}
|
||||
float ThrottleAverageFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
@ -245,6 +245,7 @@ class ThrottleAverageFilter : public Filter, public Component {
|
||||
uint32_t time_period_;
|
||||
float sum_{0.0f};
|
||||
unsigned int n_{0};
|
||||
bool have_nan_{false};
|
||||
};
|
||||
|
||||
using lambda_filter_t = std::function<optional<float>(float)>;
|
||||
|
@ -10,15 +10,15 @@ namespace socket {
|
||||
Socket::~Socket() {}
|
||||
|
||||
std::unique_ptr<Socket> socket_ip(int type, int protocol) {
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
return socket(AF_INET6, type, protocol);
|
||||
#else
|
||||
return socket(AF_INET, type, protocol);
|
||||
#endif
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) {
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
if (addrlen < sizeof(sockaddr_in6)) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
@ -47,11 +47,11 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri
|
||||
server->sin_addr.s_addr = inet_addr(ip_address.c_str());
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
#endif
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) {
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
if (addrlen < sizeof(sockaddr_in6)) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
@ -73,7 +73,7 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po
|
||||
server->sin_addr.s_addr = ESPHOME_INADDR_ANY;
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
#endif
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
} // namespace socket
|
||||
} // namespace esphome
|
||||
|
@ -19,7 +19,7 @@ SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan)
|
||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan),
|
||||
cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_SPEED): cv.invalid(
|
||||
@ -32,11 +32,14 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], output_, config[CONF_SPEED_COUNT])
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_SPEED_COUNT])
|
||||
await cg.register_component(var, config)
|
||||
await fan.register_fan(var, config)
|
||||
|
||||
if CONF_OUTPUT in config:
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(output_))
|
||||
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
cg.add(var.set_oscillating(oscillation_output))
|
||||
|
@ -36,9 +36,10 @@ void SpeedFan::control(const fan::FanCall &call) {
|
||||
}
|
||||
|
||||
void SpeedFan::write_state_() {
|
||||
float speed = this->state ? static_cast<float>(this->speed) / static_cast<float>(this->speed_count_) : 0.0f;
|
||||
this->output_->set_level(speed);
|
||||
|
||||
if (this->output_ != nullptr) {
|
||||
float speed = this->state ? static_cast<float>(this->speed) / static_cast<float>(this->speed_count_) : 0.0f;
|
||||
this->output_->set_level(speed);
|
||||
}
|
||||
if (this->oscillating_ != nullptr)
|
||||
this->oscillating_->set_state(this->oscillating);
|
||||
if (this->direction_ != nullptr)
|
||||
|
@ -12,9 +12,10 @@ namespace speed {
|
||||
|
||||
class SpeedFan : public Component, public fan::Fan {
|
||||
public:
|
||||
SpeedFan(output::FloatOutput *output, int speed_count) : output_(output), speed_count_(speed_count) {}
|
||||
SpeedFan(int speed_count) : speed_count_(speed_count) {}
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void set_output(output::FloatOutput *output) { this->output_ = output; }
|
||||
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
|
||||
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
||||
void set_preset_modes(const std::set<std::string> &presets) { this->preset_modes_ = presets; }
|
||||
@ -24,7 +25,7 @@ class SpeedFan : public Component, public fan::Fan {
|
||||
void control(const fan::FanCall &call) override;
|
||||
void write_state_();
|
||||
|
||||
output::FloatOutput *output_;
|
||||
output::FloatOutput *output_{nullptr};
|
||||
output::BinaryOutput *oscillating_{nullptr};
|
||||
output::BinaryOutput *direction_{nullptr};
|
||||
int speed_count_{};
|
||||
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_FILTERS,
|
||||
CONF_ICON,
|
||||
@ -14,12 +15,21 @@ from esphome.const import (
|
||||
CONF_STATE,
|
||||
CONF_FROM,
|
||||
CONF_TO,
|
||||
DEVICE_CLASS_DATE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
from esphome.util import Registry
|
||||
|
||||
DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_DATE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
]
|
||||
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
@ -112,10 +122,13 @@ async def map_filter_to_code(config, filter_id):
|
||||
)
|
||||
|
||||
|
||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
TEXT_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor),
|
||||
cv.GenerateID(): cv.declare_id(TextSensor),
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_FILTERS): validate_filters,
|
||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||
{
|
||||
@ -140,12 +153,21 @@ def text_sensor_schema(
|
||||
*,
|
||||
icon: str = _UNDEF,
|
||||
entity_category: str = _UNDEF,
|
||||
device_class: str = _UNDEF,
|
||||
) -> cv.Schema:
|
||||
schema = TEXT_SENSOR_SCHEMA
|
||||
if class_ is not _UNDEF:
|
||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
||||
if icon is not _UNDEF:
|
||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
||||
if device_class is not _UNDEF:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_DEVICE_CLASS, default=device_class
|
||||
): validate_device_class
|
||||
}
|
||||
)
|
||||
if entity_category is not _UNDEF:
|
||||
schema = schema.extend(
|
||||
{
|
||||
@ -164,6 +186,9 @@ async def build_filters(config):
|
||||
async def setup_text_sensor_core_(var, config):
|
||||
await setup_entity(var, config)
|
||||
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
|
||||
if config.get(CONF_FILTERS): # must exist and not be empty
|
||||
filters = await build_filters(config[CONF_FILTERS])
|
||||
cg.add(var.set_filters(filters))
|
||||
|
@ -13,6 +13,9 @@ namespace text_sensor {
|
||||
#define LOG_TEXT_SENSOR(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
if (!(obj)->get_icon().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||
} \
|
||||
@ -28,7 +31,7 @@ namespace text_sensor {
|
||||
public: \
|
||||
void set_##name##_text_sensor(text_sensor::TextSensor *text_sensor) { this->name##_text_sensor_ = text_sensor; }
|
||||
|
||||
class TextSensor : public EntityBase {
|
||||
class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
/// Getter-syntax for .state.
|
||||
std::string get_state() const;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "thermostat_climate.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace thermostat {
|
||||
@ -63,6 +62,15 @@ void ThermostatClimate::setup() {
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void ThermostatClimate::loop() {
|
||||
for (auto &timer : this->timer_) {
|
||||
if (timer.active && (timer.started + timer.time < millis())) {
|
||||
timer.active = false;
|
||||
timer.func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float ThermostatClimate::cool_deadband() { return this->cooling_deadband_; }
|
||||
float ThermostatClimate::cool_overrun() { return this->cooling_overrun_; }
|
||||
float ThermostatClimate::heat_deadband() { return this->heating_deadband_; }
|
||||
@ -439,6 +447,7 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
|
||||
this->start_timer_(thermostat::TIMER_FANNING_ON);
|
||||
trig_fan = this->fan_only_action_trigger_;
|
||||
}
|
||||
this->cooling_max_runtime_exceeded_ = false;
|
||||
trig = this->cool_action_trigger_;
|
||||
ESP_LOGVV(TAG, "Switching to COOLING action");
|
||||
action_ready = true;
|
||||
@ -452,6 +461,7 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
|
||||
this->start_timer_(thermostat::TIMER_FANNING_ON);
|
||||
trig_fan = this->fan_only_action_trigger_;
|
||||
}
|
||||
this->heating_max_runtime_exceeded_ = false;
|
||||
trig = this->heat_action_trigger_;
|
||||
ESP_LOGVV(TAG, "Switching to HEATING action");
|
||||
action_ready = true;
|
||||
@ -752,15 +762,15 @@ bool ThermostatClimate::heating_action_ready_() {
|
||||
|
||||
void ThermostatClimate::start_timer_(const ThermostatClimateTimerIndex timer_index) {
|
||||
if (this->timer_duration_(timer_index) > 0) {
|
||||
this->set_timeout(this->timer_[timer_index].name, this->timer_duration_(timer_index),
|
||||
this->timer_cbf_(timer_index));
|
||||
this->timer_[timer_index].started = millis();
|
||||
this->timer_[timer_index].active = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ThermostatClimate::cancel_timer_(ThermostatClimateTimerIndex timer_index) {
|
||||
auto ret = this->timer_[timer_index].active;
|
||||
this->timer_[timer_index].active = false;
|
||||
return this->cancel_timeout(this->timer_[timer_index].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ThermostatClimate::timer_active_(ThermostatClimateTimerIndex timer_index) {
|
||||
@ -777,7 +787,6 @@ std::function<void()> ThermostatClimate::timer_cbf_(ThermostatClimateTimerIndex
|
||||
|
||||
void ThermostatClimate::cooling_max_run_time_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "cooling_max_run_time timer expired");
|
||||
this->timer_[thermostat::TIMER_COOLING_MAX_RUN_TIME].active = false;
|
||||
this->cooling_max_runtime_exceeded_ = true;
|
||||
this->trigger_supplemental_action_();
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
@ -785,21 +794,18 @@ void ThermostatClimate::cooling_max_run_time_timer_callback_() {
|
||||
|
||||
void ThermostatClimate::cooling_off_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "cooling_off timer expired");
|
||||
this->timer_[thermostat::TIMER_COOLING_OFF].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::cooling_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "cooling_on timer expired");
|
||||
this->timer_[thermostat::TIMER_COOLING_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::fan_mode_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fan_mode timer expired");
|
||||
this->timer_[thermostat::TIMER_FAN_MODE].active = false;
|
||||
this->switch_to_fan_mode_(this->fan_mode.value_or(climate::CLIMATE_FAN_ON));
|
||||
if (this->supports_fan_only_action_uses_fan_mode_timer_)
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
@ -807,19 +813,16 @@ void ThermostatClimate::fan_mode_timer_callback_() {
|
||||
|
||||
void ThermostatClimate::fanning_off_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fanning_off timer expired");
|
||||
this->timer_[thermostat::TIMER_FANNING_OFF].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::fanning_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fanning_on timer expired");
|
||||
this->timer_[thermostat::TIMER_FANNING_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::heating_max_run_time_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "heating_max_run_time timer expired");
|
||||
this->timer_[thermostat::TIMER_HEATING_MAX_RUN_TIME].active = false;
|
||||
this->heating_max_runtime_exceeded_ = true;
|
||||
this->trigger_supplemental_action_();
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
@ -827,21 +830,18 @@ void ThermostatClimate::heating_max_run_time_timer_callback_() {
|
||||
|
||||
void ThermostatClimate::heating_off_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "heating_off timer expired");
|
||||
this->timer_[thermostat::TIMER_HEATING_OFF].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::heating_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "heating_on timer expired");
|
||||
this->timer_[thermostat::TIMER_HEATING_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::idle_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "idle_on timer expired");
|
||||
this->timer_[thermostat::TIMER_IDLE_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@ -26,9 +28,9 @@ enum ThermostatClimateTimerIndex : size_t {
|
||||
|
||||
enum OnBootRestoreFrom : size_t { MEMORY = 0, DEFAULT_PRESET = 1 };
|
||||
struct ThermostatClimateTimer {
|
||||
const std::string name;
|
||||
bool active;
|
||||
uint32_t time;
|
||||
uint32_t started;
|
||||
std::function<void()> func;
|
||||
};
|
||||
|
||||
@ -59,6 +61,7 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||
ThermostatClimate();
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
void set_default_preset(const std::string &custom_preset);
|
||||
void set_default_preset(climate::ClimatePreset preset);
|
||||
@ -441,16 +444,17 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||
|
||||
/// Climate action timers
|
||||
std::vector<ThermostatClimateTimer> timer_{
|
||||
{"cool_run", false, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
|
||||
{"cool_off", false, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)},
|
||||
{"cool_on", false, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)},
|
||||
{"fan_mode", false, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)},
|
||||
{"fan_off", false, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)},
|
||||
{"fan_on", false, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)},
|
||||
{"heat_run", false, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)},
|
||||
{"heat_off", false, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
|
||||
{"heat_on", false, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
|
||||
{"idle_on", false, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)}};
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)},
|
||||
};
|
||||
|
||||
/// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)
|
||||
std::map<climate::ClimatePreset, ThermostatClimateTargetTempConfig> preset_config_{};
|
||||
|
@ -3,14 +3,18 @@ import esphome.codegen as cg
|
||||
|
||||
from esphome.components import display
|
||||
from esphome import automation
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ON_TOUCH,
|
||||
CONF_ON_RELEASE,
|
||||
CONF_ON_UPDATE,
|
||||
CONF_SWAP_XY,
|
||||
CONF_MIRROR_X,
|
||||
CONF_MIRROR_Y,
|
||||
CONF_SWAP_XY,
|
||||
CONF_TRANSFORM,
|
||||
CONF_CALIBRATION,
|
||||
)
|
||||
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@jesserockz", "@nielsnl68"]
|
||||
@ -30,10 +34,59 @@ TouchListener = touchscreen_ns.class_("TouchListener")
|
||||
CONF_DISPLAY = "display"
|
||||
CONF_TOUCHSCREEN_ID = "touchscreen_id"
|
||||
CONF_REPORT_INTERVAL = "report_interval" # not used yet:
|
||||
CONF_ON_UPDATE = "on_update"
|
||||
CONF_TOUCH_TIMEOUT = "touch_timeout"
|
||||
|
||||
|
||||
CONF_X_MIN = "x_min"
|
||||
CONF_X_MAX = "x_max"
|
||||
CONF_Y_MIN = "y_min"
|
||||
CONF_Y_MAX = "y_max"
|
||||
|
||||
|
||||
def validate_calibration(config):
|
||||
if CONF_CALIBRATION in config:
|
||||
calibration_config = config[CONF_CALIBRATION]
|
||||
if (
|
||||
cv.int_([CONF_X_MIN]) != 0
|
||||
and cv.int_(calibration_config[CONF_X_MAX]) != 0
|
||||
and abs(
|
||||
cv.int_(calibration_config[CONF_X_MIN])
|
||||
- cv.int_(calibration_config[CONF_X_MAX])
|
||||
)
|
||||
< 10
|
||||
):
|
||||
raise cv.Invalid("Calibration X values difference must be more than 10")
|
||||
|
||||
if (
|
||||
cv.int_(calibration_config[CONF_Y_MIN]) != 0
|
||||
and cv.int_(calibration_config[CONF_Y_MAX]) != 0
|
||||
and abs(
|
||||
cv.int_(calibration_config[CONF_Y_MIN])
|
||||
- cv.int_(calibration_config[CONF_Y_MAX])
|
||||
)
|
||||
< 10
|
||||
):
|
||||
raise cv.Invalid("Calibration Y values difference must be more than 10")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def calibration_schema(default_max_values):
|
||||
return cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_X_MIN, default=0): cv.int_range(min=0, max=4095),
|
||||
cv.Optional(CONF_X_MAX, default=default_max_values): cv.int_range(
|
||||
min=0, max=4095
|
||||
),
|
||||
cv.Optional(CONF_Y_MIN, default=0): cv.int_range(min=0, max=4095),
|
||||
cv.Optional(CONF_Y_MAX, default=default_max_values): cv.int_range(
|
||||
min=0, max=4095
|
||||
),
|
||||
},
|
||||
validate_calibration,
|
||||
)
|
||||
|
||||
|
||||
def touchscreen_schema(default_touch_timeout):
|
||||
return cv.Schema(
|
||||
{
|
||||
@ -49,6 +102,7 @@ def touchscreen_schema(default_touch_timeout):
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION): calibration_schema(0),
|
||||
cv.Optional(CONF_ON_TOUCH): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_ON_UPDATE): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True),
|
||||
@ -74,6 +128,17 @@ async def register_touchscreen(var, config):
|
||||
cg.add(var.set_mirror_x(transform[CONF_MIRROR_X]))
|
||||
cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y]))
|
||||
|
||||
if CONF_CALIBRATION in config:
|
||||
calibration_config = config[CONF_CALIBRATION]
|
||||
cg.add(
|
||||
var.set_calibration(
|
||||
calibration_config[CONF_X_MIN],
|
||||
calibration_config[CONF_X_MAX],
|
||||
calibration_config[CONF_Y_MIN],
|
||||
calibration_config[CONF_Y_MAX],
|
||||
)
|
||||
)
|
||||
|
||||
if CONF_ON_TOUCH in config:
|
||||
await automation.build_automation(
|
||||
var.get_touch_trigger(),
|
||||
|
@ -13,6 +13,15 @@ void Touchscreen::attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::Int
|
||||
irq_pin->attach_interrupt(TouchscreenInterrupt::gpio_intr, &this->store_, type);
|
||||
this->store_.init = true;
|
||||
this->store_.touched = false;
|
||||
ESP_LOGD(TAG, "Attach Touch Interupt");
|
||||
}
|
||||
|
||||
void Touchscreen::call_setup() {
|
||||
if (this->display_ != nullptr) {
|
||||
this->display_width_ = this->display_->get_native_width();
|
||||
this->display_height_ = this->display_->get_native_height();
|
||||
}
|
||||
PollingComponent::call_setup();
|
||||
}
|
||||
|
||||
void Touchscreen::update() {
|
||||
@ -20,19 +29,22 @@ void Touchscreen::update() {
|
||||
this->store_.touched = true;
|
||||
} else {
|
||||
// no need to poll if we have interrupts.
|
||||
ESP_LOGW(TAG, "Touch Polling Stopped. You can safely remove the 'update_interval:' variable from the YAML file.");
|
||||
this->stop_poller();
|
||||
}
|
||||
}
|
||||
|
||||
void Touchscreen::loop() {
|
||||
if (this->store_.touched) {
|
||||
ESP_LOGVV(TAG, "<< Do Touch loop >>");
|
||||
this->first_touch_ = this->touches_.empty();
|
||||
this->need_update_ = false;
|
||||
this->was_touched_ = this->is_touched_;
|
||||
this->is_touched_ = false;
|
||||
this->skip_update_ = false;
|
||||
for (auto &tp : this->touches_) {
|
||||
if (tp.second.state == STATE_PRESSED || tp.second.state == STATE_UPDATED) {
|
||||
tp.second.state = tp.second.state | STATE_RELEASING;
|
||||
tp.second.state |= STATE_RELEASING;
|
||||
} else {
|
||||
tp.second.state = STATE_RELEASED;
|
||||
}
|
||||
@ -42,7 +54,7 @@ void Touchscreen::loop() {
|
||||
this->update_touches();
|
||||
if (this->skip_update_) {
|
||||
for (auto &tp : this->touches_) {
|
||||
tp.second.state = tp.second.state & -STATE_RELEASING;
|
||||
tp.second.state &= ~STATE_RELEASING;
|
||||
}
|
||||
} else {
|
||||
this->store_.touched = false;
|
||||
@ -65,21 +77,25 @@ void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_r
|
||||
} else {
|
||||
tp = this->touches_[id];
|
||||
tp.state = STATE_UPDATED;
|
||||
tp.y_prev = tp.y;
|
||||
tp.x_prev = tp.x;
|
||||
}
|
||||
tp.x_raw = x_raw;
|
||||
tp.y_raw = y_raw;
|
||||
tp.z_raw = z_raw;
|
||||
if (this->x_raw_max_ != this->x_raw_min_ and this->y_raw_max_ != this->y_raw_min_) {
|
||||
x = this->normalize_(x_raw, this->x_raw_min_, this->x_raw_max_, this->invert_x_);
|
||||
y = this->normalize_(y_raw, this->y_raw_min_, this->y_raw_max_, this->invert_y_);
|
||||
|
||||
x = this->normalize_(x_raw, this->x_raw_min_, this->x_raw_max_, this->invert_x_);
|
||||
y = this->normalize_(y_raw, this->y_raw_min_, this->y_raw_max_, this->invert_y_);
|
||||
if (this->swap_x_y_) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
|
||||
if (this->swap_x_y_) {
|
||||
std::swap(x, y);
|
||||
tp.x = (uint16_t) ((int) x * this->display_width_ / 0x1000);
|
||||
tp.y = (uint16_t) ((int) y * this->display_height_ / 0x1000);
|
||||
} else {
|
||||
tp.state |= STATE_CALIBRATE;
|
||||
}
|
||||
|
||||
tp.x = (uint16_t) ((int) x * this->get_width_() / 0x1000);
|
||||
tp.y = (uint16_t) ((int) y * this->get_height_() / 0x1000);
|
||||
|
||||
if (tp.state == STATE_PRESSED) {
|
||||
tp.x_org = tp.x;
|
||||
tp.y_org = tp.y;
|
||||
@ -94,19 +110,30 @@ void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_r
|
||||
}
|
||||
|
||||
void Touchscreen::send_touches_() {
|
||||
TouchPoints_t touches;
|
||||
for (auto tp : this->touches_) {
|
||||
ESP_LOGV(TAG, "Touch status: %d/%d: raw:(%4d,%4d,%4d) calc:(%3d,%4d)", tp.second.id, tp.second.state,
|
||||
tp.second.x_raw, tp.second.y_raw, tp.second.z_raw, tp.second.x, tp.second.y);
|
||||
touches.push_back(tp.second);
|
||||
}
|
||||
if (this->need_update_ || (!this->is_touched_ && this->was_touched_)) {
|
||||
this->update_trigger_.trigger(touches);
|
||||
for (auto *listener : this->touch_listeners_) {
|
||||
listener->update(touches);
|
||||
}
|
||||
}
|
||||
if (!this->is_touched_) {
|
||||
if (this->touch_timeout_ > 0) {
|
||||
this->cancel_timeout(TAG);
|
||||
if (this->was_touched_) {
|
||||
if (this->touch_timeout_ > 0) {
|
||||
this->cancel_timeout(TAG);
|
||||
}
|
||||
this->release_trigger_.trigger();
|
||||
for (auto *listener : this->touch_listeners_)
|
||||
listener->release();
|
||||
this->touches_.clear();
|
||||
this->was_touched_ = false;
|
||||
}
|
||||
this->release_trigger_.trigger();
|
||||
for (auto *listener : this->touch_listeners_)
|
||||
listener->release();
|
||||
this->touches_.clear();
|
||||
} else {
|
||||
TouchPoints_t touches;
|
||||
for (auto tp : this->touches_) {
|
||||
touches.push_back(tp.second);
|
||||
}
|
||||
if (this->first_touch_) {
|
||||
TouchPoint tp = this->touches_.begin()->second;
|
||||
this->touch_trigger_.trigger(tp, touches);
|
||||
@ -114,12 +141,6 @@ void Touchscreen::send_touches_() {
|
||||
listener->touch(tp);
|
||||
}
|
||||
}
|
||||
if (this->need_update_) {
|
||||
this->update_trigger_.trigger(touches);
|
||||
for (auto *listener : this->touch_listeners_) {
|
||||
listener->update(touches);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ static const uint8_t STATE_RELEASED = 0x00;
|
||||
static const uint8_t STATE_PRESSED = 0x01;
|
||||
static const uint8_t STATE_UPDATED = 0x02;
|
||||
static const uint8_t STATE_RELEASING = 0x04;
|
||||
static const uint8_t STATE_CALIBRATE = 0x07;
|
||||
|
||||
struct TouchPoint {
|
||||
uint8_t id;
|
||||
@ -68,8 +69,6 @@ class Touchscreen : public PollingComponent {
|
||||
|
||||
void register_listener(TouchListener *listener) { this->touch_listeners_.push_back(listener); }
|
||||
|
||||
virtual void update_touches() = 0;
|
||||
|
||||
optional<TouchPoint> get_touch() { return this->touches_.begin()->second; }
|
||||
|
||||
TouchPoints_t get_touches() {
|
||||
@ -82,6 +81,7 @@ class Touchscreen : public PollingComponent {
|
||||
|
||||
void update() override;
|
||||
void loop() override;
|
||||
void call_setup() override;
|
||||
|
||||
protected:
|
||||
/// Call this function to send touch points to the `on_touch` listener and the binary_sensors.
|
||||
@ -90,17 +90,17 @@ class Touchscreen : public PollingComponent {
|
||||
|
||||
void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0);
|
||||
|
||||
virtual void update_touches() = 0;
|
||||
|
||||
void send_touches_();
|
||||
|
||||
int16_t normalize_(int16_t val, int16_t min_val, int16_t max_val, bool inverted = false);
|
||||
|
||||
uint16_t get_width_() { return this->display_->get_width(); }
|
||||
|
||||
uint16_t get_height_() { return this->display_->get_height(); }
|
||||
|
||||
display::Display *display_{nullptr};
|
||||
|
||||
int16_t x_raw_min_{0}, x_raw_max_{0}, y_raw_min_{0}, y_raw_max_{0};
|
||||
int16_t display_width_{0}, display_height_{0};
|
||||
|
||||
uint16_t touch_timeout_{0};
|
||||
bool invert_x_{false}, invert_y_{false}, swap_x_y_{false};
|
||||
|
||||
@ -115,6 +115,7 @@ class Touchscreen : public PollingComponent {
|
||||
bool first_touch_{true};
|
||||
bool need_update_{false};
|
||||
bool is_touched_{false};
|
||||
bool was_touched_{false};
|
||||
bool skip_update_{false};
|
||||
};
|
||||
|
||||
|
@ -20,7 +20,7 @@ CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TT21100Touchscreen),
|
||||
cv.Required(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x24))
|
||||
@ -32,8 +32,9 @@ async def to_code(config):
|
||||
await touchscreen.register_touchscreen(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
|
||||
cg.add(var.set_interrupt_pin(interrupt_pin))
|
||||
if CONF_INTERRUPT_PIN in config:
|
||||
interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
|
||||
cg.add(var.set_interrupt_pin(interrupt_pin))
|
||||
|
||||
if CONF_RESET_PIN in config:
|
||||
rts_pin = await cg.gpio_pin_expression(config[CONF_RESET_PIN])
|
||||
|
@ -50,10 +50,11 @@ void TT21100Touchscreen::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up TT21100 Touchscreen...");
|
||||
|
||||
// Register interrupt pin
|
||||
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||
this->interrupt_pin_->setup();
|
||||
|
||||
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||
this->interrupt_pin_->setup();
|
||||
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
|
||||
}
|
||||
|
||||
// Perform reset if necessary
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
@ -62,8 +63,14 @@ void TT21100Touchscreen::setup() {
|
||||
}
|
||||
|
||||
// Update display dimensions if they were updated during display setup
|
||||
this->x_raw_max_ = this->get_width_();
|
||||
this->y_raw_max_ = this->get_height_();
|
||||
if (this->display_ != nullptr) {
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_width();
|
||||
}
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_height();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger initial read to activate the interrupt
|
||||
this->store_.touched = true;
|
||||
|
@ -32,8 +32,12 @@ void WakeOnLanButton::press_action() {
|
||||
bool end_status = false;
|
||||
IPAddress broadcast = IPAddress(255, 255, 255, 255);
|
||||
#ifdef USE_ESP8266
|
||||
begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9,
|
||||
IPAddress((ip_addr_t) esphome::network::get_ip_address()), 128);
|
||||
for (auto ip : esphome::network::get_ip_addresses()) {
|
||||
if (ip.is_ip4()) {
|
||||
begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9, ip, 128);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ESP32
|
||||
begin_status = this->udp_client_.beginPacket(broadcast, 9);
|
||||
|
@ -45,6 +45,9 @@ WaveshareEPaper2P9InB = waveshare_epaper_ns.class_(
|
||||
WaveshareEPaper2P9InBV3 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InBV3", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P9InV2R2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InV2R2", WaveshareEPaper
|
||||
)
|
||||
GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper)
|
||||
WaveshareEPaper4P2In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper4P2In", WaveshareEPaper
|
||||
@ -107,6 +110,7 @@ MODELS = {
|
||||
"2.70inv2": ("b", WaveshareEPaper2P7InV2),
|
||||
"2.90in-b": ("b", WaveshareEPaper2P9InB),
|
||||
"2.90in-bv3": ("b", WaveshareEPaper2P9InBV3),
|
||||
"2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2),
|
||||
"4.20in": ("b", WaveshareEPaper4P2In),
|
||||
"4.20in-bv2": ("b", WaveshareEPaper4P2InBV2),
|
||||
"5.83in": ("b", WaveshareEPaper5P8In),
|
||||
|
@ -83,6 +83,33 @@ static const uint8_t PARTIAL_UPDATE_LUT_TTGO_B1[LUT_SIZE_TTGO_B1] = {
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
// clang-format off
|
||||
// Disable formatting to preserve the same look as in Waveshare examples
|
||||
static const uint8_t PARTIAL_UPD_2IN9_LUT_SIZE = 159;
|
||||
static const uint8_t PARTIAL_UPD_2IN9_LUT[PARTIAL_UPD_2IN9_LUT_SIZE] =
|
||||
{
|
||||
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00,
|
||||
0x22, 0x17, 0x41, 0xB0, 0x32, 0x36,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void WaveshareEPaperBase::setup_pins_() {
|
||||
this->init_internal_(this->get_buffer_length_());
|
||||
this->dc_pin_->setup(); // OUTPUT
|
||||
@ -1118,6 +1145,192 @@ void WaveshareEPaper2P9InBV3::dump_config() {
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// 2.90in v2 rev2
|
||||
// based on SDK and examples in ZIP file from:
|
||||
// https://www.waveshare.com/pico-epaper-2.9.htm
|
||||
// ========================================================
|
||||
|
||||
void WaveshareEPaper2P9InV2R2::initialize() {
|
||||
this->reset_();
|
||||
this->wait_until_idle_();
|
||||
|
||||
this->command(0x12); // SWRESET
|
||||
this->wait_until_idle_();
|
||||
|
||||
this->command(0x01);
|
||||
this->data(0x27);
|
||||
this->data(0x01);
|
||||
this->data(0x00);
|
||||
|
||||
this->command(0x11);
|
||||
this->data(0x03);
|
||||
|
||||
// SetWindows(0, 0, w, h)
|
||||
this->command(0x44);
|
||||
this->data(0x00);
|
||||
this->data(((this->get_width_controller() - 1) >> 3) & 0xFF);
|
||||
|
||||
this->command(0x45);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data((this->get_height_internal() - 1) & 0xFF);
|
||||
this->data(((this->get_height_internal() - 1) >> 8) & 0xFF);
|
||||
|
||||
this->command(0x21);
|
||||
this->data(0x00);
|
||||
this->data(0x80);
|
||||
|
||||
// SetCursor(0, 0)
|
||||
this->command(0x4E);
|
||||
this->data(0x00);
|
||||
this->command(0x4f);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
|
||||
this->wait_until_idle_();
|
||||
}
|
||||
|
||||
WaveshareEPaper2P9InV2R2::WaveshareEPaper2P9InV2R2() { this->reset_duration_ = 10; }
|
||||
|
||||
void WaveshareEPaper2P9InV2R2::reset_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(reset_duration_); // NOLINT
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(reset_duration_); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P9InV2R2::display() {
|
||||
if (!this->wait_until_idle_()) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGE(TAG, "fail idle 1");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->full_update_every_ == 1) {
|
||||
// do single full update
|
||||
this->command(0x24);
|
||||
this->start_data_();
|
||||
this->write_array(this->buffer_, this->get_buffer_length_());
|
||||
this->end_data_();
|
||||
|
||||
// TurnOnDisplay
|
||||
this->command(0x22);
|
||||
this->data(0xF7);
|
||||
this->command(0x20);
|
||||
return;
|
||||
}
|
||||
|
||||
// if (this->full_update_every_ == 1 ||
|
||||
if (this->at_update_ == 0) {
|
||||
// do base update
|
||||
this->command(0x24);
|
||||
this->start_data_();
|
||||
this->write_array(this->buffer_, this->get_buffer_length_());
|
||||
this->end_data_();
|
||||
|
||||
this->command(0x26);
|
||||
this->start_data_();
|
||||
this->write_array(this->buffer_, this->get_buffer_length_());
|
||||
this->end_data_();
|
||||
|
||||
// TurnOnDisplay
|
||||
this->command(0x22);
|
||||
this->data(0xF7);
|
||||
this->command(0x20);
|
||||
} else {
|
||||
// do partial update
|
||||
this->reset_();
|
||||
|
||||
this->write_lut_(PARTIAL_UPD_2IN9_LUT, PARTIAL_UPD_2IN9_LUT_SIZE);
|
||||
|
||||
this->command(0x37);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x40);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
|
||||
this->command(0x3C);
|
||||
this->data(0x80);
|
||||
|
||||
this->command(0x22);
|
||||
this->data(0xC0);
|
||||
this->command(0x20);
|
||||
|
||||
if (!this->wait_until_idle_()) {
|
||||
ESP_LOGE(TAG, "fail idle 2");
|
||||
}
|
||||
|
||||
// SetWindows(0, 0, w, h)
|
||||
this->command(0x44);
|
||||
this->data(0x00);
|
||||
this->data(((this->get_width_controller() - 1) >> 3) & 0xFF);
|
||||
|
||||
this->command(0x45);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data((this->get_height_internal() - 1) & 0xFF);
|
||||
this->data(((this->get_height_internal() - 1) >> 8) & 0xFF);
|
||||
|
||||
// SetCursor(0, 0)
|
||||
this->command(0x4E);
|
||||
this->data(0x00);
|
||||
this->command(0x4f);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
|
||||
// write b/w
|
||||
this->command(0x24);
|
||||
this->start_data_();
|
||||
this->write_array(this->buffer_, this->get_buffer_length_());
|
||||
this->end_data_();
|
||||
|
||||
// TurnOnDisplayPartial
|
||||
this->command(0x22);
|
||||
this->data(0x0F);
|
||||
this->command(0x20);
|
||||
}
|
||||
|
||||
this->at_update_ = (this->at_update_ + 1) % this->full_update_every_;
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P9InV2R2::write_lut_(const uint8_t *lut, const uint8_t size) {
|
||||
// COMMAND WRITE LUT REGISTER
|
||||
this->command(0x32);
|
||||
for (uint8_t i = 0; i < size; i++)
|
||||
this->data(lut[i]);
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P9InV2R2::dump_config() {
|
||||
LOG_DISPLAY("", "Waveshare E-Paper", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 2.9inV2R2");
|
||||
ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_);
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void WaveshareEPaper2P9InV2R2::deep_sleep() {
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
int WaveshareEPaper2P9InV2R2::get_width_internal() { return 128; }
|
||||
int WaveshareEPaper2P9InV2R2::get_height_internal() { return 296; }
|
||||
int WaveshareEPaper2P9InV2R2::get_width_controller() { return this->get_width_internal(); }
|
||||
void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every) {
|
||||
this->full_update_every_ = full_update_every;
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// Good Display 2.9in black/white/grey
|
||||
// Datasheet:
|
||||
|
@ -337,6 +337,36 @@ class WaveshareEPaper2P9InBV3 : public WaveshareEPaper {
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InV2R2 : public WaveshareEPaper {
|
||||
public:
|
||||
WaveshareEPaper2P9InV2R2();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override;
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
void write_lut_(const uint8_t *lut, uint8_t size);
|
||||
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
int get_width_controller() override;
|
||||
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
|
||||
private:
|
||||
void reset_();
|
||||
};
|
||||
|
||||
class WaveshareEPaper4P2In : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
@ -207,14 +207,13 @@ void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ =
|
||||
void WiFiComponent::set_btm(bool btm) { this->btm_ = btm; }
|
||||
void WiFiComponent::set_rrm(bool rrm) { this->rrm_ = rrm; }
|
||||
#endif
|
||||
|
||||
network::IPAddress WiFiComponent::get_ip_address() {
|
||||
network::IPAddresses WiFiComponent::get_ip_addresses() {
|
||||
if (this->has_sta())
|
||||
return this->wifi_sta_ip();
|
||||
return this->wifi_sta_ip_addresses();
|
||||
|
||||
#ifdef USE_WIFI_AP
|
||||
if (this->has_ap())
|
||||
return this->wifi_soft_ap_ip();
|
||||
return {this->wifi_soft_ap_ip()};
|
||||
#endif // USE_WIFI_AP
|
||||
|
||||
return {};
|
||||
@ -412,7 +411,11 @@ void WiFiComponent::print_connect_params_() {
|
||||
return;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str());
|
||||
ESP_LOGCONFIG(TAG, " IP Address: %s", wifi_sta_ip().str().c_str());
|
||||
for (auto &ip : wifi_sta_ip_addresses()) {
|
||||
if (ip.is_set()) {
|
||||
ESP_LOGCONFIG(TAG, " IP Address: %s", ip.str().c_str());
|
||||
}
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3],
|
||||
bssid[4], bssid[5]);
|
||||
ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str());
|
||||
|
@ -258,7 +258,7 @@ class WiFiComponent : public Component {
|
||||
#endif
|
||||
|
||||
network::IPAddress get_dns_address(int num);
|
||||
network::IPAddress get_ip_address();
|
||||
network::IPAddresses get_ip_addresses();
|
||||
std::string get_use_address() const;
|
||||
void set_use_address(const std::string &use_address);
|
||||
|
||||
@ -293,7 +293,7 @@ class WiFiComponent : public Component {
|
||||
});
|
||||
}
|
||||
|
||||
network::IPAddress wifi_sta_ip();
|
||||
network::IPAddresses wifi_sta_ip_addresses();
|
||||
std::string wifi_ssid();
|
||||
bssid_t wifi_bssid();
|
||||
|
||||
@ -396,6 +396,10 @@ class WiFiComponent : public Component {
|
||||
bool rrm_{false};
|
||||
#endif
|
||||
bool enable_on_boot_;
|
||||
bool got_ipv4_address_{false};
|
||||
#if USE_NETWORK_IPV6
|
||||
uint8_t num_ipv6_addresses_{0};
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
Trigger<> *connect_trigger_{new Trigger<>()};
|
||||
Trigger<> *disconnect_trigger_{new Trigger<>()};
|
||||
|
@ -131,7 +131,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
// TODO: is this needed?
|
||||
#if LWIP_IPV6
|
||||
dns.type = IPADDR_TYPE_V4;
|
||||
#endif
|
||||
#endif /* LWIP_IPV6 */
|
||||
if (manual_ip->dns1.is_set()) {
|
||||
dns = manual_ip->dns1;
|
||||
dns_setserver(0, &dns);
|
||||
@ -144,12 +144,36 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
return true;
|
||||
}
|
||||
|
||||
network::IPAddress WiFiComponent::wifi_sta_ip() {
|
||||
network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
|
||||
if (!this->has_sta())
|
||||
return {};
|
||||
network::IPAddresses addresses;
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
|
||||
return network::IPAddress(&ip.ip);
|
||||
esp_err_t err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
|
||||
// TODO: do something smarter
|
||||
// return false;
|
||||
} else {
|
||||
addresses[0] = network::IPAddress(&ip.ip);
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
ip6_addr_t ipv6;
|
||||
err = tcpip_adapter_get_ip6_global(TCPIP_ADAPTER_IF_STA, &ipv6);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_netif_get_ip6_gobal failed: %s", esp_err_to_name(err));
|
||||
} else {
|
||||
addresses[1] = network::IPAddress(&ipv6);
|
||||
}
|
||||
err = tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &ipv6);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_netif_get_ip6_linklocal failed: %s", esp_err_to_name(err));
|
||||
} else {
|
||||
addresses[2] = network::IPAddress(&ipv6);
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_apply_hostname_() {
|
||||
@ -440,9 +464,9 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_
|
||||
buf[it.ssid_len] = '\0';
|
||||
ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf,
|
||||
format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode));
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
this->set_timeout(100, [] { WiFi.enableIpV6(); });
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
break;
|
||||
}
|
||||
@ -494,18 +518,26 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_
|
||||
auto it = info.got_ip.ip_info;
|
||||
ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(),
|
||||
format_ip4_addr(it.gw).c_str());
|
||||
this->got_ipv4_address_ = true;
|
||||
#if USE_NETWORK_IPV6
|
||||
s_sta_connecting = this->num_ipv6_addresses_ < USE_NETWORK_MIN_IPV6_ADDR_COUNT;
|
||||
#else
|
||||
s_sta_connecting = false;
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
break;
|
||||
}
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: {
|
||||
auto it = info.got_ip6.ip6_info;
|
||||
ESP_LOGV(TAG, "Got IPv6 address=" IPV6STR, IPV62STR(it.ip));
|
||||
this->num_ipv6_addresses_++;
|
||||
s_sta_connecting = !(this->got_ipv4_address_ & (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT));
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: {
|
||||
ESP_LOGV(TAG, "Event: Lost IP");
|
||||
this->got_ipv4_address_ = false;
|
||||
break;
|
||||
}
|
||||
case ESPHOME_EVENT_ID_WIFI_AP_START: {
|
||||
|
@ -17,10 +17,8 @@ extern "C" {
|
||||
#include "lwip/dhcp.h"
|
||||
#include "lwip/init.h" // LWIP_VERSION_
|
||||
#include "lwip/apps/sntp.h"
|
||||
#if LWIP_IPV6
|
||||
#include "lwip/netif.h" // struct netif
|
||||
#include <AddrList.h>
|
||||
#endif
|
||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0)
|
||||
#include "LwipDhcpServer.h"
|
||||
#define wifi_softap_set_dhcps_lease(lease) dhcpSoftAP.set_dhcps_lease(lease)
|
||||
@ -185,12 +183,15 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
network::IPAddress WiFiComponent::wifi_sta_ip() {
|
||||
network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
|
||||
if (!this->has_sta())
|
||||
return {};
|
||||
struct ip_info ip {};
|
||||
wifi_get_ip_info(STATION_IF, &ip);
|
||||
return network::IPAddress(&ip.ip);
|
||||
network::IPAddresses addresses;
|
||||
uint8_t index = 0;
|
||||
for (auto &addr : addrList) {
|
||||
addresses[index++] = addr.ipFromNetifNum();
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
bool WiFiComponent::wifi_apply_hostname_() {
|
||||
const std::string &hostname = App.get_name();
|
||||
@ -327,17 +328,20 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ENABLE_IPV6
|
||||
for (bool configured = false; !configured;) {
|
||||
#if USE_NETWORK_IPV6
|
||||
bool connected = false;
|
||||
while (!connected) {
|
||||
uint8_t ipv6_addr_count = 0;
|
||||
for (auto addr : addrList) {
|
||||
ESP_LOGV(TAG, "Address %s", addr.toString().c_str());
|
||||
if ((configured = !addr.isLocal() && addr.isV6())) {
|
||||
break;
|
||||
if (addr.isV6()) {
|
||||
ipv6_addr_count++;
|
||||
}
|
||||
}
|
||||
delay(500); // NOLINT
|
||||
connected = (ipv6_addr_count >= USE_NETWORK_MIN_IPV6_ADDR_COUNT);
|
||||
}
|
||||
#endif
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
if (ap.get_channel().has_value()) {
|
||||
ret = wifi_set_channel(*ap.get_channel());
|
||||
|
@ -39,14 +39,11 @@ static const char *const TAG = "wifi_esp32";
|
||||
static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static QueueHandle_t s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
#ifdef USE_WIFI_AP
|
||||
static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
#endif // USE_WIFI_AP
|
||||
|
||||
static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
#endif // USE_WIFI_AP
|
||||
static bool s_sta_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_ap_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@ -66,9 +63,9 @@ struct IDFWiFiEvent {
|
||||
wifi_event_ap_probe_req_rx_t ap_probe_req_rx;
|
||||
wifi_event_bss_rssi_low_t bss_rssi_low;
|
||||
ip_event_got_ip_t ip_got_ip;
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
ip_event_got_ip6_t ip_got_ip6;
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
ip_event_ap_staipassigned_t ip_ap_staipassigned;
|
||||
} data;
|
||||
};
|
||||
@ -92,7 +89,7 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi
|
||||
memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t));
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t));
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) {
|
||||
memcpy(&event.data.ip_got_ip6, event_data, sizeof(ip_event_got_ip6_t));
|
||||
#endif
|
||||
@ -411,7 +408,6 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
|
||||
// may be called from wifi_station_connect
|
||||
s_sta_connecting = true;
|
||||
s_sta_connected = false;
|
||||
s_sta_got_ip = false;
|
||||
s_sta_connect_error = false;
|
||||
s_sta_connect_not_found = false;
|
||||
|
||||
@ -476,17 +472,29 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
return true;
|
||||
}
|
||||
|
||||
network::IPAddress WiFiComponent::wifi_sta_ip() {
|
||||
network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
|
||||
if (!this->has_sta())
|
||||
return {};
|
||||
network::IPAddresses addresses;
|
||||
esp_netif_ip_info_t ip;
|
||||
esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
|
||||
// TODO: do something smarter
|
||||
// return false;
|
||||
} else {
|
||||
addresses[0] = network::IPAddress(&ip.ip);
|
||||
}
|
||||
return network::IPAddress(&ip.ip);
|
||||
#if USE_NETWORK_IPV6
|
||||
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
||||
uint8_t count = 0;
|
||||
count = esp_netif_get_all_ip6(s_sta_netif, if_ip6s);
|
||||
assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
|
||||
for (int i = 0; i < count; i++) {
|
||||
addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
|
||||
}
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
return addresses;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_apply_hostname_() {
|
||||
@ -521,7 +529,7 @@ const char *get_auth_mode_str(uint8_t mode) {
|
||||
std::string format_ip4_addr(const esp_ip4_addr_t &ip) { return str_snprintf(IPSTR, 15, IP2STR(&ip)); }
|
||||
#if LWIP_IPV6
|
||||
std::string format_ip6_addr(const esp_ip6_addr_t &ip) { return str_snprintf(IPV6STR, 39, IPV62STR(ip)); }
|
||||
#endif
|
||||
#endif /* LWIP_IPV6 */
|
||||
const char *get_disconnect_reason_str(uint8_t reason) {
|
||||
switch (reason) {
|
||||
case WIFI_REASON_AUTH_EXPIRE:
|
||||
@ -658,22 +666,23 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
|
||||
} else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) {
|
||||
const auto &it = data->data.ip_got_ip;
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
esp_netif_create_ip6_linklocal(s_sta_netif);
|
||||
#endif /* ENABLE_IPV6 */
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(),
|
||||
format_ip4_addr(it.ip_info.gw).c_str());
|
||||
s_sta_got_ip = true;
|
||||
this->got_ipv4_address_ = true;
|
||||
|
||||
#if ENABLE_IPV6
|
||||
#if USE_NETWORK_IPV6
|
||||
} else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_GOT_IP6) {
|
||||
const auto &it = data->data.ip_got_ip6;
|
||||
ESP_LOGV(TAG, "Event: Got IPv6 address=%s", format_ip6_addr(it.ip6_info.ip).c_str());
|
||||
#endif /* ENABLE_IPV6 */
|
||||
this->num_ipv6_addresses_++;
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
} else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) {
|
||||
ESP_LOGV(TAG, "Event: Lost IP");
|
||||
s_sta_got_ip = false;
|
||||
this->got_ipv4_address_ = false;
|
||||
|
||||
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) {
|
||||
const auto &it = data->data.sta_scan_done;
|
||||
@ -737,8 +746,14 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
}
|
||||
|
||||
WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
||||
if (s_sta_connected && s_sta_got_ip) {
|
||||
if (s_sta_connected && this->got_ipv4_address_) {
|
||||
#if USE_NETWORK_IPV6
|
||||
if (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT) {
|
||||
return WiFiSTAConnectStatus::CONNECTED;
|
||||
}
|
||||
#else
|
||||
return WiFiSTAConnectStatus::CONNECTED;
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
if (s_sta_connect_error) {
|
||||
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
|
||||
|
@ -81,7 +81,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
return true;
|
||||
}
|
||||
|
||||
network::IPAddress WiFiComponent::wifi_sta_ip() {
|
||||
network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
|
||||
if (!this->has_sta())
|
||||
return {};
|
||||
return {WiFi.localIP()};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netif.h"
|
||||
#include <AddrList.h>
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
@ -173,7 +174,14 @@ std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); }
|
||||
int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); }
|
||||
int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); }
|
||||
|
||||
network::IPAddress WiFiComponent::wifi_sta_ip() { return {(const ip_addr_t *) WiFi.localIP()}; }
|
||||
network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() {
|
||||
network::IPAddresses addresses;
|
||||
uint8_t index = 0;
|
||||
for (auto addr : addrList) {
|
||||
addresses[index++] = addr.ipFromNetifNum();
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; }
|
||||
network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; }
|
||||
network::IPAddress WiFiComponent::wifi_dns_ip_(int num) {
|
||||
|
@ -37,7 +37,16 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema(
|
||||
IPAddressWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
).extend(cv.polling_component_schema("1s")),
|
||||
)
|
||||
.extend(cv.polling_component_schema("1s"))
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(f"address_{x}"): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
for x in range(5)
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_SCAN_RESULTS): text_sensor.text_sensor_schema(
|
||||
ScanResultsWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
).extend(cv.polling_component_schema("60s")),
|
||||
@ -65,9 +74,15 @@ async def setup_conf(config, key):
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
await setup_conf(config, CONF_IP_ADDRESS)
|
||||
await setup_conf(config, CONF_SSID)
|
||||
await setup_conf(config, CONF_BSSID)
|
||||
await setup_conf(config, CONF_MAC_ADDRESS)
|
||||
await setup_conf(config, CONF_SCAN_RESULTS)
|
||||
await setup_conf(config, CONF_DNS_ADDRESS)
|
||||
if conf := config.get(CONF_IP_ADDRESS):
|
||||
wifi_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS])
|
||||
await cg.register_component(wifi_info, config[CONF_IP_ADDRESS])
|
||||
for x in range(5):
|
||||
if sensor_conf := conf.get(f"address_{x}"):
|
||||
sens = await text_sensor.new_text_sensor(sensor_conf)
|
||||
cg.add(wifi_info.add_ip_sensors(x, sens))
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#include <array>
|
||||
|
||||
namespace esphome {
|
||||
namespace wifi_info {
|
||||
@ -10,18 +11,29 @@ namespace wifi_info {
|
||||
class IPAddressWiFiInfo : public PollingComponent, public text_sensor::TextSensor {
|
||||
public:
|
||||
void update() override {
|
||||
auto ip = wifi::global_wifi_component->wifi_sta_ip();
|
||||
if (ip != this->last_ip_) {
|
||||
this->last_ip_ = ip;
|
||||
this->publish_state(ip.str());
|
||||
auto ips = wifi::global_wifi_component->wifi_sta_ip_addresses();
|
||||
if (ips != this->last_ips_) {
|
||||
this->last_ips_ = ips;
|
||||
this->publish_state(ips[0].str());
|
||||
uint8_t sensor = 0;
|
||||
for (auto &ip : ips) {
|
||||
if (ip.is_set()) {
|
||||
if (this->ip_sensors_[sensor] != nullptr) {
|
||||
this->ip_sensors_[sensor]->publish_state(ip.str());
|
||||
}
|
||||
sensor++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||
std::string unique_id() override { return get_mac_address() + "-wifiinfo-ip"; }
|
||||
void dump_config() override;
|
||||
void add_ip_sensors(uint8_t index, text_sensor::TextSensor *s) { this->ip_sensors_[index] = s; }
|
||||
|
||||
protected:
|
||||
network::IPAddress last_ip_;
|
||||
network::IPAddresses last_ips_;
|
||||
std::array<text_sensor::TextSensor *, 5> ip_sensors_;
|
||||
};
|
||||
|
||||
class DNSAddressWifiInfo : public PollingComponent, public text_sensor::TextSensor {
|
||||
|
@ -15,35 +15,11 @@ XPT2046Component = XPT2046_ns.class_(
|
||||
spi.SPIDevice,
|
||||
)
|
||||
|
||||
|
||||
CONF_CALIBRATION_X_MIN = "calibration_x_min"
|
||||
CONF_CALIBRATION_X_MAX = "calibration_x_max"
|
||||
CONF_CALIBRATION_Y_MIN = "calibration_y_min"
|
||||
CONF_CALIBRATION_Y_MAX = "calibration_y_max"
|
||||
|
||||
|
||||
def validate_xpt2046(config):
|
||||
if (
|
||||
abs(
|
||||
cv.int_(config[CONF_CALIBRATION_X_MAX])
|
||||
- cv.int_(config[CONF_CALIBRATION_X_MIN])
|
||||
)
|
||||
< 1000
|
||||
):
|
||||
raise cv.Invalid("Calibration X values difference < 1000")
|
||||
|
||||
if (
|
||||
abs(
|
||||
cv.int_(config[CONF_CALIBRATION_Y_MAX])
|
||||
- cv.int_(config[CONF_CALIBRATION_Y_MIN])
|
||||
)
|
||||
< 1000
|
||||
):
|
||||
raise cv.Invalid("Calibration Y values difference < 1000")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
touchscreen.TOUCHSCREEN_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
@ -52,42 +28,41 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_INTERRUPT_PIN): cv.All(
|
||||
pins.internal_gpio_input_pin_schema
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_X_MIN, default=0): cv.int_range(
|
||||
min=0, max=4095
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_X_MAX, default=4095): cv.int_range(
|
||||
min=0, max=4095
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_Y_MIN, default=0): cv.int_range(
|
||||
min=0, max=4095
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_Y_MAX, default=4095): cv.int_range(
|
||||
min=0, max=4095
|
||||
),
|
||||
cv.Optional(CONF_THRESHOLD, default=400): cv.int_range(min=0, max=4095),
|
||||
cv.Optional(
|
||||
touchscreen.CONF_CALIBRATION
|
||||
): touchscreen.calibration_schema(4095),
|
||||
cv.Optional(CONF_CALIBRATION_X_MIN): cv.invalid(
|
||||
"Deprecated: use the new 'calibration' configuration variable"
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_X_MAX): cv.invalid(
|
||||
"Deprecated: use the new 'calibration' configuration variable"
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_Y_MIN): cv.invalid(
|
||||
"Deprecated: use the new 'calibration' configuration variable"
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_Y_MAX): cv.invalid(
|
||||
"Deprecated: use the new 'calibration' configuration variable"
|
||||
),
|
||||
cv.Optional(CONF_CALIBRATION_Y_MAX): cv.invalid(
|
||||
"Deprecated: use the new 'calibration' configuration variable"
|
||||
),
|
||||
cv.Optional("report_interval"): cv.invalid(
|
||||
"Deprecated: use the 'update_interval' configuration variable"
|
||||
),
|
||||
},
|
||||
)
|
||||
).extend(spi.spi_device_schema()),
|
||||
validate_xpt2046,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await touchscreen.register_touchscreen(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
await touchscreen.register_touchscreen(var, config)
|
||||
|
||||
cg.add(var.set_threshold(config[CONF_THRESHOLD]))
|
||||
|
||||
cg.add(
|
||||
var.set_calibration(
|
||||
config[CONF_CALIBRATION_X_MIN],
|
||||
config[CONF_CALIBRATION_X_MAX],
|
||||
config[CONF_CALIBRATION_Y_MIN],
|
||||
config[CONF_CALIBRATION_Y_MAX],
|
||||
)
|
||||
)
|
||||
|
||||
if CONF_INTERRUPT_PIN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
|
||||
cg.add(var.set_irq_pin(pin))
|
||||
|
@ -32,9 +32,8 @@ void XPT2046Component::update_touches() {
|
||||
|
||||
int16_t touch_pressure_1 = this->read_adc_(0xB1 /* touch_pressure_1 */);
|
||||
int16_t touch_pressure_2 = this->read_adc_(0xC1 /* touch_pressure_2 */);
|
||||
ESP_LOGVV(TAG, "touch_pressure %d, %d", touch_pressure_1, touch_pressure_2);
|
||||
z_raw = touch_pressure_1 + 0Xfff - touch_pressure_2;
|
||||
|
||||
ESP_LOGVV(TAG, "Touchscreen Update z = %d", z_raw);
|
||||
touch = (z_raw >= this->threshold_);
|
||||
if (touch) {
|
||||
read_adc_(0xD1 /* X */); // dummy Y measure, 1st is always noisy
|
||||
@ -53,7 +52,7 @@ void XPT2046Component::update_touches() {
|
||||
x_raw = best_two_avg(data[1], data[3], data[5]);
|
||||
y_raw = best_two_avg(data[0], data[2], data[4]);
|
||||
|
||||
ESP_LOGV(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw);
|
||||
ESP_LOGD(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw);
|
||||
|
||||
this->add_raw_touch_position_(0, x_raw, y_raw, z_raw);
|
||||
}
|
||||
@ -77,7 +76,7 @@ void XPT2046Component::dump_config() {
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
float XPT2046Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
// float XPT2046Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
int16_t XPT2046Component::best_two_avg(int16_t value1, int16_t value2, int16_t value3) {
|
||||
int16_t delta_a, delta_b, delta_c;
|
||||
|
@ -23,7 +23,7 @@ class XPT2046Component : public Touchscreen,
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
// float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
static int16_t best_two_avg(int16_t value1, int16_t value2, int16_t value3);
|
||||
|
@ -449,6 +449,7 @@ CONF_MIN_FANNING_RUN_TIME = "min_fanning_run_time"
|
||||
CONF_MIN_HEATING_OFF_TIME = "min_heating_off_time"
|
||||
CONF_MIN_HEATING_RUN_TIME = "min_heating_run_time"
|
||||
CONF_MIN_IDLE_TIME = "min_idle_time"
|
||||
CONF_MIN_IPV6_ADDR_COUNT = "min_ipv6_addr_count"
|
||||
CONF_MIN_LENGTH = "min_length"
|
||||
CONF_MIN_LEVEL = "min_level"
|
||||
CONF_MIN_POWER = "min_power"
|
||||
@ -536,6 +537,7 @@ CONF_ON_TOUCH = "on_touch"
|
||||
CONF_ON_TURN_OFF = "on_turn_off"
|
||||
CONF_ON_TURN_ON = "on_turn_on"
|
||||
CONF_ON_UNLOCK = "on_unlock"
|
||||
CONF_ON_UPDATE = "on_update"
|
||||
CONF_ON_VALUE = "on_value"
|
||||
CONF_ON_VALUE_RANGE = "on_value_range"
|
||||
CONF_ONE = "one"
|
||||
@ -730,6 +732,7 @@ CONF_SPEED_COUNT = "speed_count"
|
||||
CONF_SPEED_LEVEL_COMMAND_TOPIC = "speed_level_command_topic"
|
||||
CONF_SPEED_LEVEL_STATE_TOPIC = "speed_level_state_topic"
|
||||
CONF_SPEED_STATE_TOPIC = "speed_state_topic"
|
||||
CONF_SPI = "spi"
|
||||
CONF_SPI_ID = "spi_id"
|
||||
CONF_SPIKE_REJECTION = "spike_rejection"
|
||||
CONF_SSID = "ssid"
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -125,6 +127,27 @@ class LoopTrigger : public Trigger<>, public Component {
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
};
|
||||
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
class ProjectUpdateTrigger : public Trigger<std::string>, public Component {
|
||||
public:
|
||||
void setup() override {
|
||||
uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME);
|
||||
ESPPreferenceObject pref = global_preferences->make_preference<char[30]>(hash, true);
|
||||
char previous_version[30];
|
||||
char current_version[30] = ESPHOME_PROJECT_VERSION;
|
||||
if (pref.load(&previous_version)) {
|
||||
int cmp = strcmp(previous_version, current_version);
|
||||
if (cmp < 0) {
|
||||
this->trigger(previous_version);
|
||||
}
|
||||
}
|
||||
pref.save(¤t_version);
|
||||
global_preferences->sync();
|
||||
}
|
||||
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename... Ts> class DelayAction : public Action<Ts...>, public Component {
|
||||
public:
|
||||
explicit DelayAction() = default;
|
||||
|
@ -24,6 +24,7 @@ from esphome.const import (
|
||||
CONF_ON_BOOT,
|
||||
CONF_ON_LOOP,
|
||||
CONF_ON_SHUTDOWN,
|
||||
CONF_ON_UPDATE,
|
||||
CONF_PLATFORM,
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
CONF_PRIORITY,
|
||||
@ -38,7 +39,7 @@ from esphome.const import (
|
||||
__version__ as ESPHOME_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.helpers import copy_file_if_changed, walk_files
|
||||
from esphome.helpers import copy_file_if_changed, get_str_env, walk_files
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -52,6 +53,9 @@ ShutdownTrigger = cg.esphome_ns.class_(
|
||||
LoopTrigger = cg.esphome_ns.class_(
|
||||
"LoopTrigger", cg.Component, automation.Trigger.template()
|
||||
)
|
||||
ProjectUpdateTrigger = cg.esphome_ns.class_(
|
||||
"ProjectUpdateTrigger", cg.Component, automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$")
|
||||
|
||||
@ -151,6 +155,13 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.string_strict, valid_project_name
|
||||
),
|
||||
cv.Required(CONF_VERSION): cv.string_strict,
|
||||
cv.Optional(CONF_ON_UPDATE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
ProjectUpdateTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All(
|
||||
@ -190,7 +201,8 @@ def preload_core_config(config, result):
|
||||
CORE.data[KEY_CORE] = {}
|
||||
|
||||
if CONF_BUILD_PATH not in conf:
|
||||
conf[CONF_BUILD_PATH] = f"build/{CORE.name}"
|
||||
build_path = get_str_env("ESPHOME_BUILD_PATH", "build")
|
||||
conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name)
|
||||
CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH])
|
||||
|
||||
has_oldstyle = CONF_PLATFORM in conf
|
||||
@ -379,9 +391,15 @@ async def to_code(config):
|
||||
if config[CONF_INCLUDES]:
|
||||
CORE.add_job(add_includes, config[CONF_INCLUDES])
|
||||
|
||||
if CONF_PROJECT in config:
|
||||
cg.add_define("ESPHOME_PROJECT_NAME", config[CONF_PROJECT][CONF_NAME])
|
||||
cg.add_define("ESPHOME_PROJECT_VERSION", config[CONF_PROJECT][CONF_VERSION])
|
||||
if project_conf := config.get(CONF_PROJECT):
|
||||
cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME])
|
||||
cg.add_define("ESPHOME_PROJECT_VERSION", project_conf[CONF_VERSION])
|
||||
for conf in project_conf.get(CONF_ON_UPDATE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
await cg.register_component(trigger, conf)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_string, "version")], conf
|
||||
)
|
||||
|
||||
if config[CONF_PLATFORMIO_OPTIONS]:
|
||||
CORE.add_job(_add_platformio_options, config[CONF_PLATFORMIO_OPTIONS])
|
||||
|
@ -117,7 +117,7 @@ lib_deps =
|
||||
WiFi ; wifi,web_server_base,ethernet (Arduino built-in)
|
||||
Update ; ota,web_server_base (Arduino built-in)
|
||||
${common:arduino.lib_deps}
|
||||
esphome/AsyncTCP-esphome@1.2.2 ; async_tcp
|
||||
esphome/AsyncTCP-esphome@2.1.3 ; async_tcp
|
||||
WiFiClientSecure ; http_request,nextion (Arduino built-in)
|
||||
HTTPClient ; http_request,nextion (Arduino built-in)
|
||||
ESPmDNS ; mdns (Arduino built-in)
|
||||
|
@ -13,7 +13,7 @@ platformio==6.1.13 # When updating platformio, also update Dockerfile
|
||||
esptool==4.7.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20231107.0
|
||||
aioesphomeapi==22.0.0
|
||||
aioesphomeapi==23.0.0
|
||||
zeroconf==0.131.0
|
||||
python-magic==0.4.27
|
||||
|
||||
|
@ -5,7 +5,7 @@ pyupgrade==3.15.1 # also change in .pre-commit-config.yaml when updating
|
||||
pre-commit
|
||||
|
||||
# Unit tests
|
||||
pytest==8.0.1
|
||||
pytest==8.0.2
|
||||
pytest-cov==4.1.0
|
||||
pytest-mock==3.12.0
|
||||
pytest-asyncio==0.23.5
|
||||
|
@ -28,7 +28,12 @@ start_esphome() {
|
||||
component_test_file="./tests/test_build_components/build/$target_component.$test_name.$target_platform.yaml"
|
||||
|
||||
cp $target_platform_file $component_test_file
|
||||
sed -i "s!\$component_test_file!../../.$f!g" $component_test_file
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS sed is...different
|
||||
sed -i '' "s!\$component_test_file!../../.$f!g" $component_test_file
|
||||
else
|
||||
sed -i "s!\$component_test_file!../../.$f!g" $component_test_file
|
||||
fi
|
||||
|
||||
# Start esphome process
|
||||
echo "> [$target_component] [$test_name] [$target_platform]"
|
||||
|
58
tests/component_tests/text_sensor/test_text_sensor.py
Normal file
58
tests/component_tests/text_sensor/test_text_sensor.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""Tests for the text sensor component."""
|
||||
|
||||
|
||||
def test_text_sensor_is_setup(generate_main):
|
||||
"""
|
||||
When the text is set in the yaml file, it should be registered in main
|
||||
"""
|
||||
# Given
|
||||
|
||||
# When
|
||||
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
|
||||
|
||||
# Then
|
||||
assert "new template_::TemplateTextSensor();" in main_cpp
|
||||
assert "App.register_text_sensor" in main_cpp
|
||||
|
||||
|
||||
def test_text_sensor_sets_mandatory_fields(generate_main):
|
||||
"""
|
||||
When the mandatory fields are set in the yaml, they should be set in main
|
||||
"""
|
||||
# Given
|
||||
|
||||
# When
|
||||
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
|
||||
|
||||
# Then
|
||||
assert 'ts_1->set_name("Template Text Sensor 1");' in main_cpp
|
||||
assert 'ts_2->set_name("Template Text Sensor 2");' in main_cpp
|
||||
assert 'ts_3->set_name("Template Text Sensor 3");' in main_cpp
|
||||
|
||||
|
||||
def test_text_sensor_config_value_internal_set(generate_main):
|
||||
"""
|
||||
Test that the "internal" config value is correctly set
|
||||
"""
|
||||
# Given
|
||||
|
||||
# When
|
||||
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
|
||||
|
||||
# Then
|
||||
assert "ts_2->set_internal(true);" in main_cpp
|
||||
assert "ts_3->set_internal(false);" in main_cpp
|
||||
|
||||
|
||||
def test_text_sensor_device_class_set(generate_main):
|
||||
"""
|
||||
When the device_class of text_sensor is set in the yaml file, it should be registered in main
|
||||
"""
|
||||
# Given
|
||||
|
||||
# When
|
||||
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
|
||||
|
||||
# Then
|
||||
assert 'ts_2->set_device_class("timestamp");' in main_cpp
|
||||
assert 'ts_3->set_device_class("date");' in main_cpp
|
26
tests/component_tests/text_sensor/test_text_sensor.yaml
Normal file
26
tests/component_tests/text_sensor/test_text_sensor.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
esphome:
|
||||
name: test
|
||||
platform: ESP8266
|
||||
board: d1_mini_lite
|
||||
|
||||
text_sensor:
|
||||
- platform: template
|
||||
id: ts_1
|
||||
name: "Template Text Sensor 1"
|
||||
lambda: |-
|
||||
return {"Hello World"};
|
||||
- platform: template
|
||||
id: ts_2
|
||||
name: "Template Text Sensor 2"
|
||||
lambda: |-
|
||||
return {"2023-06-22T18:43:52+00:00"};
|
||||
device_class: timestamp
|
||||
internal: true
|
||||
- platform: template
|
||||
id: ts_3
|
||||
name: "Template Text Sensor 3"
|
||||
lambda: |-
|
||||
return {"2023-06-22T18:43:52+00:00"};
|
||||
device_class: date
|
||||
internal: false
|
12
tests/components/ethernet/lan8720.esp32-idf.yaml
Normal file
12
tests/components/ethernet/lan8720.esp32-idf.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
ethernet:
|
||||
type: LAN8720
|
||||
mdc_pin: 23
|
||||
mdio_pin: 25
|
||||
clk_mode: GPIO0_IN
|
||||
phy_addr: 0
|
||||
power_pin: 26
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
12
tests/components/ethernet/lan8720.esp32.yaml
Normal file
12
tests/components/ethernet/lan8720.esp32.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
ethernet:
|
||||
type: LAN8720
|
||||
mdc_pin: 23
|
||||
mdio_pin: 25
|
||||
clk_mode: GPIO0_IN
|
||||
phy_addr: 0
|
||||
power_pin: 26
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
14
tests/components/ethernet/w5500.esp32-idf.yaml
Normal file
14
tests/components/ethernet/w5500.esp32-idf.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
ethernet:
|
||||
type: W5500
|
||||
clk_pin: GPIO19
|
||||
mosi_pin: GPIO21
|
||||
miso_pin: GPIO23
|
||||
cs_pin: GPIO18
|
||||
interrupt_pin: GPIO36
|
||||
reset_pin: GPIO22
|
||||
clock_speed: 10Mhz
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
14
tests/components/ethernet/w5500.esp32.yaml
Normal file
14
tests/components/ethernet/w5500.esp32.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
ethernet:
|
||||
type: W5500
|
||||
clk_pin: GPIO19
|
||||
mosi_pin: GPIO21
|
||||
miso_pin: GPIO23
|
||||
cs_pin: GPIO18
|
||||
interrupt_pin: GPIO36
|
||||
reset_pin: GPIO22
|
||||
clock_speed: 10Mhz
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
38
tests/components/ft63x6/test.esp32.yaml
Normal file
38
tests/components/ft63x6/test.esp32.yaml
Normal file
@ -0,0 +1,38 @@
|
||||
spi:
|
||||
clk_pin: 14
|
||||
mosi_pin: 13
|
||||
|
||||
i2c:
|
||||
sda: GPIO18
|
||||
scl: GPIO19
|
||||
|
||||
display:
|
||||
- id: my_display
|
||||
platform: ili9xxx
|
||||
dimensions: 480x320
|
||||
model: ST7796
|
||||
cs_pin: 15
|
||||
dc_pin: 21
|
||||
reset_pin: 22
|
||||
transform:
|
||||
swap_xy: true
|
||||
mirror_x: true
|
||||
mirror_y: true
|
||||
auto_clear_enabled: false
|
||||
|
||||
touchscreen:
|
||||
- platform: ft63x6
|
||||
interrupt_pin: GPIO39
|
||||
transform:
|
||||
swap_xy: true
|
||||
mirror_x: false
|
||||
mirror_y: true
|
||||
on_touch:
|
||||
- logger.log:
|
||||
format: tp touched
|
||||
on_update:
|
||||
- logger.log:
|
||||
format: to updated
|
||||
on_release:
|
||||
- logger.log:
|
||||
format: to released
|
43
tests/components/tt21100/test.esp32-s2.yaml
Normal file
43
tests/components/tt21100/test.esp32-s2.yaml
Normal file
@ -0,0 +1,43 @@
|
||||
i2c:
|
||||
sda: GPIO8
|
||||
scl: GPIO18
|
||||
|
||||
spi:
|
||||
clk_pin: 7
|
||||
mosi_pin: 11
|
||||
miso_pin: 9
|
||||
|
||||
display:
|
||||
- platform: ili9xxx
|
||||
id: my_display
|
||||
model: ili9341
|
||||
cs_pin: 5
|
||||
dc_pin: 12
|
||||
reset_pin: 33
|
||||
auto_clear_enabled: false
|
||||
data_rate: 40MHz
|
||||
dimensions: 320x240
|
||||
update_interval: never
|
||||
transform:
|
||||
mirror_y: false
|
||||
mirror_x: false
|
||||
swap_xy: true
|
||||
|
||||
touchscreen:
|
||||
- platform: tt21100
|
||||
address: 0x24
|
||||
interrupt_pin: GPIO3
|
||||
on_touch:
|
||||
- logger.log: "Touchscreen:: Touched"
|
||||
|
||||
binary_sensor:
|
||||
- platform: tt21100
|
||||
index: 0
|
||||
name: "Home"
|
||||
|
||||
- platform: touchscreen
|
||||
name: FanLo
|
||||
x_min: 0
|
||||
x_max: 105
|
||||
y_min: 0
|
||||
y_max: 80
|
37
tests/components/xpt2046/test.esp32-s2.yaml
Normal file
37
tests/components/xpt2046/test.esp32-s2.yaml
Normal file
@ -0,0 +1,37 @@
|
||||
spi:
|
||||
clk_pin: 7
|
||||
mosi_pin: 11
|
||||
miso_pin: 9
|
||||
|
||||
display:
|
||||
- platform: ili9xxx
|
||||
id: my_display
|
||||
model: ili9341
|
||||
cs_pin: 5
|
||||
dc_pin: 12
|
||||
reset_pin: 33
|
||||
auto_clear_enabled: false
|
||||
data_rate: 40MHz
|
||||
dimensions: 320x240
|
||||
update_interval: never
|
||||
transform:
|
||||
mirror_y: false
|
||||
mirror_x: false
|
||||
swap_xy: true
|
||||
|
||||
touchscreen:
|
||||
- platform: xpt2046
|
||||
display: my_display
|
||||
id: my_toucher
|
||||
update_interval: 50ms
|
||||
cs_pin: 18
|
||||
threshold: 300
|
||||
calibration:
|
||||
x_min: 210
|
||||
x_max: 3890
|
||||
y_min: 170
|
||||
y_max: 3730
|
||||
transform:
|
||||
mirror_x: false
|
||||
mirror_y: true
|
||||
swap_xy: true
|
@ -3943,6 +3943,10 @@ text_sensor:
|
||||
- platform: template
|
||||
name: Template Text Sensor
|
||||
id: ${textname}_text
|
||||
- platform: template
|
||||
name: Template Text Sensor Timestamp
|
||||
id: ${textname}_text_timestamp
|
||||
device_class: timestamp
|
||||
- platform: wifi_info
|
||||
scan_results:
|
||||
name: Scan Results
|
||||
|
@ -634,7 +634,11 @@ sensor:
|
||||
current:
|
||||
name: CSE7766 Current
|
||||
power:
|
||||
name: CSE776 Power
|
||||
name: CSE7766 Power
|
||||
apparent_power:
|
||||
name: CSE7766 Apparent Power
|
||||
power_factor:
|
||||
name: CSE7766 Power Factor
|
||||
|
||||
- platform: fingerprint_grow
|
||||
fingerprint_count:
|
||||
|
@ -976,10 +976,11 @@ touchscreen:
|
||||
display: inkplate_display
|
||||
update_interval: 50ms
|
||||
threshold: 400
|
||||
calibration_x_min: 3860
|
||||
calibration_x_max: 280
|
||||
calibration_y_min: 340
|
||||
calibration_y_max: 3860
|
||||
calibration:
|
||||
x_min: 3860
|
||||
x_max: 280
|
||||
y_min: 340
|
||||
y_max: 3860
|
||||
on_touch:
|
||||
- logger.log:
|
||||
format: Touch at (%d, %d)
|
||||
|
Loading…
Reference in New Issue
Block a user