diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index 601cd49565..c08d870f08 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -104,7 +104,12 @@ def run_miniterm(config, port, escape=False): with serial.Serial(port, baudrate=baud_rate) as ser: while True: - line = ser.readline().replace('\r', '').replace('\n', '') + try: + raw = ser.readline() + except serial.SerialException: + _LOGGER.error("Serial port closed!") + return + line = raw.replace('\r', '').replace('\n', '') time = datetime.now().time().strftime('[%H:%M:%S]') message = time + line if escape: diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index af20359e59..326ef9ec36 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -1,17 +1,18 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import cover, fan +from esphomeyaml import core +from esphomeyaml.components import cover, deep_sleep, fan, output from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \ - CONF_BELOW, \ - CONF_BLUE, CONF_BRIGHTNESS, CONF_CONDITION_ID, CONF_DELAY, CONF_EFFECT, CONF_FLASH_LENGTH, \ - CONF_GREEN, CONF_ID, CONF_IF, CONF_LAMBDA, CONF_OR, CONF_OSCILLATING, CONF_PAYLOAD, CONF_QOS, \ - CONF_RANGE, CONF_RED, CONF_RETAIN, CONF_SPEED, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, \ - CONF_TRIGGER_ID, CONF_WHITE + CONF_BELOW, CONF_BLUE, CONF_BRIGHTNESS, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, \ + CONF_EFFECT, CONF_ELSE, CONF_FLASH_LENGTH, CONF_GREEN, CONF_ID, CONF_IF, CONF_LAMBDA, \ + CONF_LEVEL, CONF_OR, CONF_OSCILLATING, CONF_PAYLOAD, CONF_QOS, CONF_RANGE, CONF_RED, \ + CONF_RETAIN, CONF_SPEED, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, CONF_TRIGGER_ID, \ + CONF_WHITE from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, \ +from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, add_job, \ bool_, esphomelib_ns, float_, get_variable, process_lambda, std_string, templatable, uint32, \ - uint8, add_job + uint8 CONF_MQTT_PUBLISH = 'mqtt.publish' CONF_LIGHT_TOGGLE = 'light.toggle' @@ -26,11 +27,37 @@ CONF_COVER_STOP = 'cover.stop' CONF_FAN_TOGGLE = 'fan.toggle' CONF_FAN_TURN_OFF = 'fan.turn_off' CONF_FAN_TURN_ON = 'fan.turn_on' +CONF_OUTPUT_TURN_ON = 'output.turn_on' +CONF_OUTPUT_TURN_OFF = 'output.turn_off' +CONF_OUTPUT_SET_LEVEL = 'output.set_level' +CONF_DEEP_SLEEP_ENTER = 'deep_sleep.enter' +CONF_DEEP_SLEEP_PREVENT = 'deep_sleep.prevent' + + +def maybe_simple_id(*validators): + validator = vol.All(*validators) + + def validate(value): + if isinstance(value, dict): + return validator(value) + return validator({CONF_ID: value}) + + return validate + + +def validate_recursive_condition(value): + return CONDITIONS_SCHEMA(value) + + +def validate_recursive_action(value): + return ACTIONS_SCHEMA(value) + 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, CONF_LAMBDA, CONF_COVER_OPEN, CONF_COVER_CLOSE, CONF_COVER_STOP, CONF_FAN_TOGGLE, - CONF_FAN_TURN_OFF, CONF_FAN_TURN_ON] + CONF_FAN_TURN_OFF, CONF_FAN_TURN_ON, CONF_OUTPUT_TURN_ON, CONF_OUTPUT_TURN_OFF, + CONF_OUTPUT_SET_LEVEL, CONF_IF, CONF_DEEP_SLEEP_ENTER, CONF_DEEP_SLEEP_PREVENT] ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ cv.GenerateID(CONF_ACTION_ID): cv.declare_variable_id(None), @@ -41,15 +68,15 @@ ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_QOS): cv.templatable(cv.mqtt_qos), vol.Optional(CONF_RETAIN): cv.templatable(cv.boolean), }), - vol.Optional(CONF_LIGHT_TOGGLE): vol.Schema({ + vol.Optional(CONF_LIGHT_TOGGLE): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), }), - vol.Optional(CONF_LIGHT_TURN_OFF): vol.Schema({ + vol.Optional(CONF_LIGHT_TURN_OFF): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), }), - vol.Optional(CONF_LIGHT_TURN_ON): vol.Schema({ + vol.Optional(CONF_LIGHT_TURN_ON): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), vol.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'): cv.templatable(cv.positive_time_period_milliseconds), @@ -62,60 +89,75 @@ ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_WHITE): cv.templatable(cv.percentage), vol.Optional(CONF_EFFECT): cv.templatable(cv.string), }), - vol.Optional(CONF_SWITCH_TOGGLE): vol.Schema({ + vol.Optional(CONF_SWITCH_TOGGLE): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_SWITCH_TURN_OFF): vol.Schema({ + vol.Optional(CONF_SWITCH_TURN_OFF): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_SWITCH_TURN_ON): vol.Schema({ + vol.Optional(CONF_SWITCH_TURN_ON): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_COVER_OPEN): vol.Schema({ + vol.Optional(CONF_COVER_OPEN): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_COVER_CLOSE): vol.Schema({ + vol.Optional(CONF_COVER_CLOSE): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_COVER_STOP): vol.Schema({ + vol.Optional(CONF_COVER_STOP): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_COVER_OPEN): vol.Schema({ + vol.Optional(CONF_COVER_OPEN): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_COVER_CLOSE): vol.Schema({ + vol.Optional(CONF_COVER_CLOSE): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_COVER_STOP): vol.Schema({ + vol.Optional(CONF_COVER_STOP): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_FAN_TOGGLE): vol.Schema({ + vol.Optional(CONF_FAN_TOGGLE): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_FAN_TURN_OFF): vol.Schema({ + vol.Optional(CONF_FAN_TURN_OFF): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), }), - vol.Optional(CONF_FAN_TURN_ON): vol.Schema({ + vol.Optional(CONF_FAN_TURN_ON): maybe_simple_id({ vol.Required(CONF_ID): cv.use_variable_id(None), vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), vol.Optional(CONF_SPEED): cv.templatable(fan.validate_fan_speed), }), + vol.Optional(CONF_OUTPUT_TURN_OFF): maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(None), + }), + vol.Optional(CONF_OUTPUT_TURN_ON): maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(None) + }), + vol.Optional(CONF_OUTPUT_SET_LEVEL): { + vol.Required(CONF_ID): cv.use_variable_id(None), + vol.Required(CONF_LEVEL): cv.zero_to_one_float, + }, + vol.Optional(CONF_DEEP_SLEEP_ENTER): maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(deep_sleep.DeepSleepComponent), + }), + vol.Optional(CONF_DEEP_SLEEP_PREVENT): maybe_simple_id({ + vol.Required(CONF_ID): cv.use_variable_id(deep_sleep.DeepSleepComponent), + }), + vol.Optional(CONF_IF): vol.All({ + vol.Required(CONF_CONDITION): validate_recursive_condition, + vol.Optional(CONF_THEN): validate_recursive_action, + vol.Optional(CONF_ELSE): validate_recursive_action, + }, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE)), vol.Optional(CONF_LAMBDA): cv.lambda_, }, cv.has_exactly_one_key(*ACTION_KEYS))]) # pylint: disable=invalid-name DelayAction = esphomelib_ns.DelayAction LambdaAction = esphomelib_ns.LambdaAction +IfAction = esphomelib_ns.IfAction Automation = esphomelib_ns.Automation - -def validate_recursive_condition(value): - return CONDITIONS_SCHEMA(value) - - -CONDITION_KEYS = [CONF_AND, CONF_OR, CONF_RANGE, CONF_LAMBDA] - -CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ +CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [cv.templatable({ cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(None), vol.Optional(CONF_AND): validate_recursive_condition, vol.Optional(CONF_OR): validate_recursive_condition, @@ -124,7 +166,7 @@ CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_BELOW): vol.Coerce(float), }), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)), vol.Optional(CONF_LAMBDA): cv.lambda_, -}), cv.has_exactly_one_key(*CONDITION_KEYS)]) +})]) # pylint: disable=invalid-name AndCondition = esphomelib_ns.AndCondition @@ -158,7 +200,12 @@ AUTOMATION_SCHEMA = vol.Schema({ def build_condition(config, arg_type): template_arg = TemplateArguments(arg_type) - if CONF_AND in config: + if isinstance(config, core.Lambda): + lambda_ = None + for lambda_ in process_lambda(config, [(arg_type, 'x')]): + yield + yield LambdaCondition.new(template_arg, lambda_) + elif CONF_AND in config: yield AndCondition.new(template_arg, build_conditions(config[CONF_AND], template_arg)) elif CONF_OR in config: yield OrCondition.new(template_arg, build_conditions(config[CONF_OR], template_arg)) @@ -197,201 +244,240 @@ def build_conditions(config, arg_type): yield ArrayInitializer(*conditions) -def build_action(config, arg_type): +def build_action(full_config, arg_type): from esphomeyaml.components import light, mqtt, switch template_arg = TemplateArguments(arg_type) # Keep pylint from freaking out var = None - if CONF_DELAY in config: + action_id = full_config[CONF_ACTION_ID] + key, config = next((k, v) for k, v in full_config.items() if k in ACTION_KEYS) + if key == CONF_DELAY: rhs = App.register_component(DelayAction.new(template_arg)) type = DelayAction.template(template_arg) - action = Pvariable(config[CONF_ACTION_ID], rhs, type=type) + action = Pvariable(action_id, rhs, type=type) template_ = None - for template_ in templatable(config[CONF_DELAY], arg_type, uint32): + for template_ in templatable(config, arg_type, uint32): yield add(action.set_delay(template_)) yield action - elif CONF_LAMBDA in config: + elif key == CONF_LAMBDA: lambda_ = None - for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]): + for lambda_ in process_lambda(config, [(arg_type, 'x')]): yield None rhs = LambdaAction.new(template_arg, lambda_) type = LambdaAction.template(template_arg) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_MQTT_PUBLISH in config: - conf = config[CONF_MQTT_PUBLISH] + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_MQTT_PUBLISH: rhs = App.Pget_mqtt_client().Pmake_publish_action(template_arg) type = mqtt.MQTTPublishAction.template(template_arg) - action = Pvariable(config[CONF_ACTION_ID], rhs, type=type) + action = Pvariable(action_id, rhs, type=type) template_ = None - for template_ in templatable(conf[CONF_TOPIC], arg_type, std_string): + for template_ in templatable(config[CONF_TOPIC], arg_type, std_string): yield None add(action.set_topic(template_)) template_ = None - for template_ in templatable(conf[CONF_PAYLOAD], arg_type, std_string): + for template_ in templatable(config[CONF_PAYLOAD], arg_type, std_string): yield None add(action.set_payload(template_)) - if CONF_QOS in conf: + if CONF_QOS in config: template_ = None - for template_ in templatable(conf[CONF_QOS], arg_type, uint8): + for template_ in templatable(config[CONF_QOS], arg_type, uint8): yield add(action.set_qos(template_)) - if CONF_RETAIN in conf: + if CONF_RETAIN in config: template_ = None - for template_ in templatable(conf[CONF_RETAIN], arg_type, bool_): + for template_ in templatable(config[CONF_RETAIN], arg_type, bool_): yield None add(action.set_retain(template_)) yield action - elif CONF_LIGHT_TOGGLE in config: - conf = config[CONF_LIGHT_TOGGLE] - for var in get_variable(conf[CONF_ID]): + elif key == CONF_LIGHT_TOGGLE: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_toggle_action(template_arg) type = light.ToggleAction.template(template_arg) - action = Pvariable(config[CONF_ACTION_ID], rhs, type=type) - if CONF_TRANSITION_LENGTH in conf: + action = Pvariable(action_id, rhs, type=type) + if CONF_TRANSITION_LENGTH in config: template_ = None - for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32): + for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32): yield None add(action.set_transition_length(template_)) yield action - elif CONF_LIGHT_TURN_OFF in config: - conf = config[CONF_LIGHT_TURN_OFF] - for var in get_variable(conf[CONF_ID]): + elif key == CONF_LIGHT_TURN_OFF: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) type = light.TurnOffAction.template(template_arg) - action = Pvariable(config[CONF_ACTION_ID], rhs, type=type) - if CONF_TRANSITION_LENGTH in conf: + action = Pvariable(action_id, rhs, type=type) + if CONF_TRANSITION_LENGTH in config: template_ = None - for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32): + for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32): yield None add(action.set_transition_length(template_)) yield action - elif CONF_LIGHT_TURN_ON in config: - conf = config[CONF_LIGHT_TURN_ON] - for var in get_variable(conf[CONF_ID]): + elif key == CONF_LIGHT_TURN_ON: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) type = light.TurnOnAction.template(template_arg) - action = Pvariable(config[CONF_ACTION_ID], rhs, type=type) - if CONF_TRANSITION_LENGTH in conf: + action = Pvariable(action_id, rhs, type=type) + if CONF_TRANSITION_LENGTH in config: template_ = None - for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32): + for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32): yield None add(action.set_transition_length(template_)) - if CONF_FLASH_LENGTH in conf: + if CONF_FLASH_LENGTH in config: template_ = None - for template_ in templatable(conf[CONF_FLASH_LENGTH], arg_type, uint32): + for template_ in templatable(config[CONF_FLASH_LENGTH], arg_type, uint32): yield None add(action.set_flash_length(template_)) - if CONF_BRIGHTNESS in conf: + if CONF_BRIGHTNESS in config: template_ = None - for template_ in templatable(conf[CONF_BRIGHTNESS], arg_type, float_): + for template_ in templatable(config[CONF_BRIGHTNESS], arg_type, float_): yield None add(action.set_brightness(template_)) - if CONF_RED in conf: + if CONF_RED in config: template_ = None - for template_ in templatable(conf[CONF_RED], arg_type, float_): + for template_ in templatable(config[CONF_RED], arg_type, float_): yield None add(action.set_red(template_)) - if CONF_GREEN in conf: + if CONF_GREEN in config: template_ = None - for template_ in templatable(conf[CONF_GREEN], arg_type, float_): + for template_ in templatable(config[CONF_GREEN], arg_type, float_): yield None add(action.set_green(template_)) - if CONF_BLUE in conf: + if CONF_BLUE in config: template_ = None - for template_ in templatable(conf[CONF_BLUE], arg_type, float_): + for template_ in templatable(config[CONF_BLUE], arg_type, float_): yield None add(action.set_blue(template_)) - if CONF_WHITE in conf: + if CONF_WHITE in config: template_ = None - for template_ in templatable(conf[CONF_WHITE], arg_type, float_): + for template_ in templatable(config[CONF_WHITE], arg_type, float_): yield None add(action.set_white(template_)) - if CONF_EFFECT in conf: + if CONF_EFFECT in config: template_ = None - for template_ in templatable(conf[CONF_EFFECT], arg_type, std_string): + for template_ in templatable(config[CONF_EFFECT], arg_type, std_string): yield None add(action.set_effect(template_)) yield action - elif CONF_SWITCH_TOGGLE in config: - conf = config[CONF_SWITCH_TOGGLE] - for var in get_variable(conf[CONF_ID]): + elif key == CONF_SWITCH_TOGGLE: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_toggle_action(template_arg) type = switch.ToggleAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_SWITCH_TURN_OFF in config: - conf = config[CONF_SWITCH_TURN_OFF] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_SWITCH_TURN_OFF: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) type = switch.TurnOffAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_SWITCH_TURN_ON in config: - conf = config[CONF_SWITCH_TURN_ON] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_SWITCH_TURN_ON: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) type = switch.TurnOnAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_COVER_OPEN in config: - conf = config[CONF_COVER_OPEN] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_COVER_OPEN: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_open_action(template_arg) type = cover.OpenAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_COVER_CLOSE in config: - conf = config[CONF_COVER_CLOSE] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_COVER_CLOSE: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_close_action(template_arg) type = cover.CloseAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_COVER_STOP in config: - conf = config[CONF_COVER_STOP] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_COVER_STOP: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_stop_action(template_arg) type = cover.StopAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_FAN_TOGGLE in config: - conf = config[CONF_FAN_TOGGLE] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_FAN_TOGGLE: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_toggle_action(template_arg) type = fan.ToggleAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_FAN_TURN_OFF in config: - conf = config[CONF_FAN_TURN_OFF] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_FAN_TURN_OFF: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_off_action(template_arg) type = fan.TurnOffAction.template(arg_type) - yield Pvariable(config[CONF_ACTION_ID], rhs, type=type) - elif CONF_FAN_TURN_ON in config: - conf = config[CONF_FAN_TURN_ON] - for var in get_variable(conf[CONF_ID]): + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_FAN_TURN_ON: + for var in get_variable(config[CONF_ID]): yield None rhs = var.make_turn_on_action(template_arg) type = fan.TurnOnAction.template(arg_type) - action = Pvariable(config[CONF_ACTION_ID], rhs, type=type) + action = Pvariable(action_id, rhs, type=type) if CONF_OSCILLATING in config: template_ = None - for template_ in templatable(conf[CONF_OSCILLATING], arg_type, bool_): + for template_ in templatable(config[CONF_OSCILLATING], arg_type, bool_): yield None add(action.set_oscillating(template_)) if CONF_SPEED in config: template_ = None - for template_ in templatable(conf[CONF_SPEED], arg_type, fan.FanSpeed): + for template_ in templatable(config[CONF_SPEED], arg_type, fan.FanSpeed): yield None add(action.set_speed(template_)) yield action + elif key == CONF_OUTPUT_TURN_OFF: + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_turn_off_action(template_arg) + type = output.TurnOffAction.template(arg_type) + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_OUTPUT_TURN_ON: + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_turn_on_action(template_arg) + type = output.TurnOnAction.template(arg_type) + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_OUTPUT_SET_LEVEL: + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_set_level_action(template_arg) + type = output.SetLevelAction.template(arg_type) + action = Pvariable(action_id, rhs, type=type) + template_ = None + for template_ in templatable(config[CONF_LEVEL], arg_type, bool_): + yield None + add(action.set_level(template_)) + yield action + elif key == CONF_IF: + for conditions in build_conditions(config[CONF_CONDITION], arg_type): + yield None + rhs = IfAction.new(template_arg, conditions) + type = IfAction.template(template_arg) + action = Pvariable(action_id, rhs, type=type) + if CONF_THEN in config: + for actions in build_actions(config[CONF_THEN], arg_type): + yield None + add(action.add_then(actions)) + if CONF_ELSE in config: + for actions in build_actions(config[CONF_ELSE], arg_type): + yield None + add(action.add_else(actions)) + yield action + elif key == CONF_DEEP_SLEEP_ENTER: + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_enter_deep_sleep_action(template_arg) + type = deep_sleep.EnterDeepSleepAction.template(arg_type) + yield Pvariable(action_id, rhs, type=type) + elif key == CONF_DEEP_SLEEP_PREVENT: + for var in get_variable(config[CONF_ID]): + yield None + rhs = var.make_prevent_deep_sleep_action(template_arg) + type = deep_sleep.PreventDeepSleepAction.template(arg_type) + yield Pvariable(action_id, rhs, type=type) else: raise ESPHomeYAMLError(u"Unsupported action {}".format(config)) diff --git a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py index 3ee313ff84..8e4737602c 100644 --- a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py +++ b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py @@ -2,16 +2,13 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor -from esphomeyaml.components.esp32_ble_tracker import ESP32BLETracker -from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME, ESP_PLATFORM_ESP32 -from esphomeyaml.core import HexInt -from esphomeyaml.helpers import ArrayInitializer, get_variable +from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ + make_address_array +from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME +from esphomeyaml.helpers import get_variable -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] DEPENDENCIES = ['esp32_ble_tracker'] -CONF_ESP32_BLE_ID = 'esp32_ble_id' - PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ vol.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker) @@ -22,9 +19,5 @@ def to_code(config): hub = None for hub in get_variable(config[CONF_ESP32_BLE_ID]): yield - addr = [HexInt(i) for i in config[CONF_MAC_ADDRESS].parts] - rhs = hub.make_device(config[CONF_NAME], ArrayInitializer(*addr, multiline=False)) + rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) binary_sensor.register_binary_sensor(rhs, config) - - -BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER' diff --git a/esphomeyaml/components/binary_sensor/pn532.py b/esphomeyaml/components/binary_sensor/pn532.py new file mode 100644 index 0000000000..6ae202a1e7 --- /dev/null +++ b/esphomeyaml/components/binary_sensor/pn532.py @@ -0,0 +1,42 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import binary_sensor +from esphomeyaml.components.pn532 import PN532Component +from esphomeyaml.const import CONF_NAME, CONF_UID +from esphomeyaml.core import HexInt +from esphomeyaml.helpers import ArrayInitializer, get_variable + +DEPENDENCIES = ['pn532'] + +CONF_PN532_ID = 'pn532_id' + + +def validate_uid(value): + value = cv.string_strict(value) + for x in value.split('-'): + if len(x) != 2: + raise vol.Invalid("Each part (separated by '-') of the UID must be two characters " + "long.") + try: + x = int(x, 16) + except ValueError: + raise vol.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") + if x < 0 or x > 255: + raise vol.Invalid("Valid values for UID parts (separated by '-') are 00 to FF") + return value + + +PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + vol.Required(CONF_UID): validate_uid, + cv.GenerateID(CONF_PN532_ID): cv.use_variable_id(PN532Component) +})) + + +def to_code(config): + hub = None + for hub in get_variable(config[CONF_PN532_ID]): + yield + addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] + rhs = hub.make_tag(config[CONF_NAME], ArrayInitializer(*addr, multiline=False)) + binary_sensor.register_binary_sensor(rhs, config) diff --git a/esphomeyaml/components/binary_sensor/rdm6300.py b/esphomeyaml/components/binary_sensor/rdm6300.py new file mode 100644 index 0000000000..bc0aac03bf --- /dev/null +++ b/esphomeyaml/components/binary_sensor/rdm6300.py @@ -0,0 +1,24 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import binary_sensor +from esphomeyaml.components.rdm6300 import RDM6300Component +from esphomeyaml.const import CONF_NAME, CONF_UID +from esphomeyaml.helpers import get_variable + +DEPENDENCIES = ['rdm6300'] + +CONF_RDM6300_ID = 'rdm6300_id' + +PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ + vol.Required(CONF_UID): cv.uint32_t, + cv.GenerateID(CONF_RDM6300_ID): cv.use_variable_id(RDM6300Component) +})) + + +def to_code(config): + hub = None + for hub in get_variable(config[CONF_RDM6300_ID]): + yield + rhs = hub.make_tag(config[CONF_NAME], config[CONF_UID]) + binary_sensor.register_binary_sensor(rhs, config) diff --git a/esphomeyaml/components/binary_sensor/remote_receiver.py b/esphomeyaml/components/binary_sensor/remote_receiver.py index c0008ffd0b..7ac53f9cfe 100644 --- a/esphomeyaml/components/binary_sensor/remote_receiver.py +++ b/esphomeyaml/components/binary_sensor/remote_receiver.py @@ -3,13 +3,20 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor from esphomeyaml.components.remote_receiver import RemoteReceiverComponent, remote_ns -from esphomeyaml.const import CONF_ADDRESS, CONF_COMMAND, CONF_DATA, \ - CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_RAW, CONF_SONY +from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \ + RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \ + RC_SWITCH_TYPE_D_SCHEMA, binary_code, build_rc_switch_protocol +from esphomeyaml.const import CONF_ADDRESS, CONF_CHANNEL, CONF_CODE, CONF_COMMAND, CONF_DATA, \ + CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \ + CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, CONF_RC_SWITCH_TYPE_A, \ + CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, CONF_SONY, CONF_STATE from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable DEPENDENCIES = ['remote_receiver'] -IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW] +REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW, CONF_RC_SWITCH_RAW, + CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, + CONF_RC_SWITCH_TYPE_D] CONF_REMOTE_RECEIVER_ID = 'remote_receiver_id' CONF_RECEIVER_ID = 'receiver_id' @@ -20,6 +27,11 @@ NECReceiver = remote_ns.NECReceiver PanasonicReceiver = remote_ns.PanasonicReceiver RawReceiver = remote_ns.RawReceiver SonyReceiver = remote_ns.SonyReceiver +RCSwitchRawReceiver = remote_ns.RCSwitchRawReceiver +RCSwitchTypeAReceiver = remote_ns.RCSwitchTypeAReceiver +RCSwitchTypeBReceiver = remote_ns.RCSwitchTypeBReceiver +RCSwitchTypeCReceiver = remote_ns.RCSwitchTypeCReceiver +RCSwitchTypeDReceiver = remote_ns.RCSwitchTypeDReceiver PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_LG): vol.Schema({ @@ -39,29 +51,54 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend vol.Required(CONF_COMMAND): cv.hex_uint32_t, }), vol.Optional(CONF_RAW): [vol.Any(vol.Coerce(int), cv.time_period_microseconds)], + vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_A): RC_SWITCH_TYPE_A_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA, + cv.GenerateID(CONF_REMOTE_RECEIVER_ID): cv.use_variable_id(RemoteReceiverComponent), cv.GenerateID(CONF_RECEIVER_ID): cv.declare_variable_id(RemoteReceiver), -}), cv.has_exactly_one_key(*IR_KEYS)) +}), cv.has_exactly_one_key(*REMOTE_KEYS)) -def receiver_base(config): - if CONF_LG in config: - conf = config[CONF_LG] - return LGReceiver.new(config[CONF_NAME], conf[CONF_DATA], conf[CONF_NBITS]) - elif CONF_NEC in config: - conf = config[CONF_NEC] - return NECReceiver.new(config[CONF_NAME], conf[CONF_ADDRESS], conf[CONF_COMMAND]) - elif CONF_PANASONIC in config: - conf = config[CONF_PANASONIC] - return PanasonicReceiver.new(config[CONF_NAME], conf[CONF_ADDRESS], conf[CONF_COMMAND]) - elif CONF_SONY in config: - conf = config[CONF_SONY] - return SonyReceiver.new(config[CONF_NAME], conf[CONF_DATA], conf[CONF_NBITS]) - elif CONF_RAW in config: +def receiver_base(full_config): + name = full_config[CONF_NAME] + key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS) + if key == CONF_LG: + return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) + elif key == CONF_NEC: + return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) + elif key == CONF_PANASONIC: + return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) + elif key == CONF_SONY: + return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) + elif key == CONF_RAW: data = ArrayInitializer(*config[CONF_RAW], multiline=False) - return RawReceiver.new(config[CONF_NAME], data) + return RawReceiver.new(name, data) + elif key == CONF_RC_SWITCH_RAW: + return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + binary_code(config[CONF_CODE]), len(config[CONF_CODE])) + elif key == CONF_RC_SWITCH_TYPE_A: + return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + binary_code(config[CONF_GROUP]), + binary_code(config[CONF_DEVICE]), + config[CONF_STATE]) + elif key == CONF_RC_SWITCH_TYPE_B: + return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + config[CONF_ADDRESS], config[CONF_CHANNEL], + config[CONF_STATE]) + elif key == CONF_RC_SWITCH_TYPE_C: + return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + ord(config[CONF_FAMILY][0]) - ord('a'), + config[CONF_GROUP], config[CONF_DEVICE], + config[CONF_STATE]) + elif key == CONF_RC_SWITCH_TYPE_D: + return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + ord(config[CONF_GROUP][0]) - ord('a'), + config[CONF_DEVICE], config[CONF_STATE]) else: - raise ValueError("Unknown receiver type {}".format(config)) + raise NotImplementedError("Unknown receiver type {}".format(config)) def to_code(config): diff --git a/esphomeyaml/components/binary_sensor/template.py b/esphomeyaml/components/binary_sensor/template.py index 7838a0f4b3..43beaeb752 100644 --- a/esphomeyaml/components/binary_sensor/template.py +++ b/esphomeyaml/components/binary_sensor/template.py @@ -3,7 +3,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import binary_sensor from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME -from esphomeyaml.helpers import App, Application, process_lambda, variable, optional, bool_ +from esphomeyaml.helpers import App, Application, process_lambda, variable, optional, bool_, add MakeTemplateBinarySensor = Application.MakeTemplateBinarySensor @@ -14,13 +14,15 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend def to_code(config): + rhs = App.make_template_binary_sensor(config[CONF_NAME]) + make = variable(config[CONF_MAKE_ID], rhs) + binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config) + template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], return_type=optional.template(bool_)): yield - rhs = App.make_template_binary_sensor(config[CONF_NAME], template_) - make = variable(config[CONF_MAKE_ID], rhs) - binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config) + add(make.Ptemplate_.set_template(template_)) BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR' diff --git a/esphomeyaml/components/cover/template.py b/esphomeyaml/components/cover/template.py index df1d1e2485..c6a85afc81 100644 --- a/esphomeyaml/components/cover/template.py +++ b/esphomeyaml/components/cover/template.py @@ -23,6 +23,8 @@ def to_code(config): rhs = App.make_template_cover(config[CONF_NAME]) make = variable(config[CONF_MAKE_ID], rhs) + cover.setup_cover(make.Ptemplate_, make.Pmqtt, config) + if CONF_LAMBDA in config: template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], @@ -41,7 +43,5 @@ def to_code(config): if CONF_OPTIMISTIC in config: add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC])) - cover.setup_cover(make.Ptemplate_, make.Pmqtt, config) - BUILD_FLAGS = '-DUSE_TEMPLATE_COVER' diff --git a/esphomeyaml/components/deep_sleep.py b/esphomeyaml/components/deep_sleep.py index fba19a37c0..b327dacbf0 100644 --- a/esphomeyaml/components/deep_sleep.py +++ b/esphomeyaml/components/deep_sleep.py @@ -15,6 +15,8 @@ def validate_pin_number(value): DeepSleepComponent = esphomelib_ns.DeepSleepComponent +EnterDeepSleepAction = esphomelib_ns.EnterDeepSleepAction +PreventDeepSleepAction = esphomelib_ns.PreventDeepSleepAction WAKEUP_PIN_MODES = { 'IGNORE': esphomelib_ns.WAKEUP_PIN_MODE_IGNORE, diff --git a/esphomeyaml/components/esp32_ble_tracker.py b/esphomeyaml/components/esp32_ble_tracker.py index c99762b8fc..600d31bd97 100644 --- a/esphomeyaml/components/esp32_ble_tracker.py +++ b/esphomeyaml/components/esp32_ble_tracker.py @@ -2,10 +2,12 @@ import voluptuous as vol from esphomeyaml import config_validation as cv from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32 -from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns +from esphomeyaml.core import HexInt +from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, ArrayInitializer ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +CONF_ESP32_BLE_ID = 'esp32_ble_id' ESP32BLETracker = esphomelib_ns.ESP32BLETracker CONFIG_SCHEMA = vol.Schema({ @@ -14,6 +16,11 @@ CONFIG_SCHEMA = vol.Schema({ }) +def make_address_array(address): + addr = [HexInt(i) for i in address.parts] + return ArrayInitializer(*addr, multiline=False) + + def to_code(config): rhs = App.make_esp32_ble_tracker() ble = Pvariable(config[CONF_ID], rhs) diff --git a/esphomeyaml/components/light/__init__.py b/esphomeyaml/components/light/__init__.py index 89151b0a48..07b1d63391 100644 --- a/esphomeyaml/components/light/__init__.py +++ b/esphomeyaml/components/light/__init__.py @@ -1,7 +1,14 @@ +import voluptuous as vol + import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \ - CONF_MQTT_ID, CONF_INTERNAL -from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component +from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \ + CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \ + CONF_GAMMA_CORRECT, \ + CONF_GREEN, CONF_ID, CONF_INTERNAL, CONF_LAMBDA, CONF_MQTT_ID, CONF_NAME, CONF_NUM_LEDS, \ + CONF_RANDOM, CONF_RED, CONF_SPEED, CONF_STATE, CONF_TRANSITION_LENGTH, CONF_UPDATE_INTERVAL, \ + CONF_WHITE, CONF_WIDTH +from esphomeyaml.helpers import Application, ArrayInitializer, Pvariable, RawExpression, \ + StructInitializer, add, add_job, esphomelib_ns, process_lambda, setup_mqtt_component PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ @@ -9,11 +16,174 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ light_ns = esphomelib_ns.namespace('light') LightState = light_ns.LightState +LightColorValues = light_ns.LightColorValues MQTTJSONLightComponent = light_ns.MQTTJSONLightComponent ToggleAction = light_ns.ToggleAction TurnOffAction = light_ns.TurnOffAction TurnOnAction = light_ns.TurnOnAction MakeLight = Application.MakeLight +RandomLightEffect = light_ns.RandomLightEffect +LambdaLightEffect = light_ns.LambdaLightEffect +StrobeLightEffect = light_ns.StrobeLightEffect +StrobeLightEffectColor = light_ns.StrobeLightEffectColor +FlickerLightEffect = light_ns.FlickerLightEffect +FastLEDLambdaLightEffect = light_ns.FastLEDLambdaLightEffect +FastLEDRainbowLightEffect = light_ns.FastLEDRainbowLightEffect +FastLEDColorWipeEffect = light_ns.FastLEDColorWipeEffect +FastLEDColorWipeEffectColor = light_ns.FastLEDColorWipeEffectColor +FastLEDScanEffect = light_ns.FastLEDScanEffect +FastLEDScanEffectColor = light_ns.FastLEDScanEffectColor +FastLEDTwinkleEffect = light_ns.FastLEDTwinkleEffect +FastLEDRandomTwinkleEffect = light_ns.FastLEDRandomTwinkleEffect +FastLEDFireworksEffect = light_ns.FastLEDFireworksEffect +FastLEDFlickerEffect = light_ns.FastLEDFlickerEffect +FastLEDLightOutputComponent = light_ns.FastLEDLightOutputComponent + +CONF_STROBE = 'strobe' +CONF_FLICKER = 'flicker' +CONF_FASTLED_LAMBDA = 'fastled_lambda' +CONF_FASTLED_RAINBOW = 'fastled_rainbow' +CONF_FASTLED_COLOR_WIPE = 'fastled_color_wipe' +CONF_FASTLED_SCAN = 'fastled_scan' +CONF_FASTLED_TWINKLE = 'fastled_twinkle' +CONF_FASTLED_RANDOM_TWINKLE = 'fastled_random_twinkle' +CONF_FASTLED_FIREWORKS = 'fastled_fireworks' +CONF_FASTLED_FLICKER = 'fastled_flicker' + +CONF_ADD_LED_INTERVAL = 'add_led_interval' +CONF_REVERSE = 'reverse' +CONF_MOVE_INTERVAL = 'move_interval' +CONF_TWINKLE_PROBABILITY = 'twinkle_probability' +CONF_PROGRESS_INTERVAL = 'progress_interval' +CONF_SPARK_PROBABILITY = 'spark_probability' +CONF_USE_RANDOM_COLOR = 'use_random_color' +CONF_FADE_OUT_RATE = 'fade_out_rate' +CONF_INTENSITY = 'intensity' + +BINARY_EFFECTS = [CONF_LAMBDA, CONF_STROBE] +MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + [CONF_FLICKER] +RGB_EFFECTS = MONOCHROMATIC_EFFECTS + [CONF_RANDOM] +FASTLED_EFFECTS = RGB_EFFECTS + [CONF_FASTLED_LAMBDA, CONF_FASTLED_RAINBOW, CONF_FASTLED_COLOR_WIPE, + CONF_FASTLED_SCAN, CONF_FASTLED_TWINKLE, + CONF_FASTLED_RANDOM_TWINKLE, CONF_FASTLED_FIREWORKS, + CONF_FASTLED_FLICKER] + +EFFECTS_SCHEMA = vol.Schema({ + vol.Optional(CONF_LAMBDA): vol.Schema({ + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_LAMBDA): cv.lambda_, + }), + vol.Optional(CONF_RANDOM): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(RandomLightEffect), + vol.Optional(CONF_NAME, default="Random"): cv.string, + vol.Optional(CONF_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + }), + vol.Optional(CONF_STROBE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(StrobeLightEffect), + vol.Optional(CONF_NAME, default="Strobe"): cv.string, + vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.All(vol.Schema({ + vol.Optional(CONF_STATE, default=True): cv.boolean, + vol.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + vol.Optional(CONF_RED, default=1.0): cv.percentage, + vol.Optional(CONF_GREEN, default=1.0): cv.percentage, + vol.Optional(CONF_BLUE, default=1.0): cv.percentage, + vol.Optional(CONF_WHITE, default=1.0): cv.percentage, + vol.Required(CONF_DURATION): cv.positive_time_period_milliseconds, + }), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, + CONF_WHITE))], vol.Length(min=2)), + }), + vol.Optional(CONF_FLICKER): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FlickerLightEffect), + vol.Optional(CONF_NAME, default="Flicker"): cv.string, + vol.Optional(CONF_ALPHA): cv.percentage, + vol.Optional(CONF_INTENSITY): cv.percentage, + }), + vol.Optional(CONF_FASTLED_LAMBDA): vol.Schema({ + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_LAMBDA): cv.lambda_, + }), + vol.Optional(CONF_FASTLED_RAINBOW): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRainbowLightEffect), + vol.Optional(CONF_NAME, default="Rainbow"): cv.string, + vol.Optional(CONF_SPEED): cv.uint32_t, + vol.Optional(CONF_WIDTH): cv.uint32_t, + }), + vol.Optional(CONF_FASTLED_COLOR_WIPE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDColorWipeEffect), + vol.Optional(CONF_NAME, default="Color Wipe"): cv.string, + vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.Schema({ + vol.Optional(CONF_RED, default=1.0): cv.percentage, + vol.Optional(CONF_GREEN, default=1.0): cv.percentage, + vol.Optional(CONF_BLUE, default=1.0): cv.percentage, + vol.Optional(CONF_RANDOM, default=False): cv.boolean, + vol.Required(CONF_NUM_LEDS): vol.All(cv.uint32_t, vol.Range(min=1)), + })]), + vol.Optional(CONF_ADD_LED_INTERVAL): cv.positive_time_period_milliseconds, + vol.Optional(CONF_REVERSE): cv.boolean, + }), + vol.Optional(CONF_FASTLED_SCAN): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDScanEffect), + vol.Optional(CONF_NAME, default="Scan"): cv.string, + vol.Optional(CONF_MOVE_INTERVAL): cv.positive_time_period_milliseconds, + }), + vol.Optional(CONF_FASTLED_TWINKLE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDTwinkleEffect), + vol.Optional(CONF_NAME, default="Twinkle"): cv.string, + vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage, + vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds, + }), + vol.Optional(CONF_FASTLED_RANDOM_TWINKLE): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRandomTwinkleEffect), + vol.Optional(CONF_NAME, default="Random Twinkle"): cv.string, + vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage, + vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds, + }), + vol.Optional(CONF_FASTLED_FIREWORKS): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFireworksEffect), + vol.Optional(CONF_NAME, default="Fireworks"): cv.string, + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + vol.Optional(CONF_SPARK_PROBABILITY): cv.percentage, + vol.Optional(CONF_USE_RANDOM_COLOR): cv.boolean, + vol.Optional(CONF_FADE_OUT_RATE): cv.uint8_t, + }), + vol.Optional(CONF_FASTLED_FLICKER): vol.Schema({ + cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFlickerEffect), + vol.Optional(CONF_NAME, default="FastLED Flicker"): cv.string, + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + vol.Optional(CONF_INTENSITY): cv.percentage, + }), +}) + + +def validate_effects(allowed_effects): + def validator(value): + value = cv.ensure_list(value) + names = set() + ret = [] + for i, effect in enumerate(value): + if not isinstance(effect, dict): + raise vol.Invalid("Each effect must be a dictionary, not {}".format(type(value))) + if len(effect) > 1: + raise vol.Invalid("Each entry in the 'effects:' option must be a single effect.") + if not effect: + raise vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i)) + key = next(iter(effect.keys())) + if key not in allowed_effects: + raise vol.Invalid("The effect '{}' does not exist or is not allowed for this " + "light type".format(key)) + effect[key] = effect[key] or {} + conf = EFFECTS_SCHEMA(effect) + name = conf[key][CONF_NAME] + if name in names: + raise vol.Invalid(u"Found the effect name '{}' twice. All effects must have " + u"unique names".format(name)) + names.add(name) + ret.append(conf) + return ret + + return validator + LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(LightState), @@ -23,6 +193,124 @@ LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ LIGHT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(LIGHT_SCHEMA.schema) +def build_effect(full_config): + key, config = next(iter(full_config.items())) + if key == CONF_LAMBDA: + lambda_ = None + for lambda_ in process_lambda(config[CONF_LAMBDA], []): + yield None + yield LambdaLightEffect.new(config[CONF_NAME], lambda_) + elif key == CONF_RANDOM: + rhs = RandomLightEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_TRANSITION_LENGTH in config: + add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH])) + if CONF_UPDATE_INTERVAL in config: + add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) + yield effect + elif key == CONF_STROBE: + rhs = StrobeLightEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + colors = [] + for color in config.get(CONF_COLORS, []): + colors.append(StructInitializer( + StrobeLightEffectColor, + ('color', LightColorValues(color[CONF_STATE], color[CONF_BRIGHTNESS], + color[CONF_RED], color[CONF_GREEN], color[CONF_BLUE], + color[CONF_WHITE])), + ('duration', color[CONF_DURATION]), + )) + if colors: + add(effect.set_colors(ArrayInitializer(*colors))) + yield effect + elif key == CONF_FLICKER: + rhs = FlickerLightEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_ALPHA in config: + add(effect.set_alpha(config[CONF_ALPHA])) + if CONF_INTENSITY in config: + add(effect.set_intensity(config[CONF_INTENSITY])) + yield effect + elif key == CONF_FASTLED_LAMBDA: + lambda_ = None + args = [(RawExpression('FastLEDLightOutputComponent &'), 'fastled')] + for lambda_ in process_lambda(config[CONF_LAMBDA], args): + yield None + yield FastLEDLambdaLightEffect.new(config[CONF_NAME], lambda_) + elif key == CONF_FASTLED_RAINBOW: + rhs = FastLEDRainbowLightEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_SPEED in config: + add(effect.set_speed(config[CONF_SPEED])) + if CONF_WIDTH in config: + add(effect.set_width(config[CONF_WIDTH])) + yield effect + elif key == CONF_FASTLED_COLOR_WIPE: + rhs = FastLEDColorWipeEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_ADD_LED_INTERVAL in config: + add(effect.set_add_led_interval(config[CONF_ADD_LED_INTERVAL])) + if CONF_REVERSE in config: + add(effect.set_reverse(config[CONF_REVERSE])) + colors = [] + for color in config.get(CONF_COLORS, []): + colors.append(StructInitializer( + FastLEDColorWipeEffectColor, + ('r', color[CONF_RED]), + ('g', color[CONF_GREEN]), + ('b', color[CONF_BLUE]), + ('random', color[CONF_RANDOM]), + ('num_leds', color[CONF_NUM_LEDS]), + )) + if colors: + add(effect.set_colors(ArrayInitializer(*colors))) + yield effect + elif key == CONF_FASTLED_SCAN: + rhs = FastLEDScanEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_MOVE_INTERVAL in config: + add(effect.set_move_interval(config[CONF_MOVE_INTERVAL])) + yield effect + elif key == CONF_FASTLED_TWINKLE: + rhs = FastLEDTwinkleEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_TWINKLE_PROBABILITY in config: + add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) + if CONF_PROGRESS_INTERVAL in config: + add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL])) + yield effect + elif key == CONF_FASTLED_RANDOM_TWINKLE: + rhs = FastLEDRandomTwinkleEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_TWINKLE_PROBABILITY in config: + add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) + if CONF_PROGRESS_INTERVAL in config: + add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL])) + yield effect + elif key == CONF_FASTLED_FIREWORKS: + rhs = FastLEDFireworksEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_UPDATE_INTERVAL in config: + add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) + if CONF_SPARK_PROBABILITY in config: + add(effect.set_spark_probability(config[CONF_SPARK_PROBABILITY])) + if CONF_USE_RANDOM_COLOR in config: + add(effect.set_spark_probability(config[CONF_USE_RANDOM_COLOR])) + if CONF_FADE_OUT_RATE in config: + add(effect.set_spark_probability(config[CONF_FADE_OUT_RATE])) + yield effect + elif key == CONF_FASTLED_FLICKER: + rhs = FastLEDFlickerEffect.new(config[CONF_NAME]) + effect = Pvariable(config[CONF_EFFECT_ID], rhs) + if CONF_UPDATE_INTERVAL in config: + add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) + if CONF_INTENSITY in config: + add(effect.set_intensity(config[CONF_INTENSITY])) + yield effect + else: + raise NotImplementedError("Effect {} not implemented".format(next(config.keys()))) + + def setup_light_core_(light_var, mqtt_var, config): if CONF_INTERNAL in config: add(light_var.set_internal(config[CONF_INTERNAL])) @@ -30,6 +318,13 @@ def setup_light_core_(light_var, mqtt_var, config): add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH])) if CONF_GAMMA_CORRECT in config: add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT])) + effects = [] + for conf in config.get(CONF_EFFECTS, []): + for effect in build_effect(conf): + yield + effects.append(effect) + if effects: + add(light_var.add_effects(ArrayInitializer(*effects))) setup_mqtt_component(mqtt_var, config) @@ -37,7 +332,7 @@ def setup_light_core_(light_var, mqtt_var, config): def setup_light(light_obj, mqtt_obj, config): light_var = Pvariable(config[CONF_ID], light_obj, has_side_effects=False) mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False) - setup_light_core_(light_var, mqtt_var, config) + add_job(setup_light_core_, light_var, mqtt_var, config) BUILD_FLAGS = '-DUSE_LIGHT' diff --git a/esphomeyaml/components/light/binary.py b/esphomeyaml/components/light/binary.py index b32de62950..5468d54d32 100644 --- a/esphomeyaml/components/light/binary.py +++ b/esphomeyaml/components/light/binary.py @@ -2,12 +2,13 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import light -from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT +from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT, CONF_EFFECTS from esphomeyaml.helpers import App, get_variable, variable PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), vol.Required(CONF_OUTPUT): cv.use_variable_id(None), + vol.Optional(CONF_EFFECTS): light.validate_effects(light.BINARY_EFFECTS), })) diff --git a/esphomeyaml/components/light/fastled_clockless.py b/esphomeyaml/components/light/fastled_clockless.py index 4cb5c5580e..1326853bd5 100644 --- a/esphomeyaml/components/light/fastled_clockless.py +++ b/esphomeyaml/components/light/fastled_clockless.py @@ -6,7 +6,7 @@ from esphomeyaml.components import light from esphomeyaml.components.power_supply import PowerSupplyComponent from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \ - CONF_RGB_ORDER + CONF_RGB_ORDER, CONF_EFFECTS from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \ get_variable, variable @@ -68,6 +68,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), + vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS), }), validate) diff --git a/esphomeyaml/components/light/fastled_spi.py b/esphomeyaml/components/light/fastled_spi.py index fb5d86ed73..ec1ccb3509 100644 --- a/esphomeyaml/components/light/fastled_spi.py +++ b/esphomeyaml/components/light/fastled_spi.py @@ -6,7 +6,7 @@ from esphomeyaml.components import light from esphomeyaml.components.power_supply import PowerSupplyComponent from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \ - CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER + CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER, CONF_EFFECTS from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \ get_variable, variable @@ -46,6 +46,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), + vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS), })) diff --git a/esphomeyaml/components/light/monochromatic.py b/esphomeyaml/components/light/monochromatic.py index ce9a88bbfb..9ab8b3d53a 100644 --- a/esphomeyaml/components/light/monochromatic.py +++ b/esphomeyaml/components/light/monochromatic.py @@ -3,7 +3,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import light from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \ - CONF_NAME, CONF_OUTPUT + CONF_NAME, CONF_OUTPUT, CONF_EFFECTS from esphomeyaml.helpers import App, get_variable, variable PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ @@ -11,6 +11,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_OUTPUT): cv.use_variable_id(None), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, + vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS), })) diff --git a/esphomeyaml/components/light/rgb.py b/esphomeyaml/components/light/rgb.py index aeeb97ff4f..7f2f360f5d 100644 --- a/esphomeyaml/components/light/rgb.py +++ b/esphomeyaml/components/light/rgb.py @@ -3,7 +3,7 @@ import voluptuous as vol 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_MAKE_ID, CONF_NAME, CONF_RED + CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_EFFECTS from esphomeyaml.helpers import App, get_variable, variable PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ @@ -13,6 +13,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_BLUE): cv.use_variable_id(None), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, + vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS), })) diff --git a/esphomeyaml/components/light/rgbw.py b/esphomeyaml/components/light/rgbw.py index 6186390f2a..edd8919eb8 100644 --- a/esphomeyaml/components/light/rgbw.py +++ b/esphomeyaml/components/light/rgbw.py @@ -3,7 +3,7 @@ import voluptuous as vol 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_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE + CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE, CONF_EFFECTS from esphomeyaml.helpers import App, get_variable, variable PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ @@ -14,6 +14,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ vol.Required(CONF_WHITE): cv.use_variable_id(None), vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, + vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS), })) diff --git a/esphomeyaml/components/output/__init__.py b/esphomeyaml/components/output/__init__.py index faf8825bd1..bb7ca1ff8e 100644 --- a/esphomeyaml/components/output/__init__.py +++ b/esphomeyaml/components/output/__init__.py @@ -23,6 +23,9 @@ FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({ FLOAT_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FLOAT_OUTPUT_SCHEMA.schema) output_ns = esphomelib_ns.namespace('output') +TurnOffAction = output_ns.TurnOffAction +TurnOnAction = output_ns.TurnOnAction +SetLevelAction = output_ns.SetLevelAction def setup_output_platform_(obj, config, skip_power_supply=False): diff --git a/esphomeyaml/components/pn532.py b/esphomeyaml/components/pn532.py new file mode 100644 index 0000000000..e3e37b6b0f --- /dev/null +++ b/esphomeyaml/components/pn532.py @@ -0,0 +1,34 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +from esphomeyaml.components import binary_sensor +from esphomeyaml.components.spi import SPIComponent +from esphomeyaml.const import CONF_CS, CONF_ID, CONF_SPI_ID, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression + +DEPENDENCIES = ['spi'] + +PN532Component = binary_sensor.binary_sensor_ns.PN532Component + +CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(PN532Component), + cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), + vol.Required(CONF_CS): pins.gpio_output_pin_schema, + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +})]) + + +def to_code(config): + for conf in config: + spi = None + for spi in get_variable(conf[CONF_SPI_ID]): + yield + cs = None + for cs in gpio_output_pin_expression(conf[CONF_CS]): + yield + rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL)) + Pvariable(conf[CONF_ID], rhs) + + +BUILD_FLAGS = '-DUSE_PN532' diff --git a/esphomeyaml/components/rdm6300.py b/esphomeyaml/components/rdm6300.py new file mode 100644 index 0000000000..7e0e5f14cf --- /dev/null +++ b/esphomeyaml/components/rdm6300.py @@ -0,0 +1,28 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import binary_sensor +from esphomeyaml.components.uart import UARTComponent +from esphomeyaml.const import CONF_ID, CONF_UART_ID +from esphomeyaml.helpers import App, Pvariable, get_variable + +DEPENDENCIES = ['uart'] + +RDM6300Component = binary_sensor.binary_sensor_ns.RDM6300Component + +CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(RDM6300Component), + cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), +})]) + + +def to_code(config): + for conf in config: + uart = None + for uart in get_variable(conf[CONF_UART_ID]): + yield + rhs = App.make_rdm6300_component(uart) + Pvariable(conf[CONF_ID], rhs) + + +BUILD_FLAGS = '-DUSE_RDM6300' diff --git a/esphomeyaml/components/remote_receiver.py b/esphomeyaml/components/remote_receiver.py index 54b35979e4..7bd3ff5b2f 100644 --- a/esphomeyaml/components/remote_receiver.py +++ b/esphomeyaml/components/remote_receiver.py @@ -16,6 +16,7 @@ DUMPERS = { 'panasonic': remote_ns.PanasonicDumper, 'raw': remote_ns.RawDumper, 'sony': remote_ns.SonyDumper, + 'rc_switch': remote_ns.RCSwitchDumper, } @@ -23,7 +24,7 @@ def validate_dumpers_all(value): if not isinstance(value, (str, unicode)): raise vol.Invalid("Not valid dumpers") if value.upper() == "ALL": - return list(DUMPERS) + return list(sorted(list(DUMPERS))) raise vol.Invalid("Not valid dumpers") diff --git a/esphomeyaml/components/remote_transmitter.py b/esphomeyaml/components/remote_transmitter.py index ba49af970a..d7437051a2 100644 --- a/esphomeyaml/components/remote_transmitter.py +++ b/esphomeyaml/components/remote_transmitter.py @@ -2,13 +2,79 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins -from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN +from esphomeyaml.components.switch.remote_transmitter import rc_switch_protocols, RCSwitchProtocol +from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_DUTY_PERCENT, CONF_CHANNEL, CONF_CODE, \ + CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_ID, CONF_INVERTED, CONF_ONE, CONF_PIN, \ + CONF_PROTOCOL, CONF_PULSE_LENGTH, CONF_STATE, CONF_SYNC, CONF_ZERO +from esphomeyaml.core import HexInt from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression remote_ns = esphomelib_ns.namespace('remote') RemoteTransmitterComponent = remote_ns.RemoteTransmitterComponent + +def validate_rc_switch_code(value): + if not isinstance(value, (str, unicode)): + raise vol.Invalid("All RCSwitch codes must be in quotes ('')") + for c in value: + if c not in ('0', '1'): + raise vol.Invalid(u"Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed" + u"".format(c)) + if len(value) > 32: + raise vol.Invalid("Maximum length for RCSwitch codes is 32, code '{}' has length {}" + "".format(value, len(value))) + if not value: + raise vol.Invalid("RCSwitch code must not be empty") + return value + + +RC_SWITCH_TIMING_SCHEMA = vol.All([cv.uint8_t], vol.Length(min=2, max=2)) + +RC_SWITCH_PROTOCOL_SCHEMA = vol.Any( + vol.All(vol.Coerce(int), vol.Range(min=1, max=7)), + vol.Schema({ + vol.Required(CONF_PULSE_LENGTH): cv.uint32_t, + vol.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA, + vol.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA, + vol.Optional(CONF_ONE, default=[3, 1]): RC_SWITCH_TIMING_SCHEMA, + vol.Optional(CONF_INVERTED, default=False): cv.boolean, + }) +) + +RC_SWITCH_RAW_SCHEMA = vol.Schema({ + vol.Required(CONF_CODE): validate_rc_switch_code, + vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, +}) +RC_SWITCH_TYPE_A_SCHEMA = vol.Schema({ + vol.Required(CONF_GROUP): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)), + vol.Required(CONF_DEVICE): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)), + vol.Required(CONF_STATE): cv.boolean, + vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, +}) +RC_SWITCH_TYPE_B_SCHEMA = vol.Schema({ + vol.Required(CONF_ADDRESS): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), + vol.Required(CONF_CHANNEL): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), + vol.Required(CONF_STATE): cv.boolean, + vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, +}) +RC_SWITCH_TYPE_C_SCHEMA = vol.Schema({ + vol.Required(CONF_FAMILY): vol.All( + cv.string, vol.Lower, + cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p')), + vol.Required(CONF_GROUP): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), + vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), + vol.Required(CONF_STATE): cv.boolean, + vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, +}) +RC_SWITCH_TYPE_D_SCHEMA = vol.Schema({ + vol.Required(CONF_GROUP): vol.All(cv.string, vol.Lower, cv.one_of('a', 'b', 'c', 'd')), + vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=3)), + vol.Required(CONF_STATE): cv.boolean, + vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, +}) + CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ cv.GenerateID(): cv.declare_variable_id(RemoteTransmitterComponent), vol.Required(CONF_PIN): pins.gpio_output_pin_schema, @@ -17,6 +83,24 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ })]) +def build_rc_switch_protocol(config): + if isinstance(config, int): + return rc_switch_protocols[config] + pl = config[CONF_PULSE_LENGTH] + return RCSwitchProtocol(config[CONF_SYNC][0] * pl, config[CONF_SYNC][1] * pl, + config[CONF_ZERO][0] * pl, config[CONF_ZERO][1] * pl, + config[CONF_ONE][0] * pl, config[CONF_ONE][1] * pl, + config[CONF_INVERTED]) + + +def binary_code(value): + code = 0 + for val in value: + code <<= 1 + code |= val == '1' + return HexInt(code) + + def to_code(config): for conf in config: pin = None diff --git a/esphomeyaml/components/sensor/ble_rssi.py b/esphomeyaml/components/sensor/ble_rssi.py new file mode 100644 index 0000000000..2b98f45c44 --- /dev/null +++ b/esphomeyaml/components/sensor/ble_rssi.py @@ -0,0 +1,23 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ + make_address_array +from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME +from esphomeyaml.helpers import get_variable + +DEPENDENCIES = ['esp32_ble_tracker'] + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + vol.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker) +})) + + +def to_code(config): + hub = None + for hub in get_variable(config[CONF_ESP32_BLE_ID]): + yield + rhs = hub.make_rssi_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) + sensor.register_sensor(rhs, config) diff --git a/esphomeyaml/components/sensor/bmp280.py b/esphomeyaml/components/sensor/bmp280.py new file mode 100644 index 0000000000..d2a04af7ee --- /dev/null +++ b/esphomeyaml/components/sensor/bmp280.py @@ -0,0 +1,67 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ADDRESS, CONF_IIR_FILTER, CONF_MAKE_ID, \ + CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Application, add, variable + +DEPENDENCIES = ['i2c'] + +OVERSAMPLING_OPTIONS = { + 'NONE': sensor.sensor_ns.BMP280_OVERSAMPLING_NONE, + '1X': sensor.sensor_ns.BMP280_OVERSAMPLING_1X, + '2X': sensor.sensor_ns.BMP280_OVERSAMPLING_2X, + '4X': sensor.sensor_ns.BMP280_OVERSAMPLING_4X, + '8X': sensor.sensor_ns.BMP280_OVERSAMPLING_8X, + '16X': sensor.sensor_ns.BMP280_OVERSAMPLING_16X, +} + +IIR_FILTER_OPTIONS = { + 'OFF': sensor.sensor_ns.BMP280_IIR_FILTER_OFF, + '2X': sensor.sensor_ns.BMP280_IIR_FILTER_2X, + '4X': sensor.sensor_ns.BMP280_IIR_FILTER_4X, + '8X': sensor.sensor_ns.BMP280_IIR_FILTER_8X, + '16X': sensor.sensor_ns.BMP280_IIR_FILTER_16X, +} + +BMP280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ + vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)), +}) + +MakeBMP280Sensor = Application.MakeBMP280Sensor + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP280Sensor), + vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address, + vol.Required(CONF_TEMPERATURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA), + vol.Required(CONF_PRESSURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA), + vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +}) + + +def to_code(config): + rhs = App.make_bmp280_sensor(config[CONF_TEMPERATURE][CONF_NAME], + config[CONF_PRESSURE][CONF_NAME], + config[CONF_ADDRESS], + config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + bmp280 = make.Pbmp280 + if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]: + constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]] + add(bmp280.set_temperature_oversampling(constant)) + if CONF_OVERSAMPLING in config[CONF_PRESSURE]: + constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]] + add(bmp280.set_pressure_oversampling(constant)) + if CONF_IIR_FILTER in config: + constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]] + add(bmp280.set_iir_filter(constant)) + + sensor.setup_sensor(bmp280.Pget_temperature_sensor(), make.Pmqtt_temperature, + config[CONF_TEMPERATURE]) + sensor.setup_sensor(bmp280.Pget_pressure_sensor(), make.Pmqtt_pressure, + config[CONF_PRESSURE]) + + +BUILD_FLAGS = '-DUSE_BMP280' diff --git a/esphomeyaml/components/sensor/hmc5883l.py b/esphomeyaml/components/sensor/hmc5883l.py new file mode 100644 index 0000000000..7fdae9f831 --- /dev/null +++ b/esphomeyaml/components/sensor/hmc5883l.py @@ -0,0 +1,73 @@ +# coding=utf-8 +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_RANGE +from esphomeyaml.helpers import App, Pvariable, add + +DEPENDENCIES = ['i2c'] + +CONF_FIELD_STRENGTH_X = 'field_strength_x' +CONF_FIELD_STRENGTH_Y = 'field_strength_y' +CONF_FIELD_STRENGTH_Z = 'field_strength_z' +CONF_HEADING = 'heading' + +HMC5883LComponent = sensor.sensor_ns.HMC5883LComponent +HMC5883LFieldStrengthSensor = sensor.sensor_ns.HMC5883LFieldStrengthSensor +HMC5883LHeadingSensor = sensor.sensor_ns.HMC5883LHeadingSensor + +HMC5883L_RANGES = { + 88: sensor.sensor_ns.HMC5883L_RANGE_88_UT, + 130: sensor.sensor_ns.HMC5883L_RANGE_130_UT, + 190: sensor.sensor_ns.HMC5883L_RANGE_190_UT, + 250: sensor.sensor_ns.HMC5883L_RANGE_250_UT, + 400: sensor.sensor_ns.HMC5883L_RANGE_400_UT, + 470: sensor.sensor_ns.HMC5883L_RANGE_470_UT, + 560: sensor.sensor_ns.HMC5883L_RANGE_560_UT, + 810: sensor.sensor_ns.HMC5883L_RANGE_810_UT, +} + + +def validate_range(value): + value = cv.string(value) + if value.endswith(u'µT') or value.endswith('uT'): + value = value[:-2] + return cv.one_of(*HMC5883L_RANGES)(value) + + +PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(HMC5883LComponent), + vol.Optional(CONF_ADDRESS): cv.i2c_address, + vol.Optional(CONF_FIELD_STRENGTH_X): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_HEADING): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + vol.Optional(CONF_RANGE): validate_range, +}), cv.has_at_least_one_key(CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, + CONF_HEADING)) + + +def to_code(config): + rhs = App.make_hmc5883l(config.get(CONF_UPDATE_INTERVAL)) + hmc = Pvariable(config[CONF_ID], rhs) + if CONF_ADDRESS in config: + add(hmc.set_address(config[CONF_ADDRESS])) + if CONF_RANGE in config: + add(hmc.set_range(HMC5883L_RANGES[config[CONF_RANGE]])) + if CONF_FIELD_STRENGTH_X in config: + conf = config[CONF_FIELD_STRENGTH_X] + sensor.register_sensor(hmc.Pmake_x_sensor(conf[CONF_NAME]), conf) + if CONF_FIELD_STRENGTH_Y in config: + conf = config[CONF_FIELD_STRENGTH_Y] + sensor.register_sensor(hmc.Pmake_y_sensor(conf[CONF_NAME]), conf) + if CONF_FIELD_STRENGTH_Z in config: + conf = config[CONF_FIELD_STRENGTH_Z] + sensor.register_sensor(hmc.Pmake_z_sensor(conf[CONF_NAME]), conf) + if CONF_HEADING in config: + conf = config[CONF_HEADING] + sensor.register_sensor(hmc.Pmake_heading_sensor(conf[CONF_NAME]), conf) + + +BUILD_FLAGS = '-DUSE_HMC5883L' diff --git a/esphomeyaml/components/sensor/hx711.py b/esphomeyaml/components/sensor/hx711.py new file mode 100644 index 0000000000..2a0ab4bc75 --- /dev/null +++ b/esphomeyaml/components/sensor/hx711.py @@ -0,0 +1,47 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_GAIN, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable + +MakeHX711Sensor = Application.MakeHX711Sensor + +CONF_DOUT_PIN = 'dout_pin' +CONF_SCK_PIN = 'sck_pin' + +GAINS = { + 128: sensor.sensor_ns.HX711_GAIN_128, + 32: sensor.sensor_ns.HX711_GAIN_32, + 64: sensor.sensor_ns.HX711_GAIN_64, +} + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHX711Sensor), + vol.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema, + vol.Required(CONF_SCK_PIN): pins.gpio_output_pin_schema, + vol.Optional(CONF_GAIN): vol.All(cv.int_, cv.one_of(*GAINS)), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +})) + + +def to_code(config): + dout_pin = None + for dout_pin in gpio_input_pin_expression(config[CONF_DOUT_PIN]): + yield + sck_pin = None + for sck_pin in gpio_input_pin_expression(config[CONF_SCK_PIN]): + yield + + rhs = App.make_hx711_sensor(config[CONF_NAME], dout_pin, sck_pin, + config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + + if CONF_GAIN in config: + add(make.Phx711.set_gain(GAINS[CONF_GAIN])) + + sensor.setup_sensor(make.Phx711, make.Pmqtt, config) + + +BUILD_FLAGS = '-DUSE_HX711' diff --git a/esphomeyaml/components/sensor/ina219.py b/esphomeyaml/components/sensor/ina219.py new file mode 100644 index 0000000000..01f9388347 --- /dev/null +++ b/esphomeyaml/components/sensor/ina219.py @@ -0,0 +1,53 @@ +# coding=utf-8 +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ADDRESS, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, \ + CONF_MAX_VOLTAGE, CONF_NAME, CONF_POWER, CONF_UPDATE_INTERVAL, CONF_BUS_VOLTAGE, \ + CONF_SHUNT_VOLTAGE, CONF_SHUNT_RESISTANCE +from esphomeyaml.helpers import App, Pvariable + +DEPENDENCIES = ['i2c'] + +INA219Component = sensor.sensor_ns.INA219Component +INA219VoltageSensor = sensor.sensor_ns.INA219VoltageSensor +INA219CurrentSensor = sensor.sensor_ns.INA219CurrentSensor +INA219PowerSensor = sensor.sensor_ns.INA219PowerSensor + +PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA219Component), + vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address, + vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance, + vol.Range(min=0.0, max=32.0)), + vol.Optional(CONF_MAX_VOLTAGE, default=32.0): vol.All(cv.voltage, vol.Range(min=0.0, max=32.0)), + vol.Optional(CONF_MAX_CURRENT, default=3.2): vol.All(cv.current, vol.Range(min=0.0)), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, + CONF_POWER)) + + +def to_code(config): + rhs = App.make_ina219(config[CONF_SHUNT_RESISTANCE], + config[CONF_MAX_CURRENT], config[CONF_MAX_VOLTAGE], + config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL)) + ina = Pvariable(config[CONF_ID], rhs) + if CONF_BUS_VOLTAGE in config: + conf = config[CONF_BUS_VOLTAGE] + sensor.register_sensor(ina.Pmake_bus_voltage_sensor(conf[CONF_NAME]), conf) + if CONF_SHUNT_VOLTAGE in config: + conf = config[CONF_SHUNT_VOLTAGE] + sensor.register_sensor(ina.Pmake_shunt_voltage_sensor(conf[CONF_NAME]), conf) + if CONF_CURRENT in config: + conf = config[CONF_CURRENT] + sensor.register_sensor(ina.Pmake_current_sensor(conf[CONF_NAME]), conf) + if CONF_POWER in config: + conf = config[CONF_POWER] + sensor.register_sensor(ina.Pmake_power_sensor(conf[CONF_NAME]), conf) + + +BUILD_FLAGS = '-DUSE_INA219' diff --git a/esphomeyaml/components/sensor/ina3221.py b/esphomeyaml/components/sensor/ina3221.py new file mode 100644 index 0000000000..9921113a0d --- /dev/null +++ b/esphomeyaml/components/sensor/ina3221.py @@ -0,0 +1,64 @@ +# coding=utf-8 +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ADDRESS, CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_NAME, \ + CONF_POWER, CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Pvariable, add + +DEPENDENCIES = ['i2c'] + +CONF_CHANNEL_1 = 'channel_1' +CONF_CHANNEL_2 = 'channel_2' +CONF_CHANNEL_3 = 'channel_3' + +INA3221Component = sensor.sensor_ns.INA3221Component +INA3221VoltageSensor = sensor.sensor_ns.INA3221VoltageSensor +INA3221CurrentSensor = sensor.sensor_ns.INA3221CurrentSensor +INA3221PowerSensor = sensor.sensor_ns.INA3221PowerSensor + +INA3221_CHANNEL_SCHEMA = vol.All(vol.Schema({ + vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance, + vol.Range(min=0.0, max=32.0)), +}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, + CONF_POWER)) + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(INA3221Component), + vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address, + vol.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA, + vol.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA, + vol.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA, + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +}) + + +def to_code(config): + rhs = App.make_ina3221(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL)) + ina = Pvariable(config[CONF_ID], rhs) + for i, channel in enumerate([CONF_CHANNEL_1, CONF_CHANNEL_2, CONF_CHANNEL_3]): + if channel not in config: + continue + conf = config[channel] + if CONF_SHUNT_RESISTANCE in conf: + add(ina.set_shunt_resistance(i, conf[CONF_SHUNT_RESISTANCE])) + if CONF_BUS_VOLTAGE in conf: + c = conf[CONF_BUS_VOLTAGE] + sensor.register_sensor(ina.Pmake_bus_voltage_sensor(i, c[CONF_NAME]), c) + if CONF_SHUNT_VOLTAGE in conf: + c = conf[CONF_SHUNT_VOLTAGE] + sensor.register_sensor(ina.Pmake_shunt_voltage_sensor(i, c[CONF_NAME]), c) + if CONF_CURRENT in conf: + c = conf[CONF_CURRENT] + sensor.register_sensor(ina.Pmake_current_sensor(i, c[CONF_NAME]), c) + if CONF_POWER in conf: + c = conf[CONF_POWER] + sensor.register_sensor(ina.Pmake_power_sensor(i, c[CONF_NAME]), c) + + +BUILD_FLAGS = '-DUSE_INA3221' diff --git a/esphomeyaml/components/sensor/max6675.py b/esphomeyaml/components/sensor/max6675.py index 1737b5c5b2..ce22f64720 100644 --- a/esphomeyaml/components/sensor/max6675.py +++ b/esphomeyaml/components/sensor/max6675.py @@ -3,33 +3,28 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import sensor -from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN_CLOCK, CONF_PIN_CS, CONF_PIN_MISO, \ - CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, \ - gpio_output_pin_expression, variable +from esphomeyaml.components.spi import SPIComponent +from esphomeyaml.const import CONF_CS, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Application, get_variable, gpio_output_pin_expression, variable MakeMAX6675Sensor = Application.MakeMAX6675Sensor PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor), - vol.Required(CONF_PIN_CS): pins.gpio_output_pin_schema, - vol.Required(CONF_PIN_CLOCK): pins.gpio_output_pin_schema, - vol.Required(CONF_PIN_MISO): pins.gpio_input_pin_schema, + cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), + vol.Required(CONF_CS): pins.gpio_output_pin_schema, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, })) def to_code(config): - pin_cs = None - for pin_cs in gpio_output_pin_expression(config[CONF_PIN_CS]): + spi = None + for spi in get_variable(config[CONF_SPI_ID]): yield - pin_clock = None - for pin_clock in gpio_output_pin_expression(config[CONF_PIN_CLOCK]): + cs = None + for cs in gpio_output_pin_expression(config[CONF_CS]): yield - pin_miso = None - for pin_miso in gpio_input_pin_expression(config[CONF_PIN_MISO]): - yield - rhs = App.make_max6675_sensor(config[CONF_NAME], pin_cs, pin_clock, pin_miso, + rhs = App.make_max6675_sensor(config[CONF_NAME], spi, cs, config.get(CONF_UPDATE_INTERVAL)) make = variable(config[CONF_MAKE_ID], rhs) sensor.setup_sensor(make.Pmax6675, make.Pmqtt, config) diff --git a/esphomeyaml/components/sensor/mhz19.py b/esphomeyaml/components/sensor/mhz19.py new file mode 100644 index 0000000000..4c40cd1a64 --- /dev/null +++ b/esphomeyaml/components/sensor/mhz19.py @@ -0,0 +1,38 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.components.uart import UARTComponent +from esphomeyaml.const import CONF_CO2, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, CONF_UART_ID, \ + CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Application, get_variable, variable + +DEPENDENCIES = ['uart'] + +MakeMHZ19Sensor = Application.MakeMHZ19Sensor + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMHZ19Sensor), + cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), + vol.Required(CONF_CO2): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +}) + + +def to_code(config): + uart = None + for uart in get_variable(config[CONF_UART_ID]): + yield + rhs = App.make_mhz19_sensor(uart, config[CONF_CO2][CONF_NAME], + config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + mhz19 = make.Pmhz19 + sensor.setup_sensor(mhz19.Pget_co2_sensor(), make.Pmqtt, config[CONF_CO2]) + + if CONF_TEMPERATURE in config: + sensor.register_sensor(mhz19.Pmake_temperature_sensor(config[CONF_TEMPERATURE][CONF_NAME]), + config[CONF_TEMPERATURE]) + + +BUILD_FLAGS = '-DUSE_MHZ19' diff --git a/esphomeyaml/components/sensor/ms5611.py b/esphomeyaml/components/sensor/ms5611.py new file mode 100644 index 0000000000..e0f337d4bb --- /dev/null +++ b/esphomeyaml/components/sensor/ms5611.py @@ -0,0 +1,37 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \ + CONF_TEMPERATURE, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Application, add, variable + +DEPENDENCIES = ['i2c'] + +MakeMS5611Sensor = Application.MakeMS5611Sensor + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMS5611Sensor), + vol.Optional(CONF_ADDRESS): cv.i2c_address, + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +}) + + +def to_code(config): + rhs = App.make_ms5611_sensor(config[CONF_TEMPERATURE][CONF_NAME], + config[CONF_PRESSURE][CONF_NAME], + config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + + if CONF_ADDRESS in config: + add(make.Pms5611.set_address(config[CONF_ADDRESS])) + + sensor.setup_sensor(make.Pms5611.Pget_temperature_sensor(), make.Pmqtt_temperature, + config[CONF_TEMPERATURE]) + sensor.setup_sensor(make.Pms5611.Pget_pressure_sensor(), make.Pmqtt_pressure, + config[CONF_PRESSURE]) + + +BUILD_FLAGS = '-DUSE_MS5611' diff --git a/esphomeyaml/components/sensor/tcs34725.py b/esphomeyaml/components/sensor/tcs34725.py new file mode 100644 index 0000000000..50ea527e5d --- /dev/null +++ b/esphomeyaml/components/sensor/tcs34725.py @@ -0,0 +1,80 @@ +# coding=utf-8 +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ADDRESS, CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, \ + CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, CONF_NAME, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Pvariable, add + +DEPENDENCIES = ['i2c'] + +CONF_RED_CHANNEL = 'red_channel' +CONF_GREEN_CHANNEL = 'green_channel' +CONF_BLUE_CHANNEL = 'blue_channel' +CONF_CLEAR_CHANNEL = 'clear_channel' + +TCS34725Component = sensor.sensor_ns.TCS34725Component + +TCS34725_INTEGRATION_TIMES = { + '2.4ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_2_4MS, + '24ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_24MS, + '50ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_50MS, + '101ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_101MS, + '154ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_154MS, + '700ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_700MS, +} + +TCS34725_GAINS = { + '1X': sensor.sensor_ns.TCS34725_GAIN_1X, + '4X': sensor.sensor_ns.TCS34725_GAIN_4X, + '16X': sensor.sensor_ns.TCS34725_GAIN_16X, + '60X': sensor.sensor_ns.TCS34725_GAIN_60X, +} + +PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(): cv.declare_variable_id(TCS34725Component), + vol.Optional(CONF_ADDRESS): cv.i2c_address, + vol.Optional(CONF_RED_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_GREEN_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_BLUE_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_CLEAR_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_INTEGRATION_TIME): cv.one_of(*TCS34725_INTEGRATION_TIMES), + vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*TCS34725_GAINS)), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +}), cv.has_at_least_one_key(CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL, + CONF_CLEAR_CHANNEL, CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE)) + + +def to_code(config): + rhs = App.make_tcs34725(config.get(CONF_UPDATE_INTERVAL)) + tcs = Pvariable(config[CONF_ID], rhs) + if CONF_ADDRESS in config: + add(tcs.set_address(config[CONF_ADDRESS])) + if CONF_INTEGRATION_TIME in config: + add(tcs.set_integration_time(TCS34725_INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]])) + if CONF_GAIN in config: + add(tcs.set_gain(TCS34725_GAINS[config[CONF_GAIN]])) + if CONF_RED_CHANNEL in config: + conf = config[CONF_RED_CHANNEL] + sensor.register_sensor(tcs.Pmake_red_sensor(conf[CONF_NAME]), conf) + if CONF_GREEN_CHANNEL in config: + conf = config[CONF_GREEN_CHANNEL] + sensor.register_sensor(tcs.Pmake_green_sensor(conf[CONF_NAME]), conf) + if CONF_BLUE_CHANNEL in config: + conf = config[CONF_BLUE_CHANNEL] + sensor.register_sensor(tcs.Pmake_blue_sensor(conf[CONF_NAME]), conf) + if CONF_CLEAR_CHANNEL in config: + conf = config[CONF_CLEAR_CHANNEL] + sensor.register_sensor(tcs.Pmake_clear_sensor(conf[CONF_NAME]), conf) + if CONF_ILLUMINANCE in config: + conf = config[CONF_ILLUMINANCE] + sensor.register_sensor(tcs.Pmake_illuminance_sensor(conf[CONF_NAME]), conf) + if CONF_COLOR_TEMPERATURE in config: + conf = config[CONF_COLOR_TEMPERATURE] + sensor.register_sensor(tcs.Pmake_color_temperature_sensor(conf[CONF_NAME]), conf) + + +BUILD_FLAGS = '-DUSE_TCS34725' diff --git a/esphomeyaml/components/sensor/template.py b/esphomeyaml/components/sensor/template.py index 8a8841fccd..9ad1952215 100644 --- a/esphomeyaml/components/sensor/template.py +++ b/esphomeyaml/components/sensor/template.py @@ -3,7 +3,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import sensor from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional +from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional, add MakeTemplateSensor = Application.MakeTemplateSensor @@ -15,14 +15,15 @@ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ def to_code(config): + rhs = App.make_template_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config) + template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], return_type=optional.template(float_)): yield - rhs = App.make_template_sensor(config[CONF_NAME], template_, - config.get(CONF_UPDATE_INTERVAL)) - make = variable(config[CONF_MAKE_ID], rhs) - sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config) + add(make.Ptemplate_.set_template(template_)) BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR' diff --git a/esphomeyaml/components/sensor/uptime.py b/esphomeyaml/components/sensor/uptime.py new file mode 100644 index 0000000000..dcb8c7498c --- /dev/null +++ b/esphomeyaml/components/sensor/uptime.py @@ -0,0 +1,22 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL +from esphomeyaml.helpers import App, Application, variable + +MakeUptimeSensor = Application.MakeUptimeSensor + +PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUptimeSensor), + vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, +})) + + +def to_code(config): + rhs = App.make_uptime_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) + make = variable(config[CONF_MAKE_ID], rhs) + sensor.setup_sensor(make.Puptime, make.Pmqtt, config) + + +BUILD_FLAGS = '-DUSE_UPTIME_SENSOR' diff --git a/esphomeyaml/components/sensor/xiaomi_miflora.py b/esphomeyaml/components/sensor/xiaomi_miflora.py new file mode 100644 index 0000000000..a5e397d059 --- /dev/null +++ b/esphomeyaml/components/sensor/xiaomi_miflora.py @@ -0,0 +1,47 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ + make_address_array +from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_CONDUCTIVITY, CONF_ILLUMINANCE, \ + CONF_MAC_ADDRESS, CONF_MAKE_ID, CONF_MOISTURE, CONF_NAME, CONF_TEMPERATURE +from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable + +DEPENDENCIES = ['esp32_ble_tracker'] + +XiaomiMiFloraDevice = esphomelib_ns.XiaomiMiFloraDevice + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiMiFloraDevice), + cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker), + vol.Required(CONF_MAC_ADDRESS): cv.mac_address, + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_MOISTURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_CONDUCTIVITY): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA), +}) + + +def to_code(config): + hub = None + for hub in get_variable(config[CONF_ESP32_BLE_ID]): + yield + rhs = hub.make_miflora_sensor(make_address_array(config[CONF_MAC_ADDRESS])) + dev = Pvariable(config[CONF_MAKE_ID], rhs) + if CONF_TEMPERATURE in config: + conf = config[CONF_TEMPERATURE] + sensor.register_sensor(dev.Pmake_temperature_sensor(conf[CONF_NAME]), conf) + if CONF_MOISTURE in config: + conf = config[CONF_MOISTURE] + sensor.register_sensor(dev.Pmake_moisture_sensor(conf[CONF_NAME]), conf) + if CONF_ILLUMINANCE in config: + conf = config[CONF_ILLUMINANCE] + sensor.register_sensor(dev.Pmake_illuminance_sensor(conf[CONF_NAME]), conf) + if CONF_CONDUCTIVITY in config: + conf = config[CONF_CONDUCTIVITY] + sensor.register_sensor(dev.Pmake_conductivity_sensor(conf[CONF_NAME]), conf) + if CONF_BATTERY_LEVEL in config: + conf = config[CONF_BATTERY_LEVEL] + sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf) diff --git a/esphomeyaml/components/sensor/xiaomi_mijia.py b/esphomeyaml/components/sensor/xiaomi_mijia.py new file mode 100644 index 0000000000..89e8b27ef8 --- /dev/null +++ b/esphomeyaml/components/sensor/xiaomi_mijia.py @@ -0,0 +1,37 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import sensor +from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ + make_address_array +from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_MAKE_ID, \ + CONF_NAME, CONF_TEMPERATURE +from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable + +DEPENDENCIES = ['esp32_ble_tracker'] + +XiaomiMiJiaDevice = esphomelib_ns.XiaomiMiJiaDevice + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiMiJiaDevice), + cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker), + vol.Required(CONF_MAC_ADDRESS): cv.mac_address, + vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA), + vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA), +}) + + +def to_code(config): + hub = None + for hub in get_variable(config[CONF_ESP32_BLE_ID]): + yield + rhs = hub.make_mijia_sensor(config[CONF_TEMPERATURE][CONF_NAME], + config[CONF_HUMIDITY][CONF_NAME], + make_address_array(config[CONF_MAC_ADDRESS])) + dev = Pvariable(config[CONF_MAKE_ID], rhs) + sensor.register_sensor(dev.Pget_temperature_sensor(), config[CONF_TEMPERATURE]) + sensor.register_sensor(dev.Pget_humidity_sensor(), config[CONF_HUMIDITY]) + if CONF_BATTERY_LEVEL in config: + conf = config[CONF_BATTERY_LEVEL] + sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf) diff --git a/esphomeyaml/components/spi.py b/esphomeyaml/components/spi.py new file mode 100644 index 0000000000..40618a5793 --- /dev/null +++ b/esphomeyaml/components/spi.py @@ -0,0 +1,37 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +from esphomeyaml.const import CONF_CLK, CONF_ID, CONF_MISO, CONF_MOSI +from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \ + gpio_output_pin_expression + +SPIComponent = esphomelib_ns.SPIComponent + +SPI_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(SPIComponent), + vol.Required(CONF_CLK): pins.gpio_output_pin_schema, + vol.Required(CONF_MISO): pins.gpio_input_pin_schema, + vol.Optional(CONF_MOSI): pins.gpio_output_pin_schema, +}) + +CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA]) + + +def to_code(config): + for conf in config: + clk = None + for clk in gpio_output_pin_expression(conf[CONF_CLK]): + yield + miso = None + for miso in gpio_input_pin_expression(conf[CONF_MISO]): + yield + mosi = None + if CONF_MOSI in conf: + for mosi in gpio_output_pin_expression(conf[CONF_MOSI]): + yield + rhs = App.init_spi(clk, miso, mosi) + Pvariable(conf[CONF_ID], rhs) + + +BUILD_FLAGS = '-DUSE_SPI' diff --git a/esphomeyaml/components/status_led.py b/esphomeyaml/components/status_led.py index e72346941a..bf24906bb7 100644 --- a/esphomeyaml/components/status_led.py +++ b/esphomeyaml/components/status_led.py @@ -4,10 +4,10 @@ from esphomeyaml import config_validation as cv, pins from esphomeyaml.const import CONF_ID, CONF_PIN from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_output_pin_expression -StatusLED = esphomelib_ns.StatusLED +StatusLEDComponent = esphomelib_ns.StatusLEDComponent CONFIG_SCHEMA = vol.Schema({ - cv.GenerateID(): cv.declare_variable_id(StatusLED), + cv.GenerateID(): cv.declare_variable_id(StatusLEDComponent), vol.Optional(CONF_PIN): pins.gpio_output_pin_schema, }) diff --git a/esphomeyaml/components/switch/remote_transmitter.py b/esphomeyaml/components/switch/remote_transmitter.py index f7c25617d3..1ca6acc601 100644 --- a/esphomeyaml/components/switch/remote_transmitter.py +++ b/esphomeyaml/components/switch/remote_transmitter.py @@ -2,15 +2,23 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml.components import switch -from esphomeyaml.components.remote_transmitter import RemoteTransmitterComponent, remote_ns -from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \ - CONF_INVERTED, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \ - CONF_PANASONIC, CONF_RAW, CONF_REPEAT, CONF_SONY, CONF_TIMES, CONF_WAIT_TIME +from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \ + RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \ + RC_SWITCH_TYPE_D_SCHEMA, RemoteTransmitterComponent, binary_code, build_rc_switch_protocol, \ + remote_ns +from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_CHANNEL, CONF_CODE, \ + CONF_COMMAND, CONF_DATA, CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_INVERTED, CONF_LG, \ + CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, \ + CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D,\ + CONF_REPEAT, CONF_SONY, CONF_STATE, CONF_TIMES, \ + CONF_WAIT_TIME from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, get_variable DEPENDENCIES = ['remote_transmitter'] -IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW] +REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW, CONF_RC_SWITCH_RAW, + CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, + CONF_RC_SWITCH_TYPE_D] CONF_REMOTE_TRANSMITTER_ID = 'remote_transmitter_id' CONF_TRANSMITTER_ID = 'transmitter_id' @@ -21,6 +29,13 @@ NECTransmitter = remote_ns.NECTransmitter PanasonicTransmitter = remote_ns.PanasonicTransmitter RawTransmitter = remote_ns.RawTransmitter SonyTransmitter = remote_ns.SonyTransmitter +RCSwitchProtocol = remote_ns.RCSwitchProtocol +rc_switch_protocols = remote_ns.rc_switch_protocols +RCSwitchRawTransmitter = remote_ns.RCSwitchRawTransmitter +RCSwitchTypeATransmitter = remote_ns.RCSwitchTypeATransmitter +RCSwitchTypeBTransmitter = remote_ns.RCSwitchTypeBTransmitter +RCSwitchTypeCTransmitter = remote_ns.RCSwitchTypeCTransmitter +RCSwitchTypeDTransmitter = remote_ns.RCSwitchTypeDTransmitter validate_raw_data = [vol.Any(vol.Coerce(int), cv.time_period_microseconds)] @@ -45,42 +60,66 @@ PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ vol.Required(CONF_DATA): validate_raw_data, vol.Optional(CONF_CARRIER_FREQUENCY): vol.All(cv.frequency, vol.Coerce(int)), })), + vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_A): RC_SWITCH_TYPE_A_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA, + vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA, + vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({ vol.Required(CONF_TIMES): cv.positive_not_null_int, vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds, })), cv.GenerateID(CONF_REMOTE_TRANSMITTER_ID): cv.use_variable_id(RemoteTransmitterComponent), cv.GenerateID(CONF_TRANSMITTER_ID): cv.declare_variable_id(RemoteTransmitter), - vol.Optional(CONF_INVERTED): cv.invalid("Remote Transmitters do not support inverted mode!"), -}), cv.has_exactly_one_key(*IR_KEYS)) +}), cv.has_exactly_one_key(*REMOTE_KEYS)) -def transmitter_base(config): - if CONF_LG in config: - conf = config[CONF_LG] - return LGTransmitter.new(config[CONF_NAME], conf[CONF_DATA], conf[CONF_NBITS]) - elif CONF_NEC in config: - conf = config[CONF_NEC] - return NECTransmitter.new(config[CONF_NAME], conf[CONF_ADDRESS], conf[CONF_COMMAND]) - elif CONF_PANASONIC in config: - conf = config[CONF_PANASONIC] - return PanasonicTransmitter.new(config[CONF_NAME], conf[CONF_ADDRESS], conf[CONF_COMMAND]) - elif CONF_SONY in config: - conf = config[CONF_SONY] - return SonyTransmitter.new(config[CONF_NAME], conf[CONF_DATA], conf[CONF_NBITS]) - elif CONF_RAW in config: - conf = config[CONF_RAW] - if isinstance(conf, dict): - data = conf[CONF_DATA] - carrier_frequency = conf.get(CONF_CARRIER_FREQUENCY) +def transmitter_base(full_config): + name = full_config[CONF_NAME] + key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS) + + if key == CONF_LG: + return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) + elif key == CONF_NEC: + return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) + elif key == CONF_PANASONIC: + return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) + elif key == CONF_SONY: + return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) + elif key == CONF_RAW: + if isinstance(config, dict): + data = config[CONF_DATA] + carrier_frequency = config.get(CONF_CARRIER_FREQUENCY) else: - data = conf + data = config carrier_frequency = None - return RawTransmitter.new(config[CONF_NAME], ArrayInitializer(*data, multiline=False), + return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False), carrier_frequency) + elif key == CONF_RC_SWITCH_RAW: + return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + binary_code(config[CONF_CODE]), len(config[CONF_CODE])) + elif key == CONF_RC_SWITCH_TYPE_A: + return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + binary_code(config[CONF_GROUP]), + binary_code(config[CONF_DEVICE]), + config[CONF_STATE]) + elif key == CONF_RC_SWITCH_TYPE_B: + return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + config[CONF_ADDRESS], config[CONF_CHANNEL], + config[CONF_STATE]) + elif key == CONF_RC_SWITCH_TYPE_C: + return RCSwitchTypeCTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + ord(config[CONF_FAMILY][0]) - ord('a'), + config[CONF_GROUP], config[CONF_DEVICE], + config[CONF_STATE]) + elif key == CONF_RC_SWITCH_TYPE_D: + return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), + ord(config[CONF_GROUP][0]) - ord('a'), + config[CONF_DEVICE], config[CONF_STATE]) else: - raise ValueError("Unknown transmitter type {}".format(config)) + raise NotImplementedError("Unknown transmitter type {}".format(config)) def to_code(config): diff --git a/esphomeyaml/components/switch/template.py b/esphomeyaml/components/switch/template.py index 515159df8c..4f33dbb10c 100644 --- a/esphomeyaml/components/switch/template.py +++ b/esphomeyaml/components/switch/template.py @@ -23,6 +23,8 @@ def to_code(config): rhs = App.make_template_switch(config[CONF_NAME]) make = variable(config[CONF_MAKE_ID], rhs) + switch.setup_switch(make.Ptemplate_, make.Pmqtt, config) + if CONF_LAMBDA in config: template_ = None for template_ in process_lambda(config[CONF_LAMBDA], [], @@ -38,7 +40,5 @@ def to_code(config): if CONF_OPTIMISTIC in config: add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC])) - switch.setup_switch(make.Ptemplate_, make.Pmqtt, config) - BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH' diff --git a/esphomeyaml/components/switch/uart.py b/esphomeyaml/components/switch/uart.py new file mode 100644 index 0000000000..622b1df0b8 --- /dev/null +++ b/esphomeyaml/components/switch/uart.py @@ -0,0 +1,45 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml.components import switch +from esphomeyaml.components.uart import UARTComponent +from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, CONF_UART_ID +from esphomeyaml.core import HexInt +from esphomeyaml.helpers import App, Application, ArrayInitializer, get_variable, variable + +DEPENDENCIES = ['uart'] + +MakeUARTSwitch = Application.MakeUARTSwitch + + +def validate_data(value): + if isinstance(value, unicode): + return value.encode('utf-8') + elif isinstance(value, str): + return value + elif isinstance(value, list): + return vol.Schema([cv.hex_uint8_t])(value) + raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes") + + +PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ + cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUARTSwitch), + cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), + vol.Required(CONF_DATA): validate_data, + vol.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"), +})) + + +def to_code(config): + uart = None + for uart in get_variable(config[CONF_UART_ID]): + yield + data = config[CONF_DATA] + if isinstance(data, str): + data = [HexInt(ord(x)) for x in data] + rhs = App.make_uart_switch(uart, config[CONF_NAME], ArrayInitializer(*data, multiline=False)) + restart = variable(config[CONF_MAKE_ID], rhs) + switch.setup_switch(restart.Puart, restart.Pmqtt, config) + + +BUILD_FLAGS = '-DUSE_UART_SWITCH' diff --git a/esphomeyaml/components/uart.py b/esphomeyaml/components/uart.py new file mode 100644 index 0000000000..32ecf0fc8f --- /dev/null +++ b/esphomeyaml/components/uart.py @@ -0,0 +1,33 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_RX, CONF_TX +from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \ + gpio_output_pin_expression + +UARTComponent = esphomelib_ns.UARTComponent + +SPI_SCHEMA = vol.Schema({ + cv.GenerateID(): cv.declare_variable_id(UARTComponent), + vol.Required(CONF_TX): pins.gpio_output_pin_schema, + vol.Required(CONF_RX): pins.gpio_input_pin_schema, + vol.Required(CONF_BAUD_RATE): cv.positive_int, +}) + +CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA]) + + +def to_code(config): + for conf in config: + tx = None + for tx in gpio_output_pin_expression(conf[CONF_TX]): + yield + rx = None + for rx in gpio_input_pin_expression(conf[CONF_RX]): + yield + rhs = App.init_uart(tx, rx, conf[CONF_BAUD_RATE]) + Pvariable(conf[CONF_ID], rhs) + + +BUILD_FLAGS = '-DUSE_UART' diff --git a/esphomeyaml/config.py b/esphomeyaml/config.py index b49bf7a961..2b0e0f00a1 100644 --- a/esphomeyaml/config.py +++ b/esphomeyaml/config.py @@ -12,7 +12,7 @@ from esphomeyaml import core, yaml_util, automation from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \ CONF_LIBRARY_URI, CONF_NAME, CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, \ ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_ON_BOOT, CONF_TRIGGER_ID, \ - CONF_PRIORITY, CONF_ON_SHUTDOWN + CONF_PRIORITY, CONF_ON_SHUTDOWN, CONF_BUILD_PATH from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, add, color, esphomelib_ns, Pvariable, NoArg, const_char_p @@ -39,6 +39,7 @@ CORE_SCHEMA = vol.Schema({ vol.Optional(CONF_ON_SHUTDOWN): vol.All(cv.ensure_list, [automation.validate_automation({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ShutdownTrigger), })]), + vol.Optional(CONF_BUILD_PATH): cv.string, }) REQUIRED_COMPONENTS = [ @@ -175,25 +176,28 @@ def validate_config(config): _comp_error(ex, CONF_ESPHOMEYAML, config) for domain, conf in config.iteritems(): - if domain == CONF_ESPHOMEYAML: + domain = str(domain) + if domain == CONF_ESPHOMEYAML or domain.startswith('.'): continue if conf is None: conf = {} component = get_component(domain) if component is None: - result.add_error(u"Component not found: {}".format(domain)) + result.add_error(u"Component not found: {}".format(domain), domain, conf) continue esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS) if core.ESP_PLATFORM not in esp_platforms: - result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM)) + result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM), + domain, conf) continue success = True dependencies = getattr(component, 'DEPENDENCIES', []) for dependency in dependencies: if dependency not in _ALL_COMPONENTS: - result.add_error(u"Component {} requires component {}".format(domain, dependency)) + result.add_error(u"Component {} requires component {}".format(domain, dependency), + domain, conf) success = False if not success: continue @@ -212,23 +216,25 @@ def validate_config(config): platforms = [] for p_config in conf: if not isinstance(p_config, dict): - result.add_error(u"Platform schemas must 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: result.add_error(u"No platform specified for {}".format(domain)) continue + p_domain = u'{}.{}'.format(domain, p_name) platform = get_platform(domain, p_name) if platform is None: - result.add_error(u"Platform not found: {}.{}".format(domain, p_name)) + result.add_error(u"Platform not found: {}".format(p_domain), p_domain, p_config) continue success = True dependencies = getattr(platform, 'DEPENDENCIES', []) for dependency in dependencies: if dependency not in _ALL_COMPONENTS: - result.add_error(u"Platform {}.{} requires component {}".format(domain, p_name, - dependency)) + result.add_error( + u"Platform {} requires component {}".format(p_domain, dependency), + p_domain, p_config) success = False if not success: continue @@ -236,14 +242,15 @@ def validate_config(config): esp_platforms = getattr(platform, 'ESP_PLATFORMS', ESP_PLATFORMS) if core.ESP_PLATFORM not in esp_platforms: result.add_error( - u"Platform {}.{} doesn't support {}.".format(domain, p_name, core.ESP_PLATFORM)) + u"Platform {} doesn't support {}.".format(p_domain, core.ESP_PLATFORM), + p_domain, p_config) continue if hasattr(platform, u'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: - _comp_error(ex, u'{}.{}'.format(domain, p_name), p_config) + _comp_error(ex, p_domain, p_config) continue platforms.append(p_validated) result[domain] = platforms @@ -365,4 +372,4 @@ def read_config(path): dump_dict(config, reset='red') print(color('reset')) return None - return dict(**res) + return OrderedDict(res) diff --git a/esphomeyaml/config_validation.py b/esphomeyaml/config_validation.py index e9894d5032..2e66efefcb 100644 --- a/esphomeyaml/config_validation.py +++ b/esphomeyaml/config_validation.py @@ -360,20 +360,29 @@ METRIC_SUFFIXES = { } -def frequency(value): - value = string(value) - match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)(?:Hz|HZ|hz)?$", value) +def float_with_unit(quantity, regex_suffix): + pattern = re.compile(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)" + regex_suffix + "$") - if match is None: - raise vol.Invalid(u"Expected frequency with unit, " - u"got {}".format(value)) + def validator(value): + match = pattern.match(string(value)) - mantissa = float(match.group(1)) - if match.group(2) not in METRIC_SUFFIXES: - raise vol.Invalid(u"Invalid frequency suffix {}".format(match.group(2))) + if match is None: + raise vol.Invalid(u"Expected {} with unit, got {}".format(quantity, value)) - multiplier = METRIC_SUFFIXES[match.group(2)] - return mantissa * multiplier + mantissa = float(match.group(1)) + if match.group(2) not in METRIC_SUFFIXES: + raise vol.Invalid(u"Invalid {} suffix {}".format(quantity, match.group(2))) + + multiplier = METRIC_SUFFIXES[match.group(2)] + return mantissa * multiplier + + return validator + + +frequency = float_with_unit("frequency", r"(Hz|HZ|hz)?") +resistance = float_with_unit("resistance", r"(Ω|Ω|ohm|Ohm|OHM)?") +current = float_with_unit("current", r"(a|A|amp|Amp|amps|Amps|ampere|Ampere)?") +voltage = float_with_unit("voltage", r"(v|V|volt|Volts)?") def validate_bytes(value): diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index 0ee1742439..0e1ef49cf4 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -220,9 +220,10 @@ CONF_ON_VALUE = 'on_value' CONF_ON_RAW_VALUE = 'on_raw_value' CONF_ON_VALUE_RANGE = 'on_value_range' CONF_ON_MESSAGE = 'on_message' -CONF_PIN_CS = 'pin_cs' -CONF_PIN_CLOCK = 'pin_clock' -CONF_PIN_MISO = 'pin_miso' +CONF_CS = 'cs' +CONF_CLK = 'clk' +CONF_MISO = 'miso' +CONF_MOSI = 'mosi' CONF_TURN_ON_ACTION = 'turn_on_action' CONF_TURN_OFF_ACTION = 'turn_off_action' CONF_OPEN_ACTION = 'open_action' @@ -247,6 +248,47 @@ CONF_DELAYED_ON = 'delayed_on' CONF_DELAYED_OFF = 'delayed_off' CONF_UUID = 'uuid' CONF_TYPE = 'type' +CONF_SPI_ID = 'spi_id' +CONF_UART_ID = 'uart_id' +CONF_UID = 'uid' +CONF_TX = 'tx' +CONF_RX = 'rx' +CONF_CO2 = 'co2' +CONF_SHUNT_RESISTANCE = 'shunt_resistance' +CONF_MAX_CURRENT = 'max_current' +CONF_MAX_VOLTAGE = 'max_voltage' +CONF_CURRENT = 'current' +CONF_POWER = 'power' +CONF_BUS_VOLTAGE = 'bus_voltage' +CONF_SHUNT_VOLTAGE = 'shunt_voltage' +CONF_CONDITION = 'condition' +CONF_ELSE = 'else' +CONF_EFFECTS = 'effects' +CONF_RANDOM = 'random' +CONF_EFFECT_ID = 'effect_id' +CONF_COLORS = 'colors' +CONF_STATE = 'state' +CONF_DURATION = 'duration' +CONF_WIDTH = 'width' +CONF_ILLUMINANCE = 'illuminance' +CONF_COLOR_TEMPERATURE = 'color_temperature' +CONF_BATTERY_LEVEL = 'battery_level' +CONF_MOISTURE = 'moisture' +CONF_CONDUCTIVITY = 'conductivity' +CONF_RC_SWITCH_RAW = 'rc_switch_raw' +CONF_RC_SWITCH_TYPE_A = 'rc_switch_type_a' +CONF_RC_SWITCH_TYPE_B = 'rc_switch_type_b' +CONF_RC_SWITCH_TYPE_C = 'rc_switch_type_c' +CONF_RC_SWITCH_TYPE_D = 'rc_switch_type_d' +CONF_CODE = 'code' +CONF_PROTOCOL = 'protocol' +CONF_PULSE_LENGTH = 'pulse_length' +CONF_SYNC = 'sync' +CONF_ZERO = 'zero' +CONF_ONE = 'one' +CONF_GROUP = 'group' +CONF_DEVICE = 'device' +CONF_FAMILY = 'family' ESP32_BOARDS = [ 'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1', diff --git a/esphomeyaml/core.py b/esphomeyaml/core.py index fd437263c6..2c51fbb8a1 100644 --- a/esphomeyaml/core.py +++ b/esphomeyaml/core.py @@ -219,6 +219,8 @@ class ID(object): return self.id def __str__(self): + if self.id is None: + return '' return self.id def __repr__(self): diff --git a/esphomeyaml/dashboard/dashboard.py b/esphomeyaml/dashboard/dashboard.py index 16be616322..63550693af 100644 --- a/esphomeyaml/dashboard/dashboard.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -66,7 +66,10 @@ class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler): break if data.endswith('\r') and random.randrange(100) < 90: continue - data = data.replace('\033', '\\033') + try: + data = data.replace('\033', '\\033') + except UnicodeDecodeError: + data = data.encode('ascii', 'backslashreplace') self.write_message({'event': 'line', 'data': data}) def proc_on_exit(self, returncode): diff --git a/esphomeyaml/dashboard/templates/index.html b/esphomeyaml/dashboard/templates/index.html index 91d7035a68..3267fdbe28 100644 --- a/esphomeyaml/dashboard/templates/index.html +++ b/esphomeyaml/dashboard/templates/index.html @@ -297,8 +297,8 @@

First, I need to know what this node should be called. Choose this name wisely, changing this later makes Over-The-Air Update attempts difficult. - It may only contain the characters a-z, - 0-9 and _ + Names must be lowercase and must not contain spaces (allowed characters: a-z, + 0-9 and _)

diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index 518eb09873..c45e1ec988 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -525,6 +525,17 @@ class MockObj(Expression): def has_side_effects(self): return self._has_side_effects + def __getitem__(self, item): + next_op = u'.' + if isinstance(item, str) and item.startswith(u'P'): + item = item[1:] + next_op = u'->' + obj = MockObj(u'{}[{}]'.format(self.base, item), next_op) + obj.requires.append(self) + if isinstance(item, Expression): + obj.requires.append(item) + return obj + global_ns = MockObj('', '') float_ = global_ns.namespace('float') diff --git a/esphomeyaml/mqtt.py b/esphomeyaml/mqtt.py index 184f2f9c01..a788e46166 100644 --- a/esphomeyaml/mqtt.py +++ b/esphomeyaml/mqtt.py @@ -76,9 +76,13 @@ def clear_topic(config, topic, username=None, password=None, client_id=None): _LOGGER.info(u"Clearing messages from %s", topic) def on_message(client, userdata, msg): - if not msg.payload: + if not msg.payload or not msg.retain: + return + try: + print(u"Clearing topic {}".format(msg.topic)) + except UnicodeDecodeError: + print(u"Skipping non-UTF-8 topic (prohibited by MQTT standard)") return - print(u"Clearing topic {}".format(msg.topic)) client.publish(msg.topic, None, retain=True) return initialize(config, [topic], on_message, username, password, client_id) diff --git a/esphomeyaml/writer.py b/esphomeyaml/writer.py index ea85786c0d..69bc4e4bba 100644 --- a/esphomeyaml/writer.py +++ b/esphomeyaml/writer.py @@ -179,10 +179,11 @@ def write_platformio_ini(content, path): def write_platformio_project(config, path): platformio_ini = os.path.join(path, 'platformio.ini') content = get_ini_content(config) - if 'esp32_ble_beacon' in config: + if 'esp32_ble_beacon' in config or 'esp32_ble_tracker' in config: content += 'board_build.partitions = partitions.csv\n' partitions_csv = os.path.join(path, 'partitions.csv') if not os.path.isfile(partitions_csv): + mkdir_p(path) with open(partitions_csv, "w") as f: f.write("nvs, data, nvs, 0x009000, 0x005000,\n") f.write("otadata, data, ota, 0x00e000, 0x002000,\n") diff --git a/esphomeyaml/yaml_util.py b/esphomeyaml/yaml_util.py index 341a3a67b9..4d0035e921 100644 --- a/esphomeyaml/yaml_util.py +++ b/esphomeyaml/yaml_util.py @@ -8,6 +8,7 @@ import uuid from collections import OrderedDict import yaml +import yaml.constructor from esphomeyaml import core from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod @@ -63,14 +64,83 @@ def dump(dict_): dict_, default_flow_style=False, allow_unicode=True) +def custom_construct_pairs(loader, node): + pairs = [] + for kv in node.value: + if isinstance(kv, yaml.ScalarNode): + obj = loader.construct_object(kv) + if not isinstance(obj, dict): + raise ESPHomeYAMLError( + "Expected mapping for anchored include tag, got {}".format(type(obj))) + for key, value in obj.iteritems(): + pairs.append((key, value)) + else: + key_node, value_node = kv + key = loader.construct_object(key_node) + value = loader.construct_object(value_node) + pairs.append((key, value)) + + return pairs + + +def custom_flatten_mapping(loader, node): + pre_merge = [] + post_merge = [] + index = 0 + while index < len(node.value): + if isinstance(node.value[index], yaml.ScalarNode): + index += 1 + continue + + key_node, value_node = node.value[index] + if key_node.tag == u'tag:yaml.org,2002:merge': + del node.value[index] + + if isinstance(value_node, yaml.MappingNode): + custom_flatten_mapping(loader, value_node) + node.value = node.value[:index] + value_node.value + node.value[index:] + elif isinstance(value_node, yaml.SequenceNode): + submerge = [] + for subnode in value_node.value: + if not isinstance(subnode, yaml.MappingNode): + raise yaml.constructor.ConstructorError( + "while constructing a mapping", node.start_mark, + "expected a mapping for merging, but found %{}".format(subnode.id), + subnode.start_mark) + custom_flatten_mapping(loader, subnode) + submerge.append(subnode.value) + # submerge.reverse() + node.value = node.value[:index] + submerge + node.value[index:] + elif isinstance(value_node, yaml.ScalarNode): + node.value = node.value[:index] + [value_node] + node.value[index:] + # post_merge.append(value_node) + else: + raise yaml.constructor.ConstructorError( + "while constructing a mapping", node.start_mark, + "expected a mapping or list of mappings for merging, " + "but found {}".format(value_node.id), value_node.start_mark) + elif key_node.tag == u'tag:yaml.org,2002:value': + key_node.tag = u'tag:yaml.org,2002:str' + index += 1 + else: + index += 1 + if pre_merge: + node.value = pre_merge + node.value + if post_merge: + node.value = node.value + post_merge + + def _ordered_dict(loader, node): """Load YAML mappings into an ordered dictionary to preserve key order.""" - loader.flatten_mapping(node) - nodes = loader.construct_pairs(node) + custom_flatten_mapping(loader, node) + nodes = custom_construct_pairs(loader, node) seen = {} - for (key, _), (child_node, _) in zip(nodes, node.value): - line = child_node.start_mark.line + for (key, _), nv in zip(nodes, node.value): + if isinstance(nv, yaml.ScalarNode): + line = nv.start_mark.line + else: + line = nv[0].start_mark.line try: hash(key) diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 92e1eb91da..0000000000 --- a/examples/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Examples - -This directory contains some of the ESP32/ESP8266 nodes I use at my home. diff --git a/examples/cabinet.yaml b/examples/cabinet.yaml deleted file mode 100644 index d5fba218b9..0000000000 --- a/examples/cabinet.yaml +++ /dev/null @@ -1,127 +0,0 @@ -esphomeyaml: - name: cabinet - platform: ESP32 - board: nodemcu-32s - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.203 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: cabinet - password: '[PASSWORD]' - # This is the default - discovery: true - -power_supply: - - id: 'atx' - pin: - number: 13 - inverted: true - -i2c: - sda: 14 - scl: 27 - frequency: 400000 - -pca9685: - - id: 'pca9685' - frequency: 500 - -output: - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'cabinet1_red' - channel: 14 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'cabinet1_green' - channel: 15 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'cabinet1_blue' - channel: 13 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'cabinet2_red' - channel: 11 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'cabinet2_green' - channel: 12 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'cabinet2_blue' - channel: 10 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'room_red' - channel: 8 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'room_green' - channel: 9 - power_supply: 'atx' - - platform: pca9685 - pca9685_id: 'pca9685' - id: 'room_blue' - channel: 7 - power_supply: 'atx' - -light: - - platform: rgb - name: 'Cabinet Light 1' - red: 'cabinet1_red' - green: 'cabinet1_green' - blue: 'cabinet1_blue' - - platform: rgb - name: 'Cabinet Light 2' - red: 'cabinet2_red' - green: 'cabinet2_green' - blue: 'cabinet2_blue' - - platform: rgb - name: 'Room Light' - red: 'room_red' - green: 'room_green' - blue: 'room_blue' - -sensor: - - platform: dht - pin: 23 - temperature: - name: 'Cabinet Temperature' - humidity: - name: 'Cabinet Humidity' - model: DHT22 - -binary_sensor: - - platform: gpio - pin: 25 - name: 'Cabinet Motion' - device_class: motion - # Simple binary sensor that uses last will and birth messages to show - # node state - - platform: status - name: "Cabinet Status" - -switch: - # Simple switch that restarts the ESP32 - - platform: restart - name: "Cabinet Restart" diff --git a/examples/dachboden.yaml b/examples/dachboden.yaml deleted file mode 100644 index 9406fc38a4..0000000000 --- a/examples/dachboden.yaml +++ /dev/null @@ -1,57 +0,0 @@ -esphomeyaml: - name: dachboden - platform: ESP8266 - board: nodemcuv2 - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.212 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: dachboden - password: '[PASSWORD]' - # This is the default - discovery: true - - -dallas: - id: 'dallas' - pin: D1 - -sensor: - - platform: dht - pin: D3 - temperature: - name: 'Dachboden Temperatur' - humidity: - name: 'Dachboden Luftfeuchtigkeit' - model: DHT22 - - platform: dallas - dallas_id: 'dallas' - address: 0x01031663650aff28 - name: "Dachboden Solar Süd Vorlauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x2b0416638fe6ff28 - name: "Dachboden Solar Süd Rücklauf" - - platform: adc - pin: A0 - name: "Dachboden Helligkeit" - -binary_sensor: - - platform: status - name: "Dachboden Status" - -switch: - - platform: restart - name: "Dachboden Neustart" diff --git a/examples/heatpump.yaml b/examples/heatpump.yaml deleted file mode 100644 index 9ba902049c..0000000000 --- a/examples/heatpump.yaml +++ /dev/null @@ -1,117 +0,0 @@ -esphomeyaml: - name: heatpump - platform: ESP32 - board: nodemcu-32s - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.204 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: heatpump - password: '[PASSWORD]' - # This is the default - discovery: true - -dallas: - id: 'dallas' - pin: 15 - -sensor: - - platform: dht - pin: 0 - temperature: - name: 'Outside Temperature' - humidity: - name: 'Outside Humidity' - model: DHT22 - - platform: pulse_counter - pin: 12 - unit_of_measurement: 'kW' - name: 'Stromverbrauch Wintergarten' - update_interval: 30s - expire_after: 60s - filters: - - multiply: 0.06 - - platform: pulse_counter - pin: 13 - unit_of_measurement: 'kW' - name: 'Stromverbrauch Wärmepumpe' - update_interval: 30s - expire_after: 60s - filters: - - multiply: 0.06 - - platform: pulse_counter - pin: 14 - unit_of_measurement: 'kW' - name: 'Stromverbrauch Gesamt' - update_interval: 30s - expire_after: 60s - filters: - - multiply: 0.06 - - platform: dallas - dallas_id: 'dallas' - address: 0xfe0000031f1eaf28 - name: "Boiler Temperatur Oben" - - platform: dallas - dallas_id: 'dallas' - address: 0xba0000031f0e5228 - name: "Boiler Temperatur Unten" - - platform: dallas - dallas_id: 'dallas' - address: 0xa40000031f055028 - name: "Boiler Temperatur Mitte" - - platform: dallas - dallas_id: 'dallas' - address: 0x790000031ee1dc28 - name: "Heizung Rücklauf" - - platform: dallas - dallas_id: 'dallas' - address: 0xdd0000031efb0428 - name: "Ölheizung Vorlauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x710000031f0e7e28 - name: "Boiler Solar Rücklauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x92041703081aff28 - name: "Boiler Solar Vorlauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x2c04173159f4ff28 - name: "Heizung Vorlauf" - - platform: dallas - dallas_id: 'dallas' - address: 0xd10417315babff28 - name: "Wärmepumpe Vorlauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x6c0517024a17ff28 - name: "Boiler Heizung Vorlauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x7d04173139eeff28 - name: "Wärmepumpe Rücklauf" - - platform: dallas - dallas_id: 'dallas' - address: 0x3204166398a5ff28 - name: "Wärmepumpe Verdampfer" - -binary_sensor: - - platform: status - name: "Heizung Status" - -switch: - - platform: restart - name: "Heizung Neustart" diff --git a/examples/kuche.yaml b/examples/kuche.yaml deleted file mode 100644 index 7cb86f3fac..0000000000 --- a/examples/kuche.yaml +++ /dev/null @@ -1,60 +0,0 @@ -esphomeyaml: - name: kuche - platform: ESP8266 - board: nodemcuv2 - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.211 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: kuche - password: '[PASSWORD]' - # This is the default - discovery: true - - -dallas: - id: 'dallas' - pin: D1 - -sensor: - - platform: dallas - dallas_id: 'dallas' - address: 0x69041662d7f1ff28 - name: "Küche Raumtemperatur" - - platform: dallas - dallas_id: 'dallas' - address: 0x800416636bebff28 - name: "Küche Heizkörpertemperatur" - - platform: adc - pin: A0 - name: "Küche Helligkeit" - -output: - - platform: gpio - pin: D2 - id: 'ventilator' - -fan: - - platform: binary - output: 'ventilator' - name: 'Küche Heizkörper Ventilator' - -binary_sensor: - - platform: status - name: "Küche Status" - -switch: - - platform: restart - name: "Küche Neustart" diff --git a/examples/lebensmittelkeller.yaml b/examples/lebensmittelkeller.yaml deleted file mode 100644 index 1c407807b5..0000000000 --- a/examples/lebensmittelkeller.yaml +++ /dev/null @@ -1,58 +0,0 @@ -esphomeyaml: - name: lebensmittelkeller - platform: ESP8266 - board: nodemcuv2 - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.209 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: lebensmittelkeller - password: '[PASSWORD]' - # This is the default - discovery: true - -sensor: - - platform: dht - pin: D3 - temperature: - name: 'Lebensmittelkeller Temperatur' - humidity: - name: 'Lebensmittelkeller Feuchtigkeit' - model: DHT22 - - platform: adc - pin: A0 - name: "Lebensmittelkeller Helligkeit" - -output: - - platform: gpio - pin: D4 - id: 'ventilator' - -fan: - - platform: binary - output: 'ventilator' - name: 'Lebensmittelkeller Ventilator' - -switch: - - platform: gpio - pin: D2 - name: 'Lebensmittelkeller Entfeuchter' - icon: 'mdi:water-off' - - platform: restart - name: "Lebensmittelkeller Neustart" - -binary_sensor: - - platform: status - name: "Lebensmittelkeller Status" diff --git a/examples/livingroom.yaml b/examples/livingroom.yaml deleted file mode 100644 index c0bd577096..0000000000 --- a/examples/livingroom.yaml +++ /dev/null @@ -1,344 +0,0 @@ -esphomeyaml: - name: livingroom - platform: ESP32 - board: nodemcu-32s - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.201 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: livingroom - password: '[PASSWORD]' - # This is the default - discovery: true - -output: - - platform: ledc - id: 'fan_float' - frequency: 50000 - pin: 22 - bit_depth: 8 - -dallas: - pin: 23 - id: dallas - -sensor: - - platform: dallas - dallas_id: dallas - address: 0x1c0000031edd2a28 - name: "Wohnzimmer Raumtemperatur" - filters: - - sliding_window_moving_average: - window_size: 15 - send_every: 15 - - filter_out: 85 - - platform: dallas - dallas_id: dallas - address: 0x7a0315a8371eff28 - name: "Wohnzimmer Heizkörpertemperatur" - update_interval: 30s - filters: - - sliding_window_moving_average: - window_size: 15 - send_every: 15 - - filter_out: 85 - -fan: - - platform: speed - output: 'fan_float' - name: 'Wohnzimmer Heizkörper Ventilator' - -binary_sensor: - - platform: status - name: "Wohnzimmer Status" - -ir_transmitter: - pin: 32 - id: 'ir' - -switch: - - platform: restart - name: "Wohnzimmer Neustart" - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV On" - panasonic: - address: 0x4004 - command: 0x100BCBD - repeat: 25 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Off" - panasonic: - address: 0x4004 - command: 0x100BCBD - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV SD Card" - panasonic: - address: 0x4004 - command: 0x190D544 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Input TV" - panasonic: - address: 0x4004 - command: 0x1400C4D - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Input AV" - panasonic: - address: 0x4004 - command: 0x100A0A1 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Menu" - panasonic: - address: 0x4004 - command: 0x1004A4B - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Aspect Ratio" - panasonic: - address: 0x4004 - command: 0x1207B5A - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Viera Cast" - panasonic: - address: 0x4004 - command: 0x190C958 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Direct TV REC" - panasonic: - address: 0x4004 - command: 0x1909100 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Info" - panasonic: - address: 0x4004 - command: 0x1009C9D - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Exit" - panasonic: - address: 0x4004 - command: 0x100CBCA - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Viera Link" - panasonic: - address: 0x4004 - command: 0x1908D1C - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Viera Tools" - panasonic: - address: 0x4004 - command: 0x100F7F6 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Guide" - panasonic: - address: 0x4004 - command: 0x190E170 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Up" - panasonic: - address: 0x4004 - command: 0x1005253 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Left" - panasonic: - address: 0x4004 - command: 0x1007273 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV OK" - panasonic: - address: 0x4004 - command: 0x1009293 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Right" - panasonic: - address: 0x4004 - command: 0x100F2F3 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Down" - panasonic: - address: 0x4004 - command: 0x100D2D3 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Option" - panasonic: - address: 0x4004 - command: 0x190E574 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Back" - panasonic: - address: 0x4004 - command: 0x1002B2A - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Red" - panasonic: - address: 0x4004 - command: 0x1000E0F - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Green" - panasonic: - address: 0x4004 - command: 0x1000E0F # TODO: FIXME - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Yellow" - panasonic: - address: 0x4004 - command: 0x1004E4F - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Blue" - panasonic: - address: 0x4004 - command: 0x100CECF - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Text" - panasonic: - address: 0x4004 - command: 0x180C041 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Subtitle" - panasonic: - address: 0x4004 - command: 0x180A021 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Index" - panasonic: - address: 0x4004 - command: 0x1801091 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Hold" - panasonic: - address: 0x4004 - command: 0x1809011 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 1" - panasonic: - address: 0x4004 - command: 0x1000809 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 2" - panasonic: - address: 0x4004 - command: 0x1008889 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 3" - panasonic: - address: 0x4004 - command: 0x1004849 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 4" - panasonic: - address: 0x4004 - command: 0x100C8C9 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 5" - panasonic: - address: 0x4004 - command: 0x1002829 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 6" - panasonic: - address: 0x4004 - command: 0x100A8A9 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 7" - panasonic: - address: 0x4004 - command: 0x1006869 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 8" - panasonic: - address: 0x4004 - command: 0x100E8E9 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 9" - panasonic: - address: 0x4004 - command: 0x1001819 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV 0" - panasonic: - address: 0x4004 - command: 0x1009899 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Mute" - panasonic: - address: 0x4004 - command: 0x1004C4D - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Last View" - panasonic: - address: 0x4004 - command: 0x100ECED - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Volume Up" - panasonic: - address: 0x4004 - command: 0x1000405 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Volume Down" - panasonic: - address: 0x4004 - command: 0x1008485 - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Program Up" - panasonic: - address: 0x4004 - command: 0x1002C2D - - platform: ir_transmitter - ir_transmitter_id: 'ir' - name: "Panasonic TV Program Down" - panasonic: - address: 0x4004 - command: 0x100ACAD diff --git a/examples/sonoff_4ch.yaml b/examples/sonoff_4ch.yaml deleted file mode 100644 index a280efdfe0..0000000000 --- a/examples/sonoff_4ch.yaml +++ /dev/null @@ -1,91 +0,0 @@ -esphomeyaml: - name: - platform: ESP8266 - board: esp01_1m - board_flash_mode: dout - -wifi: - ssid: - password: - -mqtt: - broker: - username: - password: - -logger: - -ota: - -binary_sensor: - - platform: gpio - pin: - number: GPIO0 - mode: INPUT_PULLUP - inverted: True - name: "Sonoff 4CH Button 1" - on_press: - then: - switch.toggle: - id: relay_1 - - platform: gpio - pin: - number: GPIO9 - mode: INPUT_PULLUP - inverted: True - name: "Sonoff 4CH Button 2" - on_press: - then: - switch.toggle: - id: relay_2 - - platform: gpio - pin: - number: GPIO10 - mode: INPUT_PULLUP - inverted: True - name: "Sonoff 4CH Button 3" - on_press: - then: - switch.toggle: - id: relay_3 - - platform: gpio - pin: - number: GPIO14 - mode: INPUT_PULLUP - inverted: True - name: "Sonoff 4CH Button 4" - on_press: - then: - switch.toggle: - id: relay_4 - - platform: status - name: "Sonoff 4CH Status" - -switch: - - platform: gpio - name: "Sonoff 4CH Relay 1" - pin: GPIO12 - id: relay_1 - - platform: gpio - name: "Sonoff 4CH Relay 2" - pin: GPIO5 - id: relay_2 - - platform: gpio - name: "Sonoff 4CH Relay 3" - pin: GPIO4 - id: relay_3 - - platform: gpio - name: "Sonoff 4CH Relay 4" - pin: GPIO15 - id: relay_4 - -output: - - platform: esp8266_pwm - id: blue_led - pin: GPIO13 - inverted: True - -light: - - platform: monochromatic - name: "Sonoff 4CH Blue LED" - output: blue_led diff --git a/examples/sonoff_s20.yaml b/examples/sonoff_s20.yaml deleted file mode 100644 index 72eafef9e5..0000000000 --- a/examples/sonoff_s20.yaml +++ /dev/null @@ -1,50 +0,0 @@ -esphomeyaml: - name: - platform: ESP8266 - board: esp01_1m - board_flash_mode: dout - -wifi: - ssid: - password: - -mqtt: - broker: - username: - password: - -logger: - -ota: - -binary_sensor: - - platform: gpio - pin: - number: GPIO0 - mode: INPUT_PULLUP - inverted: True - name: "Sonoff S20 Button" - on_press: - then: - - switch.toggle: - id: relay - - platform: status - name: "Sonoff S20 Status" - - -switch: - - platform: gpio - name: "Sonoff S20 Relay" - pin: GPIO12 - id: relay - -output: - - platform: esp8266_pwm - id: s20_green_led - pin: GPIO13 - inverted: True - -light: - - platform: monochromatic - name: "Sonoff S20 Green LED" - output: s20_green_led diff --git a/examples/terrasse.yaml b/examples/terrasse.yaml deleted file mode 100644 index 6c1481ebca..0000000000 --- a/examples/terrasse.yaml +++ /dev/null @@ -1,48 +0,0 @@ -esphomeyaml: - name: terrasse - platform: ESP32 - board: nodemcu-32s - -logger: - level: verbose - -wifi: - ssid: '[SSID]' - password: '[PASSWORD]' - manual_ip: - static_ip: 192.168.178.205 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - -ota: - -mqtt: - broker: 192.168.178.84 - username: terrasse - password: '[PASSWORD]' - # This is the default - discovery: true - -dallas: - pin: 25 - id: dallas - -sensor: - - platform: pulse_counter - pin: 34 - name: "Terrasse Wind" - - platform: pulse_counter - pin: 39 - name: "Terrasse Regen" - - platform: dallas - dallas_id: dallas - index: 0 - name: "Terrasse Temperatur" - -binary_sensor: - - platform: status - name: "Terrasse Status" - -switch: - - platform: restart - name: "Terrasse Neustart" diff --git a/pylintrc b/pylintrc index a3cb5bce3a..98129bb031 100644 --- a/pylintrc +++ b/pylintrc @@ -17,6 +17,7 @@ disable= invalid-name, cyclic-import, redefined-builtin, + undefined-loop-variable, additional-builtins=