This commit is contained in:
Otto Winter 2018-11-23 18:57:13 +01:00
parent 17ec2bb905
commit 7197232f3a
No known key found for this signature in database
GPG key ID: DB66C0BE6013F97E
12 changed files with 133 additions and 66 deletions

View file

@ -444,12 +444,16 @@ def parse_args(argv):
dashboard = subparsers.add_parser('dashboard', dashboard = subparsers.add_parser('dashboard',
help="Create a simple web server for a dashboard.") help="Create a simple web server for a dashboard.")
dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int, dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
default=6052) type=int, default=6052)
dashboard.add_argument("--password", help="The optional password to require for all requests.", dashboard.add_argument("--password", help="The optional password to require for all requests.",
type=str, default='') type=str, default='')
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
action='store_true') action='store_true')
dashboard.add_argument("--hassio",
help="Internal flag used to tell esphomeyaml is started as a HassIO "
"add-on.",
action="store_true")
subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added" subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added"
"to Home Assistant when not using MQTT discovery.") "to Home Assistant when not using MQTT discovery.")

View file

@ -3,7 +3,7 @@ import voluptuous as vol
from esphomeyaml.components import binary_sensor from esphomeyaml.components import binary_sensor
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA from esphomeyaml.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA
from esphomeyaml.cpp_generator import process_lambda from esphomeyaml.cpp_generator import process_lambda, variable
from esphomeyaml.cpp_types import std_vector from esphomeyaml.cpp_types import std_vector
CustomBinarySensorConstructor = binary_sensor.binary_sensor_ns.class_( CustomBinarySensorConstructor = binary_sensor.binary_sensor_ns.class_(

View file

@ -1,9 +1,9 @@
# coding=utf-8 # coding=utf-8
import voluptuous as vol import voluptuous as vol
from esphomeyaml.components.text_sensor import add_job
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import add from esphomeyaml.cpp_generator import add
from esphomeyaml.cpp_types import esphomelib_ns from esphomeyaml.cpp_types import esphomelib_ns
@ -52,7 +52,7 @@ def setup_display_core_(display_var, config):
def setup_display(display_var, config): def setup_display(display_var, config):
add_job(setup_display_core_, display_var, config) CORE.add_job(setup_display_core_, display_var, config)
BUILD_FLAGS = '-DUSE_DISPLAY' BUILD_FLAGS = '-DUSE_DISPLAY'

View file

@ -6,7 +6,7 @@ import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266 from esphomeyaml.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
from esphomeyaml.cpp_generator import Pvariable, add from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App from esphomeyaml.cpp_types import App, Component
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]

View file

@ -6,13 +6,14 @@ import math
import os import os
import re import re
from typing import Any, Dict, List
from esphomeyaml.const import CONF_ARDUINO_VERSION, CONF_DOMAIN, CONF_ESPHOMELIB_VERSION, \ 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, \ CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOCAL, CONF_MANUAL_IP, CONF_STATIC_IP, CONF_WIFI, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import ensure_unique_string 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__) _LOGGER = logging.getLogger(__name__)
@ -253,6 +254,7 @@ class ID(object):
return hash(self.id) return hash(self.id)
# pylint: disable=too-many-instance-attributes
class EsphomeyamlCore(object): class EsphomeyamlCore(object):
def __init__(self): def __init__(self):
# The name of the node # The name of the node
@ -266,9 +268,9 @@ class EsphomeyamlCore(object):
# The board that's used (for example nodemcuv2) # The board that's used (for example nodemcuv2)
self.board = None # type: str self.board = None # type: str
# The full raw configuration # The full raw configuration
self.raw_config = None # type: ConfigType self.raw_config = {} # type: ConfigType
# The validated configuration, this is None until the config has been validated # The validated configuration, this is None until the config has been validated
self.config = None # type: ConfigType self.config = {} # type: ConfigType
# The pending tasks in the task queue (mostly for C++ generation) # The pending tasks in the task queue (mostly for C++ generation)
self.pending_tasks = collections.deque() self.pending_tasks = collections.deque()
# The variable cache, for each ID this holds a MockObj of the variable obj # The variable cache, for each ID this holds a MockObj of the variable obj

View file

@ -1,11 +1,13 @@
from collections import OrderedDict from collections import OrderedDict
from typing import Any, Generator, List, Optional, Tuple, Union from esphomeyaml.core import CORE, HexInt, Lambda, TimePeriod, TimePeriodMicroseconds, \
from esphomeyaml.core import CORE, HexInt, ID, Lambda, TimePeriod, TimePeriodMicroseconds, \
TimePeriodMilliseconds, TimePeriodSeconds TimePeriodMilliseconds, TimePeriodSeconds
from esphomeyaml.helpers import cpp_string_escape, indent_all_but_first_and_last from esphomeyaml.helpers import cpp_string_escape, indent_all_but_first_and_last
# pylint: disable=unused-import, wrong-import-order
from typing import Any, Generator, List, Optional, Tuple, Union # noqa
from esphomeyaml.core import ID # noqa
class Expression(object): class Expression(object):
def __init__(self): def __init__(self):
@ -260,6 +262,7 @@ class FloatLiteral(Literal):
return u"{:f}f".format(self.float_) return u"{:f}f".format(self.float_)
# pylint: disable=bad-continuation
def safe_exp(obj # type: Union[Expression, bool, str, unicode, int, long, float, TimePeriod] def safe_exp(obj # type: Union[Expression, bool, str, unicode, int, long, float, TimePeriod]
): ):
# type: (...) -> Expression # type: (...) -> Expression

View file

@ -10,28 +10,26 @@ import os
import random import random
import subprocess import subprocess
import threading import threading
import urllib2
import tornado
import tornado.concurrent
import tornado.gen
import tornado.ioloop
import tornado.iostream
from tornado.log import access_log from tornado.log import access_log
from typing import Optional import tornado.process
import tornado.web
import tornado.websocket
from esphomeyaml import const from esphomeyaml import const
from esphomeyaml.__main__ import get_serial_ports from esphomeyaml.__main__ import get_serial_ports
from esphomeyaml.core import EsphomeyamlError
from esphomeyaml.helpers import run_system_command from esphomeyaml.helpers import run_system_command
from esphomeyaml.storage_json import StorageJSON, ext_storage_path from esphomeyaml.storage_json import StorageJSON, ext_storage_path
from esphomeyaml.util import shlex_quote from esphomeyaml.util import shlex_quote
try: # pylint: disable=unused-import, wrong-import-order
import tornado from typing import Optional # noqa
import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.process
import tornado.web
import tornado.websocket
import tornado.concurrent
except ImportError as err:
tornado = None
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONFIG_DIR = '' CONFIG_DIR = ''
@ -274,9 +272,10 @@ class MainRequestHandler(BaseHandler):
version = const.__version__ version = const.__version__
docs_link = 'https://beta.esphomelib.com/esphomeyaml/' if 'b' in version else \ docs_link = 'https://beta.esphomelib.com/esphomeyaml/' if 'b' in version else \
'https://esphomelib.com/esphomeyaml/' 'https://esphomelib.com/esphomeyaml/'
mqtt_config = get_mqtt_config_lazy()
self.render("templates/index.html", entries=entries, self.render("templates/index.html", entries=entries,
version=version, begin=begin, docs_link=docs_link) version=version, begin=begin, docs_link=docs_link, mqtt_config=mqtt_config)
def _ping_func(filename, address): def _ping_func(filename, address):
@ -372,6 +371,7 @@ def make_app(debug=False):
log_method = access_log.error log_method = access_log.error
request_time = 1000.0 * handler.request.request_time() request_time = 1000.0 * handler.request.request_time()
# pylint: disable=protected-access
log_method("%d %s %.2fms", handler.get_status(), log_method("%d %s %.2fms", handler.get_status(),
handler._request_summary(), request_time) handler._request_summary(), request_time)
@ -395,13 +395,46 @@ def make_app(debug=False):
return app return app
HASSIO_MQTT_CONFIG = None
def _get_mqtt_config_impl():
token = os.getenv('HASSIO_TOKEN')
if token is None:
raise ValueError
req = urllib2.Request('http://hassio/services/mqtt')
req.add_header('X-HASSIO-KEY', token)
resp = urllib2.urlopen(req)
content = resp.read()
mqtt_config = json.loads(content)
return {
'addon': mqtt_config['addon'],
'host': mqtt_config['host'],
'username': mqtt_config.get('username', ''),
'password': mqtt_config.get('password', '')
}
def get_mqtt_config_lazy():
global HASSIO_MQTT_CONFIG
if HASSIO_MQTT_CONFIG is None:
return None
if not HASSIO_MQTT_CONFIG:
try:
HASSIO_MQTT_CONFIG = _get_mqtt_config_impl()
except Exception: # pylint: disable=broad-except
HASSIO_MQTT_CONFIG = None
return HASSIO_MQTT_CONFIG
def start_web_server(args): def start_web_server(args):
global CONFIG_DIR global CONFIG_DIR
global PASSWORD global PASSWORD
global HASSIO_MQTT_CONFIG
if tornado is None:
raise EsphomeyamlError("Attempted to load dashboard, but tornado is not installed! "
"Please run \"pip2 install tornado esptool\" in your terminal.")
CONFIG_DIR = args.configuration CONFIG_DIR = args.configuration
if not os.path.exists(CONFIG_DIR): if not os.path.exists(CONFIG_DIR):
@ -409,10 +442,9 @@ def start_web_server(args):
# HassIO options storage # HassIO options storage
PASSWORD = args.password PASSWORD = args.password
if os.path.isfile('/data/options.json'):
with open('/data/options.json') as f: if args.hassio:
js = json.load(f) HASSIO_MQTT_CONFIG = False
PASSWORD = js.get('password') or PASSWORD
if PASSWORD: if PASSWORD:
PASSWORD = hmac.new(str(PASSWORD)).digest() PASSWORD = hmac.new(str(PASSWORD)).digest()

View file

@ -446,29 +446,47 @@
<div class="step-title waves-effect">MQTT</div> <div class="step-title waves-effect">MQTT</div>
<div class="step-content"> <div class="step-content">
<div class="row"> <div class="row">
<p> {% if mqtt_config is None %}
esphomelib connects to your Home Assistant instance via <p>
<a href="https://www.home-assistant.io/docs/mqtt/">MQTT</a>. If you haven't already, please set up esphomelib connects to your Home Assistant instance via
MQTT on your Home Assistant server, for example with the awesome <a href="https://www.home-assistant.io/docs/mqtt/">MQTT</a>.
<a href="https://www.home-assistant.io/addons/mosquitto/">Mosquitto Hass.io Add-on</a>. If you haven't already, please set up
</p> MQTT on your Home Assistant server, for example with the
<p> <a href="https://www.home-assistant.io/addons/mosquitto/">Mosquitto Hass.io Add-on</a>.
When you're done with that, please enter your MQTT broker here. For example </p>
<code class="inlinecode">192.168.1.100</code> (Note <p>
<code class="inlinecode">hassio.local</code> doesn't always work, please use a static IP). When you're done with that, please enter your MQTT broker here. For example
Please also specify the MQTT username and password you wish esphomelib to use <code class="inlinecode">192.168.1.100</code>.
(leave them empty if you're not using any authentication). Please also specify the MQTT username and password you wish esphomelib to use
</p> (leave them empty if you're not using any authentication).
</p>
{% else %}
<p>
It looks like you've already set up MQTT, the values below are taken from your "{{ escape(mqtt_config['addon']) }}" add-on.
</p>
{% end %}
<div class="input-field col s12"> <div class="input-field col s12">
<input id="mqtt_broker" class="validate" type="text" name="broker" required> {% if mqtt_config is None %}
<input id="mqtt_broker" class="validate" type="text" name="broker" required>
{% else %}
<input id="mqtt_broker" class="validate" type="text" name="broker" value="{{ mqtt_config['host'] }}" required>
{% end %}
<label for="mqtt_broker">MQTT Broker</label> <label for="mqtt_broker">MQTT Broker</label>
</div> </div>
<div class="input-field col s6"> <div class="input-field col s6">
<input id="mqtt_username" class="validate" type="text" name="mqtt_username"> {% if mqtt_config is None %}
<input id="mqtt_username" class="validate" type="text" name="mqtt_username">
{% else %}
<input id="mqtt_username" class="validate" type="text" name="mqtt_username" value="{{ mqtt_config['username'] }}">
{% end%}
<label for="mqtt_username">MQTT Username</label> <label for="mqtt_username">MQTT Username</label>
</div> </div>
<div class="input-field col s6"> <div class="input-field col s6">
<input id="mqtt_password" class="validate" name="mqtt_password" type="password"> {% if mqtt_config is None %}
<input id="mqtt_password" class="validate" name="mqtt_password" type="password">
{% else %}
<input id="mqtt_password" class="validate" name="mqtt_password" type="password" value="{{ mqtt_config['password'] }}">
{% end %}
<label for="mqtt_password">MQTT Password</label> <label for="mqtt_password">MQTT Password</label>
</div> </div>
</div> </div>

View file

@ -31,7 +31,7 @@ MAGIC_BYTES = [0x6C, 0x26, 0xF7, 0x5C, 0x45]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class ProgressBar: class ProgressBar(object):
def __init__(self): def __init__(self):
self.last_progress = None self.last_progress = None
@ -51,6 +51,7 @@ class ProgressBar:
sys.stderr.write(text) sys.stderr.write(text)
sys.stderr.flush() sys.stderr.flush()
# pylint: disable=no-self-use
def done(self): def done(self):
sys.stderr.write('\n') sys.stderr.write('\n')
sys.stderr.flush() sys.stderr.flush()
@ -193,7 +194,7 @@ def perform_ota(sock, password, file_handle, filename):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192) sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
offset = 0 offset = 0
bar = ProgressBar() progress = ProgressBar()
while True: while True:
chunk = file_handle.read(1024) chunk = file_handle.read(1024)
if not chunk: if not chunk:
@ -206,8 +207,8 @@ def perform_ota(sock, password, file_handle, filename):
sys.stderr.write('\n') sys.stderr.write('\n')
raise OTAError("Error sending data: {}".format(err)) raise OTAError("Error sending data: {}".format(err))
bar.update(offset / float(file_size)) progress.update(offset / float(file_size))
bar.done() progress.done()
# Enable nodelay for last checks # Enable nodelay for last checks
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

View file

@ -3,16 +3,16 @@ from __future__ import print_function
from datetime import datetime from datetime import datetime
import hashlib import hashlib
import logging import logging
import socket
import ssl import ssl
import sys import sys
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
from esphomeyaml import core
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \ from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \
CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SSL_FINGERPRINTS, \ CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SSL_FINGERPRINTS, \
CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME
from esphomeyaml.core import CORE from esphomeyaml.core import CORE, EsphomeyamlError
from esphomeyaml.helpers import color from esphomeyaml.helpers import color
from esphomeyaml.util import safe_print from esphomeyaml.util import safe_print
@ -41,7 +41,11 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
tls_version = ssl.PROTOCOL_SSLv23 tls_version = ssl.PROTOCOL_SSLv23
client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED,
tls_version=tls_version, ciphers=None) tls_version=tls_version, ciphers=None)
client.connect(config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT])
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: try:
client.loop_forever() client.loop_forever()

View file

@ -2,11 +2,13 @@ import codecs
import json import json
import os import os
from typing import Any, Dict, Optional from esphomeyaml.core import CORE
from esphomeyaml.core import CORE, CoreType
from esphomeyaml.helpers import mkdir_p from esphomeyaml.helpers import mkdir_p
# pylint: disable=unused-import, wrong-import-order
from esphomeyaml.core import CoreType # noqa
from typing import Any, Dict, Optional # noqa
def storage_path(): # type: () -> str def storage_path(): # type: () -> str
return CORE.relative_path('.esphomeyaml', '{}.json'.format(CORE.config_filename)) return CORE.relative_path('.esphomeyaml', '{}.json'.format(CORE.config_filename))
@ -16,6 +18,7 @@ def ext_storage_path(base_path, config_filename): # type: (str, str) -> str
return os.path.join(base_path, '.esphomeyaml', '{}.json'.format(config_filename)) return os.path.join(base_path, '.esphomeyaml', '{}.json'.format(config_filename))
# pylint: disable=too-many-instance-attributes
class StorageJSON(object): class StorageJSON(object):
def __init__(self, storage_version, name, esphomelib_version, src_version, def __init__(self, storage_version, name, esphomelib_version, src_version,
arduino_version, address, esp_platform, board, build_path, arduino_version, address, esp_platform, board, build_path,

View file

@ -325,8 +325,8 @@ def write_platformio_ini(content, path):
else: else:
prev_file = None prev_file = None
content_format = INI_BASE_FORMAT content_format = INI_BASE_FORMAT
full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \ full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + content
content + INI_AUTO_GENERATE_END + content_format[1] full_file += INI_AUTO_GENERATE_END + content_format[1]
if prev_file == full_file: if prev_file == full_file:
return return
with codecs.open(path, mode='w+', encoding='utf-8') as f_handle: with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
@ -371,9 +371,9 @@ def write_cpp(code_s):
include_s = get_include_text() include_s = get_include_text()
full_file = code_format[0] + CPP_INCLUDE_BEGIN + u'\n' + include_s + CPP_INCLUDE_END + \ full_file = code_format[0] + CPP_INCLUDE_BEGIN + u'\n' + include_s + CPP_INCLUDE_END
code_format[1] + CPP_AUTO_GENERATE_BEGIN + u'\n' + code_s + \ full_file += code_format[1] + CPP_AUTO_GENERATE_BEGIN + u'\n' + code_s + CPP_AUTO_GENERATE_END
CPP_AUTO_GENERATE_END + code_format[2] full_file += code_format[2]
if prev_file == full_file: if prev_file == full_file:
return return
with codecs.open(path, 'w+', encoding='utf-8') as f_handle: with codecs.open(path, 'w+', encoding='utf-8') as f_handle: