Update for 1.4.0

This commit is contained in:
Otto Winter 2018-05-06 15:56:12 +02:00
parent 7915e420f4
commit 49736c8c6d
No known key found for this signature in database
GPG key ID: DB66C0BE6013F97E
47 changed files with 437 additions and 128 deletions

View file

@ -139,7 +139,7 @@ def write_cpp(config):
ini_path = os.path.join(get_base_path(config), 'platformio.ini')
writer.write_platformio_ini(platformio_ini_s, ini_path)
code_s = indent('\n'.join(all_code))
code_s = indent('\n'.join(line.rstrip() for line in all_code))
cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp')
writer.write_cpp(code_s, cpp_path)
return 0

View file

@ -37,5 +37,4 @@ def to_code(config):
add(ads1115.set_rate(RawExpression(RATES[conf[CONF_RATE]])))
def build_flags(config):
return '-DUSE_ADS1115_SENSOR'
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View file

@ -35,5 +35,4 @@ def setup_mqtt_binary_sensor(obj, config):
setup_mqtt_component(obj, config)
def build_flags(config):
return '-DUSE_BINARY_SENSOR'
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'

View file

@ -21,5 +21,4 @@ def to_code(config):
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config)
def build_flags(config):
return '-DUSE_GPIO_BINARY_SENSOR'
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'

View file

@ -17,5 +17,4 @@ def to_code(config):
binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config)
def build_flags(config):
return '-DUSE_STATUS_BINARY_SENSOR'
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'

View file

@ -20,5 +20,4 @@ def to_code(config):
Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs)
def build_flags(config):
return '-DUSE_DALLAS_SENSOR'
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View file

@ -11,5 +11,4 @@ def to_code(config):
add(App.make_debug_component())
def build_flags(config):
return '-DUSE_DEBUG_COMPONENT'
BUILD_FLAGS = '-DUSE_DEBUG_COMPONENT'

View file

@ -38,5 +38,4 @@ def to_code(config):
add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION]))
def build_flags(config):
return '-DUSE_DEEP_SLEEP'
BUILD_FLAGS = '-DUSE_DEEP_SLEEP'

View file

@ -23,5 +23,4 @@ def setup_mqtt_fan(obj, config):
setup_mqtt_component(obj, config)
def build_flags(config):
return '-DUSE_FAN'
BUILD_FLAGS = '-DUSE_FAN'

View file

@ -2,19 +2,23 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA
from esphomeyaml.helpers import App, add
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID
from esphomeyaml.helpers import App, add, Pvariable
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('i2c'): cv.register_variable_id,
vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin,
vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin,
vol.Optional(CONF_FREQUENCY): vol.All(cv.only_on_esp32, cv.positive_int),
vol.Optional(CONF_SCAN): cv.boolean,
})
def to_code(config):
add(App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_FREQUENCY)))
rhs = App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_SCAN))
i2c = Pvariable('I2CComponent', config[CONF_ID], rhs)
if CONF_FREQUENCY in config:
add(i2c.set_frequency(config[CONF_FREQUENCY]))
def build_flags(config):
return '-DUSE_I2C'
BUILD_FLAGS = '-DUSE_I2C'

View file

@ -23,5 +23,4 @@ def to_code(config):
Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs)
def build_flags(config):
return '-DUSE_IR_TRANSMITTER'
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'

View file

@ -12,5 +12,4 @@ def setup_light_component(obj, config):
add(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
def build_flags(config):
return '-DUSE_LIGHT'
BUILD_FLAGS = '-DUSE_LIGHT'

View file

@ -6,15 +6,26 @@ import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_SSL_FINGERPRINTS, CONF_ID, CONF_LOG_TOPIC, \
CONF_MQTT, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, \
CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE
CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_KEEPALIVE
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, StructInitializer, add, \
exp_empty_optional, RawExpression
MQTT_WILL_BIRTH_SCHEMA = vol.Any(None, vol.Schema({
def validate_message_just_topic(value):
value = cv.publish_topic(value)
return {CONF_TOPIC: value}
MQTT_MESSAGE_BASE = vol.Schema({
vol.Required(CONF_TOPIC): cv.publish_topic,
vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
vol.Optional(CONF_RETAIN, default=True): cv.boolean,
})
MQTT_MESSAGE_TEMPLATE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE, validate_message_just_topic)
MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({
vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
}))
@ -47,12 +58,13 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_DISCOVERY): cv.boolean,
vol.Optional(CONF_DISCOVERY_RETAIN): cv.boolean,
vol.Optional(CONF_DISCOVERY_PREFIX): cv.publish_topic,
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic,
vol.Optional(CONF_LOG_TOPIC): cv.publish_topic,
vol.Optional(CONF_LOG_TOPIC): MQTT_MESSAGE_TEMPLATE_SCHEMA,
vol.Optional(CONF_SSL_FINGERPRINTS): vol.All(cv.only_on_esp8266,
cv.ensure_list, [validate_fingerprint]),
vol.Optional(CONF_KEEPALIVE): vol.All(cv.positive_time_period, cv.time_period_to_seconds)
})
@ -62,7 +74,7 @@ def exp_mqtt_message(config):
exp = StructInitializer(
'mqtt::MQTTMessage',
('topic', config[CONF_TOPIC]),
('payload', config[CONF_PAYLOAD]),
('payload', config.get(CONF_PAYLOAD, "")),
('qos', config[CONF_QOS]),
('retain', config[CONF_RETAIN])
)
@ -79,20 +91,34 @@ def to_code(config):
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
add(mqtt.set_discovery_info(discovery_prefix, discovery_retain))
if CONF_BIRTH_MESSAGE in config:
add(mqtt.set_birth_message(config[CONF_BIRTH_MESSAGE]))
if CONF_WILL_MESSAGE in config:
add(mqtt.set_last_will(config[CONF_WILL_MESSAGE]))
if CONF_TOPIC_PREFIX in config:
add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
if CONF_BIRTH_MESSAGE in config:
birth_message = config[CONF_BIRTH_MESSAGE]
if birth_message is None:
add(mqtt.disable_birth_message())
else:
add(mqtt.set_birth_message(exp_mqtt_message(birth_message)))
if CONF_WILL_MESSAGE in config:
will_message = config[CONF_WILL_MESSAGE]
if will_message is None:
add(mqtt.disable_last_will())
else:
add(mqtt.set_last_will(exp_mqtt_message(will_message)))
if CONF_CLIENT_ID in config:
add(mqtt.set_client_id(config[CONF_CLIENT_ID]))
if CONF_LOG_TOPIC in config:
add(mqtt.set_log_topic(config[CONF_LOG_TOPIC]))
log_topic = config[CONF_LOG_TOPIC]
if log_topic is None:
add(mqtt.disable_log_message())
else:
add(mqtt.set_log_topic(exp_mqtt_message(log_topic)))
if CONF_SSL_FINGERPRINTS in config:
for fingerprint in config[CONF_SSL_FINGERPRINTS]:
arr = [RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)]
add(mqtt.add_ssl_fingerprint(ArrayInitializer(*arr, multiline=False)))
if CONF_KEEPALIVE in config:
add(mqtt.set_keep_alive(config[CONF_KEEPALIVE]))
def required_build_flags(config):

View file

@ -45,5 +45,4 @@ def get_auth(config):
return config[CONF_OTA].get(CONF_PASSWORD, '')
def build_flags(config):
return '-DUSE_OTA'
BUILD_FLAGS = '-DUSE_OTA'

View file

@ -25,5 +25,4 @@ def setup_output_platform(obj, config, skip_power_supply=False):
add(obj.set_max_power(config[CONF_MAX_POWER]))
def build_flags(config):
return '-DUSE_OUTPUT'
BUILD_FLAGS = '-DUSE_OUTPUT'

View file

@ -28,5 +28,4 @@ def to_code(config):
output.setup_output_platform(gpio, config)
def build_flags(config):
return '-DUSE_ESP8266_PWM_OUTPUT'
BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT'

View file

@ -17,5 +17,4 @@ def to_code(config):
output.setup_output_platform(gpio, config)
def build_flags(config):
return '-DUSE_GPIO_OUTPUT'
BUILD_FLAGS = '-DUSE_GPIO_OUTPUT'

View file

@ -38,5 +38,4 @@ def to_code(config):
output.setup_output_platform(ledc, config)
def build_flags(config):
return '-DUSE_LEDC_OUTPUT'
BUILD_FLAGS = '-DUSE_LEDC_OUTPUT'

View file

@ -25,5 +25,4 @@ def to_code(config):
output.setup_output_platform(out, config, skip_power_supply=True)
def build_flags(config):
return '-DUSE_PCA9685_OUTPUT'
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'

View file

@ -33,5 +33,4 @@ def to_code(config):
add(pca9685.set_phase_balancer(phase_balancer))
def build_flags(config):
return '-DUSE_PCA9685_OUTPUT'
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'

View file

@ -0,0 +1,24 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_PCF8575
from esphomeyaml.helpers import App, Pvariable
DEPENDENCIES = ['i2c']
PCF8574_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.register_variable_id,
vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address,
vol.Optional(CONF_PCF8575, default=False): cv.boolean,
})
CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA])
def to_code(config):
for conf in config:
rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575])
Pvariable('io::PCF8574Component', conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_PCF8574'

View file

@ -25,5 +25,4 @@ def to_code(config):
add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME]))
def build_flags(config):
return '-DUSE_OUTPUT'
BUILD_FLAGS = '-DUSE_OUTPUT'

View file

@ -104,5 +104,4 @@ def register_sensor(var, config):
setup_mqtt_sensor_component(mqtt_sensor, config)
def build_flags(config):
return '-DUSE_SENSOR'
BUILD_FLAGS = '-DUSE_SENSOR'

View file

@ -36,5 +36,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
def build_flags(config):
return '-DUSE_ADC_SENSOR'
BUILD_FLAGS = '-DUSE_ADC_SENSOR'

View file

@ -59,5 +59,4 @@ def to_code(config):
sensor.register_sensor(sensor_, config)
def build_flags(config):
return '-DUSE_ADS1115_SENSOR'
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View file

@ -31,5 +31,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE])
def build_flags(config):
return '-DUSE_BMP085_SENSOR'
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'

View file

@ -34,5 +34,4 @@ def to_code(config):
sensor.register_sensor(sensor_, config)
def build_flags(config):
return '-DUSE_DALLAS_SENSOR'
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View file

@ -34,5 +34,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
def build_flags(config):
return '-DUSE_DHT_SENSOR'
BUILD_FLAGS = '-DUSE_DHT_SENSOR'

View file

@ -28,5 +28,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY])
def build_flags(config):
return '-DUSE_HDC1080_SENSOR'
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'

View file

@ -28,5 +28,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY])
def build_flags(config):
return '-DUSE_HTU21D_SENSOR'
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'

View file

@ -0,0 +1,73 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_ID_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_MQTT_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable
DEPENDENCIES = ['i2c']
CONF_ACCEL_X = 'accel_x'
CONF_ACCEL_Y = 'accel_y'
CONF_ACCEL_Z = 'accel_z'
CONF_GYRO_X = 'gyro_x'
CONF_GYRO_Y = 'gyro_y'
CONF_GYRO_Z = 'gyro_z'
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('mpu6050'): cv.register_variable_id,
vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address,
vol.Optional(CONF_ACCEL_X): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_ACCEL_Y): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_ACCEL_Z): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_X): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_Y): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_Z): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_TEMPERATURE): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
})
def to_code(config):
rhs = App.make_mpu6050_sensor(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
mpu = Pvariable('sensor::MPU6050Component', config[CONF_ID], rhs)
if CONF_ACCEL_X in config:
conf = config[CONF_ACCEL_X]
rhs = mpu.Pmake_accel_x_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_ACCEL_Y in config:
conf = config[CONF_ACCEL_Y]
rhs = mpu.Pmake_accel_y_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_ACCEL_Z in config:
conf = config[CONF_ACCEL_Z]
rhs = mpu.Pmake_accel_z_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_GYRO_X in config:
conf = config[CONF_GYRO_X]
rhs = mpu.Pmake_gyro_x_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_GYRO_Y in config:
conf = config[CONF_GYRO_Y]
rhs = mpu.Pmake_gyro_y_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_GYRO_Z in config:
conf = config[CONF_GYRO_Z]
rhs = mpu.Pmake_gyro_z_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE]
rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050TemperatureSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
BUILD_FLAGS = '-DUSE_MPU6050'

View file

@ -59,5 +59,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
def build_flags(config):
return '-DUSE_PULSE_COUNTER_SENSOR'
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'

View file

@ -33,5 +33,4 @@ def to_code(config):
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
def build_flags(config):
return '-DUSE_ULTRASONIC_SENSOR'
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'

View file

@ -33,5 +33,4 @@ def register_switch(var, config):
setup_mqtt_switch(mqtt_switch, config)
def build_flags(config):
return '-DUSE_SWITCH'
BUILD_FLAGS = '-DUSE_SWITCH'

View file

@ -19,5 +19,4 @@ def to_code(config):
switch.setup_mqtt_switch(gpio.Pmqtt, config)
def build_flags(config):
return '-DUSE_GPIO_SWITCH'
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'

View file

@ -92,5 +92,4 @@ def to_code(config):
switch.register_switch(switch_, config)
def build_flags(config):
return '-DUSE_IR_TRANSMITTER'
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'

View file

@ -0,0 +1,22 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('output_switch'): cv.register_variable_id,
vol.Required(CONF_ID): cv.variable_id,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
def to_code(config):
output = get_variable(config[CONF_ID])
rhs = App.make_simple_switch(config[CONF_NAME], output)
gpio = variable('Application::MakeSimpleSwitch', config[CONF_ID], rhs)
switch.setup_switch(gpio.Pswitch_, config)
switch.setup_mqtt_switch(gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_SIMPLE_SWITCH'

View file

@ -15,5 +15,4 @@ def to_code(config):
switch.setup_mqtt_switch(restart.Pmqtt, config)
def build_flags(config):
return '-DUSE_RESTART_SWITCH'
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'

View file

@ -0,0 +1,19 @@
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('shutdown_switch'): cv.register_variable_id,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
def to_code(config):
rhs = App.make_shutdown_switch(config[CONF_NAME])
shutdown = variable('Application::MakeShutdownSwitch', config[CONF_ID],
rhs)
switch.setup_switch(shutdown.Pshutdown, config)
switch.setup_mqtt_switch(shutdown.Pmqtt, config)
BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH'

View file

@ -25,5 +25,4 @@ def to_code(config):
add(web_server.set_js_url(config[CONF_JS_URL]))
def build_flags(config):
return '-DUSE_WEB_SERVER'
BUILD_FLAGS = '-DUSE_WEB_SERVER'

View file

@ -190,6 +190,9 @@ def _format_config_error(ex, domain, config):
else:
message += u'{}.'.format(humanize_error(config, ex))
if isinstance(config, list):
return message
domain_config = config.get(domain, config)
message += u" (See {}, line {}). ".format(
getattr(domain_config, '__config_file__', '?'),
@ -203,6 +206,7 @@ def load_config(path):
config = yaml_util.load_yaml(path)
except OSError:
raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path))
core.RAW_CONFIG = config
if CONF_ESPHOMEYAML not in config:
raise ESPHomeYAMLError(u"No esphomeyaml section in config")

View file

@ -3,6 +3,7 @@
from __future__ import print_function
import logging
import re
from datetime import timedelta
import voluptuous as vol
@ -192,30 +193,65 @@ time_period_dict = vol.All(
lambda value: timedelta(**value))
def time_period_str(value):
"""Validate and transform time offset."""
def time_period_str_colon(value):
"""Validate and transform time offset with format HH:MM[:SS]."""
if isinstance(value, int):
raise vol.Invalid("Make sure you wrap time values in quotes")
elif not isinstance(value, (str, unicode)):
raise vol.Invalid('Make sure you wrap time values in quotes')
elif not isinstance(value, str):
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
value = unicode(value)
if value.endswith(u'ms'):
return vol.Coerce(int)(value[:-2])
elif value.endswith(u's'):
return vol.Coerce(float)(value[:-1]) * 1000
elif value.endswith(u'min'):
return vol.Coerce(float)(value[:-3]) * 1000 * 60
elif value.endswith(u'h'):
return vol.Coerce(float)(value[:-1]) * 1000 * 60 * 60
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
negative_offset = False
if value.startswith('-'):
negative_offset = True
value = value[1:]
elif value.startswith('+'):
value = value[1:]
def time_period_milliseconds(value):
try:
return timedelta(milliseconds=int(value))
except (ValueError, TypeError):
raise vol.Invalid('Expected milliseconds, got {}'.format(value))
parsed = [int(x) for x in value.split(':')]
except ValueError:
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
if len(parsed) == 2:
hour, minute = parsed
second = 0
elif len(parsed) == 3:
hour, minute, second = parsed
else:
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
offset = timedelta(hours=hour, minutes=minute, seconds=second)
if negative_offset:
offset *= -1
return offset
def time_period_str_unit(value):
"""Validate and transform time period with time unit and integer value."""
if isinstance(value, int):
value = str(value)
elif not isinstance(value, str):
raise vol.Invalid("Expected string for time period with unit.")
unit_to_kwarg = {
'ms': 'milliseconds',
's': 'seconds',
'sec': 'seconds',
'min': 'minutes',
'h': 'hours',
'd': 'days',
}
match = re.match(r"^([-+]?\d+)\s*(\w*)$", value)
if match is None or match.group(2) not in unit_to_kwarg:
raise vol.Invalid(u"Expected time period with unit, "
u"got {}".format(value))
kwarg = unit_to_kwarg[match.group(2)]
return timedelta(**{kwarg: int(match.group(1))})
def time_period_to_milliseconds(value):
@ -223,11 +259,18 @@ def time_period_to_milliseconds(value):
return value
if isinstance(value, float):
return int(value)
return value / timedelta(milliseconds=1)
return int(value.total_seconds() * 1000)
time_period = vol.All(vol.Any(time_period_str, timedelta, time_period_dict,
time_period_milliseconds), time_period_to_milliseconds)
def time_period_to_seconds(value):
if value / 1000 != value // 1000:
raise vol.Invalid("Fractions of seconds are not supported here.")
return value / 1000
time_period = vol.All(vol.Any(time_period_str_colon, time_period_str_unit, timedelta,
time_period_dict),
time_period_to_milliseconds)
positive_time_period = vol.All(time_period, vol.Range(min=0))
positive_not_null_time_period = vol.All(time_period, vol.Range(min=0, min_included=False))
@ -294,17 +337,62 @@ def ipv4(value):
return IPAddress(*parts_)
def publish_topic(value):
value = string_strict(value)
if value.endswith('/'):
raise vol.Invalid("Publish topic can't end with '/'")
if '+' in value or '#' in value:
raise vol.Invalid("Publish topic can't contain '+' or '#'")
def _valid_topic(value):
"""Validate that this is a valid topic name/filter."""
if isinstance(value, dict):
raise vol.Invalid("Can't use dictionary with topic")
value = string(value)
try:
raw_value = value.encode('utf-8')
except UnicodeError:
raise vol.Invalid("MQTT topic name/filter must be valid UTF-8 string.")
if not raw_value:
raise vol.Invalid("MQTT topic name/filter must not be empty.")
if len(raw_value) > 65535:
raise vol.Invalid("MQTT topic name/filter must not be longer than "
"65535 encoded bytes.")
if '\0' in value:
raise vol.Invalid("MQTT topic name/filter must not contain null "
"character.")
return value
subscribe_topic = string_strict # TODO improve this
mqtt_payload = string # TODO improve this
def subscribe_topic(value):
"""Validate that we can subscribe using this MQTT topic."""
value = _valid_topic(value)
for i in (i for i, c in enumerate(value) if c == '+'):
if (i > 0 and value[i - 1] != '/') or \
(i < len(value) - 1 and value[i + 1] != '/'):
raise vol.Invalid("Single-level wildcard must occupy an entire "
"level of the filter")
index = value.find('#')
if index != -1:
if index != len(value) - 1:
# If there are multiple wildcards, this will also trigger
raise vol.Invalid("Multi-level wildcard must be the last "
"character in the topic filter.")
if len(value) > 1 and value[index - 1] != '/':
raise vol.Invalid("Multi-level wildcard must be after a topic "
"level separator.")
return value
def publish_topic(value):
"""Validate that we can publish using this MQTT topic."""
value = _valid_topic(value)
if '+' in value or '#' in value:
raise vol.Invalid("Wildcards can not be used in topic names")
return value
def mqtt_payload(value):
if value is None:
return ''
return string(value)
uint8_t = vol.All(int_, vol.Range(min=0, max=255))
uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295))

View file

@ -161,6 +161,9 @@ CONF_AP = 'ap'
CONF_CSS_URL = 'css_url'
CONF_JS_URL = 'js_url'
CONF_SSL_FINGERPRINTS = 'ssl_fingerprints'
CONF_PCF8575 = 'pcf8575'
CONF_SCAN = 'scan'
CONF_KEEPALIVE = 'keepalive'
ESP32_BOARDS = [
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',

View file

@ -22,3 +22,4 @@ CONFIG_PATH = None
SIMPLIFY = True
ESP_PLATFORM = ''
BOARD = ''
RAW_CONFIG = None

View file

@ -370,6 +370,18 @@ def get_gpio_pin_number(conf):
def exp_gpio_pin_(obj, conf, default_mode):
if isinstance(conf, int):
return conf
if 'pcf8574' in conf:
hub = get_variable(conf['pcf8574'])
if default_mode == u'INPUT':
return hub.make_input_pin(conf[CONF_NUMBER],
RawExpression('PCF8574_' + conf[CONF_MODE]),
conf[CONF_INVERTED])
elif default_mode == u'OUTPUT':
return hub.make_output_pin(conf[CONF_NUMBER], conf[CONF_INVERTED])
else:
raise ESPHomeYAMLError(u"Unknown default mode {}".format(default_mode))
if conf.get(CONF_INVERTED) is None:
return obj(conf[CONF_NUMBER], conf.get(CONF_MODE))
return obj(conf[CONF_NUMBER], RawExpression(conf.get(CONF_MODE, default_mode)),

View file

@ -5,7 +5,7 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.const import ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_NUMBER, CONF_MODE, \
CONF_INVERTED
CONF_INVERTED, CONF_ID, CONF_PCF8575
_LOGGER = logging.getLogger(__name__)
@ -72,7 +72,7 @@ def _translate_pin(value):
except ValueError:
pass
if value.startswith('GPIO'):
return vol.Coerce(int)(value[len('GPIO'):])
return vol.Coerce(int)(value[len('GPIO'):].strip())
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if value in ESP32_PINS:
return ESP32_PINS[value]
@ -179,19 +179,57 @@ def pin_mode(value):
raise vol.Invalid(u"Invalid ESP platform.")
GPIO_PIN_SCHEMA = vol.Schema({
vol.Required(CONF_NUMBER): gpio_pin,
vol.Required(CONF_MODE): pin_mode,
vol.Optional(CONF_INVERTED): cv.boolean,
})
def pcf8574_pin(value, default_mode):
if 'pcf8574' not in core.RAW_CONFIG:
raise vol.Invalid("PCF8574 not loaded, ignore this.")
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(output_pin, vol.Schema({
if isinstance(value, (str, unicode)):
value = {CONF_NUMBER: value}
if not isinstance(value, dict) or not isinstance(value.get(CONF_NUMBER), (str, unicode)) or \
value[CONF_NUMBER].count('.') != 1:
raise vol.Invalid("Not PCF8574 pin")
pcf_id, pin = value[CONF_NUMBER].split('.')
pin = vol.Coerce(int)(pin)
pcf_conf = cv.ensure_list(core.RAW_CONFIG['pcf8574'])
pcf = next((conf for conf in pcf_conf if conf[CONF_ID] == pcf_id), None)
if pcf is None:
raise vol.Invalid("Unknown PCF8574 id: {}".format(pcf_id))
if pcf.get(CONF_PCF8575, False):
pin = vol.Range(min=0, max=15)(pin)
else:
pin = vol.Range(min=0, max=7)(pin)
mode = vol.All(vol.Coerce(str), vol.Upper)(value.get(CONF_MODE, default_mode))
if mode not in ['INPUT', 'INPUT_PULLUP', 'OUTPUT']:
raise vol.Invalid("Invalid pin mode for PCF8575: {}".format(mode))
return {
'pcf8574': pcf[CONF_ID],
CONF_NUMBER: pin,
CONF_INVERTED: value.get(CONF_INVERTED, False),
CONF_MODE: mode
}
def pcf8574_output_pin(value):
return pcf8574_pin(value, 'OUTPUT')
def pcf8574_input_pin(value):
return pcf8574_pin(value, 'INPUT')
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(output_pin, pcf8574_output_pin, vol.Schema({
vol.Required(CONF_NUMBER): output_pin,
vol.Optional(CONF_MODE): pin_mode,
vol.Optional(CONF_INVERTED): cv.boolean,
}))
GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, vol.Schema({
GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, pcf8574_input_pin, vol.Schema({
vol.Required(CONF_NUMBER): input_pin,
vol.Optional(CONF_MODE): pin_mode,
vol.Optional(CONF_INVERTED): cv.boolean,

View file

@ -4,6 +4,7 @@ import codecs
import errno
import os
from esphomeyaml import core
from esphomeyaml.config import iter_components
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_NAME, \
CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
@ -50,7 +51,7 @@ platform = {platform}
board = {board}
framework = arduino
lib_deps =
{esphomeyaml_uri}
{lib_deps}
${{common.lib_deps}}
build_flags =
{build_flags}
@ -68,7 +69,9 @@ def get_build_flags(config, key):
for _, component, conf in iter_components(config):
if not hasattr(component, key):
continue
flags = getattr(component, key)(conf)
flags = getattr(component, key)
if callable(flags):
flags = flags(conf)
if flags is None:
continue
if isinstance(flags, (str, unicode)):
@ -85,12 +88,12 @@ def get_ini_content(config):
u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
u'platform': platform,
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
u'esphomeyaml_uri': config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI],
u'build_flags': u'',
}
build_flags = set()
if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]:
build_flags |= get_build_flags(config, 'build_flags')
build_flags |= get_build_flags(config, 'BUILD_FLAGS')
build_flags.add(u"-DESPHOMEYAML_USE")
build_flags |= get_build_flags(config, 'required_build_flags')
@ -98,6 +101,31 @@ def get_ini_content(config):
build_flags = sorted(list(build_flags))
if build_flags:
options[u'build_flags'] = u'\n '.join(build_flags)
lib_deps = set()
lib_deps.add(config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI])
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
lib_deps |= {
'ArduinoOTA',
'Update',
'ESPmDNS',
'Wire',
'FS',
'Preferences',
}
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
lib_deps |= {
'ESP8266WiFi',
'Wire',
'Hash',
'ESP8266mDNS',
'ArduinoOTA',
}
else:
raise ESPHomeYAMLError("Unsupported platform {}".format(core.ESP_PLATFORM))
options[u'lib_deps'] = u'\n '.join(sorted(list(lib_deps)))
return INI_CONTENT_FORMAT.format(**options)