Compare commits
37 Commits
jesserockz
...
host-targe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f0dc7b07b | ||
|
|
dc6eff83ea | ||
|
|
38ff66debd | ||
|
|
1d2e0f74ea | ||
|
|
bf60e40d0b | ||
|
|
c9094ca537 | ||
|
|
68b3fd6b8f | ||
|
|
9323b3a248 | ||
|
|
b55e9329d9 | ||
|
|
a5b4105971 | ||
|
|
d1feaa935d | ||
|
|
6919930aaa | ||
|
|
69633826bb | ||
|
|
771162bfb1 | ||
|
|
ba785e29e9 | ||
|
|
2c7b104f4a | ||
|
|
78951c197a | ||
|
|
07c1cf7137 | ||
|
|
d26141151a | ||
|
|
f59dbe4a88 | ||
|
|
8dae7f8225 | ||
|
|
5811389891 | ||
|
|
debcaf6fb7 | ||
|
|
b8d10a62c2 | ||
|
|
d2b209234f | ||
|
|
34c9d8be50 | ||
|
|
ae57ad0c81 | ||
|
|
0c1520dd9c | ||
|
|
d594f43ebd | ||
|
|
125c693e3f | ||
|
|
ad2f857e15 | ||
|
|
e445d6aada | ||
|
|
88fbb0ffbb | ||
|
|
231908fe9f | ||
|
|
f137cc10f4 | ||
|
|
1ab4928959 | ||
|
|
52b86e3440 |
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -9,4 +9,3 @@ contact_links:
|
||||
- name: Frequently Asked Question
|
||||
url: https://esphome.io/guides/faq.html
|
||||
about: Please view the FAQ for common questions and what to include in a bug report.
|
||||
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
# What does this implement/fix?
|
||||
# What does this implement/fix?
|
||||
|
||||
Quick description and explanation of changes
|
||||
|
||||
@@ -35,6 +35,6 @@ Quick description and explanation of changes
|
||||
## Checklist:
|
||||
- [ ] The code change is tested and works locally.
|
||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
|
||||
|
||||
If user exposed functionality or configuration variables are added/changed:
|
||||
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
|
||||
|
||||
10
CODEOWNERS
10
CODEOWNERS
@@ -28,6 +28,7 @@ esphome/components/atc_mithermometer/* @ahpohl
|
||||
esphome/components/b_parasite/* @rbaron
|
||||
esphome/components/ballu/* @bazuchan
|
||||
esphome/components/bang_bang/* @OttoWinter
|
||||
esphome/components/bh1750/* @OttoWinter
|
||||
esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/bl0940/* @tobias-
|
||||
esphome/components/ble_client/* @buxtronix
|
||||
@@ -43,6 +44,7 @@ esphome/components/climate/* @esphome/core
|
||||
esphome/components/climate_ir/* @glmnet
|
||||
esphome/components/color_temperature/* @jesserockz
|
||||
esphome/components/coolix/* @glmnet
|
||||
esphome/components/copy/* @OttoWinter
|
||||
esphome/components/cover/* @esphome/core
|
||||
esphome/components/cs5460a/* @balrog-kun
|
||||
esphome/components/cse7761/* @berfenger
|
||||
@@ -78,6 +80,7 @@ esphome/components/hbridge/light/* @DotNetDann
|
||||
esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/honeywellabp/* @RubyBailey
|
||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/improv_serial/* @esphome/core
|
||||
@@ -94,6 +97,7 @@ esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||
esphome/components/lock/* @esphome/core
|
||||
esphome/components/logger/* @esphome/core
|
||||
esphome/components/ltr390/* @sjtrny
|
||||
esphome/components/max44009/* @berfenger
|
||||
esphome/components/max7219digit/* @rspaargaren
|
||||
esphome/components/max9611/* @mckaymatthew
|
||||
esphome/components/mcp23008/* @jesserockz
|
||||
@@ -105,6 +109,7 @@ esphome/components/mcp23x17_base/* @jesserockz
|
||||
esphome/components/mcp23xxx_base/* @jesserockz
|
||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||
esphome/components/mcp3204/* @rsumner
|
||||
esphome/components/mcp4728/* @berfenger
|
||||
esphome/components/mcp47a1/* @jesserockz
|
||||
esphome/components/mcp9808/* @k7hpn
|
||||
esphome/components/md5/* @esphome/core
|
||||
@@ -121,6 +126,9 @@ esphome/components/modbus_controller/select/* @martgras @stegm
|
||||
esphome/components/modbus_controller/sensor/* @martgras
|
||||
esphome/components/modbus_controller/switch/* @martgras
|
||||
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||
esphome/components/mopeka_ble/* @spbrogan
|
||||
esphome/components/mopeka_pro_check/* @spbrogan
|
||||
esphome/components/mpu6886/* @fabaff
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/nextion/* @senexcrenshaw
|
||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||
@@ -141,7 +149,7 @@ esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||
esphome/components/power_supply/* @esphome/core
|
||||
esphome/components/preferences/* @esphome/core
|
||||
esphome/components/psram/* @esphome/core
|
||||
esphome/components/pulse_meter/* @stevebaxter
|
||||
esphome/components/pulse_meter/* @cstaahl @stevebaxter
|
||||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/qr_code/* @wjtje
|
||||
esphome/components/radon_eye_ble/* @jeffeb3
|
||||
|
||||
@@ -5,7 +5,7 @@ For a detailed guide, please see https://esphome.io/guides/contributing.html#con
|
||||
Things to note when contributing:
|
||||
|
||||
- Please test your changes :)
|
||||
- If a new feature is added or an existing user-facing feature is changed, you should also
|
||||
- If a new feature is added or an existing user-facing feature is changed, you should also
|
||||
update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
|
||||
for more information.
|
||||
- Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
# Check SSL requirements, if enabled
|
||||
if bashio::config.true 'ssl'; then
|
||||
if ! bashio::config.has_value 'certfile'; then
|
||||
bashio::fatal 'SSL is enabled, but no certfile was specified.'
|
||||
bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
|
||||
bashio::exit.nok
|
||||
fi
|
||||
|
||||
if ! bashio::config.has_value 'keyfile'; then
|
||||
bashio::fatal 'SSL is enabled, but no keyfile was specified'
|
||||
bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
|
||||
bashio::exit.nok
|
||||
fi
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ void APIConnection::loop() {
|
||||
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
|
||||
}
|
||||
} else if (now - this->last_traffic_ > keepalive) {
|
||||
ESP_LOGVV(TAG, "Sending keepalive PING...");
|
||||
this->sent_ping_ = true;
|
||||
this->send_ping_request(PingRequest());
|
||||
}
|
||||
@@ -908,7 +909,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
this->last_traffic_ = millis();
|
||||
// Do not set last_traffic_ on send
|
||||
return true;
|
||||
}
|
||||
void APIConnection::on_unauthenticated_access() {
|
||||
|
||||
@@ -9,18 +9,109 @@ static const char *const TAG = "bh1750.sensor";
|
||||
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000; // last 5 bits
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
|
||||
|
||||
/*
|
||||
bh1750 properties:
|
||||
|
||||
L-resolution mode:
|
||||
- resolution 4lx (@ mtreg=69)
|
||||
- measurement time: typ=16ms, max=24ms, scaled by MTreg value divided by 69
|
||||
- formula: counts / 1.2 * (69 / MTreg) lx
|
||||
H-resolution mode:
|
||||
- resolution 1lx (@ mtreg=69)
|
||||
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
|
||||
- formula: counts / 1.2 * (69 / MTreg) lx
|
||||
H-resolution mode2:
|
||||
- resolution 0.5lx (@ mtreg=69)
|
||||
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
|
||||
- formula: counts / 1.2 * (69 / MTreg) / 2 lx
|
||||
|
||||
MTreg:
|
||||
- min=31, default=69, max=254
|
||||
|
||||
-> only reason to use l-resolution is faster, but offers no higher range
|
||||
-> below ~7000lx, makes sense to use H-resolution2 @ MTreg=254
|
||||
-> try to maximize MTreg to get lowest noise level
|
||||
*/
|
||||
|
||||
void BH1750Sensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
|
||||
if (!this->write_bytes(BH1750_COMMAND_POWER_ON, nullptr, 0)) {
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t mtreg_hi = (this->measurement_duration_ >> 5) & 0b111;
|
||||
uint8_t mtreg_lo = (this->measurement_duration_ >> 0) & 0b11111;
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0);
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0);
|
||||
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
|
||||
// turn on (after one-shot sensor automatically powers down)
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Turning on BH1750 failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (active_mtreg_ != mtreg) {
|
||||
// set mtreg
|
||||
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
|
||||
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
|
||||
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
|
||||
active_mtreg_ = 0;
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
active_mtreg_ = mtreg;
|
||||
}
|
||||
|
||||
uint8_t cmd;
|
||||
uint16_t meas_time;
|
||||
switch (mode) {
|
||||
case BH1750_MODE_L:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_L;
|
||||
meas_time = 24 * mtreg / 69;
|
||||
break;
|
||||
case BH1750_MODE_H:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H;
|
||||
meas_time = 180 * mtreg / 69;
|
||||
break;
|
||||
case BH1750_MODE_H2:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H2;
|
||||
meas_time = 180 * mtreg / 69;
|
||||
break;
|
||||
default:
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
// probably not needed, but adjust for rounding
|
||||
meas_time++;
|
||||
|
||||
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
|
||||
uint16_t raw_value;
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Reading BH1750 data failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / mtreg;
|
||||
if (mode == BH1750_MODE_H2)
|
||||
lx /= 2.0f;
|
||||
|
||||
f(lx);
|
||||
});
|
||||
}
|
||||
|
||||
void BH1750Sensor::dump_config() {
|
||||
@@ -30,64 +121,49 @@ void BH1750Sensor::dump_config() {
|
||||
ESP_LOGE(TAG, "Communication with BH1750 failed!");
|
||||
}
|
||||
|
||||
const char *resolution_s;
|
||||
switch (this->resolution_) {
|
||||
case BH1750_RESOLUTION_0P5_LX:
|
||||
resolution_s = "0.5";
|
||||
break;
|
||||
case BH1750_RESOLUTION_1P0_LX:
|
||||
resolution_s = "1";
|
||||
break;
|
||||
case BH1750_RESOLUTION_4P0_LX:
|
||||
resolution_s = "4";
|
||||
break;
|
||||
default:
|
||||
resolution_s = "Unknown";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Resolution: %s", resolution_s);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void BH1750Sensor::update() {
|
||||
if (!this->write_bytes(this->resolution_, nullptr, 0))
|
||||
return;
|
||||
// first do a quick measurement in L-mode with full range
|
||||
// to find right range
|
||||
this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
|
||||
if (std::isnan(val)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t wait = 0;
|
||||
// use max conversion times
|
||||
switch (this->resolution_) {
|
||||
case BH1750_RESOLUTION_0P5_LX:
|
||||
case BH1750_RESOLUTION_1P0_LX:
|
||||
wait = 180;
|
||||
break;
|
||||
case BH1750_RESOLUTION_4P0_LX:
|
||||
wait = 24;
|
||||
break;
|
||||
}
|
||||
BH1750Mode use_mode;
|
||||
uint8_t use_mtreg;
|
||||
if (val <= 7000) {
|
||||
use_mode = BH1750_MODE_H2;
|
||||
use_mtreg = 254;
|
||||
} else {
|
||||
use_mode = BH1750_MODE_H;
|
||||
// lx = counts / 1.2 * (69 / mtreg)
|
||||
// -> mtreg = counts / 1.2 * (69 / lx)
|
||||
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
|
||||
// -> mtreg = 50000*(10/12)*(69/lx)
|
||||
int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
|
||||
use_mtreg = std::min(254, std::max(31, ideal_mtreg));
|
||||
}
|
||||
ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
|
||||
|
||||
this->set_timeout("illuminance", wait, [this]() { this->read_data_(); });
|
||||
this->read_lx_(use_mode, use_mtreg, [this](float val) {
|
||||
if (std::isnan(val)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
|
||||
this->status_clear_warning();
|
||||
this->publish_state(val);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void BH1750Sensor::read_data_() {
|
||||
uint16_t raw_value;
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / this->measurement_duration_;
|
||||
if (this->resolution_ == BH1750_RESOLUTION_0P5_LX) {
|
||||
lx /= 2.0f;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
|
||||
this->publish_state(lx);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
void BH1750Sensor::set_resolution(BH1750Resolution resolution) { this->resolution_ = resolution; }
|
||||
|
||||
} // namespace bh1750
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,29 +7,15 @@
|
||||
namespace esphome {
|
||||
namespace bh1750 {
|
||||
|
||||
/// Enum listing all resolutions that can be used with the BH1750
|
||||
enum BH1750Resolution {
|
||||
BH1750_RESOLUTION_4P0_LX = 0b00100011, // one-time low resolution mode
|
||||
BH1750_RESOLUTION_1P0_LX = 0b00100000, // one-time high resolution mode 1
|
||||
BH1750_RESOLUTION_0P5_LX = 0b00100001, // one-time high resolution mode 2
|
||||
enum BH1750Mode {
|
||||
BH1750_MODE_L,
|
||||
BH1750_MODE_H,
|
||||
BH1750_MODE_H2,
|
||||
};
|
||||
|
||||
/// This class implements support for the i2c-based BH1750 ambient light sensor.
|
||||
class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
/** Set the resolution of this sensor.
|
||||
*
|
||||
* Possible values are:
|
||||
*
|
||||
* - `BH1750_RESOLUTION_4P0_LX`
|
||||
* - `BH1750_RESOLUTION_1P0_LX`
|
||||
* - `BH1750_RESOLUTION_0P5_LX` (default)
|
||||
*
|
||||
* @param resolution The new resolution of the sensor.
|
||||
*/
|
||||
void set_resolution(BH1750Resolution resolution);
|
||||
void set_measurement_duration(uint8_t measurement_duration) { measurement_duration_ = measurement_duration; }
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
void setup() override;
|
||||
@@ -38,10 +24,9 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void read_data_();
|
||||
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
|
||||
|
||||
BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX};
|
||||
uint8_t measurement_duration_;
|
||||
uint8_t active_mtreg_{0};
|
||||
};
|
||||
|
||||
} // namespace bh1750
|
||||
|
||||
@@ -2,28 +2,20 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_RESOLUTION,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
CONF_MEASUREMENT_DURATION,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
bh1750_ns = cg.esphome_ns.namespace("bh1750")
|
||||
BH1750Resolution = bh1750_ns.enum("BH1750Resolution")
|
||||
BH1750_RESOLUTIONS = {
|
||||
4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
|
||||
1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX,
|
||||
0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX,
|
||||
}
|
||||
|
||||
BH1750Sensor = bh1750_ns.class_(
|
||||
"BH1750Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONF_MEASUREMENT_TIME = "measurement_time"
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
BH1750Sensor,
|
||||
@@ -34,14 +26,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(
|
||||
BH1750_RESOLUTIONS, float=True
|
||||
cv.Optional("resolution"): cv.invalid(
|
||||
"The 'resolution' option has been removed. The optimal value is now dynamically calculated."
|
||||
),
|
||||
cv.Optional(CONF_MEASUREMENT_DURATION, default=69): cv.int_range(
|
||||
min=31, max=254
|
||||
),
|
||||
cv.Optional(CONF_MEASUREMENT_TIME): cv.invalid(
|
||||
"The 'measurement_time' option has been replaced with 'measurement_duration' in 1.18.0"
|
||||
cv.Optional("measurement_duration"): cv.invalid(
|
||||
"The 'measurement_duration' option has been removed. The optimal value is now dynamically calculated."
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -54,6 +43,3 @@ async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||
cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION]))
|
||||
|
||||
121
esphome/components/ble_client/text_sensor/__init__.py
Normal file
121
esphome/components/ble_client/text_sensor/__init__.py
Normal file
@@ -0,0 +1,121 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor, ble_client, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SERVICE_UUID,
|
||||
)
|
||||
from esphome import automation
|
||||
from .. import ble_client_ns
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
|
||||
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
|
||||
|
||||
CONF_NOTIFY = "notify"
|
||||
CONF_ON_NOTIFY = "on_notify"
|
||||
|
||||
adv_data_t = cg.std_vector.template(cg.uint8)
|
||||
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
|
||||
|
||||
BLETextSensor = ble_client_ns.class_(
|
||||
"BLETextSensor",
|
||||
text_sensor.TextSensor,
|
||||
cg.PollingComponent,
|
||||
ble_client.BLEClientNode,
|
||||
)
|
||||
BLETextSensorNotifyTrigger = ble_client_ns.class_(
|
||||
"BLETextSensorNotifyTrigger", automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
text_sensor.TEXT_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLETextSensor),
|
||||
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
|
||||
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
BLETextSensorNotifyTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||
cg.add(
|
||||
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(uuid128))
|
||||
|
||||
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_char_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_char_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_CHARACTERISTIC_UUID]
|
||||
)
|
||||
cg.add(var.set_char_uuid128(uuid128))
|
||||
|
||||
if CONF_DESCRIPTOR_UUID in config:
|
||||
if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_descr_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_descr_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_DESCRIPTOR_UUID]
|
||||
)
|
||||
cg.add(var.set_descr_uuid128(uuid128))
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
for conf in config.get(CONF_ON_NOTIFY, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await ble_client.register_ble_node(trigger, config)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
38
esphome/components/ble_client/text_sensor/automation.h
Normal file
38
esphome/components/ble_client/text_sensor/automation.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/text_sensor/ble_text_sensor.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
class BLETextSensorNotifyTrigger : public Trigger<std::string>, public BLETextSensor {
|
||||
public:
|
||||
explicit BLETextSensorNotifyTrigger(BLETextSensor *sensor) { sensor_ = sensor; }
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
switch (event) {
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->sensor_->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
|
||||
break;
|
||||
this->trigger(this->sensor_->parse_data(param->notify.value, param->notify.value_len));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
BLETextSensor *sensor_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
137
esphome/components/ble_client/text_sensor/ble_text_sensor.cpp
Normal file
137
esphome/components/ble_client/text_sensor/ble_text_sensor.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "ble_text_sensor.h"
|
||||
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
static const char *const TAG = "ble_text_sensor";
|
||||
|
||||
static const std::string EMPTY = "";
|
||||
|
||||
uint32_t BLETextSensor::hash_base() { return 193967603UL; }
|
||||
|
||||
void BLETextSensor::loop() {}
|
||||
|
||||
void BLETextSensor::dump_config() {
|
||||
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
|
||||
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->handle = 0;
|
||||
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
|
||||
if (chr == nullptr) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
|
||||
this->char_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = chr->handle;
|
||||
if (this->descr_uuid_.get_uuid().len > 0) {
|
||||
auto *descr = chr->get_descriptor(this->descr_uuid_);
|
||||
if (descr == nullptr) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
ESP_LOGW(TAG, "No sensor descriptor found at service %s char %s descr %s",
|
||||
this->service_uuid_.to_string().c_str(), this->char_uuid_.to_string().c_str(),
|
||||
this->descr_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = descr->handle;
|
||||
}
|
||||
if (this->notify_) {
|
||||
auto status =
|
||||
esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
|
||||
}
|
||||
} else {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
if (param->read.conn_id != this->parent()->conn_id)
|
||||
break;
|
||||
if (param->read.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||
break;
|
||||
}
|
||||
if (param->read.handle == this->handle) {
|
||||
this->status_clear_warning();
|
||||
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
|
||||
break;
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
|
||||
param->notify.handle, param->notify.value[0]);
|
||||
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string BLETextSensor::parse_data(uint8_t *value, uint16_t value_len) {
|
||||
std::string text(value, value + value_len);
|
||||
return text;
|
||||
}
|
||||
|
||||
void BLETextSensor::update() {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
if (this->handle == 0) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto status =
|
||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
47
esphome/components/ble_client/text_sensor/ble_text_sensor.h
Normal file
47
esphome/components/ble_client/text_sensor/ble_text_sensor.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class BLETextSensor : public text_sensor::TextSensor, public PollingComponent, public BLEClientNode {
|
||||
public:
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_enable_notify(bool notify) { this->notify_ = notify; }
|
||||
std::string parse_data(uint8_t *value, uint16_t value_len);
|
||||
uint16_t handle;
|
||||
|
||||
protected:
|
||||
uint32_t hash_base() override;
|
||||
bool notify_;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
espbt::ESPBTUUID char_uuid_;
|
||||
espbt::ESPBTUUID descr_uuid_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
@@ -18,11 +18,7 @@ void Button::add_on_press_callback(std::function<void()> &&callback) { this->pre
|
||||
uint32_t Button::hash_base() { return 1495763804UL; }
|
||||
|
||||
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string Button::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
return "";
|
||||
}
|
||||
std::string Button::get_device_class() { return this->device_class_; }
|
||||
|
||||
} // namespace button
|
||||
} // namespace esphome
|
||||
|
||||
@@ -45,12 +45,12 @@ class Button : public EntityBase {
|
||||
protected:
|
||||
/** You should implement this virtual method if you want to create your own button.
|
||||
*/
|
||||
virtual void press_action(){};
|
||||
virtual void press_action() = 0;
|
||||
|
||||
uint32_t hash_base() override;
|
||||
|
||||
CallbackManager<void()> press_callback_{};
|
||||
optional<std::string> device_class_{};
|
||||
std::string device_class_{};
|
||||
};
|
||||
|
||||
} // namespace button
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "climate.h"
|
||||
#include "esphome/core/macros.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
@@ -326,14 +327,17 @@ optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
return recovered;
|
||||
}
|
||||
void Climate::save_state_() {
|
||||
#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY)
|
||||
#if (defined(USE_ESP_IDF) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \
|
||||
!defined(CLANG_TIDY)
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#define TEMP_IGNORE_MEMACCESS
|
||||
#endif
|
||||
ClimateDeviceRestoreState state{};
|
||||
// initialize as zero to prevent random data on stack triggering erase
|
||||
memset(&state, 0, sizeof(ClimateDeviceRestoreState));
|
||||
#if USE_ESP_IDF && !defined(CLANG_TIDY)
|
||||
#ifdef TEMP_IGNORE_MEMACCESS
|
||||
#pragma GCC diagnostic pop
|
||||
#undef TEMP_IGNORE_MEMACCESS
|
||||
#endif
|
||||
|
||||
state.mode = this->mode;
|
||||
|
||||
5
esphome/components/copy/__init__.py
Normal file
5
esphome/components/copy/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
copy_ns = cg.esphome_ns.namespace("copy")
|
||||
41
esphome/components/copy/binary_sensor/__init__.py
Normal file
41
esphome/components/copy/binary_sensor/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyBinarySensor = copy_ns.class_(
|
||||
"CopyBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.binary_sensor_schema(CopyBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(binary_sensor.BinarySensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
18
esphome/components/copy/binary_sensor/copy_binary_sensor.cpp
Normal file
18
esphome/components/copy/binary_sensor/copy_binary_sensor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "copy_binary_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.binary_sensor";
|
||||
|
||||
void CopyBinarySensor::setup() {
|
||||
source_->add_on_state_callback([this](bool value) { this->publish_state(value); });
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Copy Binary Sensor", this); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
21
esphome/components/copy/binary_sensor/copy_binary_sensor.h
Normal file
21
esphome/components/copy/binary_sensor/copy_binary_sensor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
void set_source(binary_sensor::BinarySensor *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
binary_sensor::BinarySensor *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
42
esphome/components/copy/button/__init__.py
Normal file
42
esphome/components/copy/button/__init__.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import button
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyButton = copy_ns.class_("CopyButton", button.Button, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
button.button_schema()
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyButton),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(button.Button),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await button.register_button(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
14
esphome/components/copy/button/copy_button.cpp
Normal file
14
esphome/components/copy/button/copy_button.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "copy_button.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.button";
|
||||
|
||||
void CopyButton::dump_config() { LOG_BUTTON("", "Copy Button", this); }
|
||||
|
||||
void CopyButton::press_action() { source_->press(); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
22
esphome/components/copy/button/copy_button.h
Normal file
22
esphome/components/copy/button/copy_button.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyButton : public button::Button, public Component {
|
||||
public:
|
||||
void set_source(button::Button *source) { source_ = source; }
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
||||
button::Button *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
38
esphome/components/copy/cover/__init__.py
Normal file
38
esphome/components/copy/cover/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import cover
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyCover = copy_ns.class_("CopyCover", cover.Cover, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyCover),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(cover.Cover),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cover.register_cover(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
50
esphome/components/copy/cover/copy_cover.cpp
Normal file
50
esphome/components/copy/cover/copy_cover.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "copy_cover.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.cover";
|
||||
|
||||
void CopyCover::setup() {
|
||||
source_->add_on_state_callback([this]() {
|
||||
this->current_operation = this->source_->current_operation;
|
||||
this->position = this->source_->position;
|
||||
this->tilt = this->source_->tilt;
|
||||
this->publish_state();
|
||||
});
|
||||
|
||||
this->current_operation = this->source_->current_operation;
|
||||
this->position = this->source_->position;
|
||||
this->tilt = this->source_->tilt;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void CopyCover::dump_config() { LOG_COVER("", "Copy Cover", this); }
|
||||
|
||||
cover::CoverTraits CopyCover::get_traits() {
|
||||
auto base = source_->get_traits();
|
||||
cover::CoverTraits traits{};
|
||||
// copy traits manually so it doesn't break when new options are added
|
||||
// but the control() method hasn't implemented them yet.
|
||||
traits.set_is_assumed_state(base.get_is_assumed_state());
|
||||
traits.set_supports_position(base.get_supports_position());
|
||||
traits.set_supports_tilt(base.get_supports_tilt());
|
||||
traits.set_supports_toggle(base.get_supports_toggle());
|
||||
return traits;
|
||||
}
|
||||
|
||||
void CopyCover::control(const cover::CoverCall &call) {
|
||||
auto call2 = source_->make_call();
|
||||
call2.set_stop(call.get_stop());
|
||||
if (call.get_tilt().has_value())
|
||||
call2.set_tilt(*call.get_tilt());
|
||||
if (call.get_position().has_value())
|
||||
call2.set_position(*call.get_position());
|
||||
if (call.get_tilt().has_value())
|
||||
call2.set_tilt(*call.get_tilt());
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
25
esphome/components/copy/cover/copy_cover.h
Normal file
25
esphome/components/copy/cover/copy_cover.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/cover/cover.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyCover : public cover::Cover, public Component {
|
||||
public:
|
||||
void set_source(cover::Cover *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
cover::CoverTraits get_traits() override;
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
|
||||
cover::Cover *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
36
esphome/components/copy/fan/__init__.py
Normal file
36
esphome/components/copy/fan/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import fan
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyFan = copy_ns.class_("CopyFan", fan.Fan, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyFan),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(fan.Fan),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await fan.register_fan(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
53
esphome/components/copy/fan/copy_fan.cpp
Normal file
53
esphome/components/copy/fan/copy_fan.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "copy_fan.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.fan";
|
||||
|
||||
void CopyFan::setup() {
|
||||
source_->add_on_state_callback([this]() {
|
||||
this->state = source_->state;
|
||||
this->oscillating = source_->oscillating;
|
||||
this->speed = source_->speed;
|
||||
this->direction = source_->direction;
|
||||
this->publish_state();
|
||||
});
|
||||
|
||||
this->state = source_->state;
|
||||
this->oscillating = source_->oscillating;
|
||||
this->speed = source_->speed;
|
||||
this->direction = source_->direction;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void CopyFan::dump_config() { LOG_FAN("", "Copy Fan", this); }
|
||||
|
||||
fan::FanTraits CopyFan::get_traits() {
|
||||
fan::FanTraits traits;
|
||||
auto base = source_->get_traits();
|
||||
// copy traits manually so it doesn't break when new options are added
|
||||
// but the control() method hasn't implemented them yet.
|
||||
traits.set_oscillation(base.supports_oscillation());
|
||||
traits.set_speed(base.supports_speed());
|
||||
traits.set_supported_speed_count(base.supported_speed_count());
|
||||
traits.set_direction(base.supports_direction());
|
||||
return traits;
|
||||
}
|
||||
|
||||
void CopyFan::control(const fan::FanCall &call) {
|
||||
auto call2 = source_->make_call();
|
||||
if (call.get_state().has_value())
|
||||
call2.set_state(*call.get_state());
|
||||
if (call.get_oscillating().has_value())
|
||||
call2.set_oscillating(*call.get_oscillating());
|
||||
if (call.get_speed().has_value())
|
||||
call2.set_speed(*call.get_speed());
|
||||
if (call.get_direction().has_value())
|
||||
call2.set_direction(*call.get_direction());
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
26
esphome/components/copy/fan/copy_fan.h
Normal file
26
esphome/components/copy/fan/copy_fan.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/fan/fan.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyFan : public fan::Fan, public Component {
|
||||
public:
|
||||
void set_source(fan::Fan *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
fan::FanTraits get_traits() override;
|
||||
|
||||
protected:
|
||||
void control(const fan::FanCall &call) override;
|
||||
;
|
||||
|
||||
fan::Fan *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
36
esphome/components/copy/lock/__init__.py
Normal file
36
esphome/components/copy/lock/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import lock
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyLock = copy_ns.class_("CopyLock", lock.Lock, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = lock.LOCK_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyLock),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(lock.Lock),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await lock.register_lock(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
29
esphome/components/copy/lock/copy_lock.cpp
Normal file
29
esphome/components/copy/lock/copy_lock.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "copy_lock.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.lock";
|
||||
|
||||
void CopyLock::setup() {
|
||||
source_->add_on_state_callback([this]() { this->publish_state(source_->state); });
|
||||
|
||||
traits.set_assumed_state(source_->traits.get_assumed_state());
|
||||
traits.set_requires_code(source_->traits.get_requires_code());
|
||||
traits.set_supported_states(source_->traits.get_supported_states());
|
||||
traits.set_supports_open(source_->traits.get_supports_open());
|
||||
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyLock::dump_config() { LOG_LOCK("", "Copy Lock", this); }
|
||||
|
||||
void CopyLock::control(const lock::LockCall &call) {
|
||||
auto call2 = source_->make_call();
|
||||
call2.set_state(call.get_state());
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/lock/copy_lock.h
Normal file
23
esphome/components/copy/lock/copy_lock.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/lock/lock.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyLock : public lock::Lock, public Component {
|
||||
public:
|
||||
void set_source(lock::Lock *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void control(const lock::LockCall &call) override;
|
||||
|
||||
lock::Lock *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
38
esphome/components/copy/number/__init__.py
Normal file
38
esphome/components/copy/number/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import number
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_MODE,
|
||||
CONF_SOURCE_ID,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyNumber = copy_ns.class_("CopyNumber", number.Number, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = number.NUMBER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyNumber),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(number.Number),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_UNIT_OF_MEASUREMENT, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_MODE, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await number.new_number(config, min_value=0, max_value=0, step=0)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
29
esphome/components/copy/number/copy_number.cpp
Normal file
29
esphome/components/copy/number/copy_number.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "copy_number.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.number";
|
||||
|
||||
void CopyNumber::setup() {
|
||||
source_->add_on_state_callback([this](float value) { this->publish_state(value); });
|
||||
|
||||
traits.set_min_value(source_->traits.get_min_value());
|
||||
traits.set_max_value(source_->traits.get_max_value());
|
||||
traits.set_step(source_->traits.get_step());
|
||||
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyNumber::dump_config() { LOG_NUMBER("", "Copy Number", this); }
|
||||
|
||||
void CopyNumber::control(float value) {
|
||||
auto call2 = source_->make_call();
|
||||
call2.set_value(value);
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/number/copy_number.h
Normal file
23
esphome/components/copy/number/copy_number.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/number/number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyNumber : public number::Number, public Component {
|
||||
public:
|
||||
void set_source(number::Number *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void control(float value) override;
|
||||
|
||||
number::Number *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
36
esphome/components/copy/select/__init__.py
Normal file
36
esphome/components/copy/select/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import select
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = select.SELECT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopySelect),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await select.register_select(var, config, options=[])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
27
esphome/components/copy/select/copy_select.cpp
Normal file
27
esphome/components/copy/select/copy_select.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "copy_select.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.select";
|
||||
|
||||
void CopySelect::setup() {
|
||||
source_->add_on_state_callback([this](const std::string &value) { this->publish_state(value); });
|
||||
|
||||
traits.set_options(source_->traits.get_options());
|
||||
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopySelect::dump_config() { LOG_SELECT("", "Copy Select", this); }
|
||||
|
||||
void CopySelect::control(const std::string &value) {
|
||||
auto call = source_->make_call();
|
||||
call.set_option(value);
|
||||
call.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/select/copy_select.h
Normal file
23
esphome/components/copy/select/copy_select.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/select/select.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopySelect : public select::Select, public Component {
|
||||
public:
|
||||
void set_source(select::Select *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
|
||||
select::Select *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
45
esphome/components/copy/sensor/__init__.py
Normal file
45
esphome/components/copy/sensor/__init__.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_SOURCE_ID,
|
||||
CONF_STATE_CLASS,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopySensor = copy_ns.class_("CopySensor", sensor.Sensor, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(CopySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(sensor.Sensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_UNIT_OF_MEASUREMENT, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ACCURACY_DECIMALS, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_STATE_CLASS, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
18
esphome/components/copy/sensor/copy_sensor.cpp
Normal file
18
esphome/components/copy/sensor/copy_sensor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "copy_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.sensor";
|
||||
|
||||
void CopySensor::setup() {
|
||||
source_->add_on_state_callback([this](float value) { this->publish_state(value); });
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopySensor::dump_config() { LOG_SENSOR("", "Copy Sensor", this); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
21
esphome/components/copy/sensor/copy_sensor.h
Normal file
21
esphome/components/copy/sensor/copy_sensor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopySensor : public sensor::Sensor, public Component {
|
||||
public:
|
||||
void set_source(sensor::Sensor *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
sensor::Sensor *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
38
esphome/components/copy/switch/__init__.py
Normal file
38
esphome/components/copy/switch/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopySwitch = copy_ns.class_("CopySwitch", switch.Switch, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopySwitch),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await switch.register_switch(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
26
esphome/components/copy/switch/copy_switch.cpp
Normal file
26
esphome/components/copy/switch/copy_switch.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "copy_switch.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.switch";
|
||||
|
||||
void CopySwitch::setup() {
|
||||
source_->add_on_state_callback([this](float value) { this->publish_state(value); });
|
||||
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopySwitch::dump_config() { LOG_SWITCH("", "Copy Switch", this); }
|
||||
|
||||
void CopySwitch::write_state(bool state) {
|
||||
if (state) {
|
||||
source_->turn_on();
|
||||
} else {
|
||||
source_->turn_off();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/switch/copy_switch.h
Normal file
23
esphome/components/copy/switch/copy_switch.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopySwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
void set_source(switch_::Switch *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
||||
switch_::Switch *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
37
esphome/components/copy/text_sensor/__init__.py
Normal file
37
esphome/components/copy/text_sensor/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyTextSensor = copy_ns.class_("CopyTextSensor", text_sensor.TextSensor, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
text_sensor.text_sensor_schema(CopyTextSensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(text_sensor.TextSensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
18
esphome/components/copy/text_sensor/copy_text_sensor.cpp
Normal file
18
esphome/components/copy/text_sensor/copy_text_sensor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "copy_text_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.text_sensor";
|
||||
|
||||
void CopyTextSensor::setup() {
|
||||
source_->add_on_state_callback([this](const std::string &value) { this->publish_state(value); });
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Copy Sensor", this); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
21
esphome/components/copy/text_sensor/copy_text_sensor.h
Normal file
21
esphome/components/copy/text_sensor/copy_text_sensor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyTextSensor : public text_sensor::TextSensor, public Component {
|
||||
public:
|
||||
void set_source(text_sensor::TextSensor *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
text_sensor::TextSensor *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
@@ -111,6 +111,9 @@ void DallasComponent::update() {
|
||||
if (!result) {
|
||||
ESP_LOGE(TAG, "Requesting conversion failed");
|
||||
this->status_set_warning();
|
||||
for (auto *sensor : this->sensors_) {
|
||||
sensor->publish_state(NAN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,39 @@ from esphome.const import (
|
||||
CONF_WAKEUP_PIN,
|
||||
)
|
||||
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C3,
|
||||
)
|
||||
|
||||
WAKEUP_PINS = {
|
||||
VARIANT_ESP32: [
|
||||
0,
|
||||
2,
|
||||
4,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
],
|
||||
VARIANT_ESP32C3: [0, 1, 2, 3, 4, 5],
|
||||
}
|
||||
|
||||
|
||||
def validate_pin_number(value):
|
||||
valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39]
|
||||
valid_pins = WAKEUP_PINS.get(get_esp32_variant(), WAKEUP_PINS[VARIANT_ESP32])
|
||||
if value[CONF_NUMBER] not in valid_pins:
|
||||
raise cv.Invalid(
|
||||
f"Only pins {', '.join(str(x) for x in valid_pins)} support wakeup"
|
||||
@@ -21,6 +51,14 @@ def validate_pin_number(value):
|
||||
return value
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
if get_esp32_variant() == VARIANT_ESP32C3 and CONF_ESP32_EXT1_WAKEUP in config:
|
||||
raise cv.Invalid("ESP32-C3 does not support wakeup from touch.")
|
||||
if get_esp32_variant() == VARIANT_ESP32C3 and CONF_TOUCH_WAKEUP in config:
|
||||
raise cv.Invalid("ESP32-C3 does not support wakeup from ext1")
|
||||
return config
|
||||
|
||||
|
||||
deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
|
||||
DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
|
||||
EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action)
|
||||
|
||||
@@ -104,7 +104,7 @@ void DeepSleepComponent::begin_sleep(bool manual) {
|
||||
|
||||
App.run_safe_shutdown_hooks();
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
if (this->sleep_duration_.has_value())
|
||||
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
|
||||
if (this->wakeup_pin_ != nullptr) {
|
||||
@@ -126,6 +126,18 @@ void DeepSleepComponent::begin_sleep(bool manual) {
|
||||
esp_deep_sleep_start();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_VARIANT_ESP32C3
|
||||
if (this->sleep_duration_.has_value())
|
||||
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
|
||||
if (this->wakeup_pin_ != nullptr) {
|
||||
bool level = !this->wakeup_pin_->is_inverted();
|
||||
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
||||
level = !level;
|
||||
}
|
||||
esp_deep_sleep_enable_gpio_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance)
|
||||
#endif
|
||||
|
||||
@@ -57,13 +57,16 @@ class DeepSleepComponent : public Component {
|
||||
public:
|
||||
/// Set the duration in ms the component should sleep once it's in deep sleep mode.
|
||||
void set_sleep_duration(uint32_t time_ms);
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32)
|
||||
/** Set the pin to wake up to on the ESP32 once it's in deep sleep mode.
|
||||
* Use the inverted property to set the wakeup level.
|
||||
*/
|
||||
void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; }
|
||||
|
||||
void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode);
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
|
||||
void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
|
||||
|
||||
|
||||
@@ -61,12 +61,30 @@ def set_core_data(config):
|
||||
return config
|
||||
|
||||
|
||||
def get_esp32_variant():
|
||||
return CORE.data[KEY_ESP32][KEY_VARIANT]
|
||||
def get_esp32_variant(core_obj=None):
|
||||
return (core_obj or CORE).data[KEY_ESP32][KEY_VARIANT]
|
||||
|
||||
|
||||
def is_esp32c3():
|
||||
return get_esp32_variant() == VARIANT_ESP32C3
|
||||
def only_on_variant(*, supported=None, unsupported=None):
|
||||
"""Config validator for features only available on some ESP32 variants."""
|
||||
if supported is not None and not isinstance(supported, list):
|
||||
supported = [supported]
|
||||
if unsupported is not None and not isinstance(unsupported, list):
|
||||
unsupported = [unsupported]
|
||||
|
||||
def validator_(obj):
|
||||
variant = get_esp32_variant()
|
||||
if supported is not None and variant not in supported:
|
||||
raise cv.Invalid(
|
||||
f"This feature is only available on {', '.join(supported)}"
|
||||
)
|
||||
if unsupported is not None and variant in unsupported:
|
||||
raise cv.Invalid(
|
||||
f"This feature is not available on {', '.join(unsupported)}"
|
||||
)
|
||||
return obj
|
||||
|
||||
return validator_
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664
|
||||
|
||||
import esptool
|
||||
import os
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
import esptool
|
||||
else:
|
||||
import subprocess
|
||||
from SCons.Script import ARGUMENTS
|
||||
|
||||
# pylint: disable=E0602
|
||||
@@ -42,8 +46,11 @@ def esp32_create_combined_bin(source, target, env):
|
||||
print()
|
||||
print(f"Using esptool.py arguments: {' '.join(cmd)}")
|
||||
print()
|
||||
esptool.main(cmd)
|
||||
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
esptool.main(cmd)
|
||||
else:
|
||||
subprocess.run(["esptool.py", *cmd])
|
||||
|
||||
# pylint: disable=E0602
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa
|
||||
|
||||
@@ -17,29 +17,29 @@ const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
|
||||
* turn
|
||||
* on temp mode fan swing
|
||||
* * | | | | | | *
|
||||
*
|
||||
*
|
||||
* temperatures 1 1248 124 124 1
|
||||
* auto auto 18 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000100 00000000 00000000 00000000 00000000 00000000 00000100 11110001
|
||||
* auto auto 19 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10001100 00000000 00000000 00000000 00000000 00000000 00000100 11111110
|
||||
* auto auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00000000 00000000 00000000 00000000 00000000 00000100 11110011
|
||||
*
|
||||
*
|
||||
* on flag:
|
||||
* on at 16 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000000 00100000 00000000 00000000 00000000 00000000 00000100 11010101
|
||||
* down to 16 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000000 00100000 00000000 00000000 00000000 00000000 00000100 00110101
|
||||
*
|
||||
*
|
||||
* mode options:
|
||||
* auto auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00000000 00000000 00000000 00000000 00000000 00000100 11110011
|
||||
* cool auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 10000000 00000000 00000000 00000000 00000000 00000100 01110011
|
||||
* dry auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 01000000 00000000 00000000 00000000 00000000 00000100 10110011
|
||||
* fan (auto) (30) 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 11000000 00000000 00000000 00000000 00000000 00000100 00110011
|
||||
* heat auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00100000 00000000 00000000 00000000 00000000 00000100 11010011
|
||||
*
|
||||
*
|
||||
* fan options:
|
||||
* heat 30 high 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00100000 10000000 00000000 00000000 00000000 00000100 01010011
|
||||
* heat 30 med 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 01000000 00000000 00000000 00000000 00000100 01010011
|
||||
* heat 30 low 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 11000000 00000000 00000000 00000000 00000100 10010011
|
||||
* heat 30 quiet 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 00100000 00000000 00000000 00000000 00000100 00010011
|
||||
*
|
||||
*
|
||||
* swing options:
|
||||
* heat 30 swing vert 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 00101000 00000000 00000000 00000000 00000100 00011101
|
||||
* heat 30 noswing 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 00100000 00000000 00000000 00000000 00000100 00010011
|
||||
|
||||
1
esphome/components/honeywellabp/__init__.py
Normal file
1
esphome/components/honeywellabp/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Support for Honeywell ABP"""
|
||||
102
esphome/components/honeywellabp/honeywellabp.cpp
Normal file
102
esphome/components/honeywellabp/honeywellabp.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "honeywellabp.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace honeywellabp {
|
||||
|
||||
static const char *const TAG = "honeywellabp";
|
||||
|
||||
const float MIN_COUNT = 1638.4; // 1638 counts (10% of 2^14 counts or 0x0666)
|
||||
const float MAX_COUNT = 14745.6; // 14745 counts (90% of 2^14 counts or 0x3999)
|
||||
|
||||
void HONEYWELLABPSensor::setup() {
|
||||
ESP_LOGD(TAG, "Setting up Honeywell ABP Sensor ");
|
||||
this->spi_setup();
|
||||
}
|
||||
|
||||
uint8_t HONEYWELLABPSensor::readsensor_() {
|
||||
// Polls the sensor for new data.
|
||||
// transfer 4 bytes (the last two are temperature only used by some sensors)
|
||||
this->enable();
|
||||
buf_[0] = this->read_byte();
|
||||
buf_[1] = this->read_byte();
|
||||
buf_[2] = this->read_byte();
|
||||
buf_[3] = this->read_byte();
|
||||
this->disable();
|
||||
|
||||
// Check the status codes:
|
||||
// status = 0 : normal operation
|
||||
// status = 1 : device in command mode
|
||||
// status = 2 : stale data
|
||||
// status = 3 : diagnostic condition
|
||||
status_ = buf_[0] >> 6 & 0x3;
|
||||
ESP_LOGV(TAG, "Sensor status %d", status_);
|
||||
|
||||
// if device is normal and there is new data, bitmask and save the raw data
|
||||
if (status_ == 0) {
|
||||
// 14 - bit pressure is the last 6 bits of byte 0 (high bits) & all of byte 1 (lowest 8 bits)
|
||||
pressure_count_ = ((uint16_t)(buf_[0]) << 8 & 0x3F00) | ((uint16_t)(buf_[1]) & 0xFF);
|
||||
// 11 - bit temperature is all of byte 2 (lowest 8 bits) and the first three bits of byte 3
|
||||
temperature_count_ = (((uint16_t)(buf_[2]) << 3) & 0x7F8) | (((uint16_t)(buf_[3]) >> 5) & 0x7);
|
||||
ESP_LOGV(TAG, "Sensor pressure_count_ %d", pressure_count_);
|
||||
ESP_LOGV(TAG, "Sensor temperature_count_ %d", temperature_count_);
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
|
||||
// returns status
|
||||
uint8_t HONEYWELLABPSensor::readstatus_() { return status_; }
|
||||
|
||||
// The pressure value from the most recent reading in raw counts
|
||||
int HONEYWELLABPSensor::rawpressure_() { return pressure_count_; }
|
||||
|
||||
// The temperature value from the most recent reading in raw counts
|
||||
int HONEYWELLABPSensor::rawtemperature_() { return temperature_count_; }
|
||||
|
||||
// Converts a digital pressure measurement in counts to pressure measured
|
||||
float HONEYWELLABPSensor::countstopressure_(const int counts, const float min_pressure, const float max_pressure) {
|
||||
return ((((float) counts - MIN_COUNT) * (max_pressure - min_pressure)) / (MAX_COUNT - MIN_COUNT)) + min_pressure;
|
||||
}
|
||||
|
||||
// Converts a digital temperature measurement in counts to temperature in C
|
||||
// This will be invalid if sensore daoes not have temperature measurement capability
|
||||
float HONEYWELLABPSensor::countstotemperatures_(const int counts) { return (((float) counts / 2047.0) * 200.0) - 50.0; }
|
||||
|
||||
// Pressure value from the most recent reading in units
|
||||
float HONEYWELLABPSensor::read_pressure_() {
|
||||
return countstopressure_(pressure_count_, honeywellabp_min_pressure_, honeywellabp_max_pressure_);
|
||||
}
|
||||
|
||||
// Temperature value from the most recent reading in degrees C
|
||||
float HONEYWELLABPSensor::read_temperature_() { return countstotemperatures_(temperature_count_); }
|
||||
|
||||
void HONEYWELLABPSensor::update() {
|
||||
ESP_LOGV(TAG, "Update Honeywell ABP Sensor");
|
||||
if (readsensor_() == 0) {
|
||||
if (this->pressure_sensor_ != nullptr)
|
||||
this->pressure_sensor_->publish_state(read_pressure_() * 1.0);
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(read_temperature_() * 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
float HONEYWELLABPSensor::get_setup_priority() const { return setup_priority::LATE; }
|
||||
|
||||
void HONEYWELLABPSensor::dump_config() {
|
||||
// LOG_SENSOR("", "HONEYWELLABP", this);
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", honeywellabp_min_pressure_);
|
||||
ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", honeywellabp_max_pressure_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void HONEYWELLABPSensor::set_honeywellabp_min_pressure(float min_pressure) {
|
||||
this->honeywellabp_min_pressure_ = min_pressure;
|
||||
}
|
||||
|
||||
void HONEYWELLABPSensor::set_honeywellabp_max_pressure(float max_pressure) {
|
||||
this->honeywellabp_max_pressure_ = max_pressure;
|
||||
}
|
||||
|
||||
} // namespace honeywellabp
|
||||
} // namespace esphome
|
||||
45
esphome/components/honeywellabp/honeywellabp.h
Normal file
45
esphome/components/honeywellabp/honeywellabp.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// for Honeywell ABP sensor
|
||||
// adapting code from https://github.com/vwls/Honeywell_pressure_sensors
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace honeywellabp {
|
||||
|
||||
class HONEYWELLABPSensor : public PollingComponent,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> {
|
||||
public:
|
||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void setup() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override;
|
||||
void dump_config() override;
|
||||
void set_honeywellabp_min_pressure(float min_pressure);
|
||||
void set_honeywellabp_max_pressure(float max_pressure);
|
||||
|
||||
protected:
|
||||
float honeywellabp_min_pressure_ = 0.0;
|
||||
float honeywellabp_max_pressure_ = 0.0;
|
||||
uint8_t buf_[4]; // buffer to hold sensor data
|
||||
uint8_t status_ = 0; // byte to hold status information.
|
||||
int pressure_count_ = 0; // hold raw pressure data (14 - bit, 0 - 16384)
|
||||
int temperature_count_ = 0; // hold raw temperature data (11 - bit, 0 - 2048)
|
||||
sensor::Sensor *pressure_sensor_;
|
||||
sensor::Sensor *temperature_sensor_;
|
||||
uint8_t readsensor_();
|
||||
uint8_t readstatus_();
|
||||
int rawpressure_();
|
||||
int rawtemperature_();
|
||||
float countstopressure_(int counts, float min_pressure, float max_pressure);
|
||||
float countstotemperatures_(int counts);
|
||||
float read_pressure_();
|
||||
float read_temperature_();
|
||||
};
|
||||
|
||||
} // namespace honeywellabp
|
||||
} // namespace esphome
|
||||
70
esphome/components/honeywellabp/sensor.py
Normal file
70
esphome/components/honeywellabp/sensor.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.components import spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
CODEOWNERS = ["@RubyBailey"]
|
||||
|
||||
CONF_MIN_PRESSURE = "min_pressure"
|
||||
CONF_MAX_PRESSURE = "max_pressure"
|
||||
|
||||
honeywellabp_ns = cg.esphome_ns.namespace("honeywellabp")
|
||||
HONEYWELLABPSensor = honeywellabp_ns.class_(
|
||||
"HONEYWELLABPSensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HONEYWELLABPSensor),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
unit_of_measurement="psi",
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_MIN_PRESSURE): cv.float_,
|
||||
cv.Required(CONF_MAX_PRESSURE): cv.float_,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(spi.spi_device_schema(cs_pin_required=True))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_honeywellabp_min_pressure(conf[CONF_MIN_PRESSURE]))
|
||||
cg.add(var.set_honeywellabp_max_pressure(conf[CONF_MAX_PRESSURE]))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
38
esphome/components/host/__init__.py
Normal file
38
esphome/components/host/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from .const import KEY_HOST
|
||||
|
||||
# force import gpio to register pin schema
|
||||
from .gpio import host_pin_to_code # noqa
|
||||
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
AUTO_LOAD = ["network"]
|
||||
|
||||
|
||||
def set_core_data(config):
|
||||
CORE.data[KEY_HOST] = {}
|
||||
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "host"
|
||||
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host"
|
||||
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema({}),
|
||||
set_core_data,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_build_flag("-DUSE_HOST")
|
||||
cg.add_define("ESPHOME_BOARD", "host")
|
||||
cg.add_platformio_option("platform", "platformio/native")
|
||||
5
esphome/components/host/const.py
Normal file
5
esphome/components/host/const.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
KEY_HOST = "host"
|
||||
|
||||
host_ns = cg.esphome_ns.namespace("host")
|
||||
74
esphome/components/host/core.cpp
Normal file
74
esphome/components/host/core.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#include <sched.h>
|
||||
#include <time.h>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
void IRAM_ATTR HOT yield() { ::sched_yield(); }
|
||||
uint32_t IRAM_ATTR HOT millis() {
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_MONOTONIC, &spec);
|
||||
time_t seconds = spec.tv_sec;
|
||||
uint32_t ms = round(spec.tv_nsec / 1e6);
|
||||
return ((uint32_t) seconds) * 1000U + ms;
|
||||
}
|
||||
void IRAM_ATTR HOT delay(uint32_t ms) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||
int res;
|
||||
do {
|
||||
res = nanosleep(&ts, &ts);
|
||||
} while (res != 0 && errno == EINTR);
|
||||
}
|
||||
uint32_t IRAM_ATTR HOT micros() {
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_MONOTONIC, &spec);
|
||||
time_t seconds = spec.tv_sec;
|
||||
uint32_t us = round(spec.tv_nsec / 1e3);
|
||||
return ((uint32_t) seconds) * 1000000U + us;
|
||||
}
|
||||
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = us / 1000000U;
|
||||
ts.tv_nsec = (us % 1000000U) * 1000U;
|
||||
int res;
|
||||
do {
|
||||
res = nanosleep(&ts, &ts);
|
||||
} while (res != 0 && errno == EINTR);
|
||||
}
|
||||
void arch_restart() { exit(0); }
|
||||
void IRAM_ATTR HOT arch_feed_wdt() {
|
||||
// pass
|
||||
}
|
||||
|
||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||
uint32_t arch_get_cpu_cycle_count() {
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_MONOTONIC, &spec);
|
||||
time_t seconds = spec.tv_sec;
|
||||
uint32_t us = spec.tv_nsec;
|
||||
return ((uint32_t) seconds) * 1000000000U + us;
|
||||
}
|
||||
uint32_t arch_get_cpu_freq_hz() { return 1000000000U; }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
void setup();
|
||||
void loop();
|
||||
int main() {
|
||||
esphome::host::setup_preferences();
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_HOST
|
||||
59
esphome/components/host/gpio.cpp
Normal file
59
esphome/components/host/gpio.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
static const char *const TAG = "host";
|
||||
|
||||
struct ISRPinArg {
|
||||
uint8_t pin;
|
||||
bool inverted;
|
||||
};
|
||||
|
||||
ISRInternalGPIOPin HostGPIOPin::to_isr() const {
|
||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
arg->pin = pin_;
|
||||
arg->inverted = inverted_;
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void HostGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
ESP_LOGD(TAG, "Attaching interrupt %p to pin %d and mode %d", func, pin_, (uint32_t) type);
|
||||
}
|
||||
void HostGPIOPin::pin_mode(gpio::Flags flags) { ESP_LOGD(TAG, "Setting pin %d mode to %02X", pin_, (uint32_t) flags); }
|
||||
|
||||
std::string HostGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool HostGPIOPin::digital_read() { return inverted_; }
|
||||
void HostGPIOPin::digital_write(bool value) {
|
||||
// pass
|
||||
ESP_LOGD(TAG, "Setting pin %d to %s", pin_, value != inverted_ ? "HIGH" : "LOW");
|
||||
}
|
||||
void HostGPIOPin::detach_interrupt() const {}
|
||||
|
||||
} // namespace host
|
||||
|
||||
using namespace host;
|
||||
|
||||
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
return arg->inverted;
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
||||
// pass
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
ESP_LOGD(TAG, "Clearing interrupt for pin %d", arg->pin);
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
37
esphome/components/host/gpio.h
Normal file
37
esphome/components/host/gpio.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
class HostGPIOPin : public InternalGPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
void set_flags(gpio::Flags flags) { flags_ = flags; }
|
||||
|
||||
void setup() override { pin_mode(flags_); }
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace host
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
73
esphome/components/host/gpio.py
Normal file
73
esphome/components/host/gpio.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import logging
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OPEN_DRAIN,
|
||||
CONF_OUTPUT,
|
||||
CONF_PULLDOWN,
|
||||
CONF_PULLUP,
|
||||
)
|
||||
from esphome import pins
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from .const import host_ns
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
HostGPIOPin = host_ns.class_("HostGPIOPin", cg.InternalGPIOPin)
|
||||
|
||||
|
||||
def _translate_pin(value):
|
||||
if isinstance(value, dict) or value is None:
|
||||
raise cv.Invalid(
|
||||
"This variable only supports pin numbers, not full pin schemas "
|
||||
"(with inverted and mode)."
|
||||
)
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.startswith("GPIO"):
|
||||
return cv.int_(value[len("GPIO") :].strip())
|
||||
return value
|
||||
|
||||
|
||||
def validate_gpio_pin(value):
|
||||
return _translate_pin(value)
|
||||
|
||||
|
||||
HOST_PIN_SCHEMA = cv.All(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HostGPIOPin),
|
||||
cv.Required(CONF_NUMBER): validate_gpio_pin,
|
||||
cv.Optional(CONF_MODE, default={}): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_INPUT, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PULLUP, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PULLDOWN, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register("host", HOST_PIN_SCHEMA)
|
||||
async def host_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
||||
33
esphome/components/host/preferences.cpp
Normal file
33
esphome/components/host/preferences.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "preferences.h"
|
||||
#include <cstring>
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
static const char *const TAG = "host.preferences";
|
||||
|
||||
class HostPreferences : public ESPPreferences {
|
||||
public:
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { return {}; }
|
||||
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type) override { return {}; }
|
||||
};
|
||||
|
||||
void setup_preferences() {
|
||||
auto *pref = new HostPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
global_preferences = pref;
|
||||
}
|
||||
|
||||
} // namespace host
|
||||
|
||||
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
13
esphome/components/host/preferences.h
Normal file
13
esphome/components/host/preferences.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_HOST
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
void setup_preferences();
|
||||
|
||||
} // namespace host
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -25,10 +25,10 @@ static const uint8_t PROGMEM INITCMD_M5STACK[] = {
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9341_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9341_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9341_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9341_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9341_DISPON , 0x80, // Display on
|
||||
@@ -55,10 +55,10 @@ static const uint8_t PROGMEM INITCMD_TFT[] = {
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9341_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9341_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9341_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9341_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9341_DISPON , 0x80, // Display on
|
||||
|
||||
@@ -22,7 +22,7 @@ std::string build_json(const json_build_t &f) {
|
||||
#ifdef USE_ESP8266
|
||||
const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP32)
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT) - 2048;
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) - 2048;
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument json_document(free_heap);
|
||||
@@ -42,7 +42,7 @@ void parse_json(const std::string &data, const json_parse_t &f) {
|
||||
#ifdef USE_ESP8266
|
||||
const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP32)
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT) - 2048;
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) - 2048;
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument json_document(free_heap);
|
||||
|
||||
@@ -52,6 +52,8 @@ RESTORE_MODES = {
|
||||
"ALWAYS_ON": LightRestoreMode.LIGHT_ALWAYS_ON,
|
||||
"RESTORE_INVERTED_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_OFF,
|
||||
"RESTORE_INVERTED_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_ON,
|
||||
"RESTORE_AND_OFF": LightRestoreMode.LIGHT_RESTORE_AND_OFF,
|
||||
"RESTORE_AND_ON": LightRestoreMode.LIGHT_RESTORE_AND_ON,
|
||||
}
|
||||
|
||||
LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
|
||||
@@ -68,6 +68,12 @@ void LightState::setup() {
|
||||
recovered.state = !recovered.state;
|
||||
}
|
||||
break;
|
||||
case LIGHT_RESTORE_AND_OFF:
|
||||
case LIGHT_RESTORE_AND_ON:
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
|
||||
this->rtc_.load(&recovered);
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
case LIGHT_ALWAYS_OFF:
|
||||
recovered.state = false;
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,8 @@ enum LightRestoreMode {
|
||||
LIGHT_ALWAYS_ON,
|
||||
LIGHT_RESTORE_INVERTED_DEFAULT_OFF,
|
||||
LIGHT_RESTORE_INVERTED_DEFAULT_ON,
|
||||
LIGHT_RESTORE_AND_OFF,
|
||||
LIGHT_RESTORE_AND_ON,
|
||||
};
|
||||
|
||||
/** This class represents the communication layer between the front-end MQTT layer and the
|
||||
|
||||
@@ -113,8 +113,27 @@ void LilygoT547Touchscreen::loop() {
|
||||
if (tp.state == 0x06)
|
||||
tp.state = 0x07;
|
||||
|
||||
tp.y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
||||
tp.x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
||||
uint16_t y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
||||
|
||||
switch (this->rotation_) {
|
||||
case ROTATE_0_DEGREES:
|
||||
tp.y = this->display_height_ - y;
|
||||
tp.x = x;
|
||||
break;
|
||||
case ROTATE_90_DEGREES:
|
||||
tp.x = this->display_height_ - y;
|
||||
tp.y = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_180_DEGREES:
|
||||
tp.y = y;
|
||||
tp.x = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_270_DEGREES:
|
||||
tp.x = y;
|
||||
tp.y = x;
|
||||
break;
|
||||
}
|
||||
|
||||
this->defer([this, tp]() { this->send_touch_(tp); });
|
||||
}
|
||||
@@ -122,8 +141,28 @@ void LilygoT547Touchscreen::loop() {
|
||||
TouchPoint tp;
|
||||
tp.id = (buffer[0] >> 4) & 0x0F;
|
||||
tp.state = 0x06;
|
||||
tp.y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
||||
tp.x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
||||
|
||||
uint16_t y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
||||
|
||||
switch (this->rotation_) {
|
||||
case ROTATE_0_DEGREES:
|
||||
tp.y = this->display_height_ - y;
|
||||
tp.x = x;
|
||||
break;
|
||||
case ROTATE_90_DEGREES:
|
||||
tp.x = this->display_height_ - y;
|
||||
tp.y = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_180_DEGREES:
|
||||
tp.y = y;
|
||||
tp.x = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_270_DEGREES:
|
||||
tp.x = y;
|
||||
tp.y = x;
|
||||
break;
|
||||
}
|
||||
|
||||
this->defer([this, tp]() { this->send_touch_(tp); });
|
||||
}
|
||||
|
||||
@@ -113,7 +113,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
|
||||
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes,
|
||||
cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HARDWARE_UART, default="UART0"): uart_selection,
|
||||
cv.SplitDefault(CONF_HARDWARE_UART, esp32="UART0", esp8266="UART0"): cv.All(
|
||||
cv.only_on(["esp32", "esp8266"]), uart_selection
|
||||
),
|
||||
cv.Optional(CONF_LEVEL, default="DEBUG"): is_log_level,
|
||||
cv.Optional(CONF_LOGS, default={}): cv.Schema(
|
||||
{
|
||||
@@ -138,12 +140,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
@coroutine_with_priority(90.0)
|
||||
async def to_code(config):
|
||||
baud_rate = config[CONF_BAUD_RATE]
|
||||
rhs = Logger.new(
|
||||
baud_rate,
|
||||
config[CONF_TX_BUFFER_SIZE],
|
||||
HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]],
|
||||
)
|
||||
log = cg.Pvariable(config[CONF_ID], rhs)
|
||||
log = cg.new_Pvariable(config[CONF_ID], baud_rate, config[CONF_TX_BUFFER_SIZE])
|
||||
if CONF_HARDWARE_UART in config:
|
||||
cg.add(log.set_uart_selection(config[CONF_HARDWARE_UART]))
|
||||
cg.add(log.pre_setup())
|
||||
|
||||
for tag, level in config[CONF_LOGS].items():
|
||||
|
||||
@@ -130,12 +130,14 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
|
||||
if (xPortGetFreeHeapSize() < 2048)
|
||||
return;
|
||||
#endif
|
||||
#ifdef USE_HOST
|
||||
puts(msg);
|
||||
#endif
|
||||
|
||||
this->log_callback_.call(level, tag, msg);
|
||||
}
|
||||
|
||||
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart)
|
||||
: baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size), uart_(uart) {
|
||||
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
|
||||
// add 1 to buffer size for null terminator
|
||||
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
|
||||
}
|
||||
@@ -217,7 +219,11 @@ void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
|
||||
void Logger::set_log_level(const std::string &tag, int log_level) {
|
||||
this->log_levels_.push_back(LogLevelOverride{tag, log_level});
|
||||
}
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266)
|
||||
UARTSelection Logger::get_uart() const { return this->uart_; }
|
||||
#endif
|
||||
|
||||
void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) {
|
||||
this->log_callback_.add(std::move(callback));
|
||||
}
|
||||
@@ -233,7 +239,10 @@ void Logger::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Logger:");
|
||||
ESP_LOGCONFIG(TAG, " Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||
ESP_LOGCONFIG(TAG, " Log Baud Rate: %u", this->baud_rate_);
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266)
|
||||
ESP_LOGCONFIG(TAG, " Hardware UART: %s", UART_SELECTIONS[this->uart_]);
|
||||
#endif
|
||||
|
||||
for (auto &it : this->log_levels_) {
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace esphome {
|
||||
|
||||
namespace logger {
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266)
|
||||
/** Enum for logging UART selection
|
||||
*
|
||||
* Advanced configuration (pin selection, etc) is not supported.
|
||||
@@ -31,10 +32,11 @@ enum UARTSelection {
|
||||
UART_SELECTION_UART0_SWAP,
|
||||
#endif
|
||||
};
|
||||
#endif // USE_ESP32 || USE_ESP8266
|
||||
|
||||
class Logger : public Component {
|
||||
public:
|
||||
explicit Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart);
|
||||
explicit Logger(uint32_t baud_rate, size_t tx_buffer_size);
|
||||
|
||||
/// Manually set the baud rate for serial, set to 0 to disable.
|
||||
void set_baud_rate(uint32_t baud_rate);
|
||||
@@ -46,8 +48,11 @@ class Logger : public Component {
|
||||
uart_port_t get_uart_num() const { return uart_num_; }
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266)
|
||||
void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
|
||||
/// Get the UART used by the logger.
|
||||
UARTSelection get_uart() const;
|
||||
#endif
|
||||
|
||||
/// Set the log level of the specified tag.
|
||||
void set_log_level(const std::string &tag, int log_level);
|
||||
@@ -117,7 +122,9 @@ class Logger : public Component {
|
||||
char *tx_buffer_{nullptr};
|
||||
int tx_buffer_at_{0};
|
||||
int tx_buffer_size_{0};
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266)
|
||||
UARTSelection uart_{UART_SELECTION_UART0};
|
||||
#endif
|
||||
#ifdef USE_ARDUINO
|
||||
HardwareSerial *hw_serial_{nullptr};
|
||||
#endif
|
||||
|
||||
0
esphome/components/max44009/__init__.py
Normal file
0
esphome/components/max44009/__init__.py
Normal file
144
esphome/components/max44009/max44009.cpp
Normal file
144
esphome/components/max44009/max44009.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "max44009.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace max44009 {
|
||||
|
||||
static const char *const TAG = "max44009.sensor";
|
||||
|
||||
// REGISTERS
|
||||
static const uint8_t MAX44009_REGISTER_CONFIGURATION = 0x02;
|
||||
static const uint8_t MAX44009_LUX_READING_HIGH = 0x03;
|
||||
static const uint8_t MAX44009_LUX_READING_LOW = 0x04;
|
||||
// CONFIGURATION MASKS
|
||||
static const uint8_t MAX44009_CFG_CONTINUOUS = 0x80;
|
||||
// ERROR CODES
|
||||
static const uint8_t MAX44009_OK = 0;
|
||||
static const uint8_t MAX44009_ERROR_WIRE_REQUEST = -10;
|
||||
static const uint8_t MAX44009_ERROR_OVERFLOW = -20;
|
||||
static const uint8_t MAX44009_ERROR_HIGH_BYTE = -30;
|
||||
static const uint8_t MAX44009_ERROR_LOW_BYTE = -31;
|
||||
|
||||
void MAX44009Sensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MAX44009...");
|
||||
bool state_ok = false;
|
||||
if (this->mode_ == MAX44009Mode::MAX44009_MODE_LOW_POWER) {
|
||||
state_ok = this->set_low_power_mode();
|
||||
} else if (this->mode_ == MAX44009Mode::MAX44009_MODE_CONTINUOUS) {
|
||||
state_ok = this->set_continuous_mode();
|
||||
} else {
|
||||
/*
|
||||
* Mode AUTO: Set mode depending on update interval
|
||||
* - On low power mode, the IC measures lux intensity only once every 800ms
|
||||
* regardless of integration time
|
||||
* - On continuous mode, the IC continuously measures lux intensity
|
||||
*/
|
||||
if (this->get_update_interval() < 800) {
|
||||
state_ok = this->set_continuous_mode();
|
||||
} else {
|
||||
state_ok = this->set_low_power_mode();
|
||||
}
|
||||
}
|
||||
if (!state_ok)
|
||||
this->mark_failed();
|
||||
}
|
||||
|
||||
void MAX44009Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MAX44009:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with MAX44009 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
float MAX44009Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void MAX44009Sensor::update() {
|
||||
// update sensor illuminance value
|
||||
float lux = this->read_illuminance_();
|
||||
if (this->error_ != MAX44009_OK) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
this->publish_state(lux);
|
||||
}
|
||||
}
|
||||
|
||||
float MAX44009Sensor::read_illuminance_() {
|
||||
uint8_t datahigh = this->read_(MAX44009_LUX_READING_HIGH);
|
||||
if (error_ != MAX44009_OK) {
|
||||
this->error_ = MAX44009_ERROR_HIGH_BYTE;
|
||||
return this->error_;
|
||||
}
|
||||
uint8_t datalow = this->read_(MAX44009_LUX_READING_LOW);
|
||||
if (error_ != MAX44009_OK) {
|
||||
this->error_ = MAX44009_ERROR_LOW_BYTE;
|
||||
return this->error_;
|
||||
}
|
||||
uint8_t exponent = datahigh >> 4;
|
||||
if (exponent == 0x0F) {
|
||||
this->error_ = MAX44009_ERROR_OVERFLOW;
|
||||
return this->error_;
|
||||
}
|
||||
|
||||
return this->convert_to_lux_(datahigh, datalow);
|
||||
}
|
||||
|
||||
float MAX44009Sensor::convert_to_lux_(uint8_t data_high, uint8_t data_low) {
|
||||
uint8_t exponent = data_high >> 4;
|
||||
uint32_t mantissa = ((data_high & 0x0F) << 4) + (data_low & 0x0F);
|
||||
return ((0x0001 << exponent) * 0.045) * mantissa;
|
||||
}
|
||||
|
||||
bool MAX44009Sensor::set_continuous_mode() {
|
||||
uint8_t config = this->read_(MAX44009_REGISTER_CONFIGURATION);
|
||||
if (this->error_ == MAX44009_OK) {
|
||||
config |= MAX44009_CFG_CONTINUOUS;
|
||||
this->write_(MAX44009_REGISTER_CONFIGURATION, config);
|
||||
this->status_clear_warning();
|
||||
ESP_LOGV(TAG, "set to continuous mode");
|
||||
return true;
|
||||
} else {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MAX44009Sensor::set_low_power_mode() {
|
||||
uint8_t config = this->read_(MAX44009_REGISTER_CONFIGURATION);
|
||||
if (this->error_ == MAX44009_OK) {
|
||||
config &= ~MAX44009_CFG_CONTINUOUS;
|
||||
this->write_(MAX44009_REGISTER_CONFIGURATION, config);
|
||||
this->status_clear_warning();
|
||||
ESP_LOGV(TAG, "set to low power mode");
|
||||
return true;
|
||||
} else {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MAX44009Sensor::read_(uint8_t reg) {
|
||||
uint8_t data = 0;
|
||||
if (!this->read_byte(reg, &data)) {
|
||||
this->error_ = MAX44009_ERROR_WIRE_REQUEST;
|
||||
} else {
|
||||
this->error_ = MAX44009_OK;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void MAX44009Sensor::write_(uint8_t reg, uint8_t value) {
|
||||
if (!this->write_byte(reg, value)) {
|
||||
this->error_ = MAX44009_ERROR_WIRE_REQUEST;
|
||||
} else {
|
||||
this->error_ = MAX44009_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX44009Sensor::set_mode(MAX44009Mode mode) { this->mode_ = mode; }
|
||||
|
||||
} // namespace max44009
|
||||
} // namespace esphome
|
||||
37
esphome/components/max44009/max44009.h
Normal file
37
esphome/components/max44009/max44009.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace max44009 {
|
||||
|
||||
enum MAX44009Mode { MAX44009_MODE_AUTO, MAX44009_MODE_LOW_POWER, MAX44009_MODE_CONTINUOUS };
|
||||
|
||||
/// This class implements support for the MAX44009 Illuminance i2c sensor.
|
||||
class MAX44009Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
MAX44009Sensor() {}
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
void set_mode(MAX44009Mode mode);
|
||||
bool set_continuous_mode();
|
||||
bool set_low_power_mode();
|
||||
|
||||
protected:
|
||||
/// Read the illuminance value
|
||||
float read_illuminance_();
|
||||
float convert_to_lux_(uint8_t data_high, uint8_t data_low);
|
||||
uint8_t read_(uint8_t reg);
|
||||
void write_(uint8_t reg, uint8_t value);
|
||||
|
||||
int error_;
|
||||
MAX44009Mode mode_;
|
||||
};
|
||||
|
||||
} // namespace max44009
|
||||
} // namespace esphome
|
||||
53
esphome/components/max44009/sensor.py
Normal file
53
esphome/components/max44009/sensor.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, i2c
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MODE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@berfenger"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
max44009_ns = cg.esphome_ns.namespace("max44009")
|
||||
MAX44009Sensor = max44009_ns.class_(
|
||||
"MAX44009Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
MAX44009Mode = max44009_ns.enum("MAX44009Mode")
|
||||
MODE_OPTIONS = {
|
||||
"auto": MAX44009Mode.MAX44009_MODE_AUTO,
|
||||
"low_power": MAX44009Mode.MAX44009_MODE_LOW_POWER,
|
||||
"continuous": MAX44009Mode.MAX44009_MODE_CONTINUOUS,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX44009Sensor),
|
||||
cv.Optional(CONF_MODE, default="low_power"): cv.enum(
|
||||
MODE_OPTIONS, lower=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x4A))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(var.set_mode(config[CONF_MODE]))
|
||||
29
esphome/components/mcp4728/__init__.py
Normal file
29
esphome/components/mcp4728/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@berfenger"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
CONF_STORE_IN_EEPROM = "store_in_eeprom"
|
||||
|
||||
mcp4728_ns = cg.esphome_ns.namespace("mcp4728")
|
||||
MCP4728Component = mcp4728_ns.class_("MCP4728Component", cg.Component, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MCP4728Component),
|
||||
cv.Optional(CONF_STORE_IN_EEPROM, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x60))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_STORE_IN_EEPROM])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
121
esphome/components/mcp4728/mcp4728_output.cpp
Normal file
121
esphome/components/mcp4728/mcp4728_output.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "mcp4728_output.h"
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp4728 {
|
||||
|
||||
static const char *const TAG = "mcp4728";
|
||||
|
||||
void MCP4728Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MCP4728 (0x%02X)...", this->address_);
|
||||
auto err = this->write(nullptr, 0);
|
||||
if (err != i2c::ERROR_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MCP4728:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with MCP4728 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::loop() {
|
||||
if (this->update_) {
|
||||
this->update_ = false;
|
||||
if (this->store_in_eeprom_) {
|
||||
if (!this->seq_write_()) {
|
||||
this->status_set_error();
|
||||
} else {
|
||||
this->status_clear_error();
|
||||
}
|
||||
} else {
|
||||
if (!this->multi_write_()) {
|
||||
this->status_set_error();
|
||||
} else {
|
||||
this->status_clear_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::set_channel_value_(MCP4728ChannelIdx channel, uint16_t value) {
|
||||
uint8_t cn = 0;
|
||||
if (channel == MCP4728_CHANNEL_A) {
|
||||
cn = 'A';
|
||||
} else if (channel == MCP4728_CHANNEL_B) {
|
||||
cn = 'B';
|
||||
} else if (channel == MCP4728_CHANNEL_C) {
|
||||
cn = 'C';
|
||||
} else {
|
||||
cn = 'D';
|
||||
}
|
||||
ESP_LOGV(TAG, "Setting MCP4728 channel %c to %d!", cn, value);
|
||||
reg_[channel].data = value;
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
bool MCP4728Component::multi_write_() {
|
||||
i2c::ErrorCode err[4];
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
uint8_t wd[3];
|
||||
wd[0] = ((uint8_t) CMD::MULTI_WRITE | (i << 1)) & 0xFE;
|
||||
wd[1] = ((uint8_t) reg_[i].vref << 7) | ((uint8_t) reg_[i].pd << 5) | ((uint8_t) reg_[i].gain << 4) |
|
||||
(reg_[i].data >> 8);
|
||||
wd[2] = reg_[i].data & 0xFF;
|
||||
err[i] = this->write(wd, sizeof(wd));
|
||||
}
|
||||
bool ok = true;
|
||||
for (auto &e : err) {
|
||||
if (e != i2c::ERROR_OK) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool MCP4728Component::seq_write_() {
|
||||
uint8_t wd[9];
|
||||
wd[0] = (uint8_t) CMD::SEQ_WRITE;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
wd[i * 2 + 1] = ((uint8_t) reg_[i].vref << 7) | ((uint8_t) reg_[i].pd << 5) | ((uint8_t) reg_[i].gain << 4) |
|
||||
(reg_[i].data >> 8);
|
||||
wd[i * 2 + 2] = reg_[i].data & 0xFF;
|
||||
}
|
||||
auto err = this->write(wd, sizeof(wd));
|
||||
return err == i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_vref_(MCP4728ChannelIdx channel, MCP4728Vref vref) {
|
||||
reg_[channel].vref = vref;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_power_down_(MCP4728ChannelIdx channel, MCP4728PwrDown pd) {
|
||||
reg_[channel].pd = pd;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_gain_(MCP4728ChannelIdx channel, MCP4728Gain gain) {
|
||||
reg_[channel].gain = gain;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Channel::write_state(float state) {
|
||||
const uint16_t max_duty = 4095;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint16_t>(duty_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
}
|
||||
|
||||
} // namespace mcp4728
|
||||
} // namespace esphome
|
||||
91
esphome/components/mcp4728/mcp4728_output.h
Normal file
91
esphome/components/mcp4728/mcp4728_output.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp4728 {
|
||||
|
||||
enum class CMD {
|
||||
FAST_WRITE = 0x00,
|
||||
MULTI_WRITE = 0x40,
|
||||
SINGLE_WRITE = 0x58,
|
||||
SEQ_WRITE = 0x50,
|
||||
SELECT_VREF = 0x80,
|
||||
SELECT_GAIN = 0xC0,
|
||||
SELECT_POWER_DOWN = 0xA0
|
||||
};
|
||||
|
||||
enum MCP4728Vref { MCP4728_VREF_VDD = 0, MCP4728_VREF_INTERNAL_2_8V = 1 };
|
||||
|
||||
enum MCP4728PwrDown {
|
||||
MCP4728_PD_NORMAL = 0,
|
||||
MCP4728_PD_GND_1KOHM = 1,
|
||||
MCP4728_PD_GND_100KOHM = 2,
|
||||
MCP4728_PD_GND_500KOHM = 3
|
||||
};
|
||||
|
||||
enum MCP4728Gain { MCP4728_GAIN_X1 = 0, MCP4728_GAIN_X2 = 1 };
|
||||
|
||||
enum MCP4728ChannelIdx { MCP4728_CHANNEL_A = 0, MCP4728_CHANNEL_B = 1, MCP4728_CHANNEL_C = 2, MCP4728_CHANNEL_D = 3 };
|
||||
|
||||
struct DACInputData {
|
||||
MCP4728Vref vref;
|
||||
MCP4728PwrDown pd;
|
||||
MCP4728Gain gain;
|
||||
uint16_t data;
|
||||
};
|
||||
|
||||
class MCP4728Channel;
|
||||
|
||||
/// MCP4728 float output component.
|
||||
class MCP4728Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
MCP4728Component(bool store_in_eeprom) : store_in_eeprom_(store_in_eeprom) {}
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
|
||||
protected:
|
||||
friend MCP4728Channel;
|
||||
void set_channel_value_(MCP4728ChannelIdx channel, uint16_t value);
|
||||
bool multi_write_();
|
||||
bool seq_write_();
|
||||
void select_vref_(MCP4728ChannelIdx channel, MCP4728Vref vref);
|
||||
void select_power_down_(MCP4728ChannelIdx channel, MCP4728PwrDown pd);
|
||||
void select_gain_(MCP4728ChannelIdx channel, MCP4728Gain gain);
|
||||
|
||||
private:
|
||||
DACInputData reg_[4];
|
||||
bool store_in_eeprom_ = false;
|
||||
bool update_ = false;
|
||||
};
|
||||
|
||||
class MCP4728Channel : public output::FloatOutput {
|
||||
public:
|
||||
MCP4728Channel(MCP4728Component *parent, MCP4728ChannelIdx channel, MCP4728Vref vref, MCP4728Gain gain,
|
||||
MCP4728PwrDown pwrdown)
|
||||
: parent_(parent), channel_(channel), vref_(vref), gain_(gain), pwrdown_(pwrdown) {
|
||||
// update VREF
|
||||
parent->select_vref_(channel, vref_);
|
||||
// update PD
|
||||
parent->select_power_down_(channel, pwrdown_);
|
||||
// update GAIN
|
||||
parent->select_gain_(channel, gain_);
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
MCP4728Component *parent_;
|
||||
MCP4728ChannelIdx channel_;
|
||||
MCP4728Vref vref_;
|
||||
MCP4728Gain gain_;
|
||||
MCP4728PwrDown pwrdown_;
|
||||
};
|
||||
|
||||
} // namespace mcp4728
|
||||
} // namespace esphome
|
||||
63
esphome/components/mcp4728/output.py
Normal file
63
esphome/components/mcp4728/output.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_GAIN
|
||||
from . import MCP4728Component, mcp4728_ns
|
||||
|
||||
DEPENDENCIES = ["mcp4728"]
|
||||
|
||||
MCP4728Channel = mcp4728_ns.class_("MCP4728Channel", output.FloatOutput)
|
||||
CONF_MCP4728_ID = "mcp4728_id"
|
||||
CONF_VREF = "vref"
|
||||
CONF_POWER_DOWN = "power_down"
|
||||
|
||||
MCP4728Vref = mcp4728_ns.enum("MCP4728Vref")
|
||||
VREF_OPTIONS = {
|
||||
"vdd": MCP4728Vref.MCP4728_VREF_VDD,
|
||||
"internal": MCP4728Vref.MCP4728_VREF_INTERNAL_2_8V,
|
||||
}
|
||||
|
||||
MCP4728Gain = mcp4728_ns.enum("MCP4728Gain")
|
||||
GAIN_OPTIONS = {"X1": MCP4728Gain.MCP4728_GAIN_X1, "X2": MCP4728Gain.MCP4728_GAIN_X2}
|
||||
|
||||
MCP4728PwrDown = mcp4728_ns.enum("MCP4728PwrDown")
|
||||
PWRDOWN_OPTIONS = {
|
||||
"normal": MCP4728PwrDown.MCP4728_PD_NORMAL,
|
||||
"gnd_1k": MCP4728PwrDown.MCP4728_PD_GND_1KOHM,
|
||||
"gnd_100k": MCP4728PwrDown.MCP4728_PD_GND_100KOHM,
|
||||
"gnd_500k": MCP4728PwrDown.MCP4728_PD_GND_500KOHM,
|
||||
}
|
||||
|
||||
MCP4728ChannelIdx = mcp4728_ns.enum("MCP4728ChannelIdx")
|
||||
CHANNEL_OPTIONS = {
|
||||
"A": MCP4728ChannelIdx.MCP4728_CHANNEL_A,
|
||||
"B": MCP4728ChannelIdx.MCP4728_CHANNEL_B,
|
||||
"C": MCP4728ChannelIdx.MCP4728_CHANNEL_C,
|
||||
"D": MCP4728ChannelIdx.MCP4728_CHANNEL_D,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(MCP4728Channel),
|
||||
cv.GenerateID(CONF_MCP4728_ID): cv.use_id(MCP4728Component),
|
||||
cv.Required(CONF_CHANNEL): cv.enum(CHANNEL_OPTIONS, upper=True),
|
||||
cv.Optional(CONF_VREF, default="vdd"): cv.enum(VREF_OPTIONS, upper=False),
|
||||
cv.Optional(CONF_POWER_DOWN, default="normal"): cv.enum(
|
||||
PWRDOWN_OPTIONS, upper=False
|
||||
),
|
||||
cv.Optional(CONF_GAIN, default="X1"): cv.enum(GAIN_OPTIONS, upper=True),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_MCP4728_ID])
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
paren,
|
||||
config[CONF_CHANNEL],
|
||||
config[CONF_VREF],
|
||||
config[CONF_GAIN],
|
||||
config[CONF_POWER_DOWN],
|
||||
)
|
||||
await output.register_output(var, config)
|
||||
23
esphome/components/mopeka_ble/__init__.py
Normal file
23
esphome/components/mopeka_ble/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import esp32_ble_tracker
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@spbrogan"]
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
|
||||
mopeka_ble_ns = cg.esphome_ns.namespace("mopeka_ble")
|
||||
MopekaListener = mopeka_ble_ns.class_(
|
||||
"MopekaListener", esp32_ble_tracker.ESPBTDeviceListener
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MopekaListener),
|
||||
}
|
||||
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
50
esphome/components/mopeka_ble/mopeka_ble.cpp
Normal file
50
esphome/components/mopeka_ble/mopeka_ble.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "mopeka_ble.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace mopeka_ble {
|
||||
|
||||
static const char *const TAG = "mopeka_ble";
|
||||
static const uint8_t MANUFACTURER_DATA_LENGTH = 10;
|
||||
static const uint16_t MANUFACTURER_ID = 0x0059;
|
||||
|
||||
/**
|
||||
* Parse all incoming BLE payloads to see if it is a Mopeka BLE advertisement.
|
||||
* Currently this supports the following products:
|
||||
*
|
||||
* Mopeka Pro Check.
|
||||
* If the sync button is pressed, report the MAC so a user can add this as a sensor.
|
||||
*/
|
||||
|
||||
bool MopekaListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
const auto &manu_datas = device.get_manufacturer_datas();
|
||||
|
||||
if (manu_datas.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &manu_data = manu_datas[0];
|
||||
|
||||
if (manu_data.data.size() != MANUFACTURER_DATA_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (manu_data.uuid != esp32_ble_tracker::ESPBTUUID::from_uint16(MANUFACTURER_ID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->parse_sync_button_(manu_data.data)) {
|
||||
// button pressed
|
||||
ESP_LOGI(TAG, "SENSOR FOUND: %s", device.address_str().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MopekaListener::parse_sync_button_(const std::vector<uint8_t> &message) { return (message[2] & 0x80) != 0; }
|
||||
|
||||
} // namespace mopeka_ble
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
22
esphome/components/mopeka_ble/mopeka_ble.h
Normal file
22
esphome/components/mopeka_ble/mopeka_ble.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace mopeka_ble {
|
||||
|
||||
class MopekaListener : public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
|
||||
protected:
|
||||
bool parse_sync_button_(const std::vector<uint8_t> &message);
|
||||
};
|
||||
|
||||
} // namespace mopeka_ble
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
1
esphome/components/mopeka_pro_check/__init__.py
Normal file
1
esphome/components/mopeka_pro_check/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@spbrogan"]
|
||||
136
esphome/components/mopeka_pro_check/mopeka_pro_check.cpp
Normal file
136
esphome/components/mopeka_pro_check/mopeka_pro_check.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "mopeka_pro_check.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace mopeka_pro_check {
|
||||
|
||||
static const char *const TAG = "mopeka_pro_check";
|
||||
static const uint8_t MANUFACTURER_DATA_LENGTH = 10;
|
||||
static const uint16_t MANUFACTURER_ID = 0x0059;
|
||||
static const double MOPEKA_LPG_COEF[] = {0.573045, -0.002822, -0.00000535}; // Magic numbers provided by Mopeka
|
||||
|
||||
void MopekaProCheck::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Mopeka Pro Check");
|
||||
LOG_SENSOR(" ", "Level", this->level_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
LOG_SENSOR(" ", "Reading Distance", this->distance_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main parse function that gets called for all ble advertisements.
|
||||
* Check if advertisement is for our sensor and if so decode it and
|
||||
* update the sensor state data.
|
||||
*/
|
||||
bool MopekaProCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
if (device.address_uint64() != this->address_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
|
||||
|
||||
const auto &manu_datas = device.get_manufacturer_datas();
|
||||
|
||||
if (manu_datas.size() != 1) {
|
||||
ESP_LOGE(TAG, "Unexpected manu_datas size (%d)", manu_datas.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &manu_data = manu_datas[0];
|
||||
|
||||
ESP_LOGVV(TAG, "Manufacturer data:");
|
||||
for (const uint8_t byte : manu_data.data) {
|
||||
ESP_LOGVV(TAG, "0x%02x", byte);
|
||||
}
|
||||
|
||||
if (manu_data.data.size() != MANUFACTURER_DATA_LENGTH) {
|
||||
ESP_LOGE(TAG, "Unexpected manu_data size (%d)", manu_data.data.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now parse the data - See Datasheet for definition
|
||||
|
||||
if (static_cast<SensorType>(manu_data.data[0]) != STANDARD_BOTTOM_UP) {
|
||||
ESP_LOGE(TAG, "Unsupported Sensor Type (0x%X)", manu_data.data[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get battery level first
|
||||
if (this->battery_level_ != nullptr) {
|
||||
uint8_t level = this->parse_battery_level_(manu_data.data);
|
||||
this->battery_level_->publish_state(level);
|
||||
}
|
||||
|
||||
// Get distance and level if either are sensors
|
||||
if ((this->distance_ != nullptr) || (this->level_ != nullptr)) {
|
||||
uint32_t distance_value = this->parse_distance_(manu_data.data);
|
||||
SensorReadQuality quality_value = this->parse_read_quality_(manu_data.data);
|
||||
ESP_LOGD(TAG, "Distance Sensor: Quality (0x%X) Distance (%dmm)", quality_value, distance_value);
|
||||
if (quality_value < QUALITY_HIGH) {
|
||||
ESP_LOGW(TAG, "Poor read quality.");
|
||||
}
|
||||
if (quality_value < QUALITY_MED) {
|
||||
// if really bad reading set to 0
|
||||
ESP_LOGW(TAG, "Setting distance to 0");
|
||||
distance_value = 0;
|
||||
}
|
||||
|
||||
// update distance sensor
|
||||
if (this->distance_ != nullptr) {
|
||||
this->distance_->publish_state(distance_value);
|
||||
}
|
||||
|
||||
// update level sensor
|
||||
if (this->level_ != nullptr) {
|
||||
uint8_t tank_level = 0;
|
||||
if (distance_value >= this->full_mm_) {
|
||||
tank_level = 100; // cap at 100%
|
||||
} else if (distance_value > this->empty_mm_) {
|
||||
tank_level = ((100.0f / (this->full_mm_ - this->empty_mm_)) * (distance_value - this->empty_mm_));
|
||||
}
|
||||
this->level_->publish_state(tank_level);
|
||||
}
|
||||
}
|
||||
|
||||
// Get temperature of sensor
|
||||
if (this->temperature_ != nullptr) {
|
||||
uint8_t temp_in_c = this->parse_temperature_(manu_data.data);
|
||||
this->temperature_->publish_state(temp_in_c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t MopekaProCheck::parse_battery_level_(const std::vector<uint8_t> &message) {
|
||||
float v = (float) ((message[1] & 0x7F) / 32.0f);
|
||||
// convert voltage and scale for CR2032
|
||||
float percent = (v - 2.2f) / 0.65f * 100.0f;
|
||||
if (percent < 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
if (percent > 100.0f) {
|
||||
return 100;
|
||||
}
|
||||
return (uint8_t) percent;
|
||||
}
|
||||
|
||||
uint32_t MopekaProCheck::parse_distance_(const std::vector<uint8_t> &message) {
|
||||
uint16_t raw = (message[4] * 256) + message[3];
|
||||
double raw_level = raw & 0x3FFF;
|
||||
double raw_t = (message[2] & 0x7F);
|
||||
|
||||
return (uint32_t)(raw_level * (MOPEKA_LPG_COEF[0] + MOPEKA_LPG_COEF[1] * raw_t + MOPEKA_LPG_COEF[2] * raw_t * raw_t));
|
||||
}
|
||||
|
||||
uint8_t MopekaProCheck::parse_temperature_(const std::vector<uint8_t> &message) { return (message[2] & 0x7F) - 40; }
|
||||
|
||||
SensorReadQuality MopekaProCheck::parse_read_quality_(const std::vector<uint8_t> &message) {
|
||||
return static_cast<SensorReadQuality>(message[4] >> 6);
|
||||
}
|
||||
|
||||
} // namespace mopeka_pro_check
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
58
esphome/components/mopeka_pro_check/mopeka_pro_check.h
Normal file
58
esphome/components/mopeka_pro_check/mopeka_pro_check.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace mopeka_pro_check {
|
||||
|
||||
enum SensorType {
|
||||
STANDARD_BOTTOM_UP = 0x03,
|
||||
TOP_DOWN_AIR_ABOVE = 0x04,
|
||||
BOTTOM_UP_WATER = 0x05
|
||||
// all other values are reserved
|
||||
};
|
||||
|
||||
// Sensor read quality. If sensor is poorly placed or tank level
|
||||
// gets too low the read quality will show and the distanace
|
||||
// measurement may be inaccurate.
|
||||
enum SensorReadQuality { QUALITY_HIGH = 0x3, QUALITY_MED = 0x2, QUALITY_LOW = 0x1, QUALITY_NONE = 0x0 };
|
||||
|
||||
class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
void set_address(uint64_t address) { address_ = address; };
|
||||
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
void set_level(sensor::Sensor *level) { level_ = level; };
|
||||
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; };
|
||||
void set_battery_level(sensor::Sensor *bat) { battery_level_ = bat; };
|
||||
void set_distance(sensor::Sensor *distance) { distance_ = distance; };
|
||||
void set_tank_full(float full) { full_mm_ = full; };
|
||||
void set_tank_empty(float empty) { empty_mm_ = empty; };
|
||||
|
||||
protected:
|
||||
uint64_t address_;
|
||||
sensor::Sensor *level_{nullptr};
|
||||
sensor::Sensor *temperature_{nullptr};
|
||||
sensor::Sensor *distance_{nullptr};
|
||||
sensor::Sensor *battery_level_{nullptr};
|
||||
|
||||
uint32_t full_mm_;
|
||||
uint32_t empty_mm_;
|
||||
|
||||
uint8_t parse_battery_level_(const std::vector<uint8_t> &message);
|
||||
uint32_t parse_distance_(const std::vector<uint8_t> &message);
|
||||
uint8_t parse_temperature_(const std::vector<uint8_t> &message);
|
||||
SensorReadQuality parse_read_quality_(const std::vector<uint8_t> &message);
|
||||
};
|
||||
|
||||
} // namespace mopeka_pro_check
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
131
esphome/components/mopeka_pro_check/sensor.py
Normal file
131
esphome/components/mopeka_pro_check/sensor.py
Normal file
@@ -0,0 +1,131 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_DISTANCE,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_ID,
|
||||
ICON_THERMOMETER,
|
||||
ICON_RULER,
|
||||
UNIT_PERCENT,
|
||||
CONF_LEVEL,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
UNIT_CELSIUS,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
CONF_BATTERY_LEVEL,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
)
|
||||
|
||||
CONF_TANK_TYPE = "tank_type"
|
||||
CONF_CUSTOM_DISTANCE_FULL = "custom_distance_full"
|
||||
CONF_CUSTOM_DISTANCE_EMPTY = "custom_distance_empty"
|
||||
|
||||
ICON_PROPANE_TANK = "mdi:propane-tank"
|
||||
|
||||
TANK_TYPE_CUSTOM = "CUSTOM"
|
||||
|
||||
UNIT_MILLIMETER = "mm"
|
||||
|
||||
|
||||
def small_distance(value):
|
||||
"""small_distance is stored in mm"""
|
||||
meters = cv.distance(value)
|
||||
return meters * 1000
|
||||
|
||||
|
||||
#
|
||||
# Map of standard tank types to their
|
||||
# empty and full distance values.
|
||||
# Format is - tank name: (empty distance in mm, full distance in mm)
|
||||
#
|
||||
CONF_SUPPORTED_TANKS_MAP = {
|
||||
TANK_TYPE_CUSTOM: (0, 100),
|
||||
"20LB_V": (38, 254), # empty/full readings for 20lb US tank
|
||||
"30LB_V": (38, 381),
|
||||
"40LB_V": (38, 508),
|
||||
}
|
||||
|
||||
CODEOWNERS = ["@spbrogan"]
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
|
||||
mopeka_pro_check_ns = cg.esphome_ns.namespace("mopeka_pro_check")
|
||||
MopekaProCheck = mopeka_pro_check_ns.class_(
|
||||
"MopekaProCheck", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MopekaProCheck),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_CUSTOM_DISTANCE_FULL): small_distance,
|
||||
cv.Optional(CONF_CUSTOM_DISTANCE_EMPTY): small_distance,
|
||||
cv.Required(CONF_TANK_TYPE): cv.enum(CONF_SUPPORTED_TANKS_MAP, upper=True),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_LEVEL): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_PROPANE_TANK,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
icon=ICON_RULER,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
if config[CONF_TANK_TYPE] == TANK_TYPE_CUSTOM:
|
||||
# Support custom tank min/max
|
||||
if CONF_CUSTOM_DISTANCE_EMPTY in config:
|
||||
cg.add(var.set_tank_empty(config[CONF_CUSTOM_DISTANCE_EMPTY]))
|
||||
else:
|
||||
cg.add(var.set_tank_empty(CONF_SUPPORTED_TANKS_MAP[TANK_TYPE_CUSTOM][0]))
|
||||
if CONF_CUSTOM_DISTANCE_FULL in config:
|
||||
cg.add(var.set_tank_full(config[CONF_CUSTOM_DISTANCE_FULL]))
|
||||
else:
|
||||
cg.add(var.set_tank_full(CONF_SUPPORTED_TANKS_MAP[TANK_TYPE_CUSTOM][1]))
|
||||
else:
|
||||
# Set the Tank empty and full based on map - User is requesting standard tank
|
||||
t = config[CONF_TANK_TYPE]
|
||||
cg.add(var.set_tank_empty(CONF_SUPPORTED_TANKS_MAP[t][0]))
|
||||
cg.add(var.set_tank_full(CONF_SUPPORTED_TANKS_MAP[t][1]))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature(sens))
|
||||
if CONF_LEVEL in config:
|
||||
sens = await sensor.new_sensor(config[CONF_LEVEL])
|
||||
cg.add(var.set_level(sens))
|
||||
if CONF_DISTANCE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_DISTANCE])
|
||||
cg.add(var.set_distance(sens))
|
||||
if CONF_BATTERY_LEVEL in config:
|
||||
sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||
cg.add(var.set_battery_level(sens))
|
||||
1
esphome/components/mpu6886/__init__.py
Normal file
1
esphome/components/mpu6886/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Support for MPC-6886."""
|
||||
153
esphome/components/mpu6886/mpu6886.cpp
Normal file
153
esphome/components/mpu6886/mpu6886.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "mpu6886.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mpu6886 {
|
||||
|
||||
static const char *const TAG = "mpu6886";
|
||||
|
||||
const uint8_t MPU6886_REGISTER_WHO_AM_I = 0x75;
|
||||
const uint8_t MPU6886_REGISTER_POWER_MANAGEMENT_1 = 0x6B;
|
||||
const uint8_t MPU6886_REGISTER_GYRO_CONFIG = 0x1B;
|
||||
const uint8_t MPU6886_REGISTER_ACCEL_CONFIG = 0x1C;
|
||||
const uint8_t MPU6886_REGISTER_ACCEL_XOUT_H = 0x3B;
|
||||
const uint8_t MPU6886_CLOCK_SOURCE_X_GYRO = 0b001;
|
||||
const uint8_t MPU6886_SCALE_2000_DPS = 0b11;
|
||||
const uint8_t MPU6886_WHO_AM_I_IDENTIFIER = 0x19;
|
||||
const float MPU6886_SCALE_DPS_PER_DIGIT_2000 = 0.060975f;
|
||||
const uint8_t MPU6886_RANGE_2G = 0b00;
|
||||
const float MPU6886_RANGE_PER_DIGIT_2G = 0.000061f;
|
||||
const uint8_t MPU6886_BIT_SLEEP_ENABLED = 6;
|
||||
const uint8_t MPU6886_BIT_TEMPERATURE_DISABLED = 3;
|
||||
const float GRAVITY_EARTH = 9.80665f;
|
||||
// See https://github.com/m5stack/M5-Schematic/blob/master/datasheet/MPU-6886-000193%2Bv1.1_GHIC.PDF.pdf
|
||||
// p. 43
|
||||
const float TEMPERATURE_SENSITIVITY = 326.8;
|
||||
const float TEMPERATURE_OFFSET = 25.0;
|
||||
|
||||
void MPU6886Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MPU6886...");
|
||||
uint8_t who_am_i;
|
||||
if (!this->read_byte(MPU6886_REGISTER_WHO_AM_I, &who_am_i) || who_am_i != MPU6886_WHO_AM_I_IDENTIFIER) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Power Management...");
|
||||
// Setup power management
|
||||
uint8_t power_management;
|
||||
if (!this->read_byte(MPU6886_REGISTER_POWER_MANAGEMENT_1, &power_management)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input power_management: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(power_management));
|
||||
// Set clock source - X-Gyro
|
||||
power_management &= 0b11111000;
|
||||
power_management |= MPU6886_CLOCK_SOURCE_X_GYRO;
|
||||
// Disable sleep
|
||||
power_management &= ~(1 << MPU6886_BIT_SLEEP_ENABLED);
|
||||
// Enable temperature
|
||||
power_management &= ~(1 << MPU6886_BIT_TEMPERATURE_DISABLED);
|
||||
ESP_LOGV(TAG, " Output power_management: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(power_management));
|
||||
if (!this->write_byte(MPU6886_REGISTER_POWER_MANAGEMENT_1, power_management)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Gyroscope Config...");
|
||||
// Set scale - 2000DPS
|
||||
uint8_t gyro_config;
|
||||
if (!this->read_byte(MPU6886_REGISTER_GYRO_CONFIG, &gyro_config)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input gyroscope_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||
gyro_config &= 0b11100111;
|
||||
gyro_config |= MPU6886_SCALE_2000_DPS << 3;
|
||||
ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||
if (!this->write_byte(MPU6886_REGISTER_GYRO_CONFIG, gyro_config)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Accelerometer Config...");
|
||||
// Set range - 2G
|
||||
uint8_t accel_config;
|
||||
if (!this->read_byte(MPU6886_REGISTER_ACCEL_CONFIG, &accel_config)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input accelerometer_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||
accel_config &= 0b11100111;
|
||||
accel_config |= (MPU6886_RANGE_2G << 3);
|
||||
ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||
if (!this->write_byte(MPU6886_REGISTER_GYRO_CONFIG, gyro_config)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MPU6886Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MPU6886:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with MPU6886 failed!");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_);
|
||||
LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_);
|
||||
LOG_SENSOR(" ", "Acceleration Z", this->accel_z_sensor_);
|
||||
LOG_SENSOR(" ", "Gyro X", this->gyro_x_sensor_);
|
||||
LOG_SENSOR(" ", "Gyro Y", this->gyro_y_sensor_);
|
||||
LOG_SENSOR(" ", "Gyro Z", this->gyro_z_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
}
|
||||
|
||||
void MPU6886Component::update() {
|
||||
ESP_LOGV(TAG, " Updating MPU6886...");
|
||||
uint16_t raw_data[7];
|
||||
if (!this->read_bytes_16(MPU6886_REGISTER_ACCEL_XOUT_H, raw_data, 7)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
auto *data = reinterpret_cast<int16_t *>(raw_data);
|
||||
|
||||
float accel_x = data[0] * MPU6886_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
|
||||
float accel_y = data[1] * MPU6886_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
|
||||
float accel_z = data[2] * MPU6886_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
|
||||
|
||||
float temperature = data[3] / TEMPERATURE_SENSITIVITY + TEMPERATURE_OFFSET;
|
||||
|
||||
float gyro_x = data[4] * MPU6886_SCALE_DPS_PER_DIGIT_2000;
|
||||
float gyro_y = data[5] * MPU6886_SCALE_DPS_PER_DIGIT_2000;
|
||||
float gyro_z = data[6] * MPU6886_SCALE_DPS_PER_DIGIT_2000;
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Got accel={x=%.3f m/s², y=%.3f m/s², z=%.3f m/s²}, "
|
||||
"gyro={x=%.3f °/s, y=%.3f °/s, z=%.3f °/s}, temp=%.3f°C",
|
||||
accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z, temperature);
|
||||
|
||||
if (this->accel_x_sensor_ != nullptr)
|
||||
this->accel_x_sensor_->publish_state(accel_x);
|
||||
if (this->accel_y_sensor_ != nullptr)
|
||||
this->accel_y_sensor_->publish_state(accel_y);
|
||||
if (this->accel_z_sensor_ != nullptr)
|
||||
this->accel_z_sensor_->publish_state(accel_z);
|
||||
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
|
||||
if (this->gyro_x_sensor_ != nullptr)
|
||||
this->gyro_x_sensor_->publish_state(gyro_x);
|
||||
if (this->gyro_y_sensor_ != nullptr)
|
||||
this->gyro_y_sensor_->publish_state(gyro_y);
|
||||
if (this->gyro_z_sensor_ != nullptr)
|
||||
this->gyro_z_sensor_->publish_state(gyro_z);
|
||||
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
float MPU6886Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
} // namespace mpu6886
|
||||
} // namespace esphome
|
||||
39
esphome/components/mpu6886/mpu6886.h
Normal file
39
esphome/components/mpu6886/mpu6886.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mpu6886 {
|
||||
|
||||
class MPU6886Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void set_accel_x_sensor(sensor::Sensor *accel_x_sensor) { accel_x_sensor_ = accel_x_sensor; }
|
||||
void set_accel_y_sensor(sensor::Sensor *accel_y_sensor) { accel_y_sensor_ = accel_y_sensor; }
|
||||
void set_accel_z_sensor(sensor::Sensor *accel_z_sensor) { accel_z_sensor_ = accel_z_sensor; }
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void set_gyro_x_sensor(sensor::Sensor *gyro_x_sensor) { gyro_x_sensor_ = gyro_x_sensor; }
|
||||
void set_gyro_y_sensor(sensor::Sensor *gyro_y_sensor) { gyro_y_sensor_ = gyro_y_sensor; }
|
||||
void set_gyro_z_sensor(sensor::Sensor *gyro_z_sensor) { gyro_z_sensor_ = gyro_z_sensor; }
|
||||
|
||||
protected:
|
||||
sensor::Sensor *accel_x_sensor_{nullptr};
|
||||
sensor::Sensor *accel_y_sensor_{nullptr};
|
||||
sensor::Sensor *accel_z_sensor_{nullptr};
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *gyro_x_sensor_{nullptr};
|
||||
sensor::Sensor *gyro_y_sensor_{nullptr};
|
||||
sensor::Sensor *gyro_z_sensor_{nullptr};
|
||||
};
|
||||
;
|
||||
|
||||
} // namespace mpu6886
|
||||
} // namespace esphome
|
||||
85
esphome/components/mpu6886/sensor.py
Normal file
85
esphome/components/mpu6886/sensor.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_BRIEFCASE_DOWNLOAD,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_METER_PER_SECOND_SQUARED,
|
||||
ICON_SCREEN_ROTATION,
|
||||
UNIT_DEGREE_PER_SECOND,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@fabaff"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_ACCEL_X = "accel_x"
|
||||
CONF_ACCEL_Y = "accel_y"
|
||||
CONF_ACCEL_Z = "accel_z"
|
||||
CONF_GYRO_X = "gyro_x"
|
||||
CONF_GYRO_Y = "gyro_y"
|
||||
CONF_GYRO_Z = "gyro_z"
|
||||
|
||||
mpu6886_ns = cg.esphome_ns.namespace("mpu6886")
|
||||
MPU6886Component = mpu6886_ns.class_(
|
||||
"MPU6886Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
accel_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
|
||||
icon=ICON_BRIEFCASE_DOWNLOAD,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
gyro_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
temperature_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MPU6886Component),
|
||||
cv.Optional(CONF_ACCEL_X): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Y): accel_schema,
|
||||
cv.Optional(CONF_ACCEL_Z): accel_schema,
|
||||
cv.Optional(CONF_GYRO_X): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Y): gyro_schema,
|
||||
cv.Optional(CONF_GYRO_Z): gyro_schema,
|
||||
cv.Optional(CONF_TEMPERATURE): temperature_schema,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x68))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
for d in ["x", "y", "z"]:
|
||||
accel_key = f"accel_{d}"
|
||||
if accel_key in config:
|
||||
sens = await sensor.new_sensor(config[accel_key])
|
||||
cg.add(getattr(var, f"set_accel_{d}_sensor")(sens))
|
||||
accel_key = f"gyro_{d}"
|
||||
if accel_key in config:
|
||||
sens = await sensor.new_sensor(config[accel_key])
|
||||
cg.add(getattr(var, f"set_gyro_{d}_sensor")(sens))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "mqtt_client.h"
|
||||
#define USE_MQTT
|
||||
|
||||
#ifdef USE_MQTT
|
||||
|
||||
|
||||
@@ -12,18 +12,53 @@ void PulseMeterSensor::setup() {
|
||||
this->pin_->attach_interrupt(PulseMeterSensor::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE);
|
||||
|
||||
this->last_detected_edge_us_ = 0;
|
||||
this->last_valid_edge_us_ = 0;
|
||||
this->last_valid_low_edge_us_ = 0;
|
||||
this->last_valid_high_edge_us_ = 0;
|
||||
this->sensor_is_high_ = this->isr_pin_.digital_read();
|
||||
}
|
||||
|
||||
void PulseMeterSensor::loop() {
|
||||
const uint32_t now = micros();
|
||||
|
||||
// Check to see if we should filter this edge out
|
||||
if (this->filter_mode_ == FILTER_EDGE) {
|
||||
if ((this->last_detected_edge_us_ - this->last_valid_high_edge_us_) >= this->filter_us_) {
|
||||
// Don't measure the first valid pulse (we need at least two pulses to measure the width)
|
||||
if (this->last_valid_high_edge_us_ != 0) {
|
||||
this->pulse_width_us_ = (this->last_detected_edge_us_ - this->last_valid_high_edge_us_);
|
||||
}
|
||||
this->total_pulses_++;
|
||||
this->last_valid_high_edge_us_ = this->last_detected_edge_us_;
|
||||
}
|
||||
} else {
|
||||
// Make sure the signal has been stable long enough
|
||||
if ((now - this->last_detected_edge_us_) >= this->filter_us_) {
|
||||
// Only consider HIGH pulses and "new" edges if sensor state is LOW
|
||||
if (!this->sensor_is_high_ && this->isr_pin_.digital_read() &&
|
||||
(this->last_detected_edge_us_ != this->last_valid_high_edge_us_)) {
|
||||
// Don't measure the first valid pulse (we need at least two pulses to measure the width)
|
||||
if (this->last_valid_high_edge_us_ != 0) {
|
||||
this->pulse_width_us_ = (this->last_detected_edge_us_ - this->last_valid_high_edge_us_);
|
||||
}
|
||||
this->sensor_is_high_ = true;
|
||||
this->total_pulses_++;
|
||||
this->last_valid_high_edge_us_ = this->last_detected_edge_us_;
|
||||
}
|
||||
// Only consider LOW pulses and "new" edges if sensor state is HIGH
|
||||
else if (this->sensor_is_high_ && !this->isr_pin_.digital_read() &&
|
||||
(this->last_detected_edge_us_ != this->last_valid_low_edge_us_)) {
|
||||
this->sensor_is_high_ = false;
|
||||
this->last_valid_low_edge_us_ = this->last_detected_edge_us_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we've exceeded our timeout interval without receiving any pulses, assume 0 pulses/min until
|
||||
// we get at least two valid pulses.
|
||||
const uint32_t time_since_valid_edge_us = now - this->last_valid_edge_us_;
|
||||
if ((this->last_valid_edge_us_ != 0) && (time_since_valid_edge_us > this->timeout_us_)) {
|
||||
const uint32_t time_since_valid_edge_us = now - this->last_valid_high_edge_us_;
|
||||
if ((this->last_valid_high_edge_us_ != 0) && (time_since_valid_edge_us > this->timeout_us_) &&
|
||||
(this->pulse_width_us_ != 0)) {
|
||||
ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000);
|
||||
this->last_valid_edge_us_ = 0;
|
||||
this->pulse_width_us_ = 0;
|
||||
}
|
||||
|
||||
@@ -52,7 +87,11 @@ void PulseMeterSensor::set_total_pulses(uint32_t pulses) { this->total_pulses_ =
|
||||
void PulseMeterSensor::dump_config() {
|
||||
LOG_SENSOR("", "Pulse Meter", this);
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %u µs", this->filter_us_);
|
||||
if (this->filter_mode_ == FILTER_EDGE) {
|
||||
ESP_LOGCONFIG(TAG, " Filtering rising edges less than %u µs apart", this->filter_us_);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %u µs", this->filter_us_);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Assuming 0 pulses/min after not receiving a pulse for %us", this->timeout_us_ / 1000000);
|
||||
}
|
||||
|
||||
@@ -62,23 +101,14 @@ void IRAM_ATTR PulseMeterSensor::gpio_intr(PulseMeterSensor *sensor) {
|
||||
// Get the current time before we do anything else so the measurements are consistent
|
||||
const uint32_t now = micros();
|
||||
|
||||
// We only look at rising edges
|
||||
if (!sensor->isr_pin_.digital_read()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if we should filter this edge out
|
||||
if ((now - sensor->last_detected_edge_us_) >= sensor->filter_us_) {
|
||||
// Don't measure the first valid pulse (we need at least two pulses to measure the width)
|
||||
if (sensor->last_valid_edge_us_ != 0) {
|
||||
sensor->pulse_width_us_ = (now - sensor->last_valid_edge_us_);
|
||||
// We only look at rising edges in EDGE mode, and all edges in PULSE mode
|
||||
if (sensor->filter_mode_ == FILTER_EDGE) {
|
||||
if (sensor->isr_pin_.digital_read()) {
|
||||
sensor->last_detected_edge_us_ = now;
|
||||
}
|
||||
|
||||
sensor->total_pulses_++;
|
||||
sensor->last_valid_edge_us_ = now;
|
||||
} else {
|
||||
sensor->last_detected_edge_us_ = now;
|
||||
}
|
||||
|
||||
sensor->last_detected_edge_us_ = now;
|
||||
}
|
||||
|
||||
} // namespace pulse_meter
|
||||
|
||||
@@ -10,8 +10,14 @@ namespace pulse_meter {
|
||||
|
||||
class PulseMeterSensor : public sensor::Sensor, public Component {
|
||||
public:
|
||||
enum InternalFilterMode {
|
||||
FILTER_EDGE = 0,
|
||||
FILTER_PULSE,
|
||||
};
|
||||
|
||||
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||
void set_filter_us(uint32_t filter) { this->filter_us_ = filter; }
|
||||
void set_filter_mode(InternalFilterMode mode) { this->filter_mode_ = mode; }
|
||||
void set_timeout_us(uint32_t timeout) { this->timeout_us_ = timeout; }
|
||||
void set_total_sensor(sensor::Sensor *sensor) { this->total_sensor_ = sensor; }
|
||||
|
||||
@@ -30,14 +36,17 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
|
||||
uint32_t filter_us_ = 0;
|
||||
uint32_t timeout_us_ = 1000000UL * 60UL * 5UL;
|
||||
sensor::Sensor *total_sensor_ = nullptr;
|
||||
InternalFilterMode filter_mode_{FILTER_EDGE};
|
||||
|
||||
Deduplicator<uint32_t> pulse_width_dedupe_;
|
||||
Deduplicator<uint32_t> total_dedupe_;
|
||||
|
||||
volatile uint32_t last_detected_edge_us_ = 0;
|
||||
volatile uint32_t last_valid_edge_us_ = 0;
|
||||
volatile uint32_t last_valid_low_edge_us_ = 0;
|
||||
volatile uint32_t last_valid_high_edge_us_ = 0;
|
||||
volatile uint32_t pulse_width_us_ = 0;
|
||||
volatile uint32_t total_pulses_ = 0;
|
||||
volatile bool sensor_is_high_ = false;
|
||||
};
|
||||
|
||||
} // namespace pulse_meter
|
||||
|
||||
@@ -5,6 +5,7 @@ from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INTERNAL_FILTER,
|
||||
CONF_INTERNAL_FILTER_MODE,
|
||||
CONF_PIN,
|
||||
CONF_NUMBER,
|
||||
CONF_TIMEOUT,
|
||||
@@ -18,14 +19,21 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@stevebaxter"]
|
||||
CODEOWNERS = ["@stevebaxter", "@cstaahl"]
|
||||
|
||||
pulse_meter_ns = cg.esphome_ns.namespace("pulse_meter")
|
||||
|
||||
|
||||
PulseMeterSensor = pulse_meter_ns.class_(
|
||||
"PulseMeterSensor", sensor.Sensor, cg.Component
|
||||
)
|
||||
|
||||
PulseMeterInternalFilterMode = PulseMeterSensor.enum("InternalFilterMode")
|
||||
FILTER_MODES = {
|
||||
"EDGE": PulseMeterInternalFilterMode.FILTER_EDGE,
|
||||
"PULSE": PulseMeterInternalFilterMode.FILTER_PULSE,
|
||||
}
|
||||
|
||||
SetTotalPulsesAction = pulse_meter_ns.class_("SetTotalPulsesAction", automation.Action)
|
||||
|
||||
|
||||
@@ -66,6 +74,9 @@ CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional(CONF_INTERNAL_FILTER_MODE, default="EDGE"): cv.enum(
|
||||
FILTER_MODES, upper=True
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -78,6 +89,7 @@ async def to_code(config):
|
||||
cg.add(var.set_pin(pin))
|
||||
cg.add(var.set_filter_us(config[CONF_INTERNAL_FILTER]))
|
||||
cg.add(var.set_timeout_us(config[CONF_TIMEOUT]))
|
||||
cg.add(var.set_filter_mode(config[CONF_INTERNAL_FILTER_MODE]))
|
||||
|
||||
if CONF_TOTAL in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TOTAL])
|
||||
|
||||
@@ -27,6 +27,8 @@ from esphome.const import (
|
||||
CONF_CARRIER_FREQUENCY,
|
||||
CONF_RC_CODE_1,
|
||||
CONF_RC_CODE_2,
|
||||
CONF_MAGNITUDE,
|
||||
CONF_WAND_ID,
|
||||
CONF_LEVEL,
|
||||
)
|
||||
from esphome.core import coroutine
|
||||
@@ -391,6 +393,54 @@ async def lg_action(var, config, args):
|
||||
cg.add(var.set_nbits(template_))
|
||||
|
||||
|
||||
# MagiQuest
|
||||
(
|
||||
MagiQuestData,
|
||||
MagiQuestBinarySensor,
|
||||
MagiQuestTrigger,
|
||||
MagiQuestAction,
|
||||
MagiQuestDumper,
|
||||
) = declare_protocol("MagiQuest")
|
||||
|
||||
MAGIQUEST_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_WAND_ID): cv.hex_uint32_t,
|
||||
cv.Optional(CONF_MAGNITUDE, default=0xFFFF): cv.hex_uint16_t,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@register_binary_sensor("magiquest", MagiQuestBinarySensor, MAGIQUEST_SCHEMA)
|
||||
def magiquest_binary_sensor(var, config):
|
||||
cg.add(
|
||||
var.set_data(
|
||||
cg.StructInitializer(
|
||||
MagiQuestData,
|
||||
("magnitude", config[CONF_MAGNITUDE]),
|
||||
("wand_id", config[CONF_WAND_ID]),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@register_trigger("magiquest", MagiQuestTrigger, MagiQuestData)
|
||||
def magiquest_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper("magiquest", MagiQuestDumper)
|
||||
def magiquest_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action("magiquest", MagiQuestAction, MAGIQUEST_SCHEMA)
|
||||
async def magiquest_action(var, config, args):
|
||||
template_ = await cg.templatable(config[CONF_WAND_ID], args, cg.uint32)
|
||||
cg.add(var.set_wand_id(template_))
|
||||
template_ = await cg.templatable(config[CONF_MAGNITUDE], args, cg.uint16)
|
||||
cg.add(var.set_magnitude(template_))
|
||||
|
||||
|
||||
# NEC
|
||||
NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol("NEC")
|
||||
NEC_SCHEMA = cv.Schema(
|
||||
|
||||
83
esphome/components/remote_base/magiquest_protocol.cpp
Normal file
83
esphome/components/remote_base/magiquest_protocol.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "magiquest_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
/* Based on protocol analysis from
|
||||
* https://arduino-irremote.github.io/Arduino-IRremote/ir__MagiQuest_8cpp_source.html
|
||||
*/
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *const TAG = "remote.magiquest";
|
||||
|
||||
static const uint32_t MAGIQUEST_UNIT = 288; // us
|
||||
static const uint32_t MAGIQUEST_ONE_MARK = 2 * MAGIQUEST_UNIT;
|
||||
static const uint32_t MAGIQUEST_ONE_SPACE = 2 * MAGIQUEST_UNIT;
|
||||
static const uint32_t MAGIQUEST_ZERO_MARK = MAGIQUEST_UNIT;
|
||||
static const uint32_t MAGIQUEST_ZERO_SPACE = 3 * MAGIQUEST_UNIT;
|
||||
|
||||
void MagiQuestProtocol::encode(RemoteTransmitData *dst, const MagiQuestData &data) {
|
||||
dst->reserve(101); // 2 start bits, 48 data bits, 1 stop bit
|
||||
dst->set_carrier_frequency(38000);
|
||||
|
||||
// 2 start bits
|
||||
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
|
||||
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
|
||||
for (uint32_t mask = 1 << 31; mask; mask >>= 1) {
|
||||
if (data.wand_id & mask) {
|
||||
dst->item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE);
|
||||
} else {
|
||||
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t mask = 1 << 15; mask; mask >>= 1) {
|
||||
if (data.magnitude & mask) {
|
||||
dst->item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE);
|
||||
} else {
|
||||
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
dst->mark(MAGIQUEST_UNIT);
|
||||
}
|
||||
optional<MagiQuestData> MagiQuestProtocol::decode(RemoteReceiveData src) {
|
||||
MagiQuestData data{
|
||||
.magnitude = 0,
|
||||
.wand_id = 0,
|
||||
};
|
||||
// Two start bits
|
||||
if (!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE) ||
|
||||
!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (uint32_t mask = 1 << 31; mask; mask >>= 1) {
|
||||
if (src.expect_item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE)) {
|
||||
data.wand_id |= mask;
|
||||
} else if (src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
|
||||
data.wand_id &= ~mask;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t mask = 1 << 15; mask; mask >>= 1) {
|
||||
if (src.expect_item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE)) {
|
||||
data.magnitude |= mask;
|
||||
} else if (src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
|
||||
data.magnitude &= ~mask;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
src.expect_mark(MAGIQUEST_UNIT);
|
||||
return data;
|
||||
}
|
||||
void MagiQuestProtocol::dump(const MagiQuestData &data) {
|
||||
ESP_LOGD(TAG, "Received MagiQuest: wand_id=0x%08X, magnitude=0x%04X", data.wand_id, data.magnitude);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
||||
50
esphome/components/remote_base/magiquest_protocol.h
Normal file
50
esphome/components/remote_base/magiquest_protocol.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "remote_base.h"
|
||||
|
||||
/* Based on protocol analysis from
|
||||
* https://arduino-irremote.github.io/Arduino-IRremote/ir__MagiQuest_8cpp_source.html
|
||||
*/
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct MagiQuestData {
|
||||
uint16_t magnitude;
|
||||
uint32_t wand_id;
|
||||
|
||||
bool operator==(const MagiQuestData &rhs) const {
|
||||
// Treat 0xffff as a special, wildcard magnitude
|
||||
// In testing, the wand never produces this value, and this allows us to match
|
||||
// on just the wand_id if wanted.
|
||||
if (rhs.wand_id != this->wand_id) {
|
||||
return false;
|
||||
}
|
||||
return (this->wand_id == 0xffff || rhs.wand_id == 0xffff || this->wand_id == rhs.wand_id);
|
||||
}
|
||||
};
|
||||
|
||||
class MagiQuestProtocol : public RemoteProtocol<MagiQuestData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const MagiQuestData &data) override;
|
||||
optional<MagiQuestData> decode(RemoteReceiveData src) override;
|
||||
void dump(const MagiQuestData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(MagiQuest)
|
||||
|
||||
template<typename... Ts> class MagiQuestAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, magnitude)
|
||||
TEMPLATABLE_VALUE(uint32_t, wand_id)
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
MagiQuestData data{};
|
||||
data.magnitude = this->magnitude_.value(x...);
|
||||
data.wand_id = this->wand_id_.value(x...);
|
||||
MagiQuestProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
||||
@@ -408,18 +408,30 @@ async def sliding_window_moving_average_filter_to_code(config, filter_id):
|
||||
)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"exponential_moving_average",
|
||||
ExponentialMovingAverageFilter,
|
||||
EXPONENTIAL_AVERAGE_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_ALPHA, default=0.1): cv.positive_float,
|
||||
cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int,
|
||||
cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int,
|
||||
}
|
||||
),
|
||||
validate_send_first_at,
|
||||
)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"exponential_moving_average",
|
||||
ExponentialMovingAverageFilter,
|
||||
EXPONENTIAL_AVERAGE_SCHEMA,
|
||||
)
|
||||
async def exponential_moving_average_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id, config[CONF_ALPHA], config[CONF_SEND_EVERY])
|
||||
return cg.new_Pvariable(
|
||||
filter_id,
|
||||
config[CONF_ALPHA],
|
||||
config[CONF_SEND_EVERY],
|
||||
config[CONF_SEND_FIRST_AT],
|
||||
)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
|
||||
@@ -200,8 +200,8 @@ optional<float> SlidingWindowMovingAverageFilter::new_value(float value) {
|
||||
}
|
||||
|
||||
// ExponentialMovingAverageFilter
|
||||
ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every)
|
||||
: send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {}
|
||||
ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every, size_t send_first_at)
|
||||
: send_every_(send_every), send_at_(send_every - send_first_at), alpha_(alpha) {}
|
||||
optional<float> ExponentialMovingAverageFilter::new_value(float value) {
|
||||
if (!std::isnan(value)) {
|
||||
if (this->first_value_) {
|
||||
|
||||
@@ -194,7 +194,7 @@ class SlidingWindowMovingAverageFilter : public Filter {
|
||||
*/
|
||||
class ExponentialMovingAverageFilter : public Filter {
|
||||
public:
|
||||
ExponentialMovingAverageFilter(float alpha, size_t send_every);
|
||||
ExponentialMovingAverageFilter(float alpha, size_t send_every, size_t send_first_at);
|
||||
|
||||
optional<float> new_value(float value) override;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
CONF_IMPLEMENTATION,
|
||||
esp8266=IMPLEMENTATION_LWIP_TCP,
|
||||
esp32=IMPLEMENTATION_BSD_SOCKETS,
|
||||
host=IMPLEMENTATION_BSD_SOCKETS,
|
||||
): cv.one_of(
|
||||
IMPLEMENTATION_LWIP_TCP, IMPLEMENTATION_BSD_SOCKETS, lower=True, space="_"
|
||||
),
|
||||
|
||||
@@ -119,6 +119,12 @@ struct iovec {
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_HOST
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif // USE_HOST
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
// arduino-esp32 declares a global var called INADDR_NONE which is replaced
|
||||
// by the define
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import button
|
||||
|
||||
from .. import template_ns
|
||||
|
||||
TemplateButton = template_ns.class_("TemplateButton", button.Button)
|
||||
|
||||
CONFIG_SCHEMA = button.BUTTON_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(button.Button),
|
||||
cv.GenerateID(): cv.declare_id(TemplateButton),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
15
esphome/components/template/button/template_button.h
Normal file
15
esphome/components/template/button/template_button.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateButton : public button::Button {
|
||||
public:
|
||||
// Implements the abstract `press_action` but the `on_press` trigger already handles the press.
|
||||
void press_action() override{};
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
@@ -6,6 +6,8 @@ from esphome import automation
|
||||
from esphome.const import CONF_ON_TOUCH
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["display"]
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
touchscreen_ns = cg.esphome_ns.namespace("touchscreen")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.components import binary_sensor, display
|
||||
from esphome.const import CONF_PAGE_ID
|
||||
|
||||
from .. import touchscreen_ns, CONF_TOUCHSCREEN_ID, Touchscreen, TouchListener
|
||||
|
||||
@@ -41,6 +42,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Required(CONF_X_MAX): cv.int_range(min=0, max=2000),
|
||||
cv.Required(CONF_Y_MIN): cv.int_range(min=0, max=2000),
|
||||
cv.Required(CONF_Y_MAX): cv.int_range(min=0, max=2000),
|
||||
cv.Optional(CONF_PAGE_ID): cv.use_id(display.DisplayPage),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
@@ -61,3 +63,7 @@ async def to_code(config):
|
||||
config[CONF_Y_MAX],
|
||||
)
|
||||
)
|
||||
|
||||
if CONF_PAGE_ID in config:
|
||||
page = await cg.get_variable(config[CONF_PAGE_ID])
|
||||
cg.add(var.set_page(page))
|
||||
|
||||
@@ -6,6 +6,10 @@ namespace touchscreen {
|
||||
void TouchscreenBinarySensor::touch(TouchPoint tp) {
|
||||
bool touched = (tp.x >= this->x_min_ && tp.x <= this->x_max_ && tp.y >= this->y_min_ && tp.y <= this->y_max_);
|
||||
|
||||
if (this->page_ != nullptr) {
|
||||
touched &= this->page_ == this->parent_->get_display()->get_active_page();
|
||||
}
|
||||
|
||||
if (touched) {
|
||||
this->publish_state(true);
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/components/touchscreen/touchscreen.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -23,11 +24,14 @@ class TouchscreenBinarySensor : public binary_sensor::BinarySensor,
|
||||
this->y_max_ = y_max;
|
||||
}
|
||||
|
||||
void set_page(display::DisplayPage *page) { this->page_ = page; }
|
||||
|
||||
void touch(TouchPoint tp) override;
|
||||
void release() override;
|
||||
|
||||
protected:
|
||||
int16_t x_min_, x_max_, y_min_, y_max_;
|
||||
display::DisplayPage *page_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace touchscreen
|
||||
|
||||
@@ -37,6 +37,7 @@ class Touchscreen {
|
||||
this->display_height_ = display->get_height_internal();
|
||||
this->rotation_ = static_cast<TouchRotation>(display->get_rotation());
|
||||
}
|
||||
display::DisplayBuffer *get_display() const { return this->display_; }
|
||||
|
||||
Trigger<TouchPoint> *get_touch_trigger() { return &this->touch_trigger_; }
|
||||
|
||||
|
||||
@@ -43,16 +43,34 @@ void TSL2591Component::disable_if_power_saving_() {
|
||||
}
|
||||
|
||||
void TSL2591Component::setup() {
|
||||
if (this->component_gain_ == TSL2591_CGAIN_AUTO)
|
||||
this->gain_ = TSL2591_GAIN_MED;
|
||||
switch (this->component_gain_) {
|
||||
case TSL2591_CGAIN_LOW:
|
||||
this->gain_ = TSL2591_GAIN_LOW;
|
||||
break;
|
||||
case TSL2591_CGAIN_MED:
|
||||
this->gain_ = TSL2591_GAIN_MED;
|
||||
break;
|
||||
case TSL2591_CGAIN_HIGH:
|
||||
this->gain_ = TSL2591_GAIN_HIGH;
|
||||
break;
|
||||
case TSL2591_CGAIN_MAX:
|
||||
this->gain_ = TSL2591_GAIN_MAX;
|
||||
break;
|
||||
case TSL2591_CGAIN_AUTO:
|
||||
this->gain_ = TSL2591_GAIN_MED;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t address = this->address_;
|
||||
ESP_LOGI(TAG, "Setting up TSL2591 sensor at I2C address 0x%02X", address);
|
||||
|
||||
uint8_t id;
|
||||
if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_DEVICE_ID, &id)) {
|
||||
ESP_LOGE(TAG, "Failed I2C read during setup()");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != 0x50) {
|
||||
ESP_LOGE(TAG,
|
||||
"Could not find the TSL2591 sensor. The ID register of the device at address 0x%02X reported 0x%02X "
|
||||
@@ -61,6 +79,7 @@ void TSL2591Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->set_integration_time_and_gain(this->integration_time_, this->gain_);
|
||||
this->disable_if_power_saving_();
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ WaveshareEPaper7P5InBV2 = waveshare_epaper_ns.class_(
|
||||
WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InV2", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper7P5InHDB = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper7P5InHDB", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper2P13InDKE = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P13InDKE", WaveshareEPaper
|
||||
)
|
||||
@@ -76,6 +79,7 @@ MODELS = {
|
||||
"7.50in-bv2": ("b", WaveshareEPaper7P5InBV2),
|
||||
"7.50in-bc": ("b", WaveshareEPaper7P5InBC),
|
||||
"7.50inv2": ("b", WaveshareEPaper7P5InV2),
|
||||
"7.50in-hd-b": ("b", WaveshareEPaper7P5InHDB),
|
||||
"2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE),
|
||||
}
|
||||
|
||||
|
||||
@@ -1241,6 +1241,107 @@ void WaveshareEPaper7P5InBC::dump_config() {
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void WaveshareEPaper7P5InHDB::initialize() {
|
||||
this->command(0x12); // SWRESET
|
||||
|
||||
this->wait_until_idle_(); // waiting for the electronic paper IC to release the idle signal
|
||||
|
||||
this->command(0x46); // Auto Write RAM
|
||||
this->data(0xF7);
|
||||
|
||||
this->wait_until_idle_(); // waiting for the electronic paper IC to release the idle signal
|
||||
|
||||
this->command(0x47); // Auto Write RAM
|
||||
this->data(0xF7);
|
||||
|
||||
this->wait_until_idle_(); // waiting for the electronic paper IC to release the idle signal
|
||||
|
||||
this->command(0x0C); // Soft start setting
|
||||
this->data(0xAE);
|
||||
this->data(0xC7);
|
||||
this->data(0xC3);
|
||||
this->data(0xC0);
|
||||
this->data(0x40);
|
||||
|
||||
this->command(0x01); // Set MUX as 527
|
||||
this->data(0xAF);
|
||||
this->data(0x02);
|
||||
this->data(0x01);
|
||||
|
||||
this->command(0x11); // Data entry mode
|
||||
this->data(0x01);
|
||||
|
||||
this->command(0x44);
|
||||
this->data(0x00); // RAM x address start at 0
|
||||
this->data(0x00);
|
||||
this->data(0x6F); // RAM x address end at 36Fh -> 879
|
||||
this->data(0x03);
|
||||
|
||||
this->command(0x45);
|
||||
this->data(0xAF); // RAM y address start at 20Fh;
|
||||
this->data(0x02);
|
||||
this->data(0x00); // RAM y address end at 00h;
|
||||
this->data(0x00);
|
||||
|
||||
this->command(0x3C); // VBD
|
||||
this->data(0x01); // LUT1, for white
|
||||
|
||||
this->command(0x18);
|
||||
this->data(0X80);
|
||||
|
||||
this->command(0x22);
|
||||
this->data(0XB1); // Load Temperature and waveform setting.
|
||||
|
||||
this->command(0x20);
|
||||
|
||||
this->wait_until_idle_(); // waiting for the electronic paper IC to release the idle signal
|
||||
|
||||
this->command(0x4E);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
|
||||
this->command(0x4F);
|
||||
this->data(0xAF);
|
||||
this->data(0x02);
|
||||
}
|
||||
|
||||
void HOT WaveshareEPaper7P5InHDB::display() {
|
||||
this->command(0x4F);
|
||||
this->data(0xAf);
|
||||
this->data(0x02);
|
||||
|
||||
// BLACK
|
||||
this->command(0x24);
|
||||
this->start_data_();
|
||||
this->write_array(this->buffer_, this->get_buffer_length_());
|
||||
this->end_data_();
|
||||
|
||||
// RED
|
||||
this->command(0x26);
|
||||
this->start_data_();
|
||||
for (size_t i = 0; i < this->get_buffer_length_(); i++)
|
||||
this->write_byte(0x00);
|
||||
this->end_data_();
|
||||
|
||||
this->command(0x22);
|
||||
this->data(0xC7);
|
||||
this->command(0x20);
|
||||
delay(100); // NOLINT
|
||||
}
|
||||
|
||||
int WaveshareEPaper7P5InHDB::get_width_internal() { return 880; }
|
||||
|
||||
int WaveshareEPaper7P5InHDB::get_height_internal() { return 528; }
|
||||
|
||||
void WaveshareEPaper7P5InHDB::dump_config() {
|
||||
LOG_DISPLAY("", "Waveshare E-Paper", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 7.5in-HD-b");
|
||||
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_SIZE_TTGO_DKE_PART = 153;
|
||||
|
||||
static const uint8_t PART_UPDATE_LUT_TTGO_DKE[LUT_SIZE_TTGO_DKE_PART] = {
|
||||
|
||||
@@ -350,6 +350,26 @@ class WaveshareEPaper7P5InV2 : public WaveshareEPaper {
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper7P5InHDB : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// deep sleep
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P13InDKE : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
@@ -11,6 +11,7 @@ from esphome.const import (
|
||||
UNIT_OHM,
|
||||
CONF_IMPEDANCE,
|
||||
ICON_OMEGA,
|
||||
CONF_CLEAR_IMPEDANCE,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
@@ -25,6 +26,7 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(XiaomiMiscale),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_CLEAR_IMPEDANCE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_WEIGHT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_KILOGRAM,
|
||||
icon=ICON_SCALE_BATHROOM,
|
||||
@@ -50,6 +52,7 @@ async def to_code(config):
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
cg.add(var.set_clear_impedance(config[CONF_CLEAR_IMPEDANCE]))
|
||||
|
||||
if CONF_WEIGHT in config:
|
||||
sens = await sensor.new_sensor(config[CONF_WEIGHT])
|
||||
|
||||
@@ -24,25 +24,30 @@ bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
bool success = false;
|
||||
for (auto &service_data : device.get_service_datas()) {
|
||||
auto res = parse_header_(service_data);
|
||||
if (!res.has_value()) {
|
||||
if (!res.has_value())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(parse_message_(service_data.data, *res))) {
|
||||
if (!parse_message_(service_data.data, *res))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(report_results_(res, device.address_str()))) {
|
||||
if (!report_results_(res, device.address_str()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res->weight.has_value() && this->weight_ != nullptr)
|
||||
this->weight_->publish_state(*res->weight);
|
||||
|
||||
if (res->version == 1 && this->impedance_ != nullptr) {
|
||||
ESP_LOGW(TAG, "Impedance is only supported on version 2. Your scale was identified as verison 1.");
|
||||
} else if (res->impedance.has_value() && this->impedance_ != nullptr)
|
||||
this->impedance_->publish_state(*res->impedance);
|
||||
if (this->impedance_ != nullptr) {
|
||||
if (res->version == 1) {
|
||||
ESP_LOGW(TAG, "Impedance is only supported on version 2. Your scale was identified as verison 1.");
|
||||
} else {
|
||||
if (res->impedance.has_value()) {
|
||||
this->impedance_->publish_state(*res->impedance);
|
||||
} else {
|
||||
if (clear_impedance_)
|
||||
this->impedance_->publish_state(NAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,13 @@ class XiaomiMiscale : public Component, public esp32_ble_tracker::ESPBTDeviceLis
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_weight(sensor::Sensor *weight) { weight_ = weight; }
|
||||
void set_impedance(sensor::Sensor *impedance) { impedance_ = impedance; }
|
||||
void set_clear_impedance(bool clear_impedance) { clear_impedance_ = clear_impedance; }
|
||||
|
||||
protected:
|
||||
uint64_t address_;
|
||||
sensor::Sensor *weight_{nullptr};
|
||||
sensor::Sensor *impedance_{nullptr};
|
||||
bool clear_impedance_{false};
|
||||
|
||||
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
|
||||
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
|
||||
|
||||
@@ -1426,6 +1426,7 @@ class SplitDefault(Optional):
|
||||
esp32=vol.UNDEFINED,
|
||||
esp32_arduino=vol.UNDEFINED,
|
||||
esp32_idf=vol.UNDEFINED,
|
||||
host=vol.UNDEFINED,
|
||||
):
|
||||
super().__init__(key)
|
||||
self._esp8266_default = vol.default_factory(esp8266)
|
||||
@@ -1435,6 +1436,7 @@ class SplitDefault(Optional):
|
||||
self._esp32_idf_default = vol.default_factory(
|
||||
esp32_idf if esp32 is vol.UNDEFINED else esp32
|
||||
)
|
||||
self._host_default = vol.default_factory(host)
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
@@ -1444,6 +1446,8 @@ class SplitDefault(Optional):
|
||||
return self._esp32_arduino_default
|
||||
if CORE.is_esp32 and CORE.using_esp_idf:
|
||||
return self._esp32_idf_default
|
||||
if CORE.is_host:
|
||||
return self._host_default
|
||||
raise NotImplementedError
|
||||
|
||||
@default.setter
|
||||
|
||||
@@ -4,10 +4,13 @@ __version__ = "2022.3.0-dev"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
|
||||
TARGET_FRAMEWORKS = ["arduino", "esp-idf"]
|
||||
|
||||
PLATFORM_ESP32 = "esp32"
|
||||
PLATFORM_ESP8266 = "esp8266"
|
||||
PLATFORM_HOST = "host"
|
||||
|
||||
TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266]
|
||||
TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_HOST]
|
||||
|
||||
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
|
||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
|
||||
@@ -89,6 +92,7 @@ CONF_CHANGE_MODE_EVERY = "change_mode_every"
|
||||
CONF_CHANNEL = "channel"
|
||||
CONF_CHANNELS = "channels"
|
||||
CONF_CHIPSET = "chipset"
|
||||
CONF_CLEAR_IMPEDANCE = "clear_impedance"
|
||||
CONF_CLIENT_ID = "client_id"
|
||||
CONF_CLK_PIN = "clk_pin"
|
||||
CONF_CLOCK_PIN = "clock_pin"
|
||||
@@ -306,6 +310,7 @@ CONF_INTENSITY = "intensity"
|
||||
CONF_INTERLOCK = "interlock"
|
||||
CONF_INTERNAL = "internal"
|
||||
CONF_INTERNAL_FILTER = "internal_filter"
|
||||
CONF_INTERNAL_FILTER_MODE = "internal_filter_mode"
|
||||
CONF_INTERRUPT = "interrupt"
|
||||
CONF_INTERVAL = "interval"
|
||||
CONF_INVALID_COOLDOWN = "invalid_cooldown"
|
||||
@@ -344,6 +349,7 @@ CONF_LOOP_TIME = "loop_time"
|
||||
CONF_LOW = "low"
|
||||
CONF_LOW_VOLTAGE_REFERENCE = "low_voltage_reference"
|
||||
CONF_MAC_ADDRESS = "mac_address"
|
||||
CONF_MAGNITUDE = "magnitude"
|
||||
CONF_MAINS_FILTER = "mains_filter"
|
||||
CONF_MAKE_ID = "make_id"
|
||||
CONF_MANUAL_IP = "manual_ip"
|
||||
@@ -619,6 +625,7 @@ CONF_SLEEP_PIN = "sleep_pin"
|
||||
CONF_SLEEP_WHEN_DONE = "sleep_when_done"
|
||||
CONF_SONY = "sony"
|
||||
CONF_SOURCE = "source"
|
||||
CONF_SOURCE_ID = "source_id"
|
||||
CONF_SPEED = "speed"
|
||||
CONF_SPEED_COMMAND_TOPIC = "speed_command_topic"
|
||||
CONF_SPEED_COUNT = "speed_count"
|
||||
@@ -735,6 +742,7 @@ CONF_VOLTAGE_DIVIDER = "voltage_divider"
|
||||
CONF_WAIT_TIME = "wait_time"
|
||||
CONF_WAIT_UNTIL = "wait_until"
|
||||
CONF_WAKEUP_PIN = "wakeup_pin"
|
||||
CONF_WAND_ID = "wand_id"
|
||||
CONF_WARM_WHITE = "warm_white"
|
||||
CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature"
|
||||
CONF_WATCHDOG_THRESHOLD = "watchdog_threshold"
|
||||
|
||||
@@ -593,6 +593,10 @@ class EsphomeCore:
|
||||
def is_esp32(self):
|
||||
return self.target_platform == "esp32"
|
||||
|
||||
@property
|
||||
def is_host(self):
|
||||
return self.target_platform == "host"
|
||||
|
||||
@property
|
||||
def target_framework(self):
|
||||
return self.data[KEY_CORE][KEY_TARGET_FRAMEWORK]
|
||||
|
||||
@@ -83,6 +83,10 @@
|
||||
#define USE_SOCKET_IMPL_LWIP_TCP
|
||||
#endif
|
||||
|
||||
#ifdef USE_HOST
|
||||
#define USE_SOCKET_IMPL_BSD_SOCKETS
|
||||
#endif
|
||||
|
||||
// Disabled feature flags
|
||||
//#define USE_BSEC // Requires a library with proprietary license.
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#include "esp_system.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/portmacro.h>
|
||||
#elif defined(USE_HOST)
|
||||
#include <cstdio>
|
||||
#include <random>
|
||||
#include <limits>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
|
||||
@@ -76,6 +80,11 @@ uint32_t random_uint32() {
|
||||
return esp_random();
|
||||
#elif defined(USE_ESP8266)
|
||||
return os_random();
|
||||
#elif defined(USE_HOST)
|
||||
std::random_device dev;
|
||||
std::mt19937 rng(dev());
|
||||
std::uniform_int_distribution<uint32_t> dist(0, std::numeric_limits<uint32_t>::max());
|
||||
return dist(rng);
|
||||
#else
|
||||
#error "No random source available for this configuration."
|
||||
#endif
|
||||
@@ -87,6 +96,19 @@ bool random_bytes(uint8_t *data, size_t len) {
|
||||
return true;
|
||||
#elif defined(USE_ESP8266)
|
||||
return os_get_random(data, len) == 0;
|
||||
#elif defined(USE_HOST)
|
||||
FILE *fp = fopen("/dev/urandom", "r");
|
||||
if (fp == nullptr) {
|
||||
ESP_LOGW(TAG, "Could not open /dev/urandom, errno=%d", errno);
|
||||
exit(1);
|
||||
}
|
||||
size_t read = fread(data, 1, len, fp);
|
||||
if (read != len) {
|
||||
ESP_LOGW(TAG, "Not enough data from /dev/urandom");
|
||||
exit(1);
|
||||
}
|
||||
fclose(fp);
|
||||
return true
|
||||
#else
|
||||
#error "No random source available for this configuration."
|
||||
#endif
|
||||
|
||||
@@ -116,7 +116,7 @@ optional<uint32_t> HOT Scheduler::next_schedule_in() {
|
||||
return 0;
|
||||
return next_time - now;
|
||||
}
|
||||
void IRAM_ATTR HOT Scheduler::call() {
|
||||
void HOT Scheduler::call() {
|
||||
const uint32_t now = this->millis_();
|
||||
this->process_to_add();
|
||||
|
||||
|
||||
@@ -954,7 +954,7 @@ class MockObjEnum(MockObj):
|
||||
base = kwargs.pop("base")
|
||||
if self._is_class:
|
||||
base = f"{base}::{self._enum}"
|
||||
kwargs["op"] = "::"
|
||||
kwargs["op"] = "::"
|
||||
kwargs["base"] = base
|
||||
MockObj.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class StorageJSON:
|
||||
# Web server port of the ESP, for example 80
|
||||
assert web_port is None or isinstance(web_port, int)
|
||||
self.web_port = web_port # type: int
|
||||
# The type of ESP in use, either ESP32 or ESP8266
|
||||
# The type of hardware in use, like "ESP32", "ESP32C3", "ESP8266", etc.
|
||||
self.target_platform = target_platform # type: str
|
||||
# The absolute path to the platformio project
|
||||
self.build_path = build_path # type: str
|
||||
@@ -99,6 +99,11 @@ class StorageJSON:
|
||||
def from_esphome_core(
|
||||
esph, old
|
||||
): # type: (CoreType, Optional[StorageJSON]) -> StorageJSON
|
||||
hardware = esph.target_platform.upper()
|
||||
if esph.is_esp32:
|
||||
from esphome.components import esp32
|
||||
|
||||
hardware = esp32.get_esp32_variant(esph)
|
||||
return StorageJSON(
|
||||
storage_version=1,
|
||||
name=esph.name,
|
||||
@@ -107,15 +112,14 @@ class StorageJSON:
|
||||
src_version=1,
|
||||
address=esph.address,
|
||||
web_port=esph.web_port,
|
||||
target_platform=esph.target_platform,
|
||||
target_platform=hardware,
|
||||
build_path=esph.build_path,
|
||||
firmware_bin_path=esph.firmware_bin,
|
||||
loaded_integrations=list(esph.loaded_integrations),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_wizard(name, address, esp_platform):
|
||||
# type: (str, str, str) -> StorageJSON
|
||||
def from_wizard(name: str, address: str, esp_platform: str) -> "StorageJSON":
|
||||
return StorageJSON(
|
||||
storage_version=1,
|
||||
name=name,
|
||||
|
||||
@@ -67,6 +67,27 @@ esp32:
|
||||
type: arduino
|
||||
"""
|
||||
|
||||
ESP32S2_CONFIG = """
|
||||
esp32:
|
||||
board: {board}
|
||||
framework:
|
||||
type: esp-idf
|
||||
"""
|
||||
|
||||
ESP32C3_CONFIG = """
|
||||
esp32:
|
||||
board: {board}
|
||||
framework:
|
||||
type: esp-idf
|
||||
"""
|
||||
|
||||
HARDWARE_BASE_CONFIGS = {
|
||||
"ESP8266": ESP8266_CONFIG,
|
||||
"ESP32": ESP32_CONFIG,
|
||||
"ESP32S2": ESP32S2_CONFIG,
|
||||
"ESP32C3": ESP32C3_CONFIG,
|
||||
}
|
||||
|
||||
|
||||
def sanitize_double_quotes(value):
|
||||
return value.replace("\\", "\\\\").replace('"', '\\"')
|
||||
@@ -83,11 +104,7 @@ def wizard_file(**kwargs):
|
||||
|
||||
config = BASE_CONFIG.format(**kwargs)
|
||||
|
||||
config += (
|
||||
ESP8266_CONFIG.format(**kwargs)
|
||||
if kwargs["platform"] == "ESP8266"
|
||||
else ESP32_CONFIG.format(**kwargs)
|
||||
)
|
||||
config += HARDWARE_BASE_CONFIGS[kwargs["platform"]].format(**kwargs)
|
||||
|
||||
config += LOGGER_API_CONFIG
|
||||
|
||||
@@ -119,16 +136,26 @@ def wizard_file(**kwargs):
|
||||
"""
|
||||
|
||||
# pylint: disable=consider-using-f-string
|
||||
config += """
|
||||
if kwargs["platform"] in ["ESP8266", "ESP32"]:
|
||||
config += """
|
||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||
ap:
|
||||
ssid: "{fallback_name}"
|
||||
password: "{fallback_psk}"
|
||||
|
||||
captive_portal:
|
||||
""".format(
|
||||
**kwargs
|
||||
)
|
||||
""".format(
|
||||
**kwargs
|
||||
)
|
||||
else:
|
||||
config += """
|
||||
# Enable fallback hotspot in case wifi connection fails
|
||||
ap:
|
||||
ssid: "{fallback_name}"
|
||||
password: "{fallback_psk}"
|
||||
""".format(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
@@ -147,10 +174,10 @@ def wizard_write(path, **kwargs):
|
||||
kwargs["platform"] = (
|
||||
"ESP8266" if board in esp8266_boards.ESP8266_BOARD_PINS else "ESP32"
|
||||
)
|
||||
platform = kwargs["platform"]
|
||||
hardware = kwargs["platform"]
|
||||
|
||||
write_file(path, wizard_file(**kwargs))
|
||||
storage = StorageJSON.from_wizard(name, f"{name}.local", platform)
|
||||
storage = StorageJSON.from_wizard(name, f"{name}.local", hardware)
|
||||
storage_path = ext_storage_path(os.path.dirname(path), os.path.basename(path))
|
||||
storage.save(storage_path)
|
||||
|
||||
|
||||
@@ -220,3 +220,13 @@ board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32s2-idf-tidy
|
||||
build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
|
||||
[env:host]
|
||||
extends = common
|
||||
platform = platformio/native
|
||||
lib_deps =
|
||||
esphome/noise-c@0.1.1 ; used by api
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_HOST
|
||||
${flags:runtime.build_flags}
|
||||
@@ -9,7 +9,7 @@ pyserial==3.5
|
||||
platformio==5.2.5 # When updating platformio, also update Dockerfile
|
||||
esptool==3.2
|
||||
click==8.0.3
|
||||
esphome-dashboard==20220209.0
|
||||
esphome-dashboard==20220219.0
|
||||
aioesphomeapi==10.8.2
|
||||
zeroconf==0.38.3
|
||||
|
||||
|
||||
@@ -591,6 +591,11 @@ def lint_inclusive_language(fname, match):
|
||||
)
|
||||
|
||||
|
||||
@lint_re_check(r"[\t\r\f\v ]+$")
|
||||
def lint_trailing_whitespace(fname, match):
|
||||
return "Trailing whitespace detected"
|
||||
|
||||
|
||||
@lint_content_find_check(
|
||||
"ESP_LOG",
|
||||
include=["*.h", "*.tcc"],
|
||||
|
||||
@@ -18,4 +18,3 @@ button:
|
||||
name: wol_test_2
|
||||
id: wol_2
|
||||
internal: false
|
||||
|
||||
|
||||
110
tests/test1.yaml
110
tests/test1.yaml
@@ -262,7 +262,7 @@ power_supply:
|
||||
deep_sleep:
|
||||
run_duration: 20s
|
||||
sleep_duration: 50s
|
||||
wakeup_pin: GPIO39
|
||||
wakeup_pin: GPIO2
|
||||
wakeup_pin_mode: INVERT_WAKEUP
|
||||
|
||||
ads1115:
|
||||
@@ -358,6 +358,7 @@ sensor:
|
||||
- exponential_moving_average:
|
||||
alpha: 0.1
|
||||
send_every: 15
|
||||
send_first_at: 15
|
||||
- throttle_average: 60s
|
||||
- throttle: 1s
|
||||
- heartbeat: 5s
|
||||
@@ -456,12 +457,17 @@ sensor:
|
||||
name: "Living Room Brightness 3"
|
||||
internal: true
|
||||
address: 0x23
|
||||
resolution: 1.0
|
||||
update_interval: 30s
|
||||
retain: False
|
||||
availability:
|
||||
state_topic: livingroom/custom_state_topic
|
||||
measurement_duration: 31
|
||||
i2c_id: i2c_bus
|
||||
- platform: max44009
|
||||
name: "Outside Brightness 1"
|
||||
internal: true
|
||||
address: 0x4A
|
||||
update_interval: 30s
|
||||
mode: low_power
|
||||
i2c_id: i2c_bus
|
||||
- platform: bme280
|
||||
temperature:
|
||||
@@ -594,6 +600,14 @@ sensor:
|
||||
oversampling: 8x
|
||||
update_interval: 15s
|
||||
i2c_id: i2c_bus
|
||||
- platform: honeywellabp
|
||||
pressure:
|
||||
name: "Honeywell pressure"
|
||||
min_pressure: 0
|
||||
max_pressure: 15
|
||||
temperature:
|
||||
name: "Honeywell temperature"
|
||||
cs_pin: GPIO5
|
||||
- platform: qmc5883l
|
||||
address: 0x0D
|
||||
field_strength_x:
|
||||
@@ -719,6 +733,23 @@ sensor:
|
||||
temperature:
|
||||
name: "MPU6050 Temperature"
|
||||
i2c_id: i2c_bus
|
||||
- platform: mpu6886
|
||||
address: 0x68
|
||||
accel_x:
|
||||
name: "MPU6886 Accel X"
|
||||
accel_y:
|
||||
name: "MPU6886 Accel Y"
|
||||
accel_z:
|
||||
name: "MPU6886 Accel z"
|
||||
gyro_x:
|
||||
name: "MPU6886 Gyro X"
|
||||
gyro_y:
|
||||
name: "MPU6886 Gyro Y"
|
||||
gyro_z:
|
||||
name: "MPU6886 Gyro z"
|
||||
temperature:
|
||||
name: "MPU6886 Temperature"
|
||||
i2c_id: i2c_bus
|
||||
- platform: ms5611
|
||||
temperature:
|
||||
name: "Outside Temperature"
|
||||
@@ -1090,8 +1121,8 @@ sensor:
|
||||
temperature:
|
||||
name: Max9611 Temp
|
||||
update_interval: 1s
|
||||
|
||||
|
||||
|
||||
|
||||
esp32_touch:
|
||||
setup_mode: False
|
||||
iir_filter: 10ms
|
||||
@@ -1310,18 +1341,18 @@ binary_sensor:
|
||||
- platform: analog_threshold
|
||||
name: Analog Trheshold 1
|
||||
sensor_id: template_sensor
|
||||
threshold:
|
||||
threshold:
|
||||
upper: 110
|
||||
lower: 90
|
||||
filters:
|
||||
filters:
|
||||
- delayed_on: 0s
|
||||
- delayed_off: 10s
|
||||
- platform: analog_threshold
|
||||
name: Analog Trheshold 2
|
||||
sensor_id: template_sensor
|
||||
threshold: 100
|
||||
filters:
|
||||
- invert:
|
||||
filters:
|
||||
- invert:
|
||||
|
||||
pca9685:
|
||||
frequency: 500
|
||||
@@ -1478,6 +1509,28 @@ output:
|
||||
- platform: mcp4725
|
||||
id: mcp4725_dac_output
|
||||
i2c_id: i2c_bus
|
||||
- platform: mcp4728
|
||||
id: mcp4728_dac_output_a
|
||||
channel: A
|
||||
vref: vdd
|
||||
power_down: normal
|
||||
- platform: mcp4728
|
||||
id: mcp4728_dac_output_b
|
||||
channel: B
|
||||
vref: internal
|
||||
gain: X1
|
||||
power_down: gnd_1k
|
||||
- platform: mcp4728
|
||||
id: mcp4728_dac_output_c
|
||||
channel: C
|
||||
vref: vdd
|
||||
power_down: gnd_100k
|
||||
- platform: mcp4728
|
||||
id: mcp4728_dac_output_d
|
||||
channel: D
|
||||
vref: internal
|
||||
gain: X2
|
||||
power_down: gnd_500k
|
||||
|
||||
e131:
|
||||
|
||||
@@ -1861,6 +1914,11 @@ switch:
|
||||
turn_on_action:
|
||||
remote_transmitter.transmit_jvc:
|
||||
data: 0x10EF
|
||||
- platform: template
|
||||
name: MagiQuest
|
||||
turn_on_action:
|
||||
remote_transmitter.transmit_magiquest:
|
||||
wand_id: 0x01234567
|
||||
- platform: template
|
||||
name: NEC
|
||||
id: living_room_lights_off
|
||||
@@ -1985,6 +2043,9 @@ switch:
|
||||
- output.set_level:
|
||||
id: mcp4725_dac_output
|
||||
level: !lambda "return 0.5;"
|
||||
- output.set_level:
|
||||
id: mcp4728_dac_output_a
|
||||
level: !lambda "return 0.5;"
|
||||
turn_off_action:
|
||||
- switch.turn_on: living_room_lights_off
|
||||
restore_state: False
|
||||
@@ -2094,6 +2155,9 @@ fan:
|
||||
on_speed_set:
|
||||
then:
|
||||
- logger.log: "Fan speed was changed!"
|
||||
- platform: copy
|
||||
source_id: fan_speed
|
||||
name: "Fan Speed Copy"
|
||||
|
||||
interval:
|
||||
- interval: 10s
|
||||
@@ -2362,6 +2426,12 @@ rc522_i2c:
|
||||
ESP_LOGD("main", "Found tag %s", x.c_str());
|
||||
i2c_id: i2c_bus
|
||||
|
||||
mcp4728:
|
||||
- id: mcp4728_dac
|
||||
store_in_eeprom: False
|
||||
address: 0x60
|
||||
i2c_id: i2c_bus
|
||||
|
||||
gps:
|
||||
uart_id: uart0
|
||||
|
||||
@@ -2491,6 +2561,18 @@ globals:
|
||||
initial_value: "false"
|
||||
|
||||
text_sensor:
|
||||
- platform: ble_client
|
||||
ble_client_id: ble_foo
|
||||
name: 'Sensor Location'
|
||||
service_uuid: '180d'
|
||||
characteristic_uuid: '2a38'
|
||||
descriptor_uuid: '2902'
|
||||
notify: true
|
||||
update_interval: never
|
||||
on_notify:
|
||||
then:
|
||||
- lambda: |-
|
||||
ESP_LOGD("green_btn", "Location changed: %s", x.c_str());
|
||||
- platform: mqtt_subscribe
|
||||
name: "MQTT Subscribe Text"
|
||||
topic: "the/topic"
|
||||
@@ -2558,7 +2640,7 @@ canbus:
|
||||
then:
|
||||
- lambda: |-
|
||||
std::string b(x.begin(), x.end());
|
||||
ESP_LOGD("canid 500", "%s", &b[0] );
|
||||
ESP_LOGD("canid 500", "%s", b.c_str());
|
||||
- can_id: 23
|
||||
then:
|
||||
- if:
|
||||
@@ -2596,7 +2678,7 @@ canbus:
|
||||
then:
|
||||
- lambda: |-
|
||||
std::string b(x.begin(), x.end());
|
||||
ESP_LOGD("canid 500", "%s", &b[0] );
|
||||
ESP_LOGD("canid 500", "%s", b.c_str() );
|
||||
- can_id: 23
|
||||
then:
|
||||
- if:
|
||||
@@ -2649,6 +2731,9 @@ select:
|
||||
- one
|
||||
- two
|
||||
optimistic: true
|
||||
- platform: copy
|
||||
source_id: test_select
|
||||
name: Test Select Copy
|
||||
|
||||
qr_code:
|
||||
- id: homepage_qr
|
||||
@@ -2678,3 +2763,6 @@ lock:
|
||||
name: "Generic Output Lock"
|
||||
id: test_lock2
|
||||
output: pca_6
|
||||
- platform: copy
|
||||
source_id: test_lock2
|
||||
name: Generic Output Lock Copy
|
||||
|
||||
@@ -60,7 +60,7 @@ deep_sleep:
|
||||
gpio_wakeup_reason: 10s
|
||||
touch_wakeup_reason: 15s
|
||||
sleep_duration: 50s
|
||||
wakeup_pin: GPIO39
|
||||
wakeup_pin: GPIO2
|
||||
wakeup_pin_mode: INVERT_WAKEUP
|
||||
|
||||
as3935_i2c:
|
||||
@@ -331,6 +331,19 @@ sensor:
|
||||
name: "RD200 Radon"
|
||||
radon_long_term:
|
||||
name: "RD200 Radon Long Term"
|
||||
- platform: mopeka_pro_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
|
||||
@@ -442,6 +455,7 @@ ruuvi_ble:
|
||||
|
||||
xiaomi_ble:
|
||||
|
||||
mopeka_ble:
|
||||
|
||||
#esp32_ble_beacon:
|
||||
# type: iBeacon
|
||||
|
||||
@@ -221,6 +221,10 @@ sensor:
|
||||
- platform: mcp3204
|
||||
name: "MCP3204 Pin 1"
|
||||
number: 1
|
||||
id: mcp_sensor
|
||||
- platform: copy
|
||||
source_id: mcp_sensor
|
||||
name: "MCP binary sensor copy"
|
||||
|
||||
#
|
||||
# platform sensor.apds9960 requires component apds9960
|
||||
@@ -364,6 +368,9 @@ switch:
|
||||
name: inverter0_pv_ok_condition_for_parallel
|
||||
pv_power_balance:
|
||||
name: inverter0_pv_power_balance
|
||||
- platform: copy
|
||||
source_id: tuya_switch
|
||||
name: Tuya Switch Copy
|
||||
|
||||
light:
|
||||
- platform: fastled_clockless
|
||||
@@ -391,6 +398,9 @@ cover:
|
||||
- platform: tuya
|
||||
id: tuya_cover
|
||||
position_datapoint: 2
|
||||
- platform: copy
|
||||
source_id: tuya_cover
|
||||
name: "Tuya Cover copy"
|
||||
|
||||
display:
|
||||
- platform: addressable_light
|
||||
@@ -465,6 +475,9 @@ number:
|
||||
min_value: 0
|
||||
max_value: 17
|
||||
step: 1
|
||||
- platform: copy
|
||||
source_id: tuya_number
|
||||
name: Tuya Number Copy
|
||||
|
||||
text_sensor:
|
||||
- platform: pipsolar
|
||||
@@ -484,6 +497,9 @@ text_sensor:
|
||||
last_qflag:
|
||||
id: inverter0_last_qflag
|
||||
name: inverter0_last_qflag
|
||||
- platform: copy
|
||||
source_id: inverter0_device_mode
|
||||
name: "Inverter Text Sensor Copy"
|
||||
|
||||
output:
|
||||
- platform: pipsolar
|
||||
@@ -552,6 +568,11 @@ button:
|
||||
name: Safe Mode Button
|
||||
- platform: shutdown
|
||||
name: Shutdown Button
|
||||
id: shutdown_btn
|
||||
- platform: copy
|
||||
source_id: shutdown_btn
|
||||
name: Shutdown Button Copy
|
||||
|
||||
|
||||
touchscreen:
|
||||
- platform: ektf2232
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
import esphome.wizard as wz
|
||||
import pytest
|
||||
from esphome.components.esp8266.boards import ESP8266_BOARD_PINS
|
||||
from mock import MagicMock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def default_config():
|
||||
return {
|
||||
"name": "test-name",
|
||||
"platform": "test_platform",
|
||||
"platform": "ESP8266",
|
||||
"board": "esp01_1m",
|
||||
"ssid": "test_ssid",
|
||||
"psk": "test_psk",
|
||||
|
||||
Reference in New Issue
Block a user