def do_substitution_pass(config): if CONF_SUBSTITUTIONS not in config: return config substitutions = config[CONF_SUBSTITUTIONS] if not isinstance(substitutions, dict): raise EsphomeyamlError( u"Substitutions must be a key to value mapping, got {}" u"".format(type(substitutions))) key = '' try: replace_keys = [] for key, value in substitutions.items(): sub = validate_substitution_key(key) if sub != key: replace_keys.append((key, sub)) substitutions[key] = cv.string_strict(value) for old, new in replace_keys: substitutions[new] = substitutions[old] del substitutions[old] except vol.Invalid as err: err.path.append(key) raise EsphomeyamlError( u"Error while parsing substitutions: {}".format(err)) config[CONF_SUBSTITUTIONS] = substitutions _substitute_item(substitutions, config, []) return config
def load_yaml(fname): """Load a YAML file.""" try: with codecs.open(fname, encoding='utf-8') as conf_file: return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict() except yaml.YAMLError as exc: raise EsphomeyamlError(exc) except IOError as exc: raise EsphomeyamlError(u"Error accessing file {}: {}".format( fname, exc)) except UnicodeDecodeError as exc: _LOGGER.error(u"Unable to read file %s: %s", fname, exc) raise EsphomeyamlError(exc)
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 EsphomeyamlError(u"The local log level {} for {} must be less severe than the " u"global log level {}.".format(level, tag, global_level)) return value
def write_cpp(code_s): path = CORE.relative_build_path('src', 'main.cpp') if os.path.isfile(path): try: with codecs.open(path, 'r', encoding='utf-8') as f_handle: text = f_handle.read() except OSError: raise EsphomeyamlError(u"Could not read C++ file at {}".format(path)) prev_file = text code_format = find_begin_end(text, CPP_AUTO_GENERATE_BEGIN, CPP_AUTO_GENERATE_END) code_format_ = find_begin_end(code_format[0], CPP_INCLUDE_BEGIN, CPP_INCLUDE_END) code_format = (code_format_[0], code_format_[1], code_format[1]) else: prev_file = None mkdir_p(os.path.dirname(path)) code_format = CPP_BASE_FORMAT include_s = get_include_text() full_file = code_format[0] + CPP_INCLUDE_BEGIN + u'\n' + include_s + CPP_INCLUDE_END full_file += code_format[1] + CPP_AUTO_GENERATE_BEGIN + u'\n' + code_s + CPP_AUTO_GENERATE_END full_file += code_format[2] if prev_file == full_file: return with codecs.open(path, 'w+', encoding='utf-8') as f_handle: f_handle.write(full_file)
def __init__(self, config): if 'mqtt' not in config: raise EsphomeyamlError( "Cannot generate Home Assistant MQTT config if MQTT is not " "used!") mqtt = config[CONF_MQTT] self.topic_prefix = mqtt.get(CONF_TOPIC_PREFIX, config[CONF_ESPHOMEYAML][CONF_NAME]) birth_message = mqtt.get(CONF_BIRTH_MESSAGE) if CONF_BIRTH_MESSAGE not in mqtt: birth_message = { CONF_TOPIC: self.topic_prefix + '/status', CONF_PAYLOAD: 'online', } will_message = mqtt.get(CONF_WILL_MESSAGE) if CONF_WILL_MESSAGE not in mqtt: will_message = { CONF_TOPIC: self.topic_prefix + '/status', CONF_PAYLOAD: 'offline' } if not birth_message or not will_message: self.availability = None elif birth_message[CONF_TOPIC] != will_message[CONF_TOPIC]: self.availability = None else: self.availability = { CONF_TOPIC: birth_message[CONF_TOPIC], CONF_PAYLOAD_AVAILABLE: birth_message[CONF_PAYLOAD], CONF_PAYLOAD_NOT_AVAILABLE: will_message[CONF_PAYLOAD], }
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 esphomeyaml.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 EsphomeyamlError(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 _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(secret_path) if node.value not in secrets: raise EsphomeyamlError(u"Secret {} not defined".format(node.value)) return secrets[node.value]
def _ordered_dict(loader, node): """Load YAML mappings into an ordered dictionary to preserve key order.""" custom_flatten_mapping(loader, node) nodes = custom_construct_pairs(loader, node) seen = {} for (key, _), nv in zip(nodes, node.value): if isinstance(nv, yaml.ScalarNode): line = nv.start_mark.line else: line = nv[0].start_mark.line try: hash(key) except TypeError: fname = getattr(loader.stream, 'name', '') raise yaml.MarkedYAMLError( context="invalid key: \"{}\"".format(key), context_mark=yaml.Mark(fname, 0, line, -1, None, None)) if key in seen: fname = getattr(loader.stream, 'name', '') raise EsphomeyamlError( u'YAML file {} contains duplicate key "{}". ' u'Check lines {} and {}.'.format(fname, key, seen[key], line)) seen[key] = line return _add_reference(OrderedDict(nodes), loader, node)
def find_begin_end(text, begin_s, end_s): begin_index = text.find(begin_s) if begin_index == -1: raise EsphomeyamlError(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 EsphomeyamlError(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 EsphomeyamlError(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 EsphomeyamlError(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 resolve_ip_address(host): try: ip = socket.gethostbyname(host) except socket.error as err: from esphomeyaml.core import EsphomeyamlError raise EsphomeyamlError("Error resolving IP address: {}".format(err)) return ip
def preload_core_config(config): if CONF_ESPHOMEYAML not in config: raise EsphomeyamlError(u"No esphomeyaml section in config") core_conf = config[CONF_ESPHOMEYAML] if CONF_PLATFORM not in core_conf: raise EsphomeyamlError("esphomeyaml.platform not specified.") if CONF_BOARD not in core_conf: raise EsphomeyamlError("esphomeyaml.board not specified.") if CONF_NAME not in core_conf: raise EsphomeyamlError("esphomeyaml.name not specified.") try: CORE.esp_platform = validate_platform(core_conf[CONF_PLATFORM]) CORE.board = validate_board(core_conf[CONF_BOARD]) CORE.name = cv.valid_name(core_conf[CONF_NAME]) CORE.build_path = CORE.relative_path( cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path()))) except vol.Invalid as e: raise EsphomeyamlError(text_type(e))
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 EsphomeyamlError(u"Environment variable {} not defined.".format( node.value))
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 EsphomeyamlError("Cannot connect to MQTT broker: {}".format(err)) try: client.loop_forever() except KeyboardInterrupt: pass return 0
def show_logs(config, args, port): if 'logger' not in config: raise EsphomeyamlError("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 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 EsphomeyamlError( "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 load_config(): try: config = yaml_util.load_yaml(CORE.config_path) except OSError: raise EsphomeyamlError(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 EsphomeyamlError: raise except Exception: _LOGGER.error(u"Unexpected exception while reading configuration:") raise return result
def write_platformio_ini(content, path): symlink_esphomelib_version(CORE.esphomelib_version) update_esphomelib_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 EsphomeyamlError(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)