diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index 8250008829..d094b9f59f 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -125,6 +125,9 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): # First try as a sequence of actions return [schema({CONF_THEN: value})] except vol.Invalid as err: + if err.path and err.path[0] == CONF_THEN: + err.path.pop(0) + # Next try as a sequence of automations try: return vol.Schema([schema])(value) diff --git a/esphomeyaml/components/binary_sensor/template.py b/esphomeyaml/components/binary_sensor/template.py index b0beda0f01..e025eea1ed 100644 --- a/esphomeyaml/components/binary_sensor/template.py +++ b/esphomeyaml/components/binary_sensor/template.py @@ -1,15 +1,18 @@ import voluptuous as vol +from esphomeyaml.automation import ACTION_REGISTRY from esphomeyaml.components import binary_sensor import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_NAME -from esphomeyaml.cpp_generator import Pvariable, add, process_lambda +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable from esphomeyaml.cpp_helpers import setup_component -from esphomeyaml.cpp_types import App, Component, bool_, optional +from esphomeyaml.cpp_types import Action, App, Component, bool_, optional TemplateBinarySensor = binary_sensor.binary_sensor_ns.class_('TemplateBinarySensor', binary_sensor.BinarySensor, Component) +BinarySensorPublishAction = binary_sensor.binary_sensor_ns.class_('BinarySensorPublishAction', + Action) PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(TemplateBinarySensor), @@ -31,6 +34,26 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR' +CONF_BINARY_SENSOR_TEMPLATE_PUBLISH = 'binary_sensor.template.publish' +BINARY_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.use_variable_id(binary_sensor.BinarySensor), + vol.Required(CONF_STATE): cv.templatable(cv.boolean), +}) + + +@ACTION_REGISTRY.register(CONF_BINARY_SENSOR_TEMPLATE_PUBLISH, + BINARY_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA) +def binary_sensor_template_publish_to_code(config, action_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_binary_sensor_publish_action(template_arg) + type = BinarySensorPublishAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + for template_ in templatable(config[CONF_STATE], arg_type, bool_): + yield None + add(action.set_state(template_)) + yield action + def to_hass_config(data, config): return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/cover/__init__.py b/esphomeyaml/components/cover/__init__.py index 32befd8081..bfa9d4fbcb 100644 --- a/esphomeyaml/components/cover/__init__.py +++ b/esphomeyaml/components/cover/__init__.py @@ -22,6 +22,12 @@ CoverState = cover_ns.class_('CoverState') COVER_OPEN = cover_ns.COVER_OPEN COVER_CLOSED = cover_ns.COVER_CLOSED +validate_cover_state = cv.one_of('OPEN', 'CLOSED', upper=True) +COVER_STATES = { + 'OPEN': COVER_OPEN, + 'CLOSED': COVER_CLOSED, +} + # Actions OpenAction = cover_ns.class_('OpenAction', Action) CloseAction = cover_ns.class_('CloseAction', Action) diff --git a/esphomeyaml/components/cover/template.py b/esphomeyaml/components/cover/template.py index a19c7a07d2..83829746d2 100644 --- a/esphomeyaml/components/cover/template.py +++ b/esphomeyaml/components/cover/template.py @@ -1,15 +1,18 @@ import voluptuous as vol from esphomeyaml import automation +from esphomeyaml.automation import ACTION_REGISTRY from esphomeyaml.components import cover import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_ID, CONF_LAMBDA, \ - CONF_NAME, CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_STOP_ACTION -from esphomeyaml.cpp_generator import Pvariable, add, process_lambda + CONF_NAME, CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_STATE, CONF_STOP_ACTION +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable from esphomeyaml.cpp_helpers import setup_component -from esphomeyaml.cpp_types import App, NoArg, optional +from esphomeyaml.cpp_types import Action, App, NoArg, optional +from esphomeyaml.py_compat import string_types TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover) +CoverPublishAction = cover.cover_ns.class_('CoverPublishAction', Action) PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(TemplateCover), @@ -51,6 +54,30 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_COVER' +CONF_COVER_TEMPLATE_PUBLISH = 'cover.template.publish' +COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.use_variable_id(cover.Cover), + vol.Required(CONF_STATE): cv.templatable(cover.validate_cover_state), +}) + + +@ACTION_REGISTRY.register(CONF_COVER_TEMPLATE_PUBLISH, + COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA) +def cover_template_publish_to_code(config, action_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_cover_publish_action(template_arg) + type = CoverPublishAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + state = config[CONF_STATE] + if isinstance(state, string_types): + template_ = cover.COVER_STATES[state] + else: + for template_ in templatable(state, arg_type, cover.CoverState): + yield None + add(action.set_state(template_)) + yield action + def to_hass_config(data, config): ret = cover.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/template.py b/esphomeyaml/components/sensor/template.py index ce4e1b7a9c..27d5d91a67 100644 --- a/esphomeyaml/components/sensor/template.py +++ b/esphomeyaml/components/sensor/template.py @@ -1,13 +1,15 @@ import voluptuous as vol +from esphomeyaml.automation import ACTION_REGISTRY from esphomeyaml.components import sensor import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.cpp_generator import Pvariable, add, process_lambda +from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE, CONF_UPDATE_INTERVAL +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable from esphomeyaml.cpp_helpers import setup_component -from esphomeyaml.cpp_types import App, float_, optional +from esphomeyaml.cpp_types import Action, App, float_, optional TemplateSensor = sensor.sensor_ns.class_('TemplateSensor', sensor.PollingSensorComponent) +SensorPublishAction = sensor.sensor_ns.class_('SensorPublishAction', Action) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(TemplateSensor), @@ -31,6 +33,25 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR' +CONF_SENSOR_TEMPLATE_PUBLISH = 'sensor.template.publish' +SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.use_variable_id(sensor.Sensor), + vol.Required(CONF_STATE): cv.templatable(cv.float_), +}) + + +@ACTION_REGISTRY.register(CONF_SENSOR_TEMPLATE_PUBLISH, SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA) +def sensor_template_publish_to_code(config, action_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_sensor_publish_action(template_arg) + type = SensorPublishAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + for template_ in templatable(config[CONF_STATE], arg_type, float_): + yield None + add(action.set_state(template_)) + yield action + def to_hass_config(data, config): return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/__init__.py b/esphomeyaml/components/switch/__init__.py index fc9c248401..a3b9c10295 100644 --- a/esphomeyaml/components/switch/__init__.py +++ b/esphomeyaml/components/switch/__init__.py @@ -1,14 +1,15 @@ import voluptuous as vol +from esphomeyaml import automation from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY, CONDITION_REGISTRY, Condition from esphomeyaml.components import mqtt from esphomeyaml.components.mqtt import setup_mqtt_component import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL, \ - CONF_OPTIMISTIC + CONF_OPTIMISTIC, CONF_ON_TURN_ON, CONF_ON_TURN_OFF, CONF_TRIGGER_ID from esphomeyaml.core import CORE from esphomeyaml.cpp_generator import add, Pvariable, get_variable -from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Action, App +from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Action, App, Trigger, NoArg PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -24,11 +25,19 @@ TurnOffAction = switch_ns.class_('TurnOffAction', Action) TurnOnAction = switch_ns.class_('TurnOnAction', Action) SwitchCondition = switch_ns.class_('SwitchCondition', Condition) +SwitchTurnOnTrigger = switch_ns.class_('SwitchTurnOnTrigger', Trigger.template(NoArg)) +SwitchTurnOffTrigger = switch_ns.class_('SwitchTurnOffTrigger', Trigger.template(NoArg)) SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent), vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_INVERTED): cv.boolean, + vol.Optional(CONF_ON_TURN_ON): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SwitchTurnOnTrigger), + }), + vol.Optional(CONF_ON_TURN_OFF): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SwitchTurnOffTrigger), + }), }) SWITCH_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SWITCH_SCHEMA.schema) @@ -41,6 +50,14 @@ def setup_switch_core_(switch_var, config): add(switch_var.set_icon(config[CONF_ICON])) if CONF_INVERTED in config: add(switch_var.set_inverted(config[CONF_INVERTED])) + for conf in config.get(CONF_ON_TURN_ON, []): + rhs = switch_var.make_switch_turn_on_trigger() + trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) + automation.build_automation(trigger, NoArg, conf) + for conf in config.get(CONF_ON_TURN_OFF, []): + rhs = switch_var.make_switch_turn_off_trigger() + trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) + automation.build_automation(trigger, NoArg, conf) setup_mqtt_component(switch_var.Pget_mqtt(), config) diff --git a/esphomeyaml/components/switch/template.py b/esphomeyaml/components/switch/template.py index 8e16815513..9b15e26126 100644 --- a/esphomeyaml/components/switch/template.py +++ b/esphomeyaml/components/switch/template.py @@ -1,15 +1,18 @@ import voluptuous as vol from esphomeyaml import automation +from esphomeyaml.automation import ACTION_REGISTRY from esphomeyaml.components import switch import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OPTIMISTIC, \ - CONF_RESTORE_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION, CONF_ASSUMED_STATE -from esphomeyaml.cpp_generator import Pvariable, add, process_lambda + CONF_RESTORE_STATE, \ + CONF_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION, CONF_ASSUMED_STATE +from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable from esphomeyaml.cpp_helpers import setup_component -from esphomeyaml.cpp_types import App, Component, NoArg, bool_, optional +from esphomeyaml.cpp_types import Action, App, Component, NoArg, bool_, optional TemplateSwitch = switch.switch_ns.class_('TemplateSwitch', switch.Switch, Component) +SwitchPublishAction = switch.switch_ns.class_('SwitchPublishAction', Action) PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(TemplateSwitch), @@ -52,6 +55,25 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH' +CONF_SWITCH_TEMPLATE_PUBLISH = 'switch.template.publish' +SWITCH_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ + vol.Required(CONF_ID): cv.use_variable_id(switch.Switch), + vol.Required(CONF_STATE): cv.templatable(cv.boolean), +}) + + +@ACTION_REGISTRY.register(CONF_SWITCH_TEMPLATE_PUBLISH, SWITCH_TEMPLATE_PUBLISH_ACTION_SCHEMA) +def switch_template_publish_to_code(config, action_id, arg_type, template_arg): + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_switch_publish_action(template_arg) + type = SwitchPublishAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + for template_ in templatable(config[CONF_STATE], arg_type, bool_): + yield None + add(action.set_state(template_)) + yield action + def to_hass_config(data, config): return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index 878386ad2f..04a2ebebc6 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -401,6 +401,8 @@ CONF_VARIANT = 'variant' CONF_METHOD = 'method' CONF_FAST_CONNECT = 'fast_connect' CONF_INTERLOCK = 'interlock' +CONF_ON_TURN_ON = 'on_turn_on' +CONF_ON_TURN_OFF = 'on_turn_off' ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_' ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage' diff --git a/tests/test1.yaml b/tests/test1.yaml index 24db85034f..70d1437598 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -433,13 +433,21 @@ sensor: update_interval: 15s - platform: template name: "Template Sensor" - lambda: >- + id: template_sensor + lambda: |- if (id(ultrasonic_sensor1).state > 1) { return 42.0; } else { return {}; } update_interval: 15s + on_value: + - sensor.template.publish: + id: template_sensor + state: 43.0 + - sensor.template.publish: + id: template_sensor + state: !lambda 'return NAN;' - platform: tsl2561 name: "TSL2561 Ambient Light" address: 0x39 @@ -560,7 +568,8 @@ binary_sensor: name: "Nextion Component 2 Touch" - platform: template name: "Garage Door Open" - lambda: >- + id: garage_door + lambda: |- if (isnan(id(my_sensor).state)) { // isnan checks if the ultrasonic sensor echo // has timed out, resulting in a NaN (not a number) state @@ -573,6 +582,10 @@ binary_sensor: // Garage Door is closed. return false; } + on_press: + - binary_sensor.template.publish: + id: garage_door + state: OFF - platform: pn532 uid: 74-10-37-94 name: "PN532 NFC Tag" @@ -885,6 +898,7 @@ switch: - -1000 - platform: template name: Living Room Lights + id: livingroom_lights optimistic: True assumed_state: yes turn_on_action: @@ -898,6 +912,10 @@ switch: turn_off_action: - switch.turn_on: living_room_lights_off restore_state: False + on_turn_on: + - switch.template.publish: + id: livingroom_lights + state: yes - platform: restart name: "Living Room Restart" - platform: shutdown @@ -926,6 +944,10 @@ switch: optimistic: true assumed_state: no restore_state: True + on_turn_off: + - switch.template.publish: + id: my_switch + state: !lambda 'return false;' - platform: uart name: "UART String Output" data: 'DataToSend' @@ -1041,13 +1063,18 @@ time: cover: - platform: template name: "Template Cover" - lambda: >- + id: template_cover + lambda: |- if (id(binary_sensor1).state) { return cover::COVER_OPEN; } else { return {}; } optimistic: true + open_action: + - cover.template.publish: + id: template_cover + state: CLOSED assumed_state: no debug: