mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
CLI user experience improvements (#1805)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
0277218319
commit
33625e2dd3
9 changed files with 187 additions and 93 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -126,7 +126,7 @@ jobs:
|
|||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
- run: esphome compile tests/${{ matrix.test }}.yaml
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.github/workflows/release-dev.yml
vendored
2
.github/workflows/release-dev.yml
vendored
|
@ -123,7 +123,7 @@ jobs:
|
|||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
- run: esphome compile tests/${{ matrix.test }}.yaml
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -121,7 +121,7 @@ jobs:
|
|||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
- run: esphome compile tests/${{ matrix.test }}.yaml
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
|
@ -4,7 +4,7 @@
|
|||
{
|
||||
"label": "run",
|
||||
"type": "shell",
|
||||
"command": "python3 -m esphome config dashboard",
|
||||
"command": "python3 -m esphome dashboard config",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
|
|
|
@ -27,4 +27,4 @@ WORKDIR /config
|
|||
# in every docker command twice
|
||||
ENTRYPOINT ["esphome"]
|
||||
# When no arguments given, start the dashboard in the workdir
|
||||
CMD ["/config", "dashboard"]
|
||||
CMD ["dashboard", "/config"]
|
||||
|
|
|
@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
|
|||
fi
|
||||
|
||||
bashio::log.info "Starting ESPHome dashboard..."
|
||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
|
||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
||||
|
|
|
@ -268,7 +268,7 @@ def clean_mqtt(config, args):
|
|||
def command_wizard(args):
|
||||
from esphome import wizard
|
||||
|
||||
return wizard.wizard(args.configuration[0])
|
||||
return wizard.wizard(args.configuration)
|
||||
|
||||
|
||||
def command_config(args, config):
|
||||
|
@ -284,7 +284,7 @@ def command_vscode(args):
|
|||
|
||||
logging.disable(logging.INFO)
|
||||
logging.disable(logging.WARNING)
|
||||
CORE.config_path = args.configuration[0]
|
||||
CORE.config_path = args.configuration
|
||||
vscode.read_config(args)
|
||||
|
||||
|
||||
|
@ -304,7 +304,7 @@ def command_compile(args, config):
|
|||
|
||||
def command_upload(args, config):
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
|
@ -319,7 +319,7 @@ def command_upload(args, config):
|
|||
|
||||
def command_logs(args, config):
|
||||
port = choose_upload_log_host(
|
||||
default=args.serial_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
|
@ -337,7 +337,7 @@ def command_run(args, config):
|
|||
return exit_code
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
|
@ -350,7 +350,7 @@ def command_run(args, config):
|
|||
if args.no_logs:
|
||||
return 0
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=port,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
|
@ -394,7 +394,7 @@ def command_update_all(args):
|
|||
import click
|
||||
|
||||
success = {}
|
||||
files = list_yaml_files(args.configuration[0])
|
||||
files = list_yaml_files(args.configuration)
|
||||
twidth = 60
|
||||
|
||||
def print_bar(middle_text):
|
||||
|
@ -408,7 +408,7 @@ def command_update_all(args):
|
|||
print("-" * twidth)
|
||||
print()
|
||||
rc = run_external_process(
|
||||
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
|
||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||
)
|
||||
if rc == 0:
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||
|
@ -453,15 +453,17 @@ POST_CONFIG_ACTIONS = {
|
|||
|
||||
|
||||
def parse_args(argv):
|
||||
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose esphome logs.", action="store_true"
|
||||
options_parser = argparse.ArgumentParser(add_help=False)
|
||||
options_parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q", "--quiet", help="Disable all esphome logs.", action="store_true"
|
||||
options_parser.add_argument(
|
||||
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||
)
|
||||
parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true")
|
||||
parser.add_argument(
|
||||
options_parser.add_argument(
|
||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
options_parser.add_argument(
|
||||
"-s",
|
||||
"--substitution",
|
||||
nargs=2,
|
||||
|
@ -469,17 +471,87 @@ def parse_args(argv):
|
|||
help="Add a substitution",
|
||||
metavar=("key", "value"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"configuration", help="Your YAML configuration file.", nargs="*"
|
||||
|
||||
# Keep backward compatibility with the old command line format of
|
||||
# esphome <config> <command>.
|
||||
#
|
||||
# Unfortunately this can't be done by adding another configuration argument to the
|
||||
# main config parser, as argparse is greedy when parsing arguments, so in regular
|
||||
# usage it'll eat the command as the configuration argument and error out out
|
||||
# because it can't parse the configuration as a command.
|
||||
#
|
||||
# Instead, construct an ad-hoc parser for the old format that doesn't actually
|
||||
# process the arguments, but parses them enough to let us figure out if the old
|
||||
# format is used. In that case, swap the command and configuration in the arguments
|
||||
# and continue on with the normal parser (after raising a deprecation warning).
|
||||
#
|
||||
# Disable argparse's built-in help option and add it manually to prevent this
|
||||
# parser from printing the help messagefor the old format when invoked with -h.
|
||||
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
|
||||
compat_parser.add_argument("-h", "--help")
|
||||
compat_parser.add_argument("configuration", nargs="*")
|
||||
compat_parser.add_argument(
|
||||
"command",
|
||||
choices=[
|
||||
"config",
|
||||
"compile",
|
||||
"upload",
|
||||
"logs",
|
||||
"run",
|
||||
"clean-mqtt",
|
||||
"wizard",
|
||||
"mqtt-fingerprint",
|
||||
"version",
|
||||
"clean",
|
||||
"dashboard",
|
||||
],
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(help="Commands", dest="command")
|
||||
# on Python 3.9+ we can simply set exit_on_error=False in the constructor
|
||||
def _raise(x):
|
||||
raise argparse.ArgumentError(None, x)
|
||||
|
||||
compat_parser.error = _raise
|
||||
|
||||
try:
|
||||
result, unparsed = compat_parser.parse_known_args(argv[1:])
|
||||
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
|
||||
argv = argv[0:last_option] + [result.command] + result.configuration + unparsed
|
||||
deprecated_argv_suggestion = argv
|
||||
except argparse.ArgumentError:
|
||||
# This is not an old-style command line, so we don't have to do anything.
|
||||
deprecated_argv_suggestion = None
|
||||
|
||||
# And continue on with regular parsing
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"ESPHome v{const.__version__}", parents=[options_parser]
|
||||
)
|
||||
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
|
||||
|
||||
mqtt_options = argparse.ArgumentParser(add_help=False)
|
||||
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
|
||||
mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
|
||||
mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
|
||||
mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
help="Command to run:", dest="command", metavar="command"
|
||||
)
|
||||
subparsers.required = True
|
||||
subparsers.add_parser("config", help="Validate the configuration and spit it out.")
|
||||
|
||||
parser_config = subparsers.add_parser(
|
||||
"config", help="Validate the configuration and spit it out."
|
||||
)
|
||||
parser_config.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
parser_compile = subparsers.add_parser(
|
||||
"compile", help="Read the configuration and compile a program."
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--only-generate",
|
||||
help="Only generate source code, do not compile.",
|
||||
|
@ -487,106 +559,124 @@ def parse_args(argv):
|
|||
)
|
||||
|
||||
parser_upload = subparsers.add_parser(
|
||||
"upload", help="Validate the configuration " "and upload the latest binary."
|
||||
"upload", help="Validate the configuration and upload the latest binary."
|
||||
)
|
||||
parser_upload.add_argument(
|
||||
"--upload-port",
|
||||
help="Manually specify the upload port to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_upload.add_argument(
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
|
||||
parser_logs = subparsers.add_parser(
|
||||
"logs", help="Validate the configuration " "and show all MQTT logs."
|
||||
"logs",
|
||||
help="Validate the configuration and show all logs.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||
parser_logs.add_argument("--username", help="Manually set the username.")
|
||||
parser_logs.add_argument("--password", help="Manually set the password.")
|
||||
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.",
|
||||
"configuration", help="Your YAML configuration file.", nargs=1
|
||||
)
|
||||
parser_logs.add_argument(
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
|
||||
parser_run = subparsers.add_parser(
|
||||
"run",
|
||||
help="Validate the configuration, create a binary, "
|
||||
"upload it, and start MQTT logs.",
|
||||
help="Validate the configuration, create a binary, upload it, and start logs.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--upload-port",
|
||||
help="Manually specify the upload port/ip to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--no-logs", help="Disable starting MQTT logs.", action="store_true"
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--topic", help="Manually set the topic to subscribe to for logs."
|
||||
"--no-logs", help="Disable starting logs.", action="store_true"
|
||||
)
|
||||
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_clean = subparsers.add_parser(
|
||||
"clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages."
|
||||
"clean-mqtt",
|
||||
help="Helper to clear retained messages from an MQTT topic.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_clean.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||
parser_clean.add_argument("--username", help="Manually set the username.")
|
||||
parser_clean.add_argument("--password", help="Manually set the password.")
|
||||
parser_clean.add_argument("--client-id", help="Manually set the client id.")
|
||||
|
||||
subparsers.add_parser(
|
||||
parser_wizard = subparsers.add_parser(
|
||||
"wizard",
|
||||
help="A helpful setup wizard that will guide "
|
||||
"you through setting up esphome.",
|
||||
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
||||
)
|
||||
parser_wizard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file.",
|
||||
)
|
||||
|
||||
subparsers.add_parser(
|
||||
parser_fingerprint = subparsers.add_parser(
|
||||
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||
)
|
||||
parser_fingerprint.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
subparsers.add_parser("version", help="Print the esphome version and exit.")
|
||||
subparsers.add_parser("version", help="Print the ESPHome version and exit.")
|
||||
|
||||
subparsers.add_parser("clean", help="Delete all temporary build files.")
|
||||
parser_clean = subparsers.add_parser(
|
||||
"clean", help="Delete all temporary build files."
|
||||
)
|
||||
parser_clean.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
dashboard = subparsers.add_parser(
|
||||
parser_dashboard = subparsers.add_parser(
|
||||
"dashboard", help="Create a simple web server for a dashboard."
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file directory.",
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--port",
|
||||
help="The HTTP port to open connections on. Defaults to 6052.",
|
||||
type=int,
|
||||
default=6052,
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--username",
|
||||
help="The optional username to require " "for authentication.",
|
||||
help="The optional username to require for authentication.",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--password",
|
||||
help="The optional password to require " "for authentication.",
|
||||
help="The optional password to require for authentication.",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
|
||||
)
|
||||
dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true")
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--hassio", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--socket", help="Make the dashboard serve under a unix socket", type=str
|
||||
)
|
||||
|
||||
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
|
||||
vscode.add_argument("--ace", action="store_true")
|
||||
parser_vscode = subparsers.add_parser("vscode")
|
||||
parser_vscode.add_argument(
|
||||
"configuration", help="Your YAML configuration file.", nargs=1
|
||||
)
|
||||
parser_vscode.add_argument("--ace", action="store_true")
|
||||
|
||||
subparsers.add_parser("update-all", help=argparse.SUPPRESS)
|
||||
parser_update = subparsers.add_parser("update-all")
|
||||
parser_update.add_argument(
|
||||
"configuration", help="Your YAML configuration file directory.", nargs=1
|
||||
)
|
||||
|
||||
return parser.parse_args(argv[1:])
|
||||
|
||||
|
@ -596,9 +686,13 @@ def run_esphome(argv):
|
|||
CORE.dashboard = args.dashboard
|
||||
|
||||
setup_log(args.verbose, args.quiet)
|
||||
if args.command != "version" and not args.configuration:
|
||||
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
||||
return 1
|
||||
if args.deprecated_argv_suggestion is not None:
|
||||
_LOGGER.warning(
|
||||
"Calling ESPHome with the configuration before the command is deprecated "
|
||||
"and will be removed in the future. "
|
||||
)
|
||||
_LOGGER.warning("Please instead use:")
|
||||
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
|
||||
|
||||
if sys.version_info < (3, 7, 0):
|
||||
_LOGGER.error(
|
||||
|
|
|
@ -62,7 +62,7 @@ class DashboardSettings:
|
|||
self.using_password = bool(password)
|
||||
if self.using_password:
|
||||
self.password_hash = password_hash(password)
|
||||
self.config_dir = args.configuration[0]
|
||||
self.config_dir = args.configuration
|
||||
|
||||
@property
|
||||
def relative_url(self):
|
||||
|
@ -274,9 +274,9 @@ class EsphomeLogsHandler(EsphomeCommandWebSocket):
|
|||
return [
|
||||
"esphome",
|
||||
"--dashboard",
|
||||
config_file,
|
||||
"logs",
|
||||
"--serial-port",
|
||||
config_file,
|
||||
"--device",
|
||||
json_message["port"],
|
||||
]
|
||||
|
||||
|
@ -287,9 +287,9 @@ class EsphomeUploadHandler(EsphomeCommandWebSocket):
|
|||
return [
|
||||
"esphome",
|
||||
"--dashboard",
|
||||
config_file,
|
||||
"run",
|
||||
"--upload-port",
|
||||
config_file,
|
||||
"--device",
|
||||
json_message["port"],
|
||||
]
|
||||
|
||||
|
@ -297,40 +297,40 @@ class EsphomeUploadHandler(EsphomeCommandWebSocket):
|
|||
class EsphomeCompileHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "compile"]
|
||||
return ["esphome", "--dashboard", "compile", config_file]
|
||||
|
||||
|
||||
class EsphomeValidateHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "config"]
|
||||
return ["esphome", "--dashboard", "config", config_file]
|
||||
|
||||
|
||||
class EsphomeCleanMqttHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "clean-mqtt"]
|
||||
return ["esphome", "--dashboard", "clean-mqtt", config_file]
|
||||
|
||||
|
||||
class EsphomeCleanHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
config_file = settings.rel_path(json_message["configuration"])
|
||||
return ["esphome", "--dashboard", config_file, "clean"]
|
||||
return ["esphome", "--dashboard", "clean", config_file]
|
||||
|
||||
|
||||
class EsphomeVscodeHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
return ["esphome", "--dashboard", "-q", "dummy", "vscode"]
|
||||
return ["esphome", "--dashboard", "-q", "vscode", "dummy"]
|
||||
|
||||
|
||||
class EsphomeAceEditorHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
return ["esphome", "--dashboard", "-q", settings.config_dir, "vscode", "--ace"]
|
||||
return ["esphome", "--dashboard", "-q", "vscode", settings.config_dir, "--ace"]
|
||||
|
||||
|
||||
class EsphomeUpdateAllHandler(EsphomeCommandWebSocket):
|
||||
def build_command(self, json_message):
|
||||
return ["esphome", "--dashboard", settings.config_dir, "update-all"]
|
||||
return ["esphome", "--dashboard", "update-all", settings.config_dir]
|
||||
|
||||
|
||||
class SerialPortRequestHandler(BaseHandler):
|
||||
|
|
|
@ -6,7 +6,7 @@ cd "$(dirname "$0")/.."
|
|||
|
||||
set -x
|
||||
|
||||
esphome tests/test1.yaml compile
|
||||
esphome tests/test2.yaml compile
|
||||
esphome tests/test3.yaml compile
|
||||
esphome tests/test4.yaml compile
|
||||
esphome compile tests/test1.yaml
|
||||
esphome compile tests/test2.yaml
|
||||
esphome compile tests/test3.yaml
|
||||
esphome compile tests/test4.yaml
|
||||
|
|
Loading…
Reference in a new issue