mirror of
https://github.com/esphome/esphome.git
synced 2024-12-25 15:04:54 +01:00
Merge branch 'dev' of github:esphome/esphome into openthread
This commit is contained in:
commit
ba103403d2
24 changed files with 278 additions and 169 deletions
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -65,7 +65,7 @@ jobs:
|
||||||
pip3 install build
|
pip3 install build
|
||||||
python3 -m build
|
python3 -m build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: pypa/gh-action-pypi-publish@v1.10.1
|
uses: pypa/gh-action-pypi-publish@v1.10.2
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build ESPHome ${{ matrix.platform }}
|
name: Build ESPHome ${{ matrix.platform }}
|
||||||
|
|
|
@ -7,3 +7,5 @@
|
||||||
For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
|
For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
|
||||||
|
|
||||||
For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
|
For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
|
||||||
|
|
||||||
|
[![ESPHome - A project from the Open Home Foundation](https://www.openhomefoundation.org/badges/esphome.png)](https://www.openhomefoundation.org/)
|
||||||
|
|
|
@ -26,7 +26,6 @@ from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
CODEOWNERS = ["@rfdarter", "@jesserockz"]
|
CODEOWNERS = ["@rfdarter", "@jesserockz"]
|
||||||
DEPENDENCIES = ["time"]
|
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
|
@ -62,20 +61,28 @@ DATETIME_MODES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
_DATETIME_SCHEMA = (
|
def _validate_time_present(config):
|
||||||
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
config = config.copy()
|
||||||
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
if CONF_ON_TIME in config and CONF_TIME_ID not in config:
|
||||||
.extend(
|
time_id = cv.use_id(time.RealTimeClock)(None)
|
||||||
|
config[CONF_TIME_ID] = time_id
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
_DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
|
web_server.WEBSERVER_SORTING_SCHEMA,
|
||||||
|
cv.MQTT_COMMAND_COMPONENT_SCHEMA,
|
||||||
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
)
|
).add_extra(_validate_time_present)
|
||||||
|
|
||||||
|
|
||||||
def date_schema(class_: MockObjClass) -> cv.Schema:
|
def date_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
|
@ -138,8 +145,9 @@ async def setup_datetime_core_(var, config):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
||||||
|
|
||||||
rtc = await cg.get_variable(config[CONF_TIME_ID])
|
if CONF_TIME_ID in config:
|
||||||
cg.add(var.set_rtc(rtc))
|
rtc = await cg.get_variable(config[CONF_TIME_ID])
|
||||||
|
cg.add(var.set_rtc(rtc))
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_TIME, []):
|
for conf in config.get(CONF_ON_TIME, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/time.h"
|
#include "esphome/core/time.h"
|
||||||
|
#ifdef USE_TIME
|
||||||
#include "esphome/components/time/real_time_clock.h"
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace datetime {
|
namespace datetime {
|
||||||
|
@ -19,23 +20,29 @@ class DateTimeBase : public EntityBase {
|
||||||
|
|
||||||
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
void set_rtc(time::RealTimeClock *rtc) { this->rtc_ = rtc; }
|
void set_rtc(time::RealTimeClock *rtc) { this->rtc_ = rtc; }
|
||||||
time::RealTimeClock *get_rtc() const { return this->rtc_; }
|
time::RealTimeClock *get_rtc() const { return this->rtc_; }
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CallbackManager<void()> state_callback_;
|
CallbackManager<void()> state_callback_;
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
time::RealTimeClock *rtc_;
|
time::RealTimeClock *rtc_;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool has_state_{false};
|
bool has_state_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
class DateTimeStateTrigger : public Trigger<ESPTime> {
|
class DateTimeStateTrigger : public Trigger<ESPTime> {
|
||||||
public:
|
public:
|
||||||
explicit DateTimeStateTrigger(DateTimeBase *parent) {
|
explicit DateTimeStateTrigger(DateTimeBase *parent) {
|
||||||
parent->add_on_state_callback([this, parent]() { this->trigger(parent->state_as_esptime()); });
|
parent->add_on_state_callback([this, parent]() { this->trigger(parent->state_as_esptime()); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace datetime
|
} // namespace datetime
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -192,6 +192,7 @@ void DateTimeEntityRestoreState::apply(DateTimeEntity *time) {
|
||||||
time->publish_state();
|
time->publish_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
||||||
// there has been a drastic time synchronization
|
// there has been a drastic time synchronization
|
||||||
|
|
||||||
|
@ -245,6 +246,7 @@ bool OnDateTimeTrigger::matches_(const ESPTime &time) const {
|
||||||
time.day_of_month == this->parent_->day && time.hour == this->parent_->hour &&
|
time.day_of_month == this->parent_->day && time.hour == this->parent_->hour &&
|
||||||
time.minute == this->parent_->minute && time.second == this->parent_->second;
|
time.minute == this->parent_->minute && time.second == this->parent_->second;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace datetime
|
} // namespace datetime
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -134,6 +134,7 @@ template<typename... Ts> class DateTimeSetAction : public Action<Ts...>, public
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<DateTimeEntity> {
|
class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<DateTimeEntity> {
|
||||||
public:
|
public:
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
@ -143,6 +144,7 @@ class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<Da
|
||||||
|
|
||||||
optional<ESPTime> last_check_;
|
optional<ESPTime> last_check_;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace datetime
|
} // namespace datetime
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -94,6 +94,7 @@ void TimeEntityRestoreState::apply(TimeEntity *time) {
|
||||||
time->publish_state();
|
time->publish_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
||||||
// there has been a drastic time synchronization
|
// there has been a drastic time synchronization
|
||||||
|
|
||||||
|
@ -145,6 +146,7 @@ bool OnTimeTrigger::matches_(const ESPTime &time) const {
|
||||||
return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
|
return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
|
||||||
time.second == this->parent_->second;
|
time.second == this->parent_->second;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace datetime
|
} // namespace datetime
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -113,6 +113,7 @@ template<typename... Ts> class TimeSetAction : public Action<Ts...>, public Pare
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEntity> {
|
class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEntity> {
|
||||||
public:
|
public:
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
@ -122,6 +123,7 @@ class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEn
|
||||||
|
|
||||||
optional<ESPTime> last_check_;
|
optional<ESPTime> last_check_;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace datetime
|
} // namespace datetime
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -13,6 +13,7 @@ from esphome.const import (
|
||||||
CONF_COMPONENTS,
|
CONF_COMPONENTS,
|
||||||
CONF_ESPHOME,
|
CONF_ESPHOME,
|
||||||
CONF_FRAMEWORK,
|
CONF_FRAMEWORK,
|
||||||
|
CONF_IGNORE_EFUSE_CUSTOM_MAC,
|
||||||
CONF_IGNORE_EFUSE_MAC_CRC,
|
CONF_IGNORE_EFUSE_MAC_CRC,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PATH,
|
CONF_PATH,
|
||||||
|
@ -401,6 +402,9 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
|
||||||
},
|
},
|
||||||
cv.Optional(CONF_ADVANCED, default={}): cv.Schema(
|
cv.Optional(CONF_ADVANCED, default={}): cv.Schema(
|
||||||
{
|
{
|
||||||
|
cv.Optional(
|
||||||
|
CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False
|
||||||
|
): cv.boolean,
|
||||||
cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean,
|
cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -526,6 +530,8 @@ async def to_code(config):
|
||||||
for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
|
for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
|
||||||
add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
|
add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
|
||||||
|
|
||||||
|
if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]:
|
||||||
|
cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC")
|
||||||
if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]:
|
if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]:
|
||||||
cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC")
|
cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC")
|
||||||
if (framework_ver.major, framework_ver.minor) >= (4, 4):
|
if (framework_ver.major, framework_ver.minor) >= (4, 4):
|
||||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from magic import Magic
|
import puremagic
|
||||||
|
|
||||||
from esphome import core, external_files
|
from esphome import core, external_files
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
@ -237,8 +237,8 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
def load_svg_image(file: bytes, resize: tuple[int, int]):
|
def load_svg_image(file: bytes, resize: tuple[int, int]):
|
||||||
# Local import only to allow "validate_pillow_installed" to run *before* importing it
|
# Local imports only to allow "validate_pillow_installed" to run *before* importing it
|
||||||
# This import is only needed in case of SVG images; adding it
|
# cairosvg is only needed in case of SVG images; adding it
|
||||||
# to the top would force configurations not using SVG to also have it
|
# to the top would force configurations not using SVG to also have it
|
||||||
# installed for no reason.
|
# installed for no reason.
|
||||||
from cairosvg import svg2png
|
from cairosvg import svg2png
|
||||||
|
@ -281,8 +281,7 @@ async def to_code(config):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
||||||
|
|
||||||
mime = Magic(mime=True)
|
file_type = puremagic.from_string(file_contents, mime=True)
|
||||||
file_type = mime.from_buffer(file_contents)
|
|
||||||
|
|
||||||
resize = config.get(CONF_RESIZE)
|
resize = config.get(CONF_RESIZE)
|
||||||
if "svg" in file_type:
|
if "svg" in file_type:
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
from esphome.components import sensor, i2c
|
|
||||||
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_AMMONIA,
|
||||||
|
CONF_CARBON_MONOXIDE,
|
||||||
|
CONF_ETHANOL,
|
||||||
|
CONF_HYDROGEN,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_METHANE,
|
||||||
|
CONF_NITROGEN_DIOXIDE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_PARTS_PER_MILLION,
|
UNIT_PARTS_PER_MILLION,
|
||||||
)
|
)
|
||||||
|
@ -12,13 +16,6 @@ from esphome.const import (
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
CONF_CARBON_MONOXIDE = "carbon_monoxide"
|
|
||||||
CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide"
|
|
||||||
CONF_METHANE = "methane"
|
|
||||||
CONF_ETHANOL = "ethanol"
|
|
||||||
CONF_HYDROGEN = "hydrogen"
|
|
||||||
CONF_AMMONIA = "ammonia"
|
|
||||||
|
|
||||||
|
|
||||||
mics_4514_ns = cg.esphome_ns.namespace("mics_4514")
|
mics_4514_ns = cg.esphome_ns.namespace("mics_4514")
|
||||||
MICS4514Component = mics_4514_ns.class_(
|
MICS4514Component = mics_4514_ns.class_(
|
||||||
|
@ -31,6 +28,7 @@ SENSORS = [
|
||||||
CONF_ETHANOL,
|
CONF_ETHANOL,
|
||||||
CONF_HYDROGEN,
|
CONF_HYDROGEN,
|
||||||
CONF_AMMONIA,
|
CONF_AMMONIA,
|
||||||
|
CONF_NITROGEN_DIOXIDE,
|
||||||
]
|
]
|
||||||
|
|
||||||
common_sensor_schema = sensor.sensor_schema(
|
common_sensor_schema = sensor.sensor_schema(
|
||||||
|
@ -40,16 +38,7 @@ common_sensor_schema = sensor.sensor_schema(
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema({cv.GenerateID(): cv.declare_id(MICS4514Component)})
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(MICS4514Component),
|
|
||||||
cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend({cv.Optional(sensor_type): common_sensor_schema for sensor_type in SENSORS})
|
.extend({cv.Optional(sensor_type): common_sensor_schema for sensor_type in SENSORS})
|
||||||
.extend(i2c.i2c_device_schema(0x75))
|
.extend(i2c.i2c_device_schema(0x75))
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
@ -62,10 +51,6 @@ async def to_code(config):
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
for sensor_type in SENSORS:
|
for sensor_type in SENSORS:
|
||||||
if sensor_type in config:
|
if sensor_config := config.get(sensor_type):
|
||||||
sens = await sensor.new_sensor(config[sensor_type])
|
sens = await sensor.new_sensor(sensor_config)
|
||||||
cg.add(getattr(var, f"set_{sensor_type}_sensor")(sens))
|
cg.add(getattr(var, f"set_{sensor_type}_sensor")(sens))
|
||||||
|
|
||||||
if CONF_NITROGEN_DIOXIDE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_NITROGEN_DIOXIDE])
|
|
||||||
cg.add(var.set_nitrogen_dioxide_sensor(sens))
|
|
||||||
|
|
|
@ -71,6 +71,14 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
|
||||||
# return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
|
# return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_platform_version(value):
|
||||||
|
value = cv.string(value)
|
||||||
|
if value.startswith("http"):
|
||||||
|
return value
|
||||||
|
|
||||||
|
return f"https://github.com/maxgerhardt/platform-raspberrypi.git#{value}"
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Keep this in mind when updating the recommended version:
|
# NOTE: Keep this in mind when updating the recommended version:
|
||||||
# * The new version needs to be thoroughly validated before changing the
|
# * The new version needs to be thoroughly validated before changing the
|
||||||
# recommended version as otherwise a bunch of devices could be bricked
|
# recommended version as otherwise a bunch of devices could be bricked
|
||||||
|
@ -82,10 +90,9 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
|
||||||
# - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico
|
# - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico
|
||||||
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 9, 4)
|
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 9, 4)
|
||||||
|
|
||||||
# The platformio/raspberrypi version to use for arduino frameworks
|
# The raspberrypi platform version to use for arduino frameworks
|
||||||
# - https://github.com/platformio/platform-raspberrypi/releases
|
# - https://github.com/maxgerhardt/platform-raspberrypi/tags
|
||||||
# - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi
|
RECOMMENDED_ARDUINO_PLATFORM_VERSION = "v1.2.0-gcc12"
|
||||||
ARDUINO_PLATFORM_VERSION = cv.Version(1, 13, 0)
|
|
||||||
|
|
||||||
|
|
||||||
def _arduino_check_versions(value):
|
def _arduino_check_versions(value):
|
||||||
|
@ -111,7 +118,8 @@ def _arduino_check_versions(value):
|
||||||
value[CONF_SOURCE] = source or _format_framework_arduino_version(version)
|
value[CONF_SOURCE] = source or _format_framework_arduino_version(version)
|
||||||
|
|
||||||
value[CONF_PLATFORM_VERSION] = value.get(
|
value[CONF_PLATFORM_VERSION] = value.get(
|
||||||
CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION))
|
CONF_PLATFORM_VERSION,
|
||||||
|
_parse_platform_version(RECOMMENDED_ARDUINO_PLATFORM_VERSION),
|
||||||
)
|
)
|
||||||
|
|
||||||
if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
|
if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
|
||||||
|
@ -122,15 +130,6 @@ def _arduino_check_versions(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def _parse_platform_version(value):
|
|
||||||
try:
|
|
||||||
# if platform version is a valid version constraint, prefix the default package
|
|
||||||
cv.platformio_version_constraint(value)
|
|
||||||
return f"platformio/raspberrypi@{value}"
|
|
||||||
except cv.Invalid:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
|
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tcs34725 {
|
namespace tcs34725 {
|
||||||
|
@ -14,10 +15,7 @@ static const uint8_t TCS34725_REGISTER_ID = TCS34725_COMMAND_BIT | 0x12;
|
||||||
static const uint8_t TCS34725_REGISTER_ATIME = TCS34725_COMMAND_BIT | 0x01;
|
static const uint8_t TCS34725_REGISTER_ATIME = TCS34725_COMMAND_BIT | 0x01;
|
||||||
static const uint8_t TCS34725_REGISTER_CONTROL = TCS34725_COMMAND_BIT | 0x0F;
|
static const uint8_t TCS34725_REGISTER_CONTROL = TCS34725_COMMAND_BIT | 0x0F;
|
||||||
static const uint8_t TCS34725_REGISTER_ENABLE = TCS34725_COMMAND_BIT | 0x00;
|
static const uint8_t TCS34725_REGISTER_ENABLE = TCS34725_COMMAND_BIT | 0x00;
|
||||||
static const uint8_t TCS34725_REGISTER_CDATAL = TCS34725_COMMAND_BIT | 0x14;
|
static const uint8_t TCS34725_REGISTER_CRGBDATAL = TCS34725_COMMAND_BIT | 0x14;
|
||||||
static const uint8_t TCS34725_REGISTER_RDATAL = TCS34725_COMMAND_BIT | 0x16;
|
|
||||||
static const uint8_t TCS34725_REGISTER_GDATAL = TCS34725_COMMAND_BIT | 0x18;
|
|
||||||
static const uint8_t TCS34725_REGISTER_BDATAL = TCS34725_COMMAND_BIT | 0x1A;
|
|
||||||
|
|
||||||
void TCS34725Component::setup() {
|
void TCS34725Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up TCS34725...");
|
ESP_LOGCONFIG(TAG, "Setting up TCS34725...");
|
||||||
|
@ -75,20 +73,21 @@ float TCS34725Component::get_setup_priority() const { return setup_priority::DAT
|
||||||
* @return Color temperature in degrees Kelvin
|
* @return Color temperature in degrees Kelvin
|
||||||
*/
|
*/
|
||||||
void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, uint16_t c) {
|
void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, uint16_t c) {
|
||||||
float r2, g2, b2; /* RGB values minus IR component */
|
float sat; /* Digital saturation level */
|
||||||
float sat; /* Digital saturation level */
|
|
||||||
float ir; /* Inferred IR content */
|
|
||||||
|
|
||||||
this->illuminance_ = 0; // Assign 0 value before calculation
|
this->illuminance_ = NAN;
|
||||||
this->color_temperature_ = 0;
|
this->color_temperature_ = NAN;
|
||||||
|
|
||||||
const float ga = this->glass_attenuation_; // Glass Attenuation Factor
|
const float ga = this->glass_attenuation_; // Glass Attenuation Factor
|
||||||
static const float DF = 310.f; // Device Factor
|
static const float DF = 310.f; // Device Factor
|
||||||
static const float R_COEF = 0.136f; //
|
static const float R_COEF = 0.136f; //
|
||||||
static const float G_COEF = 1.f; // used in lux computation
|
static const float G_COEF = 1.f; // used in lux computation
|
||||||
static const float B_COEF = -0.444f; //
|
static const float B_COEF = -0.444f; //
|
||||||
static const float CT_COEF = 3810.f; // Color Temperature Coefficient
|
static const float CT_COEF = 3810.f; // Color Temperature Coefficient
|
||||||
static const float CT_OFFSET = 1391.f; // Color Temperatuer Offset
|
static const float CT_OFFSET = 1391.f; // Color Temperatuer Offset
|
||||||
|
static const float MAX_ILLUMINANCE = 100000.0f; // Cap illuminance at 100,000 lux
|
||||||
|
static const float MAX_COLOR_TEMPERATURE = 15000.0f; // Maximum expected color temperature in Kelvin
|
||||||
|
static const float MIN_COLOR_TEMPERATURE = 1000.0f; // Maximum reasonable color temperature in Kelvin
|
||||||
|
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -139,69 +138,66 @@ void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, u
|
||||||
if (c >= sat) {
|
if (c >= sat) {
|
||||||
if (this->integration_time_auto_) {
|
if (this->integration_time_auto_) {
|
||||||
ESP_LOGI(TAG, "Saturation too high, sample discarded, autogain ongoing");
|
ESP_LOGI(TAG, "Saturation too high, sample discarded, autogain ongoing");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(
|
|
||||||
TAG,
|
|
||||||
"Saturation too high, sample with saturation %.1f and clear %d treat values carefully or use grey filter",
|
|
||||||
sat, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* AMS RGB sensors have no IR channel, so the IR content must be */
|
|
||||||
/* calculated indirectly. */
|
|
||||||
ir = ((r + g + b) > c) ? (r + g + b - c) / 2 : 0;
|
|
||||||
|
|
||||||
/* Remove the IR component from the raw RGB values */
|
|
||||||
r2 = r - ir;
|
|
||||||
g2 = g - ir;
|
|
||||||
b2 = b - ir;
|
|
||||||
|
|
||||||
// discarding super low values? not recemmonded, and avoided by using auto gain.
|
|
||||||
if (r2 == 0) {
|
|
||||||
// legacy code
|
|
||||||
if (!this->integration_time_auto_) {
|
|
||||||
ESP_LOGW(TAG,
|
ESP_LOGW(TAG,
|
||||||
"No light detected on red channel, switch to auto gain or adjust timing, values will be unreliable");
|
"Saturation too high, sample with saturation %.1f and clear %d lux/color temperature cannot reliably "
|
||||||
|
"calculated, reduce integration/gain or use a grey filter.",
|
||||||
|
sat, c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lux Calculation (DN40 3.2)
|
// Lux Calculation (DN40 3.2)
|
||||||
|
|
||||||
float g1 = R_COEF * r2 + G_COEF * g2 + B_COEF * b2;
|
float g1 = R_COEF * (float) r + G_COEF * (float) g + B_COEF * (float) b;
|
||||||
float cpl = (this->integration_time_ * this->gain_) / (ga * DF);
|
float cpl = (this->integration_time_ * this->gain_) / (ga * DF);
|
||||||
this->illuminance_ = g1 / cpl;
|
|
||||||
|
this->illuminance_ = std::max(g1 / cpl, 0.0f);
|
||||||
|
|
||||||
|
if (this->illuminance_ > MAX_ILLUMINANCE) {
|
||||||
|
ESP_LOGW(TAG, "Calculated illuminance greater than limit (%f), setting to NAN", this->illuminance_);
|
||||||
|
this->illuminance_ = NAN;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
ESP_LOGW(TAG, "Red channel is zero, cannot compute color temperature");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Color Temperature Calculation (DN40)
|
// Color Temperature Calculation (DN40)
|
||||||
/* A simple method of measuring color temp is to use the ratio of blue */
|
/* A simple method of measuring color temp is to use the ratio of blue */
|
||||||
/* to red light, taking IR cancellation into account. */
|
/* to red light. */
|
||||||
this->color_temperature_ = (CT_COEF * b2) / /** Color temp coefficient. */
|
|
||||||
r2 +
|
this->color_temperature_ = (CT_COEF * (float) b) / (float) r + CT_OFFSET;
|
||||||
CT_OFFSET; /** Color temp offset. */
|
|
||||||
|
// Ensure the color temperature stays within reasonable bounds
|
||||||
|
if (this->color_temperature_ < MIN_COLOR_TEMPERATURE) {
|
||||||
|
ESP_LOGW(TAG, "Calculated color temperature value too low (%f), setting to NAN", this->color_temperature_);
|
||||||
|
this->color_temperature_ = NAN;
|
||||||
|
} else if (this->color_temperature_ > MAX_COLOR_TEMPERATURE) {
|
||||||
|
ESP_LOGW(TAG, "Calculated color temperature value too high (%f), setting to NAN", this->color_temperature_);
|
||||||
|
this->color_temperature_ = NAN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCS34725Component::update() {
|
void TCS34725Component::update() {
|
||||||
uint16_t raw_c;
|
uint8_t data[8]; // Buffer to hold the 8 bytes (2 bytes for each of the 4 channels)
|
||||||
uint16_t raw_r;
|
|
||||||
uint16_t raw_g;
|
|
||||||
uint16_t raw_b;
|
|
||||||
|
|
||||||
if (this->read_data_register_(TCS34725_REGISTER_CDATAL, raw_c) != i2c::ERROR_OK) {
|
// Perform burst
|
||||||
this->status_set_warning();
|
if (this->read_register(TCS34725_REGISTER_CRGBDATAL, data, 8) != i2c::ERROR_OK) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->read_data_register_(TCS34725_REGISTER_RDATAL, raw_r) != i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->read_data_register_(TCS34725_REGISTER_GDATAL, raw_g) != i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->read_data_register_(TCS34725_REGISTER_BDATAL, raw_b) != i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
ESP_LOGW(TAG, "Error reading TCS34725 sensor data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract the data
|
||||||
|
uint16_t raw_c = encode_uint16(data[1], data[0]); // Clear channel
|
||||||
|
uint16_t raw_r = encode_uint16(data[3], data[2]); // Red channel
|
||||||
|
uint16_t raw_g = encode_uint16(data[5], data[4]); // Green channel
|
||||||
|
uint16_t raw_b = encode_uint16(data[7], data[6]); // Blue channel
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Raw values clear=%d red=%d green=%d blue=%d", raw_c, raw_r, raw_g, raw_b);
|
ESP_LOGV(TAG, "Raw values clear=%d red=%d green=%d blue=%d", raw_c, raw_r, raw_g, raw_b);
|
||||||
|
|
||||||
float channel_c;
|
float channel_c;
|
||||||
|
|
|
@ -219,9 +219,16 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
|
||||||
for (sensor::Sensor *obj : App.get_sensors()) {
|
for (sensor::Sensor *obj : App.get_sensors()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
std::string data = this->sensor_json(obj, obj->state, DETAIL_STATE);
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
request->send(200, "application/json", data.c_str());
|
auto detail = DETAIL_STATE;
|
||||||
return;
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->sensor_json(obj, obj->state, detail);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
@ -257,9 +264,16 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const
|
||||||
for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
|
for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
std::string data = this->text_sensor_json(obj, obj->state, DETAIL_STATE);
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
request->send(200, "application/json", data.c_str());
|
auto detail = DETAIL_STATE;
|
||||||
return;
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->text_sensor_json(obj, obj->state, detail);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
@ -288,7 +302,12 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->switch_json(obj, obj->state, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->switch_json(obj, obj->state, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
} else if (match.method == "toggle") {
|
} else if (match.method == "toggle") {
|
||||||
this->schedule_([obj]() { obj->toggle(); });
|
this->schedule_([obj]() { obj->toggle(); });
|
||||||
|
@ -324,7 +343,15 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
|
||||||
for (button::Button *obj : App.get_buttons()) {
|
for (button::Button *obj : App.get_buttons()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
if (match.method == "press") {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->button_json(obj, detail);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
|
} else if (match.method == "press") {
|
||||||
this->schedule_([obj]() { obj->press(); });
|
this->schedule_([obj]() { obj->press(); });
|
||||||
request->send(200);
|
request->send(200);
|
||||||
return;
|
return;
|
||||||
|
@ -357,9 +384,16 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
|
||||||
for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
|
for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
std::string data = this->binary_sensor_json(obj, obj->state, DETAIL_STATE);
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
request->send(200, "application/json", data.c_str());
|
auto detail = DETAIL_STATE;
|
||||||
return;
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->binary_sensor_json(obj, obj->state, detail);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
@ -388,7 +422,12 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->fan_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->fan_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
} else if (match.method == "toggle") {
|
} else if (match.method == "toggle") {
|
||||||
this->schedule_([obj]() { obj->toggle().perform(); });
|
this->schedule_([obj]() { obj->toggle().perform(); });
|
||||||
|
@ -466,7 +505,12 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->light_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->light_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
} else if (match.method == "toggle") {
|
} else if (match.method == "toggle") {
|
||||||
this->schedule_([obj]() { obj->toggle().perform(); });
|
this->schedule_([obj]() { obj->toggle().perform(); });
|
||||||
|
@ -577,9 +621,14 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->cover_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->cover_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto call = obj->make_call();
|
auto call = obj->make_call();
|
||||||
|
@ -653,7 +702,12 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->number_json(obj, obj->state, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->number_json(obj, obj->state, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -717,8 +771,13 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
for (auto *obj : App.get_dates()) {
|
for (auto *obj : App.get_dates()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
if (request->method() == HTTP_GET) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->date_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->date_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -772,7 +831,12 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->time_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->time_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -825,7 +889,12 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->datetime_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->datetime_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -880,8 +949,13 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->text_json(obj, obj->state, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
request->send(200, "text/json", data.c_str());
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->text_json(obj, obj->state, detail);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (match.method != "set") {
|
if (match.method != "set") {
|
||||||
|
@ -995,11 +1069,15 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->climate_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->climate_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match.method != "set") {
|
if (match.method != "set") {
|
||||||
request->send(404);
|
request->send(404);
|
||||||
return;
|
return;
|
||||||
|
@ -1149,7 +1227,12 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->lock_json(obj, obj->state, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->lock_json(obj, obj->state, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
} else if (match.method == "lock") {
|
} else if (match.method == "lock") {
|
||||||
this->schedule_([obj]() { obj->lock(); });
|
this->schedule_([obj]() { obj->lock(); });
|
||||||
|
@ -1192,9 +1275,14 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->valve_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->valve_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto call = obj->make_call();
|
auto call = obj->make_call();
|
||||||
|
@ -1257,7 +1345,12 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1314,7 +1407,12 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
std::string data = this->update_json(obj, DETAIL_STATE);
|
auto detail = DETAIL_STATE;
|
||||||
|
auto *param = request->getParam("detail");
|
||||||
|
if (param && param->value() == "all") {
|
||||||
|
detail = DETAIL_ALL;
|
||||||
|
}
|
||||||
|
std::string data = this->update_json(obj, detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -750,6 +750,7 @@ def time_period_str_unit(value):
|
||||||
"ns": "nanoseconds",
|
"ns": "nanoseconds",
|
||||||
"nanoseconds": "nanoseconds",
|
"nanoseconds": "nanoseconds",
|
||||||
"us": "microseconds",
|
"us": "microseconds",
|
||||||
|
"µs": "microseconds",
|
||||||
"microseconds": "microseconds",
|
"microseconds": "microseconds",
|
||||||
"ms": "milliseconds",
|
"ms": "milliseconds",
|
||||||
"milliseconds": "milliseconds",
|
"milliseconds": "milliseconds",
|
||||||
|
|
|
@ -53,6 +53,7 @@ CONF_ALLOW_OTHER_USES = "allow_other_uses"
|
||||||
CONF_ALPHA = "alpha"
|
CONF_ALPHA = "alpha"
|
||||||
CONF_ALTITUDE = "altitude"
|
CONF_ALTITUDE = "altitude"
|
||||||
CONF_AMBIENT_LIGHT = "ambient_light"
|
CONF_AMBIENT_LIGHT = "ambient_light"
|
||||||
|
CONF_AMMONIA = "ammonia"
|
||||||
CONF_ANALOG = "analog"
|
CONF_ANALOG = "analog"
|
||||||
CONF_AND = "and"
|
CONF_AND = "and"
|
||||||
CONF_ANGLE = "angle"
|
CONF_ANGLE = "angle"
|
||||||
|
@ -110,6 +111,7 @@ CONF_CALIBRATE_LINEAR = "calibrate_linear"
|
||||||
CONF_CALIBRATION = "calibration"
|
CONF_CALIBRATION = "calibration"
|
||||||
CONF_CAPACITANCE = "capacitance"
|
CONF_CAPACITANCE = "capacitance"
|
||||||
CONF_CAPACITY = "capacity"
|
CONF_CAPACITY = "capacity"
|
||||||
|
CONF_CARBON_MONOXIDE = "carbon_monoxide"
|
||||||
CONF_CARRIER_DUTY_PERCENT = "carrier_duty_percent"
|
CONF_CARRIER_DUTY_PERCENT = "carrier_duty_percent"
|
||||||
CONF_CARRIER_FREQUENCY = "carrier_frequency"
|
CONF_CARRIER_FREQUENCY = "carrier_frequency"
|
||||||
CONF_CERTIFICATE = "certificate"
|
CONF_CERTIFICATE = "certificate"
|
||||||
|
@ -263,6 +265,7 @@ CONF_ENUM_DATAPOINT = "enum_datapoint"
|
||||||
CONF_EQUATION = "equation"
|
CONF_EQUATION = "equation"
|
||||||
CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support"
|
CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support"
|
||||||
CONF_ESPHOME = "esphome"
|
CONF_ESPHOME = "esphome"
|
||||||
|
CONF_ETHANOL = "ethanol"
|
||||||
CONF_ETHERNET = "ethernet"
|
CONF_ETHERNET = "ethernet"
|
||||||
CONF_EVENT = "event"
|
CONF_EVENT = "event"
|
||||||
CONF_EVENT_TYPE = "event_type"
|
CONF_EVENT_TYPE = "event_type"
|
||||||
|
@ -361,6 +364,7 @@ CONF_HOURS = "hours"
|
||||||
CONF_HSYNC_PIN = "hsync_pin"
|
CONF_HSYNC_PIN = "hsync_pin"
|
||||||
CONF_HUMIDITY = "humidity"
|
CONF_HUMIDITY = "humidity"
|
||||||
CONF_HUMIDITY_SENSOR = "humidity_sensor"
|
CONF_HUMIDITY_SENSOR = "humidity_sensor"
|
||||||
|
CONF_HYDROGEN = "hydrogen"
|
||||||
CONF_HYSTERESIS = "hysteresis"
|
CONF_HYSTERESIS = "hysteresis"
|
||||||
CONF_I2C = "i2c"
|
CONF_I2C = "i2c"
|
||||||
CONF_I2C_ID = "i2c_id"
|
CONF_I2C_ID = "i2c_id"
|
||||||
|
@ -376,6 +380,7 @@ CONF_IDLE_ACTION = "idle_action"
|
||||||
CONF_IDLE_LEVEL = "idle_level"
|
CONF_IDLE_LEVEL = "idle_level"
|
||||||
CONF_IDLE_TIME = "idle_time"
|
CONF_IDLE_TIME = "idle_time"
|
||||||
CONF_IF = "if"
|
CONF_IF = "if"
|
||||||
|
CONF_IGNORE_EFUSE_CUSTOM_MAC = "ignore_efuse_custom_mac"
|
||||||
CONF_IGNORE_EFUSE_MAC_CRC = "ignore_efuse_mac_crc"
|
CONF_IGNORE_EFUSE_MAC_CRC = "ignore_efuse_mac_crc"
|
||||||
CONF_IGNORE_OUT_OF_RANGE = "ignore_out_of_range"
|
CONF_IGNORE_OUT_OF_RANGE = "ignore_out_of_range"
|
||||||
CONF_IGNORE_PIN_VALIDATION_ERROR = "ignore_pin_validation_error"
|
CONF_IGNORE_PIN_VALIDATION_ERROR = "ignore_pin_validation_error"
|
||||||
|
@ -476,6 +481,7 @@ CONF_MEDIA_PLAYER = "media_player"
|
||||||
CONF_MEDIUM = "medium"
|
CONF_MEDIUM = "medium"
|
||||||
CONF_MEMORY_BLOCKS = "memory_blocks"
|
CONF_MEMORY_BLOCKS = "memory_blocks"
|
||||||
CONF_MESSAGE = "message"
|
CONF_MESSAGE = "message"
|
||||||
|
CONF_METHANE = "methane"
|
||||||
CONF_METHOD = "method"
|
CONF_METHOD = "method"
|
||||||
CONF_MICROPHONE = "microphone"
|
CONF_MICROPHONE = "microphone"
|
||||||
CONF_MIN_BRIGHTNESS = "min_brightness"
|
CONF_MIN_BRIGHTNESS = "min_brightness"
|
||||||
|
@ -522,6 +528,7 @@ CONF_NBITS = "nbits"
|
||||||
CONF_NEC = "nec"
|
CONF_NEC = "nec"
|
||||||
CONF_NETWORKS = "networks"
|
CONF_NETWORKS = "networks"
|
||||||
CONF_NEW_PASSWORD = "new_password"
|
CONF_NEW_PASSWORD = "new_password"
|
||||||
|
CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide"
|
||||||
CONF_NOISE_LEVEL = "noise_level"
|
CONF_NOISE_LEVEL = "noise_level"
|
||||||
CONF_NUM_ATTEMPTS = "num_attempts"
|
CONF_NUM_ATTEMPTS = "num_attempts"
|
||||||
CONF_NUM_CHANNELS = "num_channels"
|
CONF_NUM_CHANNELS = "num_channels"
|
||||||
|
|
|
@ -44,9 +44,7 @@
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include "esp32/rom/crc.h"
|
#include "esp32/rom/crc.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC)
|
|
||||||
#include "esp_efuse.h"
|
#include "esp_efuse.h"
|
||||||
#include "esp_efuse_table.h"
|
#include "esp_efuse_table.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -711,12 +709,13 @@ void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool has_custom_mac_address() {
|
bool has_custom_mac_address() {
|
||||||
#ifdef USE_ESP32
|
#if defined(USE_ESP32) && !defined(USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC)
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC)
|
// do not use 'esp_efuse_mac_get_custom(mac)' because it drops an error in the logs whenever it fails
|
||||||
return (esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac);
|
#ifndef USE_ESP32_VARIANT_ESP32
|
||||||
|
return (esp_efuse_read_field_blob(ESP_EFUSE_USER_DATA_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac);
|
||||||
#else
|
#else
|
||||||
return (esp_efuse_mac_get_custom(mac) == ESP_OK) && mac_address_is_valid(mac);
|
return (esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -90,9 +90,6 @@ esp32:
|
||||||
RP2040_CONFIG = """
|
RP2040_CONFIG = """
|
||||||
rp2040:
|
rp2040:
|
||||||
board: {board}
|
board: {board}
|
||||||
framework:
|
|
||||||
# Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged
|
|
||||||
platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BK72XX_CONFIG = """
|
BK72XX_CONFIG = """
|
||||||
|
|
|
@ -165,7 +165,7 @@ platform_packages =
|
||||||
extends = common:arduino
|
extends = common:arduino
|
||||||
board_build.filesystem_size = 0.5m
|
board_build.filesystem_size = 0.5m
|
||||||
|
|
||||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12
|
||||||
platform_packages =
|
platform_packages =
|
||||||
; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted
|
; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted
|
||||||
earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip
|
earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip
|
||||||
|
|
|
@ -15,7 +15,7 @@ click==8.1.7
|
||||||
esphome-dashboard==20240620.0
|
esphome-dashboard==20240620.0
|
||||||
aioesphomeapi==24.6.2
|
aioesphomeapi==24.6.2
|
||||||
zeroconf==0.132.2
|
zeroconf==0.132.2
|
||||||
python-magic==0.4.27
|
puremagic==1.27
|
||||||
ruamel.yaml==0.18.6 # dashboard_import
|
ruamel.yaml==0.18.6 # dashboard_import
|
||||||
|
|
||||||
# esp-idf requires this, but doesn't bundle it by default
|
# esp-idf requires this, but doesn't bundle it by default
|
||||||
|
|
|
@ -98,7 +98,7 @@ def clang_options(idedata):
|
||||||
cmd.extend(["-isystem", directory])
|
cmd.extend(["-isystem", directory])
|
||||||
|
|
||||||
# add library include directories using -isystem to suppress their errors
|
# add library include directories using -isystem to suppress their errors
|
||||||
for directory in sorted(set(idedata["includes"]["build"])):
|
for directory in list(idedata["includes"]["build"]):
|
||||||
# skip our own directories, we add those later
|
# skip our own directories, we add those later
|
||||||
if (
|
if (
|
||||||
not directory.startswith(f"{root_path}/")
|
not directory.startswith(f"{root_path}/")
|
||||||
|
|
|
@ -12,7 +12,7 @@ light:
|
||||||
num_leds: 60
|
num_leds: 60
|
||||||
rmt_channel: 1
|
rmt_channel: 1
|
||||||
rgb_order: RGB
|
rgb_order: RGB
|
||||||
bit0_high: 100us
|
bit0_high: 100µs
|
||||||
bit0_low: 100us
|
bit0_low: 100µs
|
||||||
bit1_high: 100us
|
bit1_high: 100µs
|
||||||
bit1_low: 100us
|
bit1_low: 100µs
|
||||||
|
|
|
@ -12,7 +12,7 @@ light:
|
||||||
num_leds: 60
|
num_leds: 60
|
||||||
rmt_channel: 2
|
rmt_channel: 2
|
||||||
rgb_order: RGB
|
rgb_order: RGB
|
||||||
bit0_high: 100us
|
bit0_high: 100µs
|
||||||
bit0_low: 100us
|
bit0_low: 100µs
|
||||||
bit1_high: 100us
|
bit1_high: 100µs
|
||||||
bit1_low: 100us
|
bit1_low: 100µs
|
||||||
|
|
|
@ -4,9 +4,6 @@ esphome:
|
||||||
|
|
||||||
rp2040:
|
rp2040:
|
||||||
board: rpipicow
|
board: rpipicow
|
||||||
framework:
|
|
||||||
# Waiting for https://github.com/platformio/platform-raspberrypi/pull/36
|
|
||||||
platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c
|
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
level: VERY_VERBOSE
|
level: VERY_VERBOSE
|
||||||
|
|
Loading…
Reference in a new issue