mirror of
https://github.com/esphome/esphome.git
synced 2024-11-29 10:14:13 +01:00
Merge branch 'dev' of https://github.com/esphome/esphome into dev
This commit is contained in:
commit
37c1513da2
30 changed files with 113 additions and 86 deletions
|
@ -181,7 +181,7 @@ class APIClient(threading.Thread):
|
||||||
self._address)
|
self._address)
|
||||||
_LOGGER.warning("(If this error persists, please set a static IP address: "
|
_LOGGER.warning("(If this error persists, please set a static IP address: "
|
||||||
"https://esphome.io/components/wifi.html#manual-ips)")
|
"https://esphome.io/components/wifi.html#manual-ips)")
|
||||||
raise APIConnectionError(err)
|
raise APIConnectionError(err) from err
|
||||||
|
|
||||||
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
|
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
|
||||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
@ -346,12 +346,12 @@ class APIClient(threading.Thread):
|
||||||
raise APIConnectionError("No socket!")
|
raise APIConnectionError("No socket!")
|
||||||
try:
|
try:
|
||||||
val = self._socket.recv(amount - len(ret))
|
val = self._socket.recv(amount - len(ret))
|
||||||
except AttributeError:
|
except AttributeError as err:
|
||||||
raise APIConnectionError("Socket was closed")
|
raise APIConnectionError("Socket was closed") from err
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
continue
|
continue
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
raise APIConnectionError(f"Error while receiving data: {err}")
|
raise APIConnectionError(f"Error while receiving data: {err}") from err
|
||||||
ret += val
|
ret += val
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||||
return cv.Schema([schema])(value)
|
return cv.Schema([schema])(value)
|
||||||
except cv.Invalid as err2:
|
except cv.Invalid as err2:
|
||||||
if 'extra keys not allowed' in str(err2) and len(err2.path) == 2:
|
if 'extra keys not allowed' in str(err2) and len(err2.path) == 2:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise err
|
raise err
|
||||||
if 'Unable to find action' in str(err):
|
if 'Unable to find action' in str(err):
|
||||||
raise err2
|
raise err2
|
||||||
|
|
|
@ -3,7 +3,8 @@ import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition
|
from esphome.automation import Condition
|
||||||
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
|
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
|
||||||
CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT
|
CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT, \
|
||||||
|
CONF_TAG
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import coroutine_with_priority
|
||||||
|
|
||||||
DEPENDENCIES = ['network']
|
DEPENDENCIES = ['network']
|
||||||
|
@ -137,6 +138,23 @@ def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value({
|
||||||
|
cv.GenerateID(): cv.use_id(APIServer),
|
||||||
|
cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
|
||||||
|
}, key=CONF_TAG)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action('homeassistant.tag_scanned', HomeAssistantServiceCallAction,
|
||||||
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA)
|
||||||
|
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||||
|
serv = yield cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||||
|
cg.add(var.set_service('esphome.tag_scanned'))
|
||||||
|
templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||||
|
cg.add(var.add_data('tag_id', templ))
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@automation.register_condition('api.connected', APIConnectedCondition, {})
|
@automation.register_condition('api.connected', APIConnectedCondition, {})
|
||||||
def api_connected_to_code(config, condition_id, template_arg, args):
|
def api_connected_to_code(config, condition_id, template_arg, args):
|
||||||
yield cg.new_Pvariable(condition_id, template_arg)
|
yield cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
|
|
@ -104,6 +104,7 @@ def parse_multi_click_timing_str(value):
|
||||||
try:
|
try:
|
||||||
state = cv.boolean(parts[0])
|
state = cv.boolean(parts[0])
|
||||||
except cv.Invalid:
|
except cv.Invalid:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
|
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
|
||||||
|
|
||||||
if parts[1] != 'for':
|
if parts[1] != 'for':
|
||||||
|
|
|
@ -42,9 +42,9 @@ def validate_glyphs(value):
|
||||||
def validate_pillow_installed(value):
|
def validate_pillow_installed(value):
|
||||||
try:
|
try:
|
||||||
import PIL
|
import PIL
|
||||||
except ImportError:
|
except ImportError as err:
|
||||||
raise cv.Invalid("Please install the pillow python package to use this feature. "
|
raise cv.Invalid("Please install the pillow python package to use this feature. "
|
||||||
"(pip install pillow)")
|
"(pip install pillow)") from err
|
||||||
|
|
||||||
if PIL.__version__[0] < '4':
|
if PIL.__version__[0] < '4':
|
||||||
raise cv.Invalid("Please update your pillow installation to at least 4.0.x. "
|
raise cv.Invalid("Please update your pillow installation to at least 4.0.x. "
|
||||||
|
|
|
@ -43,8 +43,8 @@ def validate_url(value):
|
||||||
value = cv.string(value)
|
value = cv.string(value)
|
||||||
try:
|
try:
|
||||||
parsed = list(urlparse.urlparse(value))
|
parsed = list(urlparse.urlparse(value))
|
||||||
except Exception:
|
except Exception as err:
|
||||||
raise cv.Invalid('Invalid URL')
|
raise cv.Invalid('Invalid URL') from err
|
||||||
|
|
||||||
if not parsed[0] or not parsed[1]:
|
if not parsed[0] or not parsed[1]:
|
||||||
raise cv.Invalid('URL must have a URL scheme and host')
|
raise cv.Invalid('URL must have a URL scheme and host')
|
||||||
|
|
|
@ -44,7 +44,7 @@ void MAX31855Sensor::read_data_() {
|
||||||
const uint32_t mem = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3] << 0;
|
const uint32_t mem = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3] << 0;
|
||||||
|
|
||||||
// Verify we got data
|
// Verify we got data
|
||||||
if (mem != 0 && mem != 0xFFFFFFFF) {
|
if (mem != 0xFFFFFFFF) {
|
||||||
this->status_clear_error();
|
this->status_clear_error();
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "No data received from MAX31855 (0x%08X). Check wiring!", mem);
|
ESP_LOGE(TAG, "No data received from MAX31855 (0x%08X). Check wiring!", mem);
|
||||||
|
|
|
@ -18,8 +18,8 @@ def validate_uid(value):
|
||||||
"long.")
|
"long.")
|
||||||
try:
|
try:
|
||||||
x = int(x, 16)
|
x = int(x, 16)
|
||||||
except ValueError:
|
except ValueError as err:
|
||||||
raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.")
|
raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err
|
||||||
if x < 0 or x > 255:
|
if x < 0 or x > 255:
|
||||||
raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF")
|
raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF")
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -11,6 +11,7 @@ static const char *TAG = "remote_receiver.esp32";
|
||||||
|
|
||||||
void RemoteReceiverComponent::setup() {
|
void RemoteReceiverComponent::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up Remote Receiver...");
|
ESP_LOGCONFIG(TAG, "Setting up Remote Receiver...");
|
||||||
|
this->pin_->setup();
|
||||||
rmt_config_t rmt{};
|
rmt_config_t rmt{};
|
||||||
this->config_rmt(rmt);
|
this->config_rmt(rmt);
|
||||||
rmt.gpio_num = gpio_num_t(this->pin_->get_pin());
|
rmt.gpio_num = gpio_num_t(this->pin_->get_pin());
|
||||||
|
|
|
@ -28,6 +28,7 @@ def validate_acceleration(value):
|
||||||
try:
|
try:
|
||||||
value = float(value)
|
value = float(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise cv.Invalid(f"Expected acceleration as floating point number, got {value}")
|
raise cv.Invalid(f"Expected acceleration as floating point number, got {value}")
|
||||||
|
|
||||||
if value <= 0:
|
if value <= 0:
|
||||||
|
@ -48,6 +49,7 @@ def validate_speed(value):
|
||||||
try:
|
try:
|
||||||
value = float(value)
|
value = float(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise cv.Invalid(f"Expected speed as floating point number, got {value}")
|
raise cv.Invalid(f"Expected speed as floating point number, got {value}")
|
||||||
|
|
||||||
if value <= 0:
|
if value <= 0:
|
||||||
|
|
|
@ -138,6 +138,7 @@ def _parse_cron_int(value, special_mapping, message):
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise cv.Invalid(message.format(value))
|
raise cv.Invalid(message.format(value))
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,6 +159,7 @@ def _parse_cron_part(part, min_value, max_value, special_mapping):
|
||||||
try:
|
try:
|
||||||
repeat_n = int(repeat)
|
repeat_n = int(repeat)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise cv.Invalid("Repeat for '/' time expression must be an integer, got {}"
|
raise cv.Invalid("Repeat for '/' time expression must be an integer, got {}"
|
||||||
.format(repeat))
|
.format(repeat))
|
||||||
return set(range(offset_n, max_value + 1, repeat_n))
|
return set(range(offset_n, max_value + 1, repeat_n))
|
||||||
|
|
|
@ -7,9 +7,9 @@ from .. import tuya_ns, CONF_TUYA_ID, Tuya
|
||||||
DEPENDENCIES = ['tuya']
|
DEPENDENCIES = ['tuya']
|
||||||
CODEOWNERS = ['@jesserockz']
|
CODEOWNERS = ['@jesserockz']
|
||||||
|
|
||||||
CONF_TARGET_TEMPERATURE_DATAPOINT = "target_temperature_datapoint"
|
CONF_TARGET_TEMPERATURE_DATAPOINT = 'target_temperature_datapoint'
|
||||||
CONF_CURRENT_TEMPERATURE_DATAPOINT = "current_temperature_datapoint"
|
CONF_CURRENT_TEMPERATURE_DATAPOINT = 'current_temperature_datapoint'
|
||||||
# CONF_ECO_MODE_DATAPOINT = "eco_mode_datapoint"
|
CONF_TEMPERATURE_MULTIPLIER = 'temperature_multiplier'
|
||||||
|
|
||||||
TuyaClimate = tuya_ns.class_('TuyaClimate', climate.Climate, cg.Component)
|
TuyaClimate = tuya_ns.class_('TuyaClimate', climate.Climate, cg.Component)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
||||||
cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
||||||
# cv.Optional(CONF_ECO_MODE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_TEMPERATURE_MULTIPLIER, default=1): cv.positive_float,
|
||||||
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(
|
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(
|
||||||
CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT))
|
CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT))
|
||||||
|
|
||||||
|
@ -38,5 +38,4 @@ def to_code(config):
|
||||||
cg.add(var.set_target_temperature_id(config[CONF_TARGET_TEMPERATURE_DATAPOINT]))
|
cg.add(var.set_target_temperature_id(config[CONF_TARGET_TEMPERATURE_DATAPOINT]))
|
||||||
if CONF_CURRENT_TEMPERATURE_DATAPOINT in config:
|
if CONF_CURRENT_TEMPERATURE_DATAPOINT in config:
|
||||||
cg.add(var.set_current_temperature_id(config[CONF_CURRENT_TEMPERATURE_DATAPOINT]))
|
cg.add(var.set_current_temperature_id(config[CONF_CURRENT_TEMPERATURE_DATAPOINT]))
|
||||||
# if CONF_ECO_MODE_DATAPOINT in config:
|
cg.add(var.set_temperature_multiplier(config[CONF_TEMPERATURE_MULTIPLIER]))
|
||||||
# cg.add(var.set_eco_mode_id(config[CONF_ECO_MODE_DATAPOINT]))
|
|
||||||
|
|
|
@ -21,28 +21,20 @@ void TuyaClimate::setup() {
|
||||||
}
|
}
|
||||||
if (this->target_temperature_id_.has_value()) {
|
if (this->target_temperature_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->target_temperature_id_, [this](TuyaDatapoint datapoint) {
|
this->parent_->register_listener(*this->target_temperature_id_, [this](TuyaDatapoint datapoint) {
|
||||||
this->target_temperature = datapoint.value_int;
|
this->target_temperature = datapoint.value_int * this->temperature_multiplier_;
|
||||||
this->compute_state_();
|
this->compute_state_();
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
ESP_LOGD(TAG, "MCU reported target temperature is: %d", datapoint.value_int);
|
ESP_LOGD(TAG, "MCU reported target temperature is: %.1f", this->target_temperature);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this->current_temperature_id_.has_value()) {
|
if (this->current_temperature_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->current_temperature_id_, [this](TuyaDatapoint datapoint) {
|
this->parent_->register_listener(*this->current_temperature_id_, [this](TuyaDatapoint datapoint) {
|
||||||
this->current_temperature = datapoint.value_int;
|
this->current_temperature = datapoint.value_int * this->temperature_multiplier_;
|
||||||
this->compute_state_();
|
this->compute_state_();
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
ESP_LOGD(TAG, "MCU reported current temperature is: %d", datapoint.value_int);
|
ESP_LOGD(TAG, "MCU reported current temperature is: %.1f", this->current_temperature);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// if (this->eco_mode_id_.has_value()) {
|
|
||||||
// this->parent_->register_listener(*this->eco_mode_id_, [this](TuyaDatapoint datapoint) {
|
|
||||||
// this->eco_mode = datapoint.value_bool;
|
|
||||||
// this->compute_state_();
|
|
||||||
// this->publish_state();
|
|
||||||
// ESP_LOGD(TAG, "MCU reported eco mode of: %s", ONOFF(datapoint.value_bool));
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TuyaClimate::control(const climate::ClimateCall &call) {
|
void TuyaClimate::control(const climate::ClimateCall &call) {
|
||||||
|
@ -56,30 +48,17 @@ void TuyaClimate::control(const climate::ClimateCall &call) {
|
||||||
this->parent_->set_datapoint_value(datapoint);
|
this->parent_->set_datapoint_value(datapoint);
|
||||||
ESP_LOGD(TAG, "Setting switch: %s", ONOFF(datapoint.value_bool));
|
ESP_LOGD(TAG, "Setting switch: %s", ONOFF(datapoint.value_bool));
|
||||||
}
|
}
|
||||||
if (call.get_target_temperature_low().has_value())
|
|
||||||
this->target_temperature_low = *call.get_target_temperature_low();
|
|
||||||
if (call.get_target_temperature_high().has_value())
|
|
||||||
this->target_temperature_high = *call.get_target_temperature_high();
|
|
||||||
if (call.get_target_temperature().has_value()) {
|
if (call.get_target_temperature().has_value()) {
|
||||||
this->target_temperature = *call.get_target_temperature();
|
this->target_temperature = *call.get_target_temperature();
|
||||||
|
|
||||||
TuyaDatapoint datapoint{};
|
TuyaDatapoint datapoint{};
|
||||||
datapoint.id = *this->target_temperature_id_;
|
datapoint.id = *this->target_temperature_id_;
|
||||||
datapoint.type = TuyaDatapointType::INTEGER;
|
datapoint.type = TuyaDatapointType::INTEGER;
|
||||||
datapoint.value_int = (int) this->target_temperature;
|
datapoint.value_int = (int) (this->target_temperature / this->temperature_multiplier_);
|
||||||
this->parent_->set_datapoint_value(datapoint);
|
this->parent_->set_datapoint_value(datapoint);
|
||||||
ESP_LOGD(TAG, "Setting target temperature: %d", datapoint.value_int);
|
ESP_LOGD(TAG, "Setting target temperature: %.1f", this->target_temperature);
|
||||||
}
|
}
|
||||||
// if (call.get_eco_mode().has_value()) {
|
|
||||||
// this->eco_mode = *call.get_eco_mode();
|
|
||||||
|
|
||||||
// TuyaDatapoint datapoint{};
|
|
||||||
// datapoint.id = *this->eco_mode_id_;
|
|
||||||
// datapoint.type = TuyaDatapointType::BOOLEAN;
|
|
||||||
// datapoint.value_bool = this->eco_mode;
|
|
||||||
// this->parent_->set_datapoint_value(datapoint);
|
|
||||||
// ESP_LOGD(TAG, "Setting eco mode: %s", ONOFF(datapoint.value_bool));
|
|
||||||
// }
|
|
||||||
|
|
||||||
this->compute_state_();
|
this->compute_state_();
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
|
@ -89,7 +68,6 @@ climate::ClimateTraits TuyaClimate::traits() {
|
||||||
auto traits = climate::ClimateTraits();
|
auto traits = climate::ClimateTraits();
|
||||||
traits.set_supports_current_temperature(this->current_temperature_id_.has_value());
|
traits.set_supports_current_temperature(this->current_temperature_id_.has_value());
|
||||||
traits.set_supports_heat_mode(true);
|
traits.set_supports_heat_mode(true);
|
||||||
// traits.set_supports_eco_mode(this->eco_mode_id_.has_value());
|
|
||||||
traits.set_supports_action(true);
|
traits.set_supports_action(true);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
@ -102,8 +80,6 @@ void TuyaClimate::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_);
|
ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_);
|
||||||
if (this->current_temperature_id_.has_value())
|
if (this->current_temperature_id_.has_value())
|
||||||
ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_);
|
ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_);
|
||||||
// if (this->eco_mode_id_.has_value())
|
|
||||||
// ESP_LOGCONFIG(TAG, " Eco Mode has datapoint ID %u", *this->mode_id_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TuyaClimate::compute_state_() {
|
void TuyaClimate::compute_state_() {
|
||||||
|
|
|
@ -18,7 +18,9 @@ class TuyaClimate : public climate::Climate, public Component {
|
||||||
void set_current_temperature_id(uint8_t current_temperature_id) {
|
void set_current_temperature_id(uint8_t current_temperature_id) {
|
||||||
this->current_temperature_id_ = current_temperature_id;
|
this->current_temperature_id_ = current_temperature_id;
|
||||||
}
|
}
|
||||||
// void set_eco_mode_id(uint8_t eco_mode_id) { this->eco_mode_id_ = eco_mode_id; }
|
void set_temperature_multiplier(float temperature_multiplier) {
|
||||||
|
this->temperature_multiplier_ = temperature_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
|
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ class TuyaClimate : public climate::Climate, public Component {
|
||||||
optional<uint8_t> switch_id_{};
|
optional<uint8_t> switch_id_{};
|
||||||
optional<uint8_t> target_temperature_id_{};
|
optional<uint8_t> target_temperature_id_{};
|
||||||
optional<uint8_t> current_temperature_id_{};
|
optional<uint8_t> current_temperature_id_{};
|
||||||
// optional<uint8_t> eco_mode_id_{};
|
float temperature_multiplier_{1.0f};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tuya
|
} // namespace tuya
|
||||||
|
|
|
@ -18,9 +18,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
def validate_cryptography_installed():
|
def validate_cryptography_installed():
|
||||||
try:
|
try:
|
||||||
import cryptography
|
import cryptography
|
||||||
except ImportError:
|
except ImportError as err:
|
||||||
raise cv.Invalid("This settings requires the cryptography python package. "
|
raise cv.Invalid("This settings requires the cryptography python package. "
|
||||||
"Please install it with `pip install cryptography`")
|
"Please install it with `pip install cryptography`") from err
|
||||||
|
|
||||||
if cryptography.__version__[0] < '2':
|
if cryptography.__version__[0] < '2':
|
||||||
raise cv.Invalid("Please update your python cryptography installation to least 2.x "
|
raise cv.Invalid("Please update your python cryptography installation to least 2.x "
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, esp32_ble_tracker
|
from esphome.components import sensor, esp32_ble_tracker
|
||||||
from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
|
from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
|
||||||
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, CONF_ID, \
|
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \
|
||||||
CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \
|
CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \
|
||||||
UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER
|
UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
|
||||||
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0),
|
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0),
|
||||||
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0),
|
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0),
|
||||||
|
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0),
|
||||||
cv.Optional(CONF_CONDUCTIVITY):
|
cv.Optional(CONF_CONDUCTIVITY):
|
||||||
sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0),
|
sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0),
|
||||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
@ -43,3 +44,6 @@ def to_code(config):
|
||||||
if CONF_CONDUCTIVITY in config:
|
if CONF_CONDUCTIVITY in config:
|
||||||
sens = yield sensor.new_sensor(config[CONF_CONDUCTIVITY])
|
sens = yield sensor.new_sensor(config[CONF_CONDUCTIVITY])
|
||||||
cg.add(var.set_conductivity(sens))
|
cg.add(var.set_conductivity(sens))
|
||||||
|
if CONF_BATTERY_LEVEL in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||||
|
cg.add(var.set_battery_level(sens))
|
||||||
|
|
|
@ -14,6 +14,7 @@ void XiaomiHHCCJCY01::dump_config() {
|
||||||
LOG_SENSOR(" ", "Moisture", this->moisture_);
|
LOG_SENSOR(" ", "Moisture", this->moisture_);
|
||||||
LOG_SENSOR(" ", "Conductivity", this->conductivity_);
|
LOG_SENSOR(" ", "Conductivity", this->conductivity_);
|
||||||
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||||
|
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
|
@ -50,6 +51,8 @@ bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
||||||
this->conductivity_->publish_state(*res->conductivity);
|
this->conductivity_->publish_state(*res->conductivity);
|
||||||
if (res->illuminance.has_value() && this->illuminance_ != nullptr)
|
if (res->illuminance.has_value() && this->illuminance_ != nullptr)
|
||||||
this->illuminance_->publish_state(*res->illuminance);
|
this->illuminance_->publish_state(*res->illuminance);
|
||||||
|
if (res->battery_level.has_value() && this->battery_level_ != nullptr)
|
||||||
|
this->battery_level_->publish_state(*res->battery_level);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ class XiaomiHHCCJCY01 : public Component, public esp32_ble_tracker::ESPBTDeviceL
|
||||||
void set_moisture(sensor::Sensor *moisture) { moisture_ = moisture; }
|
void set_moisture(sensor::Sensor *moisture) { moisture_ = moisture; }
|
||||||
void set_conductivity(sensor::Sensor *conductivity) { conductivity_ = conductivity; }
|
void set_conductivity(sensor::Sensor *conductivity) { conductivity_ = conductivity; }
|
||||||
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||||
|
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t address_;
|
uint64_t address_;
|
||||||
|
@ -29,6 +30,7 @@ class XiaomiHHCCJCY01 : public Component, public esp32_ble_tracker::ESPBTDeviceL
|
||||||
sensor::Sensor *moisture_{nullptr};
|
sensor::Sensor *moisture_{nullptr};
|
||||||
sensor::Sensor *conductivity_{nullptr};
|
sensor::Sensor *conductivity_{nullptr};
|
||||||
sensor::Sensor *illuminance_{nullptr};
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
|
sensor::Sensor *battery_level_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xiaomi_hhccjcy01
|
} // namespace xiaomi_hhccjcy01
|
||||||
|
|
|
@ -675,7 +675,7 @@ def _load_config(command_line_substitutions):
|
||||||
try:
|
try:
|
||||||
config = yaml_util.load_yaml(CORE.config_path)
|
config = yaml_util.load_yaml(CORE.config_path)
|
||||||
except EsphomeError as e:
|
except EsphomeError as e:
|
||||||
raise InvalidYAMLError(e)
|
raise InvalidYAMLError(e) from e
|
||||||
CORE.raw_config = config
|
CORE.raw_config = config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -693,7 +693,7 @@ def load_config(command_line_substitutions):
|
||||||
try:
|
try:
|
||||||
return _load_config(command_line_substitutions)
|
return _load_config(command_line_substitutions)
|
||||||
except vol.Invalid as err:
|
except vol.Invalid as err:
|
||||||
raise EsphomeError(f"Error while parsing config: {err}")
|
raise EsphomeError(f"Error while parsing config: {err}") from err
|
||||||
|
|
||||||
|
|
||||||
def line_info(obj, highlight=True):
|
def line_info(obj, highlight=True):
|
||||||
|
|
|
@ -224,6 +224,7 @@ def int_(value):
|
||||||
try:
|
try:
|
||||||
return int(value, base)
|
return int(value, base)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid(f"Expected integer, but cannot parse {value} as an integer")
|
raise Invalid(f"Expected integer, but cannot parse {value} as an integer")
|
||||||
|
|
||||||
|
|
||||||
|
@ -423,6 +424,7 @@ def time_period_str_colon(value):
|
||||||
try:
|
try:
|
||||||
parsed = [int(x) for x in value.split(':')]
|
parsed = [int(x) for x in value.split(':')]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid(TIME_PERIOD_ERROR.format(value))
|
raise Invalid(TIME_PERIOD_ERROR.format(value))
|
||||||
|
|
||||||
if len(parsed) == 2:
|
if len(parsed) == 2:
|
||||||
|
@ -527,6 +529,7 @@ def time_of_day(value):
|
||||||
try:
|
try:
|
||||||
date = datetime.strptime(value, '%H:%M:%S %p')
|
date = datetime.strptime(value, '%H:%M:%S %p')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid(f"Invalid time of day: {err}")
|
raise Invalid(f"Invalid time of day: {err}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -548,6 +551,7 @@ def mac_address(value):
|
||||||
try:
|
try:
|
||||||
parts_int.append(int(part, 16))
|
parts_int.append(int(part, 16))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
|
raise Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
|
||||||
|
|
||||||
return core.MACAddress(*parts_int)
|
return core.MACAddress(*parts_int)
|
||||||
|
@ -565,6 +569,7 @@ def bind_key(value):
|
||||||
try:
|
try:
|
||||||
parts_int.append(int(part, 16))
|
parts_int.append(int(part, 16))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid("Bind key must be hex values from 00 to FF")
|
raise Invalid("Bind key must be hex values from 00 to FF")
|
||||||
|
|
||||||
return ''.join(f'{part:02X}' for part in parts_int)
|
return ''.join(f'{part:02X}' for part in parts_int)
|
||||||
|
@ -686,8 +691,8 @@ def domain(value):
|
||||||
return value
|
return value
|
||||||
try:
|
try:
|
||||||
return str(ipv4(value))
|
return str(ipv4(value))
|
||||||
except Invalid:
|
except Invalid as err:
|
||||||
raise Invalid(f"Invalid domain: {value}")
|
raise Invalid(f"Invalid domain: {value}") from err
|
||||||
|
|
||||||
|
|
||||||
def domain_name(value):
|
def domain_name(value):
|
||||||
|
@ -739,8 +744,8 @@ def _valid_topic(value):
|
||||||
value = string(value)
|
value = string(value)
|
||||||
try:
|
try:
|
||||||
raw_value = value.encode('utf-8')
|
raw_value = value.encode('utf-8')
|
||||||
except UnicodeError:
|
except UnicodeError as err:
|
||||||
raise Invalid("MQTT topic name/filter must be valid UTF-8 string.")
|
raise Invalid("MQTT topic name/filter must be valid UTF-8 string.") from err
|
||||||
if not raw_value:
|
if not raw_value:
|
||||||
raise Invalid("MQTT topic name/filter must not be empty.")
|
raise Invalid("MQTT topic name/filter must not be empty.")
|
||||||
if len(raw_value) > 65535:
|
if len(raw_value) > 65535:
|
||||||
|
@ -792,6 +797,7 @@ def mqtt_qos(value):
|
||||||
try:
|
try:
|
||||||
value = int(value)
|
value = int(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid(f"MQTT Quality of Service must be integer, got {value}")
|
raise Invalid(f"MQTT Quality of Service must be integer, got {value}")
|
||||||
return one_of(0, 1, 2)(value)
|
return one_of(0, 1, 2)(value)
|
||||||
|
|
||||||
|
@ -836,6 +842,7 @@ def possibly_negative_percentage(value):
|
||||||
else:
|
else:
|
||||||
value = float(value)
|
value = float(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid("invalid number")
|
raise Invalid("invalid number")
|
||||||
if value > 1:
|
if value > 1:
|
||||||
msg = "Percentage must not be higher than 100%."
|
msg = "Percentage must not be higher than 100%."
|
||||||
|
@ -1006,6 +1013,7 @@ def dimensions(value):
|
||||||
try:
|
try:
|
||||||
width, height = int(value[0]), int(value[1])
|
width, height = int(value[0]), int(value[1])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise Invalid("Width and height dimensions must be integers")
|
raise Invalid("Width and height dimensions must be integers")
|
||||||
if width <= 0 or height <= 0:
|
if width <= 0 or height <= 0:
|
||||||
raise Invalid("Width and height must at least be 1")
|
raise Invalid("Width and height must at least be 1")
|
||||||
|
|
|
@ -83,19 +83,19 @@ def receive_exactly(sock, amount, msg, expect, decode=True):
|
||||||
try:
|
try:
|
||||||
data += recv_decode(sock, 1, decode=decode)
|
data += recv_decode(sock, 1, decode=decode)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
raise OTAError(f"Error receiving acknowledge {msg}: {err}")
|
raise OTAError(f"Error receiving acknowledge {msg}: {err}") from err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_error(data, expect)
|
check_error(data, expect)
|
||||||
except OTAError as err:
|
except OTAError as err:
|
||||||
sock.close()
|
sock.close()
|
||||||
raise OTAError(f"Error {msg}: {err}")
|
raise OTAError(f"Error {msg}: {err}") from err
|
||||||
|
|
||||||
while len(data) < amount:
|
while len(data) < amount:
|
||||||
try:
|
try:
|
||||||
data += recv_decode(sock, amount - len(data), decode=decode)
|
data += recv_decode(sock, amount - len(data), decode=decode)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
raise OTAError(f"Error receiving {msg}: {err}")
|
raise OTAError(f"Error receiving {msg}: {err}") from err
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ def send_check(sock, data, msg):
|
||||||
|
|
||||||
sock.sendall(data)
|
sock.sendall(data)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
raise OTAError(f"Error sending {msg}: {err}")
|
raise OTAError(f"Error sending {msg}: {err}") from err
|
||||||
|
|
||||||
|
|
||||||
def perform_ota(sock, password, file_handle, filename):
|
def perform_ota(sock, password, file_handle, filename):
|
||||||
|
@ -226,7 +226,7 @@ def perform_ota(sock, password, file_handle, filename):
|
||||||
sock.sendall(chunk)
|
sock.sendall(chunk)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
sys.stderr.write('\n')
|
sys.stderr.write('\n')
|
||||||
raise OTAError(f"Error sending data: {err}")
|
raise OTAError(f"Error sending data: {err}") from err
|
||||||
|
|
||||||
progress.update(offset / float(file_size))
|
progress.update(offset / float(file_size))
|
||||||
progress.done()
|
progress.done()
|
||||||
|
@ -259,7 +259,7 @@ def run_ota_impl_(remote_host, remote_port, password, filename):
|
||||||
remote_host)
|
remote_host)
|
||||||
_LOGGER.error("(If this error persists, please set a static IP address: "
|
_LOGGER.error("(If this error persists, please set a static IP address: "
|
||||||
"https://esphome.io/components/wifi.html#manual-ips)")
|
"https://esphome.io/components/wifi.html#manual-ips)")
|
||||||
raise OTAError(err)
|
raise OTAError(err) from err
|
||||||
_LOGGER.info(" -> %s", ip)
|
_LOGGER.info(" -> %s", ip)
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
|
@ -89,7 +89,7 @@ def mkdir_p(path):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
raise EsphomeError(f"Error creating directories {path}: {err}")
|
raise EsphomeError(f"Error creating directories {path}: {err}") from err
|
||||||
|
|
||||||
|
|
||||||
def is_ip_address(host):
|
def is_ip_address(host):
|
||||||
|
@ -110,13 +110,13 @@ def _resolve_with_zeroconf(host):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zc = Zeroconf()
|
zc = Zeroconf()
|
||||||
except Exception:
|
except Exception as err:
|
||||||
raise EsphomeError("Cannot start mDNS sockets, is this a docker container without "
|
raise EsphomeError("Cannot start mDNS sockets, is this a docker container without "
|
||||||
"host network mode?")
|
"host network mode?") from err
|
||||||
try:
|
try:
|
||||||
info = zc.resolve_host(host + '.')
|
info = zc.resolve_host(host + '.')
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise EsphomeError(f"Error resolving mDNS hostname: {err}")
|
raise EsphomeError(f"Error resolving mDNS hostname: {err}") from err
|
||||||
finally:
|
finally:
|
||||||
zc.close()
|
zc.close()
|
||||||
if info is None:
|
if info is None:
|
||||||
|
@ -142,7 +142,7 @@ def resolve_ip_address(host):
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
errs.append(str(err))
|
errs.append(str(err))
|
||||||
raise EsphomeError("Error resolving IP address: {}"
|
raise EsphomeError("Error resolving IP address: {}"
|
||||||
"".format(', '.join(errs)))
|
"".format(', '.join(errs))) from err
|
||||||
|
|
||||||
|
|
||||||
def get_bool_env(var, default=False):
|
def get_bool_env(var, default=False):
|
||||||
|
@ -165,10 +165,10 @@ def read_file(path):
|
||||||
return f_handle.read()
|
return f_handle.read()
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
raise EsphomeError(f"Error reading file {path}: {err}")
|
raise EsphomeError(f"Error reading file {path}: {err}") from err
|
||||||
except UnicodeDecodeError as err:
|
except UnicodeDecodeError as err:
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
raise EsphomeError(f"Error reading file {path}: {err}")
|
raise EsphomeError(f"Error reading file {path}: {err}") from err
|
||||||
|
|
||||||
|
|
||||||
def _write_file(path: Union[Path, str], text: Union[str, bytes]):
|
def _write_file(path: Union[Path, str], text: Union[str, bytes]):
|
||||||
|
@ -205,9 +205,9 @@ def _write_file(path: Union[Path, str], text: Union[str, bytes]):
|
||||||
def write_file(path: Union[Path, str], text: str):
|
def write_file(path: Union[Path, str], text: str):
|
||||||
try:
|
try:
|
||||||
_write_file(path, text)
|
_write_file(path, text)
|
||||||
except OSError:
|
except OSError as err:
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
raise EsphomeError(f"Could not write file at {path}")
|
raise EsphomeError(f"Could not write file at {path}") from err
|
||||||
|
|
||||||
|
|
||||||
def write_file_if_changed(path: Union[Path, str], text: str):
|
def write_file_if_changed(path: Union[Path, str], text: str):
|
||||||
|
@ -230,7 +230,7 @@ def copy_file_if_changed(src, dst):
|
||||||
shutil.copy(src, dst)
|
shutil.copy(src, dst)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
raise EsphomeError(f"Error copying file {src} to {dst}: {err}")
|
raise EsphomeError(f"Error copying file {src} to {dst}: {err}") from err
|
||||||
|
|
||||||
|
|
||||||
def list_starts_with(list_, sub):
|
def list_starts_with(list_, sub):
|
||||||
|
|
|
@ -67,7 +67,7 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
|
||||||
port = int(config[CONF_MQTT][CONF_PORT])
|
port = int(config[CONF_MQTT][CONF_PORT])
|
||||||
client.connect(host, port)
|
client.connect(host, port)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
raise EsphomeError(f"Cannot connect to MQTT broker: {err}")
|
raise EsphomeError(f"Cannot connect to MQTT broker: {err}") from err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client.loop_forever()
|
client.loop_forever()
|
||||||
|
|
|
@ -22,13 +22,13 @@ def patch_structhash():
|
||||||
from os import makedirs
|
from os import makedirs
|
||||||
|
|
||||||
def patched_clean_build_dir(build_dir, *args):
|
def patched_clean_build_dir(build_dir, *args):
|
||||||
from platformio import util
|
from platformio import fs
|
||||||
from platformio.project.helpers import get_project_dir
|
from platformio.project.helpers import get_project_dir
|
||||||
platformio_ini = join(get_project_dir(), "platformio.ini")
|
platformio_ini = join(get_project_dir(), "platformio.ini")
|
||||||
|
|
||||||
# if project's config is modified
|
# if project's config is modified
|
||||||
if isdir(build_dir) and getmtime(platformio_ini) > getmtime(build_dir):
|
if isdir(build_dir) and getmtime(platformio_ini) > getmtime(build_dir):
|
||||||
util.rmtree_(build_dir)
|
fs.rmtree(build_dir)
|
||||||
|
|
||||||
if not isdir(build_dir):
|
if not isdir(build_dir):
|
||||||
makedirs(build_dir)
|
makedirs(build_dir)
|
||||||
|
|
|
@ -32,6 +32,7 @@ class _Schema(vol.Schema):
|
||||||
try:
|
try:
|
||||||
res = extra(res)
|
res = extra(res)
|
||||||
except vol.Invalid as err:
|
except vol.Invalid as err:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise ensure_multiple_invalid(err)
|
raise ensure_multiple_invalid(err)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
|
||||||
try:
|
try:
|
||||||
hash(key)
|
hash(key)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise yaml.constructor.ConstructorError(
|
raise yaml.constructor.ConstructorError(
|
||||||
f'Invalid key "{key}" (not hashable)', key_node.start_mark)
|
f'Invalid key "{key}" (not hashable)', key_node.start_mark)
|
||||||
|
|
||||||
|
@ -297,7 +298,7 @@ def _load_yaml_internal(fname):
|
||||||
try:
|
try:
|
||||||
return loader.get_single_data() or OrderedDict()
|
return loader.get_single_data() or OrderedDict()
|
||||||
except yaml.YAMLError as exc:
|
except yaml.YAMLError as exc:
|
||||||
raise EsphomeError(exc)
|
raise EsphomeError(exc) from exc
|
||||||
finally:
|
finally:
|
||||||
loader.dispose()
|
loader.dispose()
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ PyYAML==5.3.1
|
||||||
paho-mqtt==1.5.0
|
paho-mqtt==1.5.0
|
||||||
colorlog==4.2.1
|
colorlog==4.2.1
|
||||||
tornado==6.0.4
|
tornado==6.0.4
|
||||||
protobuf==3.12.4
|
protobuf==3.13.0
|
||||||
tzlocal==2.1
|
tzlocal==2.1
|
||||||
pytz==2020.1
|
pytz==2020.1
|
||||||
pyserial==3.4
|
pyserial==3.4
|
||||||
ifaddr==0.1.7
|
ifaddr==0.1.7
|
||||||
platformio==4.3.4
|
platformio==5.0.1
|
||||||
esptool==2.8
|
esptool==2.8
|
||||||
click==7.1.2
|
click==7.1.2
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
pylint==2.5.3
|
pylint==2.6.0
|
||||||
flake8==3.8.3
|
flake8==3.8.3
|
||||||
pillow>4.0.0
|
pillow>4.0.0
|
||||||
cryptography>=2.0.0,<4
|
cryptography>=2.0.0,<4
|
||||||
pexpect==4.8.0
|
pexpect==4.8.0
|
||||||
|
|
||||||
# Unit tests
|
# Unit tests
|
||||||
pytest==6.0.1
|
pytest==6.0.2
|
||||||
pytest-cov==2.10.0
|
pytest-cov==2.10.0
|
||||||
pytest-mock==3.2.0
|
pytest-mock==3.3.1
|
||||||
asyncmock==0.4.2
|
asyncmock==0.4.2
|
||||||
hypothesis==5.21.0
|
hypothesis==5.21.0
|
||||||
|
|
|
@ -101,6 +101,8 @@ sensor:
|
||||||
name: "Xiaomi HHCCJCY01 Illuminance"
|
name: "Xiaomi HHCCJCY01 Illuminance"
|
||||||
conductivity:
|
conductivity:
|
||||||
name: "Xiaomi HHCCJCY01 Soil Conductivity"
|
name: "Xiaomi HHCCJCY01 Soil Conductivity"
|
||||||
|
battery_level:
|
||||||
|
name: "Xiaomi HHCCJCY01 Battery Level"
|
||||||
- platform: xiaomi_lywsdcgq
|
- platform: xiaomi_lywsdcgq
|
||||||
mac_address: 7A:80:8E:19:36:BA
|
mac_address: 7A:80:8E:19:36:BA
|
||||||
temperature:
|
temperature:
|
||||||
|
@ -280,6 +282,9 @@ text_sensor:
|
||||||
service: light.turn_on
|
service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.my_light
|
entity_id: light.my_light
|
||||||
|
- homeassistant.tag_scanned:
|
||||||
|
tag: 1234-abcd
|
||||||
|
- homeassistant.tag_scanned: 1234-abcd
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Template Text Sensor"
|
name: "Template Text Sensor"
|
||||||
lambda: |-
|
lambda: |-
|
||||||
|
|
|
@ -87,6 +87,7 @@ climate:
|
||||||
id: tuya_climate
|
id: tuya_climate
|
||||||
switch_datapoint: 1
|
switch_datapoint: 1
|
||||||
target_temperature_datapoint: 3
|
target_temperature_datapoint: 3
|
||||||
|
temperature_multiplier: 0.5
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: tuya
|
- platform: tuya
|
||||||
|
|
Loading…
Reference in a new issue