diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index 8ef802e520..4793ba24f3 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -1,11 +1,12 @@ import voluptuous as vol import esphomeyaml.config_validation as cv +from esphomeyaml.components import cover, fan from esphomeyaml.const import CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, CONF_BLUE, \ CONF_BRIGHTNESS, CONF_CONDITION_ID, CONF_DELAY, CONF_EFFECT, CONF_FLASH_LENGTH, CONF_GREEN, \ CONF_ID, CONF_IF, CONF_LAMBDA, CONF_MAX, CONF_MIN, CONF_OR, CONF_PAYLOAD, CONF_QOS, \ CONF_RANGE, CONF_RED, CONF_RETAIN, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, \ - CONF_TRIGGER_ID, CONF_WHITE + CONF_TRIGGER_ID, CONF_WHITE, CONF_OSCILLATING, CONF_SPEED from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, \ bool_, esphomelib_ns, float_, get_variable, process_lambda, std_string, templatable, uint32, \ @@ -18,6 +19,12 @@ CONF_LIGHT_TURN_ON = 'light.turn_on' CONF_SWITCH_TOGGLE = 'switch.toggle' CONF_SWITCH_TURN_OFF = 'switch.turn_off' CONF_SWITCH_TURN_ON = 'switch.turn_on' +CONF_COVER_OPEN = 'cover.open' +CONF_COVER_CLOSE = 'cover.close' +CONF_COVER_STOP = 'cover.stop' +CONF_FAN_TOGGLE = 'fan.toggle' +CONF_FAN_TURN_OFF = 'fan.turn_off' +CONF_FAN_TURN_ON = 'fan.turn_on' ACTION_KEYS = [CONF_DELAY, CONF_MQTT_PUBLISH, CONF_LIGHT_TOGGLE, CONF_LIGHT_TURN_OFF, CONF_LIGHT_TURN_ON, CONF_SWITCH_TOGGLE, CONF_SWITCH_TURN_OFF, CONF_SWITCH_TURN_ON, @@ -60,6 +67,35 @@ ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_SWITCH_TURN_ON): vol.Schema({ vol.Required(CONF_ID): cv.variable_id, }), + vol.Optional(CONF_COVER_OPEN): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_COVER_CLOSE): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_COVER_STOP): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_COVER_OPEN): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_COVER_CLOSE): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_COVER_STOP): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_FAN_TOGGLE): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_FAN_TURN_OFF): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + }), + vol.Optional(CONF_FAN_TURN_ON): vol.Schema({ + vol.Required(CONF_ID): cv.variable_id, + vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), + vol.Optional(CONF_SPEED): cv.templatable(fan.validate_fan_speed), + }), vol.Optional(CONF_LAMBDA): cv.lambda_, }, cv.has_at_exactly_one_key(*ACTION_KEYS))]) @@ -208,6 +244,41 @@ def build_action(config, arg_type): var = get_variable(conf[CONF_ID]) rhs = var.make_turn_on_action(template_arg) return Pvariable(switch.TurnOnAction.template(arg_type), config[CONF_ACTION_ID], rhs) + elif CONF_COVER_OPEN in config: + conf = config[CONF_COVER_OPEN] + var = get_variable(conf[CONF_ID]) + rhs = var.make_open_action(template_arg) + return Pvariable(cover.OpenAction.template(arg_type), config[CONF_ACTION_ID], rhs) + elif CONF_COVER_CLOSE in config: + conf = config[CONF_SWITCH_TURN_OFF] + var = get_variable(conf[CONF_ID]) + rhs = var.make_close_action(template_arg) + return Pvariable(cover.CloseAction.template(arg_type), config[CONF_ACTION_ID], rhs) + elif CONF_COVER_STOP in config: + conf = config[CONF_SWITCH_TURN_ON] + var = get_variable(conf[CONF_ID]) + rhs = var.make_stop_action(template_arg) + return Pvariable(cover.StopAction.template(arg_type), config[CONF_ACTION_ID], rhs) + elif CONF_FAN_TOGGLE in config: + conf = config[CONF_FAN_TOGGLE] + var = get_variable(conf[CONF_ID]) + rhs = var.make_toggle_action(template_arg) + return Pvariable(fan.ToggleAction.template(arg_type), config[CONF_ACTION_ID], rhs) + elif CONF_FAN_TURN_OFF in config: + conf = config[CONF_FAN_TURN_OFF] + var = get_variable(conf[CONF_ID]) + rhs = var.make_turn_off_action(template_arg) + return Pvariable(fan.TurnOffAction.template(arg_type), config[CONF_ACTION_ID], rhs) + elif CONF_FAN_TURN_ON in config: + conf = config[CONF_FAN_TURN_ON] + var = get_variable(conf[CONF_ID]) + rhs = var.make_turn_on_action(template_arg) + action = Pvariable(fan.TurnOnAction.template(arg_type), config[CONF_ACTION_ID], rhs) + if CONF_OSCILLATING in config: + add(action.set_oscillating(templatable(conf[CONF_OSCILLATING], arg_type, bool_))) + if CONF_SPEED in config: + add(action.set_speed(templatable(conf[CONF_SPEED], arg_type, fan.FanSpeed))) + return action raise ESPHomeYAMLError(u"Unsupported action {}".format(config)) diff --git a/esphomeyaml/components/cover/__init__.py b/esphomeyaml/components/cover/__init__.py index 2a80a88911..c57ec76c90 100644 --- a/esphomeyaml/components/cover/__init__.py +++ b/esphomeyaml/components/cover/__init__.py @@ -17,6 +17,9 @@ MQTTCoverComponent = cover_ns.MQTTCoverComponent CoverState = cover_ns.CoverState COVER_OPEN = cover_ns.COVER_OPEN COVER_CLOSED = cover_ns.COVER_CLOSED +OpenAction = cover_ns.OpenAction +CloseAction = cover_ns.CloseAction +StopAction = cover_ns.StopAction def setup_cover_core_(cover_var, mqtt_var, config): diff --git a/esphomeyaml/components/cover/template.py b/esphomeyaml/components/cover/template.py index eb31153e69..ffb4f2406c 100644 --- a/esphomeyaml/components/cover/template.py +++ b/esphomeyaml/components/cover/template.py @@ -4,22 +4,23 @@ import esphomeyaml.config_validation as cv from esphomeyaml import automation from esphomeyaml.components import cover from esphomeyaml.const import CONF_CLOSE_ACTION, CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, \ - CONF_OPEN_ACTION, CONF_STOP_ACTION + CONF_OPEN_ACTION, CONF_STOP_ACTION, CONF_OPTIMISTIC from esphomeyaml.helpers import App, Application, NoArg, add, process_lambda, variable -PLATFORM_SCHEMA = cover.PLATFORM_SCHEMA.extend({ +PLATFORM_SCHEMA = vol.All(cover.PLATFORM_SCHEMA.extend({ cv.GenerateID('template_cover', CONF_MAKE_ID): cv.register_variable_id, - vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Optional(CONF_LAMBDA): cv.lambda_, + vol.Optional(CONF_OPTIMISTIC): cv.boolean, vol.Optional(CONF_OPEN_ACTION): automation.ACTIONS_SCHEMA, vol.Optional(CONF_CLOSE_ACTION): automation.ACTIONS_SCHEMA, vol.Optional(CONF_STOP_ACTION): automation.ACTIONS_SCHEMA, -}).extend(cover.COVER_SCHEMA.schema) +}).extend(cover.COVER_SCHEMA.schema), cv.has_at_exactly_one_key(CONF_LAMBDA, CONF_OPTIMISTIC)) MakeTemplateCover = Application.MakeTemplateCover def to_code(config): - template_ = process_lambda(config[CONF_LAMBDA], []) + template_ = process_lambda(config.get(CONF_LAMBDA), []) rhs = App.make_template_cover(config[CONF_NAME], template_) make = variable(MakeTemplateCover, config[CONF_MAKE_ID], rhs) @@ -32,6 +33,8 @@ def to_code(config): if CONF_STOP_ACTION in config: actions = automation.build_actions(config[CONF_STOP_ACTION], NoArg) add(make.Ptemplate_.add_stop_actions(actions)) + if CONF_OPTIMISTIC in config: + add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC])) cover.setup_cover(make.Ptemplate_, make.Pmqtt, config) diff --git a/esphomeyaml/components/fan/__init__.py b/esphomeyaml/components/fan/__init__.py index 18b302178c..9bfe765600 100644 --- a/esphomeyaml/components/fan/__init__.py +++ b/esphomeyaml/components/fan/__init__.py @@ -20,6 +20,26 @@ fan_ns = esphomelib_ns.namespace('fan') FanState = fan_ns.FanState MQTTFanComponent = fan_ns.MQTTFanComponent MakeFan = Application.MakeFan +TurnOnAction = fan_ns.TurnOnAction +TurnOffAction = fan_ns.TurnOffAction +ToggleAction = fan_ns.ToggleAction +FanSpeed = fan_ns.FanSpeed +FAN_SPEED_OFF = fan_ns.FAN_SPEED_OFF +FAN_SPEED_LOW = fan_ns.FAN_SPEED_LOW +FAN_SPEED_MEDIUM = fan_ns.FAN_SPEED_MEDIUM +FAN_SPEED_HIGH = fan_ns.FAN_SPEED_HIGH + + +FAN_SPEEDS = { + 'OFF': FAN_SPEED_OFF, + 'LOW': FAN_SPEED_LOW, + 'MEDIUM': FAN_SPEED_MEDIUM, + 'HIGH': FAN_SPEED_HIGH, +} + + +def validate_fan_speed(value): + return vol.All(vol.Upper, cv.one_of(FAN_SPEEDS))(value) def setup_fan_core_(fan_var, mqtt_var, config): diff --git a/esphomeyaml/components/switch/template.py b/esphomeyaml/components/switch/template.py index 8c30e4b271..47d0c10fb7 100644 --- a/esphomeyaml/components/switch/template.py +++ b/esphomeyaml/components/switch/template.py @@ -4,21 +4,22 @@ import esphomeyaml.config_validation as cv from esphomeyaml import automation from esphomeyaml.components import switch from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_TURN_OFF_ACTION, \ - CONF_TURN_ON_ACTION + CONF_TURN_ON_ACTION, CONF_OPTIMISTIC from esphomeyaml.helpers import App, Application, process_lambda, variable, NoArg, add -PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ +PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({ cv.GenerateID('template_switch', CONF_MAKE_ID): cv.register_variable_id, - vol.Required(CONF_LAMBDA): cv.lambda_, + vol.Optional(CONF_LAMBDA): cv.lambda_, + vol.Optional(CONF_OPTIMISTIC): cv.boolean, vol.Optional(CONF_TURN_OFF_ACTION): automation.ACTIONS_SCHEMA, vol.Optional(CONF_TURN_ON_ACTION): automation.ACTIONS_SCHEMA, -}).extend(switch.SWITCH_SCHEMA.schema) +}).extend(switch.SWITCH_SCHEMA.schema), cv.has_at_exactly_one_key(CONF_LAMBDA, CONF_OPTIMISTIC)) MakeTemplateSwitch = Application.MakeTemplateSwitch def to_code(config): - template_ = process_lambda(config[CONF_LAMBDA], []) + template_ = process_lambda(config.get(CONF_LAMBDA), []) rhs = App.make_template_switch(config[CONF_NAME], template_) make = variable(MakeTemplateSwitch, config[CONF_MAKE_ID], rhs) @@ -28,6 +29,8 @@ def to_code(config): if CONF_TURN_ON_ACTION in config: actions = automation.build_actions(config[CONF_TURN_ON_ACTION], NoArg) add(make.Ptemplate_.add_turn_on_actions(actions)) + if CONF_OPTIMISTIC in config: + add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC])) switch.setup_switch(make.Ptemplate_, make.Pmqtt, config) diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index 1ec76cacd3..c4af929854 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -164,6 +164,7 @@ CONF_OSCILLATION_OUTPUT = 'oscillation_output' CONF_SPEED = 'speed' CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic' CONF_OSCILLATION_COMMAND_TOPIC = 'oscillation_command_topic' +CONF_OSCILLATING = 'oscillating' CONF_SPEED_STATE_TOPIC = 'speed_state_topic' CONF_SPEED_COMMAND_TOPIC = 'speed_command_topic' CONF_LOW = 'low' @@ -229,6 +230,7 @@ CONF_OPEN_ACTION = 'open_action' CONF_CLOSE_ACTION = 'close_action' CONF_STOP_ACTION = 'stop_action' CONF_DOMAIN = 'domain' +CONF_OPTIMISTIC = 'optimistic' ESP32_BOARDS = [ 'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1', diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index 1431779840..8f8fb814f2 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -413,6 +413,8 @@ def get_variable(id, type=None): def process_lambda(value, parameters, capture='=', return_type=None): + if value is None: + return None parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)\.', value.value) for i in range(1, len(parts), 2): parts[i] = get_variable(parts[i])._