[Huge] Util Refactor, Dashboard Improvements, Hass.io Auth API, Better Validation Errors, Conditions, Custom Platforms, Substitutions (#234)

* Implement custom sensor platform

* Update

* Ethernet

* Lint

* Fix

* Login page

* Rename cookie secret

* Update manifest

* Update cookie check logic

* Favicon

* Fix

* Favicon manifest

* Fix

* Fix

* Fix

* Use hostname

* Message

* Temporary commit for screenshot

* Automatic board selection

* Undo temporary commit

* Update esphomeyaml-edge

* In-dashboard editing and hosting files locally

* Update esphomeyaml-edge

* Better ANSI color escaping

* Message

* Lint

* Download Efficiency

* Fix gitlab

* Fix

* Rename extra_libraries to libraries

* Add example

* Update README.md

* Update README.md

* Update README.md

* HassIO -> Hass.io

* Updates

* Add update available notice

* Update

* Fix substitutions

* Better error message

* Re-do dashboard ANSI colors

* Only include FastLED if user says so

* Autoscroll logs

* Remove old checks

* Use safer RedirectText

* Improvements

* Fix

* Use enviornment variable

* Use http://hassio/host/info

* Fix conditions

* Update platformio versions

* Revert "Use enviornment variable"

This reverts commit 7f038eb5d26df72f76ea9ae76774e2cec1fd7f59.

* Fix

* README update

* Temp

* Better invalid config messages

* Platformio debug

* Improve error messages

* Debug

* Remove debug

* Multi Conf

* Update

* Better paths

* Remove unused

* Fixes

* Lint

* lib_ignore

* Try fix platformio colors

* Fix dashboard scrolling

* Revert

* Lint

* Revert
This commit is contained in:
Otto Winter
2018-12-05 21:22:06 +01:00
committed by GitHub
parent 2b88c987da
commit 7c7032c59e
192 changed files with 6156 additions and 2845 deletions
+191 -26
View File
@@ -1,9 +1,23 @@
import math
import re
import collections
from collections import OrderedDict
import inspect
import logging
import math
import os
import re
from esphomeyaml.const import CONF_ARDUINO_VERSION, CONF_DOMAIN, CONF_ESPHOMELIB_VERSION, \
CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOCAL, CONF_MANUAL_IP, CONF_STATIC_IP, CONF_WIFI, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import ensure_unique_string
# pylint: disable=unused-import, wrong-import-order
from typing import Any, Dict, List # noqa
_LOGGER = logging.getLogger(__name__)
class ESPHomeYAMLError(Exception):
class EsphomeyamlError(Exception):
"""General esphomeyaml exception occurred."""
pass
@@ -35,10 +49,10 @@ class MACAddress(object):
return ':'.join('{:02X}'.format(part) for part in self.parts)
def as_hex(self):
import esphomeyaml.helpers
from esphomeyaml.cpp_generator import RawExpression
num = ''.join('{:02X}'.format(part) for part in self.parts)
return esphomeyaml.helpers.RawExpression('0x{}ULL'.format(num))
return RawExpression('0x{}ULL'.format(num))
def is_approximately_integer(value):
@@ -195,11 +209,36 @@ class TimePeriodSeconds(TimePeriod):
pass
LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)')
class Lambda(object):
def __init__(self, value):
self.value = value
self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)', value)
self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)]
self._value = value
self._parts = None
self._requires_ids = None
@property
def parts(self):
if self._parts is None:
self._parts = re.split(LAMBDA_PROG, self._value)
return self._parts
@property
def requires_ids(self):
if self._requires_ids is None:
self._requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)]
return self._requires_ids
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value
self._parts = None
self._requires_ids = None
def __str__(self):
return self.value
@@ -208,19 +247,6 @@ class Lambda(object):
return u'Lambda<{}>'.format(self.value)
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
class ID(object):
def __init__(self, id, is_declaration=False, type=None):
self.id = id
@@ -253,8 +279,147 @@ class ID(object):
return hash(self.id)
CONFIG_PATH = None
ESP_PLATFORM = ''
BOARD = ''
RAW_CONFIG = None
NAME = ''
# pylint: disable=too-many-instance-attributes
class EsphomeyamlCore(object):
def __init__(self):
# The name of the node
self.name = None # type: str
# The relative path to the configuration YAML
self.config_path = None # type: str
# The relative path to where all build files are stored
self.build_path = None # type: str
# The platform (ESP8266, ESP32) of this device
self.esp_platform = None # type: str
# The board that's used (for example nodemcuv2)
self.board = None # type: str
# The full raw configuration
self.raw_config = {} # type: ConfigType
# The validated configuration, this is None until the config has been validated
self.config = {} # type: ConfigType
# The pending tasks in the task queue (mostly for C++ generation)
self.pending_tasks = collections.deque()
# The variable cache, for each ID this holds a MockObj of the variable obj
self.variables = {} # type: Dict[str, MockObj]
# The list of expressions for the C++ generation
self.expressions = [] # type: List[Expression]
@property
def address(self): # type: () -> str
if CONF_MANUAL_IP in self.config[CONF_WIFI]:
return str(self.config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
elif CONF_HOSTNAME in self.config[CONF_WIFI]:
hostname = self.config[CONF_WIFI][CONF_HOSTNAME]
else:
hostname = self.name
return hostname + self.config[CONF_WIFI][CONF_DOMAIN]
@property
def esphomelib_version(self): # type: () -> Dict[str, str]
return self.config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION]
@property
def is_local_esphomelib_copy(self):
return CONF_LOCAL in self.esphomelib_version
@property
def arduino_version(self): # type: () -> str
return self.config[CONF_ESPHOMEYAML][CONF_ARDUINO_VERSION]
@property
def config_dir(self):
return os.path.dirname(self.config_path)
@property
def config_filename(self):
return os.path.basename(self.config_path)
def relative_path(self, *path):
path_ = os.path.expanduser(os.path.join(*path))
return os.path.join(self.config_dir, path_)
def relative_build_path(self, *path):
path_ = os.path.expanduser(os.path.join(*path))
return os.path.join(self.build_path, path_)
@property
def firmware_bin(self):
return self.relative_build_path('.pioenvs', self.name, 'firmware.bin')
@property
def is_esp8266(self):
if self.esp_platform is None:
raise ValueError
return self.esp_platform == ESP_PLATFORM_ESP8266
@property
def is_esp32(self):
if self.esp_platform is None:
raise ValueError
return self.esp_platform == ESP_PLATFORM_ESP32
def add_job(self, func, *args, **kwargs):
domain = kwargs.get('domain')
if inspect.isgeneratorfunction(func):
def func_():
yield
for _ in func(*args):
yield
else:
def func_():
yield
func(*args)
gen = func_()
self.pending_tasks.append((gen, domain))
return gen
def flush_tasks(self):
i = 0
while self.pending_tasks:
i += 1
if i > 1000000:
raise EsphomeyamlError("Circular dependency detected!")
task, domain = self.pending_tasks.popleft()
_LOGGER.debug("Executing task for domain=%s", domain)
try:
task.next()
self.pending_tasks.append((task, domain))
except StopIteration:
_LOGGER.debug(" -> %s finished", domain)
def add(self, expression, require=True):
from esphomeyaml.cpp_generator import Expression
if require and isinstance(expression, Expression):
expression.require()
self.expressions.append(expression)
_LOGGER.debug("Adding: %s", expression)
return expression
def get_variable(self, id):
while True:
if id in self.variables:
yield self.variables[id]
return
_LOGGER.debug("Waiting for variable %s", id)
yield None
def get_variable_with_full_id(self, id):
while True:
if id in self.variables:
for k, v in self.variables.iteritems():
if k == id:
yield (k, v)
return
_LOGGER.debug("Waiting for variable %s", id)
yield None, None
def register_variable(self, id, obj):
_LOGGER.debug("Registered variable %s of type %s", id.id, id.type)
self.variables[id] = obj
CORE = EsphomeyamlCore()
ConfigType = Dict[str, Any]
CoreType = EsphomeyamlCore