1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-05 09:31:54 +00:00

Compare commits

...

44 Commits

Author SHA1 Message Date
Otto Winter
60e6366521 Bump version to v1.14.4 2020-06-05 12:56:23 +02:00
Otto Winter
072b2c445c Add ESP8266 core v2.6.2 (#905)
* Add ESP8266 core v2.6.2

* Upstream ESP8266 Wifi fixes

* Replace disable_interrupt with InterruptLock C++ class

* Update code to use InterruptLock

* Lint

* Update dht.cpp

* Improve InterruptLock docs, mark as ICACHE_RAM_ATTR

* Fixes
2020-06-05 12:56:10 +02:00
Otto Winter
219fe41831 Update ESP32 BLE ADV parse to match BLE spec (#904)
* Update ESP32 BLE ADV parse to match BLE spec

* Update xiaomi

* Update ruuvi

* Format

* Update esp32_ble_tracker.cpp

* Fix log

* Format

* Update xiaomi_ble.cpp
2020-06-05 12:55:24 +02:00
Otto Winter
dcc8bb83af Ignore ESP32 Camera unknown framesizes (#901)
Fixes https://github.com/esphome/issues/issues/866
2020-06-05 12:52:05 +02:00
Otto Winter
a8e3521f3c web_server call setup_controller (#899) 2020-06-05 12:52:05 +02:00
Otto Winter
706dc6d116 Fix MQTT logs Int or String expected Python 3 (#898)
Fixes https://github.com/esphome/issues/issues/850
2020-06-05 12:52:04 +02:00
Guillermo Ruffino
84accb6df6 fix climate_ir on receive optional (#897)
* fix climate on receive optional

* add climate tests
2020-06-05 12:52:04 +02:00
warpzone
8421570b18 fix the problem of missing part of advertising packet when activ… (#868)
* fix the problem of missing part of advertising packet when active scan is enabled.

* fix for ci-suggest-changes
2020-06-05 12:52:04 +02:00
Otto Winter
d355543ac9 Merge branch '1.14.4' 2020-06-05 12:42:56 +02:00
Otto Winter
3a597c5aa6 Fix gitignore intellij idea 2020-06-05 12:38:05 +02:00
Otto Winter
c7dddaded4 Upgrade docker base image to 2.1.1 2020-06-05 12:28:37 +02:00
Otto Winter
aae4b2ea5d Upgrade docker base image to 2.1.0 2020-06-05 12:27:26 +02:00
Otto Winter
310e2a0b20 Fix build 2020-06-05 12:25:29 +02:00
Niklas Wagner
0b04d143ac Upgrade dependencies 2020-06-05 12:00:20 +02:00
Otto Winter
53c231a7eb Revert "Atm90e32 pf fix (#841)"
This reverts commit 7842a55c81.
2019-11-17 23:37:00 +01:00
Otto Winter
d44ce82aa1 Bump version to v1.14.3 2019-11-17 23:34:04 +01:00
Otto Winter
a055de48e4 Change ESP8266 default wifi output power (#862)
See also https://github.com/esphome/issues/issues/455
2019-11-17 23:34:01 +01:00
Otto Winter
37b8d665fe Revert ESP32 BLE Tracker defaults (#863)
Fixes https://github.com/esphome/issues/issues/824
Fixes https://github.com/esphome/issues/issues/851
2019-11-17 23:34:01 +01:00
Otto Winter
dd7c8dabb1 Fix MQTT python 3 stringify IPAddress Type (#864)
Fixes https://github.com/esphome/issues/issues/850
2019-11-17 23:34:01 +01:00
Otto Winter
e41a9875e3 Improve WiFi disconnect messages (#857)
* Improve WiFi disconnect messages

* Fix

* Update wifi_component_esp32.cpp
2019-11-17 23:34:01 +01:00
Brandon Davidson
c5c42c4338 Tuya: Fix init sequence and handle wifi test command (#820)
* Handle WiFi test command

Also rename commands to match Tuya protocol docs

* Fix init sequence and product info check

* Fix clang-format suggestions

* Additional changes based on code review

* Fix temp command buffer scope

* Let the interval timer fire the first heatbeat

* Fix init steps; add logging

* Lint

* Remove setup_priority override

* Add delay to dump_config

* Refactor dump sequence

* Fix verbose logging

* Fix lints

* Don't bother suppressing duplicate config dumps

* nolint


Co-authored-by: Otto Winter <otto@otto-winter.com>
2019-11-17 23:34:00 +01:00
Brandon Davidson
531428b8b0 Fix logger uart conflict check (#858)
* Fix logger uart conflict check

* Fix class for check func

* Fix syntax

Hope lint is OK with moving the end of the conditional outside the #IFDEF

* Move end of conditional inside ifdef and remove extra whitespace

* Simplify

clang-format did not like the ifdefs and was reformatting in a way that killed clang-tidy.

Simple solution is to use logger's hw_serial as source of truth

Also simplifies the code - uart doesn't need to know what the logger uart settings mean
2019-11-17 23:34:00 +01:00
Otto Winter
ea8068e001 Switch to 115200 baud upload if 460800 fails (#856)
* Switch to 115200 baud upload if 460800 fails

* Update __main__.py
2019-11-17 23:34:00 +01:00
Mark
7842a55c81 Atm90e32 pf fix (#841)
* correct set_pf_sensor to set_power_factor_senor

* remove junk files added in error

* correct sensors.yaml reference to set_reactive_power

* Fixes
2019-11-17 23:33:56 +01:00
Samuel Sieb
51d39862b1 add position reporting to the template cover (#821)
* add position reporting to the template cover

* remove duplicate import

* use config flag instead


Co-authored-by: Samuel Sieb <samuel@sieb.net>
2019-11-17 23:33:21 +01:00
Otto Winter
bfea6ca79b Mark python 3.5 support deprecated (#849)
* Mark python 3.5 unsupported

Fixes https://github.com/esphome/issues/issues/831

* Update .travis.yml

* Update typing dep
2019-11-17 23:33:20 +01:00
Otto Winter
6297395018 Fix PZEM004T v2 (#846)
Fixes https://github.com/esphome/issues/issues/817
2019-11-17 23:33:20 +01:00
Otto Winter
a5b49dbfa6 Adjust some units (#852)
* Adjust some units

Fixes https://github.com/esphome/issues/issues/843

* Lint
2019-11-17 23:33:12 +01:00
Otto Winter
7c0d777173 Check DHT sensor exists before publishing (#850)
Fixes https://github.com/esphome/issues/issues/841
2019-11-17 23:31:36 +01:00
Otto Winter
74878276fc Web server CORS headers (#840)
* Add CORS header to web server

* Refactor

* Cleanup

See also https://github.com/esphome/issues/issues/806
2019-11-17 23:31:35 +01:00
Otto Winter
226e3b1dad Fix sensor force_update native API (#847)
Fixes https://github.com/esphome/issues/issues/842
2019-11-17 23:31:35 +01:00
Otto Winter
7752794fc5 Fix neopixelbus missing method pins (#848)
Fixes https://github.com/esphome/issues/issues/839
2019-11-17 23:31:35 +01:00
Otto Winter
b3094d6a53 Add missing state attribute (#851)
* Add api missing_state attribute

Fixes https://github.com/esphome/issues/issues/828

Adds a new property for missing state, so that HA can now when a sensor does not have a state yet.

* Update api.proto
2019-11-17 23:31:35 +01:00
Otto Winter
e3640e710f Add wifi output_power setting (#853)
* Add wifi output_power setting

See also:
 - https://github.com/esphome/feature-requests/issues/471#issuecomment-552350467
 - https://github.com/esp8266/Arduino/issues/6366
 - https://github.com/esp8266/Arduino/issues/6471
 - 849f8cf920/code/espurna/config/general.h (L593-L599)
 - https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/network/esp_wifi.html#_CPPv425esp_wifi_set_max_tx_power6int8_t

* Lint
2019-11-17 23:31:35 +01:00
Guillermo Ruffino
2ef64b55c5 fix missing checks of is_playing condition (#844) 2019-11-17 23:31:34 +01:00
Otto Winter
7f6672bb37 Fix calculations for negative sun declination (#839)
Fixes https://github.com/esphome/issues/issues/793

Also adds a clampd function that operates with doubles, not floats
2019-11-17 23:31:34 +01:00
Otto Winter
68a3b31628 Update variable in scheduler (#838)
Fixes https://github.com/esphome/issues/issues/826
2019-11-17 23:31:34 +01:00
Otto Winter
1b35855e68 Update platformio libraries (#837)
* Update platformio libraries

* Lint
2019-11-17 23:31:34 +01:00
Otto Winter
1e1837000d Fix homeassistant.service schema lambda (#833)
* Fix homeassistant.service schema lambda

Fixes https://github.com/esphome/issues/issues/820

* Improve

* Fix
2019-11-17 23:31:34 +01:00
Otto Winter
e2d5257632 Fix ESP32 rotary encoder (#834)
* Fix ESP32 rotary encoder

Fixes https://github.com/esphome/issues/issues/672

* Update rotary_encoder.cpp

* Lint
2019-11-17 23:31:34 +01:00
Otto Winter
387c75793b WiFi AP apply manual ip settings (#836) 2019-11-17 23:31:33 +01:00
Otto Winter
4f3a74d08a ESP8266 remove default opmode check (#835) 2019-11-17 23:31:33 +01:00
Otto Winter
fdbc59a159 Bump version to v1.14.2 2019-11-03 00:20:31 +01:00
Otto Winter
0db37bb55c Fix weird ESP8266 wifi crashes (#831)
* Try fix ESP8266 weird crashes

* Only call disconnect if STA is active
2019-11-03 00:20:25 +01:00
64 changed files with 831 additions and 468 deletions

1
.gitignore vendored
View File

@@ -114,3 +114,4 @@ config/
tests/build/ tests/build/
tests/.esphome/ tests/.esphome/
/.temp-clang-tidy.cpp /.temp-clang-tidy.cpp
/.idea/

View File

@@ -3,7 +3,7 @@
variables: variables:
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375/ DOCKER_HOST: tcp://docker:2375/
BASE_VERSION: '2.0.1' BASE_VERSION: '2.1.1'
TZ: UTC TZ: UTC
stages: stages:
@@ -33,7 +33,7 @@ stages:
- docker info - docker info
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
script: script:
- docker run --rm --privileged multiarch/qemu-user-static:4.1.0-1 --reset -p yes - docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes
- TAG="${CI_COMMIT_TAG#v}" - TAG="${CI_COMMIT_TAG#v}"
- TAG="${TAG:-${CI_COMMIT_SHA:0:7}}" - TAG="${TAG:-${CI_COMMIT_SHA:0:7}}"
- echo "Tag ${TAG}" - echo "Tag ${TAG}"

View File

@@ -1,6 +1,6 @@
sudo: false sudo: false
language: python language: python
python: '3.5' python: '3.6'
install: script/setup install: script/setup
cache: cache:
directories: directories:
@@ -15,8 +15,8 @@ matrix:
- script/ci-custom.py - script/ci-custom.py
- flake8 esphome - flake8 esphome
- pylint esphome - pylint esphome
- python: "3.5" - python: "3.6"
env: TARGET=Test3.5 env: TARGET=Test3.6
script: script:
- esphome tests/test1.yaml compile - esphome tests/test1.yaml compile
- esphome tests/test2.yaml compile - esphome tests/test2.yaml compile

View File

@@ -1,4 +1,4 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:2.0.1 ARG BUILD_FROM=esphome/esphome-base-amd64:2.1.1
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
COPY . . COPY . .

View File

@@ -1,4 +1,4 @@
FROM esphome/esphome-base-amd64:2.0.1 FROM esphome/esphome-base-amd64:2.1.1
RUN \ RUN \
apt-get update \ apt-get update \

View File

@@ -14,7 +14,7 @@ from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
from esphome.helpers import color, indent from esphome.helpers import color, indent
from esphome.py_compat import IS_PY2, safe_input from esphome.py_compat import IS_PY2, safe_input, IS_PY3
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -165,16 +165,27 @@ def compile_program(args, config):
def upload_using_esptool(config, port): def upload_using_esptool(config, port):
path = CORE.firmware_bin path = CORE.firmware_bin
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)
'--baud', str(config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)),
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: def run_esptool(baud_rate):
import esptool cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
# pylint: disable=protected-access '--baud', str(baud_rate),
return run_external_command(esptool._main, *cmd) '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
return run_external_process(*cmd) if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
import esptool
# pylint: disable=protected-access
return run_external_command(esptool._main, *cmd)
return run_external_process(*cmd)
rc = run_esptool(first_baudrate)
if rc == 0 or first_baudrate == 115200:
return rc
# Try with 115200 baud rate, with some serial chips the faster baud rates do not work well
_LOGGER.info("Upload with baud rate %s failed. Trying again with baud rate 115200.",
first_baudrate)
return run_esptool(115200)
def upload_program(config, args, host): def upload_program(config, args, host):
@@ -514,6 +525,10 @@ def run_esphome(argv):
_LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated " _LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated "
"and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 " "and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 "
"or higher.") "or higher.")
elif IS_PY3 and sys.version_info < (3, 6, 0):
_LOGGER.warning("You're using ESPHome with python 3.5. Support for python 3.5 is "
"deprecated and will be removed in 1.15.0. Please reinstall ESPHome with "
"python 3.6 or higher.")
if args.command in PRE_CONFIG_ACTIONS: if args.command in PRE_CONFIG_ACTIONS:
try: try:

View File

@@ -70,14 +70,14 @@ def to_code(config):
cg.add_global(api_ns.using) cg.add_global(api_ns.using)
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string)}) KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
cv.GenerateID(): cv.use_id(APIServer), cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_SERVICE): cv.templatable(cv.string), cv.Required(CONF_SERVICE): cv.templatable(cv.string),
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}),
}) })

View File

@@ -216,6 +216,9 @@ message BinarySensorStateResponse {
fixed32 key = 1; fixed32 key = 1;
bool state = 2; bool state = 2;
// If the binary sensor does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
} }
// ==================== COVER ==================== // ==================== COVER ====================
@@ -416,6 +419,9 @@ message SensorStateResponse {
fixed32 key = 1; fixed32 key = 1;
float state = 2; float state = 2;
// If the sensor does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
} }
// ==================== SWITCH ==================== // ==================== SWITCH ====================
@@ -472,6 +478,9 @@ message TextSensorStateResponse {
fixed32 key = 1; fixed32 key = 1;
string state = 2; string state = 2;
// If the text sensor does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
} }
// ==================== SUBSCRIBE LOGS ==================== // ==================== SUBSCRIBE LOGS ====================

View File

@@ -163,6 +163,7 @@ bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary
BinarySensorStateResponse resp; BinarySensorStateResponse resp;
resp.key = binary_sensor->get_object_id_hash(); resp.key = binary_sensor->get_object_id_hash();
resp.state = state; resp.state = state;
resp.missing_state = !binary_sensor->has_state();
return this->send_binary_sensor_state_response(resp); return this->send_binary_sensor_state_response(resp);
} }
bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) { bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
@@ -362,6 +363,7 @@ bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
SensorStateResponse resp{}; SensorStateResponse resp{};
resp.key = sensor->get_object_id_hash(); resp.key = sensor->get_object_id_hash();
resp.state = state; resp.state = state;
resp.missing_state = !sensor->has_state();
return this->send_sensor_state_response(resp); return this->send_sensor_state_response(resp);
} }
bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
@@ -375,6 +377,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.icon = sensor->get_icon(); msg.icon = sensor->get_icon();
msg.unit_of_measurement = sensor->get_unit_of_measurement(); msg.unit_of_measurement = sensor->get_unit_of_measurement();
msg.accuracy_decimals = sensor->get_accuracy_decimals(); msg.accuracy_decimals = sensor->get_accuracy_decimals();
msg.force_update = sensor->get_force_update();
return this->send_list_entities_sensor_response(msg); return this->send_list_entities_sensor_response(msg);
} }
#endif #endif
@@ -419,6 +422,7 @@ bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor,
TextSensorStateResponse resp{}; TextSensorStateResponse resp{};
resp.key = text_sensor->get_object_id_hash(); resp.key = text_sensor->get_object_id_hash();
resp.state = std::move(state); resp.state = std::move(state);
resp.missing_state = !text_sensor->has_state();
return this->send_text_sensor_state_response(resp); return this->send_text_sensor_state_response(resp);
} }
bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) { bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {

View File

@@ -404,6 +404,10 @@ bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->state = value.as_bool(); this->state = value.as_bool();
return true; return true;
} }
case 3: {
this->missing_state = value.as_bool();
return true;
}
default: default:
return false; return false;
} }
@@ -421,6 +425,7 @@ bool BinarySensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value
void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const { void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key); buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state); buffer.encode_bool(2, this->state);
buffer.encode_bool(3, this->missing_state);
} }
void BinarySensorStateResponse::dump_to(std::string &out) const { void BinarySensorStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@@ -433,6 +438,10 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
out.append(" state: "); out.append(" state: ");
out.append(YESNO(this->state)); out.append(YESNO(this->state));
out.append("\n"); out.append("\n");
out.append(" missing_state: ");
out.append(YESNO(this->missing_state));
out.append("\n");
out.append("}"); out.append("}");
} }
bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@@ -1451,6 +1460,16 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
} }
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
this->missing_state = value.as_bool();
return true;
}
default:
return false;
}
}
bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {
@@ -1468,6 +1487,7 @@ bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
void SensorStateResponse::encode(ProtoWriteBuffer buffer) const { void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key); buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->state); buffer.encode_float(2, this->state);
buffer.encode_bool(3, this->missing_state);
} }
void SensorStateResponse::dump_to(std::string &out) const { void SensorStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@@ -1481,6 +1501,10 @@ void SensorStateResponse::dump_to(std::string &out) const {
sprintf(buffer, "%g", this->state); sprintf(buffer, "%g", this->state);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" missing_state: ");
out.append(YESNO(this->missing_state));
out.append("\n");
out.append("}"); out.append("}");
} }
bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@@ -1700,6 +1724,16 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
} }
bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
this->missing_state = value.as_bool();
return true;
}
default:
return false;
}
}
bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) { switch (field_id) {
case 2: { case 2: {
@@ -1723,6 +1757,7 @@ bool TextSensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value)
void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const { void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key); buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->state); buffer.encode_string(2, this->state);
buffer.encode_bool(3, this->missing_state);
} }
void TextSensorStateResponse::dump_to(std::string &out) const { void TextSensorStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@@ -1735,6 +1770,10 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
out.append(" state: "); out.append(" state: ");
out.append("'").append(this->state).append("'"); out.append("'").append(this->state).append("'");
out.append("\n"); out.append("\n");
out.append(" missing_state: ");
out.append(YESNO(this->missing_state));
out.append("\n");
out.append("}"); out.append("}");
} }
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {

View File

@@ -188,8 +188,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
}; };
class BinarySensorStateResponse : public ProtoMessage { class BinarySensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0}; // NOLINT
bool state{false}; // NOLINT bool state{false}; // NOLINT
bool missing_state{false}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@@ -380,13 +381,15 @@ class ListEntitiesSensorResponse : public ProtoMessage {
}; };
class SensorStateResponse : public ProtoMessage { class SensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0}; // NOLINT
float state{0.0f}; // NOLINT float state{0.0f}; // NOLINT
bool missing_state{false}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
protected: protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class ListEntitiesSwitchResponse : public ProtoMessage { class ListEntitiesSwitchResponse : public ProtoMessage {
public: public:
@@ -442,14 +445,16 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
}; };
class TextSensorStateResponse : public ProtoMessage { class TextSensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0}; // NOLINT
std::string state{}; // NOLINT std::string state{}; // NOLINT
bool missing_state{false}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
protected: protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class SubscribeLogsRequest : public ProtoMessage { class SubscribeLogsRequest : public ProtoMessage {
public: public:

View File

@@ -7,9 +7,6 @@ namespace api {
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
if (!binary_sensor->has_state())
return true;
return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state); return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
} }
#endif #endif
@@ -24,9 +21,6 @@ bool InitialStateIterator::on_light(light::LightState *light) { return this->cli
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
if (!sensor->has_state())
return true;
return this->client_->send_sensor_state(sensor, sensor->state); return this->client_->send_sensor_state(sensor, sensor->state);
} }
#endif #endif
@@ -37,9 +31,6 @@ bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
if (!text_sensor->has_state())
return true;
return this->client_->send_text_sensor_state(text_sensor, text_sensor->state); return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
} }
#endif #endif

View File

@@ -1,23 +1,13 @@
# Dummy integration to allow relying on AsyncTCP # Dummy integration to allow relying on AsyncTCP
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import ARDUINO_VERSION_ESP32_1_0_0, ARDUINO_VERSION_ESP32_1_0_1, \
ARDUINO_VERSION_ESP32_1_0_2
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
@coroutine_with_priority(200.0) @coroutine_with_priority(200.0)
def to_code(config): def to_code(config):
if CORE.is_esp32: if CORE.is_esp32:
# https://github.com/me-no-dev/AsyncTCP/blob/master/library.json # https://github.com/OttoWinter/AsyncTCP/blob/master/library.json
versions_requiring_older_asynctcp = [ cg.add_library('AsyncTCP-esphome', '1.1.1')
ARDUINO_VERSION_ESP32_1_0_0,
ARDUINO_VERSION_ESP32_1_0_1,
ARDUINO_VERSION_ESP32_1_0_2,
]
if CORE.arduino_version in versions_requiring_older_asynctcp:
cg.add_library('AsyncTCP', '1.0.3')
else:
cg.add_library('AsyncTCP', '1.1.1')
elif CORE.is_esp8266: elif CORE.is_esp8266:
# https://github.com/OttoWinter/ESPAsyncTCP # https://github.com/OttoWinter/ESPAsyncTCP
cg.add_library('ESPAsyncTCP-esphome', '1.2.2') cg.add_library('ESPAsyncTCP-esphome', '1.2.2')

View File

@@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome.components import sensor, spi from esphome.components import sensor, spi
from esphome.const import \ from esphome.const import \
CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_FREQUENCY, \ CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_FREQUENCY, \
ICON_FLASH, UNIT_HZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_HERTZ, ICON_CURRENT_AC
CONF_PHASE_A = 'phase_a' CONF_PHASE_A = 'phase_a'
CONF_PHASE_B = 'phase_b' CONF_PHASE_B = 'phase_b'
@@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HZ, ICON_FLASH, 1), cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True), cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True),
}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA) }).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA)

View File

@@ -44,6 +44,9 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
/// Transmit via IR the state of this climate controller. /// Transmit via IR the state of this climate controller.
virtual void transmit_state() = 0; virtual void transmit_state() = 0;
// Dummy implement on_receive so implementation is optional for inheritors
bool on_receive(remote_base::RemoteReceiveData data) override { return false; };
bool supports_cool_{true}; bool supports_cool_{true};
bool supports_heat_{true}; bool supports_heat_{true};

View File

@@ -32,9 +32,11 @@ void DallasComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up DallasComponent..."); ESP_LOGCONFIG(TAG, "Setting up DallasComponent...");
yield(); yield();
disable_interrupts(); std::vector<uint64_t> raw_sensors;
std::vector<uint64_t> raw_sensors = this->one_wire_->search_vec(); {
enable_interrupts(); InterruptLock lock;
raw_sensors = this->one_wire_->search_vec();
}
for (auto &address : raw_sensors) { for (auto &address : raw_sensors) {
std::string s = uint64_to_string(address); std::string s = uint64_to_string(address);
@@ -108,16 +110,17 @@ DallasTemperatureSensor *DallasComponent::get_sensor_by_index(uint8_t index, uin
void DallasComponent::update() { void DallasComponent::update() {
this->status_clear_warning(); this->status_clear_warning();
disable_interrupts();
bool result; bool result;
if (!this->one_wire_->reset()) { {
result = false; InterruptLock lock;
} else { if (!this->one_wire_->reset()) {
result = true; result = false;
this->one_wire_->skip(); } else {
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION); result = true;
this->one_wire_->skip();
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
}
} }
enable_interrupts();
if (!result) { if (!result) {
ESP_LOGE(TAG, "Requesting conversion failed"); ESP_LOGE(TAG, "Requesting conversion failed");
@@ -127,9 +130,11 @@ void DallasComponent::update() {
for (auto *sensor : this->sensors_) { for (auto *sensor : this->sensors_) {
this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] { this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
disable_interrupts(); bool res;
bool res = sensor->read_scratch_pad(); {
enable_interrupts(); InterruptLock lock;
res = sensor->read_scratch_pad();
}
if (!res) { if (!res) {
ESP_LOGW(TAG, "'%s' - Reseting bus for read failed!", sensor->get_name().c_str()); ESP_LOGW(TAG, "'%s' - Reseting bus for read failed!", sensor->get_name().c_str());
@@ -170,7 +175,7 @@ const std::string &DallasTemperatureSensor::get_address_name() {
return this->address_name_; return this->address_name_;
} }
bool DallasTemperatureSensor::read_scratch_pad() { bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
ESPOneWire *wire = this->parent_->one_wire_; ESPOneWire *wire = this->parent_->one_wire_;
if (!wire->reset()) { if (!wire->reset()) {
return false; return false;
@@ -185,9 +190,11 @@ bool DallasTemperatureSensor::read_scratch_pad() {
return true; return true;
} }
bool DallasTemperatureSensor::setup_sensor() { bool DallasTemperatureSensor::setup_sensor() {
disable_interrupts(); bool r;
bool r = this->read_scratch_pad(); {
enable_interrupts(); InterruptLock lock;
r = this->read_scratch_pad();
}
if (!r) { if (!r) {
ESP_LOGE(TAG, "Reading scratchpad failed: reset"); ESP_LOGE(TAG, "Reading scratchpad failed: reset");
@@ -222,20 +229,21 @@ bool DallasTemperatureSensor::setup_sensor() {
} }
ESPOneWire *wire = this->parent_->one_wire_; ESPOneWire *wire = this->parent_->one_wire_;
disable_interrupts(); {
if (wire->reset()) { InterruptLock lock;
wire->select(this->address_); if (wire->reset()) {
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD); wire->select(this->address_);
wire->write8(this->scratch_pad_[2]); // high alarm temp wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
wire->write8(this->scratch_pad_[3]); // low alarm temp wire->write8(this->scratch_pad_[2]); // high alarm temp
wire->write8(this->scratch_pad_[4]); // resolution wire->write8(this->scratch_pad_[3]); // low alarm temp
wire->reset(); wire->write8(this->scratch_pad_[4]); // resolution
wire->reset();
// write value to EEPROM // write value to EEPROM
wire->select(this->address_); wire->select(this->address_);
wire->write8(0x48); wire->write8(0x48);
}
} }
enable_interrupts();
delay(20); // allow it to finish operation delay(20); // allow it to finish operation
wire->reset(); wire->reset();

View File

@@ -12,7 +12,7 @@ const int ONE_WIRE_ROM_SEARCH = 0xF0;
ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {} ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {}
bool HOT ESPOneWire::reset() { bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() {
uint8_t retries = 125; uint8_t retries = 125;
// Wait for communication to clear // Wait for communication to clear
@@ -39,7 +39,7 @@ bool HOT ESPOneWire::reset() {
return r; return r;
} }
void HOT ESPOneWire::write_bit(bool bit) { void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) {
// Initiate write/read by pulling low. // Initiate write/read by pulling low.
this->pin_->pin_mode(OUTPUT); this->pin_->pin_mode(OUTPUT);
this->pin_->digital_write(false); this->pin_->digital_write(false);
@@ -60,7 +60,7 @@ void HOT ESPOneWire::write_bit(bool bit) {
} }
} }
bool HOT ESPOneWire::read_bit() { bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() {
// Initiate read slot by pulling LOW for at least 1µs // Initiate read slot by pulling LOW for at least 1µs
this->pin_->pin_mode(OUTPUT); this->pin_->pin_mode(OUTPUT);
this->pin_->digital_write(false); this->pin_->digital_write(false);
@@ -76,43 +76,43 @@ bool HOT ESPOneWire::read_bit() {
return r; return r;
} }
void ESPOneWire::write8(uint8_t val) { void ICACHE_RAM_ATTR ESPOneWire::write8(uint8_t val) {
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
this->write_bit(bool((1u << i) & val)); this->write_bit(bool((1u << i) & val));
} }
} }
void ESPOneWire::write64(uint64_t val) { void ICACHE_RAM_ATTR ESPOneWire::write64(uint64_t val) {
for (uint8_t i = 0; i < 64; i++) { for (uint8_t i = 0; i < 64; i++) {
this->write_bit(bool((1ULL << i) & val)); this->write_bit(bool((1ULL << i) & val));
} }
} }
uint8_t ESPOneWire::read8() { uint8_t ICACHE_RAM_ATTR ESPOneWire::read8() {
uint8_t ret = 0; uint8_t ret = 0;
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
ret |= (uint8_t(this->read_bit()) << i); ret |= (uint8_t(this->read_bit()) << i);
} }
return ret; return ret;
} }
uint64_t ESPOneWire::read64() { uint64_t ICACHE_RAM_ATTR ESPOneWire::read64() {
uint64_t ret = 0; uint64_t ret = 0;
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
ret |= (uint64_t(this->read_bit()) << i); ret |= (uint64_t(this->read_bit()) << i);
} }
return ret; return ret;
} }
void ESPOneWire::select(uint64_t address) { void ICACHE_RAM_ATTR ESPOneWire::select(uint64_t address) {
this->write8(ONE_WIRE_ROM_SELECT); this->write8(ONE_WIRE_ROM_SELECT);
this->write64(address); this->write64(address);
} }
void ESPOneWire::reset_search() { void ICACHE_RAM_ATTR ESPOneWire::reset_search() {
this->last_discrepancy_ = 0; this->last_discrepancy_ = 0;
this->last_device_flag_ = false; this->last_device_flag_ = false;
this->last_family_discrepancy_ = 0; this->last_family_discrepancy_ = 0;
this->rom_number_ = 0; this->rom_number_ = 0;
} }
uint64_t HOT ESPOneWire::search() { uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() {
if (this->last_device_flag_) { if (this->last_device_flag_) {
return 0u; return 0u;
} }
@@ -196,7 +196,7 @@ uint64_t HOT ESPOneWire::search() {
return this->rom_number_; return this->rom_number_;
} }
std::vector<uint64_t> ESPOneWire::search_vec() { std::vector<uint64_t> ICACHE_RAM_ATTR ESPOneWire::search_vec() {
std::vector<uint64_t> res; std::vector<uint64_t> res;
this->reset_search(); this->reset_search();
@@ -206,12 +206,12 @@ std::vector<uint64_t> ESPOneWire::search_vec() {
return res; return res;
} }
void ESPOneWire::skip() { void ICACHE_RAM_ATTR ESPOneWire::skip() {
this->write8(0xCC); // skip ROM this->write8(0xCC); // skip ROM
} }
GPIOPin *ESPOneWire::get_pin() { return this->pin_; } GPIOPin *ESPOneWire::get_pin() { return this->pin_; }
uint8_t *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); } uint8_t ICACHE_RAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
} // namespace dallas } // namespace dallas
} // namespace esphome } // namespace esphome

View File

@@ -8,8 +8,10 @@ static const char* TAG = "dfplayer";
void DFPlayer::play_folder(uint16_t folder, uint16_t file) { void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
if (folder < 100 && file < 256) { if (folder < 100 && file < 256) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file); this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file);
} else if (folder <= 10 && file <= 1000) { } else if (folder <= 10 && file <= 1000) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file); this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file);
} else { } else {
ESP_LOGE(TAG, "Cannot play folder %d file %d.", folder, file); ESP_LOGE(TAG, "Cannot play folder %d file %d.", folder, file);
@@ -93,6 +95,10 @@ void DFPlayer::loop() {
ESP_LOGI(TAG, "USB, TF Card available"); ESP_LOGI(TAG, "USB, TF Card available");
} }
break; break;
case 0x40:
ESP_LOGV(TAG, "Nack");
this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false;
case 0x41: case 0x41:
ESP_LOGV(TAG, "Ack ok"); ESP_LOGV(TAG, "Ack ok");
this->is_playing_ |= this->ack_set_is_playing_; this->is_playing_ |= this->ack_set_is_playing_;

View File

@@ -27,29 +27,56 @@ class DFPlayer : public uart::UARTDevice, public Component {
public: public:
void loop() override; void loop() override;
void next() { this->send_cmd_(0x01); } void next() {
void previous() { this->send_cmd_(0x02); } this->ack_set_is_playing_ = true;
this->send_cmd_(0x01);
}
void previous() {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x02);
}
void play_file(uint16_t file) { void play_file(uint16_t file) {
this->ack_set_is_playing_ = true; this->ack_set_is_playing_ = true;
this->send_cmd_(0x03, file); this->send_cmd_(0x03, file);
} }
void play_file_loop(uint16_t file) { this->send_cmd_(0x08, file); } void play_file_loop(uint16_t file) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x08, file);
}
void play_folder(uint16_t folder, uint16_t file); void play_folder(uint16_t folder, uint16_t file);
void play_folder_loop(uint16_t folder) { this->send_cmd_(0x17, folder); } void play_folder_loop(uint16_t folder) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x17, folder);
}
void volume_up() { this->send_cmd_(0x04); } void volume_up() { this->send_cmd_(0x04); }
void volume_down() { this->send_cmd_(0x05); } void volume_down() { this->send_cmd_(0x05); }
void set_device(Device device) { this->send_cmd_(0x09, device); } void set_device(Device device) { this->send_cmd_(0x09, device); }
void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); } void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); }
void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); } void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); }
void sleep() { this->send_cmd_(0x0A); } void sleep() {
void reset() { this->send_cmd_(0x0C); } this->ack_reset_is_playing_ = true;
void start() { this->send_cmd_(0x0D); } this->send_cmd_(0x0A);
}
void reset() {
this->ack_reset_is_playing_ = true;
this->send_cmd_(0x0C);
}
void start() {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x0D);
}
void pause() { void pause() {
this->ack_reset_is_playing_ = true; this->ack_reset_is_playing_ = true;
this->send_cmd_(0x0E); this->send_cmd_(0x0E);
} }
void stop() { this->send_cmd_(0x16); } void stop() {
void random() { this->send_cmd_(0x18); } this->ack_reset_is_playing_ = true;
this->send_cmd_(0x16);
}
void random() {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x18);
}
bool is_playing() { return is_playing_; } bool is_playing() { return is_playing_; }
void dump_config() override; void dump_config() override;

View File

@@ -47,8 +47,10 @@ void DHT::update() {
if (error) { if (error) {
ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity); ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
this->temperature_sensor_->publish_state(temperature); if (this->temperature_sensor_ != nullptr)
this->humidity_sensor_->publish_state(humidity); this->temperature_sensor_->publish_state(temperature);
if (this->humidity_sensor_ != nullptr)
this->humidity_sensor_->publish_state(humidity);
this->status_clear_warning(); this->status_clear_warning();
} else { } else {
const char *str = ""; const char *str = "";
@@ -56,8 +58,10 @@ void DHT::update() {
str = " and consider manually specifying the DHT model using the model option"; str = " and consider manually specifying the DHT model using the model option";
} }
ESP_LOGW(TAG, "Invalid readings! Please check your wiring (pull-up resistor, pin number)%s.", str); ESP_LOGW(TAG, "Invalid readings! Please check your wiring (pull-up resistor, pin number)%s.", str);
this->temperature_sensor_->publish_state(NAN); if (this->temperature_sensor_ != nullptr)
this->humidity_sensor_->publish_state(NAN); this->temperature_sensor_->publish_state(NAN);
if (this->humidity_sensor_ != nullptr)
this->humidity_sensor_->publish_state(NAN);
this->status_set_warning(); this->status_set_warning();
} }
} }
@@ -67,80 +71,101 @@ void DHT::set_dht_model(DHTModel model) {
this->model_ = model; this->model_ = model;
this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT; this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;
} }
bool HOT DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) {
*humidity = NAN; *humidity = NAN;
*temperature = NAN; *temperature = NAN;
disable_interrupts(); int error_code = 0;
this->pin_->digital_write(false); int8_t i = 0;
this->pin_->pin_mode(OUTPUT);
this->pin_->digital_write(false);
if (this->model_ == DHT_MODEL_DHT11) {
delayMicroseconds(18000);
} else if (this->model_ == DHT_MODEL_SI7021) {
delayMicroseconds(500);
this->pin_->digital_write(true);
delayMicroseconds(40);
} else {
delayMicroseconds(800);
}
this->pin_->pin_mode(INPUT_PULLUP);
delayMicroseconds(40);
uint8_t data[5] = {0, 0, 0, 0, 0}; uint8_t data[5] = {0, 0, 0, 0, 0};
uint8_t bit = 7;
uint8_t byte = 0;
for (int8_t i = -1; i < 40; i++) { {
uint32_t start_time = micros(); InterruptLock lock;
// Wait for rising edge this->pin_->digital_write(false);
while (!this->pin_->digital_read()) { this->pin_->pin_mode(OUTPUT);
if (micros() - start_time > 90) { this->pin_->digital_write(false);
enable_interrupts();
if (report_errors) { if (this->model_ == DHT_MODEL_DHT11) {
if (i < 0) { delayMicroseconds(18000);
ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!"); } else if (this->model_ == DHT_MODEL_SI7021) {
} else { delayMicroseconds(500);
ESP_LOGW(TAG, "Rising edge for bit %d failed!", i); this->pin_->digital_write(true);
} delayMicroseconds(40);
} else {
delayMicroseconds(800);
}
this->pin_->pin_mode(INPUT_PULLUP);
delayMicroseconds(40);
uint8_t bit = 7;
uint8_t byte = 0;
for (i = -1; i < 40; i++) {
uint32_t start_time = micros();
// Wait for rising edge
while (!this->pin_->digital_read()) {
if (micros() - start_time > 90) {
if (i < 0)
error_code = 1;
else
error_code = 2;
break;
} }
return false;
} }
} if (error_code != 0)
break;
start_time = micros(); start_time = micros();
uint32_t end_time = start_time; uint32_t end_time = start_time;
// Wait for falling edge // Wait for falling edge
while (this->pin_->digital_read()) { while (this->pin_->digital_read()) {
if ((end_time = micros()) - start_time > 90) { if ((end_time = micros()) - start_time > 90) {
enable_interrupts(); if (i < 0)
if (report_errors) { error_code = 3;
if (i < 0) { else
ESP_LOGW(TAG, "Requesting data from DHT failed!"); error_code = 4;
} else { break;
ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
}
} }
return false;
} }
} if (error_code != 0)
break;
if (i < 0) if (i < 0)
continue; continue;
if (end_time - start_time >= 40) { if (end_time - start_time >= 40) {
data[byte] |= 1 << bit; data[byte] |= 1 << bit;
}
if (bit == 0) {
bit = 7;
byte++;
} else
bit--;
} }
if (bit == 0) {
bit = 7;
byte++;
} else
bit--;
} }
enable_interrupts(); if (!report_errors && error_code != 0)
return false;
switch (error_code) {
case 1:
ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!");
return false;
case 2:
ESP_LOGW(TAG, "Rising edge for bit %d failed!", i);
return false;
case 3:
ESP_LOGW(TAG, "Requesting data from DHT failed!");
return false;
case 4:
ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
return false;
case 0:
default:
break;
}
ESP_LOGVV(TAG, ESP_LOGVV(TAG,
"Data: Hum=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN "Data: Hum=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN

View File

@@ -37,7 +37,7 @@ CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(cv.Schema({ cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(cv.Schema({
cv.Optional(CONF_DURATION, default='5min'): cv.positive_time_period_seconds, cv.Optional(CONF_DURATION, default='5min'): cv.positive_time_period_seconds,
cv.Optional(CONF_INTERVAL, default='320ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_INTERVAL, default='320ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WINDOW, default='200ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_WINDOW, default='30ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ACTIVE, default=True): cv.boolean, cv.Optional(CONF_ACTIVE, default=True): cv.boolean,
}), validate_scan_parameters), }), validate_scan_parameters),

View File

@@ -203,10 +203,6 @@ void ESP32BLETracker::gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_res
} }
} }
std::string hexencode_string(const std::string &raw_data) {
return hexencode(reinterpret_cast<const uint8_t *>(raw_data.c_str()), raw_data.size());
}
ESPBTUUID::ESPBTUUID() : uuid_() {} ESPBTUUID::ESPBTUUID() : uuid_() {}
ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) {
ESPBTUUID ret; ESPBTUUID ret;
@@ -266,13 +262,13 @@ std::string ESPBTUUID::to_string() {
} }
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const std::string &data) { optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
if (data.size() != 25) if (!data.uuid.contains(0x4C, 0x00))
return {};
if (data[0] != 0x4C || data[1] != 0x00)
return {}; return {};
return ESPBLEiBeacon(reinterpret_cast<const uint8_t *>(data.data())); if (data.data.size() != 23)
return {};
return ESPBLEiBeacon(data.data.data());
} }
void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) { void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
@@ -304,8 +300,8 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
ESP_LOGVV(TAG, " RSSI: %d", this->rssi_); ESP_LOGVV(TAG, " RSSI: %d", this->rssi_);
ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str()); ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str());
if (this->tx_power_.has_value()) { for (auto &it : this->tx_powers_) {
ESP_LOGVV(TAG, " TX Power: %d", *this->tx_power_); ESP_LOGVV(TAG, " TX Power: %d", it);
} }
if (this->appearance_.has_value()) { if (this->appearance_.has_value()) {
ESP_LOGVV(TAG, " Appearance: %u", *this->appearance_); ESP_LOGVV(TAG, " Appearance: %u", *this->appearance_);
@@ -313,24 +309,25 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
if (this->ad_flag_.has_value()) { if (this->ad_flag_.has_value()) {
ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_); ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_);
} }
for (auto uuid : this->service_uuids_) { for (auto &uuid : this->service_uuids_) {
ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str()); ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str());
} }
ESP_LOGVV(TAG, " Manufacturer data: %s", hexencode_string(this->manufacturer_data_).c_str()); for (auto &data : this->manufacturer_datas_) {
ESP_LOGVV(TAG, " Service data: %s", hexencode_string(this->service_data_).c_str()); ESP_LOGVV(TAG, " Manufacturer data: %s", hexencode(data.data).c_str());
}
if (this->service_data_uuid_.has_value()) { for (auto &data : this->service_datas_) {
ESP_LOGVV(TAG, " Service Data UUID: %s", this->service_data_uuid_->to_string().c_str()); ESP_LOGVV(TAG, " Service data:");
ESP_LOGVV(TAG, " UUID: %s", data.uuid.to_string().c_str());
ESP_LOGVV(TAG, " Data: %s", hexencode(data.data).c_str());
} }
ESP_LOGVV(TAG, "Adv data: %s", ESP_LOGVV(TAG, "Adv data: %s", hexencode(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
hexencode_string(std::string(reinterpret_cast<const char *>(param.ble_adv), param.adv_data_len)).c_str());
#endif #endif
} }
void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) { void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
size_t offset = 0; size_t offset = 0;
const uint8_t *payload = param.ble_adv; const uint8_t *payload = param.ble_adv;
uint8_t len = param.adv_data_len; uint8_t len = param.adv_data_len + param.scan_rsp_len;
while (offset + 2 < len) { while (offset + 2 < len) {
const uint8_t field_length = payload[offset++]; // First byte is length of adv record const uint8_t field_length = payload[offset++]; // First byte is length of adv record
@@ -343,25 +340,52 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p
const uint8_t record_length = field_length - 1; const uint8_t record_length = field_length - 1;
offset += record_length; offset += record_length;
// See also Generic Access Profile Assigned Numbers:
// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/ See also ADVERTISING AND SCAN
// RESPONSE DATA FORMAT: https://www.bluetooth.com/specifications/bluetooth-core-specification/ (vol 3, part C, 11)
// See also Core Specification Supplement: https://www.bluetooth.com/specifications/bluetooth-core-specification/
// (called CSS here)
switch (record_type) { switch (record_type) {
case ESP_BLE_AD_TYPE_NAME_CMPL: { case ESP_BLE_AD_TYPE_NAME_CMPL: {
// CSS 1.2 LOCAL NAME
// "The Local Name data type shall be the same as, or a shortened version of, the local name assigned to the
// device." CSS 1: Optional in this context; shall not appear more than once in a block.
this->name_ = std::string(reinterpret_cast<const char *>(record), record_length); this->name_ = std::string(reinterpret_cast<const char *>(record), record_length);
break; break;
} }
case ESP_BLE_AD_TYPE_TX_PWR: { case ESP_BLE_AD_TYPE_TX_PWR: {
this->tx_power_ = *payload; // CSS 1.5 TX POWER LEVEL
// "The TX Power Level data type indicates the transmitted power level of the packet containing the data type."
// CSS 1: Optional in this context (may appear more than once in a block).
this->tx_powers_.push_back(*payload);
break; break;
} }
case ESP_BLE_AD_TYPE_APPEARANCE: { case ESP_BLE_AD_TYPE_APPEARANCE: {
// CSS 1.12 APPEARANCE
// "The Appearance data type defines the external appearance of the device."
// See also https://www.bluetooth.com/specifications/gatt/characteristics/
// CSS 1: Optional in this context; shall not appear more than once in a block and shall not appear in both
// the AD and SRD of the same extended advertising interval.
this->appearance_ = *reinterpret_cast<const uint16_t *>(record); this->appearance_ = *reinterpret_cast<const uint16_t *>(record);
break; break;
} }
case ESP_BLE_AD_TYPE_FLAG: { case ESP_BLE_AD_TYPE_FLAG: {
// CSS 1.3 FLAGS
// "The Flags data type contains one bit Boolean flags. The Flags data type shall be included when any of the
// Flag bits are non-zero and the advertising packet is connectable, otherwise the Flags data type may be
// omitted."
// CSS 1: Optional in this context; shall not appear more than once in a block.
this->ad_flag_ = *record; this->ad_flag_ = *record;
break; break;
} }
// CSS 1.1 SERVICE UUID
// The Service UUID data type is used to include a list of Service or Service Class UUIDs.
// There are six data types defined for the three sizes of Service UUIDs that may be returned:
// CSS 1: Optional in this context (may appear more than once in a block).
case ESP_BLE_AD_TYPE_16SRV_CMPL: case ESP_BLE_AD_TYPE_16SRV_CMPL:
case ESP_BLE_AD_TYPE_16SRV_PART: { case ESP_BLE_AD_TYPE_16SRV_PART: {
// • 16-bit Bluetooth Service UUIDs
for (uint8_t i = 0; i < record_length / 2; i++) { for (uint8_t i = 0; i < record_length / 2; i++) {
this->service_uuids_.push_back(ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record + 2 * i))); this->service_uuids_.push_back(ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record + 2 * i)));
} }
@@ -369,6 +393,7 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p
} }
case ESP_BLE_AD_TYPE_32SRV_CMPL: case ESP_BLE_AD_TYPE_32SRV_CMPL:
case ESP_BLE_AD_TYPE_32SRV_PART: { case ESP_BLE_AD_TYPE_32SRV_PART: {
// • 32-bit Bluetooth Service UUIDs
for (uint8_t i = 0; i < record_length / 4; i++) { for (uint8_t i = 0; i < record_length / 4; i++) {
this->service_uuids_.push_back(ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record + 4 * i))); this->service_uuids_.push_back(ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record + 4 * i)));
} }
@@ -376,41 +401,70 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p
} }
case ESP_BLE_AD_TYPE_128SRV_CMPL: case ESP_BLE_AD_TYPE_128SRV_CMPL:
case ESP_BLE_AD_TYPE_128SRV_PART: { case ESP_BLE_AD_TYPE_128SRV_PART: {
// • Global 128-bit Service UUIDs
this->service_uuids_.push_back(ESPBTUUID::from_raw(record)); this->service_uuids_.push_back(ESPBTUUID::from_raw(record));
break; break;
} }
case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: { case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: {
this->manufacturer_data_ = std::string(reinterpret_cast<const char *>(record), record_length); // CSS 1.4 MANUFACTURER SPECIFIC DATA
// "The Manufacturer Specific data type is used for manufacturer specific data. The first two data octets shall
// contain a company identifier from Assigned Numbers. The interpretation of any other octets within the data
// shall be defined by the manufacturer specified by the company identifier."
// CSS 1: Optional in this context (may appear more than once in a block).
if (record_length < 2) {
ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE");
break;
}
ServiceData data{};
data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record));
data.data.assign(record + 2UL, record + record_length);
this->manufacturer_datas_.push_back(data);
break; break;
} }
// CSS 1.11 SERVICE DATA
// "The Service Data data type consists of a service UUID with the data associated with that service."
// CSS 1: Optional in this context (may appear more than once in a block).
case ESP_BLE_AD_TYPE_SERVICE_DATA: { case ESP_BLE_AD_TYPE_SERVICE_DATA: {
// «Service Data - 16 bit UUID»
// Size: 2 or more octets
// The first 2 octets contain the 16 bit Service UUID fol- lowed by additional service data
if (record_length < 2) { if (record_length < 2) {
ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
break; break;
} }
this->service_data_uuid_ = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record)); ServiceData data{};
if (record_length > 2) data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record));
this->service_data_ = std::string(reinterpret_cast<const char *>(record + 2), record_length - 2UL); data.data.assign(record + 2UL, record + record_length);
this->service_datas_.push_back(data);
break; break;
} }
case ESP_BLE_AD_TYPE_32SERVICE_DATA: { case ESP_BLE_AD_TYPE_32SERVICE_DATA: {
// «Service Data - 32 bit UUID»
// Size: 4 or more octets
// The first 4 octets contain the 32 bit Service UUID fol- lowed by additional service data
if (record_length < 4) { if (record_length < 4) {
ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
break; break;
} }
this->service_data_uuid_ = ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record)); ServiceData data{};
if (record_length > 4) data.uuid = ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record));
this->service_data_ = std::string(reinterpret_cast<const char *>(record + 4), record_length - 4UL); data.data.assign(record + 4UL, record + record_length);
this->service_datas_.push_back(data);
break; break;
} }
case ESP_BLE_AD_TYPE_128SERVICE_DATA: { case ESP_BLE_AD_TYPE_128SERVICE_DATA: {
// «Service Data - 128 bit UUID»
// Size: 16 or more octets
// The first 16 octets contain the 128 bit Service UUID followed by additional service data
if (record_length < 16) { if (record_length < 16) {
ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
break; break;
} }
this->service_data_uuid_ = ESPBTUUID::from_raw(record); ServiceData data{};
if (record_length > 16) data.uuid = ESPBTUUID::from_raw(record);
this->service_data_ = std::string(reinterpret_cast<const char *>(record + 16), record_length - 16UL); data.data.assign(record + 16UL, record + record_length);
this->service_datas_.push_back(data);
break; break;
} }
default: { default: {
@@ -427,22 +481,12 @@ std::string ESPBTDevice::address_str() const {
return mac; return mac;
} }
uint64_t ESPBTDevice::address_uint64() const { return ble_addr_to_uint64(this->address_); } uint64_t ESPBTDevice::address_uint64() const { return ble_addr_to_uint64(this->address_); }
esp_ble_addr_type_t ESPBTDevice::get_address_type() const { return this->address_type_; }
int ESPBTDevice::get_rssi() const { return this->rssi_; }
const std::string &ESPBTDevice::get_name() const { return this->name_; }
const optional<int8_t> &ESPBTDevice::get_tx_power() const { return this->tx_power_; }
const optional<uint16_t> &ESPBTDevice::get_appearance() const { return this->appearance_; }
const optional<uint8_t> &ESPBTDevice::get_ad_flag() const { return this->ad_flag_; }
const std::vector<ESPBTUUID> &ESPBTDevice::get_service_uuids() const { return this->service_uuids_; }
const std::string &ESPBTDevice::get_manufacturer_data() const { return this->manufacturer_data_; }
const std::string &ESPBTDevice::get_service_data() const { return this->service_data_; }
const optional<ESPBTUUID> &ESPBTDevice::get_service_data_uuid() const { return this->service_data_uuid_; }
void ESP32BLETracker::dump_config() { void ESP32BLETracker::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Tracker:"); ESP_LOGCONFIG(TAG, "BLE Tracker:");
ESP_LOGCONFIG(TAG, " Scan Duration: %u s", this->scan_duration_); ESP_LOGCONFIG(TAG, " Scan Duration: %u s", this->scan_duration_);
ESP_LOGCONFIG(TAG, " Scan Interval: %u ms", this->scan_interval_); ESP_LOGCONFIG(TAG, " Scan Interval: %.1f ms", this->scan_interval_ * 0.625f);
ESP_LOGCONFIG(TAG, " Scan Window: %u ms", this->scan_window_); ESP_LOGCONFIG(TAG, " Scan Window: %.1f ms", this->scan_window_ * 0.625f);
ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE"); ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE");
} }
void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
@@ -477,8 +521,8 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
ESP_LOGD(TAG, " Address Type: %s", address_type_s); ESP_LOGD(TAG, " Address Type: %s", address_type_s);
if (!device.get_name().empty()) if (!device.get_name().empty())
ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str()); ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str());
if (device.get_tx_power().has_value()) { for (auto &tx_power : device.get_tx_powers()) {
ESP_LOGD(TAG, " TX Power: %d", *device.get_tx_power()); ESP_LOGD(TAG, " TX Power: %d", tx_power);
} }
} }

View File

@@ -31,11 +31,18 @@ class ESPBTUUID {
esp_bt_uuid_t uuid_; esp_bt_uuid_t uuid_;
}; };
using adv_data_t = std::vector<uint8_t>;
struct ServiceData {
ESPBTUUID uuid;
adv_data_t data;
};
class ESPBLEiBeacon { class ESPBLEiBeacon {
public: public:
ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); }
ESPBLEiBeacon(const uint8_t *data); ESPBLEiBeacon(const uint8_t *data);
static optional<ESPBLEiBeacon> from_manufacturer_data(const std::string &data); static optional<ESPBLEiBeacon> from_manufacturer_data(const ServiceData &data);
uint16_t get_major() { return reverse_bits_16(this->beacon_data_.major); } uint16_t get_major() { return reverse_bits_16(this->beacon_data_.major); }
uint16_t get_minor() { return reverse_bits_16(this->beacon_data_.minor); } uint16_t get_minor() { return reverse_bits_16(this->beacon_data_.minor); }
@@ -44,7 +51,6 @@ class ESPBLEiBeacon {
protected: protected:
struct { struct {
uint16_t manufacturer_id;
uint8_t sub_type; uint8_t sub_type;
uint8_t proximity_uuid[16]; uint8_t proximity_uuid[16];
uint16_t major; uint16_t major;
@@ -61,18 +67,33 @@ class ESPBTDevice {
uint64_t address_uint64() const; uint64_t address_uint64() const;
esp_ble_addr_type_t get_address_type() const; esp_ble_addr_type_t get_address_type() const { return this->address_type_; }
int get_rssi() const; int get_rssi() const { return rssi_; }
const std::string &get_name() const; const std::string &get_name() const { return this->name_; }
const optional<int8_t> &get_tx_power() const;
const optional<uint16_t> &get_appearance() const; ESPDEPRECATED("Use get_tx_powers() instead")
const optional<uint8_t> &get_ad_flag() const; optional<int8_t> get_tx_power() const {
const std::vector<ESPBTUUID> &get_service_uuids() const; if (this->tx_powers_.empty())
const std::string &get_manufacturer_data() const; return {};
const std::string &get_service_data() const; return this->tx_powers_[0];
const optional<ESPBTUUID> &get_service_data_uuid() const; }
const optional<ESPBLEiBeacon> get_ibeacon() const { const std::vector<int8_t> &get_tx_powers() const { return tx_powers_; }
return ESPBLEiBeacon::from_manufacturer_data(this->manufacturer_data_);
const optional<uint16_t> &get_appearance() const { return appearance_; }
const optional<uint8_t> &get_ad_flag() const { return ad_flag_; }
const std::vector<ESPBTUUID> &get_service_uuids() const { return service_uuids_; }
const std::vector<ServiceData> &get_manufacturer_datas() const { return manufacturer_datas_; }
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
optional<ESPBLEiBeacon> get_ibeacon() const {
for (auto &it : this->manufacturer_datas_) {
auto res = ESPBLEiBeacon::from_manufacturer_data(it);
if (res.has_value())
return *res;
}
return {};
} }
protected: protected:
@@ -84,13 +105,12 @@ class ESPBTDevice {
esp_ble_addr_type_t address_type_{BLE_ADDR_TYPE_PUBLIC}; esp_ble_addr_type_t address_type_{BLE_ADDR_TYPE_PUBLIC};
int rssi_{0}; int rssi_{0};
std::string name_{}; std::string name_{};
optional<int8_t> tx_power_{}; std::vector<int8_t> tx_powers_{};
optional<uint16_t> appearance_{}; optional<uint16_t> appearance_{};
optional<uint8_t> ad_flag_{}; optional<uint8_t> ad_flag_{};
std::vector<ESPBTUUID> service_uuids_; std::vector<ESPBTUUID> service_uuids_;
std::string manufacturer_data_{}; std::vector<ServiceData> manufacturer_datas_{};
std::string service_data_{}; std::vector<ServiceData> service_datas_{};
optional<ESPBTUUID> service_data_uuid_{};
}; };
class ESP32BLETracker; class ESP32BLETracker;

View File

@@ -85,6 +85,8 @@ void ESP32Camera::dump_config() {
case FRAMESIZE_UXGA: case FRAMESIZE_UXGA:
ESP_LOGCONFIG(TAG, " Resolution: 1600x1200 (UXGA)"); ESP_LOGCONFIG(TAG, " Resolution: 1600x1200 (UXGA)");
break; break;
default:
break;
} }
if (this->is_failed()) { if (this->is_failed()) {

View File

@@ -42,23 +42,24 @@ bool HX711Sensor::read_sensor_(uint32_t *result) {
this->status_clear_warning(); this->status_clear_warning();
uint32_t data = 0; uint32_t data = 0;
disable_interrupts(); {
for (uint8_t i = 0; i < 24; i++) { InterruptLock lock;
this->sck_pin_->digital_write(true); for (uint8_t i = 0; i < 24; i++) {
delayMicroseconds(1); this->sck_pin_->digital_write(true);
data |= uint32_t(this->dout_pin_->digital_read()) << (23 - i); delayMicroseconds(1);
this->sck_pin_->digital_write(false); data |= uint32_t(this->dout_pin_->digital_read()) << (23 - i);
delayMicroseconds(1); this->sck_pin_->digital_write(false);
} delayMicroseconds(1);
}
// Cycle clock pin for gain setting // Cycle clock pin for gain setting
for (uint8_t i = 0; i < this->gain_; i++) { for (uint8_t i = 0; i < this->gain_; i++) {
this->sck_pin_->digital_write(true); this->sck_pin_->digital_write(true);
delayMicroseconds(1); delayMicroseconds(1);
this->sck_pin_->digital_write(false); this->sck_pin_->digital_write(false);
delayMicroseconds(1); delayMicroseconds(1);
}
} }
enable_interrupts();
if (data & 0x800000ULL) { if (data & 0x800000ULL) {
data |= 0xFF000000ULL; data |= 0xFF000000ULL;

View File

@@ -31,6 +31,7 @@ class Logger : public Component {
/// Manually set the baud rate for serial, set to 0 to disable. /// Manually set the baud rate for serial, set to 0 to disable.
void set_baud_rate(uint32_t baud_rate); void set_baud_rate(uint32_t baud_rate);
uint32_t get_baud_rate() const { return baud_rate_; } uint32_t get_baud_rate() const { return baud_rate_; }
HardwareSerial *get_hw_serial() const { return hw_serial_; }
/// Get the UART used by the logger. /// Get the UART used by the logger.
UARTSelection get_uart() const; UARTSelection get_uart() const;

View File

@@ -155,7 +155,7 @@ def to_code(config):
yield cg.register_component(var, config) yield cg.register_component(var, config)
# https://github.com/OttoWinter/async-mqtt-client/blob/master/library.json # https://github.com/OttoWinter/async-mqtt-client/blob/master/library.json
cg.add_library('AsyncMqttClient-esphome', '0.8.3') cg.add_library('AsyncMqttClient-esphome', '0.8.4')
cg.add_define('USE_MQTT') cg.add_define('USE_MQTT')
cg.add_global(mqtt_ns.using) cg.add_global(mqtt_ns.using)

View File

@@ -74,7 +74,11 @@ def validate_method_pin(value):
method_pins['BIT_BANG'] = list(range(0, 16)) method_pins['BIT_BANG'] = list(range(0, 16))
elif CORE.is_esp32: elif CORE.is_esp32:
method_pins['BIT_BANG'] = list(range(0, 32)) method_pins['BIT_BANG'] = list(range(0, 32))
pins_ = method_pins[method] pins_ = method_pins.get(method)
if pins_ is None:
# all pins allowed for this method
return value
for opt in (CONF_PIN, CONF_CLOCK_PIN, CONF_DATA_PIN): for opt in (CONF_PIN, CONF_CLOCK_PIN, CONF_DATA_PIN):
if opt in value and value[opt] not in pins_: if opt in value and value[opt] not in pins_:
raise cv.Invalid("Method {} only supports pin(s) {}".format( raise cv.Invalid("Method {} only supports pin(s) {}".format(

View File

@@ -8,7 +8,7 @@ static const char *TAG = "pzem004t";
void PZEM004T::loop() { void PZEM004T::loop() {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_read_ > 500 && this->available()) { if (now - this->last_read_ > 500 && this->available() < 7) {
while (this->available()) while (this->available())
this->read(); this->read();
this->last_read_ = now; this->last_read_ = now;
@@ -78,7 +78,7 @@ void PZEM004T::loop() {
this->last_read_ = now; this->last_read_ = now;
} }
} }
void PZEM004T::update() { this->write_state_(SET_ADDRESS); } void PZEM004T::update() { this->write_state_(READ_VOLTAGE); }
void PZEM004T::write_state_(PZEM004T::PZEM004TReadState state) { void PZEM004T::write_state_(PZEM004T::PZEM004TReadState state) {
if (state == DONE) { if (state == DONE) {
this->read_state_ = state; this->read_state_ = state;

View File

@@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome.components import sensor, modbus from esphome.components import sensor, modbus
from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \
CONF_FREQUENCY, UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, \ CONF_FREQUENCY, UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, \
ICON_POWER, CONF_POWER_FACTOR, ICON_CURRENT_AC ICON_POWER, CONF_POWER_FACTOR, ICON_CURRENT_AC, UNIT_HERTZ
AUTO_LOAD = ['modbus'] AUTO_LOAD = ['modbus']
@@ -15,7 +15,7 @@ CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 3), cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 3),
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_POWER, 1), cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_POWER, 1),
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_EMPTY, ICON_CURRENT_AC, 1), cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2), cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2),
}).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01)) }).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01))

View File

@@ -67,22 +67,22 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
uint32_t on_time, off_time; uint32_t on_time, off_time;
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
for (uint32_t i = 0; i < send_times; i++) { for (uint32_t i = 0; i < send_times; i++) {
disable_interrupts(); {
for (int32_t item : this->temp_.get_data()) { InterruptLock lock;
if (item > 0) { for (int32_t item : this->temp_.get_data()) {
const auto length = uint32_t(item); if (item > 0) {
this->mark_(on_time, off_time, length); const auto length = uint32_t(item);
} else { this->mark_(on_time, off_time, length);
const auto length = uint32_t(-item); } else {
this->space_(length); const auto length = uint32_t(-item);
this->space_(length);
}
App.feed_wdt();
} }
App.feed_wdt();
} }
enable_interrupts();
if (i + 1 < send_times) { if (i + 1 < send_times) {
delay(send_wait / 1000UL); delay_microseconds_accurate(send_wait);
delayMicroseconds(send_wait % 1000UL);
} }
} }
} }

View File

@@ -32,7 +32,13 @@ static const uint16_t STATE_DECREMENT_COUNTER_1 = 0x1000;
// Bit 2&3 (0x0C) encodes state S0-S3 // Bit 2&3 (0x0C) encodes state S0-S3
// Bit 4 (0x10) encodes clockwise/counter-clockwise rotation // Bit 4 (0x10) encodes clockwise/counter-clockwise rotation
static const uint16_t STATE_LOOKUP_TABLE[32] = { // Only apply if DRAM_ATTR exists on this platform (exists on ESP32, not on ESP8266)
#ifndef DRAM_ATTR
#define DRAM_ATTR
#endif
// array needs to be placed in .dram1 for ESP32
// otherwise it will automatically go into flash, and cause cache disabled issues
static const uint16_t DRAM_ATTR STATE_LOOKUP_TABLE[32] = {
// act state S0 in CCW direction // act state S0 in CCW direction
STATE_CCW | STATE_S0, // 0x00: stay here STATE_CCW | STATE_S0, // 0x00: stay here
STATE_CW | STATE_S1 | STATE_INCREMENT_COUNTER_1, // 0x01: goto CW+S1 and increment counter (dir change) STATE_CW | STATE_S1 | STATE_INCREMENT_COUNTER_1, // 0x01: goto CW+S1 and increment counter (dir change)

View File

@@ -52,6 +52,14 @@ double Sun::azimuth() {
return NAN; return NAN;
return this->azimuth_(time); return this->azimuth_(time);
} }
// like clamp, but with doubles
double clampd(double val, double min, double max) {
if (val < min)
return min;
if (val > max)
return max;
return val;
}
double Sun::sun_declination_(double sun_time) { double Sun::sun_declination_(double sun_time) {
double n = sun_time - 1.0; double n = sun_time - 1.0;
// maximum declination // maximum declination
@@ -67,7 +75,7 @@ double Sun::sun_declination_(double sun_time) {
const double c = TAU / 365.24; const double c = TAU / 365.24;
double v = cos(c * days_since_december_solstice + 2 * eccentricity * sin(c * days_since_perihelion)); double v = cos(c * days_since_december_solstice + 2 * eccentricity * sin(c * days_since_perihelion));
// Make sure value is in range (double error may lead to results slightly larger than 1) // Make sure value is in range (double error may lead to results slightly larger than 1)
double x = clamp(tot * v, 0, 1); double x = clampd(tot * v, -1.0, 1.0);
return asin(x); return asin(x);
} }
double Sun::elevation_ratio_(double sun_time) { double Sun::elevation_ratio_(double sun_time) {
@@ -75,7 +83,7 @@ double Sun::elevation_ratio_(double sun_time) {
double hangle = this->hour_angle_(sun_time); double hangle = this->hour_angle_(sun_time);
double a = sin(this->latitude_rad_()) * sin(decl); double a = sin(this->latitude_rad_()) * sin(decl);
double b = cos(this->latitude_rad_()) * cos(decl) * cos(hangle); double b = cos(this->latitude_rad_()) * cos(decl) * cos(hangle);
double val = clamp(a + b, -1.0, 1.0); double val = clampd(a + b, -1.0, 1.0);
return val; return val;
} }
double Sun::latitude_rad_() { return this->latitude_ * TO_RADIANS; } double Sun::latitude_rad_() { return this->latitude_ * TO_RADIANS; }
@@ -92,7 +100,7 @@ double Sun::azimuth_rad_(double sun_time) {
double zen = this->zenith_rad_(sun_time); double zen = this->zenith_rad_(sun_time);
double nom = cos(zen) * sin(this->latitude_rad_()) - sin(decl); double nom = cos(zen) * sin(this->latitude_rad_()) - sin(decl);
double denom = sin(zen) * cos(this->latitude_rad_()); double denom = sin(zen) * cos(this->latitude_rad_());
double v = clamp(nom / denom, -1.0, 1.0); double v = clampd(nom / denom, -1.0, 1.0);
double az = PI - acos(v); double az = PI - acos(v);
if (hangle > 0) if (hangle > 0)
az = -az; az = -az;

View File

@@ -16,11 +16,14 @@ RESTORE_MODES = {
'RESTORE_AND_CALL': TemplateCoverRestoreMode.COVER_RESTORE_AND_CALL, 'RESTORE_AND_CALL': TemplateCoverRestoreMode.COVER_RESTORE_AND_CALL,
} }
CONF_HAS_POSITION = 'has_position'
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(TemplateCover), cv.GenerateID(): cv.declare_id(TemplateCover),
cv.Optional(CONF_LAMBDA): cv.returning_lambda, cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
cv.Optional(CONF_HAS_POSITION, default=False): cv.boolean,
cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True),
@@ -56,6 +59,7 @@ def to_code(config):
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
cg.add(var.set_has_position(config[CONF_HAS_POSITION]))
@automation.register_action('cover.template.publish', cover.CoverPublishAction, cv.Schema({ @automation.register_action('cover.template.publish', cover.CoverPublishAction, cv.Schema({

View File

@@ -8,7 +8,6 @@ namespace tuya {
static const char *TAG = "tuya"; static const char *TAG = "tuya";
void Tuya::setup() { void Tuya::setup() {
this->send_empty_command_(TuyaCommandType::MCU_CONF);
this->set_interval("heartbeat", 1000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); }); this->set_interval("heartbeat", 1000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); });
} }
@@ -22,8 +21,12 @@ void Tuya::loop() {
void Tuya::dump_config() { void Tuya::dump_config() {
ESP_LOGCONFIG(TAG, "Tuya:"); ESP_LOGCONFIG(TAG, "Tuya:");
if ((gpio_status_ != -1) || (gpio_reset_ != -1)) if (this->init_state_ != TuyaInitState::INIT_DONE) {
ESP_LOGCONFIG(TAG, " GPIO MCU configuration not supported!"); ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u", // NOLINT
this->init_state_);
ESP_LOGCONFIG(TAG, " If no further output is received, confirm that this is a supported Tuya device.");
return;
}
for (auto &info : this->datapoints_) { for (auto &info : this->datapoints_) {
if (info.type == TuyaDatapointType::BOOLEAN) if (info.type == TuyaDatapointType::BOOLEAN)
ESP_LOGCONFIG(TAG, " Datapoint %d: switch (value: %s)", info.id, ONOFF(info.value_bool)); ESP_LOGCONFIG(TAG, " Datapoint %d: switch (value: %s)", info.id, ONOFF(info.value_bool));
@@ -36,9 +39,11 @@ void Tuya::dump_config() {
else else
ESP_LOGCONFIG(TAG, " Datapoint %d: unknown", info.id); ESP_LOGCONFIG(TAG, " Datapoint %d: unknown", info.id);
} }
if (this->datapoints_.empty()) { if ((this->gpio_status_ != -1) || (this->gpio_reset_ != -1)) {
ESP_LOGCONFIG(TAG, " Received no datapoints! Please make sure this is a supported Tuya device."); ESP_LOGCONFIG(TAG, " GPIO Configuration: status: pin %d, reset: pin %d (not supported)", this->gpio_status_,
this->gpio_reset_);
} }
ESP_LOGCONFIG(TAG, " Product: '%s'", this->product_.c_str());
this->check_uart_settings(9600); this->check_uart_settings(9600);
} }
@@ -89,8 +94,8 @@ bool Tuya::validate_message_() {
// valid message // valid message
const uint8_t *message_data = data + 6; const uint8_t *message_data = data + 6;
ESP_LOGV(TAG, "Received Tuya: CMD=0x%02X VERSION=%u DATA=[%s]", command, version, ESP_LOGV(TAG, "Received Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command, version, // NOLINT
hexencode(message_data, length).c_str()); hexencode(message_data, length).c_str(), this->init_state_);
this->handle_command_(command, version, message_data, length); this->handle_command_(command, version, message_data, length);
// return false to reset rx buffer // return false to reset rx buffer
@@ -105,41 +110,58 @@ void Tuya::handle_char_(uint8_t c) {
} }
void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len) { void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len) {
uint8_t c;
switch ((TuyaCommandType) command) { switch ((TuyaCommandType) command) {
case TuyaCommandType::HEARTBEAT: case TuyaCommandType::HEARTBEAT:
ESP_LOGV(TAG, "MCU Heartbeat (0x%02X)", buffer[0]); ESP_LOGV(TAG, "MCU Heartbeat (0x%02X)", buffer[0]);
if (buffer[0] == 0) { if (buffer[0] == 0) {
ESP_LOGI(TAG, "MCU restarted"); ESP_LOGI(TAG, "MCU restarted");
this->send_empty_command_(TuyaCommandType::QUERY_STATE); this->init_state_ = TuyaInitState::INIT_HEARTBEAT;
}
if (this->init_state_ == TuyaInitState::INIT_HEARTBEAT) {
this->init_state_ = TuyaInitState::INIT_PRODUCT;
this->send_empty_command_(TuyaCommandType::PRODUCT_QUERY);
} }
break; break;
case TuyaCommandType::QUERY_PRODUCT: { case TuyaCommandType::PRODUCT_QUERY: {
// check it is a valid string // check it is a valid string made up of printable characters
bool valid = false; bool valid = true;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (buffer[i] == 0x00) { if (!std::isprint(buffer[i])) {
valid = true; valid = false;
break; break;
} }
} }
if (valid) { if (valid) {
ESP_LOGD(TAG, "Tuya Product Code: %s", reinterpret_cast<const char *>(buffer)); this->product_ = std::string(reinterpret_cast<const char *>(buffer), len);
} else {
this->product_ = R"({"p":"INVALID"})";
}
if (this->init_state_ == TuyaInitState::INIT_PRODUCT) {
this->init_state_ = TuyaInitState::INIT_CONF;
this->send_empty_command_(TuyaCommandType::CONF_QUERY);
} }
break; break;
} }
case TuyaCommandType::MCU_CONF: case TuyaCommandType::CONF_QUERY: {
if (len >= 2) { if (len >= 2) {
gpio_status_ = buffer[0]; gpio_status_ = buffer[0];
gpio_reset_ = buffer[1]; gpio_reset_ = buffer[1];
} }
// set wifi state LED to off or on depending on the MCU firmware if (this->init_state_ == TuyaInitState::INIT_CONF) {
// but it shouldn't be blinking // If we were following the spec to the letter we would send
c = 0x3; // state updates until connected to both WiFi and API/MQTT.
this->send_command_(TuyaCommandType::WIFI_STATE, &c, 1); // Instead we just claim to be connected immediately and move on.
this->send_empty_command_(TuyaCommandType::QUERY_STATE); uint8_t c[] = {0x04};
this->init_state_ = TuyaInitState::INIT_WIFI;
this->send_command_(TuyaCommandType::WIFI_STATE, c, 1);
}
break; break;
}
case TuyaCommandType::WIFI_STATE: case TuyaCommandType::WIFI_STATE:
if (this->init_state_ == TuyaInitState::INIT_WIFI) {
this->init_state_ = TuyaInitState::INIT_DATAPOINT;
this->send_empty_command_(TuyaCommandType::DATAPOINT_QUERY);
}
break; break;
case TuyaCommandType::WIFI_RESET: case TuyaCommandType::WIFI_RESET:
ESP_LOGE(TAG, "TUYA_CMD_WIFI_RESET is not handled"); ESP_LOGE(TAG, "TUYA_CMD_WIFI_RESET is not handled");
@@ -147,14 +169,22 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff
case TuyaCommandType::WIFI_SELECT: case TuyaCommandType::WIFI_SELECT:
ESP_LOGE(TAG, "TUYA_CMD_WIFI_SELECT is not handled"); ESP_LOGE(TAG, "TUYA_CMD_WIFI_SELECT is not handled");
break; break;
case TuyaCommandType::SET_DATAPOINT: case TuyaCommandType::DATAPOINT_DELIVER:
break; break;
case TuyaCommandType::STATE: { case TuyaCommandType::DATAPOINT_REPORT:
if (this->init_state_ == TuyaInitState::INIT_DATAPOINT) {
this->init_state_ = TuyaInitState::INIT_DONE;
this->set_timeout("datapoint_dump", 1000, [this] { this->dump_config(); });
}
this->handle_datapoint_(buffer, len); this->handle_datapoint_(buffer, len);
break; break;
} case TuyaCommandType::DATAPOINT_QUERY:
case TuyaCommandType::QUERY_STATE:
break; break;
case TuyaCommandType::WIFI_TEST: {
uint8_t c[] = {0x00, 0x00};
this->send_command_(TuyaCommandType::WIFI_TEST, c, 2);
break;
}
default: default:
ESP_LOGE(TAG, "invalid command (%02x) received", command); ESP_LOGE(TAG, "invalid command (%02x) received", command);
} }
@@ -214,8 +244,6 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) {
} }
if (!found) { if (!found) {
this->datapoints_.push_back(datapoint); this->datapoints_.push_back(datapoint);
// New datapoint found, reprint dump_config after a delay.
this->set_timeout("datapoint_dump", 100, [this] { this->dump_config(); });
} }
// Run through listeners // Run through listeners
@@ -227,9 +255,12 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) {
void Tuya::send_command_(TuyaCommandType command, const uint8_t *buffer, uint16_t len) { void Tuya::send_command_(TuyaCommandType command, const uint8_t *buffer, uint16_t len) {
uint8_t len_hi = len >> 8; uint8_t len_hi = len >> 8;
uint8_t len_lo = len >> 0; uint8_t len_lo = len >> 0;
this->write_array({0x55, 0xAA, uint8_t version = 0;
0x00, // version
(uint8_t) command, len_hi, len_lo}); ESP_LOGV(TAG, "Sending Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command, version, // NOLINT
hexencode(buffer, len).c_str(), this->init_state_);
this->write_array({0x55, 0xAA, version, (uint8_t) command, len_hi, len_lo});
if (len != 0) if (len != 0)
this->write_array(buffer, len); this->write_array(buffer, len);
@@ -278,7 +309,7 @@ void Tuya::set_datapoint_value(TuyaDatapoint datapoint) {
buffer.push_back(data.size() >> 8); buffer.push_back(data.size() >> 8);
buffer.push_back(data.size() >> 0); buffer.push_back(data.size() >> 0);
buffer.insert(buffer.end(), data.begin(), data.end()); buffer.insert(buffer.end(), data.begin(), data.end());
this->send_command_(TuyaCommandType::SET_DATAPOINT, buffer.data(), buffer.size()); this->send_command_(TuyaCommandType::DATAPOINT_DELIVER, buffer.data(), buffer.size());
} }
void Tuya::register_listener(uint8_t datapoint_id, const std::function<void(TuyaDatapoint)> &func) { void Tuya::register_listener(uint8_t datapoint_id, const std::function<void(TuyaDatapoint)> &func) {

View File

@@ -34,19 +34,29 @@ struct TuyaDatapointListener {
enum class TuyaCommandType : uint8_t { enum class TuyaCommandType : uint8_t {
HEARTBEAT = 0x00, HEARTBEAT = 0x00,
QUERY_PRODUCT = 0x01, PRODUCT_QUERY = 0x01,
MCU_CONF = 0x02, CONF_QUERY = 0x02,
WIFI_STATE = 0x03, WIFI_STATE = 0x03,
WIFI_RESET = 0x04, WIFI_RESET = 0x04,
WIFI_SELECT = 0x05, WIFI_SELECT = 0x05,
SET_DATAPOINT = 0x06, DATAPOINT_DELIVER = 0x06,
STATE = 0x07, DATAPOINT_REPORT = 0x07,
QUERY_STATE = 0x08, DATAPOINT_QUERY = 0x08,
WIFI_TEST = 0x0E,
};
enum class TuyaInitState : uint8_t {
INIT_HEARTBEAT = 0x00,
INIT_PRODUCT,
INIT_CONF,
INIT_WIFI,
INIT_DATAPOINT,
INIT_DONE,
}; };
class Tuya : public Component, public uart::UARTDevice { class Tuya : public Component, public uart::UARTDevice {
public: public:
float get_setup_priority() const override { return setup_priority::HARDWARE; } float get_setup_priority() const override { return setup_priority::LATE; }
void setup() override; void setup() override;
void loop() override; void loop() override;
void dump_config() override; void dump_config() override;
@@ -62,8 +72,10 @@ class Tuya : public Component, public uart::UARTDevice {
void send_command_(TuyaCommandType command, const uint8_t *buffer, uint16_t len); void send_command_(TuyaCommandType command, const uint8_t *buffer, uint16_t len);
void send_empty_command_(TuyaCommandType command) { this->send_command_(command, nullptr, 0); } void send_empty_command_(TuyaCommandType command) { this->send_command_(command, nullptr, 0); }
TuyaInitState init_state_ = TuyaInitState::INIT_HEARTBEAT;
int gpio_status_ = -1; int gpio_status_ = -1;
int gpio_reset_ = -1; int gpio_reset_ = -1;
std::string product_ = "";
std::vector<TuyaDatapointListener> listeners_; std::vector<TuyaDatapointListener> listeners_;
std::vector<TuyaDatapoint> datapoints_; std::vector<TuyaDatapoint> datapoints_;
std::vector<uint8_t> rx_message_; std::vector<uint8_t> rx_message_;

View File

@@ -4,7 +4,7 @@ from esphome import pins
from esphome.components import sensor from esphome.components import sensor
from esphome.const import CONF_ID, CONF_WIND_SPEED, CONF_PIN, \ from esphome.const import CONF_ID, CONF_WIND_SPEED, CONF_PIN, \
CONF_WIND_DIRECTION_DEGREES, UNIT_KILOMETER_PER_HOUR, \ CONF_WIND_DIRECTION_DEGREES, UNIT_KILOMETER_PER_HOUR, \
UNIT_EMPTY, ICON_WEATHER_WINDY, ICON_SIGN_DIRECTION ICON_WEATHER_WINDY, ICON_SIGN_DIRECTION, UNIT_DEGREES
tx20_ns = cg.esphome_ns.namespace('tx20') tx20_ns = cg.esphome_ns.namespace('tx20')
Tx20Component = tx20_ns.class_('Tx20Component', cg.Component) Tx20Component = tx20_ns.class_('Tx20Component', cg.Component)
@@ -14,7 +14,7 @@ CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_WIND_SPEED): cv.Optional(CONF_WIND_SPEED):
sensor.sensor_schema(UNIT_KILOMETER_PER_HOUR, ICON_WEATHER_WINDY, 1), sensor.sensor_schema(UNIT_KILOMETER_PER_HOUR, ICON_WEATHER_WINDY, 1),
cv.Optional(CONF_WIND_DIRECTION_DEGREES): cv.Optional(CONF_WIND_DIRECTION_DEGREES):
sensor.sensor_schema(UNIT_EMPTY, ICON_SIGN_DIRECTION, 1), sensor.sensor_schema(UNIT_DEGREES, ICON_SIGN_DIRECTION, 1),
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema,
pins.validate_has_interrupt), pins.validate_has_interrupt),
}).extend(cv.COMPONENT_SCHEMA) }).extend(cv.COMPONENT_SCHEMA)

View File

@@ -46,12 +46,7 @@ void UARTComponent::dump_config() {
} }
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
#ifdef USE_LOGGER this->check_logger_conflict_();
if (this->hw_serial_ == &Serial && logger::global_logger->get_baud_rate() != 0) {
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
"disable logging over the serial port by setting logger->baud_rate to 0.");
}
#endif
} }
void UARTComponent::write_byte(uint8_t data) { void UARTComponent::write_byte(uint8_t data) {
@@ -156,13 +151,7 @@ void UARTComponent::dump_config() {
} else { } else {
ESP_LOGCONFIG(TAG, " Using software serial"); ESP_LOGCONFIG(TAG, " Using software serial");
} }
this->check_logger_conflict_();
#ifdef USE_LOGGER
if (this->hw_serial_ == &Serial && logger::global_logger->get_baud_rate() != 0) {
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
"disable logging over the serial port by setting logger->baud_rate to 0.");
}
#endif
} }
void UARTComponent::write_byte(uint8_t data) { void UARTComponent::write_byte(uint8_t data) {
@@ -306,24 +295,25 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
return; return;
} }
disable_interrupts(); {
uint32_t wait = this->bit_time_; InterruptLock lock;
const uint32_t start = ESP.getCycleCount(); uint32_t wait = this->bit_time_;
// Start bit const uint32_t start = ESP.getCycleCount();
this->write_bit_(false, &wait, start); // Start bit
this->write_bit_(data & (1 << 0), &wait, start); this->write_bit_(false, &wait, start);
this->write_bit_(data & (1 << 1), &wait, start); this->write_bit_(data & (1 << 0), &wait, start);
this->write_bit_(data & (1 << 2), &wait, start); this->write_bit_(data & (1 << 1), &wait, start);
this->write_bit_(data & (1 << 3), &wait, start); this->write_bit_(data & (1 << 2), &wait, start);
this->write_bit_(data & (1 << 4), &wait, start); this->write_bit_(data & (1 << 3), &wait, start);
this->write_bit_(data & (1 << 5), &wait, start); this->write_bit_(data & (1 << 4), &wait, start);
this->write_bit_(data & (1 << 6), &wait, start); this->write_bit_(data & (1 << 5), &wait, start);
this->write_bit_(data & (1 << 7), &wait, start); this->write_bit_(data & (1 << 6), &wait, start);
// Stop bit this->write_bit_(data & (1 << 7), &wait, start);
this->write_bit_(true, &wait, start); // Stop bit
if (this->stop_bits_ == 2) this->write_bit_(true, &wait, start);
this->wait_(&wait, start); if (this->stop_bits_ == 2)
enable_interrupts(); this->wait_(&wait, start);
}
} }
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
while (ESP.getCycleCount() - start < *wait) while (ESP.getCycleCount() - start < *wait)
@@ -334,7 +324,7 @@ bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint
this->wait_(wait, start); this->wait_(wait, start);
return this->rx_pin_->digital_read(); return this->rx_pin_->digital_read();
} }
void ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { void ICACHE_RAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) {
this->tx_pin_->digital_write(bit); this->tx_pin_->digital_write(bit);
this->wait_(wait, start); this->wait_(wait, start);
} }
@@ -378,6 +368,19 @@ int UARTComponent::peek() {
return data; return data;
} }
void UARTComponent::check_logger_conflict_() {
#ifdef USE_LOGGER
if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
return;
}
if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
"disable logging over the serial port by setting logger->baud_rate to 0.");
}
#endif
}
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits) { void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits) {
if (this->parent_->baud_rate_ != baud_rate) { if (this->parent_->baud_rate_ != baud_rate) {
ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate, ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate,

View File

@@ -76,6 +76,7 @@ class UARTComponent : public Component, public Stream {
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
protected: protected:
void check_logger_conflict_();
bool check_read_timeout_(size_t len = 1); bool check_read_timeout_(size_t len = 1);
friend class UARTDevice; friend class UARTDevice;

View File

@@ -18,6 +18,8 @@ namespace web_server {
static const char *TAG = "web_server"; static const char *TAG = "web_server";
void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action) { void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action) {
if (obj->is_internal())
return;
stream->print("<tr class=\""); stream->print("<tr class=\"");
stream->print(klass.c_str()); stream->print(klass.c_str());
stream->print("\" id=\""); stream->print("\" id=\"");
@@ -62,6 +64,7 @@ void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; }
void WebServer::setup() { void WebServer::setup() {
ESP_LOGCONFIG(TAG, "Setting up web server..."); ESP_LOGCONFIG(TAG, "Setting up web server...");
this->setup_controller();
this->base_->init(); this->base_->init();
this->events_.onConnect([this](AsyncEventSourceClient *client) { this->events_.onConnect([this](AsyncEventSourceClient *client) {
@@ -135,41 +138,37 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
stream->print(F("\"></head><body><article class=\"markdown-body\"><h1>")); stream->print(F("\"></head><body><article class=\"markdown-body\"><h1>"));
stream->print(title.c_str()); stream->print(title.c_str());
stream->print(F("</h1><h2>States</h2><table id=\"states\"><thead><tr><th>Name<th>State<th>Actions<tbody>")); stream->print(F("</h1><h2>States</h2><table id=\"states\"><thead><tr><th>Name<th>State<th>Actions<tbody>"));
// All content is controlled and created by user - so allowing all origins is fine here.
stream->addHeader("Access-Control-Allow-Origin", "*");
#ifdef USE_SENSOR #ifdef USE_SENSOR
for (auto *obj : App.get_sensors()) for (auto *obj : App.get_sensors())
if (!obj->is_internal()) write_row(stream, obj, "sensor", "");
write_row(stream, obj, "sensor", "");
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
for (auto *obj : App.get_switches()) for (auto *obj : App.get_switches())
if (!obj->is_internal()) write_row(stream, obj, "switch", "<button>Toggle</button>");
write_row(stream, obj, "switch", "<button>Toggle</button>");
#endif #endif
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
for (auto *obj : App.get_binary_sensors()) for (auto *obj : App.get_binary_sensors())
if (!obj->is_internal()) write_row(stream, obj, "binary_sensor", "");
write_row(stream, obj, "binary_sensor", "");
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
for (auto *obj : App.get_fans()) for (auto *obj : App.get_fans())
if (!obj->is_internal()) write_row(stream, obj, "fan", "<button>Toggle</button>");
write_row(stream, obj, "fan", "<button>Toggle</button>");
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
for (auto *obj : App.get_lights()) for (auto *obj : App.get_lights())
if (!obj->is_internal()) write_row(stream, obj, "light", "<button>Toggle</button>");
write_row(stream, obj, "light", "<button>Toggle</button>");
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
for (auto *obj : App.get_text_sensors()) for (auto *obj : App.get_text_sensors())
if (!obj->is_internal()) write_row(stream, obj, "text_sensor", "");
write_row(stream, obj, "text_sensor", "");
#endif #endif
stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for " stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for "

View File

@@ -23,4 +23,4 @@ def to_code(config):
if CORE.is_esp32: if CORE.is_esp32:
cg.add_library('FS', None) cg.add_library('FS', None)
# https://github.com/OttoWinter/ESPAsyncWebServer/blob/master/library.json # https://github.com/OttoWinter/ESPAsyncWebServer/blob/master/library.json
cg.add_library('ESPAsyncWebServer-esphome', '1.2.5') cg.add_library('ESPAsyncWebServer-esphome', '1.2.6')

View File

@@ -110,6 +110,7 @@ def validate(config):
return config return config
CONF_OUTPUT_POWER = 'output_power'
CONFIG_SCHEMA = cv.All(cv.Schema({ CONFIG_SCHEMA = cv.All(cv.Schema({
cv.GenerateID(): cv.declare_id(WiFiComponent), cv.GenerateID(): cv.declare_id(WiFiComponent),
cv.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), cv.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA),
@@ -125,6 +126,8 @@ CONFIG_SCHEMA = cv.All(cv.Schema({
cv.enum(WIFI_POWER_SAVE_MODES, upper=True), cv.enum(WIFI_POWER_SAVE_MODES, upper=True),
cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean, cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean,
cv.Optional(CONF_USE_ADDRESS): cv.string_strict, cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All(
cv.decibel, cv.float_range(min=10.0, max=20.5)),
cv.Optional('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"), cv.Optional('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"),
}), validate) }), validate)
@@ -179,12 +182,15 @@ def to_code(config):
if CONF_AP in config: if CONF_AP in config:
conf = config[CONF_AP] conf = config[CONF_AP]
cg.add(var.set_ap(wifi_network(conf, config.get(CONF_MANUAL_IP)))) ip_config = conf.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP))
cg.add(var.set_ap(wifi_network(conf, ip_config)))
cg.add(var.set_ap_timeout(conf[CONF_AP_TIMEOUT])) cg.add(var.set_ap_timeout(conf[CONF_AP_TIMEOUT]))
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE])) cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
cg.add(var.set_fast_connect(config[CONF_FAST_CONNECT])) cg.add(var.set_fast_connect(config[CONF_FAST_CONNECT]))
if CONF_OUTPUT_POWER in config:
cg.add(var.set_output_power(config[CONF_OUTPUT_POWER]))
if CORE.is_esp8266: if CORE.is_esp8266:
cg.add_library('ESP8266WiFi', None) cg.add_library('ESP8266WiFi', None)

View File

@@ -36,6 +36,9 @@ void WiFiComponent::setup() {
if (this->has_sta()) { if (this->has_sta()) {
this->wifi_sta_pre_setup_(); this->wifi_sta_pre_setup_();
if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) {
ESP_LOGV(TAG, "Setting Power Save Option failed!");
}
if (!this->wifi_apply_power_save_()) { if (!this->wifi_apply_power_save_()) {
ESP_LOGV(TAG, "Setting Power Save Option failed!"); ESP_LOGV(TAG, "Setting Power Save Option failed!");
@@ -49,6 +52,9 @@ void WiFiComponent::setup() {
} }
} else if (this->has_ap()) { } else if (this->has_ap()) {
this->setup_ap_config_(); this->setup_ap_config_();
if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) {
ESP_LOGV(TAG, "Setting Power Save Option failed!");
}
#ifdef USE_CAPTIVE_PORTAL #ifdef USE_CAPTIVE_PORTAL
if (captive_portal::global_captive_portal != nullptr) if (captive_portal::global_captive_portal != nullptr)
captive_portal::global_captive_portal->start(); captive_portal::global_captive_portal->start();

View File

@@ -162,6 +162,7 @@ class WiFiComponent : public Component {
bool is_connected(); bool is_connected();
void set_power_save_mode(WiFiPowerSaveMode power_save); void set_power_save_mode(WiFiPowerSaveMode power_save);
void set_output_power(float output_power) { output_power_ = output_power; }
// ========== INTERNAL METHODS ========== // ========== INTERNAL METHODS ==========
// (In most use cases you won't need these) // (In most use cases you won't need these)
@@ -217,6 +218,7 @@ class WiFiComponent : public Component {
bool wifi_mode_(optional<bool> sta, optional<bool> ap); bool wifi_mode_(optional<bool> sta, optional<bool> ap);
bool wifi_sta_pre_setup_(); bool wifi_sta_pre_setup_();
bool wifi_apply_output_power_(float output_power);
bool wifi_apply_power_save_(); bool wifi_apply_power_save_();
bool wifi_sta_ip_config_(optional<ManualIP> manual_ip); bool wifi_sta_ip_config_(optional<ManualIP> manual_ip);
IPAddress wifi_sta_ip_(); IPAddress wifi_sta_ip_();
@@ -260,6 +262,7 @@ class WiFiComponent : public Component {
std::vector<WiFiScanResult> scan_result_; std::vector<WiFiScanResult> scan_result_;
bool scan_done_{false}; bool scan_done_{false};
bool ap_setup_{false}; bool ap_setup_{false};
optional<float> output_power_;
}; };
extern WiFiComponent *global_wifi_component; extern WiFiComponent *global_wifi_component;

View File

@@ -53,6 +53,10 @@ bool WiFiComponent::wifi_mode_(optional<bool> sta, optional<bool> ap) {
return ret; return ret;
} }
bool WiFiComponent::wifi_apply_output_power_(float output_power) {
int8_t val = static_cast<uint8_t>(output_power * 4);
return esp_wifi_set_max_tx_power(val) == ESP_OK;
}
bool WiFiComponent::wifi_sta_pre_setup_() { bool WiFiComponent::wifi_sta_pre_setup_() {
if (!this->wifi_mode_(true, {})) if (!this->wifi_mode_(true, {}))
return false; return false;
@@ -325,8 +329,12 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i
char buf[33]; char buf[33];
memcpy(buf, it.ssid, it.ssid_len); memcpy(buf, it.ssid, it.ssid_len);
buf[it.ssid_len] = '\0'; buf[it.ssid_len] = '\0';
ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason=%s", buf, if (it.reason == WIFI_REASON_NO_AP_FOUND) {
format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf);
} else {
ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf,
format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason));
}
break; break;
} }
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: {

View File

@@ -6,8 +6,16 @@
#include <utility> #include <utility>
#include <algorithm> #include <algorithm>
extern "C" {
#include "lwip/err.h" #include "lwip/err.h"
#include "lwip/dns.h" #include "lwip/dns.h"
#include "lwip/dhcp.h"
#include "lwip/init.h" // LWIP_VERSION_
#if LWIP_IPV6
#include "lwip/netif.h" // struct netif
#endif
}
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@@ -74,6 +82,19 @@ bool WiFiComponent::wifi_apply_power_save_() {
} }
return wifi_set_sleep_type(power_save); return wifi_set_sleep_type(power_save);
} }
#if LWIP_VERSION_MAJOR != 1
/*
lwip v2 needs to be notified of IP changes, see also
https://github.com/d-a-v/Arduino/blob/0e7d21e17144cfc5f53c016191daca8723e89ee8/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp#L251
*/
#undef netif_set_addr // need to call lwIP-v1.4 netif_set_addr()
extern "C" {
struct netif *eagle_lwip_getif(int netif_index);
void netif_set_addr(struct netif *netif, const ip4_addr_t *ip, const ip4_addr_t *netmask, const ip4_addr_t *gw);
};
#endif
bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) { bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
// enable STA // enable STA
if (!this->wifi_mode_(true, {})) if (!this->wifi_mode_(true, {}))
@@ -94,6 +115,13 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
bool ret = true; bool ret = true;
#if LWIP_VERSION_MAJOR != 1
// get current->previous IP address
// (check below)
ip_info previp{};
wifi_get_ip_info(STATION_IF, &previp);
#endif
struct ip_info info {}; struct ip_info info {};
info.ip.addr = static_cast<uint32_t>(manual_ip->static_ip); info.ip.addr = static_cast<uint32_t>(manual_ip->static_ip);
info.gw.addr = static_cast<uint32_t>(manual_ip->gateway); info.gw.addr = static_cast<uint32_t>(manual_ip->gateway);
@@ -122,6 +150,14 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
dns_setserver(1, &dns); dns_setserver(1, &dns);
} }
#if LWIP_VERSION_MAJOR != 1
// trigger address change by calling lwIP-v1.4 api
// only when ip is already set by other mean (generally dhcp)
if (previp.ip.addr != 0 && previp.ip.addr != info.ip.addr) {
netif_set_addr(eagle_lwip_getif(STATION_IF), reinterpret_cast<const ip4_addr_t *>(&info.ip),
reinterpret_cast<const ip4_addr_t *>(&info.netmask), reinterpret_cast<const ip4_addr_t *>(&info.gw));
}
#endif
return ret; return ret;
} }
@@ -133,10 +169,31 @@ IPAddress WiFiComponent::wifi_sta_ip_() {
return {ip.ip.addr}; return {ip.ip.addr};
} }
bool WiFiComponent::wifi_apply_hostname_() { bool WiFiComponent::wifi_apply_hostname_() {
bool ret = wifi_station_set_hostname(const_cast<char *>(App.get_name().c_str())); const std::string &hostname = App.get_name();
bool ret = wifi_station_set_hostname(const_cast<char *>(hostname.c_str()));
if (!ret) { if (!ret) {
ESP_LOGV(TAG, "Setting WiFi Hostname failed!"); ESP_LOGV(TAG, "Setting WiFi Hostname failed!");
} }
// inform dhcp server of hostname change using dhcp_renew()
for (netif *intf = netif_list; intf; intf = intf->next) {
// unconditionally update all known interfaces
#if LWIP_VERSION_MAJOR == 1
intf->hostname = (char *) wifi_station_get_hostname();
#else
intf->hostname = wifi_station_get_hostname();
#endif
if (netif_dhcp_data(intf) != nullptr) {
// renew already started DHCP leases
err_t lwipret = dhcp_renew(intf);
if (lwipret != ERR_OK) {
ESP_LOGW(TAG, "wifi_apply_hostname_(%s): lwIP error %d on interface %c%c (index %d)", intf->hostname,
(int) lwipret, intf->name[0], intf->name[1], intf->num);
ret = false;
}
}
}
return ret; return ret;
} }
@@ -330,8 +387,12 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
char buf[33]; char buf[33];
memcpy(buf, it.ssid, it.ssid_len); memcpy(buf, it.ssid, it.ssid_len);
buf[it.ssid_len] = '\0'; buf[it.ssid_len] = '\0';
ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=%s reason='%s'", buf, format_mac_addr(it.bssid).c_str(), if (it.reason == REASON_NO_AP_FOUND) {
get_disconnect_reason_str(it.reason)); ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf);
} else {
ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf,
format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason));
}
break; break;
} }
case EVENT_STAMODE_AUTHMODE_CHANGE: { case EVENT_STAMODE_AUTHMODE_CHANGE: {
@@ -390,28 +451,15 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
WiFiMockClass::_event_callback(event); WiFiMockClass::_event_callback(event);
} }
bool WiFiComponent::wifi_apply_output_power_(float output_power) {
uint8_t val = static_cast<uint8_t>(output_power * 4);
system_phy_set_max_tpw(val);
return true;
}
bool WiFiComponent::wifi_sta_pre_setup_() { bool WiFiComponent::wifi_sta_pre_setup_() {
if (!this->wifi_mode_(true, {})) if (!this->wifi_mode_(true, {}))
return false; return false;
// Clear saved STA config
station_config default_config{};
wifi_station_get_config_default(&default_config);
bool is_zero = default_config.ssid[0] == '\0' && default_config.password[0] == '\0' && default_config.bssid[0] == 0 &&
default_config.bssid_set == 0;
if (!is_zero) {
ESP_LOGV(TAG, "Clearing default wifi STA config");
memset(&default_config, 0, sizeof(default_config));
ETS_UART_INTR_DISABLE();
bool ret = wifi_station_set_config(&default_config);
ETS_UART_INTR_ENABLE();
if (!ret) {
ESP_LOGW(TAG, "Clearing default wif STA config failed!");
}
}
bool ret1, ret2; bool ret1, ret2;
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
ret1 = wifi_station_set_auto_connect(0); ret1 = wifi_station_set_auto_connect(0);
@@ -428,19 +476,6 @@ bool WiFiComponent::wifi_sta_pre_setup_() {
void WiFiComponent::wifi_pre_setup_() { void WiFiComponent::wifi_pre_setup_() {
wifi_set_event_handler_cb(&WiFiComponent::wifi_event_callback); wifi_set_event_handler_cb(&WiFiComponent::wifi_event_callback);
// Make sure the default opmode is OFF
uint8_t default_opmode = wifi_get_opmode_default();
if (default_opmode != 0) {
ESP_LOGV(TAG, "Setting default WiFi Mode to 0 (was %u)", default_opmode);
ETS_UART_INTR_DISABLE();
bool ret = wifi_set_opmode(0);
ETS_UART_INTR_ENABLE();
if (!ret) {
ESP_LOGW(TAG, "Setting default WiFi mode failed!");
}
}
// Make sure WiFi is in clean state before anything starts // Make sure WiFi is in clean state before anything starts
this->wifi_mode_(false, false); this->wifi_mode_(false, false);
@@ -496,11 +531,14 @@ bool WiFiComponent::wifi_scan_start_() {
return ret; return ret;
} }
bool WiFiComponent::wifi_disconnect_() { bool WiFiComponent::wifi_disconnect_() {
bool ret = true;
// Only call disconnect if interface is up
if (wifi_get_opmode() & WIFI_STA)
ret = wifi_station_disconnect();
station_config conf{}; station_config conf{};
memset(&conf, 0, sizeof(conf)); memset(&conf, 0, sizeof(conf));
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
wifi_station_set_config(&conf); wifi_station_set_config_current(&conf);
bool ret = wifi_station_disconnect();
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
return ret; return ret;
} }
@@ -594,7 +632,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
strcpy(reinterpret_cast<char *>(conf.ssid), ap.get_ssid().c_str()); strcpy(reinterpret_cast<char *>(conf.ssid), ap.get_ssid().c_str());
conf.ssid_len = static_cast<uint8>(ap.get_ssid().size()); conf.ssid_len = static_cast<uint8>(ap.get_ssid().size());
conf.channel = ap.get_channel().value_or(1); conf.channel = ap.get_channel().value_or(1);
conf.ssid_hidden = 0; conf.ssid_hidden = ap.get_hidden();
conf.max_connection = 5; conf.max_connection = 5;
conf.beacon_interval = 100; conf.beacon_interval = 100;

View File

@@ -63,22 +63,17 @@ bool parse_xiaomi_data_byte(uint8_t data_type, const uint8_t *data, uint8_t data
return false; return false;
} }
} }
optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device) { bool parse_xiaomi_service_data(XiaomiParseResult &result, const esp32_ble_tracker::ServiceData &service_data) {
if (!device.get_service_data_uuid().has_value()) { if (!service_data.uuid.contains(0x95, 0xFE)) {
// ESP_LOGVV(TAG, "Xiaomi no service data");
return {};
}
if (!device.get_service_data_uuid()->contains(0x95, 0xFE)) {
// ESP_LOGVV(TAG, "Xiaomi no service data UUID magic bytes"); // ESP_LOGVV(TAG, "Xiaomi no service data UUID magic bytes");
return {}; return false;
} }
const auto *raw = reinterpret_cast<const uint8_t *>(device.get_service_data().data()); const auto raw = service_data.data;
if (device.get_service_data().size() < 14) { if (raw.size() < 14) {
// ESP_LOGVV(TAG, "Xiaomi service data too short!"); // ESP_LOGVV(TAG, "Xiaomi service data too short!");
return {}; return false;
} }
bool is_lywsdcgq = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01; bool is_lywsdcgq = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01;
@@ -88,10 +83,9 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d
if (!is_lywsdcgq && !is_hhccjcy01 && !is_lywsd02 && !is_cgg1) { if (!is_lywsdcgq && !is_hhccjcy01 && !is_lywsd02 && !is_cgg1) {
// ESP_LOGVV(TAG, "Xiaomi no magic bytes"); // ESP_LOGVV(TAG, "Xiaomi no magic bytes");
return {}; return false;
} }
XiaomiParseResult result;
result.type = XiaomiParseResult::TYPE_HHCCJCY01; result.type = XiaomiParseResult::TYPE_HHCCJCY01;
if (is_lywsdcgq) { if (is_lywsdcgq) {
result.type = XiaomiParseResult::TYPE_LYWSDCGQ; result.type = XiaomiParseResult::TYPE_LYWSDCGQ;
@@ -111,7 +105,7 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d
const uint8_t *raw_data = &raw[raw_offset]; const uint8_t *raw_data = &raw[raw_offset];
uint8_t data_offset = 0; uint8_t data_offset = 0;
uint8_t data_length = device.get_service_data().size() - raw_offset; uint8_t data_length = raw.size() - raw_offset;
bool success = false; bool success = false;
while (true) { while (true) {
@@ -136,6 +130,15 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d
data_offset += 3 + datapoint_length; data_offset += 3 + datapoint_length;
} }
return success;
}
optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device) {
XiaomiParseResult result;
bool success = false;
for (auto &service_data : device.get_service_datas()) {
if (parse_xiaomi_service_data(result, service_data))
success = true;
}
if (!success) if (!success)
return {}; return {};
return result; return result;

View File

@@ -616,6 +616,7 @@ angle = float_with_unit("angle", u"(°|deg)", optional_unit=True)
_temperature_c = float_with_unit("temperature", u"(°C|° C|°|C)?") _temperature_c = float_with_unit("temperature", u"(°C|° C|°|C)?")
_temperature_k = float_with_unit("temperature", u"(° K|° K|K)?") _temperature_k = float_with_unit("temperature", u"(° K|° K|K)?")
_temperature_f = float_with_unit("temperature", u"(°F|° F|F)?") _temperature_f = float_with_unit("temperature", u"(°F|° F|F)?")
decibel = float_with_unit("decibel", u"(dB|dBm|db|dbm)", optional_unit=True)
if IS_PY2: if IS_PY2:
# Override voluptuous invalid to unicode for py2 # Override voluptuous invalid to unicode for py2

View File

@@ -3,7 +3,7 @@
MAJOR_VERSION = 1 MAJOR_VERSION = 1
MINOR_VERSION = 14 MINOR_VERSION = 14
PATCH_VERSION = '1' PATCH_VERSION = '4'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
@@ -520,7 +520,7 @@ UNIT_DEGREE_PER_SECOND = u'°/s'
UNIT_DEGREES = u'°' UNIT_DEGREES = u'°'
UNIT_EMPTY = '' UNIT_EMPTY = ''
UNIT_HECTOPASCAL = 'hPa' UNIT_HECTOPASCAL = 'hPa'
UNIT_HZ = 'hz' UNIT_HERTZ = 'hz'
UNIT_KELVIN = 'K' UNIT_KELVIN = 'K'
UNIT_KILOMETER = 'km' UNIT_KILOMETER = 'km'
UNIT_KILOMETER_PER_HOUR = 'km/h' UNIT_KILOMETER_PER_HOUR = 'km/h'
@@ -538,6 +538,8 @@ UNIT_PULSES_PER_MINUTE = 'pulses/min'
UNIT_SECOND = 's' UNIT_SECOND = 's'
UNIT_STEPS = 'steps' UNIT_STEPS = 'steps'
UNIT_VOLT = 'V' UNIT_VOLT = 'V'
UNIT_VOLT_AMPS = 'VA'
UNIT_VOLT_AMPS_REACTIVE = 'VAR'
UNIT_WATT = 'W' UNIT_WATT = 'W'
DEVICE_CLASS_CONNECTIVITY = 'connectivity' DEVICE_CLASS_CONNECTIVITY = 'connectivity'

View File

@@ -567,10 +567,12 @@ class EsphomeCore(object):
return os.path.basename(self.config_path) return os.path.basename(self.config_path)
def relative_config_path(self, *path): def relative_config_path(self, *path):
# pylint: disable=no-value-for-parameter
path_ = os.path.expanduser(os.path.join(*path)) path_ = os.path.expanduser(os.path.join(*path))
return os.path.join(self.config_dir, path_) return os.path.join(self.config_dir, path_)
def relative_build_path(self, *path): def relative_build_path(self, *path):
# pylint: disable=no-value-for-parameter
path_ = os.path.expanduser(os.path.join(*path)) path_ = os.path.expanduser(os.path.join(*path))
return os.path.join(self.build_path, path_) return os.path.join(self.build_path, path_)

View File

@@ -156,21 +156,6 @@ ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
const char *HOSTNAME_CHARACTER_WHITELIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; const char *HOSTNAME_CHARACTER_WHITELIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
void disable_interrupts() {
#ifdef ARDUINO_ARCH_ESP32
portDISABLE_INTERRUPTS();
#else
noInterrupts();
#endif
}
void enable_interrupts() {
#ifdef ARDUINO_ARCH_ESP32
portENABLE_INTERRUPTS();
#else
interrupts();
#endif
}
uint8_t crc8(uint8_t *data, uint8_t len) { uint8_t crc8(uint8_t *data, uint8_t len) {
uint8_t crc = 0; uint8_t crc = 0;
@@ -193,8 +178,8 @@ void delay_microseconds_accurate(uint32_t usec) {
if (usec <= 16383UL) { if (usec <= 16383UL) {
delayMicroseconds(usec); delayMicroseconds(usec);
} else { } else {
delay(usec / 1000UL); delay(usec / 16383UL);
delayMicroseconds(usec % 1000UL); delayMicroseconds(usec % 16383UL);
} }
} }
@@ -330,4 +315,13 @@ std::string hexencode(const uint8_t *data, uint32_t len) {
return res; return res;
} }
#ifdef ARDUINO_ARCH_ESP8266
ICACHE_RAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); }
ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); }
#endif
#ifdef ARDUINO_ARCH_ESP32
ICACHE_RAM_ATTR InterruptLock::InterruptLock() { portENABLE_INTERRUPTS(); }
ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { portDISABLE_INTERRUPTS(); }
#endif
} // namespace esphome } // namespace esphome

View File

@@ -133,16 +133,38 @@ uint16_t encode_uint16(uint8_t msb, uint8_t lsb);
/// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte. /// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte.
std::array<uint8_t, 2> decode_uint16(uint16_t value); std::array<uint8_t, 2> decode_uint16(uint16_t value);
/** Cross-platform method to disable interrupts. /***
* An interrupt helper class.
* *
* Useful when you need to do some timing-dependent communication. * This behaves like std::lock_guard. As long as the value is visible in the current stack, all interrupts
* (including flash reads) will be disabled.
* *
* @see Do not forget to call `enable_interrupts()` again or otherwise things will go very wrong. * Please note all functions called when the interrupt lock must be marked ICACHE_RAM_ATTR (loading code into
* instruction cache is done via interrupts; disabling interrupts prevents data not already in cache from being
* pulled from flash).
*
* Example:
*
* ```cpp
* // interrupts are enabled
* {
* InterruptLock lock;
* // do something
* // interrupts are disabled
* }
* // interrupts are enabled
* ```
*/ */
void disable_interrupts(); class InterruptLock {
public:
InterruptLock();
~InterruptLock();
/// Cross-platform method to enable interrupts after they have been disabled. protected:
void enable_interrupts(); #ifdef ARDUINO_ARCH_ESP8266
uint32_t xt_state_;
#endif
};
/// Calculate a crc8 of data with the provided data length. /// Calculate a crc8 of data with the provided data length.
uint8_t crc8(uint8_t *data, uint8_t len); uint8_t crc8(uint8_t *data, uint8_t len);
@@ -158,6 +180,7 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch
// Encode raw data to a human-readable string (for debugging) // Encode raw data to a human-readable string (for debugging)
std::string hexencode(const uint8_t *data, uint32_t len); std::string hexencode(const uint8_t *data, uint32_t len);
template<typename T> std::string hexencode(const T &data) { return hexencode(data.data(), data.size()); }
// https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971 // https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971
template<int...> struct seq {}; // NOLINT template<int...> struct seq {}; // NOLINT

View File

@@ -105,16 +105,18 @@ void ESPPreferences::save_esp8266_flash_() {
return; return;
ESP_LOGVV(TAG, "Saving preferences to flash..."); ESP_LOGVV(TAG, "Saving preferences to flash...");
disable_interrupts(); SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK;
auto erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); {
InterruptLock lock;
erase_res = spi_flash_erase_sector(get_esp8266_flash_sector());
if (erase_res == SPI_FLASH_RESULT_OK) {
write_res = spi_flash_write(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4);
}
}
if (erase_res != SPI_FLASH_RESULT_OK) { if (erase_res != SPI_FLASH_RESULT_OK) {
enable_interrupts();
ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); ESP_LOGV(TAG, "Erase ESP8266 flash failed!");
return; return;
} }
auto write_res = spi_flash_write(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4);
enable_interrupts();
if (write_res != SPI_FLASH_RESULT_OK) { if (write_res != SPI_FLASH_RESULT_OK) {
ESP_LOGV(TAG, "Write ESP8266 flash failed!"); ESP_LOGV(TAG, "Write ESP8266 flash failed!");
return; return;
@@ -173,9 +175,11 @@ ESPPreferences::ESPPreferences()
void ESPPreferences::begin() { void ESPPreferences::begin() {
this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE];
ESP_LOGVV(TAG, "Loading preferences from flash..."); ESP_LOGVV(TAG, "Loading preferences from flash...");
disable_interrupts();
spi_flash_read(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); {
enable_interrupts(); InterruptLock lock;
spi_flash_read(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4);
}
} }
ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) { ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) {

View File

@@ -211,6 +211,7 @@ uint32_t Scheduler::millis_() {
ESP_LOGD(TAG, "Incrementing scheduler major"); ESP_LOGD(TAG, "Incrementing scheduler major");
this->millis_major_++; this->millis_major_++;
} }
this->last_millis_ = now;
return now; return now;
} }

View File

@@ -45,6 +45,9 @@ def validate_board(value):
validate_platform = cv.one_of('ESP32', 'ESP8266', upper=True) validate_platform = cv.one_of('ESP32', 'ESP8266', upper=True)
PLATFORMIO_ESP8266_LUT = { PLATFORMIO_ESP8266_LUT = {
'2.6.3': 'espressif8266@2.4.0',
'2.6.2': 'espressif8266@2.3.1',
'2.6.1': 'espressif8266@2.3.0',
'2.5.2': 'espressif8266@2.2.3', '2.5.2': 'espressif8266@2.2.3',
'2.5.1': 'espressif8266@2.1.0', '2.5.1': 'espressif8266@2.1.0',
'2.5.0': 'espressif8266@2.0.1', '2.5.0': 'espressif8266@2.0.1',
@@ -62,8 +65,8 @@ PLATFORMIO_ESP32_LUT = {
'1.0.1': 'espressif32@1.6.0', '1.0.1': 'espressif32@1.6.0',
'1.0.2': 'espressif32@1.9.0', '1.0.2': 'espressif32@1.9.0',
'1.0.3': 'espressif32@1.10.0', '1.0.3': 'espressif32@1.10.0',
'1.0.4': 'espressif32@1.11.0', '1.0.4': 'espressif32@1.12.1',
'RECOMMENDED': 'espressif32@1.11.0', 'RECOMMENDED': 'espressif32@1.12.1',
'LATEST': 'espressif32', 'LATEST': 'espressif32',
'DEV': ARDUINO_VERSION_ESP32_DEV, 'DEV': ARDUINO_VERSION_ESP32_DEV,
} }

View File

@@ -67,7 +67,9 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
tls_version=tls_version, ciphers=None) tls_version=tls_version, ciphers=None)
try: try:
client.connect(config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT]) host = str(config[CONF_MQTT][CONF_BROKER])
port = int(config[CONF_MQTT][CONF_PORT])
client.connect(host, port)
except socket.error as err: except socket.error as err:
raise EsphomeError("Cannot connect to MQTT broker: {}".format(err)) raise EsphomeError("Cannot connect to MQTT broker: {}".format(err))
@@ -127,7 +129,7 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
# From marvinroger/async-mqtt-client -> scripts/get-fingerprint/get-fingerprint.py # From marvinroger/async-mqtt-client -> scripts/get-fingerprint/get-fingerprint.py
def get_fingerprint(config): def get_fingerprint(config):
addr = config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT] addr = str(config[CONF_MQTT][CONF_BROKER]), int(config[CONF_MQTT][CONF_PORT])
_LOGGER.info("Getting fingerprint from %s:%s", addr[0], addr[1]) _LOGGER.info("Getting fingerprint from %s:%s", addr[0], addr[1])
try: try:
cert_pem = ssl.get_server_certificate(addr) cert_pem = ssl.get_server_certificate(addr)

View File

@@ -60,6 +60,7 @@ FILTER_PLATFORMIO_LINES = [
r"Using cache: .*", r"Using cache: .*",
r'Installing dependencies', r'Installing dependencies',
r'.* @ .* is already installed', r'.* @ .* is already installed',
r'Building in .* mode',
] ]

View File

@@ -184,6 +184,7 @@ def run_external_command(func, *cmd, **kwargs):
except Exception as err: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
_LOGGER.error(u"Running command failed: %s", err) _LOGGER.error(u"Running command failed: %s", err)
_LOGGER.error(u"Please try running %s locally.", full_cmd) _LOGGER.error(u"Please try running %s locally.", full_cmd)
return 1
finally: finally:
sys.argv = orig_argv sys.argv = orig_argv
sys.exit = orig_exit sys.exit = orig_exit
@@ -216,6 +217,7 @@ def run_external_process(*cmd, **kwargs):
except Exception as err: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
_LOGGER.error(u"Running command failed: %s", err) _LOGGER.error(u"Running command failed: %s", err)
_LOGGER.error(u"Please try running %s locally.", full_cmd) _LOGGER.error(u"Please try running %s locally.", full_cmd)
return 1
finally: finally:
if capture_stdout: if capture_stdout:
# pylint: disable=lost-exception # pylint: disable=lost-exception

View File

@@ -179,7 +179,7 @@ class _Schema(vol.Schema):
self._extra_schemas.append(validator) self._extra_schemas.append(validator)
return self return self
# pylint: disable=arguments-differ # pylint: disable=signature-differs,arguments-differ
def extend(self, *schemas, **kwargs): def extend(self, *schemas, **kwargs):
extra = kwargs.pop('extra', None) extra = kwargs.pop('extra', None)
if kwargs: if kwargs:

View File

@@ -10,10 +10,10 @@ include_dir = include
[common] [common]
lib_deps = lib_deps =
AsyncTCP@1.1.1 AsyncTCP-esphome@1.1.1
AsyncMqttClient-esphome@0.8.3 AsyncMqttClient-esphome@0.8.4
ArduinoJson-esphomelib@5.13.3 ArduinoJson-esphomelib@5.13.3
ESPAsyncWebServer-esphome@1.2.5 ESPAsyncWebServer-esphome@1.2.6
FastLED@3.2.9 FastLED@3.2.9
NeoPixelBus-esphome@2.5.2 NeoPixelBus-esphome@2.5.2
ESPAsyncTCP-esphome@1.2.2 ESPAsyncTCP-esphome@1.2.2
@@ -42,7 +42,7 @@ build_flags = ${common.build_flags}
src_filter = ${common.src_filter} +<tests/livingroom8266.cpp> src_filter = ${common.src_filter} +<tests/livingroom8266.cpp>
[env:livingroom32] [env:livingroom32]
platform = espressif32@1.11.0 platform = espressif32@1.12.1
board = nodemcu-32s board = nodemcu-32s
framework = arduino framework = arduino
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}

View File

@@ -1,13 +1,13 @@
voluptuous==0.11.7 voluptuous==0.11.7
PyYAML==5.1.2 PyYAML==5.3.1
paho-mqtt==1.4.0 paho-mqtt==1.5.0
colorlog==4.0.2 colorlog==4.1.0
tornado==5.1.1 tornado==5.1.1
typing>=3.6.6;python_version<"3.5" typing>=3.6.6;python_version<"3.5"
protobuf==3.10.0 protobuf==3.11.3
tzlocal==2.0.0 tzlocal==2.0.0
pytz==2019.3 pytz==2020.1
pyserial==3.4 pyserial==3.4
ifaddr==0.1.6 ifaddr==0.1.6
platformio==4.0.3 platformio==4.3.4
esptool==2.7 esptool==2.8

View File

@@ -1,16 +1,16 @@
voluptuous==0.11.7 voluptuous==0.11.7
PyYAML==5.1.2 PyYAML==5.3.1
paho-mqtt==1.4.0 paho-mqtt==1.5.0
colorlog==4.0.2 colorlog==4.1.0
tornado==5.1.1 tornado==5.1.1
typing>=3.6.6;python_version<"3.5" typing>=3.6.6;python_version<"3.5"
protobuf==3.10.0 protobuf==3.11.3
tzlocal==2.0.0 tzlocal==2.0.0
pytz==2019.3 pytz==2020.1
pyserial==3.4 pyserial==3.4
ifaddr==0.1.6 ifaddr==0.1.6
platformio==4.0.3 platformio==4.3.4
esptool==2.7 esptool==2.8
pylint==1.9.4 ; python_version<"3" pylint==1.9.4 ; python_version<"3"
pylint==2.3.0 ; python_version>"3" pylint==2.3.0 ; python_version>"3"

View File

@@ -24,14 +24,14 @@ DOWNLOAD_URL = '{}/archive/v{}.zip'.format(GITHUB_URL, const.__version__)
REQUIRES = [ REQUIRES = [
'voluptuous==0.11.7', 'voluptuous==0.11.7',
'PyYAML==5.1.2', 'PyYAML==5.3.1',
'paho-mqtt==1.4.0', 'paho-mqtt==1.5.0',
'colorlog==4.0.2', 'colorlog==4.1.0',
'tornado==5.1.1', 'tornado==5.1.1',
'typing>=3.6.6;python_version<"3.5"', 'typing>=3.6.6;python_version<"3.6"',
'protobuf==3.10.0', 'protobuf==3.11.3',
'tzlocal==2.0.0', 'tzlocal==2.0.0',
'pytz==2019.3', 'pytz==2020.1',
'pyserial==3.4', 'pyserial==3.4',
'ifaddr==0.1.6', 'ifaddr==0.1.6',
] ]
@@ -41,8 +41,8 @@ REQUIRES = [
# This means they have to be in your $PATH. # This means they have to be in your $PATH.
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
REQUIRES.extend([ REQUIRES.extend([
'platformio==4.0.3', 'platformio==4.3.4',
'esptool==2.7', 'esptool==2.8',
]) ])
CLASSIFIERS = [ CLASSIFIERS = [

View File

@@ -618,7 +618,7 @@ sensor:
name: "Windspeed" name: "Windspeed"
wind_direction_degrees: wind_direction_degrees:
name: "Winddirection Degrees" name: "Winddirection Degrees"
pin: pin:
number: GPIO04 number: GPIO04
mode: INPUT mode: INPUT
- platform: zyaura - platform: zyaura
@@ -1033,7 +1033,7 @@ light:
name: "Test For Custom Lambda Effect" name: "Test For Custom Lambda Effect"
lambda: |- lambda: |-
it[0] = current_color; it[0] = current_color;
- automation: - automation:
name: Custom Effect name: Custom Effect
sequence: sequence:
@@ -1105,6 +1105,11 @@ climate:
sensor: my_sensor sensor: my_sensor
- platform: coolix - platform: coolix
name: Coolix Climate name: Coolix Climate
- platform: fujitsu_general
name: Fujitsu General Climate
- platform: yashima
name: Yashima Climate
switch: switch:
- platform: gpio - platform: gpio
@@ -1302,7 +1307,7 @@ interval:
static uint16_t btn_left_state = id(btn_left)->get_value(); static uint16_t btn_left_state = id(btn_left)->get_value();
ESP_LOGD("adaptive touch", "___ Touch Pad '%s' (T%u): val: %u state: %u tres:%u", id(btn_left)->get_name().c_str(), id(btn_left)->get_touch_pad(), id(btn_left)->get_value(), btn_left_state, id(btn_left)->get_threshold()); ESP_LOGD("adaptive touch", "___ Touch Pad '%s' (T%u): val: %u state: %u tres:%u", id(btn_left)->get_name().c_str(), id(btn_left)->get_touch_pad(), id(btn_left)->get_value(), btn_left_state, id(btn_left)->get_threshold());
btn_left_state = ((uint32_t) id(btn_left)->get_value() + 63 * (uint32_t)btn_left_state) >> 6; btn_left_state = ((uint32_t) id(btn_left)->get_value() + 63 * (uint32_t)btn_left_state) >> 6;
id(btn_left)->set_threshold(btn_left_state * 0.9); id(btn_left)->set_threshold(btn_left_state * 0.9);