2018-04-07 01:23:03 +02:00
|
|
|
from __future__ import print_function
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
import codecs
|
2018-04-07 01:23:03 +02:00
|
|
|
import logging
|
2018-09-23 18:58:41 +02:00
|
|
|
import os
|
2018-04-07 01:23:03 +02:00
|
|
|
|
2019-02-13 16:54:02 +01:00
|
|
|
from esphome.py_compat import char_to_byte, text_type
|
2019-01-02 14:11:11 +01:00
|
|
|
|
2018-04-07 01:23:03 +02:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_unique_string(preferred_string, current_strings):
|
|
|
|
test_string = preferred_string
|
|
|
|
current_strings_set = set(current_strings)
|
|
|
|
|
|
|
|
tries = 1
|
|
|
|
|
|
|
|
while test_string in current_strings_set:
|
|
|
|
tries += 1
|
|
|
|
test_string = u"{}_{}".format(preferred_string, tries)
|
|
|
|
|
|
|
|
return test_string
|
|
|
|
|
|
|
|
|
|
|
|
def indent_all_but_first_and_last(text, padding=u' '):
|
|
|
|
lines = text.splitlines(True)
|
|
|
|
if len(lines) <= 2:
|
|
|
|
return text
|
|
|
|
return lines[0] + u''.join(padding + line for line in lines[1:-1]) + lines[-1]
|
|
|
|
|
|
|
|
|
|
|
|
def indent_list(text, padding=u' '):
|
|
|
|
return [padding + line for line in text.splitlines()]
|
|
|
|
|
|
|
|
|
|
|
|
def indent(text, padding=u' '):
|
|
|
|
return u'\n'.join(indent_list(text, padding))
|
|
|
|
|
|
|
|
|
2018-05-15 11:09:27 +02:00
|
|
|
# From https://stackoverflow.com/a/14945195/8924614
|
2018-05-17 19:57:55 +02:00
|
|
|
def cpp_string_escape(string, encoding='utf-8'):
|
2019-01-05 14:24:15 +01:00
|
|
|
def _should_escape(byte): # type: (int) -> bool
|
|
|
|
if not 32 <= byte < 127:
|
|
|
|
return True
|
|
|
|
if byte in (char_to_byte('\\'), char_to_byte('"')):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2019-01-02 14:11:11 +01:00
|
|
|
if isinstance(string, text_type):
|
2018-05-17 19:57:55 +02:00
|
|
|
string = string.encode(encoding)
|
2018-05-15 11:09:27 +02:00
|
|
|
result = ''
|
2018-05-17 19:57:55 +02:00
|
|
|
for character in string:
|
2019-01-05 14:24:15 +01:00
|
|
|
character = char_to_byte(character)
|
|
|
|
if _should_escape(character):
|
2019-01-02 14:11:11 +01:00
|
|
|
result += '\\%03o' % character
|
2018-05-15 11:09:27 +02:00
|
|
|
else:
|
2019-01-02 14:11:11 +01:00
|
|
|
result += chr(character)
|
2018-05-15 11:09:27 +02:00
|
|
|
return '"' + result + '"'
|
|
|
|
|
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
def color(the_color, message=''):
|
|
|
|
from colorlog.escape_codes import escape_codes, parse_colors
|
2018-06-02 22:22:20 +02:00
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
if not message:
|
|
|
|
res = parse_colors(the_color)
|
2018-06-02 22:22:20 +02:00
|
|
|
else:
|
2018-12-05 21:22:06 +01:00
|
|
|
res = parse_colors(the_color) + message + escape_codes['reset']
|
2018-04-07 01:23:03 +02:00
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
return res
|
2018-04-07 01:23:03 +02:00
|
|
|
|
2018-04-18 18:43:13 +02:00
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
def run_system_command(*args):
|
2019-04-08 22:19:21 +02:00
|
|
|
import subprocess
|
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
stdout, stderr = p.communicate()
|
|
|
|
rc = p.returncode
|
|
|
|
return rc, stdout, stderr
|
2018-04-07 01:23:03 +02:00
|
|
|
|
2018-05-20 12:41:52 +02:00
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
def mkdir_p(path):
|
2019-04-08 22:19:21 +02:00
|
|
|
import errno
|
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
try:
|
|
|
|
os.makedirs(path)
|
|
|
|
except OSError as exc:
|
|
|
|
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
|
|
|
pass
|
2018-06-06 08:35:23 +02:00
|
|
|
else:
|
2018-12-05 21:22:06 +01:00
|
|
|
raise
|
2018-12-18 19:31:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
def is_ip_address(host):
|
|
|
|
parts = host.split('.')
|
|
|
|
if len(parts) != 4:
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
for p in parts:
|
|
|
|
int(p)
|
|
|
|
return True
|
|
|
|
except ValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2019-02-10 16:57:34 +01:00
|
|
|
def _resolve_with_zeroconf(host):
|
2019-02-13 16:54:02 +01:00
|
|
|
from esphome.core import EsphomeError
|
2019-04-08 22:19:21 +02:00
|
|
|
from esphome.zeroconf import Zeroconf
|
|
|
|
|
2019-02-10 16:57:34 +01:00
|
|
|
try:
|
|
|
|
zc = Zeroconf()
|
|
|
|
except Exception:
|
2019-02-13 16:54:02 +01:00
|
|
|
raise EsphomeError("Cannot start mDNS sockets, is this a docker container without "
|
|
|
|
"host network mode?")
|
2019-02-10 16:57:34 +01:00
|
|
|
try:
|
|
|
|
info = zc.resolve_host(host + '.')
|
|
|
|
except Exception as err:
|
2019-02-13 16:54:02 +01:00
|
|
|
raise EsphomeError("Error resolving mDNS hostname: {}".format(err))
|
2019-02-10 16:57:34 +01:00
|
|
|
finally:
|
|
|
|
zc.close()
|
|
|
|
if info is None:
|
2019-02-13 16:54:02 +01:00
|
|
|
raise EsphomeError("Error resolving address with mDNS: Did not respond. "
|
|
|
|
"Maybe the device is offline.")
|
2019-02-10 16:57:34 +01:00
|
|
|
return info
|
|
|
|
|
|
|
|
|
2018-12-18 19:31:43 +01:00
|
|
|
def resolve_ip_address(host):
|
2019-02-13 16:54:02 +01:00
|
|
|
from esphome.core import EsphomeError
|
2019-04-08 22:19:21 +02:00
|
|
|
import socket
|
2019-02-10 16:57:34 +01:00
|
|
|
|
2018-12-18 19:31:43 +01:00
|
|
|
try:
|
|
|
|
ip = socket.gethostbyname(host)
|
|
|
|
except socket.error as err:
|
2019-02-10 16:57:34 +01:00
|
|
|
if host.endswith('.local'):
|
|
|
|
ip = _resolve_with_zeroconf(host)
|
|
|
|
else:
|
2019-02-13 16:54:02 +01:00
|
|
|
raise EsphomeError("Error resolving IP address: {}".format(err))
|
2018-12-18 19:31:43 +01:00
|
|
|
|
|
|
|
return ip
|
2019-02-28 10:15:57 +01:00
|
|
|
|
|
|
|
|
2019-03-03 16:50:06 +01:00
|
|
|
def get_bool_env(var, default=False):
|
|
|
|
return bool(os.getenv(var, default))
|
|
|
|
|
|
|
|
|
|
|
|
def is_hassio():
|
|
|
|
return get_bool_env('ESPHOME_IS_HASSIO')
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
def copy_file_if_changed(src, dst):
|
|
|
|
src_text = read_file(src)
|
|
|
|
if os.path.isfile(dst):
|
|
|
|
dst_text = read_file(dst)
|
|
|
|
else:
|
|
|
|
dst_text = None
|
|
|
|
if src_text == dst_text:
|
|
|
|
return
|
|
|
|
write_file(dst, src_text)
|
|
|
|
|
|
|
|
|
|
|
|
def read_file(path):
|
|
|
|
try:
|
|
|
|
with codecs.open(path, 'r', encoding='utf-8') as f_handle:
|
|
|
|
return f_handle.read()
|
|
|
|
except OSError:
|
|
|
|
from esphome.core import EsphomeError
|
|
|
|
raise EsphomeError(u"Could not read file at {}".format(path))
|
|
|
|
|
|
|
|
|
|
|
|
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 write_file_if_changed(text, dst):
|
|
|
|
src_content = None
|
|
|
|
if os.path.isfile(dst):
|
|
|
|
src_content = read_file(dst)
|
|
|
|
if src_content != text:
|
|
|
|
write_file(dst, text)
|
2019-04-22 21:56:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
def list_starts_with(list_, sub):
|
|
|
|
return len(sub) <= len(list_) and all(list_[i] == x for i, x in enumerate(sub))
|