def initialize(config, subscriptions, on_message, username, password, client_id): def on_connect(client, userdata, flags, return_code): for topic in subscriptions: client.subscribe(topic) def on_disconnect(client, userdata, result_code): if result_code == 0: return tries = 0 while True: try: if client.reconnect() == 0: _LOGGER.info("Successfully reconnected to the MQTT server") break except socket.error: pass wait_time = min(2**tries, 300) _LOGGER.warning( "Disconnected from MQTT (%s). Trying to reconnect in %s s", result_code, wait_time) time.sleep(wait_time) tries += 1 client = mqtt.Client(client_id or u'') client.on_connect = on_connect client.on_message = on_message client.on_disconnect = on_disconnect if username is None: if config[CONF_MQTT].get(CONF_USERNAME): client.username_pw_set(config[CONF_MQTT][CONF_USERNAME], config[CONF_MQTT][CONF_PASSWORD]) elif username: client.username_pw_set(username, password) if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS): if sys.version_info >= (2, 7, 13): tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member else: tls_version = ssl.PROTOCOL_SSLv23 client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=tls_version, ciphers=None) try: client.connect(config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT]) except socket.error as err: raise EsphomeError("Cannot connect to MQTT broker: {}".format(err)) try: client.loop_forever() except KeyboardInterrupt: pass return 0
def choose_prompt(options): if not options: raise EsphomeError( "Found no valid options for upload/logging, please make sure relevant " "sections (ota, api, mqtt, ...) are in your configuration and/or the " "device is plugged in.") if len(options) == 1: return options[0][1] safe_print("Found multiple options, please choose one:") for i, (desc, _) in enumerate(options): safe_print(f" [{i+1}] {desc}") while True: opt = input("(number): ") if opt in options: opt = options.index(opt) break try: opt = int(opt) if opt < 1 or opt > len(options): raise ValueError break except ValueError: safe_print(color(Fore.RED, f"Invalid option: '{opt}'")) return options[opt - 1][1]
def write_file(path: Union[Path, str], text: str): try: _write_file(path, text) except OSError as err: from esphome.core import EsphomeError raise EsphomeError(f"Could not write file at {path}") from err
def show_logs(config, args, port): if 'logger' not in config: raise EsphomeError("Logger is not configured!") if get_port_type(port) == 'SERIAL': run_miniterm(config, port) return 0 if get_port_type(port) == 'NETWORK' and 'api' in config: from esphome.api.client import run_logs return run_logs(config, port) if get_port_type(port) == 'MQTT' and 'mqtt' in config: from esphome import mqtt return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
def validate_local_no_higher_than_global(value): global_level = value.get(CONF_LEVEL, "DEBUG") for tag, level in value.get(CONF_LOGS, {}).items(): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): raise EsphomeError( f"The local log level {level} for {tag} must be less severe than the global log level {global_level}." ) return value
def find_begin_end(text, begin_s, end_s): begin_index = text.find(begin_s) if begin_index == -1: raise EsphomeError(u"Could not find auto generated code begin in file, either " u"delete the main sketch file or insert the comment again.") if text.find(begin_s, begin_index + 1) != -1: raise EsphomeError(u"Found multiple auto generate code begins, don't know " u"which to chose, please remove one of them.") end_index = text.find(end_s) if end_index == -1: raise EsphomeError(u"Could not find auto generated code end in file, either " u"delete the main sketch file or insert the comment again.") if text.find(end_s, end_index + 1) != -1: raise EsphomeError(u"Found multiple auto generate code endings, don't know " u"which to chose, please remove one of them.") return text[:begin_index], text[(end_index + len(end_s)):]
def write_file(path, text): try: mkdir_p(os.path.dirname(path)) with codecs.open(path, 'w+', encoding='utf-8') as f_handle: f_handle.write(text) except OSError: from esphome.core import EsphomeError raise EsphomeError(u"Could not write file at {}".format(path))
def show_logs(config, args, port): if "logger" not in config: raise EsphomeError("Logger is not configured!") if get_port_type(port) == "SERIAL": run_miniterm(config, port) return 0 if get_port_type(port) == "NETWORK" and "api" in config: from esphome.api.client import run_logs return run_logs(config, port) if get_port_type(port) == "MQTT" and "mqtt" in config: from esphome import mqtt return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) raise EsphomeError( "No remote or local logging method configured (api/mqtt/logger)")
def _secret_yaml(loader, node): """Load secrets and embed it into the configuration YAML.""" secret_path = os.path.join(os.path.dirname(loader.name), SECRET_YAML) secrets = _load_yaml_internal(secret_path) if node.value not in secrets: raise EsphomeError(u"Secret {} not defined".format(node.value)) val = secrets[node.value] _SECRET_VALUES[text_type(val)] = node.value return val
def validate_local_no_higher_than_global(value): global_level = value.get(CONF_LEVEL, 'DEBUG') for tag, level in value.get(CONF_LOGS, {}).items(): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index( global_level): raise EsphomeError( u"The local log level {} for {} must be less severe than the " u"global log level {}.".format(level, tag, global_level)) return value
def copy_file_if_changed(src, dst): import shutil if file_compare(src, dst): return mkdir_p(os.path.dirname(dst)) try: shutil.copy(src, dst) except OSError as err: from esphome.core import EsphomeError raise EsphomeError(f"Error copying file {src} to {dst}: {err}")
def _resolve_with_zeroconf(host): from esphome.core import EsphomeError try: zc = Zeroconf() except Exception: raise EsphomeError( "Cannot start mDNS sockets, is this a docker container without " "host network mode?") try: info = zc.resolve_host(host + '.') except Exception as err: raise EsphomeError("Error resolving mDNS hostname: {}".format(err)) finally: zc.close() if info is None: raise EsphomeError( "Error resolving address with mDNS: Did not respond. " "Maybe the device is offline.") return info
def _env_var_yaml(_, node): """Load environment variables and embed it into the configuration YAML.""" args = node.value.split() # Check for a default value if len(args) > 1: return os.getenv(args[0], u' '.join(args[1:])) if args[0] in os.environ: return os.environ[args[0]] raise EsphomeError(u"Environment variable {} not defined.".format(node.value))
def read_config_file(path): # type: (basestring) -> unicode if CORE.vscode and (not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)): print(json.dumps({ 'type': 'read_file', 'path': path, })) data = json.loads(safe_input()) assert data['type'] == 'file_response' return data['content'] try: with codecs.open(path, encoding='utf-8') as handle: return handle.read() except IOError as exc: raise EsphomeError(u"Error accessing file {}: {}".format(path, exc)) except UnicodeDecodeError as exc: raise EsphomeError(u"Unable to read file {}: {}".format(path, exc))
def _load_yaml_internal(fname): content = read_config_file(fname) loader = ESPHomeLoader(content) loader.name = fname try: return loader.get_single_data() or OrderedDict() except yaml.YAMLError as exc: raise EsphomeError(exc) from exc finally: loader.dispose()
def mkdir_p(path): try: os.makedirs(path) except OSError as err: import errno if err.errno == errno.EEXIST and os.path.isdir(path): pass else: from esphome.core import EsphomeError raise EsphomeError(u"Error creating directories {}: {}".format( path, err))
def resolve_ip_address(host): from esphome.core import EsphomeError try: ip = socket.gethostbyname(host) except socket.error as err: if host.endswith('.local'): ip = _resolve_with_zeroconf(host) else: raise EsphomeError("Error resolving IP address: {}".format(err)) return ip
def generic_gpio_pin_expression_(conf, mock_obj, default_mode): if conf is None: return number = conf[CONF_NUMBER] inverted = conf.get(CONF_INVERTED) if CONF_PCF8574 in conf: from esphome.components import pcf8574 for hub in CORE.get_variable(conf[CONF_PCF8574]): yield None if default_mode == u'INPUT': mode = pcf8574.PCF8675_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')] yield hub.make_input_pin(number, mode, inverted) return if default_mode == u'OUTPUT': yield hub.make_output_pin(number, inverted) return raise EsphomeError(u"Unknown default mode {}".format(default_mode)) if CONF_MCP23017 in conf: from esphome.components import mcp23017 for hub in CORE.get_variable(conf[CONF_MCP23017]): yield None if default_mode == u'INPUT': mode = mcp23017.MCP23017_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')] yield hub.make_input_pin(number, mode, inverted) return if default_mode == u'OUTPUT': yield hub.make_output_pin(number, inverted) return raise EsphomeError(u"Unknown default mode {}".format(default_mode)) if len(conf) == 1: yield IntLiteral(number) return mode = RawExpression(conf.get(CONF_MODE, default_mode)) yield mock_obj(number, mode, inverted)
def symlink(src, dst): csl = ctypes.windll.kernel32.CreateSymbolicLinkW csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32) csl.restype = ctypes.c_ubyte flags = 1 if os.path.isdir(src) else 0 if csl(dst, src, flags) == 0: error = ctypes.WinError() # pylint: disable=no-member if error.winerror == 1314 and error.errno == 22: from esphome.core import EsphomeError raise EsphomeError("Cannot create symlink from '%s' to '%s'. Try running tool \ with elevated privileges" % (src, dst)) raise error
def show_logs(config, args, port): if 'logger' not in config: raise EsphomeError("Logger is not configured!") if get_port_type(port) == 'SERIAL': run_miniterm(config, port) return 0 if get_port_type(port) == 'NETWORK' and 'api' in config: return run_logs(config, port) if get_port_type(port) == 'MQTT' and 'mqtt' in config: return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) raise ValueError
def mkdir_p(path): if not path: # Empty path - means create current dir return try: os.makedirs(path) except OSError as err: import errno if err.errno == errno.EEXIST and os.path.isdir(path): pass else: from esphome.core import EsphomeError raise EsphomeError(f"Error creating directories {path}: {err}")
def upload_program(config, args, host): # if upload is to a serial port use platformio, otherwise assume ota if get_port_type(host) == "SERIAL": return upload_using_esptool(config, host) from esphome import espota2 if CONF_OTA not in config: raise EsphomeError( "Cannot upload Over the Air as the config does not include the ota: " "component") ota_conf = config[CONF_OTA] remote_port = ota_conf[CONF_PORT] password = ota_conf.get(CONF_PASSWORD, "") return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
def custom_construct_pairs(loader, node): pairs = [] for kv in node.value: if isinstance(kv, yaml.ScalarNode): obj = loader.construct_object(kv) if not isinstance(obj, dict): raise EsphomeError( "Expected mapping for anchored include tag, got {}".format(type(obj))) for key, value in obj.items(): pairs.append((key, value)) else: key_node, value_node = kv key = loader.construct_object(key_node) value = loader.construct_object(value_node) pairs.append((key, value)) return pairs
def resolve_ip_address(host): from esphome.core import EsphomeError import socket errs = [] if host.endswith(".local"): try: return _resolve_with_zeroconf(host) except EsphomeError as err: errs.append(str(err)) try: return socket.gethostbyname(host) except OSError as err: errs.append(str(err)) raise EsphomeError(f"Error resolving IP address: {', '.join(errs)}") from err
def load_config(): try: config = yaml_util.load_yaml(CORE.config_path) except OSError: raise EsphomeError(u"Invalid YAML at {}".format(CORE.config_path)) CORE.raw_config = config config = substitutions.do_substitution_pass(config) core_config.preload_core_config(config) try: result = validate_config(config) except EsphomeError: raise except Exception: _LOGGER.error(u"Unexpected exception while reading configuration:") raise return result
def write_platformio_ini(content, path): symlink_esphome_core_version(CORE.esphome_core_version) update_esphome_core_repo() update_storage_json() if os.path.isfile(path): try: with codecs.open(path, 'r', encoding='utf-8') as f_handle: text = f_handle.read() except OSError: raise EsphomeError(u"Could not read ini file at {}".format(path)) prev_file = text content_format = find_begin_end(text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END) else: prev_file = None content_format = INI_BASE_FORMAT full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + content full_file += INI_AUTO_GENERATE_END + content_format[1] if prev_file == full_file: return with codecs.open(path, mode='w+', encoding='utf-8') as f_handle: f_handle.write(full_file)
def load_config(): try: return _load_config() except vol.Invalid as err: raise EsphomeError("Error while parsing config: {}".format(err))
def load_config(command_line_substitutions): try: return _load_config(command_line_substitutions) except vol.Invalid as err: raise EsphomeError(f"Error while parsing config: {err}") from err
def write_file(path, text): try: _write_file(path, text) except OSError: from esphome.core import EsphomeError raise EsphomeError(f"Could not write file at {path}")
def write_file(path, text): try: _write_file(path, text) except OSError: from esphome.core import EsphomeError raise EsphomeError(u"Could not write file at {}".format(path))