diff --git a/esphomeyaml/components/binary_sensor/gpio.py b/esphomeyaml/components/binary_sensor/gpio.py index c4e362aa13..04e5b9a6f3 100644 --- a/esphomeyaml/components/binary_sensor/gpio.py +++ b/esphomeyaml/components/binary_sensor/gpio.py @@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import binary_sensor from esphomeyaml.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN -from esphomeyaml.helpers import App, add, exp_gpio_input_pin, variable +from esphomeyaml.helpers import App, add, variable, gpio_input_pin_expression PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID('gpio_binary_sensor'): cv.register_variable_id, @@ -13,7 +13,8 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_gpio_binary_sensor(config[CONF_NAME], exp_gpio_input_pin(config[CONF_PIN])) + rhs = App.make_gpio_binary_sensor(config[CONF_NAME], + gpio_input_pin_expression(config[CONF_PIN])) gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs) if CONF_INVERTED in config: add(gpio.Pgpio.set_inverted(config[CONF_INVERTED])) diff --git a/esphomeyaml/components/deep_sleep.py b/esphomeyaml/components/deep_sleep.py index 2ddf2a8cd3..1818897e4e 100644 --- a/esphomeyaml/components/deep_sleep.py +++ b/esphomeyaml/components/deep_sleep.py @@ -1,14 +1,14 @@ import voluptuous as vol from esphomeyaml import config_validation as cv, pins -from esphomeyaml.const import CONF_ID, CONF_RUN_CYCLES, CONF_RUN_DURATION, CONF_SLEEP_DURATION, \ - CONF_WAKEUP_PIN -from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_input_pin +from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \ + CONF_SLEEP_DURATION, CONF_WAKEUP_PIN +from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression def validate_pin_number(value): valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 39] - if value not in valid_pins: + if value[CONF_NUMBER] not in valid_pins: raise vol.Invalid(u"Only pins {} support wakeup" u"".format(', '.join(str(x) for x in valid_pins))) return value @@ -17,8 +17,8 @@ def validate_pin_number(value): CONFIG_SCHEMA = vol.Schema({ cv.GenerateID('deep_sleep'): cv.register_variable_id, vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, - vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INPUT_PIN_SCHEMA, - pins.schema_validate_number(validate_pin_number)), + vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA, + validate_pin_number), vol.Optional(CONF_RUN_CYCLES): cv.positive_int, vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, }) @@ -30,7 +30,7 @@ def to_code(config): if CONF_SLEEP_DURATION in config: add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION])) if CONF_WAKEUP_PIN in config: - pin = exp_gpio_input_pin(config[CONF_WAKEUP_PIN]) + pin = gpio_input_pin_expression(config[CONF_WAKEUP_PIN]) add(deep_sleep.set_wakeup_pin(pin)) if CONF_RUN_CYCLES in config: add(deep_sleep.set_run_cycles(config[CONF_RUN_CYCLES])) diff --git a/esphomeyaml/components/ir_transmitter.py b/esphomeyaml/components/ir_transmitter.py index c6595636ba..c402aedeb9 100644 --- a/esphomeyaml/components/ir_transmitter.py +++ b/esphomeyaml/components/ir_transmitter.py @@ -3,7 +3,7 @@ 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.helpers import App, Pvariable, exp_gpio_output_pin +from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent' @@ -17,7 +17,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ def to_code(config): for conf in config: - pin = exp_gpio_output_pin(conf[CONF_PIN]) + pin = gpio_output_pin_expression(conf[CONF_PIN]) rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT)) Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs) diff --git a/esphomeyaml/components/output/esp8266_pwm.py b/esphomeyaml/components/output/esp8266_pwm.py index c89db95604..8bc507df2c 100644 --- a/esphomeyaml/components/output/esp8266_pwm.py +++ b/esphomeyaml/components/output/esp8266_pwm.py @@ -2,27 +2,26 @@ import voluptuous as vol from esphomeyaml import pins from esphomeyaml.components import output -from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266 +from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266, CONF_NUMBER from esphomeyaml.core import ESPHomeYAMLError -from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin +from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] def valid_pwm_pin(value): - if value >= 16: + if value[CONF_NUMBER] >= 16: raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.") return value PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({ - vol.Required(CONF_PIN): vol.All(pins.GPIO_OUTPUT_PIN_SCHEMA, - pins.schema_validate_number(valid_pwm_pin)), + vol.Required(CONF_PIN): vol.All(pins.GPIO_INTERNAL_OUTPUT_PIN_SCHEMA, valid_pwm_pin), }) def to_code(config): - pin = exp_gpio_output_pin(config[CONF_PIN]) + pin = gpio_output_pin_expression(config[CONF_PIN]) rhs = App.make_esp8266_pwm_output(pin) gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs) output.setup_output_platform(gpio, config) diff --git a/esphomeyaml/components/output/gpio.py b/esphomeyaml/components/output/gpio.py index 0a4d11248d..c6c67a396f 100644 --- a/esphomeyaml/components/output/gpio.py +++ b/esphomeyaml/components/output/gpio.py @@ -3,7 +3,7 @@ import voluptuous as vol from esphomeyaml import pins from esphomeyaml.components import output from esphomeyaml.const import CONF_ID, CONF_PIN -from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin +from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({ vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, @@ -11,7 +11,7 @@ PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({ def to_code(config): - pin = exp_gpio_output_pin(config[CONF_PIN]) + pin = gpio_output_pin_expression(config[CONF_PIN]) rhs = App.make_gpio_output(pin) gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs) output.setup_output_platform(gpio, config) diff --git a/esphomeyaml/components/power_supply.py b/esphomeyaml/components/power_supply.py index c9b240a863..d70383454d 100644 --- a/esphomeyaml/components/power_supply.py +++ b/esphomeyaml/components/power_supply.py @@ -3,7 +3,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN -from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_output_pin +from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({ vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, @@ -16,7 +16,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA]) def to_code(config): for conf in config: - pin = exp_gpio_output_pin(conf[CONF_PIN]) + pin = gpio_output_pin_expression(conf[CONF_PIN]) rhs = App.make_power_supply(pin) psu = Pvariable('PowerSupplyComponent', conf[CONF_ID], rhs) if CONF_ENABLE_TIME in conf: diff --git a/esphomeyaml/components/sensor/dht.py b/esphomeyaml/components/sensor/dht.py index 0aef34ac7e..54edae5e72 100644 --- a/esphomeyaml/components/sensor/dht.py +++ b/esphomeyaml/components/sensor/dht.py @@ -5,7 +5,7 @@ from esphomeyaml.components import sensor from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \ CONF_TEMPERATURE, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, RawExpression, add, variable, exp_gpio_output_pin +from esphomeyaml.helpers import App, RawExpression, add, variable, gpio_output_pin_expression from esphomeyaml.pins import GPIO_OUTPUT_PIN_SCHEMA DHT_MODELS = { @@ -27,7 +27,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - pin = exp_gpio_output_pin(config[CONF_PIN]) + pin = gpio_output_pin_expression(config[CONF_PIN]) rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME], pin, config.get(CONF_UPDATE_INTERVAL)) diff --git a/esphomeyaml/components/sensor/rotary_encoder.py b/esphomeyaml/components/sensor/rotary_encoder.py new file mode 100644 index 0000000000..c470da5cdd --- /dev/null +++ b/esphomeyaml/components/sensor/rotary_encoder.py @@ -0,0 +1,44 @@ +import voluptuous as vol + +import esphomeyaml.config_validation as cv +from esphomeyaml import pins +from esphomeyaml.components import sensor +from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_RESOLUTION +from esphomeyaml.helpers import App, RawExpression, add, gpio_input_pin_expression, variable + +RESOLUTIONS = { + '1': 'sensor::ROTARY_ENCODER_1_PULSE_PER_CYCLE', + '2': 'sensor::ROTARY_ENCODER_2_PULSES_PER_CYCLE', + '4': 'sensor::ROTARY_ENCODER_4_PULSES_PER_CYCLE', +} + +CONF_PIN_A = 'pin_a' +CONF_PIN_B = 'pin_b' +CONF_PIN_RESET = 'pin_reset' + +PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ + cv.GenerateID('rotary_encoder'): cv.register_variable_id, + vol.Required(CONF_PIN_A): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA, + vol.Required(CONF_PIN_B): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA, + vol.Optional(CONF_PIN_RESET): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA, + vol.Optional(CONF_RESOLUTION): vol.All(cv.string, vol.Any(*RESOLUTIONS)), +}).extend(sensor.MQTT_SENSOR_SCHEMA.schema) + + +def to_code(config): + pin_a = gpio_input_pin_expression(config[CONF_PIN_A]) + pin_b = gpio_input_pin_expression(config[CONF_PIN_B]) + rhs = App.make_rotary_encoder_sensor(config[CONF_NAME], pin_a, pin_b) + make = variable('Application::MakeRotaryEncoderSensor', config[CONF_ID], rhs) + encoder = make.Protary_encoder + if CONF_PIN_RESET in config: + pin_i = gpio_input_pin_expression(config[CONF_PIN_RESET]) + add(encoder.set_reset_pin(pin_i)) + if CONF_RESOLUTION in config: + resolution = RESOLUTIONS[config[CONF_RESOLUTION]] + add(encoder.set_resolution(RawExpression(resolution))) + sensor.setup_sensor(encoder, config) + sensor.setup_mqtt_sensor_component(make.Pmqtt, config) + + +BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR' diff --git a/esphomeyaml/components/sensor/ultrasonic.py b/esphomeyaml/components/sensor/ultrasonic.py index 557e2989bd..1144bf8421 100644 --- a/esphomeyaml/components/sensor/ultrasonic.py +++ b/esphomeyaml/components/sensor/ultrasonic.py @@ -5,8 +5,8 @@ from esphomeyaml import pins from esphomeyaml.components import sensor from esphomeyaml.const import CONF_ECHO_PIN, CONF_ID, CONF_NAME, \ CONF_TIMEOUT_METER, CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL -from esphomeyaml.helpers import App, add, exp_gpio_input_pin, exp_gpio_output_pin, \ - variable +from esphomeyaml.helpers import App, add, variable, gpio_output_pin_expression, \ + gpio_input_pin_expression PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID('ultrasonic'): cv.register_variable_id, @@ -19,8 +19,8 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ def to_code(config): - trigger = exp_gpio_output_pin(config[CONF_TRIGGER_PIN]) - echo = exp_gpio_input_pin(config[CONF_ECHO_PIN]) + trigger = gpio_output_pin_expression(config[CONF_TRIGGER_PIN]) + echo = gpio_input_pin_expression(config[CONF_ECHO_PIN]) rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo, config.get(CONF_UPDATE_INTERVAL)) make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs) diff --git a/esphomeyaml/components/switch/gpio.py b/esphomeyaml/components/switch/gpio.py index e37872c6c8..cddd13b9ff 100644 --- a/esphomeyaml/components/switch/gpio.py +++ b/esphomeyaml/components/switch/gpio.py @@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml import pins from esphomeyaml.components import switch from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN -from esphomeyaml.helpers import App, exp_gpio_output_pin, variable +from esphomeyaml.helpers import App, variable, gpio_output_pin_expression PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ cv.GenerateID('gpio_switch'): cv.register_variable_id, @@ -13,7 +13,7 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ def to_code(config): - rhs = App.make_gpio_switch(config[CONF_NAME], exp_gpio_output_pin(config[CONF_PIN])) + rhs = App.make_gpio_switch(config[CONF_NAME], gpio_output_pin_expression(config[CONF_PIN])) gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs) switch.setup_switch(gpio.Pswitch_, config) switch.setup_mqtt_switch(gpio.Pmqtt, config) diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index a891f5ad57..fa11b4073a 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -186,15 +186,15 @@ class Literal(Expression): # From https://stackoverflow.com/a/14945195/8924614 -def cpp_string_escape(s, encoding='utf-8'): - if isinstance(s, unicode): - s = s.encode(encoding) +def cpp_string_escape(string, encoding='utf-8'): + if isinstance(string, unicode): + string = string.encode(encoding) result = '' - for c in s: - if not (32 <= ord(c) < 127) or c in ('\\', '"'): - result += '\\%03o' % ord(c) + for character in string: + if not (32 <= ord(character) < 127) or character in ('\\', '"'): + result += '\\%03o' % ord(character) else: - result += c + result += character return '"' + result + '"' @@ -408,37 +408,34 @@ def get_gpio_pin_number(conf): return conf[CONF_NUMBER] -def exp_gpio_pin_(obj, conf, default_mode): - if isinstance(conf, int): - return conf - +def generic_gpio_pin_expression_(conf, mock_obj, default_mode): + if conf is None: + return None + number = conf[CONF_NUMBER] + inverted = conf.get(CONF_INVERTED) if CONF_PCF8574 in conf: hub = get_variable(conf[CONF_PCF8574], 'io::PCF8574Component') if default_mode == u'INPUT': - return hub.make_input_pin(conf[CONF_NUMBER], - RawExpression('PCF8574_' + conf[CONF_MODE]), - conf[CONF_INVERTED]) + mode = conf.get(CONF_MODE, u'INPUT') + return hub.make_input_pin(number, + RawExpression('PCF8574_' + mode), + inverted) elif default_mode == u'OUTPUT': - return hub.make_output_pin(conf[CONF_NUMBER], conf[CONF_INVERTED]) + return hub.make_output_pin(number, inverted) else: raise ESPHomeYAMLError(u"Unknown default mode {}".format(default_mode)) - - if conf.get(CONF_INVERTED) is None: - return obj(conf[CONF_NUMBER], conf.get(CONF_MODE)) - return obj(conf[CONF_NUMBER], RawExpression(conf.get(CONF_MODE, default_mode)), - conf[CONF_INVERTED]) + if len(conf) == 1: + return IntLiteral(number) + mode = RawExpression(conf.get(CONF_MODE, default_mode)) + return mock_obj(number, mode, inverted) -def exp_gpio_pin(conf): - return GPIOPin(conf[CONF_NUMBER], conf[CONF_MODE], conf.get(CONF_INVERTED)) +def gpio_output_pin_expression(conf): + return generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT') -def exp_gpio_output_pin(conf): - return exp_gpio_pin_(GPIOOutputPin, conf, u'OUTPUT') - - -def exp_gpio_input_pin(conf): - return exp_gpio_pin_(GPIOInputPin, conf, u'INPUT') +def gpio_input_pin_expression(conf): + return generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT') def setup_mqtt_component(obj, config): diff --git a/esphomeyaml/pins.py b/esphomeyaml/pins.py index ed6c305b72..0b0ea5351a 100644 --- a/esphomeyaml/pins.py +++ b/esphomeyaml/pins.py @@ -177,6 +177,29 @@ def pin_mode(value): raise vol.Invalid(u"Invalid ESP platform.") +GPIO_FULL_OUTPUT_PIN_SCHEMA = vol.Schema({ + vol.Required(CONF_NUMBER): output_pin, + vol.Optional(CONF_MODE): pin_mode, + vol.Optional(CONF_INVERTED): cv.boolean, +}) + +GPIO_FULL_INPUT_PIN_SCHEMA = vol.Schema({ + vol.Required(CONF_NUMBER): output_pin, + vol.Optional(CONF_MODE): pin_mode, + vol.Optional(CONF_INVERTED): cv.boolean, +}) + + +def shorthand_output_pin(value): + value = output_pin(value) + return {CONF_NUMBER: value} + + +def shorthand_input_pin(value): + value = input_pin(value) + return {CONF_NUMBER: value} + + PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({ vol.Required(CONF_PCF8574): cv.variable_id, vol.Required(CONF_NUMBER): vol.Coerce(int), @@ -184,28 +207,13 @@ PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({ }) PCF8574_INPUT_PIN_SCHEMA = PCF8574_OUTPUT_PIN_SCHEMA.extend({ - vol.Optional(CONF_MODE, default='INPUT'): vol.All(vol.Upper, vol.Any("INPUT", "INPUT_PULLUP")), + vol.Optional(CONF_MODE): vol.All(vol.Upper, vol.Any("INPUT", "INPUT_PULLUP")), }) -GPIO_OUTPUT_PIN_SCHEMA = vol.Any(output_pin, PCF8574_OUTPUT_PIN_SCHEMA, vol.Schema({ - vol.Required(CONF_NUMBER): output_pin, - vol.Optional(CONF_MODE): pin_mode, - vol.Optional(CONF_INVERTED): cv.boolean, -})) +GPIO_INTERNAL_OUTPUT_PIN_SCHEMA = vol.Any(shorthand_output_pin, GPIO_FULL_OUTPUT_PIN_SCHEMA) -GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, PCF8574_INPUT_PIN_SCHEMA, vol.Schema({ - vol.Required(CONF_NUMBER): input_pin, - vol.Optional(CONF_MODE): pin_mode, - vol.Optional(CONF_INVERTED): cv.boolean, -})) +GPIO_OUTPUT_PIN_SCHEMA = vol.Any(PCF8574_OUTPUT_PIN_SCHEMA, GPIO_INTERNAL_OUTPUT_PIN_SCHEMA) +GPIO_INTERNAL_INPUT_PIN_SCHEMA = vol.Any(shorthand_input_pin, GPIO_FULL_INPUT_PIN_SCHEMA) -def schema_validate_number(validator): - def valid(value): - if isinstance(value, dict): - value[CONF_NUMBER] = validator(value[CONF_NUMBER]) - else: - value = validator(value) - return value - - return valid +GPIO_INPUT_PIN_SCHEMA = vol.Any(PCF8574_INPUT_PIN_SCHEMA, GPIO_INTERNAL_INPUT_PIN_SCHEMA)