mirror of
https://github.com/esphome/esphome.git
synced 2024-12-28 16:31:44 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
2467a257f4
67 changed files with 2141 additions and 1690 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -241,12 +241,6 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||
- name: Cache platformio
|
||||
uses: actions/cache@v3.3.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
# yamllint disable-line rule:line-length
|
||||
key: platformio-test${{ matrix.file }}-${{ hashFiles('platformio.ini') }}
|
||||
- name: Run esphome compile tests/test${{ matrix.file }}.yaml
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
|
|
15
.github/workflows/sync-device-classes.yml
vendored
15
.github/workflows/sync-device-classes.yml
vendored
|
@ -6,14 +6,12 @@ on:
|
|||
schedule:
|
||||
- cron: '45 6 * * *'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync Device Classes
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'esphome/esphome'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
@ -38,15 +36,6 @@ jobs:
|
|||
run: |
|
||||
python ./script/sync-device_class.py
|
||||
|
||||
- name: Get PR template
|
||||
id: pr-template-body
|
||||
run: |
|
||||
body=$(cat .github/PULL_REQUEST_TEMPLATE.md)
|
||||
delimiter="$(openssl rand -hex 8)"
|
||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
||||
echo "$body" >> $GITHUB_OUTPUT
|
||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit changes
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
|
@ -56,5 +45,5 @@ jobs:
|
|||
branch: sync/device-classes
|
||||
delete-branch: true
|
||||
title: "Synchronise Device Classes from Home Assistant"
|
||||
body: ${{ steps.pr-template-body.outputs.body }}
|
||||
body-path: .github/PULL_REQUEST_TEMPLATE.md
|
||||
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}
|
||||
|
|
|
@ -27,7 +27,7 @@ repos:
|
|||
- --branch=release
|
||||
- --branch=beta
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.4.0
|
||||
rev: v3.7.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py39-plus]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
|
||||
from esphome import automation, core
|
||||
from esphome.components import display, font
|
||||
from esphome.components import font
|
||||
import esphome.components.image as espImage
|
||||
from esphome.components.image import CONF_USE_TRANSPARENCY
|
||||
import esphome.config_validation as cv
|
||||
|
@ -18,6 +18,7 @@ from esphome.core import CORE, HexInt
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
AUTO_LOAD = ["image"]
|
||||
CODEOWNERS = ["@syndlex"]
|
||||
DEPENDENCIES = ["display"]
|
||||
MULTI_CONF = True
|
||||
|
@ -27,16 +28,18 @@ CONF_START_FRAME = "start_frame"
|
|||
CONF_END_FRAME = "end_frame"
|
||||
CONF_FRAME = "frame"
|
||||
|
||||
Animation_ = display.display_ns.class_("Animation", espImage.Image_)
|
||||
animation_ns = cg.esphome_ns.namespace("animation")
|
||||
|
||||
Animation_ = animation_ns.class_("Animation", espImage.Image_)
|
||||
|
||||
# Actions
|
||||
NextFrameAction = display.display_ns.class_(
|
||||
NextFrameAction = animation_ns.class_(
|
||||
"AnimationNextFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
PrevFrameAction = display.display_ns.class_(
|
||||
PrevFrameAction = animation_ns.class_(
|
||||
"AnimationPrevFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
SetFrameAction = display.display_ns.class_(
|
||||
SetFrameAction = animation_ns.class_(
|
||||
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
namespace animation {
|
||||
|
||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
|
||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
|
||||
image::ImageType type)
|
||||
: Image(data_start, width, height, type),
|
||||
animation_data_start_(data_start),
|
||||
current_frame_(0),
|
||||
|
@ -65,5 +66,5 @@ void Animation::update_data_start_() {
|
|||
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
} // namespace animation
|
||||
} // namespace esphome
|
|
@ -1,14 +1,14 @@
|
|||
#pragma once
|
||||
#include "image.h"
|
||||
#include "esphome/components/image/image.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
namespace animation {
|
||||
|
||||
class Animation : public Image {
|
||||
class Animation : public image::Image {
|
||||
public:
|
||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type);
|
||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type);
|
||||
|
||||
uint32_t get_animation_frame_count() const;
|
||||
int get_current_frame() const;
|
||||
|
@ -63,5 +63,5 @@ template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
|
|||
Animation *parent_;
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace animation
|
||||
} // namespace esphome
|
|
@ -47,7 +47,7 @@ async def async_run_logs(config, address):
|
|||
except APIConnectionError:
|
||||
cli.disconnect()
|
||||
|
||||
async def on_disconnect():
|
||||
async def on_disconnect(expected_disconnect: bool) -> None:
|
||||
_LOGGER.warning("Disconnected from API")
|
||||
|
||||
zc = zeroconf.Zeroconf()
|
||||
|
|
|
@ -95,6 +95,14 @@ DEVICE_CLASSES = [
|
|||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
CONF_TIME_OFF = "time_off"
|
||||
CONF_TIME_ON = "time_on"
|
||||
|
||||
DEFAULT_DELAY = "1s"
|
||||
DEFAULT_TIME_OFF = "100ms"
|
||||
DEFAULT_TIME_ON = "900ms"
|
||||
|
||||
|
||||
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
|
||||
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
|
||||
BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
||||
|
@ -138,47 +146,75 @@ FILTER_REGISTRY = Registry()
|
|||
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("invert", InvertFilter, {})
|
||||
def register_filter(name, filter_type, schema):
|
||||
return FILTER_REGISTRY.register(name, filter_type, schema)
|
||||
|
||||
|
||||
@register_filter("invert", InvertFilter, {})
|
||||
async def invert_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
|
||||
@register_filter(
|
||||
"delayed_on_off",
|
||||
DelayedOnOffFilter,
|
||||
cv.Any(
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_TIME_ON): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
cv.Required(CONF_TIME_OFF): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
}
|
||||
),
|
||||
msg="'delayed_on_off' filter requires either a delay time to be used for both "
|
||||
"turn-on and turn-off delays, or two parameters 'time_on' and 'time_off' if "
|
||||
"different delay times are required.",
|
||||
),
|
||||
)
|
||||
async def delayed_on_off_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
var = cg.new_Pvariable(filter_id)
|
||||
await cg.register_component(var, {})
|
||||
if isinstance(config, dict):
|
||||
template_ = await cg.templatable(config[CONF_TIME_ON], [], cg.uint32)
|
||||
cg.add(var.set_on_delay(template_))
|
||||
template_ = await cg.templatable(config[CONF_TIME_OFF], [], cg.uint32)
|
||||
cg.add(var.set_off_delay(template_))
|
||||
else:
|
||||
template_ = await cg.templatable(config, [], cg.uint32)
|
||||
cg.add(var.set_on_delay(template_))
|
||||
cg.add(var.set_off_delay(template_))
|
||||
return var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
|
||||
@register_filter(
|
||||
"delayed_on", DelayedOnFilter, cv.templatable(cv.positive_time_period_milliseconds)
|
||||
)
|
||||
async def delayed_on_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
var = cg.new_Pvariable(filter_id)
|
||||
await cg.register_component(var, {})
|
||||
template_ = await cg.templatable(config, [], cg.uint32)
|
||||
cg.add(var.set_delay(template_))
|
||||
return var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds
|
||||
@register_filter(
|
||||
"delayed_off",
|
||||
DelayedOffFilter,
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
)
|
||||
async def delayed_off_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
var = cg.new_Pvariable(filter_id)
|
||||
await cg.register_component(var, {})
|
||||
template_ = await cg.templatable(config, [], cg.uint32)
|
||||
cg.add(var.set_delay(template_))
|
||||
return var
|
||||
|
||||
|
||||
CONF_TIME_OFF = "time_off"
|
||||
CONF_TIME_ON = "time_on"
|
||||
|
||||
DEFAULT_DELAY = "1s"
|
||||
DEFAULT_TIME_OFF = "100ms"
|
||||
DEFAULT_TIME_ON = "900ms"
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
@register_filter(
|
||||
"autorepeat",
|
||||
AutorepeatFilter,
|
||||
cv.All(
|
||||
|
@ -215,7 +251,7 @@ async def autorepeat_filter_to_code(config, filter_id):
|
|||
return var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
|
||||
@register_filter("lambda", LambdaFilter, cv.returning_lambda)
|
||||
async def lambda_filter_to_code(config, filter_id):
|
||||
lambda_ = await cg.process_lambda(
|
||||
config, [(bool, "x")], return_type=cg.optional.template(bool)
|
||||
|
|
|
@ -26,22 +26,20 @@ void Filter::input(bool value, bool is_initial) {
|
|||
}
|
||||
}
|
||||
|
||||
DelayedOnOffFilter::DelayedOnOffFilter(uint32_t delay) : delay_(delay) {}
|
||||
optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) {
|
||||
if (value) {
|
||||
this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(true, is_initial); });
|
||||
this->set_timeout("ON_OFF", this->on_delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
|
||||
} else {
|
||||
this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); });
|
||||
this->set_timeout("ON_OFF", this->off_delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
DelayedOnFilter::DelayedOnFilter(uint32_t delay) : delay_(delay) {}
|
||||
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
||||
if (value) {
|
||||
this->set_timeout("ON", this->delay_, [this, is_initial]() { this->output(true, is_initial); });
|
||||
this->set_timeout("ON", this->delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
|
||||
return {};
|
||||
} else {
|
||||
this->cancel_timeout("ON");
|
||||
|
@ -51,10 +49,9 @@ optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
|||
|
||||
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
DelayedOffFilter::DelayedOffFilter(uint32_t delay) : delay_(delay) {}
|
||||
optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
|
||||
if (!value) {
|
||||
this->set_timeout("OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); });
|
||||
this->set_timeout("OFF", this->delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
|
||||
return {};
|
||||
} else {
|
||||
this->cancel_timeout("OFF");
|
||||
|
@ -114,15 +111,6 @@ LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move
|
|||
|
||||
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
||||
|
||||
optional<bool> UniqueFilter::new_value(bool value, bool is_initial) {
|
||||
if (this->last_value_.has_value() && *this->last_value_ == value) {
|
||||
return {};
|
||||
} else {
|
||||
this->last_value_ = value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace binary_sensor
|
||||
|
||||
} // namespace esphome
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
|
@ -29,38 +30,40 @@ class Filter {
|
|||
|
||||
class DelayedOnOffFilter : public Filter, public Component {
|
||||
public:
|
||||
explicit DelayedOnOffFilter(uint32_t delay);
|
||||
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
template<typename T> void set_on_delay(T delay) { this->on_delay_ = delay; }
|
||||
template<typename T> void set_off_delay(T delay) { this->off_delay_ = delay; }
|
||||
|
||||
protected:
|
||||
uint32_t delay_;
|
||||
TemplatableValue<uint32_t> on_delay_{};
|
||||
TemplatableValue<uint32_t> off_delay_{};
|
||||
};
|
||||
|
||||
class DelayedOnFilter : public Filter, public Component {
|
||||
public:
|
||||
explicit DelayedOnFilter(uint32_t delay);
|
||||
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
|
||||
|
||||
protected:
|
||||
uint32_t delay_;
|
||||
TemplatableValue<uint32_t> delay_{};
|
||||
};
|
||||
|
||||
class DelayedOffFilter : public Filter, public Component {
|
||||
public:
|
||||
explicit DelayedOffFilter(uint32_t delay);
|
||||
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
|
||||
|
||||
protected:
|
||||
uint32_t delay_;
|
||||
TemplatableValue<uint32_t> delay_{};
|
||||
};
|
||||
|
||||
class InvertFilter : public Filter {
|
||||
|
@ -105,14 +108,6 @@ class LambdaFilter : public Filter {
|
|||
std::function<optional<bool>(bool)> f_;
|
||||
};
|
||||
|
||||
class UniqueFilter : public Filter {
|
||||
public:
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
protected:
|
||||
optional<bool> last_value_{};
|
||||
};
|
||||
|
||||
} // namespace binary_sensor
|
||||
|
||||
} // namespace esphome
|
||||
|
|
|
@ -12,6 +12,7 @@ from esphome.const import (
|
|||
CONF_TRIGGER_ID,
|
||||
CONF_MQTT_ID,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_IDENTIFY,
|
||||
DEVICE_CLASS_RESTART,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
)
|
||||
|
@ -24,6 +25,7 @@ IS_PLATFORM_COMPONENT = True
|
|||
|
||||
DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_IDENTIFY,
|
||||
DEVICE_CLASS_RESTART,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
]
|
||||
|
|
|
@ -38,7 +38,6 @@ CONFIG_SCHEMA = cv.All(
|
|||
),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s")),
|
||||
cv.only_on(["esp32", "esp8266"]),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/version.h"
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
|
@ -13,6 +14,7 @@
|
|||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#include <esp32/rom/rtc.h>
|
||||
#include <esp_chip_info.h>
|
||||
#else
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
|
@ -20,8 +22,12 @@
|
|||
#endif // USE_ESP32
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#ifdef USE_RP2040
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <Esp.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace debug {
|
||||
|
@ -33,6 +39,8 @@ static uint32_t get_free_heap() {
|
|||
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP32)
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
#elif defined(USE_RP2040)
|
||||
return rp2040.getFreeHeap();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -61,9 +69,9 @@ void DebugComponent::dump_config() {
|
|||
device_info += ESPHOME_VERSION;
|
||||
|
||||
this->free_heap_ = get_free_heap();
|
||||
ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
|
||||
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2040)
|
||||
const char *flash_mode;
|
||||
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
|
||||
case FM_QIO:
|
||||
|
@ -272,6 +280,11 @@ void DebugComponent::dump_config() {
|
|||
reset_reason = ESP.getResetReason().c_str();
|
||||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
|
||||
device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
|
||||
#endif // USE_RP2040
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->device_info_ != nullptr) {
|
||||
if (device_info.length() > 255)
|
||||
|
@ -289,7 +302,7 @@ void DebugComponent::loop() {
|
|||
uint32_t new_free_heap = get_free_heap();
|
||||
if (new_free_heap < this->free_heap_ / 2) {
|
||||
this->free_heap_ = new_free_heap;
|
||||
ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
|
||||
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
|
||||
this->status_momentary_warning("heap", 1000);
|
||||
}
|
||||
|
||||
|
|
343
esphome/components/display/display.cpp
Normal file
343
esphome/components/display/display.cpp
Normal file
|
@ -0,0 +1,343 @@
|
|||
#include "display.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
static const char *const TAG = "display";
|
||||
|
||||
const Color COLOR_OFF(0, 0, 0, 0);
|
||||
const Color COLOR_ON(255, 255, 255, 255);
|
||||
|
||||
void Display::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
|
||||
void Display::clear() { this->fill(COLOR_OFF); }
|
||||
void Display::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
|
||||
void HOT Display::line(int x1, int y1, int x2, int y2, Color color) {
|
||||
const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
|
||||
const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
|
||||
int32_t err = dx + dy;
|
||||
|
||||
while (true) {
|
||||
this->draw_pixel_at(x1, y1, color);
|
||||
if (x1 == x2 && y1 == y2)
|
||||
break;
|
||||
int32_t e2 = 2 * err;
|
||||
if (e2 >= dy) {
|
||||
err += dy;
|
||||
x1 += sx;
|
||||
}
|
||||
if (e2 <= dx) {
|
||||
err += dx;
|
||||
y1 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
void HOT Display::horizontal_line(int x, int y, int width, Color color) {
|
||||
// Future: Could be made more efficient by manipulating buffer directly in certain rotations.
|
||||
for (int i = x; i < x + width; i++)
|
||||
this->draw_pixel_at(i, y, color);
|
||||
}
|
||||
void HOT Display::vertical_line(int x, int y, int height, Color color) {
|
||||
// Future: Could be made more efficient by manipulating buffer directly in certain rotations.
|
||||
for (int i = y; i < y + height; i++)
|
||||
this->draw_pixel_at(x, i, color);
|
||||
}
|
||||
void Display::rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
this->horizontal_line(x1, y1, width, color);
|
||||
this->horizontal_line(x1, y1 + height - 1, width, color);
|
||||
this->vertical_line(x1, y1, height, color);
|
||||
this->vertical_line(x1 + width - 1, y1, height, color);
|
||||
}
|
||||
void Display::filled_rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
// Future: Use vertical_line and horizontal_line methods depending on rotation to reduce memory accesses.
|
||||
for (int i = y1; i < y1 + height; i++) {
|
||||
this->horizontal_line(x1, i, width, color);
|
||||
}
|
||||
}
|
||||
void HOT Display::circle(int center_x, int center_xy, int radius, Color color) {
|
||||
int dx = -radius;
|
||||
int dy = 0;
|
||||
int err = 2 - 2 * radius;
|
||||
int e2;
|
||||
|
||||
do {
|
||||
this->draw_pixel_at(center_x - dx, center_xy + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_xy + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_xy - dy, color);
|
||||
this->draw_pixel_at(center_x - dx, center_xy - dy, color);
|
||||
e2 = err;
|
||||
if (e2 < dy) {
|
||||
err += ++dy * 2 + 1;
|
||||
if (-dx == dy && e2 <= dx) {
|
||||
e2 = 0;
|
||||
}
|
||||
}
|
||||
if (e2 > dx) {
|
||||
err += ++dx * 2 + 1;
|
||||
}
|
||||
} while (dx <= 0);
|
||||
}
|
||||
void Display::filled_circle(int center_x, int center_y, int radius, Color color) {
|
||||
int dx = -int32_t(radius);
|
||||
int dy = 0;
|
||||
int err = 2 - 2 * radius;
|
||||
int e2;
|
||||
|
||||
do {
|
||||
this->draw_pixel_at(center_x - dx, center_y + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_y + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_y - dy, color);
|
||||
this->draw_pixel_at(center_x - dx, center_y - dy, color);
|
||||
int hline_width = 2 * (-dx) + 1;
|
||||
this->horizontal_line(center_x + dx, center_y + dy, hline_width, color);
|
||||
this->horizontal_line(center_x + dx, center_y - dy, hline_width, color);
|
||||
e2 = err;
|
||||
if (e2 < dy) {
|
||||
err += ++dy * 2 + 1;
|
||||
if (-dx == dy && e2 <= dx) {
|
||||
e2 = 0;
|
||||
}
|
||||
}
|
||||
if (e2 > dx) {
|
||||
err += ++dx * 2 + 1;
|
||||
}
|
||||
} while (dx <= 0);
|
||||
}
|
||||
|
||||
void Display::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) {
|
||||
int x_start, y_start;
|
||||
int width, height;
|
||||
this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height);
|
||||
font->print(x_start, y_start, this, color, text);
|
||||
}
|
||||
void Display::vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg) {
|
||||
char buffer[256];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
if (ret > 0)
|
||||
this->print(x, y, font, color, align, buffer);
|
||||
}
|
||||
|
||||
void Display::image(int x, int y, BaseImage *image, Color color_on, Color color_off) {
|
||||
this->image(x, y, image, ImageAlign::TOP_LEFT, color_on, color_off);
|
||||
}
|
||||
|
||||
void Display::image(int x, int y, BaseImage *image, ImageAlign align, Color color_on, Color color_off) {
|
||||
auto x_align = ImageAlign(int(align) & (int(ImageAlign::HORIZONTAL_ALIGNMENT)));
|
||||
auto y_align = ImageAlign(int(align) & (int(ImageAlign::VERTICAL_ALIGNMENT)));
|
||||
|
||||
switch (x_align) {
|
||||
case ImageAlign::RIGHT:
|
||||
x -= image->get_width();
|
||||
break;
|
||||
case ImageAlign::CENTER_HORIZONTAL:
|
||||
x -= image->get_width() / 2;
|
||||
break;
|
||||
case ImageAlign::LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (y_align) {
|
||||
case ImageAlign::BOTTOM:
|
||||
y -= image->get_height();
|
||||
break;
|
||||
case ImageAlign::CENTER_VERTICAL:
|
||||
y -= image->get_height() / 2;
|
||||
break;
|
||||
case ImageAlign::TOP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image->draw(x, y, this, color_on, color_off);
|
||||
}
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
void Display::graph(int x, int y, graph::Graph *graph, Color color_on) { graph->draw(this, x, y, color_on); }
|
||||
void Display::legend(int x, int y, graph::Graph *graph, Color color_on) { graph->draw_legend(this, x, y, color_on); }
|
||||
#endif // USE_GRAPH
|
||||
|
||||
#ifdef USE_QR_CODE
|
||||
void Display::qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on, int scale) {
|
||||
qr_code->draw(this, x, y, color_on, scale);
|
||||
}
|
||||
#endif // USE_QR_CODE
|
||||
|
||||
void Display::get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1,
|
||||
int *width, int *height) {
|
||||
int x_offset, baseline;
|
||||
font->measure(text, width, &x_offset, &baseline, height);
|
||||
|
||||
auto x_align = TextAlign(int(align) & 0x18);
|
||||
auto y_align = TextAlign(int(align) & 0x07);
|
||||
|
||||
switch (x_align) {
|
||||
case TextAlign::RIGHT:
|
||||
*x1 = x - *width;
|
||||
break;
|
||||
case TextAlign::CENTER_HORIZONTAL:
|
||||
*x1 = x - (*width) / 2;
|
||||
break;
|
||||
case TextAlign::LEFT:
|
||||
default:
|
||||
// LEFT
|
||||
*x1 = x;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (y_align) {
|
||||
case TextAlign::BOTTOM:
|
||||
*y1 = y - *height;
|
||||
break;
|
||||
case TextAlign::BASELINE:
|
||||
*y1 = y - baseline;
|
||||
break;
|
||||
case TextAlign::CENTER_VERTICAL:
|
||||
*y1 = y - (*height) / 2;
|
||||
break;
|
||||
case TextAlign::TOP:
|
||||
default:
|
||||
*y1 = y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Display::print(int x, int y, BaseFont *font, Color color, const char *text) {
|
||||
this->print(x, y, font, color, TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
void Display::print(int x, int y, BaseFont *font, TextAlign align, const char *text) {
|
||||
this->print(x, y, font, COLOR_ON, align, text);
|
||||
}
|
||||
void Display::print(int x, int y, BaseFont *font, const char *text) {
|
||||
this->print(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
void Display::printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, color, align, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void Display::printf(int x, int y, BaseFont *font, Color color, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, color, TextAlign::TOP_LEFT, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void Display::printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, COLOR_ON, align, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void Display::printf(int x, int y, BaseFont *font, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void Display::set_writer(display_writer_t &&writer) { this->writer_ = writer; }
|
||||
void Display::set_pages(std::vector<DisplayPage *> pages) {
|
||||
for (auto *page : pages)
|
||||
page->set_parent(this);
|
||||
|
||||
for (uint32_t i = 0; i < pages.size() - 1; i++) {
|
||||
pages[i]->set_next(pages[i + 1]);
|
||||
pages[i + 1]->set_prev(pages[i]);
|
||||
}
|
||||
pages[0]->set_prev(pages[pages.size() - 1]);
|
||||
pages[pages.size() - 1]->set_next(pages[0]);
|
||||
this->show_page(pages[0]);
|
||||
}
|
||||
void Display::show_page(DisplayPage *page) {
|
||||
this->previous_page_ = this->page_;
|
||||
this->page_ = page;
|
||||
if (this->previous_page_ != this->page_) {
|
||||
for (auto *t : on_page_change_triggers_)
|
||||
t->process(this->previous_page_, this->page_);
|
||||
}
|
||||
}
|
||||
void Display::show_next_page() { this->page_->show_next(); }
|
||||
void Display::show_prev_page() { this->page_->show_prev(); }
|
||||
void Display::do_update_() {
|
||||
if (this->auto_clear_enabled_) {
|
||||
this->clear();
|
||||
}
|
||||
if (this->page_ != nullptr) {
|
||||
this->page_->get_writer()(*this);
|
||||
} else if (this->writer_.has_value()) {
|
||||
(*this->writer_)(*this);
|
||||
}
|
||||
// remove all not ended clipping regions
|
||||
while (is_clipping()) {
|
||||
end_clipping();
|
||||
}
|
||||
}
|
||||
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
|
||||
if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
|
||||
this->trigger(from, to);
|
||||
}
|
||||
void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) {
|
||||
char buffer[64];
|
||||
size_t ret = time.strftime(buffer, sizeof(buffer), format);
|
||||
if (ret > 0)
|
||||
this->print(x, y, font, color, align, buffer);
|
||||
}
|
||||
void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) {
|
||||
this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time);
|
||||
}
|
||||
void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) {
|
||||
this->strftime(x, y, font, COLOR_ON, align, format, time);
|
||||
}
|
||||
void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) {
|
||||
this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time);
|
||||
}
|
||||
|
||||
void Display::start_clipping(Rect rect) {
|
||||
if (!this->clipping_rectangle_.empty()) {
|
||||
Rect r = this->clipping_rectangle_.back();
|
||||
rect.shrink(r);
|
||||
}
|
||||
this->clipping_rectangle_.push_back(rect);
|
||||
}
|
||||
void Display::end_clipping() {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
ESP_LOGE(TAG, "clear: Clipping is not set.");
|
||||
} else {
|
||||
this->clipping_rectangle_.pop_back();
|
||||
}
|
||||
}
|
||||
void Display::extend_clipping(Rect add_rect) {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
ESP_LOGE(TAG, "add: Clipping is not set.");
|
||||
} else {
|
||||
this->clipping_rectangle_.back().extend(add_rect);
|
||||
}
|
||||
}
|
||||
void Display::shrink_clipping(Rect add_rect) {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
ESP_LOGE(TAG, "add: Clipping is not set.");
|
||||
} else {
|
||||
this->clipping_rectangle_.back().shrink(add_rect);
|
||||
}
|
||||
}
|
||||
Rect Display::get_clipping() {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
return Rect();
|
||||
} else {
|
||||
return this->clipping_rectangle_.back();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
|
||||
void DisplayPage::show() { this->parent_->show_page(this); }
|
||||
void DisplayPage::show_next() { this->next_->show(); }
|
||||
void DisplayPage::show_prev() { this->prev_->show(); }
|
||||
void DisplayPage::set_parent(Display *parent) { this->parent_ = parent; }
|
||||
void DisplayPage::set_prev(DisplayPage *prev) { this->prev_ = prev; }
|
||||
void DisplayPage::set_next(DisplayPage *next) { this->next_ = next; }
|
||||
const display_writer_t &DisplayPage::get_writer() const { return this->writer_; }
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
575
esphome/components/display/display.h
Normal file
575
esphome/components/display/display.h
Normal file
|
@ -0,0 +1,575 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdarg>
|
||||
#include <vector>
|
||||
|
||||
#include "rect.h"
|
||||
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
#include "esphome/components/graph/graph.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_QR_CODE
|
||||
#include "esphome/components/qr_code/qr_code.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
/** TextAlign is used to tell the display class how to position a piece of text. By default
|
||||
* the coordinates you enter for the print*() functions take the upper left corner of the text
|
||||
* as the "anchor" point. You can customize this behavior to, for example, make the coordinates
|
||||
* refer to the *center* of the text.
|
||||
*
|
||||
* All text alignments consist of an X and Y-coordinate alignment. For the alignment along the X-axis
|
||||
* these options are allowed:
|
||||
*
|
||||
* - LEFT (x-coordinate of anchor point is on left)
|
||||
* - CENTER_HORIZONTAL (x-coordinate of anchor point is in the horizontal center of the text)
|
||||
* - RIGHT (x-coordinate of anchor point is on right)
|
||||
*
|
||||
* For the Y-Axis alignment these options are allowed:
|
||||
*
|
||||
* - TOP (y-coordinate of anchor is on the top of the text)
|
||||
* - CENTER_VERTICAL (y-coordinate of anchor is in the vertical center of the text)
|
||||
* - BASELINE (y-coordinate of anchor is on the baseline of the text)
|
||||
* - BOTTOM (y-coordinate of anchor is on the bottom of the text)
|
||||
*
|
||||
* These options are then combined to create combined TextAlignment options like:
|
||||
* - TOP_LEFT (default)
|
||||
* - CENTER (anchor point is in the middle of the text bounds)
|
||||
* - ...
|
||||
*/
|
||||
enum class TextAlign {
|
||||
TOP = 0x00,
|
||||
CENTER_VERTICAL = 0x01,
|
||||
BASELINE = 0x02,
|
||||
BOTTOM = 0x04,
|
||||
|
||||
LEFT = 0x00,
|
||||
CENTER_HORIZONTAL = 0x08,
|
||||
RIGHT = 0x10,
|
||||
|
||||
TOP_LEFT = TOP | LEFT,
|
||||
TOP_CENTER = TOP | CENTER_HORIZONTAL,
|
||||
TOP_RIGHT = TOP | RIGHT,
|
||||
|
||||
CENTER_LEFT = CENTER_VERTICAL | LEFT,
|
||||
CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL,
|
||||
CENTER_RIGHT = CENTER_VERTICAL | RIGHT,
|
||||
|
||||
BASELINE_LEFT = BASELINE | LEFT,
|
||||
BASELINE_CENTER = BASELINE | CENTER_HORIZONTAL,
|
||||
BASELINE_RIGHT = BASELINE | RIGHT,
|
||||
|
||||
BOTTOM_LEFT = BOTTOM | LEFT,
|
||||
BOTTOM_CENTER = BOTTOM | CENTER_HORIZONTAL,
|
||||
BOTTOM_RIGHT = BOTTOM | RIGHT,
|
||||
};
|
||||
|
||||
/** ImageAlign is used to tell the display class how to position a image. By default
|
||||
* the coordinates you enter for the image() functions take the upper left corner of the image
|
||||
* as the "anchor" point. You can customize this behavior to, for example, make the coordinates
|
||||
* refer to the *center* of the image.
|
||||
*
|
||||
* All image alignments consist of an X and Y-coordinate alignment. For the alignment along the X-axis
|
||||
* these options are allowed:
|
||||
*
|
||||
* - LEFT (x-coordinate of anchor point is on left)
|
||||
* - CENTER_HORIZONTAL (x-coordinate of anchor point is in the horizontal center of the image)
|
||||
* - RIGHT (x-coordinate of anchor point is on right)
|
||||
*
|
||||
* For the Y-Axis alignment these options are allowed:
|
||||
*
|
||||
* - TOP (y-coordinate of anchor is on the top of the image)
|
||||
* - CENTER_VERTICAL (y-coordinate of anchor is in the vertical center of the image)
|
||||
* - BOTTOM (y-coordinate of anchor is on the bottom of the image)
|
||||
*
|
||||
* These options are then combined to create combined TextAlignment options like:
|
||||
* - TOP_LEFT (default)
|
||||
* - CENTER (anchor point is in the middle of the image bounds)
|
||||
* - ...
|
||||
*/
|
||||
enum class ImageAlign {
|
||||
TOP = 0x00,
|
||||
CENTER_VERTICAL = 0x01,
|
||||
BOTTOM = 0x02,
|
||||
|
||||
LEFT = 0x00,
|
||||
CENTER_HORIZONTAL = 0x04,
|
||||
RIGHT = 0x08,
|
||||
|
||||
TOP_LEFT = TOP | LEFT,
|
||||
TOP_CENTER = TOP | CENTER_HORIZONTAL,
|
||||
TOP_RIGHT = TOP | RIGHT,
|
||||
|
||||
CENTER_LEFT = CENTER_VERTICAL | LEFT,
|
||||
CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL,
|
||||
CENTER_RIGHT = CENTER_VERTICAL | RIGHT,
|
||||
|
||||
BOTTOM_LEFT = BOTTOM | LEFT,
|
||||
BOTTOM_CENTER = BOTTOM | CENTER_HORIZONTAL,
|
||||
BOTTOM_RIGHT = BOTTOM | RIGHT,
|
||||
|
||||
HORIZONTAL_ALIGNMENT = LEFT | CENTER_HORIZONTAL | RIGHT,
|
||||
VERTICAL_ALIGNMENT = TOP | CENTER_VERTICAL | BOTTOM
|
||||
};
|
||||
|
||||
enum DisplayType {
|
||||
DISPLAY_TYPE_BINARY = 1,
|
||||
DISPLAY_TYPE_GRAYSCALE = 2,
|
||||
DISPLAY_TYPE_COLOR = 3,
|
||||
};
|
||||
|
||||
enum DisplayRotation {
|
||||
DISPLAY_ROTATION_0_DEGREES = 0,
|
||||
DISPLAY_ROTATION_90_DEGREES = 90,
|
||||
DISPLAY_ROTATION_180_DEGREES = 180,
|
||||
DISPLAY_ROTATION_270_DEGREES = 270,
|
||||
};
|
||||
|
||||
class Display;
|
||||
class DisplayBuffer;
|
||||
class DisplayPage;
|
||||
class DisplayOnPageChangeTrigger;
|
||||
|
||||
using display_writer_t = std::function<void(Display &)>;
|
||||
using display_buffer_writer_t = std::function<void(DisplayBuffer &)>;
|
||||
|
||||
#define LOG_DISPLAY(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, prefix type); \
|
||||
ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, (obj)->rotation_); \
|
||||
ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \
|
||||
}
|
||||
|
||||
/// Turn the pixel OFF.
|
||||
extern const Color COLOR_OFF;
|
||||
/// Turn the pixel ON.
|
||||
extern const Color COLOR_ON;
|
||||
|
||||
class BaseImage {
|
||||
public:
|
||||
virtual void draw(int x, int y, Display *display, Color color_on, Color color_off) = 0;
|
||||
virtual int get_width() const = 0;
|
||||
virtual int get_height() const = 0;
|
||||
};
|
||||
|
||||
class BaseFont {
|
||||
public:
|
||||
virtual void print(int x, int y, Display *display, Color color, const char *text) = 0;
|
||||
virtual void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) = 0;
|
||||
};
|
||||
|
||||
class Display {
|
||||
public:
|
||||
/// Fill the entire screen with the given color.
|
||||
virtual void fill(Color color);
|
||||
/// 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;
|
||||
|
||||
/// 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); }
|
||||
|
||||
/// Set a single pixel at the specified coordinates to the given color.
|
||||
virtual void draw_pixel_at(int x, int y, Color color) = 0;
|
||||
|
||||
/// Draw a straight line from the point [x1,y1] to [x2,y2] with the given color.
|
||||
void line(int x1, int y1, int x2, int y2, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a horizontal line from the point [x,y] to [x+width,y] with the given color.
|
||||
void horizontal_line(int x, int y, int width, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a vertical line from the point [x,y] to [x,y+width] with the given color.
|
||||
void vertical_line(int x, int y, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a rectangle with the top left point at [x1,y1] and the bottom right point at
|
||||
/// [x1+width,y1+height].
|
||||
void rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a rectangle with the top left point at [x1,y1] and the bottom right point at [x1+width,y1+height].
|
||||
void filled_rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void circle(int center_x, int center_xy, int radius, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text);
|
||||
|
||||
/** Print `text` with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, Color color, const char *text);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, TextAlign align, const char *text);
|
||||
|
||||
/** Print `text` with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, const char *text);
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...)
|
||||
__attribute__((format(printf, 7, 8)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, Color color, const char *format, ...) __attribute__((format(printf, 6, 7)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time)
|
||||
__attribute__((format(strftime, 7, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time)
|
||||
__attribute__((format(strftime, 6, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time)
|
||||
__attribute__((format(strftime, 6, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) __attribute__((format(strftime, 5, 0)));
|
||||
|
||||
/** Draw the `image` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param image The image to draw.
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
* @param color_off The color to replace in binary images for the off bits.
|
||||
*/
|
||||
void image(int x, int y, BaseImage *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||
|
||||
/** Draw the `image` at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param image The image to draw.
|
||||
* @param align The alignment of the image.
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
* @param color_off The color to replace in binary images for the off bits.
|
||||
*/
|
||||
void image(int x, int y, BaseImage *image, ImageAlign align, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
/** Draw the `graph` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param graph The graph id to draw
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
*/
|
||||
void graph(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON);
|
||||
|
||||
/** Draw the `legend` for graph with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param graph The graph id for which the legend applies to
|
||||
* @param graph The graph id for which the legend applies to
|
||||
* @param graph The graph id for which the legend applies to
|
||||
* @param name_font The font used for the trace name
|
||||
* @param value_font The font used for the trace value and units
|
||||
* @param color_on The color of the border
|
||||
*/
|
||||
void legend(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON);
|
||||
#endif // USE_GRAPH
|
||||
|
||||
#ifdef USE_QR_CODE
|
||||
/** Draw the `qr_code` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param qr_code The qr_code to draw
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
*/
|
||||
void qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on = COLOR_ON, int scale = 1);
|
||||
#endif
|
||||
|
||||
/** Get the text bounds of the given string.
|
||||
*
|
||||
* @param x The x coordinate to place the string at, can be 0 if only interested in dimensions.
|
||||
* @param y The y coordinate to place the string at, can be 0 if only interested in dimensions.
|
||||
* @param text The text to measure.
|
||||
* @param font The font to measure the text bounds with.
|
||||
* @param align The alignment of the text. Set to TextAlign::TOP_LEFT if only interested in dimensions.
|
||||
* @param x1 A pointer to store the returned x coordinate of the upper left corner in.
|
||||
* @param y1 A pointer to store the returned y coordinate of the upper left corner in.
|
||||
* @param width A pointer to store the returned text width in.
|
||||
* @param height A pointer to store the returned text height in.
|
||||
*/
|
||||
void get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1, int *width,
|
||||
int *height);
|
||||
|
||||
/// Internal method to set the display writer lambda.
|
||||
void set_writer(display_writer_t &&writer);
|
||||
void set_writer(const display_buffer_writer_t &writer) {
|
||||
// Temporary mapping to be removed once all lambdas are changed to use `display.DisplayRef`
|
||||
this->set_writer([writer](Display &display) { return writer((display::DisplayBuffer &) display); });
|
||||
}
|
||||
|
||||
void show_page(DisplayPage *page);
|
||||
void show_next_page();
|
||||
void show_prev_page();
|
||||
|
||||
void set_pages(std::vector<DisplayPage *> pages);
|
||||
|
||||
const DisplayPage *get_active_page() const { return this->page_; }
|
||||
|
||||
void add_on_page_change_trigger(DisplayOnPageChangeTrigger *t) { this->on_page_change_triggers_.push_back(t); }
|
||||
|
||||
/// Internal method to set the display rotation with.
|
||||
void set_rotation(DisplayRotation rotation);
|
||||
|
||||
// Internal method to set display auto clearing.
|
||||
void set_auto_clear(bool auto_clear_enabled) { this->auto_clear_enabled_ = auto_clear_enabled; }
|
||||
|
||||
DisplayRotation get_rotation() const { return this->rotation_; }
|
||||
|
||||
/** Get the type of display that the buffer corresponds to. In case of dynamically configurable displays,
|
||||
* returns the type the display is currently configured to.
|
||||
*/
|
||||
virtual DisplayType get_display_type() = 0;
|
||||
|
||||
/** Set the clipping rectangle for further drawing
|
||||
*
|
||||
* @param[in] rect: Pointer to Rect for clipping (or NULL for entire screen)
|
||||
*
|
||||
* return true if success, false if error
|
||||
*/
|
||||
void start_clipping(Rect rect);
|
||||
void start_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
|
||||
start_clipping(Rect(left, top, right - left, bottom - top));
|
||||
};
|
||||
|
||||
/** Add a rectangular region to the invalidation region
|
||||
* - This is usually called when an element has been modified
|
||||
*
|
||||
* @param[in] rect: Rectangle to add to the invalidation region
|
||||
*/
|
||||
void extend_clipping(Rect rect);
|
||||
void extend_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
|
||||
this->extend_clipping(Rect(left, top, right - left, bottom - top));
|
||||
};
|
||||
|
||||
/** substract a rectangular region to the invalidation region
|
||||
* - This is usually called when an element has been modified
|
||||
*
|
||||
* @param[in] rect: Rectangle to add to the invalidation region
|
||||
*/
|
||||
void shrink_clipping(Rect rect);
|
||||
void shrink_clipping(uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
|
||||
this->shrink_clipping(Rect(left, top, right - left, bottom - top));
|
||||
};
|
||||
|
||||
/** Reset the invalidation region
|
||||
*/
|
||||
void end_clipping();
|
||||
|
||||
/** Get the current the clipping rectangle
|
||||
*
|
||||
* return rect for active clipping region
|
||||
*/
|
||||
Rect get_clipping();
|
||||
|
||||
bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
|
||||
|
||||
protected:
|
||||
void vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg);
|
||||
|
||||
void do_update_();
|
||||
|
||||
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
|
||||
optional<display_writer_t> writer_{};
|
||||
DisplayPage *page_{nullptr};
|
||||
DisplayPage *previous_page_{nullptr};
|
||||
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
|
||||
bool auto_clear_enabled_{true};
|
||||
std::vector<Rect> clipping_rectangle_;
|
||||
};
|
||||
|
||||
class DisplayPage {
|
||||
public:
|
||||
DisplayPage(display_writer_t writer);
|
||||
// Temporary mapping to be removed once all lambdas are changed to use `display.DisplayRef`
|
||||
DisplayPage(const display_buffer_writer_t &writer)
|
||||
: DisplayPage([writer](Display &display) { return writer((display::DisplayBuffer &) display); }) {}
|
||||
void show();
|
||||
void show_next();
|
||||
void show_prev();
|
||||
void set_parent(Display *parent);
|
||||
void set_prev(DisplayPage *prev);
|
||||
void set_next(DisplayPage *next);
|
||||
const display_writer_t &get_writer() const;
|
||||
|
||||
protected:
|
||||
Display *parent_;
|
||||
display_writer_t writer_;
|
||||
DisplayPage *prev_{nullptr};
|
||||
DisplayPage *next_{nullptr};
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowAction : public Action<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(DisplayPage *, page)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto *page = this->page_.value(x...);
|
||||
if (page != nullptr) {
|
||||
page->show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowNextAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayPageShowNextAction(Display *buffer) : buffer_(buffer) {}
|
||||
|
||||
void play(Ts... x) override { this->buffer_->show_next_page(); }
|
||||
|
||||
Display *buffer_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowPrevAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayPageShowPrevAction(Display *buffer) : buffer_(buffer) {}
|
||||
|
||||
void play(Ts... x) override { this->buffer_->show_prev_page(); }
|
||||
|
||||
Display *buffer_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayIsDisplayingPageCondition : public Condition<Ts...> {
|
||||
public:
|
||||
DisplayIsDisplayingPageCondition(Display *parent) : parent_(parent) {}
|
||||
|
||||
void set_page(DisplayPage *page) { this->page_ = page; }
|
||||
bool check(Ts... x) override { return this->parent_->get_active_page() == this->page_; }
|
||||
|
||||
protected:
|
||||
Display *parent_;
|
||||
DisplayPage *page_;
|
||||
};
|
||||
|
||||
class DisplayOnPageChangeTrigger : public Trigger<DisplayPage *, DisplayPage *> {
|
||||
public:
|
||||
explicit DisplayOnPageChangeTrigger(Display *parent) { parent->add_on_page_change_trigger(this); }
|
||||
void process(DisplayPage *from, DisplayPage *to);
|
||||
void set_from(DisplayPage *p) { this->from_ = p; }
|
||||
void set_to(DisplayPage *p) { this->to_ = p; }
|
||||
|
||||
protected:
|
||||
DisplayPage *from_{nullptr};
|
||||
DisplayPage *to_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
|
@ -1,10 +1,8 @@
|
|||
#include "display_buffer.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
|
@ -12,9 +10,6 @@ namespace display {
|
|||
|
||||
static const char *const TAG = "display";
|
||||
|
||||
const Color COLOR_OFF(0, 0, 0, 0);
|
||||
const Color COLOR_ON(255, 255, 255, 255);
|
||||
|
||||
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
this->buffer_ = allocator.allocate(buffer_length);
|
||||
|
@ -25,8 +20,6 @@ void DisplayBuffer::init_internal_(uint32_t buffer_length) {
|
|||
this->clear();
|
||||
}
|
||||
|
||||
void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
|
||||
void DisplayBuffer::clear() { this->fill(COLOR_OFF); }
|
||||
int DisplayBuffer::get_width() {
|
||||
switch (this->rotation_) {
|
||||
case DISPLAY_ROTATION_90_DEGREES:
|
||||
|
@ -38,6 +31,7 @@ int DisplayBuffer::get_width() {
|
|||
return this->get_width_internal();
|
||||
}
|
||||
}
|
||||
|
||||
int DisplayBuffer::get_height() {
|
||||
switch (this->rotation_) {
|
||||
case DISPLAY_ROTATION_0_DEGREES:
|
||||
|
@ -49,7 +43,7 @@ int DisplayBuffer::get_height() {
|
|||
return this->get_width_internal();
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
|
||||
|
||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
return; // NOLINT
|
||||
|
@ -73,333 +67,6 @@ void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
|
|||
this->draw_absolute_pixel_internal(x, y, color);
|
||||
App.feed_wdt();
|
||||
}
|
||||
void HOT DisplayBuffer::line(int x1, int y1, int x2, int y2, Color color) {
|
||||
const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
|
||||
const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
|
||||
int32_t err = dx + dy;
|
||||
|
||||
while (true) {
|
||||
this->draw_pixel_at(x1, y1, color);
|
||||
if (x1 == x2 && y1 == y2)
|
||||
break;
|
||||
int32_t e2 = 2 * err;
|
||||
if (e2 >= dy) {
|
||||
err += dy;
|
||||
x1 += sx;
|
||||
}
|
||||
if (e2 <= dx) {
|
||||
err += dx;
|
||||
y1 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
void HOT DisplayBuffer::horizontal_line(int x, int y, int width, Color color) {
|
||||
// Future: Could be made more efficient by manipulating buffer directly in certain rotations.
|
||||
for (int i = x; i < x + width; i++)
|
||||
this->draw_pixel_at(i, y, color);
|
||||
}
|
||||
void HOT DisplayBuffer::vertical_line(int x, int y, int height, Color color) {
|
||||
// Future: Could be made more efficient by manipulating buffer directly in certain rotations.
|
||||
for (int i = y; i < y + height; i++)
|
||||
this->draw_pixel_at(x, i, color);
|
||||
}
|
||||
void DisplayBuffer::rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
this->horizontal_line(x1, y1, width, color);
|
||||
this->horizontal_line(x1, y1 + height - 1, width, color);
|
||||
this->vertical_line(x1, y1, height, color);
|
||||
this->vertical_line(x1 + width - 1, y1, height, color);
|
||||
}
|
||||
void DisplayBuffer::filled_rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
// Future: Use vertical_line and horizontal_line methods depending on rotation to reduce memory accesses.
|
||||
for (int i = y1; i < y1 + height; i++) {
|
||||
this->horizontal_line(x1, i, width, color);
|
||||
}
|
||||
}
|
||||
void HOT DisplayBuffer::circle(int center_x, int center_xy, int radius, Color color) {
|
||||
int dx = -radius;
|
||||
int dy = 0;
|
||||
int err = 2 - 2 * radius;
|
||||
int e2;
|
||||
|
||||
do {
|
||||
this->draw_pixel_at(center_x - dx, center_xy + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_xy + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_xy - dy, color);
|
||||
this->draw_pixel_at(center_x - dx, center_xy - dy, color);
|
||||
e2 = err;
|
||||
if (e2 < dy) {
|
||||
err += ++dy * 2 + 1;
|
||||
if (-dx == dy && e2 <= dx) {
|
||||
e2 = 0;
|
||||
}
|
||||
}
|
||||
if (e2 > dx) {
|
||||
err += ++dx * 2 + 1;
|
||||
}
|
||||
} while (dx <= 0);
|
||||
}
|
||||
void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, Color color) {
|
||||
int dx = -int32_t(radius);
|
||||
int dy = 0;
|
||||
int err = 2 - 2 * radius;
|
||||
int e2;
|
||||
|
||||
do {
|
||||
this->draw_pixel_at(center_x - dx, center_y + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_y + dy, color);
|
||||
this->draw_pixel_at(center_x + dx, center_y - dy, color);
|
||||
this->draw_pixel_at(center_x - dx, center_y - dy, color);
|
||||
int hline_width = 2 * (-dx) + 1;
|
||||
this->horizontal_line(center_x + dx, center_y + dy, hline_width, color);
|
||||
this->horizontal_line(center_x + dx, center_y - dy, hline_width, color);
|
||||
e2 = err;
|
||||
if (e2 < dy) {
|
||||
err += ++dy * 2 + 1;
|
||||
if (-dx == dy && e2 <= dx) {
|
||||
e2 = 0;
|
||||
}
|
||||
}
|
||||
if (e2 > dx) {
|
||||
err += ++dx * 2 + 1;
|
||||
}
|
||||
} while (dx <= 0);
|
||||
}
|
||||
|
||||
void DisplayBuffer::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) {
|
||||
int x_start, y_start;
|
||||
int width, height;
|
||||
this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height);
|
||||
font->print(x_start, y_start, this, color, text);
|
||||
}
|
||||
void DisplayBuffer::vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format,
|
||||
va_list arg) {
|
||||
char buffer[256];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
if (ret > 0)
|
||||
this->print(x, y, font, color, align, buffer);
|
||||
}
|
||||
|
||||
void DisplayBuffer::image(int x, int y, BaseImage *image, Color color_on, Color color_off) {
|
||||
this->image(x, y, image, ImageAlign::TOP_LEFT, color_on, color_off);
|
||||
}
|
||||
|
||||
void DisplayBuffer::image(int x, int y, BaseImage *image, ImageAlign align, Color color_on, Color color_off) {
|
||||
auto x_align = ImageAlign(int(align) & (int(ImageAlign::HORIZONTAL_ALIGNMENT)));
|
||||
auto y_align = ImageAlign(int(align) & (int(ImageAlign::VERTICAL_ALIGNMENT)));
|
||||
|
||||
switch (x_align) {
|
||||
case ImageAlign::RIGHT:
|
||||
x -= image->get_width();
|
||||
break;
|
||||
case ImageAlign::CENTER_HORIZONTAL:
|
||||
x -= image->get_width() / 2;
|
||||
break;
|
||||
case ImageAlign::LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (y_align) {
|
||||
case ImageAlign::BOTTOM:
|
||||
y -= image->get_height();
|
||||
break;
|
||||
case ImageAlign::CENTER_VERTICAL:
|
||||
y -= image->get_height() / 2;
|
||||
break;
|
||||
case ImageAlign::TOP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image->draw(x, y, this, color_on, color_off);
|
||||
}
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
void DisplayBuffer::graph(int x, int y, graph::Graph *graph, Color color_on) { graph->draw(this, x, y, color_on); }
|
||||
void DisplayBuffer::legend(int x, int y, graph::Graph *graph, Color color_on) {
|
||||
graph->draw_legend(this, x, y, color_on);
|
||||
}
|
||||
#endif // USE_GRAPH
|
||||
|
||||
#ifdef USE_QR_CODE
|
||||
void DisplayBuffer::qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on, int scale) {
|
||||
qr_code->draw(this, x, y, color_on, scale);
|
||||
}
|
||||
#endif // USE_QR_CODE
|
||||
|
||||
void DisplayBuffer::get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1,
|
||||
int *width, int *height) {
|
||||
int x_offset, baseline;
|
||||
font->measure(text, width, &x_offset, &baseline, height);
|
||||
|
||||
auto x_align = TextAlign(int(align) & 0x18);
|
||||
auto y_align = TextAlign(int(align) & 0x07);
|
||||
|
||||
switch (x_align) {
|
||||
case TextAlign::RIGHT:
|
||||
*x1 = x - *width;
|
||||
break;
|
||||
case TextAlign::CENTER_HORIZONTAL:
|
||||
*x1 = x - (*width) / 2;
|
||||
break;
|
||||
case TextAlign::LEFT:
|
||||
default:
|
||||
// LEFT
|
||||
*x1 = x;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (y_align) {
|
||||
case TextAlign::BOTTOM:
|
||||
*y1 = y - *height;
|
||||
break;
|
||||
case TextAlign::BASELINE:
|
||||
*y1 = y - baseline;
|
||||
break;
|
||||
case TextAlign::CENTER_VERTICAL:
|
||||
*y1 = y - (*height) / 2;
|
||||
break;
|
||||
case TextAlign::TOP:
|
||||
default:
|
||||
*y1 = y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::print(int x, int y, BaseFont *font, Color color, const char *text) {
|
||||
this->print(x, y, font, color, TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
void DisplayBuffer::print(int x, int y, BaseFont *font, TextAlign align, const char *text) {
|
||||
this->print(x, y, font, COLOR_ON, align, text);
|
||||
}
|
||||
void DisplayBuffer::print(int x, int y, BaseFont *font, const char *text) {
|
||||
this->print(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
void DisplayBuffer::printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, color, align, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void DisplayBuffer::printf(int x, int y, BaseFont *font, Color color, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, color, TextAlign::TOP_LEFT, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void DisplayBuffer::printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, COLOR_ON, align, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void DisplayBuffer::printf(int x, int y, BaseFont *font, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
this->vprintf_(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
void DisplayBuffer::set_writer(display_writer_t &&writer) { this->writer_ = writer; }
|
||||
void DisplayBuffer::set_pages(std::vector<DisplayPage *> pages) {
|
||||
for (auto *page : pages)
|
||||
page->set_parent(this);
|
||||
|
||||
for (uint32_t i = 0; i < pages.size() - 1; i++) {
|
||||
pages[i]->set_next(pages[i + 1]);
|
||||
pages[i + 1]->set_prev(pages[i]);
|
||||
}
|
||||
pages[0]->set_prev(pages[pages.size() - 1]);
|
||||
pages[pages.size() - 1]->set_next(pages[0]);
|
||||
this->show_page(pages[0]);
|
||||
}
|
||||
void DisplayBuffer::show_page(DisplayPage *page) {
|
||||
this->previous_page_ = this->page_;
|
||||
this->page_ = page;
|
||||
if (this->previous_page_ != this->page_) {
|
||||
for (auto *t : on_page_change_triggers_)
|
||||
t->process(this->previous_page_, this->page_);
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::show_next_page() { this->page_->show_next(); }
|
||||
void DisplayBuffer::show_prev_page() { this->page_->show_prev(); }
|
||||
void DisplayBuffer::do_update_() {
|
||||
if (this->auto_clear_enabled_) {
|
||||
this->clear();
|
||||
}
|
||||
if (this->page_ != nullptr) {
|
||||
this->page_->get_writer()(*this);
|
||||
} else if (this->writer_.has_value()) {
|
||||
(*this->writer_)(*this);
|
||||
}
|
||||
// remove all not ended clipping regions
|
||||
while (is_clipping()) {
|
||||
end_clipping();
|
||||
}
|
||||
}
|
||||
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
|
||||
if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
|
||||
this->trigger(from, to);
|
||||
}
|
||||
void DisplayBuffer::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format,
|
||||
ESPTime time) {
|
||||
char buffer[64];
|
||||
size_t ret = time.strftime(buffer, sizeof(buffer), format);
|
||||
if (ret > 0)
|
||||
this->print(x, y, font, color, align, buffer);
|
||||
}
|
||||
void DisplayBuffer::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) {
|
||||
this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time);
|
||||
}
|
||||
void DisplayBuffer::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) {
|
||||
this->strftime(x, y, font, COLOR_ON, align, format, time);
|
||||
}
|
||||
void DisplayBuffer::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) {
|
||||
this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time);
|
||||
}
|
||||
|
||||
void DisplayBuffer::start_clipping(Rect rect) {
|
||||
if (!this->clipping_rectangle_.empty()) {
|
||||
Rect r = this->clipping_rectangle_.back();
|
||||
rect.shrink(r);
|
||||
}
|
||||
this->clipping_rectangle_.push_back(rect);
|
||||
}
|
||||
void DisplayBuffer::end_clipping() {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
ESP_LOGE(TAG, "clear: Clipping is not set.");
|
||||
} else {
|
||||
this->clipping_rectangle_.pop_back();
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::extend_clipping(Rect add_rect) {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
ESP_LOGE(TAG, "add: Clipping is not set.");
|
||||
} else {
|
||||
this->clipping_rectangle_.back().extend(add_rect);
|
||||
}
|
||||
}
|
||||
void DisplayBuffer::shrink_clipping(Rect add_rect) {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
ESP_LOGE(TAG, "add: Clipping is not set.");
|
||||
} else {
|
||||
this->clipping_rectangle_.back().shrink(add_rect);
|
||||
}
|
||||
}
|
||||
Rect DisplayBuffer::get_clipping() {
|
||||
if (this->clipping_rectangle_.empty()) {
|
||||
return Rect();
|
||||
} else {
|
||||
return this->clipping_rectangle_.back();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
|
||||
void DisplayPage::show() { this->parent_->show_page(this); }
|
||||
void DisplayPage::show_next() { this->next_->show(); }
|
||||
void DisplayPage::show_prev() { this->prev_->show(); }
|
||||
void DisplayPage::set_parent(DisplayBuffer *parent) { this->parent_ = parent; }
|
||||
void DisplayPage::set_prev(DisplayPage *prev) { this->prev_ = prev; }
|
||||
void DisplayPage::set_next(DisplayPage *next) { this->next_ = next; }
|
||||
const display_writer_t &DisplayPage::get_writer() const { return this->writer_; }
|
||||
|
||||
} // namespace display
|
||||
} // namespace esphome
|
||||
|
|
|
@ -2,568 +2,35 @@
|
|||
|
||||
#include <cstdarg>
|
||||
#include <vector>
|
||||
#include "rect.h"
|
||||
|
||||
#include "display.h"
|
||||
#include "display_color_utils.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
#include "esphome/components/graph/graph.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_QR_CODE
|
||||
#include "esphome/components/qr_code/qr_code.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
/** TextAlign is used to tell the display class how to position a piece of text. By default
|
||||
* the coordinates you enter for the print*() functions take the upper left corner of the text
|
||||
* as the "anchor" point. You can customize this behavior to, for example, make the coordinates
|
||||
* refer to the *center* of the text.
|
||||
*
|
||||
* All text alignments consist of an X and Y-coordinate alignment. For the alignment along the X-axis
|
||||
* these options are allowed:
|
||||
*
|
||||
* - LEFT (x-coordinate of anchor point is on left)
|
||||
* - CENTER_HORIZONTAL (x-coordinate of anchor point is in the horizontal center of the text)
|
||||
* - RIGHT (x-coordinate of anchor point is on right)
|
||||
*
|
||||
* For the Y-Axis alignment these options are allowed:
|
||||
*
|
||||
* - TOP (y-coordinate of anchor is on the top of the text)
|
||||
* - CENTER_VERTICAL (y-coordinate of anchor is in the vertical center of the text)
|
||||
* - BASELINE (y-coordinate of anchor is on the baseline of the text)
|
||||
* - BOTTOM (y-coordinate of anchor is on the bottom of the text)
|
||||
*
|
||||
* These options are then combined to create combined TextAlignment options like:
|
||||
* - TOP_LEFT (default)
|
||||
* - CENTER (anchor point is in the middle of the text bounds)
|
||||
* - ...
|
||||
*/
|
||||
enum class TextAlign {
|
||||
TOP = 0x00,
|
||||
CENTER_VERTICAL = 0x01,
|
||||
BASELINE = 0x02,
|
||||
BOTTOM = 0x04,
|
||||
|
||||
LEFT = 0x00,
|
||||
CENTER_HORIZONTAL = 0x08,
|
||||
RIGHT = 0x10,
|
||||
|
||||
TOP_LEFT = TOP | LEFT,
|
||||
TOP_CENTER = TOP | CENTER_HORIZONTAL,
|
||||
TOP_RIGHT = TOP | RIGHT,
|
||||
|
||||
CENTER_LEFT = CENTER_VERTICAL | LEFT,
|
||||
CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL,
|
||||
CENTER_RIGHT = CENTER_VERTICAL | RIGHT,
|
||||
|
||||
BASELINE_LEFT = BASELINE | LEFT,
|
||||
BASELINE_CENTER = BASELINE | CENTER_HORIZONTAL,
|
||||
BASELINE_RIGHT = BASELINE | RIGHT,
|
||||
|
||||
BOTTOM_LEFT = BOTTOM | LEFT,
|
||||
BOTTOM_CENTER = BOTTOM | CENTER_HORIZONTAL,
|
||||
BOTTOM_RIGHT = BOTTOM | RIGHT,
|
||||
};
|
||||
|
||||
/** ImageAlign is used to tell the display class how to position a image. By default
|
||||
* the coordinates you enter for the image() functions take the upper left corner of the image
|
||||
* as the "anchor" point. You can customize this behavior to, for example, make the coordinates
|
||||
* refer to the *center* of the image.
|
||||
*
|
||||
* All image alignments consist of an X and Y-coordinate alignment. For the alignment along the X-axis
|
||||
* these options are allowed:
|
||||
*
|
||||
* - LEFT (x-coordinate of anchor point is on left)
|
||||
* - CENTER_HORIZONTAL (x-coordinate of anchor point is in the horizontal center of the image)
|
||||
* - RIGHT (x-coordinate of anchor point is on right)
|
||||
*
|
||||
* For the Y-Axis alignment these options are allowed:
|
||||
*
|
||||
* - TOP (y-coordinate of anchor is on the top of the image)
|
||||
* - CENTER_VERTICAL (y-coordinate of anchor is in the vertical center of the image)
|
||||
* - BOTTOM (y-coordinate of anchor is on the bottom of the image)
|
||||
*
|
||||
* These options are then combined to create combined TextAlignment options like:
|
||||
* - TOP_LEFT (default)
|
||||
* - CENTER (anchor point is in the middle of the image bounds)
|
||||
* - ...
|
||||
*/
|
||||
enum class ImageAlign {
|
||||
TOP = 0x00,
|
||||
CENTER_VERTICAL = 0x01,
|
||||
BOTTOM = 0x02,
|
||||
|
||||
LEFT = 0x00,
|
||||
CENTER_HORIZONTAL = 0x04,
|
||||
RIGHT = 0x08,
|
||||
|
||||
TOP_LEFT = TOP | LEFT,
|
||||
TOP_CENTER = TOP | CENTER_HORIZONTAL,
|
||||
TOP_RIGHT = TOP | RIGHT,
|
||||
|
||||
CENTER_LEFT = CENTER_VERTICAL | LEFT,
|
||||
CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL,
|
||||
CENTER_RIGHT = CENTER_VERTICAL | RIGHT,
|
||||
|
||||
BOTTOM_LEFT = BOTTOM | LEFT,
|
||||
BOTTOM_CENTER = BOTTOM | CENTER_HORIZONTAL,
|
||||
BOTTOM_RIGHT = BOTTOM | RIGHT,
|
||||
|
||||
HORIZONTAL_ALIGNMENT = LEFT | CENTER_HORIZONTAL | RIGHT,
|
||||
VERTICAL_ALIGNMENT = TOP | CENTER_VERTICAL | BOTTOM
|
||||
};
|
||||
|
||||
enum DisplayType {
|
||||
DISPLAY_TYPE_BINARY = 1,
|
||||
DISPLAY_TYPE_GRAYSCALE = 2,
|
||||
DISPLAY_TYPE_COLOR = 3,
|
||||
};
|
||||
|
||||
enum DisplayRotation {
|
||||
DISPLAY_ROTATION_0_DEGREES = 0,
|
||||
DISPLAY_ROTATION_90_DEGREES = 90,
|
||||
DISPLAY_ROTATION_180_DEGREES = 180,
|
||||
DISPLAY_ROTATION_270_DEGREES = 270,
|
||||
};
|
||||
|
||||
class DisplayBuffer;
|
||||
class DisplayPage;
|
||||
class DisplayOnPageChangeTrigger;
|
||||
|
||||
using display_writer_t = std::function<void(DisplayBuffer &)>;
|
||||
|
||||
#define LOG_DISPLAY(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, prefix type); \
|
||||
ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, (obj)->rotation_); \
|
||||
ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \
|
||||
}
|
||||
|
||||
/// Turn the pixel OFF.
|
||||
extern const Color COLOR_OFF;
|
||||
/// Turn the pixel ON.
|
||||
extern const Color COLOR_ON;
|
||||
|
||||
class BaseImage {
|
||||
class DisplayBuffer : public Display {
|
||||
public:
|
||||
virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0;
|
||||
virtual int get_width() const = 0;
|
||||
virtual int get_height() const = 0;
|
||||
};
|
||||
|
||||
class BaseFont {
|
||||
public:
|
||||
virtual void print(int x, int y, DisplayBuffer *display, Color color, const char *text) = 0;
|
||||
virtual void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) = 0;
|
||||
};
|
||||
|
||||
class DisplayBuffer {
|
||||
public:
|
||||
/// Fill the entire screen with the given color.
|
||||
virtual void fill(Color color);
|
||||
/// Clear the entire screen by filling it with OFF pixels.
|
||||
void clear();
|
||||
|
||||
/// Get the width of the image in pixels with rotation applied.
|
||||
int get_width();
|
||||
int get_width() override;
|
||||
/// Get the height of the image in pixels with rotation applied.
|
||||
int get_height();
|
||||
int get_height() override;
|
||||
|
||||
/// Set a single pixel at the specified coordinates to the given color.
|
||||
void draw_pixel_at(int x, int y, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a straight line from the point [x1,y1] to [x2,y2] with the given color.
|
||||
void line(int x1, int y1, int x2, int y2, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a horizontal line from the point [x,y] to [x+width,y] with the given color.
|
||||
void horizontal_line(int x, int y, int width, Color color = COLOR_ON);
|
||||
|
||||
/// Draw a vertical line from the point [x,y] to [x,y+width] with the given color.
|
||||
void vertical_line(int x, int y, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a rectangle with the top left point at [x1,y1] and the bottom right point at
|
||||
/// [x1+width,y1+height].
|
||||
void rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a rectangle with the top left point at [x1,y1] and the bottom right point at [x1+width,y1+height].
|
||||
void filled_rectangle(int x1, int y1, int width, int height, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void circle(int center_x, int center_xy, int radius, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text);
|
||||
|
||||
/** Print `text` with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, Color color, const char *text);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, TextAlign align, const char *text);
|
||||
|
||||
/** Print `text` with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param text The text to draw.
|
||||
*/
|
||||
void print(int x, int y, BaseFont *font, const char *text);
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...)
|
||||
__attribute__((format(printf, 7, 8)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, Color color, const char *format, ...) __attribute__((format(printf, 6, 7)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param format The format to use.
|
||||
* @param ... The arguments to use for the text formatting.
|
||||
*/
|
||||
void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time)
|
||||
__attribute__((format(strftime, 7, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param color The color to draw the text with.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time)
|
||||
__attribute__((format(strftime, 6, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
* @param y The y coordinate of the text alignment anchor point.
|
||||
* @param font The font to draw the text with.
|
||||
* @param align The alignment of the text.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time)
|
||||
__attribute__((format(strftime, 6, 0)));
|
||||
|
||||
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param font The font to draw the text with.
|
||||
* @param format The strftime format to use.
|
||||
* @param time The time to format.
|
||||
*/
|
||||
void strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) __attribute__((format(strftime, 5, 0)));
|
||||
|
||||
/** Draw the `image` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param image The image to draw.
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
* @param color_off The color to replace in binary images for the off bits.
|
||||
*/
|
||||
void image(int x, int y, BaseImage *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||
|
||||
/** Draw the `image` at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param image The image to draw.
|
||||
* @param align The alignment of the image.
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
* @param color_off The color to replace in binary images for the off bits.
|
||||
*/
|
||||
void image(int x, int y, BaseImage *image, ImageAlign align, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||
|
||||
#ifdef USE_GRAPH
|
||||
/** Draw the `graph` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param graph The graph id to draw
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
*/
|
||||
void graph(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON);
|
||||
|
||||
/** Draw the `legend` for graph with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param graph The graph id for which the legend applies to
|
||||
* @param graph The graph id for which the legend applies to
|
||||
* @param graph The graph id for which the legend applies to
|
||||
* @param name_font The font used for the trace name
|
||||
* @param value_font The font used for the trace value and units
|
||||
* @param color_on The color of the border
|
||||
*/
|
||||
void legend(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON);
|
||||
#endif // USE_GRAPH
|
||||
|
||||
#ifdef USE_QR_CODE
|
||||
/** Draw the `qr_code` with the top-left corner at [x,y] to the screen.
|
||||
*
|
||||
* @param x The x coordinate of the upper left corner.
|
||||
* @param y The y coordinate of the upper left corner.
|
||||
* @param qr_code The qr_code to draw
|
||||
* @param color_on The color to replace in binary images for the on bits.
|
||||
*/
|
||||
void qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on = COLOR_ON, int scale = 1);
|
||||
#endif
|
||||
|
||||
/** Get the text bounds of the given string.
|
||||
*
|
||||
* @param x The x coordinate to place the string at, can be 0 if only interested in dimensions.
|
||||
* @param y The y coordinate to place the string at, can be 0 if only interested in dimensions.
|
||||
* @param text The text to measure.
|
||||
* @param font The font to measure the text bounds with.
|
||||
* @param align The alignment of the text. Set to TextAlign::TOP_LEFT if only interested in dimensions.
|
||||
* @param x1 A pointer to store the returned x coordinate of the upper left corner in.
|
||||
* @param y1 A pointer to store the returned y coordinate of the upper left corner in.
|
||||
* @param width A pointer to store the returned text width in.
|
||||
* @param height A pointer to store the returned text height in.
|
||||
*/
|
||||
void get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1, int *width,
|
||||
int *height);
|
||||
|
||||
/// Internal method to set the display writer lambda.
|
||||
void set_writer(display_writer_t &&writer);
|
||||
|
||||
void show_page(DisplayPage *page);
|
||||
void show_next_page();
|
||||
void show_prev_page();
|
||||
|
||||
void set_pages(std::vector<DisplayPage *> pages);
|
||||
|
||||
const DisplayPage *get_active_page() const { return this->page_; }
|
||||
|
||||
void add_on_page_change_trigger(DisplayOnPageChangeTrigger *t) { this->on_page_change_triggers_.push_back(t); }
|
||||
|
||||
/// Internal method to set the display rotation with.
|
||||
void set_rotation(DisplayRotation rotation);
|
||||
|
||||
// Internal method to set display auto clearing.
|
||||
void set_auto_clear(bool auto_clear_enabled) { this->auto_clear_enabled_ = auto_clear_enabled; }
|
||||
void draw_pixel_at(int x, int y, Color color) override;
|
||||
|
||||
virtual int get_height_internal() = 0;
|
||||
virtual int get_width_internal() = 0;
|
||||
DisplayRotation get_rotation() const { return this->rotation_; }
|
||||
|
||||
/** Get the type of display that the buffer corresponds to. In case of dynamically configurable displays,
|
||||
* returns the type the display is currently configured to.
|
||||
*/
|
||||
virtual DisplayType get_display_type() = 0;
|
||||
|
||||
/** Set the clipping rectangle for further drawing
|
||||
*
|
||||
* @param[in] rect: Pointer to Rect for clipping (or NULL for entire screen)
|
||||
*
|
||||
* return true if success, false if error
|
||||
*/
|
||||
void start_clipping(Rect rect);
|
||||
void start_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
|
||||
start_clipping(Rect(left, top, right - left, bottom - top));
|
||||
};
|
||||
|
||||
/** Add a rectangular region to the invalidation region
|
||||
* - This is usually called when an element has been modified
|
||||
*
|
||||
* @param[in] rect: Rectangle to add to the invalidation region
|
||||
*/
|
||||
void extend_clipping(Rect rect);
|
||||
void extend_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
|
||||
this->extend_clipping(Rect(left, top, right - left, bottom - top));
|
||||
};
|
||||
|
||||
/** substract a rectangular region to the invalidation region
|
||||
* - This is usually called when an element has been modified
|
||||
*
|
||||
* @param[in] rect: Rectangle to add to the invalidation region
|
||||
*/
|
||||
void shrink_clipping(Rect rect);
|
||||
void shrink_clipping(uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
|
||||
this->shrink_clipping(Rect(left, top, right - left, bottom - top));
|
||||
};
|
||||
|
||||
/** Reset the invalidation region
|
||||
*/
|
||||
void end_clipping();
|
||||
|
||||
/** Get the current the clipping rectangle
|
||||
*
|
||||
* return rect for active clipping region
|
||||
*/
|
||||
Rect get_clipping();
|
||||
|
||||
bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
|
||||
|
||||
protected:
|
||||
void vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg);
|
||||
|
||||
virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0;
|
||||
|
||||
void init_internal_(uint32_t buffer_length);
|
||||
|
||||
void do_update_();
|
||||
|
||||
uint8_t *buffer_{nullptr};
|
||||
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
|
||||
optional<display_writer_t> writer_{};
|
||||
DisplayPage *page_{nullptr};
|
||||
DisplayPage *previous_page_{nullptr};
|
||||
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
|
||||
bool auto_clear_enabled_{true};
|
||||
std::vector<Rect> clipping_rectangle_;
|
||||
};
|
||||
|
||||
class DisplayPage {
|
||||
public:
|
||||
DisplayPage(display_writer_t writer);
|
||||
void show();
|
||||
void show_next();
|
||||
void show_prev();
|
||||
void set_parent(DisplayBuffer *parent);
|
||||
void set_prev(DisplayPage *prev);
|
||||
void set_next(DisplayPage *next);
|
||||
const display_writer_t &get_writer() const;
|
||||
|
||||
protected:
|
||||
DisplayBuffer *parent_;
|
||||
display_writer_t writer_;
|
||||
DisplayPage *prev_{nullptr};
|
||||
DisplayPage *next_{nullptr};
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowAction : public Action<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(DisplayPage *, page)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto *page = this->page_.value(x...);
|
||||
if (page != nullptr) {
|
||||
page->show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowNextAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayPageShowNextAction(DisplayBuffer *buffer) : buffer_(buffer) {}
|
||||
|
||||
void play(Ts... x) override { this->buffer_->show_next_page(); }
|
||||
|
||||
DisplayBuffer *buffer_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayPageShowPrevAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayPageShowPrevAction(DisplayBuffer *buffer) : buffer_(buffer) {}
|
||||
|
||||
void play(Ts... x) override { this->buffer_->show_prev_page(); }
|
||||
|
||||
DisplayBuffer *buffer_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayIsDisplayingPageCondition : public Condition<Ts...> {
|
||||
public:
|
||||
DisplayIsDisplayingPageCondition(DisplayBuffer *parent) : parent_(parent) {}
|
||||
|
||||
void set_page(DisplayPage *page) { this->page_ = page; }
|
||||
bool check(Ts... x) override { return this->parent_->get_active_page() == this->page_; }
|
||||
|
||||
protected:
|
||||
DisplayBuffer *parent_;
|
||||
DisplayPage *page_;
|
||||
};
|
||||
|
||||
class DisplayOnPageChangeTrigger : public Trigger<DisplayPage *, DisplayPage *> {
|
||||
public:
|
||||
explicit DisplayOnPageChangeTrigger(DisplayBuffer *parent) { parent->add_on_page_change_trigger(this); }
|
||||
void process(DisplayPage *from, DisplayPage *to);
|
||||
void set_from(DisplayPage *p) { this->from_ = p; }
|
||||
void set_to(DisplayPage *p) { this->to_ = p; }
|
||||
|
||||
protected:
|
||||
DisplayPage *from_{nullptr};
|
||||
DisplayPage *to_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
|
|
|
@ -19,6 +19,7 @@ CONF_CRC_CHECK = "crc_check"
|
|||
CONF_DECRYPTION_KEY = "decryption_key"
|
||||
CONF_DSMR_ID = "dsmr_id"
|
||||
CONF_GAS_MBUS_ID = "gas_mbus_id"
|
||||
CONF_WATER_MBUS_ID = "water_mbus_id"
|
||||
CONF_MAX_TELEGRAM_LENGTH = "max_telegram_length"
|
||||
CONF_REQUEST_INTERVAL = "request_interval"
|
||||
CONF_REQUEST_PIN = "request_pin"
|
||||
|
@ -53,6 +54,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Optional(CONF_DECRYPTION_KEY): _validate_key,
|
||||
cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean,
|
||||
cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
|
||||
cv.Optional(CONF_WATER_MBUS_ID, default=2): cv.int_,
|
||||
cv.Optional(CONF_MAX_TELEGRAM_LENGTH, default=1500): cv.int_,
|
||||
cv.Optional(CONF_REQUEST_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
|
@ -82,9 +84,10 @@ async def to_code(config):
|
|||
cg.add(var.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT].total_milliseconds))
|
||||
|
||||
cg.add_build_flag("-DDSMR_GAS_MBUS_ID=" + str(config[CONF_GAS_MBUS_ID]))
|
||||
cg.add_build_flag("-DDSMR_WATER_MBUS_ID=" + str(config[CONF_WATER_MBUS_ID]))
|
||||
|
||||
# DSMR Parser
|
||||
cg.add_library("glmnet/Dsmr", "0.5")
|
||||
cg.add_library("glmnet/Dsmr", "0.7")
|
||||
|
||||
# Crypto
|
||||
cg.add_library("rweather/Crypto", "0.4.0")
|
||||
|
|
|
@ -8,6 +8,7 @@ from esphome.const import (
|
|||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
DEVICE_CLASS_WATER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_AMPERE,
|
||||
|
@ -236,6 +237,12 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
device_class=DEVICE_CLASS_GAS,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("water_delivered"): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CUBIC_METER,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_WATER,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
|
|
@ -547,6 +547,8 @@ def copy_files():
|
|||
CORE.relative_build_path(f"components/{name}"),
|
||||
dirs_exist_ok=True,
|
||||
ignore=shutil.ignore_patterns(".git", ".github"),
|
||||
symlinks=True,
|
||||
ignore_dangling_symlinks=True,
|
||||
)
|
||||
|
||||
dir = os.path.dirname(__file__)
|
||||
|
|
|
@ -55,3 +55,4 @@ async def to_code(config):
|
|||
|
||||
if CORE.using_esp_idf:
|
||||
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
|
||||
add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True)
|
||||
|
|
|
@ -19,7 +19,11 @@
|
|||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_eth.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#include "esp_eth_phy_802_3.h"
|
||||
#else
|
||||
#include "eth_phy_regs_struct.h"
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
|
@ -170,7 +174,11 @@ static esp_err_t jl1101_reset_hw(esp_eth_phy_t *phy) {
|
|||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *nego_state) {
|
||||
#else
|
||||
static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy) {
|
||||
#endif
|
||||
phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
|
||||
esp_eth_mediator_t *eth = jl1101->eth;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
|
@ -285,7 +293,11 @@ static esp_err_t jl1101_init(esp_eth_phy_t *phy) {
|
|||
esp_eth_mediator_t *eth = jl1101->eth;
|
||||
// Detect PHY address
|
||||
if (jl1101->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
PHY_CHECK(esp_eth_phy_802_3_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err);
|
||||
#else
|
||||
PHY_CHECK(esp_eth_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err);
|
||||
#endif
|
||||
}
|
||||
/* Power on Ethernet PHY */
|
||||
PHY_CHECK(jl1101_pwrctl(phy, true) == ESP_OK, "power control failed", err);
|
||||
|
@ -324,7 +336,11 @@ esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config) {
|
|||
jl1101->parent.init = jl1101_init;
|
||||
jl1101->parent.deinit = jl1101_deinit;
|
||||
jl1101->parent.set_mediator = jl1101_set_mediator;
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
jl1101->parent.autonego_ctrl = jl1101_negotiate;
|
||||
#else
|
||||
jl1101->parent.negotiate = jl1101_negotiate;
|
||||
#endif
|
||||
jl1101->parent.get_link = jl1101_get_link;
|
||||
jl1101->parent.pwrctl = jl1101_pwrctl;
|
||||
jl1101->parent.get_addr = jl1101_get_addr;
|
||||
|
|
|
@ -41,18 +41,27 @@ void EthernetComponent::setup() {
|
|||
this->eth_netif_ = esp_netif_new(&cfg);
|
||||
|
||||
// Init MAC and PHY configs to default
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
|
||||
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_;
|
||||
esp32_emac_config.smi_mdio_gpio_num = this->mdio_pin_;
|
||||
esp32_emac_config.clock_config.rmii.clock_mode = this->clk_mode_;
|
||||
esp32_emac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
|
||||
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
#else
|
||||
mac_config.smi_mdc_gpio_num = this->mdc_pin_;
|
||||
mac_config.smi_mdio_gpio_num = this->mdio_pin_;
|
||||
mac_config.clock_config.rmii.clock_mode = this->clk_mode_;
|
||||
mac_config.clock_config.rmii.clock_gpio = this->clk_gpio_;
|
||||
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
|
||||
#endif
|
||||
|
||||
switch (this->type_) {
|
||||
case ETHERNET_TYPE_LAN8720: {
|
||||
|
@ -76,7 +85,11 @@ void EthernetComponent::setup() {
|
|||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_KSZ8081: {
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
this->phy_ = esp_eth_phy_new_ksz80xx(&phy_config);
|
||||
#else
|
||||
this->phy_ = esp_eth_phy_new_ksz8081(&phy_config);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -221,13 +234,13 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base
|
|||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "[Ethernet event] %s (num=%d)", event_name, event);
|
||||
ESP_LOGV(TAG, "[Ethernet event] %s (num=%" PRId32 ")", event_name, event);
|
||||
}
|
||||
|
||||
void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data) {
|
||||
global_eth_component->connected_ = true;
|
||||
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%d)", event_id);
|
||||
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%" PRId32 ")", event_id);
|
||||
}
|
||||
|
||||
void EthernetComponent::start_connect_() {
|
||||
|
|
|
@ -7,7 +7,6 @@ import re
|
|||
import requests
|
||||
|
||||
from esphome import core
|
||||
from esphome.components import display
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.helpers import copy_file_if_changed
|
||||
|
@ -29,9 +28,11 @@ DOMAIN = "font"
|
|||
DEPENDENCIES = ["display"]
|
||||
MULTI_CONF = True
|
||||
|
||||
Font = display.display_ns.class_("Font")
|
||||
Glyph = display.display_ns.class_("Glyph")
|
||||
GlyphData = display.display_ns.struct("GlyphData")
|
||||
font_ns = cg.esphome_ns.namespace("font")
|
||||
|
||||
Font = font_ns.class_("Font")
|
||||
Glyph = font_ns.class_("Glyph")
|
||||
GlyphData = font_ns.struct("GlyphData")
|
||||
|
||||
|
||||
def validate_glyphs(value):
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
namespace font {
|
||||
|
||||
static const char *const TAG = "display";
|
||||
static const char *const TAG = "font";
|
||||
|
||||
void Glyph::draw(int x_at, int y_start, DisplayBuffer *display, Color color) const {
|
||||
void Glyph::draw(int x_at, int y_start, display::Display *display, Color color) const {
|
||||
int scan_x1, scan_y1, scan_width, scan_height;
|
||||
this->scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
|
||||
|
||||
|
@ -116,7 +118,7 @@ void Font::measure(const char *str, int *width, int *x_offset, int *baseline, in
|
|||
*x_offset = min_x;
|
||||
*width = x - min_x;
|
||||
}
|
||||
void Font::print(int x_start, int y_start, DisplayBuffer *display, Color color, const char *text) {
|
||||
void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text) {
|
||||
int i = 0;
|
||||
int x_at = x_start;
|
||||
while (text[i] != '\0') {
|
||||
|
@ -143,5 +145,5 @@ void Font::print(int x_start, int y_start, DisplayBuffer *display, Color color,
|
|||
}
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
} // namespace font
|
||||
} // namespace esphome
|
|
@ -1,12 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/datatypes.h"
|
||||
#include "display_buffer.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
namespace font {
|
||||
|
||||
class DisplayBuffer;
|
||||
class Font;
|
||||
|
||||
struct GlyphData {
|
||||
|
@ -22,7 +22,7 @@ class Glyph {
|
|||
public:
|
||||
Glyph(const GlyphData *data) : glyph_data_(data) {}
|
||||
|
||||
void draw(int x, int y, DisplayBuffer *display, Color color) const;
|
||||
void draw(int x, int y, display::Display *display, Color color) const;
|
||||
|
||||
const char *get_char() const;
|
||||
|
||||
|
@ -38,7 +38,7 @@ class Glyph {
|
|||
const GlyphData *glyph_data_;
|
||||
};
|
||||
|
||||
class Font : public BaseFont {
|
||||
class Font : public display::BaseFont {
|
||||
public:
|
||||
/** Construct the font with the given glyphs.
|
||||
*
|
||||
|
@ -50,7 +50,7 @@ class Font : public BaseFont {
|
|||
|
||||
int match_next_glyph(const char *str, int *match_length);
|
||||
|
||||
void print(int x_start, int y_start, DisplayBuffer *display, Color color, const char *text) override;
|
||||
void print(int x_start, int y_start, display::Display *display, Color color, const char *text) override;
|
||||
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override;
|
||||
inline int get_baseline() { return this->baseline_; }
|
||||
inline int get_height() { return this->height_; }
|
||||
|
@ -63,5 +63,5 @@ class Font : public BaseFont {
|
|||
int height_;
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace font
|
||||
} // namespace esphome
|
|
@ -1,6 +1,5 @@
|
|||
#include "graph.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/components/display/font.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
@ -57,7 +56,7 @@ void GraphTrace::init(Graph *g) {
|
|||
this->data_.set_update_time_ms(g->get_duration() * 1000 / g->get_width());
|
||||
}
|
||||
|
||||
void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color) {
|
||||
void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color color) {
|
||||
/// Plot border
|
||||
if (this->border_) {
|
||||
buff->horizontal_line(x_offset, y_offset, this->width_, color);
|
||||
|
@ -304,7 +303,7 @@ void GraphLegend::init(Graph *g) {
|
|||
}
|
||||
}
|
||||
|
||||
void Graph::draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color) {
|
||||
void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color) {
|
||||
if (!legend_)
|
||||
return;
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
namespace esphome {
|
||||
|
||||
// forward declare DisplayBuffer
|
||||
// forward declare Display
|
||||
namespace display {
|
||||
class DisplayBuffer;
|
||||
class Font;
|
||||
class Display;
|
||||
class BaseFont;
|
||||
} // namespace display
|
||||
|
||||
namespace graph {
|
||||
|
@ -45,8 +45,8 @@ enum ValuePositionType {
|
|||
class GraphLegend {
|
||||
public:
|
||||
void init(Graph *g);
|
||||
void set_name_font(display::Font *font) { this->font_label_ = font; }
|
||||
void set_value_font(display::Font *font) { this->font_value_ = font; }
|
||||
void set_name_font(display::BaseFont *font) { this->font_label_ = font; }
|
||||
void set_value_font(display::BaseFont *font) { this->font_value_ = font; }
|
||||
void set_width(uint32_t width) { this->width_ = width; }
|
||||
void set_height(uint32_t height) { this->height_ = height; }
|
||||
void set_border(bool val) { this->border_ = val; }
|
||||
|
@ -63,8 +63,8 @@ class GraphLegend {
|
|||
ValuePositionType values_{VALUE_POSITION_TYPE_AUTO};
|
||||
bool units_{true};
|
||||
DirectionType direction_{DIRECTION_TYPE_AUTO};
|
||||
display::Font *font_label_{nullptr};
|
||||
display::Font *font_value_{nullptr};
|
||||
display::BaseFont *font_label_{nullptr};
|
||||
display::BaseFont *font_value_{nullptr};
|
||||
// Calculated values
|
||||
Graph *parent_{nullptr};
|
||||
// (x0) (xs,ys) (xs,ys)
|
||||
|
@ -133,8 +133,8 @@ class GraphTrace {
|
|||
|
||||
class Graph : public Component {
|
||||
public:
|
||||
void draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color);
|
||||
void draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color);
|
||||
void draw(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color);
|
||||
void draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color);
|
||||
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace i2c {
|
|||
static const char *const TAG = "i2c.idf";
|
||||
|
||||
void IDFI2CBus::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up I2C bus...");
|
||||
static i2c_port_t next_port = 0;
|
||||
port_ = next_port++;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from esphome.const import (
|
|||
CONF_PAGES,
|
||||
CONF_RESET_PIN,
|
||||
CONF_DIMENSIONS,
|
||||
CONF_DATA_RATE,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
@ -43,6 +44,7 @@ MODELS = {
|
|||
"ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI),
|
||||
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
|
||||
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
|
||||
"ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI),
|
||||
"ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI),
|
||||
"S3BOX": ili9XXX_ns.class_("ILI9XXXS3Box", ili9XXXSPI),
|
||||
"S3BOX_LITE": ili9XXX_ns.class_("ILI9XXXS3BoxLite", ili9XXXSPI),
|
||||
|
@ -97,6 +99,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list(
|
||||
cv.file_
|
||||
),
|
||||
cv.Optional(CONF_DATA_RATE, default="40MHz"): spi.SPI_DATA_RATE_SCHEMA,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("1s"))
|
||||
|
@ -175,3 +178,6 @@ async def to_code(config):
|
|||
if rhs is not None:
|
||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||
cg.add(var.set_palette(prog_arr))
|
||||
|
||||
spi_data_rate = str(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]])
|
||||
cg.add_define("ILI9XXXDisplay_DATA_RATE", cg.RawExpression(spi_data_rate))
|
||||
|
|
|
@ -152,12 +152,10 @@ void ILI9XXXDisplay::update() {
|
|||
this->need_update_ = true;
|
||||
return;
|
||||
}
|
||||
this->prossing_update_ = true;
|
||||
do {
|
||||
this->prossing_update_ = true;
|
||||
this->need_update_ = false;
|
||||
if (!this->need_update_) {
|
||||
this->do_update_();
|
||||
}
|
||||
this->do_update_();
|
||||
} while (this->need_update_);
|
||||
this->prossing_update_ = false;
|
||||
this->display_();
|
||||
|
@ -411,6 +409,17 @@ void ILI9XXXILI9488::initialize() {
|
|||
this->is_18bitdisplay_ = true;
|
||||
}
|
||||
// 40_TFT display
|
||||
void ILI9XXXILI9488A::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9488_A);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 480;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 320;
|
||||
}
|
||||
this->is_18bitdisplay_ = true;
|
||||
}
|
||||
// 40_TFT display
|
||||
void ILI9XXXST7796::initialize() {
|
||||
this->init_lcd_(INITCMD_ST7796);
|
||||
if (this->width_ == 0) {
|
||||
|
|
|
@ -15,10 +15,14 @@ enum ILI9XXXColorMode {
|
|||
BITS_16 = 0x10,
|
||||
};
|
||||
|
||||
#ifndef ILI9XXXDisplay_DATA_RATE
|
||||
#define ILI9XXXDisplay_DATA_RATE spi::DATA_RATE_40MHZ
|
||||
#endif // ILI9XXXDisplay_DATA_RATE
|
||||
|
||||
class ILI9XXXDisplay : public PollingComponent,
|
||||
public display::DisplayBuffer,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_40MHZ> {
|
||||
spi::CLOCK_PHASE_LEADING, ILI9XXXDisplay_DATA_RATE> {
|
||||
public:
|
||||
void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; }
|
||||
float get_setup_priority() const override;
|
||||
|
@ -128,6 +132,12 @@ class ILI9XXXILI9488 : public ILI9XXXDisplay {
|
|||
void initialize() override;
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_35_TFT origin colors rotated display --------------
|
||||
class ILI9XXXILI9488A : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_35_TFT rotated display --------------
|
||||
class ILI9XXXST7796 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
|
|
|
@ -139,6 +139,40 @@ static const uint8_t PROGMEM INITCMD_ILI9488[] = {
|
|||
|
||||
|
||||
|
||||
// 5 frames
|
||||
//ILI9XXX_ETMOD, 1, 0xC6, //
|
||||
|
||||
|
||||
ILI9XXX_SLPOUT, 0x80, // Exit sleep mode
|
||||
//ILI9XXX_INVON , 0,
|
||||
ILI9XXX_DISPON, 0x80, // Set display on
|
||||
0x00 // end
|
||||
};
|
||||
|
||||
static const uint8_t PROGMEM INITCMD_ILI9488_A[] = {
|
||||
ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F,
|
||||
ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F,
|
||||
|
||||
ILI9XXX_PWCTR1, 2, 0x17, 0x15, // VRH1 VRH2
|
||||
ILI9XXX_PWCTR2, 1, 0x41, // VGH, VGL
|
||||
ILI9XXX_VMCTR1, 3, 0x00, 0x12, 0x80, // nVM VCM_REG VCM_REG_EN
|
||||
|
||||
ILI9XXX_IFMODE, 1, 0x00,
|
||||
ILI9XXX_FRMCTR1, 1, 0xA0, // Frame rate = 60Hz
|
||||
ILI9XXX_INVCTR, 1, 0x02, // Display Inversion Control = 2dot
|
||||
|
||||
ILI9XXX_DFUNCTR, 2, 0x02, 0x02, // Nomal scan
|
||||
|
||||
0xE9, 1, 0x00, // Set Image Functio. Disable 24 bit data
|
||||
|
||||
ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3
|
||||
|
||||
ILI9XXX_MADCTL, 1, 0x28,
|
||||
//ILI9XXX_PIXFMT, 1, 0x55, // Interface Pixel Format = 16bit
|
||||
ILI9XXX_PIXFMT, 1, 0x66, //ILI9488 only supports 18-bit pixel format in 4/3 wire SPI mode
|
||||
|
||||
|
||||
|
||||
// 5 frames
|
||||
//ILI9XXX_ETMOD, 1, 0xC6, //
|
||||
|
||||
|
@ -218,12 +252,12 @@ static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
|
|||
ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9XXX_GMCTRP1 , 14, 0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, // Set Gamma
|
||||
0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14,
|
||||
0x18, 0x1B,
|
||||
ILI9XXX_GMCTRN1 , 14, 0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, // Set Gamma
|
||||
0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14,
|
||||
0x17, 0x1B,
|
||||
ILI9XXX_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9XXX_DISPON , 0x80, // Display on
|
||||
0x00 // End of list
|
||||
|
|
|
@ -6,7 +6,7 @@ import re
|
|||
import requests
|
||||
|
||||
from esphome import core
|
||||
from esphome.components import display, font
|
||||
from esphome.components import font
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import (
|
||||
|
@ -28,7 +28,9 @@ DOMAIN = "image"
|
|||
DEPENDENCIES = ["display"]
|
||||
MULTI_CONF = True
|
||||
|
||||
ImageType = display.display_ns.enum("ImageType")
|
||||
image_ns = cg.esphome_ns.namespace("image")
|
||||
|
||||
ImageType = image_ns.enum("ImageType")
|
||||
IMAGE_TYPE = {
|
||||
"BINARY": ImageType.IMAGE_TYPE_BINARY,
|
||||
"TRANSPARENT_BINARY": ImageType.IMAGE_TYPE_BINARY,
|
||||
|
@ -46,7 +48,7 @@ MDI_DOWNLOAD_TIMEOUT = 30 # seconds
|
|||
SOURCE_LOCAL = "local"
|
||||
SOURCE_MDI = "mdi"
|
||||
|
||||
Image_ = display.display_ns.class_("Image")
|
||||
Image_ = image_ns.class_("Image")
|
||||
|
||||
|
||||
def _compute_local_icon_path(value) -> Path:
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
namespace image {
|
||||
|
||||
void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) {
|
||||
void Image::draw(int x, int y, display::Display *display, Color color_on, Color color_off) {
|
||||
switch (type_) {
|
||||
case IMAGE_TYPE_BINARY: {
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
|
@ -130,5 +130,5 @@ ImageType Image::get_type() const { return this->type_; }
|
|||
Image::Image(const uint8_t *data_start, int width, int height, ImageType type)
|
||||
: width_(width), height_(height), type_(type), data_start_(data_start) {}
|
||||
|
||||
} // namespace display
|
||||
} // namespace image
|
||||
} // namespace esphome
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
#include "esphome/core/color.h"
|
||||
#include "display_buffer.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
namespace image {
|
||||
|
||||
enum ImageType {
|
||||
IMAGE_TYPE_BINARY = 0,
|
||||
|
@ -31,15 +31,15 @@ inline int image_type_to_bpp(ImageType type) {
|
|||
|
||||
inline int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; }
|
||||
|
||||
class Image : public BaseImage {
|
||||
class Image : public display::BaseImage {
|
||||
public:
|
||||
Image(const uint8_t *data_start, int width, int height, ImageType type);
|
||||
Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const;
|
||||
Color get_pixel(int x, int y, Color color_on = display::COLOR_ON, Color color_off = display::COLOR_OFF) const;
|
||||
int get_width() const override;
|
||||
int get_height() const override;
|
||||
ImageType get_type() const;
|
||||
|
||||
void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override;
|
||||
void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override;
|
||||
|
||||
void set_transparency(bool transparent) { transparent_ = transparent; }
|
||||
bool has_transparency() const { return transparent_; }
|
||||
|
@ -58,5 +58,5 @@ class Image : public BaseImage {
|
|||
bool transparent_;
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
} // namespace image
|
||||
} // namespace esphome
|
|
@ -57,6 +57,10 @@ void MDNSComponent::compile_records_() {
|
|||
service.txt_records.push_back({"network", "ethernet"});
|
||||
#endif
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
service.txt_records.push_back({"api_encryption", "Noise_NNpsk0_25519_ChaChaPoly_SHA256"});
|
||||
#endif
|
||||
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME});
|
||||
service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION});
|
||||
|
|
|
@ -54,16 +54,16 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||
const auto &manu_datas = device.get_manufacturer_datas();
|
||||
|
||||
if (manu_datas.size() != 1) {
|
||||
ESP_LOGE(TAG, "%s: Unexpected manu_datas size (%d)", device.address_str().c_str(), manu_datas.size());
|
||||
ESP_LOGE(TAG, "[%s] Unexpected manu_datas size (%d)", device.address_str().c_str(), manu_datas.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &manu_data = manu_datas[0];
|
||||
|
||||
ESP_LOGVV(TAG, "%s: Manufacturer data: %s", device.address_str().c_str(), format_hex_pretty(manu_data.data).c_str());
|
||||
ESP_LOGVV(TAG, "[%s] Manufacturer data: %s", device.address_str().c_str(), format_hex_pretty(manu_data.data).c_str());
|
||||
|
||||
if (manu_data.data.size() != MANUFACTURER_DATA_LENGTH) {
|
||||
ESP_LOGE(TAG, "%s: Unexpected manu_data size (%d)", device.address_str().c_str(), manu_data.data.size());
|
||||
ESP_LOGE(TAG, "[%s] Unexpected manu_data size (%d)", device.address_str().c_str(), manu_data.data.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -72,20 +72,20 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||
|
||||
const u_int8_t hardware_id = mopeka_data->data_1 & 0xCF;
|
||||
if (static_cast<SensorType>(hardware_id) != STANDARD && static_cast<SensorType>(hardware_id) != XL) {
|
||||
ESP_LOGE(TAG, "%s: Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id);
|
||||
ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "%s: Sensor slow update rate: %d", device.address_str().c_str(), mopeka_data->slow_update_rate);
|
||||
ESP_LOGVV(TAG, "%s: Sensor sync pressed: %d", device.address_str().c_str(), mopeka_data->sync_pressed);
|
||||
ESP_LOGVV(TAG, "[%s] Sensor slow update rate: %d", device.address_str().c_str(), mopeka_data->slow_update_rate);
|
||||
ESP_LOGVV(TAG, "[%s] Sensor sync pressed: %d", device.address_str().c_str(), mopeka_data->sync_pressed);
|
||||
for (u_int8_t i = 0; i < 3; i++) {
|
||||
ESP_LOGVV(TAG, "%s: %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 1,
|
||||
ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 1,
|
||||
mopeka_data->val[i].value_0, mopeka_data->val[i].time_0);
|
||||
ESP_LOGVV(TAG, "%s: %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 2,
|
||||
ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 2,
|
||||
mopeka_data->val[i].value_1, mopeka_data->val[i].time_1);
|
||||
ESP_LOGVV(TAG, "%s: %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 3,
|
||||
ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 3,
|
||||
mopeka_data->val[i].value_2, mopeka_data->val[i].time_2);
|
||||
ESP_LOGVV(TAG, "%s: %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 4,
|
||||
ESP_LOGVV(TAG, "[%s] %u. Sensor data %u time %u.", device.address_str().c_str(), (i * 4) + 4,
|
||||
mopeka_data->val[i].value_3, mopeka_data->val[i].time_3);
|
||||
}
|
||||
|
||||
|
@ -146,19 +146,19 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||
// This value is better than a previous one.
|
||||
best_value = measurements_value[i];
|
||||
best_time = measurement_time;
|
||||
// Reset measurement_time or next values.
|
||||
measurement_time = 0;
|
||||
}
|
||||
// Reset measurement_time or next values.
|
||||
measurement_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "%s: Found %u values with best data %u time %u.", device.address_str().c_str(),
|
||||
ESP_LOGV(TAG, "[%s] Found %u values with best data %u time %u.", device.address_str().c_str(),
|
||||
number_of_usable_values, best_value, best_time);
|
||||
|
||||
if (number_of_usable_values < 2 || best_value < 2 || best_time < 2) {
|
||||
if (number_of_usable_values < 1 || best_value < 2 || best_time < 2) {
|
||||
// At least two measurement values must be present.
|
||||
ESP_LOGW(TAG, "%s: Poor read quality. Setting distance to 0.", device.address_str().c_str());
|
||||
ESP_LOGW(TAG, "[%s] Poor read quality. Setting distance to 0.", device.address_str().c_str());
|
||||
if (this->distance_ != nullptr) {
|
||||
this->distance_->publish_state(0);
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||
}
|
||||
} else {
|
||||
float lpg_speed_of_sound = this->get_lpg_speed_of_sound_(temp_in_c);
|
||||
ESP_LOGV(TAG, "%s: Speed of sound in current fluid %f m/s", device.address_str().c_str(), lpg_speed_of_sound);
|
||||
ESP_LOGV(TAG, "[%s] Speed of sound in current fluid %f m/s", device.address_str().c_str(), lpg_speed_of_sound);
|
||||
|
||||
uint32_t distance_value = lpg_speed_of_sound * best_time / 100.0f;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "qr_code.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
|
@ -33,7 +33,7 @@ void QrCode::generate_qr_code() {
|
|||
}
|
||||
}
|
||||
|
||||
void QrCode::draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale) {
|
||||
void QrCode::draw(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale) {
|
||||
ESP_LOGV(TAG, "Drawing QR code at (%d, %d)", x_offset, y_offset);
|
||||
|
||||
if (this->needs_update_) {
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
namespace esphome {
|
||||
// forward declare DisplayBuffer
|
||||
namespace display {
|
||||
class DisplayBuffer;
|
||||
class Display;
|
||||
} // namespace display
|
||||
|
||||
namespace qr_code {
|
||||
class QrCode : public Component {
|
||||
public:
|
||||
void draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale);
|
||||
void draw(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale);
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
|
|
|
@ -42,13 +42,18 @@ void SCD30Component::setup() {
|
|||
ESP_LOGD(TAG, "SCD30 Firmware v%0d.%02d", (uint16_t(raw_firmware_version[0]) >> 8),
|
||||
uint16_t(raw_firmware_version[0] & 0xFF));
|
||||
|
||||
if (this->temperature_offset_ != 0) {
|
||||
if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, (uint16_t) (temperature_offset_ * 100.0))) {
|
||||
ESP_LOGE(TAG, "Sensor SCD30 error setting temperature offset.");
|
||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
uint16_t temp_offset;
|
||||
if (this->temperature_offset_ > 0) {
|
||||
temp_offset = (this->temperature_offset_ * 100);
|
||||
} else {
|
||||
temp_offset = 0;
|
||||
}
|
||||
|
||||
if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, temp_offset)) {
|
||||
ESP_LOGE(TAG, "Sensor SCD30 error setting temperature offset.");
|
||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#ifdef USE_ESP32
|
||||
// According ESP32 clock stretching is typically 30ms and up to 150ms "due to
|
||||
|
|
|
@ -68,7 +68,10 @@ CONFIG_SCHEMA = (
|
|||
cv.int_range(min=0, max=0xFFFF, max_included=False),
|
||||
),
|
||||
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
|
||||
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.temperature,
|
||||
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.All(
|
||||
cv.temperature,
|
||||
cv.float_range(min=0, max=655.35),
|
||||
),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All(
|
||||
cv.positive_time_period_seconds,
|
||||
cv.Range(
|
||||
|
|
|
@ -33,6 +33,7 @@ SCRIPT_MODES = {
|
|||
|
||||
PARAMETER_TYPE_TRANSLATIONS = {
|
||||
"string": "std::string",
|
||||
"boolean": "bool",
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,6 +150,16 @@ async def to_code(config):
|
|||
),
|
||||
)
|
||||
async def script_execute_action_to_code(config, action_id, template_arg, args):
|
||||
def convert(type: str):
|
||||
def converter(value):
|
||||
if type == "std::string":
|
||||
return value
|
||||
if type == "bool":
|
||||
return cg.RawExpression(str(value).lower())
|
||||
return cg.RawExpression(str(value))
|
||||
|
||||
return converter
|
||||
|
||||
async def get_ordered_args(config, script_params):
|
||||
config_args = config.copy()
|
||||
config_args.pop(CONF_ID)
|
||||
|
@ -160,7 +171,9 @@ async def script_execute_action_to_code(config, action_id, template_arg, args):
|
|||
raise EsphomeError(
|
||||
f"Missing parameter: '{name}' in script.execute {config[CONF_ID]}"
|
||||
)
|
||||
arg = await cg.templatable(config_args[name], args, type)
|
||||
arg = await cg.templatable(
|
||||
config_args[name], args, type, convert(str(type))
|
||||
)
|
||||
script_args.append(arg)
|
||||
return script_args
|
||||
|
||||
|
|
|
@ -16,6 +16,22 @@ CODEOWNERS = ["@esphome/core"]
|
|||
spi_ns = cg.esphome_ns.namespace("spi")
|
||||
SPIComponent = spi_ns.class_("SPIComponent", cg.Component)
|
||||
SPIDevice = spi_ns.class_("SPIDevice")
|
||||
SPIDataRate = spi_ns.enum("SPIDataRate")
|
||||
|
||||
SPI_DATA_RATE_OPTIONS = {
|
||||
80e6: SPIDataRate.DATA_RATE_80MHZ,
|
||||
40e6: SPIDataRate.DATA_RATE_40MHZ,
|
||||
20e6: SPIDataRate.DATA_RATE_20MHZ,
|
||||
10e6: SPIDataRate.DATA_RATE_10MHZ,
|
||||
5e6: SPIDataRate.DATA_RATE_5MHZ,
|
||||
2e6: SPIDataRate.DATA_RATE_2MHZ,
|
||||
1e6: SPIDataRate.DATA_RATE_1MHZ,
|
||||
2e5: SPIDataRate.DATA_RATE_200KHZ,
|
||||
75e3: SPIDataRate.DATA_RATE_75KHZ,
|
||||
1e3: SPIDataRate.DATA_RATE_1KHZ,
|
||||
}
|
||||
SPI_DATA_RATE_SCHEMA = cv.All(cv.frequency, cv.enum(SPI_DATA_RATE_OPTIONS))
|
||||
|
||||
MULTI_CONF = True
|
||||
CONF_FORCE_SW = "force_sw"
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ enum SPIDataRate : uint32_t {
|
|||
DATA_RATE_10MHZ = 10000000,
|
||||
DATA_RATE_20MHZ = 20000000,
|
||||
DATA_RATE_40MHZ = 40000000,
|
||||
DATA_RATE_80MHZ = 80000000,
|
||||
};
|
||||
|
||||
class SPIComponent : public Component {
|
||||
|
|
|
@ -6,11 +6,21 @@ namespace template_ {
|
|||
|
||||
static const char *const TAG = "template.binary_sensor";
|
||||
|
||||
void TemplateBinarySensor::loop() {
|
||||
if (!this->f_.has_value())
|
||||
void TemplateBinarySensor::setup() {
|
||||
if (!this->publish_initial_state_)
|
||||
return;
|
||||
|
||||
auto s = (*this->f_)();
|
||||
if (this->f_ != nullptr) {
|
||||
this->publish_initial_state(*this->f_());
|
||||
} else {
|
||||
this->publish_initial_state(false);
|
||||
}
|
||||
}
|
||||
void TemplateBinarySensor::loop() {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
|
||||
auto s = this->f_();
|
||||
if (s.has_value()) {
|
||||
this->publish_state(*s);
|
||||
}
|
||||
|
|
|
@ -10,13 +10,14 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso
|
|||
public:
|
||||
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
optional<std::function<optional<bool>()>> f_{};
|
||||
std::function<optional<bool>()> f_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
|
|
|
@ -7,6 +7,17 @@ namespace touchscreen {
|
|||
|
||||
static const char *const TAG = "touchscreen";
|
||||
|
||||
void Touchscreen::set_display(display::Display *display) {
|
||||
this->display_ = display;
|
||||
this->display_width_ = display->get_width();
|
||||
this->display_height_ = display->get_height();
|
||||
this->rotation_ = static_cast<TouchRotation>(display->get_rotation());
|
||||
|
||||
if (this->rotation_ == ROTATE_90_DEGREES || this->rotation_ == ROTATE_270_DEGREES) {
|
||||
std::swap(this->display_width_, this->display_height_);
|
||||
}
|
||||
}
|
||||
|
||||
void Touchscreen::send_touch_(TouchPoint tp) {
|
||||
ESP_LOGV(TAG, "Touch (x=%d, y=%d)", tp.x, tp.y);
|
||||
this->touch_trigger_.trigger(tp);
|
||||
|
|
|
@ -31,13 +31,8 @@ enum TouchRotation {
|
|||
|
||||
class Touchscreen {
|
||||
public:
|
||||
void set_display(display::DisplayBuffer *display) {
|
||||
this->display_ = display;
|
||||
this->display_width_ = display->get_width_internal();
|
||||
this->display_height_ = display->get_height_internal();
|
||||
this->rotation_ = static_cast<TouchRotation>(display->get_rotation());
|
||||
}
|
||||
display::DisplayBuffer *get_display() const { return this->display_; }
|
||||
void set_display(display::Display *display);
|
||||
display::Display *get_display() const { return this->display_; }
|
||||
|
||||
Trigger<TouchPoint> *get_touch_trigger() { return &this->touch_trigger_; }
|
||||
|
||||
|
@ -49,7 +44,7 @@ class Touchscreen {
|
|||
|
||||
uint16_t display_width_;
|
||||
uint16_t display_height_;
|
||||
display::DisplayBuffer *display_;
|
||||
display::Display *display_;
|
||||
TouchRotation rotation_;
|
||||
Trigger<TouchPoint> touch_trigger_;
|
||||
std::vector<TouchListener *> touch_listeners_;
|
||||
|
|
|
@ -63,6 +63,7 @@ WaveshareEPaper7P5InHDB = waveshare_epaper_ns.class_(
|
|||
WaveshareEPaper2P13InDKE = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P13InDKE", WaveshareEPaper
|
||||
)
|
||||
GDEW0154M09 = waveshare_epaper_ns.class_("GDEW0154M09", WaveshareEPaper)
|
||||
|
||||
WaveshareEPaperTypeAModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeAModel")
|
||||
WaveshareEPaperTypeBModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeBModel")
|
||||
|
@ -91,6 +92,7 @@ MODELS = {
|
|||
"7.50inv2alt": ("b", WaveshareEPaper7P5InV2alt),
|
||||
"7.50in-hd-b": ("b", WaveshareEPaper7P5InHDB),
|
||||
"2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE),
|
||||
"1.54in-m5coreink-m09": ("c", GDEW0154M09),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -763,6 +763,146 @@ void GDEY029T94::dump_config() {
|
|||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// Good Display 1.54in black/white/grey GDEW0154M09
|
||||
// As used in M5Stack Core Ink
|
||||
// Datasheet:
|
||||
// - https://v4.cecdn.yun300.cn/100001_1909185148/GDEW0154M09-200709.pdf
|
||||
// - https://github.com/m5stack/M5Core-Ink
|
||||
// Reference code from GoodDisplay:
|
||||
// - https://github.com/GoodDisplay/E-paper-Display-Library-of-GoodDisplay/
|
||||
// -> /Monochrome_E-paper-Display/1.54inch_JD79653_GDEW0154M09_200x200/ESP32-Arduino%20IDE/GDEW0154M09_Arduino.ino
|
||||
// M5Stack Core Ink spec:
|
||||
// - https://docs.m5stack.com/en/core/coreink
|
||||
// ========================================================
|
||||
|
||||
void GDEW0154M09::initialize() {
|
||||
this->init_internal_();
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
this->lastbuff_ = allocator.allocate(this->get_buffer_length_());
|
||||
if (this->lastbuff_ != nullptr) {
|
||||
memset(this->lastbuff_, 0xff, sizeof(uint8_t) * this->get_buffer_length_());
|
||||
}
|
||||
this->clear_();
|
||||
}
|
||||
|
||||
void GDEW0154M09::reset_() {
|
||||
// RST is inverse from other einks in this project
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(10);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void GDEW0154M09::init_internal_() {
|
||||
this->reset_();
|
||||
|
||||
// clang-format off
|
||||
// 200x200 resolution: 11
|
||||
// LUT from OTP: 0
|
||||
// B/W mode (doesn't work): 1
|
||||
// scan-up: 1
|
||||
// shift-right: 1
|
||||
// booster ON: 1
|
||||
// no soft reset: 1
|
||||
const uint8_t panel_setting_1 = 0b11011111;
|
||||
|
||||
// VCOM status off 0
|
||||
// Temp sensing default 1
|
||||
// VGL Power Off Floating 1
|
||||
// NORG expect refresh 1
|
||||
// VCOM Off on displ off 0
|
||||
const uint8_t panel_setting_2 = 0b01110;
|
||||
|
||||
const uint8_t wf_t0154_cz_b3_list[] = {
|
||||
11, // 11 commands in list
|
||||
CMD_PSR_PANEL_SETTING, 2, panel_setting_1, panel_setting_2,
|
||||
CMD_UNDOCUMENTED_0x4D, 1, 0x55,
|
||||
CMD_UNDOCUMENTED_0xAA, 1, 0x0f,
|
||||
CMD_UNDOCUMENTED_0xE9, 1, 0x02,
|
||||
CMD_UNDOCUMENTED_0xB6, 1, 0x11,
|
||||
CMD_UNDOCUMENTED_0xF3, 1, 0x0a,
|
||||
CMD_TRES_RESOLUTION_SETTING, 3, 0xc8, 0x00, 0xc8,
|
||||
CMD_TCON_TCONSETTING, 1, 0x00,
|
||||
CMD_CDI_VCOM_DATA_INTERVAL, 1, 0xd7,
|
||||
CMD_PWS_POWER_SAVING, 1, 0x00,
|
||||
CMD_PON_POWER_ON, 0
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
this->write_init_list_(wf_t0154_cz_b3_list);
|
||||
delay(100); // NOLINT
|
||||
this->wait_until_idle_();
|
||||
}
|
||||
|
||||
void GDEW0154M09::write_init_list_(const uint8_t *list) {
|
||||
uint8_t list_limit = list[0];
|
||||
uint8_t *start_ptr = ((uint8_t *) list + 1);
|
||||
for (uint8_t i = 0; i < list_limit; i++) {
|
||||
this->command(*(start_ptr + 0));
|
||||
for (uint8_t dnum = 0; dnum < *(start_ptr + 1); dnum++) {
|
||||
this->data(*(start_ptr + 2 + dnum));
|
||||
}
|
||||
start_ptr += (*(start_ptr + 1) + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void GDEW0154M09::clear_() {
|
||||
uint32_t pixsize = this->get_buffer_length_();
|
||||
for (uint8_t j = 0; j < 2; j++) {
|
||||
this->command(CMD_DTM1_DATA_START_TRANS);
|
||||
for (int count = 0; count < pixsize; count++) {
|
||||
this->data(0x00);
|
||||
}
|
||||
this->command(CMD_DTM2_DATA_START_TRANS2);
|
||||
for (int count = 0; count < pixsize; count++) {
|
||||
this->data(0xff);
|
||||
}
|
||||
this->command(CMD_DISPLAY_REFRESH);
|
||||
delay(10);
|
||||
this->wait_until_idle_();
|
||||
}
|
||||
}
|
||||
|
||||
void HOT GDEW0154M09::display() {
|
||||
this->init_internal_();
|
||||
// "Mode 0 display" for now
|
||||
this->command(CMD_DTM1_DATA_START_TRANS);
|
||||
for (int i = 0; i < this->get_buffer_length_(); i++) {
|
||||
this->data(0xff);
|
||||
}
|
||||
this->command(CMD_DTM2_DATA_START_TRANS2); // write 'new' data to SRAM
|
||||
for (int i = 0; i < this->get_buffer_length_(); i++) {
|
||||
this->data(this->buffer_[i]);
|
||||
}
|
||||
this->command(CMD_DISPLAY_REFRESH);
|
||||
delay(10);
|
||||
this->wait_until_idle_();
|
||||
this->deep_sleep();
|
||||
}
|
||||
|
||||
void GDEW0154M09::deep_sleep() {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(CMD_POF_POWER_OFF);
|
||||
this->wait_until_idle_();
|
||||
delay(1000); // NOLINT
|
||||
this->command(CMD_DSLP_DEEP_SLEEP);
|
||||
this->data(DATA_DSLP_DEEP_SLEEP);
|
||||
}
|
||||
|
||||
int GDEW0154M09::get_width_internal() { return 200; }
|
||||
int GDEW0154M09::get_height_internal() { return 200; }
|
||||
void GDEW0154M09::dump_config() {
|
||||
LOG_DISPLAY("", "M5Stack CoreInk E-Paper (Good Display)", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 1.54in Greyscale GDEW0154M09");
|
||||
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);
|
||||
}
|
||||
|
||||
static const uint8_t LUT_VCOM_DC_4_2[] = {
|
||||
0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 0x00, 0x0A, 0x01,
|
||||
0x00, 0x00, 0x01, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
|
|
@ -170,6 +170,46 @@ class GDEY029T94 : public WaveshareEPaper {
|
|||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class GDEW0154M09 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
void display() override;
|
||||
void dump_config() override;
|
||||
void deep_sleep() override;
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
|
||||
private:
|
||||
static const uint8_t CMD_DTM1_DATA_START_TRANS = 0x10;
|
||||
static const uint8_t CMD_DTM2_DATA_START_TRANS2 = 0x13;
|
||||
static const uint8_t CMD_DISPLAY_REFRESH = 0x12;
|
||||
static const uint8_t CMD_AUTO_SEQ = 0x17;
|
||||
static const uint8_t DATA_AUTO_PON_DSR_POF_DSLP = 0xA7;
|
||||
static const uint8_t CMD_PSR_PANEL_SETTING = 0x00;
|
||||
static const uint8_t CMD_UNDOCUMENTED_0x4D = 0x4D; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xAA = 0xaa; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xE9 = 0xe9; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xB6 = 0xb6; // NOLINT
|
||||
static const uint8_t CMD_UNDOCUMENTED_0xF3 = 0xf3; // NOLINT
|
||||
static const uint8_t CMD_TRES_RESOLUTION_SETTING = 0x61;
|
||||
static const uint8_t CMD_TCON_TCONSETTING = 0x60;
|
||||
static const uint8_t CMD_CDI_VCOM_DATA_INTERVAL = 0x50;
|
||||
static const uint8_t CMD_POF_POWER_OFF = 0x02;
|
||||
static const uint8_t CMD_DSLP_DEEP_SLEEP = 0x07;
|
||||
static const uint8_t DATA_DSLP_DEEP_SLEEP = 0xA5;
|
||||
static const uint8_t CMD_PWS_POWER_SAVING = 0xe3;
|
||||
static const uint8_t CMD_PON_POWER_ON = 0x04;
|
||||
static const uint8_t CMD_PTL_PARTIAL_WINDOW = 0x90;
|
||||
|
||||
uint8_t *lastbuff_ = nullptr;
|
||||
void reset_();
|
||||
void clear_();
|
||||
void write_init_list_(const uint8_t *list);
|
||||
void init_internal_();
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InB : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -108,6 +108,7 @@ ROOT_CONFIG_PATH = object()
|
|||
|
||||
RESERVED_IDS = [
|
||||
# C++ keywords http://en.cppreference.com/w/cpp/keyword
|
||||
"alarm",
|
||||
"alignas",
|
||||
"alignof",
|
||||
"and",
|
||||
|
|
|
@ -968,6 +968,7 @@ DEVICE_CLASS_GAS = "gas"
|
|||
DEVICE_CLASS_GATE = "gate"
|
||||
DEVICE_CLASS_HEAT = "heat"
|
||||
DEVICE_CLASS_HUMIDITY = "humidity"
|
||||
DEVICE_CLASS_IDENTIFY = "identify"
|
||||
DEVICE_CLASS_ILLUMINANCE = "illuminance"
|
||||
DEVICE_CLASS_IRRADIANCE = "irradiance"
|
||||
DEVICE_CLASS_LIGHT = "light"
|
||||
|
|
|
@ -201,8 +201,8 @@ WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() {
|
|||
uint32_t now = millis();
|
||||
if (now - started_ > 50) {
|
||||
const char *src = component_ == nullptr ? "<null>" : component_->get_component_source();
|
||||
ESP_LOGV(TAG, "Component %s took a long time for an operation (%.2f s).", src, (now - started_) / 1e3f);
|
||||
ESP_LOGV(TAG, "Components should block for at most 20-30ms.");
|
||||
ESP_LOGW(TAG, "Component %s took a long time for an operation (%.2f s).", src, (now - started_) / 1e3f);
|
||||
ESP_LOGW(TAG, "Components should block for at most 20-30ms.");
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u
|
|||
if (timeout == SCHEDULER_DONT_RUN)
|
||||
return;
|
||||
|
||||
ESP_LOGVV(TAG, "set_timeout(name='%s', timeout=%u)", name.c_str(), timeout);
|
||||
ESP_LOGVV(TAG, "set_timeout(name='%s', timeout=%" PRIu32 ")", name.c_str(), timeout);
|
||||
|
||||
auto item = make_unique<SchedulerItem>();
|
||||
item->component = component;
|
||||
|
@ -60,7 +60,7 @@ void HOT Scheduler::set_interval(Component *component, const std::string &name,
|
|||
if (interval != 0)
|
||||
offset = (random_uint32() % interval) / 2;
|
||||
|
||||
ESP_LOGVV(TAG, "set_interval(name='%s', interval=%u, offset=%u)", name.c_str(), interval, offset);
|
||||
ESP_LOGVV(TAG, "set_interval(name='%s', interval=%" PRIu32 ", offset=%" PRIu32 ")", name.c_str(), interval, offset);
|
||||
|
||||
auto item = make_unique<SchedulerItem>();
|
||||
item->component = component;
|
||||
|
@ -108,8 +108,8 @@ void HOT Scheduler::set_retry(Component *component, const std::string &name, uin
|
|||
if (initial_wait_time == SCHEDULER_DONT_RUN)
|
||||
return;
|
||||
|
||||
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%u, max_attempts=%u, backoff_factor=%0.1f)", name.c_str(),
|
||||
initial_wait_time, max_attempts, backoff_increase_factor);
|
||||
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
|
||||
name.c_str(), initial_wait_time, max_attempts, backoff_increase_factor);
|
||||
|
||||
if (backoff_increase_factor < 0.0001) {
|
||||
ESP_LOGE(TAG,
|
||||
|
@ -154,16 +154,16 @@ void HOT Scheduler::call() {
|
|||
if (now - last_print > 2000) {
|
||||
last_print = now;
|
||||
std::vector<std::unique_ptr<SchedulerItem>> old_items;
|
||||
ESP_LOGVV(TAG, "Items: count=%u, now=%u", this->items_.size(), now);
|
||||
ESP_LOGVV(TAG, "Items: count=%u, now=%" PRIu32, this->items_.size(), now);
|
||||
while (!this->empty_()) {
|
||||
this->lock_.lock();
|
||||
auto item = std::move(this->items_[0]);
|
||||
this->pop_raw_();
|
||||
this->lock_.unlock();
|
||||
|
||||
ESP_LOGVV(TAG, " %s '%s' interval=%u last_execution=%u (%u) next=%u (%u)", item->get_type_str(),
|
||||
item->name.c_str(), item->interval, item->last_execution, item->last_execution_major,
|
||||
item->next_execution(), item->next_execution_major());
|
||||
ESP_LOGVV(TAG, " %s '%s' interval=%" PRIu32 " last_execution=%" PRIu32 " (%u) next=%" PRIu32 " (%u)",
|
||||
item->get_type_str(), item->name.c_str(), item->interval, item->last_execution,
|
||||
item->last_execution_major, item->next_execution(), item->next_execution_major());
|
||||
|
||||
old_items.push_back(std::move(item));
|
||||
}
|
||||
|
@ -222,8 +222,8 @@ void HOT Scheduler::call() {
|
|||
}
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", item->get_type_str(),
|
||||
item->name.c_str(), item->interval, item->last_execution, now);
|
||||
ESP_LOGVV(TAG, "Running %s '%s' with interval=%" PRIu32 " last_execution=%" PRIu32 " (now=%" PRIu32 ")",
|
||||
item->get_type_str(), item->name.c_str(), item->interval, item->last_execution, now);
|
||||
#endif
|
||||
|
||||
// Warning: During callback(), a lot of stuff can happen, including:
|
||||
|
|
|
@ -546,22 +546,11 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
|||
return
|
||||
|
||||
with open(path, "rb") as f:
|
||||
while True:
|
||||
# For a 528KB image used as benchmark:
|
||||
# - using 256KB blocks resulted in the smallest file size.
|
||||
# - blocks larger than 256KB didn't improve the size of compressed file.
|
||||
# - blocks smaller than 256KB hindered compression, making the output file larger.
|
||||
data = f.read()
|
||||
if compressed:
|
||||
data = gzip.compress(data, 9)
|
||||
self.write(data)
|
||||
|
||||
# Read file in blocks of 256KB.
|
||||
data = f.read(256 * 1024)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
if compressed:
|
||||
data = gzip.compress(data, 9)
|
||||
|
||||
self.write(data)
|
||||
self.finish()
|
||||
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ lib_deps =
|
|||
fastled/FastLED@3.3.2 ; fastled_base
|
||||
mikalhart/TinyGPSPlus@1.0.2 ; gps
|
||||
freekode/TM1651@1.0.1 ; tm1651
|
||||
glmnet/Dsmr@0.5 ; dsmr
|
||||
glmnet/Dsmr@0.7 ; dsmr
|
||||
rweather/Crypto@0.4.0 ; dsmr
|
||||
dudanov/MideaUART@1.1.8 ; midea
|
||||
tonia/HeatpumpIR@1.0.20 ; heatpumpir
|
||||
|
|
|
@ -7,10 +7,10 @@ tzlocal==5.0.1 # from time
|
|||
tzdata>=2021.1 # from time
|
||||
pyserial==3.5
|
||||
platformio==6.1.7 # When updating platformio, also update Dockerfile
|
||||
esptool==4.6
|
||||
esptool==4.6.2
|
||||
click==8.1.3
|
||||
esphome-dashboard==20230621.0
|
||||
aioesphomeapi==14.1.0
|
||||
aioesphomeapi==15.0.0
|
||||
zeroconf==0.69.0
|
||||
|
||||
# esp-idf requires this, but doesn't bundle it by default
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
pylint==2.17.4
|
||||
flake8==6.0.0 # also change in .pre-commit-config.yaml when updating
|
||||
black==23.3.0 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.4.0 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.7.0 # also change in .pre-commit-config.yaml when updating
|
||||
pre-commit
|
||||
|
||||
# Unit tests
|
||||
pytest==7.3.2
|
||||
pytest==7.4.0
|
||||
pytest-cov==4.1.0
|
||||
pytest-mock==3.10.0
|
||||
pytest-mock==3.11.1
|
||||
pytest-asyncio==0.21.0
|
||||
asyncmock==0.4.2
|
||||
hypothesis==5.49.0
|
||||
|
|
|
@ -461,8 +461,10 @@ def merge(source, destination):
|
|||
|
||||
def is_platform_schema(schema_name):
|
||||
# added mostly because of schema_name == "microphone.MICROPHONE_SCHEMA"
|
||||
# and "alarm_control_panel"
|
||||
# which is shrunk because there is only one component of the schema (i2s_audio)
|
||||
return schema_name == "microphone.MICROPHONE_SCHEMA"
|
||||
component = schema_name.split(".")[0]
|
||||
return component in components and components[component].is_platform_component
|
||||
|
||||
|
||||
def shrink():
|
||||
|
@ -530,6 +532,10 @@ def shrink():
|
|||
elif not key_s:
|
||||
for target in paths:
|
||||
target_s = get_arr_path_schema(target)
|
||||
if S_SCHEMA not in target_s:
|
||||
# an empty schema like speaker.SPEAKER_SCHEMA
|
||||
target_s[S_EXTENDS].remove(x)
|
||||
continue
|
||||
assert target_s[S_SCHEMA][S_EXTENDS] == [x]
|
||||
target_s.pop(S_SCHEMA)
|
||||
target_s.pop(S_TYPE) # undefined
|
||||
|
|
|
@ -607,7 +607,7 @@ def lint_trailing_whitespace(fname, match):
|
|||
"esphome/components/button/button.h",
|
||||
"esphome/components/climate/climate.h",
|
||||
"esphome/components/cover/cover.h",
|
||||
"esphome/components/display/display_buffer.h",
|
||||
"esphome/components/display/display.h",
|
||||
"esphome/components/fan/fan.h",
|
||||
"esphome/components/i2c/i2c.h",
|
||||
"esphome/components/lock/lock.h",
|
||||
|
|
|
@ -9,6 +9,9 @@ set -x
|
|||
esphome compile tests/test1.yaml
|
||||
esphome compile tests/test2.yaml
|
||||
esphome compile tests/test3.yaml
|
||||
esphome compile tests/test3.1.yaml
|
||||
esphome compile tests/test4.yaml
|
||||
esphome compile tests/test5.yaml
|
||||
esphome compile tests/test6.yaml
|
||||
esphome compile tests/test7.yaml
|
||||
esphome compile tests/test8.yaml
|
||||
|
|
|
@ -1370,8 +1370,15 @@ binary_sensor:
|
|||
device_class: window
|
||||
filters:
|
||||
- invert:
|
||||
- delayed_on_off: 40ms
|
||||
- delayed_on_off:
|
||||
time_on: 10s
|
||||
time_off: !lambda "return 1000;"
|
||||
- delayed_on: 40ms
|
||||
- delayed_off: 40ms
|
||||
- delayed_on_off: !lambda "return 10;"
|
||||
- delayed_on: !lambda "return 1000;"
|
||||
- delayed_off: !lambda "return 0;"
|
||||
on_press:
|
||||
then:
|
||||
- lambda: >-
|
||||
|
|
|
@ -397,6 +397,19 @@ sensor:
|
|||
name: MICS-4514 C2H5OH
|
||||
ammonia:
|
||||
name: MICS-4514 NH3
|
||||
- platform: mopeka_std_check
|
||||
mac_address: D3:75:F2:DC:16:91
|
||||
tank_type: CUSTOM
|
||||
custom_distance_full: 40cm
|
||||
custom_distance_empty: 10mm
|
||||
temperature:
|
||||
name: Propane test temp
|
||||
level:
|
||||
name: Propane test level
|
||||
distance:
|
||||
name: Propane test distance
|
||||
battery_level:
|
||||
name: Propane test battery level
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
|
|
|
@ -504,6 +504,14 @@ display:
|
|||
full_update_every: 30
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: waveshare_epaper
|
||||
cs_pin: GPIO23
|
||||
dc_pin: GPIO23
|
||||
busy_pin: GPIO23
|
||||
reset_pin: GPIO23
|
||||
model: 1.54in-m5coreink-m09
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: inkplate6
|
||||
id: inkplate_display
|
||||
greyscale: false
|
||||
|
|
Loading…
Reference in a new issue