mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-06-13 08:03:32 +02:00
[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:
+191
-26
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user