mirror of
https://github.com/esphome/esphome.git
synced 2024-11-29 12:55:46 +01:00
Add "esphome rename" command (#3403)
* Add "esphome rename" command * Only open file once * Update esphome/__main__.py Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io> * Add final return * Use match.group consistently * Validate name characters * Add whitespace to regex so it is only replacing exact match * Validate yaml config file after manipulation Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
7a778f3f33
commit
757b98748b
@ -2,6 +2,7 @@ import argparse
|
|||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -9,15 +10,18 @@ from esphome import const, writer, yaml_util
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.config import iter_components, read_config, strip_default_ids
|
from esphome.config import iter_components, read_config, strip_default_ids
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
ALLOWED_NAME_CHARS,
|
||||||
CONF_BAUD_RATE,
|
CONF_BAUD_RATE,
|
||||||
CONF_BROKER,
|
CONF_BROKER,
|
||||||
CONF_DEASSERT_RTS_DTR,
|
CONF_DEASSERT_RTS_DTR,
|
||||||
CONF_LOGGER,
|
CONF_LOGGER,
|
||||||
|
CONF_NAME,
|
||||||
CONF_OTA,
|
CONF_OTA,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_ESPHOME,
|
CONF_ESPHOME,
|
||||||
CONF_PLATFORMIO_OPTIONS,
|
CONF_PLATFORMIO_OPTIONS,
|
||||||
|
CONF_SUBSTITUTIONS,
|
||||||
SECRETS_FILES,
|
SECRETS_FILES,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine
|
from esphome.core import CORE, EsphomeError, coroutine
|
||||||
@ -481,6 +485,96 @@ def command_idedata(args, config):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def command_rename(args, config):
|
||||||
|
for c in args.name:
|
||||||
|
if c not in ALLOWED_NAME_CHARS:
|
||||||
|
print(
|
||||||
|
color(
|
||||||
|
Fore.BOLD_RED,
|
||||||
|
f"'{c}' is an invalid character for names. Valid characters are: "
|
||||||
|
f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
with open(CORE.config_path, mode="r+", encoding="utf-8") as raw_file:
|
||||||
|
raw_contents = raw_file.read()
|
||||||
|
yaml = yaml_util.load_yaml(CORE.config_path)
|
||||||
|
if CONF_ESPHOME not in yaml or CONF_NAME not in yaml[CONF_ESPHOME]:
|
||||||
|
print(
|
||||||
|
color(
|
||||||
|
Fore.BOLD_RED, "Complex YAML files cannot be automatically renamed."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
old_name = yaml[CONF_ESPHOME][CONF_NAME]
|
||||||
|
match = re.match(r"^\$\{?([a-zA-Z0-9_]+)\}?$", old_name)
|
||||||
|
if match is None:
|
||||||
|
new_raw = re.sub(
|
||||||
|
rf"name:\s+[\"']?{old_name}[\"']?",
|
||||||
|
f'name: "{args.name}"',
|
||||||
|
raw_contents,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
old_name = yaml[CONF_SUBSTITUTIONS][match.group(1)]
|
||||||
|
if (
|
||||||
|
len(
|
||||||
|
re.findall(
|
||||||
|
rf"^\s+{match.group(1)}:\s+[\"']?{old_name}[\"']?",
|
||||||
|
raw_contents,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
> 1
|
||||||
|
):
|
||||||
|
print(color(Fore.BOLD_RED, "Too many matches in YAML to safely rename"))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
new_raw = re.sub(
|
||||||
|
rf"^(\s+{match.group(1)}):\s+[\"']?{old_name}[\"']?",
|
||||||
|
f'\\1: "{args.name}"',
|
||||||
|
raw_contents,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_file.seek(0)
|
||||||
|
raw_file.write(new_raw)
|
||||||
|
raw_file.flush()
|
||||||
|
|
||||||
|
print(f"Updating {color(Fore.CYAN, CORE.config_path)}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
rc = run_external_process("esphome", "config", CORE.config_path)
|
||||||
|
if rc != 0:
|
||||||
|
raw_file.seek(0)
|
||||||
|
raw_file.write(raw_contents)
|
||||||
|
print(color(Fore.BOLD_RED, "Rename failed. Reverting changes."))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
cli_args = [
|
||||||
|
"run",
|
||||||
|
CORE.config_path,
|
||||||
|
"--no-logs",
|
||||||
|
"--device",
|
||||||
|
CORE.address,
|
||||||
|
]
|
||||||
|
|
||||||
|
if args.dashboard:
|
||||||
|
cli_args.insert(0, "--dashboard")
|
||||||
|
|
||||||
|
try:
|
||||||
|
rc = run_external_process("esphome", *cli_args)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
rc = 1
|
||||||
|
if rc != 0:
|
||||||
|
raw_file.seek(0)
|
||||||
|
raw_file.write(raw_contents)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(color(Fore.BOLD_GREEN, "SUCCESS"))
|
||||||
|
print()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
PRE_CONFIG_ACTIONS = {
|
PRE_CONFIG_ACTIONS = {
|
||||||
"wizard": command_wizard,
|
"wizard": command_wizard,
|
||||||
"version": command_version,
|
"version": command_version,
|
||||||
@ -499,6 +593,7 @@ POST_CONFIG_ACTIONS = {
|
|||||||
"mqtt-fingerprint": command_mqtt_fingerprint,
|
"mqtt-fingerprint": command_mqtt_fingerprint,
|
||||||
"clean": command_clean,
|
"clean": command_clean,
|
||||||
"idedata": command_idedata,
|
"idedata": command_idedata,
|
||||||
|
"rename": command_rename,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -681,6 +776,12 @@ def parse_args(argv):
|
|||||||
"configuration", help="Your YAML configuration file(s).", nargs=1
|
"configuration", help="Your YAML configuration file(s).", nargs=1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser_rename = subparsers.add_parser("rename")
|
||||||
|
parser_rename.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file.", nargs=1
|
||||||
|
)
|
||||||
|
parser_rename.add_argument("name", help="The new name for the device.", type=str)
|
||||||
|
|
||||||
# Keep backward compatibility with the old command line format of
|
# Keep backward compatibility with the old command line format of
|
||||||
# esphome <config> <command>.
|
# esphome <config> <command>.
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user