diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index 083600e65f..f73859a4e1 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -6,19 +6,16 @@ import os import random import sys -from esphomeyaml import helpers, mqtt, writer, yaml_util, wizard -from esphomeyaml.config import add_component_task, read_config -from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_MANUAL_IP, CONF_NAME, \ - CONF_STATIC_IP, \ - CONF_WIFI, CONF_LOGGER, CONF_BAUD_RATE -from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, \ - get_variable, indent, quote, statement, color +from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const +from esphomeyaml.config import core_to_code, get_component, iter_components, read_config +from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOGGER, \ + CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI +from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \ + color, get_variable, indent, quote, statement, Expression _LOGGER = logging.getLogger(__name__) -PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'i2c'] - -CONFIG_PATH = None +PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c'] def get_name(config): @@ -26,7 +23,7 @@ def get_name(config): def get_base_path(config): - return os.path.join(os.path.dirname(CONFIG_PATH), get_name(config)) + return os.path.join(os.path.dirname(core.CONFIG_PATH), get_name(config)) def discover_serial_ports(): @@ -47,8 +44,6 @@ def discover_serial_ports(): if not result: return None - if len(result) == 1: - return result[0] print(u"Found multiple serial port options, please choose one:") for i, (res, desc) in enumerate(zip(result, descs)): print(u" [{}] {} ({})".format(i, res, desc)) @@ -107,18 +102,24 @@ def run_miniterm(config, port): def write_cpp(config): _LOGGER.info("Generating C++ source...") + + add_task(core_to_code, config[CONF_ESPHOMEYAML]) for domain in PRE_INITIALIZE: + if domain == CONF_ESPHOMEYAML: + continue if domain in config: - add_component_task(domain, config[domain]) + add_task(get_component(domain).to_code, config[domain]) # Clear queue get_variable(None) add(RawStatement('')) - for domain, conf in config.iteritems(): + for domain, component, conf in iter_components(config): if domain in PRE_INITIALIZE: continue - add_component_task(domain, conf) + if not hasattr(component, 'to_code'): + continue + add_task(component.to_code, conf) # Clear queue get_variable(None) @@ -127,8 +128,11 @@ def write_cpp(config): all_code = [] for exp in _EXPRESSIONS: - if helpers.SIMPLIFY and isinstance(exp, AssignmentExpression) and exp.obj.usages == 0: - exp = exp.rhs + if core.SIMPLIFY: + if isinstance(exp, Expression) and not exp.required: + continue + if isinstance(exp, AssignmentExpression) and not exp.obj.required: + exp = exp.rhs all_code.append(unicode(statement(exp))) platformio_ini_s = writer.get_ini_content(config) @@ -211,8 +215,6 @@ def setup_log(): def main(): - global CONFIG_PATH - setup_log() parser = argparse.ArgumentParser(prog='esphomeyaml') @@ -260,13 +262,17 @@ def main(): subparsers.add_parser('wizard', help="A helpful setup wizard that will guide " "you through setting up esphomeyaml.") + subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.") + subparsers.add_parser('version', help="Print the esphomeyaml version and exit.") + args = parser.parse_args() if args.command == 'wizard': return wizard.wizard(args.configuration) - CONFIG_PATH = args.configuration - config = read_config(CONFIG_PATH) + core.CONFIG_PATH = args.configuration + + config = read_config(core.CONFIG_PATH) if config is None: return 1 @@ -294,6 +300,8 @@ def main(): return show_logs(config, args, port) elif args.command == 'clean-mqtt': return clean_mqtt(config, args) + elif args.command == 'mqtt-fingerprint': + return mqtt.get_fingerprint(config) elif args.command == 'run': exit_code = write_cpp(config) if exit_code != 0: @@ -310,6 +318,9 @@ def main(): return exit_code _LOGGER.info(u"Successfully uploaded program.") return show_logs(config, args, port) + elif args.command == 'version': + print(u"Version: {}".format(const.__version__)) + return 0 print(u"Unknown command {}".format(args.command)) return 1 diff --git a/esphomeyaml/components/ads1115.py b/esphomeyaml/components/ads1115.py index af179f6a19..7204ca6cf4 100644 --- a/esphomeyaml/components/ads1115.py +++ b/esphomeyaml/components/ads1115.py @@ -35,3 +35,7 @@ def to_code(config): ads1115 = Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs) if CONF_RATE in conf: add(ads1115.set_rate(RawExpression(RATES[conf[CONF_RATE]]))) + + +def build_flags(config): + return '-DUSE_ADS1115_SENSOR' diff --git a/esphomeyaml/components/binary_sensor/__init__.py b/esphomeyaml/components/binary_sensor/__init__.py index b6dae25013..05371135f7 100644 --- a/esphomeyaml/components/binary_sensor/__init__.py +++ b/esphomeyaml/components/binary_sensor/__init__.py @@ -4,10 +4,6 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED from esphomeyaml.helpers import add, setup_mqtt_component -PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_INVERTED): cv.boolean, -}) - DEVICE_CLASSES = [ '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', 'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy', @@ -17,13 +13,27 @@ DEVICE_CLASSES = [ DEVICE_CLASSES_MSG = "Unknown device class. Must be one of {}".format(', '.join(DEVICE_CLASSES)) -MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ +PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_INVERTED): cv.boolean, vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, vol.Any(*DEVICE_CLASSES, msg=DEVICE_CLASSES_MSG)), }) +MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ -def setup_mqtt_binary_sensor(obj, config, skip_device_class=False): - if not skip_device_class and CONF_DEVICE_CLASS in config: +}) + + +def setup_binary_sensor(obj, config): + if CONF_DEVICE_CLASS in config: add(obj.set_device_class(config[CONF_DEVICE_CLASS])) + if CONF_INVERTED in config: + add(obj.set_inverted(config[CONF_INVERTED])) + + +def setup_mqtt_binary_sensor(obj, config): setup_mqtt_component(obj, config) + + +def build_flags(config): + return '-DUSE_BINARY_SENSOR' diff --git a/esphomeyaml/components/binary_sensor/gpio.py b/esphomeyaml/components/binary_sensor/gpio.py index 2dda302204..20ccb31814 100644 --- a/esphomeyaml/components/binary_sensor/gpio.py +++ b/esphomeyaml/components/binary_sensor/gpio.py @@ -3,7 +3,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import binary_sensor -from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN +from esphomeyaml.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN from esphomeyaml.helpers import App, add, exp_gpio_input_pin, variable PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ @@ -13,9 +13,13 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_gpio_binary_sensor(exp_gpio_input_pin(config[CONF_PIN]), - config[CONF_NAME], config.get(CONF_DEVICE_CLASS)) - gpio = variable('Application::SimpleBinarySensor', config[CONF_ID], rhs) + rhs = App.make_gpio_binary_sensor(config[CONF_NAME], exp_gpio_input_pin(config[CONF_PIN])) + gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs) if CONF_INVERTED in config: add(gpio.Pgpio.set_inverted(config[CONF_INVERTED])) - binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config, skip_device_class=True) + binary_sensor.setup_binary_sensor(gpio.Pgpio, config) + binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_GPIO_BINARY_SENSOR' diff --git a/esphomeyaml/components/binary_sensor/status.py b/esphomeyaml/components/binary_sensor/status.py index bd6685b74b..749f0f72dc 100644 --- a/esphomeyaml/components/binary_sensor/status.py +++ b/esphomeyaml/components/binary_sensor/status.py @@ -1,7 +1,9 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor from esphomeyaml.const import CONF_ID, CONF_NAME -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.helpers import App, variable + +DEPENDENCIES = ['mqtt'] PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID('status_binary_sensor'): cv.register_variable_id, @@ -10,5 +12,10 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ def to_code(config): rhs = App.make_status_binary_sensor(config[CONF_NAME]) - gpio = Pvariable('binary_sensor::MQTTBinarySensorComponent', config[CONF_ID], rhs) - binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config) + status = variable('Application::MakeStatusBinarySensor', config[CONF_ID], rhs) + binary_sensor.setup_binary_sensor(status.Pstatus, config) + binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_STATUS_BINARY_SENSOR' diff --git a/esphomeyaml/components/dallas.py b/esphomeyaml/components/dallas.py index 87852104e2..752c0ce7e6 100644 --- a/esphomeyaml/components/dallas.py +++ b/esphomeyaml/components/dallas.py @@ -18,3 +18,7 @@ def to_code(config): for conf in config: rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL)) Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs) + + +def build_flags(config): + return '-DUSE_DALLAS_SENSOR' diff --git a/esphomeyaml/components/debug.py b/esphomeyaml/components/debug.py new file mode 100644 index 0000000000..c20969dc4c --- /dev/null +++ b/esphomeyaml/components/debug.py @@ -0,0 +1,15 @@ +import voluptuous as vol + +from esphomeyaml.helpers import App, add + +DEPENDENCIES = ['logger'] + +CONFIG_SCHEMA = vol.Schema({}) + + +def to_code(config): + add(App.make_debug_component()) + + +def build_flags(config): + return '-DUSE_DEBUG_COMPONENT' diff --git a/esphomeyaml/components/deep_sleep.py b/esphomeyaml/components/deep_sleep.py new file mode 100644 index 0000000000..3ed50ae369 --- /dev/null +++ b/esphomeyaml/components/deep_sleep.py @@ -0,0 +1,45 @@ +import voluptuous as vol + +from esphomeyaml import config_validation as cv, pins +from esphomeyaml.const import CONF_SLEEP_DURATION, CONF_WAKEUP_PIN, CONF_RUN_CYCLES, \ + CONF_RUN_DURATION, CONF_ID, CONF_NUMBER +from esphomeyaml.helpers import App, add, Pvariable, exp_gpio_input_pin + +DEPENDENCIES = ['logger'] + + +def validate_pin_number(value): + valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 39] + if value not in valid_pins: + raise vol.Invalid(u"Only pins {} support wakeup".format( + ', '.join(str(x) for x in valid_pins)) + ) + return value + + +CONFIG_SCHEMA = vol.Schema({ + cv.GenerateID('deep_sleep'): cv.register_variable_id, + vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period, + vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INPUT_PIN_SCHEMA, + pins.schema_validate_number(validate_pin_number)), + vol.Optional(CONF_RUN_CYCLES): cv.positive_int, + vol.Optional(CONF_RUN_DURATION): cv.positive_time_period, +}) + + +def to_code(config): + rhs = App.make_deep_sleep_component() + deep_sleep = Pvariable('DeepSleepComponent', config[CONF_ID], rhs) + if CONF_SLEEP_DURATION in config: + add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION])) + if CONF_WAKEUP_PIN in config: + pin = exp_gpio_input_pin(config[CONF_WAKEUP_PIN]) + add(deep_sleep.set_wakeup_pin(pin)) + if CONF_RUN_CYCLES in config: + add(deep_sleep.set_run_cycles(config[CONF_RUN_CYCLES])) + if CONF_RUN_DURATION in config: + add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION])) + + +def build_flags(config): + return '-DUSE_DEEP_SLEEP' diff --git a/esphomeyaml/components/fan/__init__.py b/esphomeyaml/components/fan/__init__.py index 6237857f25..ddf4861da4 100644 --- a/esphomeyaml/components/fan/__init__.py +++ b/esphomeyaml/components/fan/__init__.py @@ -21,3 +21,7 @@ def setup_mqtt_fan(obj, config): if CONF_SPEED_COMMAND_TOPIC in config: add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC])) setup_mqtt_component(obj, config) + + +def build_flags(config): + return '-DUSE_FAN' diff --git a/esphomeyaml/components/fan/binary.py b/esphomeyaml/components/fan/binary.py index 044bef557d..b62dc9f1ba 100644 --- a/esphomeyaml/components/fan/binary.py +++ b/esphomeyaml/components/fan/binary.py @@ -15,7 +15,7 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({ def to_code(config): output = get_variable(config[CONF_OUTPUT]) rhs = App.make_fan(config[CONF_NAME]) - fan_struct = variable('Application::FanStruct', config[CONF_ID], rhs) + fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs) add(fan_struct.Poutput.set_binary(output)) if CONF_OSCILLATION_OUTPUT in config: oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT]) diff --git a/esphomeyaml/components/fan/speed.py b/esphomeyaml/components/fan/speed.py index 1b18cba13f..0d94aacac7 100644 --- a/esphomeyaml/components/fan/speed.py +++ b/esphomeyaml/components/fan/speed.py @@ -24,7 +24,7 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({ def to_code(config): output = get_variable(config[CONF_OUTPUT]) rhs = App.make_fan(config[CONF_NAME]) - fan_struct = variable('Application::FanStruct', config[CONF_ID], rhs) + fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs) if CONF_SPEED in config: speeds = config[CONF_SPEED] add(fan_struct.Poutput.set_speed(output, 0.0, diff --git a/esphomeyaml/components/i2c.py b/esphomeyaml/components/i2c.py index a052245cd3..459f29e7c2 100644 --- a/esphomeyaml/components/i2c.py +++ b/esphomeyaml/components/i2c.py @@ -14,3 +14,7 @@ CONFIG_SCHEMA = vol.Schema({ def to_code(config): add(App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_FREQUENCY))) + + +def build_flags(config): + return '-DUSE_I2C' diff --git a/esphomeyaml/components/ir_transmitter.py b/esphomeyaml/components/ir_transmitter.py index d20a35f69a..b1be71bf29 100644 --- a/esphomeyaml/components/ir_transmitter.py +++ b/esphomeyaml/components/ir_transmitter.py @@ -21,3 +21,7 @@ def to_code(config): pin = exp_gpio_output_pin(conf[CONF_PIN]) rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT)) Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs) + + +def build_flags(config): + return '-DUSE_IR_TRANSMITTER' diff --git a/esphomeyaml/components/light/__init__.py b/esphomeyaml/components/light/__init__.py index ec0b02ba75..8ac678537a 100644 --- a/esphomeyaml/components/light/__init__.py +++ b/esphomeyaml/components/light/__init__.py @@ -1,13 +1,16 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH -from esphomeyaml.helpers import add, setup_mqtt_component +from esphomeyaml.helpers import add PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema) -def setup_mqtt_light_component(obj, config): +def setup_light_component(obj, config): if CONF_DEFAULT_TRANSITION_LENGTH in config: add(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH])) - setup_mqtt_component(obj, config) + + +def build_flags(config): + return '-DUSE_LIGHT' diff --git a/esphomeyaml/components/light/binary.py b/esphomeyaml/components/light/binary.py index b8688f9cd7..4cc808a74f 100644 --- a/esphomeyaml/components/light/binary.py +++ b/esphomeyaml/components/light/binary.py @@ -4,7 +4,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import light from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT -from esphomeyaml.helpers import App, get_variable, variable +from esphomeyaml.helpers import App, get_variable, variable, setup_mqtt_component PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ cv.GenerateID('binary_light'): cv.register_variable_id, @@ -15,5 +15,6 @@ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ def to_code(config): output = get_variable(config[CONF_OUTPUT]) rhs = App.make_binary_light(config[CONF_NAME], output) - light_struct = variable('Application::LightStruct', config[CONF_ID], rhs) - light.setup_mqtt_light_component(light_struct.Pmqtt, config) + light_struct = variable('Application::MakeLight', config[CONF_ID], rhs) + setup_mqtt_component(light_struct.Pmqtt, config) + light.setup_light_component(light_struct.Pstate, config) diff --git a/esphomeyaml/components/light/monochromatic.py b/esphomeyaml/components/light/monochromatic.py index e9dce19b46..d7a0878bf8 100644 --- a/esphomeyaml/components/light/monochromatic.py +++ b/esphomeyaml/components/light/monochromatic.py @@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import light from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \ CONF_NAME, CONF_OUTPUT -from esphomeyaml.helpers import App, add, get_variable, variable +from esphomeyaml.helpers import App, add, get_variable, variable, setup_mqtt_component PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ cv.GenerateID('monochromatic_light'): cv.register_variable_id, @@ -17,7 +17,8 @@ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ def to_code(config): output = get_variable(config[CONF_OUTPUT]) rhs = App.make_monochromatic_light(config[CONF_NAME], output) - light_struct = variable('Application::LightStruct', config[CONF_ID], rhs) + light_struct = variable('Application::MakeLight', config[CONF_ID], rhs) if CONF_GAMMA_CORRECT in config: add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT])) - light.setup_mqtt_light_component(light_struct.Pmqtt, config) + setup_mqtt_component(light_struct.Pmqtt, config) + light.setup_light_component(light_struct.Pstate, config) diff --git a/esphomeyaml/components/light/rgb.py b/esphomeyaml/components/light/rgb.py index eb8496bb5c..d94ea09ddf 100644 --- a/esphomeyaml/components/light/rgb.py +++ b/esphomeyaml/components/light/rgb.py @@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import light from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED -from esphomeyaml.helpers import App, add, get_variable, variable +from esphomeyaml.helpers import App, add, get_variable, variable, setup_mqtt_component PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ cv.GenerateID('rgb_light'): cv.register_variable_id, @@ -21,7 +21,8 @@ def to_code(config): green = get_variable(config[CONF_GREEN]) blue = get_variable(config[CONF_BLUE]) rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue) - light_struct = variable('Application::LightStruct', config[CONF_ID], rhs) + light_struct = variable('Application::MakeLight', config[CONF_ID], rhs) if CONF_GAMMA_CORRECT in config: add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT])) - light.setup_mqtt_light_component(light_struct.Pmqtt, config) + setup_mqtt_component(light_struct.Pmqtt, config) + light.setup_light_component(light_struct.Pstate, config) diff --git a/esphomeyaml/components/light/rgbw.py b/esphomeyaml/components/light/rgbw.py index 4a2247adf6..75190141e1 100644 --- a/esphomeyaml/components/light/rgbw.py +++ b/esphomeyaml/components/light/rgbw.py @@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import light from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE -from esphomeyaml.helpers import App, get_variable, variable, add +from esphomeyaml.helpers import App, add, get_variable, setup_mqtt_component, variable PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ cv.GenerateID('rgbw_light'): cv.register_variable_id, @@ -23,7 +23,8 @@ def to_code(config): blue = get_variable(config[CONF_BLUE]) white = get_variable(config[CONF_WHITE]) rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white) - light_struct = variable('Application::LightStruct', config[CONF_ID], rhs) + light_struct = variable('Application::MakeLight', config[CONF_ID], rhs) if CONF_GAMMA_CORRECT in config: add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT])) - light.setup_mqtt_light_component(light_struct.Pmqtt, config) + setup_mqtt_component(light_struct.Pmqtt, config) + light.setup_light_component(light_struct.Pstate, config) diff --git a/esphomeyaml/components/logger.py b/esphomeyaml/components/logger.py index 70993ccb55..43eda613d3 100644 --- a/esphomeyaml/components/logger.py +++ b/esphomeyaml/components/logger.py @@ -2,20 +2,18 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGGER, CONF_LOGS, \ - CONF_LOG_TOPIC, CONF_TX_BUFFER_SIZE + CONF_TX_BUFFER_SIZE from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import App, Pvariable, RawExpression, add, exp_empty_optional +from esphomeyaml.helpers import App, Pvariable, RawExpression, add LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE'] - # pylint: disable=invalid-name is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS)) -CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(CONF_LOGGER): cv.register_variable_id, vol.Optional(CONF_BAUD_RATE): cv.positive_int, - vol.Optional(CONF_LOG_TOPIC): vol.Any(None, '', cv.publish_topic), vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int, vol.Optional(CONF_LEVEL): is_log_level, vol.Optional(CONF_LOGS): vol.Schema({ @@ -33,16 +31,7 @@ def exp_log_level(level): def to_code(config): - baud_rate = config.get(CONF_BAUD_RATE) - if baud_rate is None and CONF_LOG_TOPIC in config: - baud_rate = 115200 - log_topic = None - if CONF_LOG_TOPIC in config: - if not config[CONF_LOG_TOPIC]: - log_topic = exp_empty_optional(u'std::string') - else: - log_topic = config[CONF_LOG_TOPIC] - rhs = App.init_log(baud_rate, log_topic) + rhs = App.init_log(config.get(CONF_BAUD_RATE)) log = Pvariable(u'LogComponent', config[CONF_ID], rhs) if CONF_TX_BUFFER_SIZE in config: add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE])) @@ -56,7 +45,7 @@ def to_code(config): add(log.set_log_level(tag, exp_log_level(level))) -def get_build_flags(config): +def build_flags(config): if CONF_LEVEL in config: return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL])) - return u'' + return None diff --git a/esphomeyaml/components/mqtt.py b/esphomeyaml/components/mqtt.py index d3cf3abece..0dc7ecdae8 100644 --- a/esphomeyaml/components/mqtt.py +++ b/esphomeyaml/components/mqtt.py @@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_DISCOVERY, \ CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_MQTT, CONF_PASSWORD, \ CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME, \ - CONF_WILL_MESSAGE, CONF_CLIENT_ID + CONF_WILL_MESSAGE, CONF_CLIENT_ID, CONF_LOG_TOPIC from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, exp_empty_optional MQTT_WILL_BIRTH_SCHEMA = vol.Any(None, vol.Schema({ @@ -27,7 +27,7 @@ def validate_broker(value): return value -CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(CONF_MQTT): cv.register_variable_id, vol.Required(CONF_BROKER): validate_broker, vol.Optional(CONF_PORT, default=1883): cv.port, @@ -40,6 +40,7 @@ CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic, + vol.Optional(CONF_LOG_TOPIC): cv.publish_topic, }) @@ -74,3 +75,5 @@ def to_code(config): add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX])) 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])) diff --git a/esphomeyaml/components/ota.py b/esphomeyaml/components/ota.py index 4225c4ebac..dd50810de9 100644 --- a/esphomeyaml/components/ota.py +++ b/esphomeyaml/components/ota.py @@ -4,14 +4,15 @@ import logging import voluptuous as vol import esphomeyaml.config_validation as cv +from esphomeyaml import core from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \ - ESP_PLATFORM_ESP8266, ESP_PLATFORM_ESP32 + ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, Pvariable, add _LOGGER = logging.getLogger(__name__) -CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(CONF_OTA): cv.register_variable_id, vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean, # TODO Num attempts + wait time @@ -33,12 +34,16 @@ def to_code(config): def get_port(config): if CONF_PORT in config[CONF_OTA]: return config[CONF_OTA][CONF_PORT] - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: return 3232 - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: return 8266 raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.") def get_auth(config): return config[CONF_OTA].get(CONF_PASSWORD, '') + + +def build_flags(config): + return '-DUSE_OTA' diff --git a/esphomeyaml/components/output/__init__.py b/esphomeyaml/components/output/__init__.py index ff6281191a..2cc957d243 100644 --- a/esphomeyaml/components/output/__init__.py +++ b/esphomeyaml/components/output/__init__.py @@ -23,3 +23,7 @@ def setup_output_platform(obj, config, skip_power_supply=False): add(obj.set_power_supply(power_supply)) if CONF_MAX_POWER in config: add(obj.set_max_power(config[CONF_MAX_POWER])) + + +def build_flags(config): + return '-DUSE_OUTPUT' diff --git a/esphomeyaml/components/output/esp8266_pwm.py b/esphomeyaml/components/output/esp8266_pwm.py index d4899dfe3d..dfe907a78b 100644 --- a/esphomeyaml/components/output/esp8266_pwm.py +++ b/esphomeyaml/components/output/esp8266_pwm.py @@ -9,16 +9,25 @@ from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin, get_gpio_pi ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] + +def valid_pwm_pin(value): + if value >= 16: + raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.") + return value + + PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({ - vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, + vol.Required(CONF_PIN): vol.All(pins.GPIO_OUTPUT_PIN_SCHEMA, + pins.schema_validate_number(valid_pwm_pin)), }) def to_code(config): - if get_gpio_pin_number(config[CONF_PIN]) >= 16: - # Too difficult to do in config validation - raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.") pin = exp_gpio_output_pin(config[CONF_PIN]) rhs = App.make_esp8266_pwm_output(pin) gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs) output.setup_output_platform(gpio, config) + + +def build_flags(config): + return '-DUSE_ESP8266_PWM_OUTPUT' diff --git a/esphomeyaml/components/output/gpio.py b/esphomeyaml/components/output/gpio.py index 47e9dc22d0..82f250c4cb 100644 --- a/esphomeyaml/components/output/gpio.py +++ b/esphomeyaml/components/output/gpio.py @@ -15,3 +15,7 @@ def to_code(config): rhs = App.make_gpio_output(pin) gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs) output.setup_output_platform(gpio, config) + + +def build_flags(config): + return '-DUSE_GPIO_OUTPUT' diff --git a/esphomeyaml/components/output/ledc.py b/esphomeyaml/components/output/ledc.py index f36b8478fa..f4076fd0fb 100644 --- a/esphomeyaml/components/output/ledc.py +++ b/esphomeyaml/components/output/ledc.py @@ -36,3 +36,7 @@ def to_code(config): if CONF_CHANNEL in config: add(ledc.set_channel(config[CONF_CHANNEL])) output.setup_output_platform(ledc, config) + + +def build_flags(config): + return '-DUSE_LEDC_OUTPUT' diff --git a/esphomeyaml/components/output/pca9685.py b/esphomeyaml/components/output/pca9685.py index 66207267e2..fd29bc85c8 100644 --- a/esphomeyaml/components/output/pca9685.py +++ b/esphomeyaml/components/output/pca9685.py @@ -23,3 +23,7 @@ def to_code(config): rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply) out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs) output.setup_output_platform(out, config, skip_power_supply=True) + + +def build_flags(config): + return '-DUSE_PCA9685_OUTPUT' diff --git a/esphomeyaml/components/pca9685.py b/esphomeyaml/components/pca9685.py index fb8e6cc428..5d40b8918f 100644 --- a/esphomeyaml/components/pca9685.py +++ b/esphomeyaml/components/pca9685.py @@ -31,3 +31,7 @@ def to_code(config): phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format( conf[CONF_PHASE_BALANCER])) add(pca9685.set_phase_balancer(phase_balancer)) + + +def build_flags(config): + return '-DUSE_PCA9685_OUTPUT' diff --git a/esphomeyaml/components/power_supply.py b/esphomeyaml/components/power_supply.py index 78fe375469..7cf8496210 100644 --- a/esphomeyaml/components/power_supply.py +++ b/esphomeyaml/components/power_supply.py @@ -23,3 +23,7 @@ def to_code(config): add(psu.set_enable_time(conf[CONF_ENABLE_TIME])) if CONF_KEEP_ON_TIME in conf: add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME])) + + +def build_flags(config): + return '-DUSE_OUTPUT' diff --git a/esphomeyaml/components/sensor/__init__.py b/esphomeyaml/components/sensor/__init__.py index 43af3b51bf..870fdb1a72 100644 --- a/esphomeyaml/components/sensor/__init__.py +++ b/esphomeyaml/components/sensor/__init__.py @@ -3,8 +3,8 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_EXPIRE_AFTER, \ CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_ICON, \ - CONF_ID, CONF_LAMBDA, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_SEND_EVERY, \ - CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE + CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_SEND_EVERY, \ + CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, CONF_ID from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \ setup_mqtt_component @@ -13,7 +13,6 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.Any( - # TODO Fix weird voluptuous error messages vol.Schema({vol.Required(CONF_OFFSET): vol.Coerce(float)}), vol.Schema({vol.Required(CONF_MULTIPLY): vol.Coerce(float)}), vol.Schema({vol.Required(CONF_FILTER_OUT): vol.Coerce(float)}), @@ -43,7 +42,7 @@ MQTT_SENSOR_SCHEMA = vol.Schema({ }) MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({ - cv.GenerateID('mqtt_sensor'): cv.register_variable_id, + cv.GenerateID('mqtt_sensor', CONF_MQTT_ID): cv.register_variable_id, }) # pylint: disable=invalid-name @@ -72,30 +71,38 @@ def setup_filter(config): conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE] return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY]) if CONF_LAMBDA in config: - s = '[](float x) -> Optional {{ return {}; }}'.format(config[CONF_LAMBDA]) + s = u'[](float x) -> Optional {{ return {}; }}'.format(config[CONF_LAMBDA]) return LambdaFilter(RawExpression(s)) - raise ValueError("Filter unsupported: {}".format(config)) + raise ValueError(u"Filter unsupported: {}".format(config)) def setup_mqtt_sensor_component(obj, config): + if CONF_EXPIRE_AFTER in config: + if config[CONF_EXPIRE_AFTER] is None: + add(obj.disable_expire_after()) + else: + add(obj.set_expire_after(config[CONF_EXPIRE_AFTER])) + setup_mqtt_component(obj, config) + + +def setup_sensor(obj, config): if CONF_UNIT_OF_MEASUREMENT in config: add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT])) if CONF_ICON in config: add(obj.set_icon(config[CONF_ICON])) if CONF_ACCURACY_DECIMALS in config: add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS])) - if CONF_EXPIRE_AFTER in config: - if config[CONF_EXPIRE_AFTER] is None: - add(obj.disable_expire_after()) - else: - add(obj.set_expire_after(config[CONF_EXPIRE_AFTER])) if CONF_FILTERS in config: filters = [setup_filter(x) for x in config[CONF_FILTERS]] add(obj.set_filters(ArrayInitializer(*filters))) - setup_mqtt_component(obj, config) -def make_mqtt_sensor_for(exp, config): - rhs = App.make_mqtt_sensor_for(exp, config[CONF_NAME]) - mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_ID], rhs) +def register_sensor(var, config): + setup_sensor(var, config) + rhs = App.register_sensor(var) + mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_MQTT_ID], rhs) setup_mqtt_sensor_component(mqtt_sensor, config) + + +def build_flags(config): + return '-DUSE_SENSOR' diff --git a/esphomeyaml/components/sensor/adc.py b/esphomeyaml/components/sensor/adc.py index 6deb531144..e11d80b8fb 100644 --- a/esphomeyaml/components/sensor/adc.py +++ b/esphomeyaml/components/sensor/adc.py @@ -25,11 +25,16 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_adc_sensor(config[CONF_PIN], config[CONF_NAME], + rhs = App.make_adc_sensor(config[CONF_NAME], config[CONF_PIN], config.get(CONF_UPDATE_INTERVAL)) make = variable('Application::MakeADCSensor', config[CONF_ID], rhs) adc = make.Padc if CONF_ATTENUATION in config: attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]] add(adc.set_attenuation(RawExpression(attenuation))) + sensor.setup_sensor(adc, config) sensor.setup_mqtt_sensor_component(make.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_ADC_SENSOR' diff --git a/esphomeyaml/components/sensor/ads1115.py b/esphomeyaml/components/sensor/ads1115.py index 55d7740a29..5572b73e81 100644 --- a/esphomeyaml/components/sensor/ads1115.py +++ b/esphomeyaml/components/sensor/ads1115.py @@ -2,8 +2,9 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor -from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import get_variable, RawExpression +from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \ + CONF_NAME, CONF_ID +from esphomeyaml.helpers import RawExpression, get_variable, Pvariable DEPENDENCIES = ['ads1115'] @@ -40,6 +41,7 @@ def validate_gain(value): PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID('ads1115_sensor'): cv.register_variable_id, vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))), vol.Required(CONF_GAIN): validate_gain, vol.Optional(CONF_ADS1115_ID): cv.variable_id, @@ -52,5 +54,10 @@ def to_code(config): mux = RawExpression(MUX[config[CONF_MULTIPLEXER]]) gain = RawExpression(GAIN[config[CONF_GAIN]]) - sensor_ = hub.get_sensor(mux, gain, config.get(CONF_UPDATE_INTERVAL)) - sensor.make_mqtt_sensor_for(sensor_, config) + rhs = hub.get_sensor(config[CONF_NAME], mux, gain, config.get(CONF_UPDATE_INTERVAL)) + sensor_ = Pvariable('sensor::ADS1115Sensor', config[CONF_ID], rhs) + sensor.register_sensor(sensor_, config) + + +def build_flags(config): + return '-DUSE_ADS1115_SENSOR' diff --git a/esphomeyaml/components/sensor/bmp085.py b/esphomeyaml/components/sensor/bmp085.py index 3db8e71082..50fc5134ce 100644 --- a/esphomeyaml/components/sensor/bmp085.py +++ b/esphomeyaml/components/sensor/bmp085.py @@ -22,8 +22,14 @@ def to_code(config): rhs = App.make_bmp085_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_PRESSURE][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - bmp = variable('Application::MakeBMP085Component', config[CONF_ID], rhs) + bmp = variable('Application::MakeBMP085Sensor', config[CONF_ID], rhs) if CONF_ADDRESS in config: add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS]))) + sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE]) + sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), config[CONF_PRESSURE]) sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE]) + + +def build_flags(config): + return '-DUSE_BMP085_SENSOR' diff --git a/esphomeyaml/components/sensor/dallas.py b/esphomeyaml/components/sensor/dallas.py index ee560967b3..83bfb6dd44 100644 --- a/esphomeyaml/components/sensor/dallas.py +++ b/esphomeyaml/components/sensor/dallas.py @@ -3,11 +3,13 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS -from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, \ - CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import HexIntLiteral, get_variable +from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \ + CONF_RESOLUTION, \ + CONF_UPDATE_INTERVAL, CONF_ID +from esphomeyaml.helpers import HexIntLiteral, get_variable, Pvariable PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID('dallas_sensor'): cv.register_variable_id, vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int, vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int, vol.Optional(CONF_DALLAS_ID): cv.variable_id, @@ -23,9 +25,14 @@ def to_code(config): if CONF_ADDRESS in config: address = HexIntLiteral(config[CONF_ADDRESS]) - sensor_ = hub.Pget_sensor_by_address(address, update_interval, - config.get(CONF_RESOLUTION)) + rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, update_interval, + config.get(CONF_RESOLUTION)) else: - sensor_ = hub.Pget_sensor_by_index(config[CONF_INDEX], update_interval, - config.get(CONF_RESOLUTION)) - sensor.make_mqtt_sensor_for(sensor_, config) + rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX], + update_interval, config.get(CONF_RESOLUTION)) + sensor_ = Pvariable('sensor::DallasTemperatureSensor', config[CONF_ID], rhs) + sensor.register_sensor(sensor_, config) + + +def build_flags(config): + return '-DUSE_DALLAS_SENSOR' diff --git a/esphomeyaml/components/sensor/dht.py b/esphomeyaml/components/sensor/dht.py index 810693ba8c..cbccabaab2 100644 --- a/esphomeyaml/components/sensor/dht.py +++ b/esphomeyaml/components/sensor/dht.py @@ -21,11 +21,18 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_dht_sensor(config[CONF_PIN], config[CONF_TEMPERATURE][CONF_NAME], - config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - dht = variable('Application::MakeDHTComponent', config[CONF_ID], rhs) + rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME], + config[CONF_HUMIDITY][CONF_NAME], + config[CONF_PIN], config.get(CONF_UPDATE_INTERVAL)) + dht = variable('Application::MakeDHTSensor', config[CONF_ID], rhs) if CONF_MODEL in config: model = RawExpression('DHT::{}'.format(config[CONF_MODEL])) add(dht.Pdht.set_dht_model(model)) + sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE]) + sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY]) sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY]) + + +def build_flags(config): + return '-DUSE_DHT_SENSOR' diff --git a/esphomeyaml/components/sensor/hdc1080.py b/esphomeyaml/components/sensor/hdc1080.py index 89d4e27685..24a45f7af2 100644 --- a/esphomeyaml/components/sensor/hdc1080.py +++ b/esphomeyaml/components/sensor/hdc1080.py @@ -21,6 +21,12 @@ def to_code(config): rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - hdc1080 = variable('Application::MakeHDC1080Component', config[CONF_ID], rhs) + hdc1080 = variable('Application::MakeHDC1080Sensor', config[CONF_ID], rhs) + sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(), config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_temperature, config[CONF_TEMPERATURE]) + sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), config[CONF_HUMIDITY]) sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY]) + + +def build_flags(config): + return '-DUSE_HDC1080_SENSOR' diff --git a/esphomeyaml/components/sensor/htu21d.py b/esphomeyaml/components/sensor/htu21d.py index 876a33396c..c532060cc7 100644 --- a/esphomeyaml/components/sensor/htu21d.py +++ b/esphomeyaml/components/sensor/htu21d.py @@ -21,6 +21,12 @@ def to_code(config): rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) - htu21d = variable('Application::MakeHTU21DComponent', config[CONF_ID], rhs) + htu21d = variable('Application::MakeHTU21DSensor', config[CONF_ID], rhs) + sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_temperature, config[CONF_TEMPERATURE]) + sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), config[CONF_HUMIDITY]) sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY]) + + +def build_flags(config): + return '-DUSE_HTU21D_SENSOR' diff --git a/esphomeyaml/components/sensor/pulse_counter.py b/esphomeyaml/components/sensor/pulse_counter.py index c9e03e1a16..38027f9963 100644 --- a/esphomeyaml/components/sensor/pulse_counter.py +++ b/esphomeyaml/components/sensor/pulse_counter.py @@ -41,9 +41,9 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_pulse_counter_sensor(config[CONF_PIN], config[CONF_NAME], + rhs = App.make_pulse_counter_sensor(config[CONF_NAME], config[CONF_PIN], config.get(CONF_UPDATE_INTERVAL)) - make = variable('Application::MakePulseCounter', config[CONF_ID], rhs) + make = variable('Application::MakePulseCounterSensor', config[CONF_ID], rhs) pcnt = make.Ppcnt if CONF_PULL_MODE in config: pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]] @@ -55,4 +55,9 @@ def to_code(config): add(pcnt.set_edge_mode(RawExpression(rising_edge), RawExpression(falling_edge))) if CONF_INTERNAL_FILTER in config: add(pcnt.set_filter(config[CONF_INTERNAL_FILTER])) + sensor.setup_sensor(pcnt, config) sensor.setup_mqtt_sensor_component(make.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_PULSE_COUNTER_SENSOR' diff --git a/esphomeyaml/components/sensor/ultrasonic.py b/esphomeyaml/components/sensor/ultrasonic.py index 05d0e44bfa..f227cd918d 100644 --- a/esphomeyaml/components/sensor/ultrasonic.py +++ b/esphomeyaml/components/sensor/ultrasonic.py @@ -21,7 +21,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ def to_code(config): trigger = exp_gpio_output_pin(config[CONF_TRIGGER_PIN]) echo = exp_gpio_input_pin(config[CONF_ECHO_PIN]) - rhs = App.make_ultrasonic_sensor(trigger, echo, config[CONF_NAME], + rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo, config.get(CONF_UPDATE_INTERVAL)) make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs) ultrasonic = make.Pultrasonic @@ -29,4 +29,9 @@ def to_code(config): add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME])) elif CONF_TIMEOUT_METER in config: add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER])) + sensor.setup_sensor(ultrasonic, config) sensor.setup_mqtt_sensor_component(make.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_ULTRASONIC_SENSOR' diff --git a/esphomeyaml/components/switch/__init__.py b/esphomeyaml/components/switch/__init__.py index 6afeb203fb..234a4964bb 100644 --- a/esphomeyaml/components/switch/__init__.py +++ b/esphomeyaml/components/switch/__init__.py @@ -1,7 +1,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_NAME +from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_NAME, CONF_MQTT_ID from esphomeyaml.helpers import App, Pvariable, add, setup_mqtt_component PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -12,14 +12,26 @@ MQTT_SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ vol.Optional(CONF_ICON): cv.icon, }) +MQTT_SWITCH_ID_SCHEMA = MQTT_SWITCH_SCHEMA.extend({ + cv.GenerateID('mqtt_switch', CONF_MQTT_ID): cv.register_variable_id, +}) + def setup_mqtt_switch(obj, config): - if CONF_ICON in config: - add(obj.set_icon(config[CONF_ICON])) setup_mqtt_component(obj, config) -def make_mqtt_switch_for(exp, config): - rhs = App.make_mqtt_switch_for(exp, config[CONF_NAME]) - mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_ID], rhs) +def setup_switch(obj, config): + if CONF_ICON in config: + add(obj.set_icon(config[CONF_ICON])) + + +def register_switch(var, config): + setup_switch(var, config) + rhs = App.register_switch(var) + mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_MQTT_ID], rhs) setup_mqtt_switch(mqtt_switch, config) + + +def build_flags(config): + return '-DUSE_SWITCH' diff --git a/esphomeyaml/components/switch/gpio.py b/esphomeyaml/components/switch/gpio.py index 852c0753af..6b2eaf7294 100644 --- a/esphomeyaml/components/switch/gpio.py +++ b/esphomeyaml/components/switch/gpio.py @@ -13,6 +13,11 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_gpio_switch(exp_gpio_output_pin(config[CONF_PIN]), config[CONF_NAME]) - gpio = variable('Application::GPIOSwitchStruct', config[CONF_ID], rhs) + rhs = App.make_gpio_switch(config[CONF_NAME], exp_gpio_output_pin(config[CONF_PIN])) + gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs) + switch.setup_switch(gpio.Pswitch_, config) switch.setup_mqtt_switch(gpio.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_GPIO_SWITCH' diff --git a/esphomeyaml/components/switch/ir_transmitter.py b/esphomeyaml/components/switch/ir_transmitter.py index cdc7af4045..fa5f31907f 100644 --- a/esphomeyaml/components/switch/ir_transmitter.py +++ b/esphomeyaml/components/switch/ir_transmitter.py @@ -5,12 +5,12 @@ from esphomeyaml.components import switch from esphomeyaml.components.ir_transmitter import IR_TRANSMITTER_COMPONENT_CLASS from esphomeyaml.const import CONF_ADDRESS, CONF_COMMAND, CONF_DATA, CONF_IR_TRANSMITTER_ID, \ CONF_LG, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_REPEAT, CONF_SONY, CONF_TIMES, \ - CONF_WAIT_TIME_US, CONF_RAW, CONF_CARRIER_FREQUENCY + CONF_WAIT_TIME_US, CONF_RAW, CONF_CARRIER_FREQUENCY, CONF_NAME, CONF_ID from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import HexIntLiteral, MockObj, get_variable, ArrayInitializer +from esphomeyaml.helpers import HexIntLiteral, MockObj, get_variable, ArrayInitializer, Pvariable PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ - cv.GenerateID('ir_transmitter'): cv.register_variable_id, + cv.GenerateID('ir_transmitter_switch'): cv.register_variable_id, vol.Exclusive(CONF_NEC, 'code'): vol.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_COMMAND): cv.hex_uint16_t, @@ -36,7 +36,7 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ vol.Required(CONF_WAIT_TIME_US): cv.uint32_t, })), vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id, -}).extend(switch.MQTT_SWITCH_SCHEMA.schema) +}).extend(switch.MQTT_SWITCH_ID_SCHEMA.schema) # pylint: disable=invalid-name @@ -86,4 +86,11 @@ def exp_send_data(config): def to_code(config): ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS) send_data = exp_send_data(config) - switch.make_mqtt_switch_for(ir.create_transmitter(send_data), config) + rhs = ir.create_transmitter(config[CONF_NAME], send_data) + switch_ = Pvariable(IR_TRANSMITTER_COMPONENT_CLASS + '::DataTransmitter', config[CONF_ID], + rhs) + switch.register_switch(switch_, config) + + +def build_flags(config): + return '-DUSE_IR_TRANSMITTER' diff --git a/esphomeyaml/components/switch/restart.py b/esphomeyaml/components/switch/restart.py index ff3a8e6c00..a21fc716e2 100644 --- a/esphomeyaml/components/switch/restart.py +++ b/esphomeyaml/components/switch/restart.py @@ -1,7 +1,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import switch from esphomeyaml.const import CONF_ID, CONF_NAME -from esphomeyaml.helpers import App, Pvariable +from esphomeyaml.helpers import App, variable PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ cv.GenerateID('restart_switch'): cv.register_variable_id, @@ -10,5 +10,10 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ def to_code(config): rhs = App.make_restart_switch(config[CONF_NAME]) - mqtt = Pvariable('switch_::MQTTSwitchComponent', config[CONF_ID], rhs) - switch.setup_mqtt_switch(mqtt, config) + restart = variable('Application::MakeRestartSwitch', config[CONF_ID], rhs) + switch.setup_switch(restart.Prestart, config) + switch.setup_mqtt_switch(restart.Pmqtt, config) + + +def build_flags(config): + return '-DUSE_RESTART_SWITCH' diff --git a/esphomeyaml/components/web_server.py b/esphomeyaml/components/web_server.py new file mode 100644 index 0000000000..6811c76a91 --- /dev/null +++ b/esphomeyaml/components/web_server.py @@ -0,0 +1,22 @@ +import logging + +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.const import CONF_PORT +from esphomeyaml.helpers import App, add + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = vol.Schema({ + cv.GenerateID('web_server'): cv.register_variable_id, + vol.Optional(CONF_PORT): cv.port, +}) + + +def to_code(config): + add(App.init_web_server(config.get(CONF_PORT))) + + +def build_flags(config): + return '-DUSE_WEB_SERVER' diff --git a/esphomeyaml/components/wifi.py b/esphomeyaml/components/wifi.py index 029e7c9afe..3f4c9d8630 100644 --- a/esphomeyaml/components/wifi.py +++ b/esphomeyaml/components/wifi.py @@ -5,7 +5,7 @@ from esphomeyaml.const import CONF_DNS1, CONF_DNS2, CONF_GATEWAY, CONF_HOSTNAME, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, CONF_WIFI from esphomeyaml.helpers import App, MockObj, Pvariable, StructInitializer, add -CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ +CONFIG_SCHEMA = vol.Schema({ cv.GenerateID(CONF_WIFI): cv.register_variable_id, vol.Required(CONF_SSID): cv.ssid, vol.Optional(CONF_PASSWORD): cv.string, diff --git a/esphomeyaml/config.py b/esphomeyaml/config.py index 23f0e9d029..42d11688ca 100644 --- a/esphomeyaml/config.py +++ b/esphomeyaml/config.py @@ -8,13 +8,13 @@ import voluptuous as vol from voluptuous.humanize import humanize_error import esphomeyaml.config_validation as cv -from esphomeyaml import helpers, yaml_util +from esphomeyaml import core, yaml_util from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_MQTT, \ CONF_NAME, \ CONF_PLATFORM, CONF_SIMPLIFY, CONF_WIFI, ESP_PLATFORMS, ESP_PLATFORM_ESP32, \ - ESP_PLATFORM_ESP8266 + ESP_PLATFORM_ESP8266, CONF_USE_BUILD_FLAGS from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import App, add, add_task, color +from esphomeyaml.helpers import App, add, color _LOGGER = logging.getLogger(__name__) @@ -27,6 +27,7 @@ CORE_SCHEMA = vol.Schema({ vol.Required(CONF_BOARD): cv.string, vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string, vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean, + vol.Optional(CONF_USE_BUILD_FLAGS, default=False): cv.boolean, }) REQUIRED_COMPONENTS = [ @@ -66,8 +67,17 @@ def is_platform_component(component): return hasattr(component, 'PLATFORM_SCHEMA') -def validate_schema(config, schema): - return schema(config) +def iter_components(config): + for domain, conf in config.iteritems(): + if domain == CONF_ESPHOMEYAML: + continue + component = get_component(domain) + yield domain, component, conf + if is_platform_component(component): + for p_config in conf: + p_name = u"{}.{}".format(domain, p_config[CONF_PLATFORM]) + platform = get_component(p_name) + yield p_name, platform, p_config class Config(OrderedDict): @@ -96,7 +106,7 @@ def validate_config(config): result.add_error(_format_config_error(ex, domain, config), domain, config) try: - result[CONF_ESPHOMEYAML] = validate_schema(config[CONF_ESPHOMEYAML], CORE_SCHEMA) + result[CONF_ESPHOMEYAML] = CORE_SCHEMA(config[CONF_ESPHOMEYAML]) except vol.Invalid as ex: _comp_error(ex, CONF_ESPHOMEYAML, config) @@ -111,8 +121,8 @@ def validate_config(config): continue esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS) - if cv.ESP_PLATFORM not in esp_platforms: - result.add_error(u"Component {} doesn't support {}.".format(domain, cv.ESP_PLATFORM)) + if core.ESP_PLATFORM not in esp_platforms: + result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM)) continue success = True @@ -138,7 +148,7 @@ def validate_config(config): platforms = [] for p_config in conf: if not isinstance(p_config, dict): - result.add_error(u"Platform schemas mus have 'platform:' key") + result.add_error(u"Platform schemas must have 'platform:' key") continue p_name = p_config.get(u'platform') if p_name is None: @@ -160,7 +170,7 @@ def validate_config(config): return result -REQUIRED = ['esphomeyaml', 'wifi', 'mqtt'] +REQUIRED = ['esphomeyaml', 'wifi'] def _format_config_error(ex, domain, config): @@ -186,13 +196,16 @@ def load_config(path): except OSError: raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path)) - esp_platform = unicode(config.get(CONF_ESPHOMEYAML, {}).get(CONF_PLATFORM, u"")) + if CONF_ESPHOMEYAML not in config: + raise ESPHomeYAMLError(u"No esphomeyaml section in config") + core_conf = config[CONF_ESPHOMEYAML] + esp_platform = unicode(core_conf.get(CONF_PLATFORM, u"")) esp_platform = esp_platform.upper() if esp_platform not in (ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266): raise ESPHomeYAMLError(u"Invalid ESP Platform {}".format(esp_platform)) - cv.ESP_PLATFORM = esp_platform - cv.BOARD = unicode(config.get(CONF_ESPHOMEYAML, {}).get(CONF_BOARD, u"")) - helpers.SIMPLIFY = cv.boolean(config.get(CONF_SIMPLIFY, True)) + core.ESP_PLATFORM = esp_platform + core.BOARD = unicode(core_conf.get(CONF_BOARD, u"")) + core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True)) try: result = validate_config(config) @@ -203,28 +216,6 @@ def load_config(path): return result -def add_platform_task(domain, config): - platform_ = config[CONF_PLATFORM] - platform = get_platform(domain, platform_) - if not hasattr(platform, 'to_code'): - raise ESPHomeYAMLError(u"Platform '{}.{}' doesn't have to_code.".format(domain, platform_)) - add_task(platform.to_code, config) - - -def add_component_task(domain, config): - if domain == CONF_ESPHOMEYAML: - add_task(core_to_code, config) - return - component = get_component(domain) - if is_platform_component(component): - for conf in config: - add_platform_task(domain, conf) - else: - if not hasattr(component, 'to_code'): - raise ESPHomeYAMLError(u"Component '{}' doesn't have to_code.".format(domain)) - add_task(component.to_code, config) - - def line_info(obj, **kwargs): """Display line config source.""" if hasattr(obj, '__config_file__'): diff --git a/esphomeyaml/config_validation.py b/esphomeyaml/config_validation.py index 1716308b03..132c32891c 100644 --- a/esphomeyaml/config_validation.py +++ b/esphomeyaml/config_validation.py @@ -7,6 +7,7 @@ from datetime import timedelta import voluptuous as vol +from esphomeyaml import core from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \ CONF_NAME, CONF_PAYLOAD_AVAILABLE, \ CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \ @@ -24,9 +25,6 @@ zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1)) positive_int = vol.All(vol.Coerce(int), vol.Range(min=0)) positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False)) -ESP_PLATFORM = '' -BOARD = '' - ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' RESERVED_IDS = [ @@ -150,7 +148,7 @@ def only_on(platforms): platforms = [platforms] def validator_(obj): - if ESP_PLATFORM not in platforms: + if core.ESP_PLATFORM not in platforms: raise vol.Invalid(u"This feature is only available on {}".format(platforms)) return obj @@ -300,6 +298,8 @@ 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 '#'") return value @@ -334,23 +334,19 @@ def register_variable_id(value): class GenerateID(vol.Optional): - def __init__(self, basename): + def __init__(self, basename, key=CONF_ID): self._basename = basename - super(GenerateID, self).__init__(CONF_ID, default=self.default_variable_id) + super(GenerateID, self).__init__(key, default=self.default_variable_id) def default_variable_id(self): return ensure_unique_string(self._basename, REGISTERED_IDS) -ID_SCHEMA = vol.Schema({ - vol.Required(CONF_ID): invalid, -}) - REQUIRED_ID_SCHEMA = vol.Schema({ vol.Required(CONF_ID): register_variable_id, }) -PLATFORM_SCHEMA = ID_SCHEMA.extend({ +PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): valid, }) diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index 71c642a4ab..908c4f7ffa 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -2,7 +2,7 @@ MAJOR_VERSION = 1 MINOR_VERSION = 2 -PATCH_VERSION = '2' +PATCH_VERSION = '2-dev' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) @@ -17,6 +17,7 @@ CONF_NAME = 'name' CONF_PLATFORM = 'platform' CONF_BOARD = 'board' CONF_SIMPLIFY = 'simplify' +CONF_USE_BUILD_FLAGS = 'use_build_flags' CONF_LIBRARY_URI = 'library_uri' CONF_LOGGER = 'logger' CONF_WIFI = 'wifi' @@ -32,6 +33,7 @@ CONF_BROKER = 'broker' CONF_USERNAME = 'username' CONF_POWER_SUPPLY = 'power_supply' CONF_ID = 'id' +CONF_MQTT_ID = 'mqtt_id' CONF_PIN = 'pin' CONF_NUMBER = 'number' CONF_INVERTED = 'inverted' @@ -151,6 +153,10 @@ CONF_RATE = 'rate' CONF_ADS1115_ID = 'ads1115_id' CONF_MULTIPLEXER = 'multiplexer' CONF_GAIN = 'gain' +CONF_SLEEP_DURATION = 'sleep_duration' +CONF_WAKEUP_PIN = 'wakeup_pin' +CONF_RUN_CYCLES = 'run_cycles' +CONF_RUN_DURATION = 'run_duration' ESP32_BOARDS = [ 'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1', diff --git a/esphomeyaml/core.py b/esphomeyaml/core.py index 4ac7300fed..087e4ebcaf 100644 --- a/esphomeyaml/core.py +++ b/esphomeyaml/core.py @@ -16,3 +16,9 @@ class IPAddress(object): def __str__(self): return '.'.join(str(x) for x in self.args) + + +CONFIG_PATH = None +SIMPLIFY = True +ESP_PLATFORM = '' +BOARD = '' diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index 8be62f083b..f33b09dd62 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -4,6 +4,7 @@ import logging import re from collections import OrderedDict, deque +from esphomeyaml import core from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \ CONF_INVERTED, \ CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, \ @@ -12,8 +13,6 @@ from esphomeyaml.core import ESPHomeYAMLError, HexInt _LOGGER = logging.getLogger(__name__) -SIMPLIFY = False - def ensure_unique_string(preferred_string, current_strings): test_string = preferred_string @@ -45,11 +44,19 @@ def indent(text, padding=u' '): class Expression(object): def __init__(self): - pass + self.requires = [] + self.required = False def __str__(self): raise NotImplementedError + def require(self): + self.required = True + for require in self.requires: + if require.required: + continue + require.require() + class RawExpression(Expression): def __init__(self, text): @@ -61,14 +68,20 @@ class RawExpression(Expression): class AssignmentExpression(Expression): - def __init__(self, lhs, rhs, obj): + def __init__(self, type, modifier, name, rhs, obj): super(AssignmentExpression, self).__init__() - self.obj = obj - self.lhs = safe_exp(lhs) + self.type = type + self.modifier = modifier + self.name = name self.rhs = safe_exp(rhs) + self.requires.append(self.rhs) + self.obj = obj def __str__(self): - return u"{} = {}".format(self.lhs, self.rhs) + type_ = self.type + if core.SIMPLIFY: + type_ = u'auto' + return u"{} {}{} = {}".format(type_, self.modifier, self.name, self.rhs) class ExpressionList(Expression): @@ -78,7 +91,11 @@ class ExpressionList(Expression): args = list(args) while args and args[-1] is None: args.pop() - self.args = [safe_exp(x) for x in args] + self.args = [] + for arg in args: + exp = safe_exp(arg) + self.requires.append(exp) + self.args.append(exp) def __str__(self): text = u", ".join(unicode(x) for x in self.args) @@ -90,6 +107,7 @@ class CallExpression(Expression): super(CallExpression, self).__init__() self.base = base self.args = ExpressionList(*args) + self.requires.append(self.args) def __str__(self): return u'{}({})'.format(self.base, self.args) @@ -103,8 +121,11 @@ class StructInitializer(Expression): args = OrderedDict(args) self.args = OrderedDict() for key, value in args.iteritems(): - if value is not None: - self.args[key] = safe_exp(value) + if value is None: + continue + exp = safe_exp(value) + self.args[key] = exp + self.requires.append(exp) def __str__(self): cpp = u'{}{{\n'.format(self.base) @@ -117,7 +138,13 @@ class StructInitializer(Expression): class ArrayInitializer(Expression): def __init__(self, *args): super(ArrayInitializer, self).__init__() - self.args = [safe_exp(x) for x in args if x is not None] + self.args = [] + for x in args: + if x is None: + continue + exp = safe_exp(x) + self.args.append(exp) + self.requires.append(exp) def __str__(self): if not self.args: @@ -227,20 +254,22 @@ def statement(expression): # pylint: disable=redefined-builtin, invalid-name def variable(type, id, rhs): - lhs = RawExpression(u'{} {}'.format(type if not SIMPLIFY else u'auto', id)) rhs = safe_exp(rhs) obj = MockObj(id, u'.') - add(AssignmentExpression(lhs, rhs, obj)) + assignment = AssignmentExpression(type, '', id, rhs, obj) + add(assignment) _VARIABLES[id] = obj, type + obj.requires.append(assignment) return obj def Pvariable(type, id, rhs): - lhs = RawExpression(u'{} *{}'.format(type if not SIMPLIFY else u'auto', id)) rhs = safe_exp(rhs) obj = MockObj(id, u'->') - add(AssignmentExpression(lhs, rhs, obj)) + assignment = AssignmentExpression(type, '*', id, rhs, obj) + add(assignment) _VARIABLES[id] = obj, type + obj.requires.append(assignment) return obj @@ -272,7 +301,6 @@ def get_variable(id, type=None): if result is None: raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type)) - result.usages += 1 return result @@ -280,17 +308,17 @@ def add_task(func, config): _QUEUE.append((func, config)) -def add(expression): +def add(expression, require=True): + if require and isinstance(expression, Expression): + expression.require() _EXPRESSIONS.append(expression) return expression class MockObj(Expression): - def __init__(self, base, op=u'.', parent=None): + def __init__(self, base, op=u'.'): self.base = base self.op = op - self.usages = 0 - self.parent = parent super(MockObj, self).__init__() def __getattr__(self, attr): @@ -299,18 +327,26 @@ class MockObj(Expression): attr = attr[1:] next_op = u'->' op = self.op - return MockObj(u'{}{}{}'.format(self.base, op, attr), next_op, self) + obj = MockObj(u'{}{}{}'.format(self.base, op, attr), next_op) + obj.requires.append(self) + return obj def __call__(self, *args, **kwargs): - self.usages += 1 - it = self.parent - while it is not None: - it.usages += 1 - it = it.parent - return CallExpression(self.base, *args) + call = CallExpression(self.base, *args) + obj = MockObj(call, self.op) + obj.requires.append(self) + obj.requires.append(call) + return obj def __str__(self): - return self.base + return unicode(self.base) + + def require(self): + self.required = True + for require in self.requires: + if require.required: + continue + require.require() App = MockObj(u'App') @@ -358,13 +394,8 @@ def setup_mqtt_component(obj, config): add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC])) if CONF_AVAILABILITY in config: availability = config[CONF_AVAILABILITY] - exp = StructInitializer( - u'mqtt::Availability', - (u'topic', availability[CONF_TOPIC]), - (u'payload_available', availability[CONF_PAYLOAD_AVAILABLE]), - (u'payload_not_available', availability[CONF_PAYLOAD_NOT_AVAILABLE]), - ) - add(obj.set_availability(exp)) + add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE], + availability[CONF_PAYLOAD_NOT_AVAILABLE])) def exp_empty_optional(type): diff --git a/esphomeyaml/mqtt.py b/esphomeyaml/mqtt.py index 138cbb61b5..d231df827f 100644 --- a/esphomeyaml/mqtt.py +++ b/esphomeyaml/mqtt.py @@ -1,13 +1,16 @@ from __future__ import print_function +import hashlib import logging from datetime import datetime import paho.mqtt.client as mqtt +from esphomeyaml import core from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, CONF_LOGGER, \ CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \ CONF_USERNAME +from esphomeyaml.helpers import color _LOGGER = logging.getLogger(__name__) @@ -38,8 +41,8 @@ def initialize(config, subscriptions, on_message, username, password, client_id) def show_logs(config, topic=None, username=None, password=None, client_id=None): if topic is not None: pass # already have topic - elif CONF_LOG_TOPIC in config.get(CONF_LOGGER, {}): - topic = config[CONF_LOGGER][CONF_LOG_TOPIC] + elif CONF_LOG_TOPIC in config.get(CONF_MQTT, {}): + topic = config[CONF_MQTT][CONF_LOG_TOPIC] elif CONF_TOPIC_PREFIX in config[CONF_MQTT]: topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + u'/debug' else: @@ -47,7 +50,7 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None): _LOGGER.info(u"Starting log output from %s", topic) def on_message(client, userdata, msg): - time = datetime.now().time().strftime(u'[%H:%M:%S] ') + time = datetime.now().time().strftime(u'[%H:%M:%S]') print(time + msg.payload) return initialize(config, [topic], on_message, username, password, client_id) @@ -67,3 +70,23 @@ def clear_topic(config, topic, username=None, password=None, client_id=None): client.publish(msg.topic, None, retain=True) return initialize(config, [topic], on_message, username, password, client_id) + + +# From marvinroger/async-mqtt-client -> scripts/get-fingerprint/get-fingerprint.py +def get_fingerprint(config): + import ssl + + addr = config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT] + _LOGGER.info("Getting fingerprint from %s:%s", addr[0], addr[1]) + try: + cert_pem = ssl.get_server_certificate(addr) + except IOError as err: + _LOGGER.error("Unable to connect to server: %s", err) + return 1 + cert_der = ssl.PEM_cert_to_DER_cert(cert_pem) + + sha1 = hashlib.sha1(cert_der).hexdigest() + + print(u"SHA1 Fingerprint: " + color('cyan', sha1)) + print(u"Copy above string into mqtt.ssl_fingerprints section of {}".format(core.CONFIG_PATH)) + return 0 diff --git a/esphomeyaml/pins.py b/esphomeyaml/pins.py index f96ccc463b..4605c6b6e6 100644 --- a/esphomeyaml/pins.py +++ b/esphomeyaml/pins.py @@ -3,6 +3,7 @@ import logging 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 @@ -72,32 +73,32 @@ def _translate_pin(value): pass if value.startswith('GPIO'): return vol.Coerce(int)(value[len('GPIO'):]) - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: if value in ESP32_PINS: return ESP32_PINS[value] - if cv.BOARD not in ESP32_BOARD_TO_PINS: + if core.BOARD not in ESP32_BOARD_TO_PINS: raise vol.Invalid(u"ESP32: Unknown board {} with unknown " - u"pin {}.".format(cv.BOARD, value)) - if value not in ESP32_BOARD_TO_PINS[cv.BOARD]: + u"pin {}.".format(core.BOARD, value)) + if value not in ESP32_BOARD_TO_PINS[core.BOARD]: raise vol.Invalid(u"ESP32: Board {} doesn't have" - u"pin {}".format(cv.BOARD, value)) - return ESP32_BOARD_TO_PINS[cv.BOARD][value] - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + u"pin {}".format(core.BOARD, value)) + return ESP32_BOARD_TO_PINS[core.BOARD][value] + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: if value in ESP8266_PINS: return ESP8266_PINS[value] - if cv.BOARD not in ESP8266_BOARD_TO_PINS: + if core.BOARD not in ESP8266_BOARD_TO_PINS: raise vol.Invalid(u"ESP8266: Unknown board {} with unknown " - u"pin {}.".format(cv.BOARD, value)) - if value not in ESP8266_BOARD_TO_PINS[cv.BOARD]: + u"pin {}.".format(core.BOARD, value)) + if value not in ESP8266_BOARD_TO_PINS[core.BOARD]: raise vol.Invalid(u"ESP8266: Board {} doesn't have" - u"pin {}".format(cv.BOARD, value)) - return ESP8266_BOARD_TO_PINS[cv.BOARD][value] + u"pin {}".format(core.BOARD, value)) + return ESP8266_BOARD_TO_PINS[core.BOARD][value] raise vol.Invalid(u"Invalid ESP platform.") def _validate_gpio_pin(value): value = _translate_pin(value) - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: if value < 0 or value > 39: raise vol.Invalid(u"ESP32: Invalid pin number: {}".format(value)) if 6 <= value <= 11: @@ -107,7 +108,7 @@ def _validate_gpio_pin(value): _LOGGER.warning(u"ESP32: Pin %s (20, 24, 28-31) can usually not be used. " u"Be warned.", value) return value - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: if 6 <= value <= 11: _LOGGER.warning(u"ESP8266: Pin %s (6-11) might already be used by the " u"flash interface. Be warned.", value) @@ -119,21 +120,21 @@ def _validate_gpio_pin(value): def input_pin(value): value = _validate_gpio_pin(value) - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: return value - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: return value raise vol.Invalid(u"Invalid ESP platform.") def output_pin(value): value = _validate_gpio_pin(value) - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: if 34 <= value <= 39: raise vol.Invalid(u"ESP32: Pin {} (34-39) can only be used as " u"input pins.".format(value)) return value - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: if value == 16: raise vol.Invalid(u"Pin {} doesn't support output mode".format(value)) return value @@ -142,11 +143,11 @@ def output_pin(value): def analog_pin(value): value = _validate_gpio_pin(value) - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: if 32 <= value <= 39: # ADC1 return value raise vol.Invalid(u"ESP32: Only pins 32 though 39 support ADC.") - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: if value == 17: # A0 return value raise vol.Invalid(u"ESP8266: Only pin A0 (17) supports ADC.") @@ -171,9 +172,9 @@ PIN_MODES_ESP32 = [ def pin_mode(value): value = vol.All(vol.Coerce(str), vol.Upper)(value) - if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: + if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: return vol.Any(*PIN_MODES_ESP32)(value) - elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: + elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: return vol.Any(*PIN_MODES_ESP8266)(value) raise vol.Invalid(u"Invalid ESP platform.") @@ -195,3 +196,14 @@ GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, vol.Schema({ vol.Optional(CONF_MODE): pin_mode, vol.Optional(CONF_INVERTED): cv.boolean, })) + + +def schema_validate_number(validator): + def valid(value): + if isinstance(value, dict): + value[CONF_NUMBER] = validator(value[CONF_NUMBER]) + else: + value = validator(value) + return value + + return valid \ No newline at end of file diff --git a/esphomeyaml/writer.py b/esphomeyaml/writer.py index c5757c6d2e..8f3c7f516f 100644 --- a/esphomeyaml/writer.py +++ b/esphomeyaml/writer.py @@ -4,9 +4,9 @@ import codecs import errno import os -from esphomeyaml.config import get_component -from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_LOGGER, \ - CONF_NAME, CONF_PLATFORM, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 +from esphomeyaml.config import iter_components +from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_NAME, \ + CONF_PLATFORM, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_USE_BUILD_FLAGS from esphomeyaml.core import ESPHomeYAMLError CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ===========' @@ -52,7 +52,8 @@ framework = arduino lib_deps = {esphomeyaml_uri} ${{common.lib_deps}} -build_flags ={build_flags} +build_flags = + {build_flags} ${{common.build_flags}} """ @@ -70,10 +71,21 @@ def get_ini_content(config): u'esphomeyaml_uri': config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI], u'build_flags': u'', } - if CONF_LOGGER in config: - build_flags = get_component(CONF_LOGGER).get_build_flags(config[CONF_LOGGER]) - if build_flags: - options[u'build_flags'] = u'\n ' + build_flags + if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]: + build_flags = set() + build_flags.add(u"-DESPHOMEYAML_USE") + for domain, component, conf in iter_components(config): + if not hasattr(component, u'build_flags'): + continue + flags = component.build_flags(conf) + if flags is None: + continue + if isinstance(flags, (str, unicode)): + flags = [flags] + build_flags |= set(flags) + # avoid changing build flags order + build_flags = sorted(list(build_flags)) + options[u'build_flags'] = u'\n '.join(build_flags) return INI_CONTENT_FORMAT.format(**options) @@ -120,7 +132,7 @@ def write_platformio_ini(content, path): mkdir_p(os.path.dirname(path)) content_format = INI_BASE_FORMAT full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \ - content + INI_AUTO_GENERATE_END + content_format[1] + content + INI_AUTO_GENERATE_END + content_format[1] if prev_file == full_file: return with codecs.open(path, mode='w+', encoding='utf-8') as f_handle: @@ -142,7 +154,7 @@ def write_cpp(code_s, path): code_format = CPP_BASE_FORMAT full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \ - code_s + CPP_AUTO_GENERATE_END + code_format[1] + code_s + CPP_AUTO_GENERATE_END + code_format[1] if prev_file == full_file: return with codecs.open(path, 'w+', encoding='utf-8') as f_handle: