Files
esphome-dev/esphome/helpers.py
T
Otto Winter 6682c43dfa 🏗 Merge C++ into python codebase (#504)
## Description:

Move esphome-core codebase into esphome (and a bunch of other refactors). See https://github.com/esphome/feature-requests/issues/97

Yes this is a shit ton of work and no there's no way to automate it :( But it will be worth it 👍

Progress:
- Core support (file copy etc): 80%
- Base Abstractions (light, switch): ~50%
- Integrations: ~10%
- Working? Yes, (but only with ported components).

Other refactors:
- Moves all codegen related stuff into a single class: `esphome.codegen` (imported as `cg`)
- Rework coroutine syntax
- Move from `component/platform.py` to `domain/component.py` structure as with HA
- Move all defaults out of C++ and into config validation.
- Remove `make_...` helpers from Application class. Reason: Merge conflicts with every single new integration.
- Pointer Variables are stored globally instead of locally in setup(). Reason: stack size limit.

Future work:
- Rework const.py - Move all `CONF_...` into a conf class (usage `conf.UPDATE_INTERVAL` vs `CONF_UPDATE_INTERVAL`). Reason: Less convoluted import block
- Enable loading from `custom_components` folder.

**Related issue (if applicable):** https://github.com/esphome/feature-requests/issues/97

**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>

## Checklist:
  - [ ] The code change is tested and works locally.
  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).

If user exposed functionality or configuration variables are added/changed:
  - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
2019-04-17 12:06:00 +02:00

185 lines
4.5 KiB
Python

from __future__ import print_function
import codecs
import logging
import os
from esphome.py_compat import char_to_byte, text_type
_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))
# From https://stackoverflow.com/a/14945195/8924614
def cpp_string_escape(string, encoding='utf-8'):
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
if isinstance(string, text_type):
string = string.encode(encoding)
result = ''
for character in string:
character = char_to_byte(character)
if _should_escape(character):
result += '\\%03o' % character
else:
result += chr(character)
return '"' + result + '"'
def color(the_color, message=''):
from colorlog.escape_codes import escape_codes, parse_colors
if not message:
res = parse_colors(the_color)
else:
res = parse_colors(the_color) + message + escape_codes['reset']
return res
def run_system_command(*args):
import subprocess
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
rc = p.returncode
return rc, stdout, stderr
def mkdir_p(path):
import errno
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
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
def _resolve_with_zeroconf(host):
from esphome.core import EsphomeError
from esphome.zeroconf import Zeroconf
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 resolve_ip_address(host):
from esphome.core import EsphomeError
import socket
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 get_bool_env(var, default=False):
return bool(os.getenv(var, default))
def is_hassio():
return get_bool_env('ESPHOME_IS_HASSIO')
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)