HassIO -> dashboard

This commit is contained in:
Otto Winter 2018-05-27 14:15:24 +02:00
parent 2e7d8540fb
commit 93d962dd43
No known key found for this signature in database
GPG Key ID: DB66C0BE6013F97E
15 changed files with 79 additions and 57 deletions

View File

@ -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

View File

@ -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,8 +398,9 @@ 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,
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:])

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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')

View File

@ -2,9 +2,9 @@ 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, \
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
@ -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:

View File

@ -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,

View File

@ -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)

View File

@ -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'

View File

@ -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)

View File

@ -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}`});
}
}
});