diff --git a/MANIFEST.in b/MANIFEST.in index 155442d556..ab00d7e896 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ include README.md -include esphomeyaml/hassio/templates/index.html -include esphomeyaml/hassio/static/materialize-stepper.min.css -include esphomeyaml/hassio/static/materialize-stepper.min.js +include esphomeyaml/dashboard/templates/index.html +include esphomeyaml/dashboard/static/materialize-stepper.min.css +include esphomeyaml/dashboard/static/materialize-stepper.min.js diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index 2bd1b9759a..42bf3680cd 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -314,16 +314,16 @@ def command_version(args): return 0 -def command_hassio(args): - from esphomeyaml.hassio import hassio +def command_dashboard(args): + from esphomeyaml.dashboard import dashboard - return hassio.start_web_server(args) + return dashboard.start_web_server(args) PRE_CONFIG_ACTIONS = { 'wizard': command_wizard, 'version': command_version, - 'hassio': command_hassio + 'dashboard': command_dashboard } POST_CONFIG_ACTIONS = { @@ -353,7 +353,7 @@ def parse_args(argv): "For example /dev/cu.SLAB_USBtoUART.") parser_upload.add_argument('--host-port', help="Specify the host port.", type=int) parser_upload.add_argument('--use-esptoolpy', - help="Use esptool.py for HassIO (only for ESP8266)", + help="Use esptool.py for the uploading (only for ESP8266)", action='store_true') parser_logs = subparsers.add_parser('logs', help='Validate the configuration ' @@ -364,7 +364,7 @@ def parse_args(argv): parser_logs.add_argument('--client-id', help='Manually set the client id.') parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use" "For example /dev/cu.SLAB_USBtoUART.") - parser_logs.add_argument('--escape', help="Escape ANSI color codes for HassIO", + parser_logs.add_argument('--escape', help="Escape ANSI color codes for running in dashboard", action='store_true') parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, ' @@ -378,9 +378,10 @@ def parse_args(argv): parser_run.add_argument('--username', help='Manually set the MQTT username for logs.') parser_run.add_argument('--password', help='Manually set the MQTT password for logs.') parser_run.add_argument('--client-id', help='Manually set the client id for logs.') - parser_run.add_argument('--escape', help="Escape ANSI color codes for HassIO", + parser_run.add_argument('--escape', help="Escape ANSI color codes for running in dashboard", action='store_true') - parser_run.add_argument('--use-esptoolpy', help="Use esptool.py for HassIO (only for ESP8266)", + parser_run.add_argument('--use-esptoolpy', + help="Use esptool.py for the uploading (only for ESP8266)", action='store_true') parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from " @@ -397,9 +398,10 @@ def parse_args(argv): subparsers.add_parser('version', help="Print the esphomeyaml version and exit.") - hassio = subparsers.add_parser('hassio', help="Create a simple webserver for a HassIO add-on.") - hassio.add_argument("--port", help="The HTTP port to open connections on.", type=int, - default=6052) + dashboard = subparsers.add_parser('dashboard', + help="Create a simple webserver for a dashboard.") + dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int, + default=6052) return parser.parse_args(argv[1:]) diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index 4793ba24f3..45064ae2a5 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -4,9 +4,9 @@ import esphomeyaml.config_validation as cv from esphomeyaml.components import cover, fan from esphomeyaml.const import CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, CONF_BLUE, \ CONF_BRIGHTNESS, CONF_CONDITION_ID, CONF_DELAY, CONF_EFFECT, CONF_FLASH_LENGTH, CONF_GREEN, \ - CONF_ID, CONF_IF, CONF_LAMBDA, CONF_MAX, CONF_MIN, CONF_OR, CONF_PAYLOAD, CONF_QOS, \ - CONF_RANGE, CONF_RED, CONF_RETAIN, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, \ - CONF_TRIGGER_ID, CONF_WHITE, CONF_OSCILLATING, CONF_SPEED + 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_ABOVE, CONF_BELOW from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, \ bool_, esphomelib_ns, float_, get_variable, process_lambda, std_string, templatable, uint32, \ @@ -51,11 +51,11 @@ ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Required(CONF_ID): cv.variable_id, vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), vol.Optional(CONF_FLASH_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), - vol.Optional(CONF_BRIGHTNESS): cv.templatable(cv.zero_to_one_float), - vol.Optional(CONF_RED): cv.templatable(cv.zero_to_one_float), - vol.Optional(CONF_GREEN): cv.templatable(cv.zero_to_one_float), - vol.Optional(CONF_BLUE): cv.templatable(cv.zero_to_one_float), - vol.Optional(CONF_WHITE): cv.templatable(cv.zero_to_one_float), + vol.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage), + vol.Optional(CONF_RED): cv.templatable(cv.percentage), + vol.Optional(CONF_GREEN): cv.templatable(cv.percentage), + vol.Optional(CONF_BLUE): cv.templatable(cv.percentage), + vol.Optional(CONF_WHITE): cv.templatable(cv.percentage), vol.Optional(CONF_EFFECT): cv.templatable(cv.string), }), vol.Optional(CONF_SWITCH_TOGGLE): vol.Schema({ @@ -116,9 +116,9 @@ CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ vol.Optional(CONF_AND): validate_recursive_condition, vol.Optional(CONF_OR): validate_recursive_condition, vol.Optional(CONF_RANGE): vol.All(vol.Schema({ - vol.Optional(CONF_MIN): vol.Coerce(float), - vol.Optional(CONF_MAX): vol.Coerce(float), - }), cv.has_at_least_one_key(CONF_MIN, CONF_MAX)), + vol.Optional(CONF_ABOVE): vol.Coerce(float), + 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_at_exactly_one_key(*CONDITION_KEYS)]) @@ -149,10 +149,10 @@ def build_condition(config, arg_type): conf = config[CONF_RANGE] rhs = RangeCondition.new(template_arg) condition = Pvariable(RangeCondition.template(template_arg), config[CONF_CONDITION_ID], rhs) - if CONF_MIN in conf: - condition.set_min(templatable(conf[CONF_MIN], arg_type, float_)) - if CONF_MAX in conf: - condition.set_max(templatable(conf[CONF_MAX], arg_type, float_)) + if CONF_ABOVE in conf: + condition.set_min(templatable(conf[CONF_ABOVE], arg_type, float_)) + if CONF_BELOW in conf: + condition.set_max(templatable(conf[CONF_BELOW], arg_type, float_)) return condition raise ESPHomeYAMLError(u"Unsupported condition {}".format(config)) diff --git a/esphomeyaml/components/esp32_touch.py b/esphomeyaml/components/esp32_touch.py index b77c0d0126..a89317fc8e 100644 --- a/esphomeyaml/components/esp32_touch.py +++ b/esphomeyaml/components/esp32_touch.py @@ -18,7 +18,7 @@ def validate_voltage(values): value = cv.string(value) if not value.endswith('V'): value += 'V' - return cv.one_of(values)(value) + return cv.one_of(*values)(value) return validator diff --git a/esphomeyaml/components/fan/speed.py b/esphomeyaml/components/fan/speed.py index d2e4be49e6..469fe54c67 100644 --- a/esphomeyaml/components/fan/speed.py +++ b/esphomeyaml/components/fan/speed.py @@ -14,9 +14,9 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({ vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic, vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id, vol.Optional(CONF_SPEED): vol.Schema({ - vol.Required(CONF_LOW): cv.zero_to_one_float, - vol.Required(CONF_MEDIUM): cv.zero_to_one_float, - vol.Required(CONF_HIGH): cv.zero_to_one_float, + vol.Required(CONF_LOW): cv.percentage, + vol.Required(CONF_MEDIUM): cv.percentage, + vol.Required(CONF_HIGH): cv.percentage, }), }).extend(fan.FAN_SCHEMA.schema) diff --git a/esphomeyaml/components/output/__init__.py b/esphomeyaml/components/output/__init__.py index 1c3940f8cf..d1717d3fad 100644 --- a/esphomeyaml/components/output/__init__.py +++ b/esphomeyaml/components/output/__init__.py @@ -14,7 +14,7 @@ BINARY_OUTPUT_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({ }) FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({ - vol.Optional(CONF_MAX_POWER): cv.zero_to_one_float, + vol.Optional(CONF_MAX_POWER): cv.percentage, }) output_ns = esphomelib_ns.namespace('output') diff --git a/esphomeyaml/components/sensor/__init__.py b/esphomeyaml/components/sensor/__init__.py index 21c6734f1a..976ca83eb3 100644 --- a/esphomeyaml/components/sensor/__init__.py +++ b/esphomeyaml/components/sensor/__init__.py @@ -2,10 +2,10 @@ import voluptuous as vol import esphomeyaml.config_validation as cv from esphomeyaml import automation -from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_DEBOUNCE, CONF_DELTA, \ - CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, \ - CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_LAMBDA, CONF_MAX, CONF_MIN, \ - CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE, \ +from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \ + CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, \ + CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_LAMBDA, \ + CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE,\ CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \ CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, esphomelib_ns, float_, \ @@ -59,9 +59,9 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ vol.Optional(CONF_ON_RAW_VALUE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA]), vol.Optional(CONF_ON_VALUE_RANGE): vol.All(cv.ensure_list, [vol.All( automation.AUTOMATION_SCHEMA.extend({ - vol.Optional(CONF_MIN): vol.Coerce(float), - vol.Optional(CONF_MAX): vol.Coerce(float), - }), cv.has_at_least_one_key(CONF_MIN, CONF_MAX))]), + vol.Optional(CONF_ABOVE): vol.Coerce(float), + vol.Optional(CONF_BELOW): vol.Coerce(float), + }), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW))]), }) # pylint: disable=invalid-name @@ -144,10 +144,10 @@ def setup_sensor_core_(sensor_var, mqtt_var, config): for conf in config.get(CONF_ON_VALUE_RANGE, []): rhs = sensor_var.make_value_range_trigger() trigger = Pvariable(ValueRangeTrigger, conf[CONF_TRIGGER_ID], rhs) - if CONF_MIN in conf: - trigger.set_min(templatable(conf[CONF_MIN], float_, float_)) - if CONF_MAX in conf: - trigger.set_max(templatable(conf[CONF_MAX], float_, float_)) + if CONF_ABOVE in conf: + trigger.set_min(templatable(conf[CONF_ABOVE], float_, float_)) + if CONF_BELOW in conf: + trigger.set_max(templatable(conf[CONF_BELOW], float_, float_)) automation.build_automation(trigger, float_, conf) if CONF_EXPIRE_AFTER in config: diff --git a/esphomeyaml/components/sensor/ads1115.py b/esphomeyaml/components/sensor/ads1115.py index be649f12d8..2608c40304 100644 --- a/esphomeyaml/components/sensor/ads1115.py +++ b/esphomeyaml/components/sensor/ads1115.py @@ -36,14 +36,18 @@ def validate_gain(value): elif not isinstance(value, (str, unicode)): raise vol.Invalid('invalid gain "{}"'.format(value)) - if value not in GAIN: - raise vol.Invalid("Invalid gain, options are {}".format(', '.join(GAIN.keys()))) - return value + return cv.one_of(*GAIN)(value) + + +def validate_mux(value): + value = cv.string(value).upper() + value = value.replace(' ', '_') + return cv.one_of(*MUX)(value) PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID('ads1115_sensor'): cv.register_variable_id, - vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, cv.one_of(*MUX)), + vol.Required(CONF_MULTIPLEXER): validate_mux, vol.Required(CONF_GAIN): validate_gain, vol.Optional(CONF_ADS1115_ID): cv.variable_id, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, diff --git a/esphomeyaml/config_validation.py b/esphomeyaml/config_validation.py index 79ba441d20..4b86d353bb 100644 --- a/esphomeyaml/config_validation.py +++ b/esphomeyaml/config_validation.py @@ -461,6 +461,12 @@ hex_uint32_t = vol.All(hex_int, vol.Range(min=0, max=4294967295)) i2c_address = hex_uint8_t +def percentage(value): + if isinstance(value, (str, unicode)) and value.endswith('%'): + value = float(value[:-1].rstrip()) / 100.0 + return zero_to_one_float(value) + + def invalid(message): def validator(value): raise vol.Invalid(message) diff --git a/esphomeyaml/const.py b/esphomeyaml/const.py index c4af929854..fc454f18fe 100644 --- a/esphomeyaml/const.py +++ b/esphomeyaml/const.py @@ -81,8 +81,8 @@ CONF_TRANSITION_LENGTH = 'transition_length' CONF_FLASH_LENGTH = 'flash_length' CONF_BRIGHTNESS = 'brightness' CONF_EFFECT = 'effect' -CONF_MIN = 'min' -CONF_MAX = 'max' +CONF_ABOVE = 'above' +CONF_BELOW = 'below' CONF_ON = 'on' CONF_IF = 'if' CONF_THEN = 'then' diff --git a/esphomeyaml/hassio/__init__.py b/esphomeyaml/dashboard/__init__.py similarity index 100% rename from esphomeyaml/hassio/__init__.py rename to esphomeyaml/dashboard/__init__.py diff --git a/esphomeyaml/hassio/hassio.py b/esphomeyaml/dashboard/dashboard.py similarity index 96% rename from esphomeyaml/hassio/hassio.py rename to esphomeyaml/dashboard/dashboard.py index 4b43358cc1..9ce244a181 100644 --- a/esphomeyaml/hassio/hassio.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -140,7 +140,7 @@ class DownloadBinaryRequestHandler(tornado.web.RequestHandler): class MainRequestHandler(tornado.web.RequestHandler): def get(self): - files = [f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml')] + files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml')]) full_path_files = [os.path.join(CONFIG_DIR, f) for f in files] self.render("templates/index.html", files=files, full_path_files=full_path_files, version=const.__version__) @@ -157,7 +157,7 @@ def make_app(): (r"/serial-ports", SerialPortRequestHandler), (r"/wizard.html", WizardRequestHandler), (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}), - ], debug=True) + ], debug=False) def start_web_server(args): @@ -166,7 +166,7 @@ def start_web_server(args): if not os.path.exists(CONFIG_DIR): os.makedirs(CONFIG_DIR) - _LOGGER.info("Starting HassIO add-on web server on port %s and configuration dir %s...", + _LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...", args.port, CONFIG_DIR) app = make_app() app.listen(args.port) diff --git a/esphomeyaml/hassio/static/materialize-stepper.min.css b/esphomeyaml/dashboard/static/materialize-stepper.min.css similarity index 100% rename from esphomeyaml/hassio/static/materialize-stepper.min.css rename to esphomeyaml/dashboard/static/materialize-stepper.min.css diff --git a/esphomeyaml/hassio/static/materialize-stepper.min.js b/esphomeyaml/dashboard/static/materialize-stepper.min.js similarity index 100% rename from esphomeyaml/hassio/static/materialize-stepper.min.js rename to esphomeyaml/dashboard/static/materialize-stepper.min.js diff --git a/esphomeyaml/hassio/templates/index.html b/esphomeyaml/dashboard/templates/index.html similarity index 97% rename from esphomeyaml/hassio/templates/index.html rename to esphomeyaml/dashboard/templates/index.html index 45fb1966c4..a6516510cd 100644 --- a/esphomeyaml/hassio/templates/index.html +++ b/esphomeyaml/dashboard/templates/index.html @@ -547,7 +547,11 @@ const msg = data.data; log.innerHTML += colorReplace(msg); } else if (data.event === "exit") { - M.toast({html: `Program exited with code ${data.code}`}); + if (data.code === 0) { + M.toast({html: "Program exited successfully!"}); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } } }); logSocket.addEventListener('open', () => { @@ -615,7 +619,11 @@ const msg = data.data; log.innerHTML += colorReplace(msg); } else if (data.event === "exit") { - M.toast({html: `Program exited with code ${data.code}`}); + if (data.code === 0) { + M.toast({html: "Program exited successfully!"}); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } } }); logSocket.addEventListener('open', () => { @@ -665,9 +673,11 @@ const msg = data.data; log.innerHTML += colorReplace(msg); } else if (data.event === "exit") { - M.toast({html: `Program exited with code ${data.code}`}); if (data.code === 0) { + M.toast({html: "Program exited successfully!"}); downloadButton.classList.remove('disabled'); + } else { + M.toast({html: `Program failed with code ${data.code}`}); } } });