From 4e4ffc3a24e495ed44707fa7518c64c46506b0de Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 31 Oct 2018 17:47:34 +0100 Subject: [PATCH 1/5] Bump beta version to 1.9.0b2 --- esphomeyaml-beta/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphomeyaml-beta/config.json b/esphomeyaml-beta/config.json index b636643cd0..c0eeb19981 100644 --- a/esphomeyaml-beta/config.json +++ b/esphomeyaml-beta/config.json @@ -1,6 +1,6 @@ { "name": "esphomeyaml-beta", - "version": "1.9.0b1", + "version": "1.9.0b2", "slug": "esphomeyaml-beta", "description": "Beta version of esphomeyaml HassIO add-on.", "url": "https://esphomelib.com/esphomeyaml/index.html", From 36da3b85d59ee25a4390ff49dd6155095f966250 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 3 Nov 2018 14:06:14 +0100 Subject: [PATCH 2/5] Auto-Decode stacktraces (#214) * Auto-Decode stacktraces * Fix Gitlab CI --- .gitlab-ci.yml | 13 +- esphomeyaml/__main__.py | 59 ++------ esphomeyaml/dashboard/dashboard.py | 5 +- esphomeyaml/helpers.py | 19 +-- esphomeyaml/platformio_api.py | 211 +++++++++++++++++++++++++++++ esphomeyaml/util.py | 51 +++++++ 6 files changed, 291 insertions(+), 67 deletions(-) create mode 100644 esphomeyaml/platformio_api.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 01c917866d..5e03f1a67a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -88,6 +88,10 @@ test2: docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - | + docker tag \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - | docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ @@ -96,9 +100,16 @@ test2: docker tag \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - | + docker tag \ + "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ + "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" only: - /^v\d+\.\d+\.\d+$/ except: diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index 36ebfba424..e0c5042a39 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -7,16 +7,15 @@ import random import sys from datetime import datetime -from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util +from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util, platformio_api from esphomeyaml.config import get_component, iter_components, read_config from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF_ESPHOMEYAML, \ CONF_HOSTNAME, CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_USE_CUSTOM_CODE, \ CONF_WIFI, ESP_PLATFORM_ESP8266 from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \ - _EXPRESSIONS, add, \ - add_job, color, flush_tasks, indent, quote, statement, relative_path -from esphomeyaml.util import safe_print + _EXPRESSIONS, add, add_job, color, flush_tasks, indent, statement, relative_path +from esphomeyaml.util import safe_print, run_external_command _LOGGER = logging.getLogger(__name__) @@ -62,34 +61,6 @@ def choose_serial_port(config): return result[opt][0] -def run_platformio(*cmd, **kwargs): - def mock_exit(return_code): - raise SystemExit(return_code) - - orig_argv = sys.argv - orig_exit = sys.exit # mock sys.exit - full_cmd = u' '.join(quote(x) for x in cmd) - _LOGGER.info(u"Running: %s", full_cmd) - try: - func = kwargs.get('main') - if func is None: - import platformio.__main__ - func = platformio.__main__.main - sys.argv = list(cmd) - sys.exit = mock_exit - return func() or 0 - except KeyboardInterrupt: - return 1 - except SystemExit as err: - return err.args[0] - except Exception as err: # pylint: disable=broad-except - _LOGGER.error(u"Running platformio failed: %s", err) - _LOGGER.error(u"Please try running %s locally.", full_cmd) - finally: - sys.argv = orig_argv - sys.exit = orig_exit - - def run_miniterm(config, port, escape=False): import serial if CONF_LOGGER not in config: @@ -100,6 +71,7 @@ def run_miniterm(config, port, escape=False): _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.") _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate) + backtrace_state = False with serial.Serial(port, baudrate=baud_rate) as ser: while True: try: @@ -114,6 +86,9 @@ def run_miniterm(config, port, escape=False): message = message.replace('\033', '\\033') safe_print(message) + backtrace_state = platformio_api.process_stacktrace( + config, line, backtrace_state=backtrace_state) + def write_cpp(config): _LOGGER.info("Generating C++ source...") @@ -154,11 +129,7 @@ def write_cpp(config): def compile_program(args, config): _LOGGER.info("Compiling app...") - build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) - command = ['platformio', 'run', '-d', build_path] - if args.verbose: - command.append('-v') - return run_platformio(*command) + return platformio_api.run_compile(config, args.verbose) def get_upload_host(config): @@ -176,10 +147,10 @@ def upload_using_esptool(config, port): build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') + cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', + '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] # pylint: disable=protected-access - return run_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset', - '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', - path, main=esptool._main) + return run_external_command(esptool._main, *cmd) def upload_program(config, args, port): @@ -190,11 +161,7 @@ def upload_program(config, args, port): if port != 'OTA' and serial_port: if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy: return upload_using_esptool(config, port) - command = ['platformio', 'run', '-d', build_path, - '-t', 'upload', '--upload-port', port] - if args.verbose: - command.append('-v') - return run_platformio(*command) + return platformio_api.run_upload(config, args.verbose, port) if 'ota' not in config: _LOGGER.error("No serial port found and OTA not enabled. Can't upload!") @@ -243,7 +210,7 @@ def clean_mqtt(config, args): def setup_log(debug=False): log_level = logging.DEBUG if debug else logging.INFO logging.basicConfig(level=log_level) - fmt = "%(levelname)s [%(name)s] %(message)s" + fmt = "%(levelname)s %(message)s" colorfmt = "%(log_color)s{}%(reset)s".format(fmt) datefmt = '%H:%M:%S' diff --git a/esphomeyaml/dashboard/dashboard.py b/esphomeyaml/dashboard/dashboard.py index f9739d24b9..4e8cb96b0b 100644 --- a/esphomeyaml/dashboard/dashboard.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -13,7 +13,8 @@ from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_BUILD_PATH from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml import const, core, __main__ from esphomeyaml.__main__ import get_serial_ports -from esphomeyaml.helpers import quote, relative_path +from esphomeyaml.helpers import relative_path +from esphomeyaml.util import shlex_quote try: import tornado @@ -51,7 +52,7 @@ class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler): if self.proc is not None: return command = self.build_command(message) - _LOGGER.debug(u"WebSocket opened for command %s", [quote(x) for x in command]) + _LOGGER.debug(u"WebSocket opened for command %s", [shlex_quote(x) for x in command]) self.proc = tornado.process.Subprocess(command, stdout=tornado.process.Subprocess.STREAM, stderr=subprocess.STDOUT) diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index 6ef894bdfe..aa7648172c 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -1,10 +1,9 @@ from __future__ import print_function +from collections import OrderedDict, deque import inspect import logging import os -import re -from collections import OrderedDict, deque from esphomeyaml import core from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \ @@ -648,22 +647,6 @@ def setup_mqtt_component(obj, config): availability[CONF_PAYLOAD_NOT_AVAILABLE])) -# shlex's quote for Python 2.7 -_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - - -def quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return u"''" - if _find_unsafe(s) is None: - return s - - # use single quotes, and put single quotes into double quotes - # the string $'b is then quoted as '$'"'"'b' - return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" - - def color(the_color, message='', reset=None): """Color helper.""" from colorlog.escape_codes import escape_codes, parse_colors diff --git a/esphomeyaml/platformio_api.py b/esphomeyaml/platformio_api.py new file mode 100644 index 0000000000..74f8997ed1 --- /dev/null +++ b/esphomeyaml/platformio_api.py @@ -0,0 +1,211 @@ +import json +import logging +import re +import subprocess + +from esphomeyaml.const import CONF_BUILD_PATH, CONF_ESPHOMEYAML +from esphomeyaml.helpers import relative_path +from esphomeyaml.util import run_external_command + +_LOGGER = logging.getLogger(__name__) + + +def run_platformio_cli(*args, **kwargs): + import platformio.__main__ + + cmd = ['platformio'] + list(args) + return run_external_command(platformio.__main__.main, + *cmd, **kwargs) + + +def run_platformio_cli_run(config, verbose, *args, **kwargs): + build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) + command = ['run', '-d', build_path] + if verbose: + command += ['-v'] + command += list(args) + return run_platformio_cli(*command, **kwargs) + + +def run_compile(config, verbose): + return run_platformio_cli_run(config, verbose) + + +def run_upload(config, verbose, port): + return run_platformio_cli_run(config, verbose, '-t', 'upload', '--upload-port', port) + + +def run_idedata(config): + args = ['-t', 'idedata'] + stdout = run_platformio_cli_run(config, False, *args, capture_stdout=True) + match = re.search(r'{.*}', stdout) + if match is None: + return IDEData(None) + try: + return IDEData(json.loads(match.group())) + except ValueError: + return IDEData(None) + + +IDE_DATA = None + + +def get_idedata(config): + global IDE_DATA + + if IDE_DATA is None: + _LOGGER.info("Need to fetch platformio IDE-data, please stand by") + IDE_DATA = run_idedata(config) + return IDE_DATA + + +# ESP logs stack trace decoder, based on https://github.com/me-no-dev/EspExceptionDecoder +ESP8266_EXCEPTION_CODES = { + 0: "Illegal instruction", + 1: "SYSCALL instruction", + 2: "InstructionFetchError: Processor internal physical address or data error during " + "instruction fetch", + 3: "LoadStoreError: Processor internal physical address or data error during load or store", + 4: "Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT " + "register", + 5: "Alloca: MOVSP instruction, if caller's registers are not in the register file", + 6: "IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero", + 7: "reserved", + 8: "Privileged: Attempt to execute a privileged operation when CRING ? 0", + 9: "LoadStoreAlignmentCause: Load or store to an unaligned address", + 10: "reserved", + 11: "reserved", + 12: "InstrPIFDataError: PIF data error during instruction fetch", + 13: "LoadStorePIFDataError: Synchronous PIF data error during LoadStore access", + 14: "InstrPIFAddrError: PIF address error during instruction fetch", + 15: "LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access", + 16: "InstTLBMiss: Error during Instruction TLB refill", + 17: "InstTLBMultiHit: Multiple instruction TLB entries matched", + 18: "InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level " + "less than CRING", + 19: "reserved", + 20: "InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute " + "that does not permit instruction fetch", + 21: "reserved", + 22: "reserved", + 23: "reserved", + 24: "LoadStoreTLBMiss: Error during TLB refill for a load or store", + 25: "LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store", + 26: "LoadStorePrivilege: A load or store referenced a virtual address at a ring level less " + "than ", + 27: "reserved", + 28: "LoadProhibited: A load referenced a page mapped with an attribute that does not permit " + "loads", + 29: "StoreProhibited: A store referenced a page mapped with an attribute that does not permit " + "stores", +} + + +def _decode_pc(config, addr): + idedata = get_idedata(config) + if not idedata.addr2line_path or not idedata.firmware_elf_path: + return + command = [idedata.addr2line_path, '-pfiaC', '-e', idedata.firmware_elf_path, addr] + try: + translation = subprocess.check_output(command).strip() + except Exception: # pylint: disable=broad-except + return + + if "?? ??:0" in translation: + # Nothing useful + return + translation = translation.replace(' at ??:?', '').replace(':?', '') + _LOGGER.warning("Decoded %s", translation) + + +def _parse_register(config, regex, line): + match = regex.match(line) + if match is not None: + _decode_pc(config, match.group(1)) + + +STACKTRACE_ESP8266_EXCEPTION_TYPE_RE = re.compile(r'Exception \(([0-9]*)\):') +STACKTRACE_ESP8266_PC_RE = re.compile(r'epc1=0x(4[0-9a-fA-F]{7})') +STACKTRACE_ESP8266_EXCVADDR_RE = re.compile(r'excvaddr=0x(4[0-9a-fA-F]{7})') +STACKTRACE_ESP32_PC_RE = re.compile(r'PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})') +STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r'EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})') +STACKTRACE_BAD_ALLOC_RE = re.compile(r'^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$') +STACKTRACE_ESP32_BACKTRACE_RE = re.compile(r'Backtrace:(?:\s+0x4[0-9a-fA-F]{7}:0x3[0-9a-fA-F]{7})+') +STACKTRACE_ESP32_BACKTRACE_PC_RE = re.compile(r'4[0-9a-f]{7}') +STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r'4[0-9a-f]{7}') + + +def process_stacktrace(config, line, backtrace_state): + line = line.strip() + # ESP8266 Exception type + match = re.match(STACKTRACE_ESP8266_EXCEPTION_TYPE_RE, line) + if match is not None: + code = match.group(1) + _LOGGER.warning("Exception type: %s", ESP8266_EXCEPTION_CODES.get(code, 'unknown')) + + # ESP8266 PC/EXCVADDR + _parse_register(config, STACKTRACE_ESP8266_PC_RE, line) + _parse_register(config, STACKTRACE_ESP8266_EXCVADDR_RE, line) + # ESP32 PC/EXCVADDR + _parse_register(config, STACKTRACE_ESP32_PC_RE, line) + _parse_register(config, STACKTRACE_ESP32_EXCVADDR_RE, line) + + # bad alloc + match = re.match(STACKTRACE_BAD_ALLOC_RE, line) + if match is not None: + _LOGGER.warning("Memory allocation of %s bytes failed at %s", + match.group(2), match.group(1)) + _decode_pc(config, match.group(1)) + + # ESP32 single-line backtrace + match = re.match(STACKTRACE_ESP32_BACKTRACE_RE, line) + if match is not None: + _LOGGER.warning("Found stack trace! Trying to decode it") + for addr in re.finditer(STACKTRACE_ESP32_BACKTRACE_PC_RE, line): + _decode_pc(config, addr.group()) + + # ESP8266 multi-line backtrace + if '>>>stack>>>' in line: + # Start of backtrace + backtrace_state = True + _LOGGER.warning("Found stack trace! Trying to decode it") + elif '<</.platformio/packages/toolchain-xtensa32/bin/xtensa-esp32-elf-gcc + return self.raw.get("cc_path") + + @property + def addr2line_path(self): + cc_path = self.cc_path + if cc_path is None: + return None + # replace gcc at end with addr2line + return cc_path[:-3] + 'addr2line' diff --git a/esphomeyaml/util.py b/esphomeyaml/util.py index c52b54284f..bb9f4b3e66 100644 --- a/esphomeyaml/util.py +++ b/esphomeyaml/util.py @@ -1,5 +1,12 @@ from __future__ import print_function +import io +import logging +import re +import sys + +_LOGGER = logging.getLogger(__name__) + class Registry(dict): def register(self, name): @@ -30,3 +37,47 @@ def safe_print(message=""): print(message.encode('ascii', 'backslashreplace')) except UnicodeEncodeError: print("Cannot print line because of invalid locale!") + + +def shlex_quote(s): + if not s: + return u"''" + if re.search(r'[^\w@%+=:,./-]', s) is None: + return s + + return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" + + +def run_external_command(func, *cmd, **kwargs): + def mock_exit(return_code): + raise SystemExit(return_code) + + orig_argv = sys.argv + orig_exit = sys.exit # mock sys.exit + full_cmd = u' '.join(shlex_quote(x) for x in cmd) + _LOGGER.info(u"Running: %s", full_cmd) + + capture_stdout = kwargs.get('capture_stdout', False) + if capture_stdout: + sys.stdout = io.BytesIO() + + try: + sys.argv = list(cmd) + sys.exit = mock_exit + return func() or 0 + except KeyboardInterrupt: + return 1 + except SystemExit as err: + return err.args[0] + except Exception as err: # pylint: disable=broad-except + _LOGGER.error(u"Running command failed: %s", err) + _LOGGER.error(u"Please try running %s locally.", full_cmd) + finally: + sys.argv = orig_argv + sys.exit = orig_exit + + if capture_stdout: + # pylint: disable=lost-exception + stdout = sys.stdout.getvalue() + sys.stdout = sys.__stdout__ + return stdout From 27b86d89b0e17bd79e1ea4fb33a9062ff166b922 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 3 Nov 2018 14:08:31 +0100 Subject: [PATCH 3/5] Add generate home assistant config command (#208) * Add generate home assistant config command * Lint * Lint --- esphomeyaml/__main__.py | 27 ++++++++ .../components/binary_sensor/__init__.py | 11 +++ .../binary_sensor/esp32_ble_tracker.py | 4 ++ .../components/binary_sensor/esp32_touch.py | 4 ++ esphomeyaml/components/binary_sensor/gpio.py | 4 ++ .../components/binary_sensor/nextion.py | 4 ++ esphomeyaml/components/binary_sensor/pn532.py | 4 ++ .../components/binary_sensor/rdm6300.py | 4 ++ .../binary_sensor/remote_receiver.py | 4 ++ .../components/binary_sensor/status.py | 4 ++ .../components/binary_sensor/template.py | 4 ++ esphomeyaml/components/cover/__init__.py | 8 +++ esphomeyaml/components/cover/template.py | 9 +++ esphomeyaml/components/fan/__init__.py | 15 +++- esphomeyaml/components/fan/binary.py | 4 ++ esphomeyaml/components/fan/speed.py | 13 +++- esphomeyaml/components/light/__init__.py | 22 ++++++ esphomeyaml/components/light/binary.py | 5 ++ esphomeyaml/components/light/cwww.py | 5 ++ .../components/light/fastled_clockless.py | 5 ++ esphomeyaml/components/light/fastled_spi.py | 5 ++ esphomeyaml/components/light/monochromatic.py | 5 ++ esphomeyaml/components/light/rgb.py | 5 ++ esphomeyaml/components/light/rgbw.py | 5 ++ esphomeyaml/components/light/rgbww.py | 5 ++ esphomeyaml/components/mqtt.py | 69 ++++++++++++++++++- esphomeyaml/components/sensor/__init__.py | 16 +++++ esphomeyaml/components/sensor/adc.py | 4 ++ esphomeyaml/components/sensor/ads1115.py | 4 ++ esphomeyaml/components/sensor/bh1750.py | 4 ++ esphomeyaml/components/sensor/ble_rssi.py | 4 ++ esphomeyaml/components/sensor/bme280.py | 6 ++ esphomeyaml/components/sensor/bme680.py | 7 ++ esphomeyaml/components/sensor/bmp085.py | 5 ++ esphomeyaml/components/sensor/bmp280.py | 5 ++ esphomeyaml/components/sensor/cse7766.py | 8 +++ esphomeyaml/components/sensor/dallas.py | 4 ++ esphomeyaml/components/sensor/dht.py | 5 ++ esphomeyaml/components/sensor/dht12.py | 5 ++ esphomeyaml/components/sensor/duty_cycle.py | 4 ++ esphomeyaml/components/sensor/esp32_hall.py | 4 ++ esphomeyaml/components/sensor/hdc1080.py | 5 ++ esphomeyaml/components/sensor/hlw8012.py | 8 +++ esphomeyaml/components/sensor/hmc5883l.py | 8 +++ esphomeyaml/components/sensor/htu21d.py | 5 ++ esphomeyaml/components/sensor/hx711.py | 4 ++ esphomeyaml/components/sensor/ina219.py | 8 +++ esphomeyaml/components/sensor/ina3221.py | 12 ++++ esphomeyaml/components/sensor/max6675.py | 4 ++ esphomeyaml/components/sensor/mhz19.py | 8 +++ esphomeyaml/components/sensor/mpu6050.py | 9 +++ .../components/sensor/mqtt_subscribe.py | 4 ++ esphomeyaml/components/sensor/ms5611.py | 5 ++ esphomeyaml/components/sensor/pmsx003.py | 9 +++ .../components/sensor/pulse_counter.py | 4 ++ .../components/sensor/rotary_encoder.py | 4 ++ esphomeyaml/components/sensor/sht3xd.py | 5 ++ esphomeyaml/components/sensor/tcs34725.py | 9 +++ esphomeyaml/components/sensor/template.py | 4 ++ esphomeyaml/components/sensor/tsl2561.py | 4 ++ esphomeyaml/components/sensor/ultrasonic.py | 4 ++ esphomeyaml/components/sensor/uptime.py | 4 ++ esphomeyaml/components/sensor/wifi_signal.py | 4 ++ .../components/sensor/xiaomi_miflora.py | 9 +++ esphomeyaml/components/sensor/xiaomi_mijia.py | 8 +++ esphomeyaml/components/switch/__init__.py | 15 +++- esphomeyaml/components/switch/gpio.py | 4 ++ esphomeyaml/components/switch/output.py | 4 ++ .../components/switch/remote_transmitter.py | 4 ++ esphomeyaml/components/switch/restart.py | 4 ++ esphomeyaml/components/switch/shutdown.py | 4 ++ esphomeyaml/components/switch/template.py | 4 ++ esphomeyaml/components/switch/uart.py | 4 ++ .../components/text_sensor/__init__.py | 10 +++ .../components/text_sensor/mqtt_subscribe.py | 4 ++ .../components/text_sensor/template.py | 4 ++ esphomeyaml/components/text_sensor/version.py | 4 ++ esphomeyaml/dashboard/dashboard.py | 8 +++ esphomeyaml/dashboard/templates/index.html | 61 ++++++++++++++++ esphomeyaml/espota2.py | 9 ++- 80 files changed, 625 insertions(+), 5 deletions(-) diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index e0c5042a39..af6c3223e0 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -1,6 +1,7 @@ from __future__ import print_function import argparse +from collections import OrderedDict import logging import os import random @@ -334,6 +335,28 @@ def command_clean(args, config): return 0 +def command_hass_config(args, config): + from esphomeyaml.components import mqtt as mqtt_component + + _LOGGER.info("This is what you should put in your Home Assistant YAML configuration.") + _LOGGER.info("Please note this is only necessary if you're not using MQTT discovery.") + data = mqtt_component.GenerateHassConfigData(config) + hass_config = OrderedDict() + for domain, component, conf in iter_components(config): + if not hasattr(component, 'to_hass_config'): + continue + func = getattr(component, 'to_hass_config') + ret = func(data, conf) + if not isinstance(ret, (list, tuple)): + ret = [ret] + ret = [x for x in ret if x is not None] + domain_conf = hass_config.setdefault(domain.split('.')[0], []) + domain_conf += ret + + safe_print(yaml_util.dump(hass_config)) + return 0 + + def command_dashboard(args): from esphomeyaml.dashboard import dashboard @@ -355,6 +378,7 @@ POST_CONFIG_ACTIONS = { 'clean-mqtt': command_clean_mqtt, 'mqtt-fingerprint': command_mqtt_fingerprint, 'clean': command_clean, + 'hass-config': command_hass_config, } @@ -436,6 +460,9 @@ def parse_args(argv): dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", action='store_true') + subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added" + "to Home Assistant when not using MQTT discovery.") + return parser.parse_args(argv[1:]) diff --git a/esphomeyaml/components/binary_sensor/__init__.py b/esphomeyaml/components/binary_sensor/__init__.py index 32731a08ce..82da0a9ce9 100644 --- a/esphomeyaml/components/binary_sensor/__init__.py +++ b/esphomeyaml/components/binary_sensor/__init__.py @@ -1,5 +1,6 @@ import voluptuous as vol +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml import automation from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INTERNAL, CONF_INVERTED, \ @@ -154,4 +155,14 @@ def register_binary_sensor(var, config): add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config) +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'binary_sensor', config, + include_state=True, include_command=False) + if ret is None: + return None + if CONF_DEVICE_CLASS in config: + ret['device_class'] = config[CONF_DEVICE_CLASS] + return ret + + BUILD_FLAGS = '-DUSE_BINARY_SENSOR' diff --git a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py index 8e4737602c..7057f890a3 100644 --- a/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py +++ b/esphomeyaml/components/binary_sensor/esp32_ble_tracker.py @@ -21,3 +21,7 @@ def to_code(config): yield rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/esp32_touch.py b/esphomeyaml/components/binary_sensor/esp32_touch.py index c22f6150da..f122f1b737 100644 --- a/esphomeyaml/components/binary_sensor/esp32_touch.py +++ b/esphomeyaml/components/binary_sensor/esp32_touch.py @@ -51,3 +51,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/gpio.py b/esphomeyaml/components/binary_sensor/gpio.py index 4bca5fd329..85994ca97f 100644 --- a/esphomeyaml/components/binary_sensor/gpio.py +++ b/esphomeyaml/components/binary_sensor/gpio.py @@ -24,3 +24,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/nextion.py b/esphomeyaml/components/binary_sensor/nextion.py index 5f6b1529b5..3a96b4684c 100644 --- a/esphomeyaml/components/binary_sensor/nextion.py +++ b/esphomeyaml/components/binary_sensor/nextion.py @@ -24,3 +24,7 @@ def to_code(config): rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID], config[CONF_COMPONENT_ID]) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/pn532.py b/esphomeyaml/components/binary_sensor/pn532.py index 6ae202a1e7..1c2b5be073 100644 --- a/esphomeyaml/components/binary_sensor/pn532.py +++ b/esphomeyaml/components/binary_sensor/pn532.py @@ -40,3 +40,7 @@ def to_code(config): addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] rhs = hub.make_tag(config[CONF_NAME], ArrayInitializer(*addr, multiline=False)) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/rdm6300.py b/esphomeyaml/components/binary_sensor/rdm6300.py index e1e4214011..394af241ac 100644 --- a/esphomeyaml/components/binary_sensor/rdm6300.py +++ b/esphomeyaml/components/binary_sensor/rdm6300.py @@ -21,3 +21,7 @@ def to_code(config): yield rhs = hub.make_card(config[CONF_NAME], config[CONF_UID]) binary_sensor.register_binary_sensor(rhs, config) + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/remote_receiver.py b/esphomeyaml/components/binary_sensor/remote_receiver.py index 9e1cf50d99..51c7efb42c 100644 --- a/esphomeyaml/components/binary_sensor/remote_receiver.py +++ b/esphomeyaml/components/binary_sensor/remote_receiver.py @@ -119,3 +119,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/status.py b/esphomeyaml/components/binary_sensor/status.py index fb3169217a..11324b887f 100644 --- a/esphomeyaml/components/binary_sensor/status.py +++ b/esphomeyaml/components/binary_sensor/status.py @@ -19,3 +19,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/binary_sensor/template.py b/esphomeyaml/components/binary_sensor/template.py index 43beaeb752..1d5e0f2d36 100644 --- a/esphomeyaml/components/binary_sensor/template.py +++ b/esphomeyaml/components/binary_sensor/template.py @@ -26,3 +26,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR' + + +def to_hass_config(data, config): + return binary_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/cover/__init__.py b/esphomeyaml/components/cover/__init__.py index 650542963e..b768804d60 100644 --- a/esphomeyaml/components/cover/__init__.py +++ b/esphomeyaml/components/cover/__init__.py @@ -1,6 +1,7 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_INTERNAL from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \ @@ -88,3 +89,10 @@ def cover_stop_to_code(config, action_id, arg_type): rhs = var.make_stop_action(template_arg) type = StopAction.template(arg_type) yield Pvariable(action_id, rhs, type=type) + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'cover', config, include_state=True, include_command=True) + if ret is None: + return None + return ret diff --git a/esphomeyaml/components/cover/template.py b/esphomeyaml/components/cover/template.py index df9c0f30b5..0b20c6c785 100644 --- a/esphomeyaml/components/cover/template.py +++ b/esphomeyaml/components/cover/template.py @@ -45,3 +45,12 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_COVER' + + +def to_hass_config(data, config): + ret = cover.core_to_hass_config(data, config) + if ret is None: + return None + if CONF_OPTIMISTIC in config: + ret['optimistic'] = config[CONF_OPTIMISTIC] + return ret diff --git a/esphomeyaml/components/fan/__init__.py b/esphomeyaml/components/fan/__init__.py index efdbff4087..807c463db5 100644 --- a/esphomeyaml/components/fan/__init__.py +++ b/esphomeyaml/components/fan/__init__.py @@ -1,10 +1,11 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_OSCILLATION_COMMAND_TOPIC, \ CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_INTERNAL, \ - CONF_SPEED, CONF_OSCILLATING + CONF_SPEED, CONF_OSCILLATING, CONF_OSCILLATION_OUTPUT, CONF_NAME from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \ TemplateArguments, get_variable, templatable, bool_ @@ -128,3 +129,15 @@ def fan_turn_on_to_code(config, action_id, arg_type): yield None add(action.set_speed(template_)) yield action + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'fan', config, include_state=True, include_command=True) + if ret is None: + return None + if CONF_OSCILLATION_OUTPUT in config: + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'oscillation/state') + ret['oscillation_state_topic'] = config.get(CONF_OSCILLATION_STATE_TOPIC, default) + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'oscillation/command') + ret['oscillation_command__topic'] = config.get(CONF_OSCILLATION_COMMAND_TOPIC, default) + return ret diff --git a/esphomeyaml/components/fan/binary.py b/esphomeyaml/components/fan/binary.py index 05c833dbdd..cf588989a4 100644 --- a/esphomeyaml/components/fan/binary.py +++ b/esphomeyaml/components/fan/binary.py @@ -27,3 +27,7 @@ def to_code(config): add(fan_struct.Poutput.set_oscillation(oscillation_output)) fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return fan.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/fan/speed.py b/esphomeyaml/components/fan/speed.py index f0bee8258a..468f9be8e8 100644 --- a/esphomeyaml/components/fan/speed.py +++ b/esphomeyaml/components/fan/speed.py @@ -1,7 +1,7 @@ import voluptuous as vol import esphomeyaml.config_validation as cv -from esphomeyaml.components import fan +from esphomeyaml.components import fan, mqtt from esphomeyaml.const import CONF_HIGH, CONF_LOW, CONF_MAKE_ID, CONF_MEDIUM, CONF_NAME, \ CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, \ CONF_SPEED_STATE_TOPIC @@ -43,3 +43,14 @@ def to_code(config): add(fan_struct.Poutput.set_oscillation(oscillation_output)) fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config) + + +def to_hass_config(data, config): + ret = fan.core_to_hass_config(data, config) + if ret is None: + return None + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'speed/state') + ret['speed_state_topic'] = config.get(CONF_SPEED_STATE_TOPIC, default) + default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'speed/command') + ret['speed_command__topic'] = config.get(CONF_SPEED_COMMAND_TOPIC, default) + return ret diff --git a/esphomeyaml/components/light/__init__.py b/esphomeyaml/components/light/__init__.py index ad427b643c..c75370d239 100644 --- a/esphomeyaml/components/light/__init__.py +++ b/esphomeyaml/components/light/__init__.py @@ -1,6 +1,7 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \ @@ -448,3 +449,24 @@ def light_turn_on_to_code(config, action_id, arg_type): yield None add(action.set_effect(template_)) yield action + + +def core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True, + white_value=True): + ret = mqtt.build_hass_config(data, 'light', config, include_state=True, include_command=True, + platform='mqtt_json') + if ret is None: + return None + if brightness: + ret['brightness'] = True + if rgb: + ret['rgb'] = True + if color_temp: + ret['color_temp'] = True + if white_value: + ret['white_value'] = True + for effect in config.get(CONF_EFFECTS, []): + ret["effect"] = True + effects = ret.setdefault("effect_list", []) + effects.append(next(x for x in effect.values())[CONF_NAME]) + return ret diff --git a/esphomeyaml/components/light/binary.py b/esphomeyaml/components/light/binary.py index 5468d54d32..5b2a43e0b9 100644 --- a/esphomeyaml/components/light/binary.py +++ b/esphomeyaml/components/light/binary.py @@ -19,3 +19,8 @@ def to_code(config): rhs = App.make_binary_light(config[CONF_NAME], output) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=False, rgb=False, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/cwww.py b/esphomeyaml/components/light/cwww.py index f14557c4bc..ded1f44767 100644 --- a/esphomeyaml/components/light/cwww.py +++ b/esphomeyaml/components/light/cwww.py @@ -32,3 +32,8 @@ def to_code(config): cold_white, warm_white) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=False, color_temp=True, + white_value=False) diff --git a/esphomeyaml/components/light/fastled_clockless.py b/esphomeyaml/components/light/fastled_clockless.py index 767703c152..4965820802 100644 --- a/esphomeyaml/components/light/fastled_clockless.py +++ b/esphomeyaml/components/light/fastled_clockless.py @@ -102,3 +102,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/fastled_spi.py b/esphomeyaml/components/light/fastled_spi.py index a74a435d5b..89fa933cab 100644 --- a/esphomeyaml/components/light/fastled_spi.py +++ b/esphomeyaml/components/light/fastled_spi.py @@ -82,3 +82,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/monochromatic.py b/esphomeyaml/components/light/monochromatic.py index 9ab8b3d53a..aac07f144c 100644 --- a/esphomeyaml/components/light/monochromatic.py +++ b/esphomeyaml/components/light/monochromatic.py @@ -22,3 +22,8 @@ def to_code(config): rhs = App.make_monochromatic_light(config[CONF_NAME], output) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=False, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/rgb.py b/esphomeyaml/components/light/rgb.py index 7f2f360f5d..f73865216c 100644 --- a/esphomeyaml/components/light/rgb.py +++ b/esphomeyaml/components/light/rgb.py @@ -30,3 +30,8 @@ def to_code(config): rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=False) diff --git a/esphomeyaml/components/light/rgbw.py b/esphomeyaml/components/light/rgbw.py index edd8919eb8..4e0577f7fc 100644 --- a/esphomeyaml/components/light/rgbw.py +++ b/esphomeyaml/components/light/rgbw.py @@ -34,3 +34,8 @@ def to_code(config): rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False, + white_value=True) diff --git a/esphomeyaml/components/light/rgbww.py b/esphomeyaml/components/light/rgbww.py index fe69993698..267dfd9d34 100644 --- a/esphomeyaml/components/light/rgbww.py +++ b/esphomeyaml/components/light/rgbww.py @@ -60,3 +60,8 @@ def to_code(config): red, green, blue, cold_white, warm_white) light_struct = variable(config[CONF_MAKE_ID], rhs) light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) + + +def to_hass_config(data, config): + return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True, + white_value=True) diff --git a/esphomeyaml/components/mqtt.py b/esphomeyaml/components/mqtt.py index 143f0aa4ad..c8f85a870b 100644 --- a/esphomeyaml/components/mqtt.py +++ b/esphomeyaml/components/mqtt.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import re import voluptuous as vol @@ -10,7 +11,10 @@ from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, C CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \ CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \ CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \ - CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE + CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE, \ + CONF_STATE_TOPIC, CONF_MQTT, CONF_ESPHOMEYAML, CONF_NAME, CONF_AVAILABILITY, \ + CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_INTERNAL +from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \ StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \ uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef @@ -239,3 +243,66 @@ def required_build_flags(config): if CONF_SSL_FINGERPRINTS in config: return '-DASYNC_TCP_SSL_ENABLED=1' return None + + +def get_default_topic_for(data, component_type, name, suffix): + whitelist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' + sanitized_name = ''.join(x for x in name.lower().replace(' ', '_') if x in whitelist) + return '{}/{}/{}/{}'.format(data.topic_prefix, component_type, + sanitized_name, suffix) + + +def build_hass_config(data, component_type, config, include_state=True, include_command=True, + platform='mqtt'): + if config.get(CONF_INTERNAL, False): + return None + ret = OrderedDict() + ret['platform'] = platform + ret['name'] = config[CONF_NAME] + if include_state: + default = get_default_topic_for(data, component_type, config[CONF_NAME], 'state') + ret['state_topic'] = config.get(CONF_STATE_TOPIC, default) + if include_command: + default = get_default_topic_for(data, component_type, config[CONF_NAME], 'command') + ret['command_topic'] = config.get(CONF_STATE_TOPIC, default) + avail = config.get(CONF_AVAILABILITY, data.availability) + if avail: + ret['availability_topic'] = avail[CONF_TOPIC] + payload_available = avail[CONF_PAYLOAD_AVAILABLE] + if payload_available != 'online': + ret['payload_available'] = payload_available + payload_not_available = avail[CONF_PAYLOAD_NOT_AVAILABLE] + if payload_not_available != 'offline': + ret['payload_not_available'] = payload_not_available + return ret + + +class GenerateHassConfigData(object): + def __init__(self, config): + if 'mqtt' not in config: + raise ESPHomeYAMLError("Cannot generate Home Assistant MQTT config if MQTT is not " + "used!") + mqtt = config[CONF_MQTT] + self.topic_prefix = mqtt.get(CONF_TOPIC_PREFIX, config[CONF_ESPHOMEYAML][CONF_NAME]) + birth_message = mqtt.get(CONF_BIRTH_MESSAGE) + if CONF_BIRTH_MESSAGE not in mqtt: + birth_message = { + CONF_TOPIC: self.topic_prefix + '/status', + CONF_PAYLOAD: 'online', + } + will_message = mqtt.get(CONF_WILL_MESSAGE) + if CONF_WILL_MESSAGE not in mqtt: + will_message = { + CONF_TOPIC: self.topic_prefix + '/status', + CONF_PAYLOAD: 'offline' + } + if not birth_message or not will_message: + self.availability = None + elif birth_message[CONF_TOPIC] != will_message[CONF_TOPIC]: + self.availability = None + else: + self.availability = { + CONF_TOPIC: birth_message[CONF_TOPIC], + CONF_PAYLOAD_AVAILABLE: birth_message[CONF_PAYLOAD], + CONF_PAYLOAD_NOT_AVAILABLE: will_message[CONF_PAYLOAD], + } diff --git a/esphomeyaml/components/sensor/__init__.py b/esphomeyaml/components/sensor/__init__.py index dc12106135..6b648b418f 100644 --- a/esphomeyaml/components/sensor/__init__.py +++ b/esphomeyaml/components/sensor/__init__.py @@ -1,5 +1,6 @@ import voluptuous as vol +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml import automation from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \ @@ -211,3 +212,18 @@ def register_sensor(var, config): BUILD_FLAGS = '-DUSE_SENSOR' + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'sensor', config, include_state=True, include_command=False) + if ret is None: + return None + if CONF_UNIT_OF_MEASUREMENT in config: + ret['unit_of_measurement'] = config[CONF_UNIT_OF_MEASUREMENT] + if CONF_EXPIRE_AFTER in config: + expire = config[CONF_EXPIRE_AFTER] + if expire is not None: + ret['expire_after'] = expire.total_seconds + if CONF_ICON in config: + ret['icon'] = config[CONF_ICON] + return ret diff --git a/esphomeyaml/components/sensor/adc.py b/esphomeyaml/components/sensor/adc.py index 6d75251c69..ca02b815b5 100644 --- a/esphomeyaml/components/sensor/adc.py +++ b/esphomeyaml/components/sensor/adc.py @@ -52,3 +52,7 @@ def required_build_flags(config): if config[CONF_PIN] == 'VCC': return '-DUSE_ADC_SENSOR_VCC' return None + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ads1115.py b/esphomeyaml/components/sensor/ads1115.py index ab9b594a78..008ea4c291 100644 --- a/esphomeyaml/components/sensor/ads1115.py +++ b/esphomeyaml/components/sensor/ads1115.py @@ -65,3 +65,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ADS1115_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/bh1750.py b/esphomeyaml/components/sensor/bh1750.py index c50a1494ab..dd43d5e0de 100644 --- a/esphomeyaml/components/sensor/bh1750.py +++ b/esphomeyaml/components/sensor/bh1750.py @@ -35,3 +35,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_BH1750' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ble_rssi.py b/esphomeyaml/components/sensor/ble_rssi.py index 2b98f45c44..01bc1704bb 100644 --- a/esphomeyaml/components/sensor/ble_rssi.py +++ b/esphomeyaml/components/sensor/ble_rssi.py @@ -21,3 +21,7 @@ def to_code(config): yield rhs = hub.make_rssi_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) sensor.register_sensor(rhs, config) + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/bme280.py b/esphomeyaml/components/sensor/bme280.py index e123134fb7..580add1019 100644 --- a/esphomeyaml/components/sensor/bme280.py +++ b/esphomeyaml/components/sensor/bme280.py @@ -72,3 +72,9 @@ def to_code(config): BUILD_FLAGS = '-DUSE_BME280' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/bme680.py b/esphomeyaml/components/sensor/bme680.py index 43cebc5d49..352552bb59 100644 --- a/esphomeyaml/components/sensor/bme680.py +++ b/esphomeyaml/components/sensor/bme680.py @@ -92,3 +92,10 @@ def to_code(config): BUILD_FLAGS = '-DUSE_BME680' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY]), + sensor.core_to_hass_config(data, config[CONF_GAS_RESISTANCE])] diff --git a/esphomeyaml/components/sensor/bmp085.py b/esphomeyaml/components/sensor/bmp085.py index 01659d9852..ad1e22939d 100644 --- a/esphomeyaml/components/sensor/bmp085.py +++ b/esphomeyaml/components/sensor/bmp085.py @@ -34,3 +34,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_BMP085_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE])] diff --git a/esphomeyaml/components/sensor/bmp280.py b/esphomeyaml/components/sensor/bmp280.py index 53d4a34e48..9aa67d6bc0 100644 --- a/esphomeyaml/components/sensor/bmp280.py +++ b/esphomeyaml/components/sensor/bmp280.py @@ -65,3 +65,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_BMP280' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE])] diff --git a/esphomeyaml/components/sensor/cse7766.py b/esphomeyaml/components/sensor/cse7766.py index 01dc894db8..7de127e4b6 100644 --- a/esphomeyaml/components/sensor/cse7766.py +++ b/esphomeyaml/components/sensor/cse7766.py @@ -40,3 +40,11 @@ def to_code(config): BUILD_FLAGS = '-DUSE_CSE7766' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/dallas.py b/esphomeyaml/components/sensor/dallas.py index bbf1e7429c..42fc2035b8 100644 --- a/esphomeyaml/components/sensor/dallas.py +++ b/esphomeyaml/components/sensor/dallas.py @@ -29,3 +29,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_DALLAS_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/dht.py b/esphomeyaml/components/sensor/dht.py index 0b4f013e2a..c5a6dbff2b 100644 --- a/esphomeyaml/components/sensor/dht.py +++ b/esphomeyaml/components/sensor/dht.py @@ -46,3 +46,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_DHT_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/dht12.py b/esphomeyaml/components/sensor/dht12.py index f6526ada08..e85e1fd3bc 100644 --- a/esphomeyaml/components/sensor/dht12.py +++ b/esphomeyaml/components/sensor/dht12.py @@ -31,3 +31,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_DHT12_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/duty_cycle.py b/esphomeyaml/components/sensor/duty_cycle.py index 0153f12010..0bd41b9976 100644 --- a/esphomeyaml/components/sensor/duty_cycle.py +++ b/esphomeyaml/components/sensor/duty_cycle.py @@ -26,3 +26,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_DUTY_CYCLE_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/esp32_hall.py b/esphomeyaml/components/sensor/esp32_hall.py index dd6a5fc545..2bdb062e8e 100644 --- a/esphomeyaml/components/sensor/esp32_hall.py +++ b/esphomeyaml/components/sensor/esp32_hall.py @@ -22,3 +22,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ESP32_HALL_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/hdc1080.py b/esphomeyaml/components/sensor/hdc1080.py index 642dc68f1f..d59663a71b 100644 --- a/esphomeyaml/components/sensor/hdc1080.py +++ b/esphomeyaml/components/sensor/hdc1080.py @@ -32,3 +32,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_HDC1080_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/hlw8012.py b/esphomeyaml/components/sensor/hlw8012.py index 86ed388ef4..65bafbb69f 100644 --- a/esphomeyaml/components/sensor/hlw8012.py +++ b/esphomeyaml/components/sensor/hlw8012.py @@ -55,3 +55,11 @@ def to_code(config): BUILD_FLAGS = '-DUSE_HLW8012' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/hmc5883l.py b/esphomeyaml/components/sensor/hmc5883l.py index f82ae5df1b..a63f1737c2 100644 --- a/esphomeyaml/components/sensor/hmc5883l.py +++ b/esphomeyaml/components/sensor/hmc5883l.py @@ -71,3 +71,11 @@ def to_code(config): BUILD_FLAGS = '-DUSE_HMC5883L' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, CONF_HEADING): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/htu21d.py b/esphomeyaml/components/sensor/htu21d.py index c0a381107d..60c049fea9 100644 --- a/esphomeyaml/components/sensor/htu21d.py +++ b/esphomeyaml/components/sensor/htu21d.py @@ -30,3 +30,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_HTU21D_SENSOR' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/hx711.py b/esphomeyaml/components/sensor/hx711.py index 1a26b0762b..1df25c52ab 100644 --- a/esphomeyaml/components/sensor/hx711.py +++ b/esphomeyaml/components/sensor/hx711.py @@ -44,3 +44,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_HX711' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ina219.py b/esphomeyaml/components/sensor/ina219.py index 3aefb945e1..b441b7e758 100644 --- a/esphomeyaml/components/sensor/ina219.py +++ b/esphomeyaml/components/sensor/ina219.py @@ -51,3 +51,11 @@ def to_code(config): BUILD_FLAGS = '-DUSE_INA219' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/ina3221.py b/esphomeyaml/components/sensor/ina3221.py index bd9d574906..360590960a 100644 --- a/esphomeyaml/components/sensor/ina3221.py +++ b/esphomeyaml/components/sensor/ina3221.py @@ -62,3 +62,15 @@ def to_code(config): BUILD_FLAGS = '-DUSE_INA3221' + + +def to_hass_config(data, config): + ret = [] + for channel in (CONF_CHANNEL_1, CONF_CHANNEL_2, CONF_CHANNEL_3): + if channel not in config: + continue + conf = config[channel] + for key in (CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER): + if key in conf: + ret.append(sensor.core_to_hass_config(data, conf[key])) + return ret diff --git a/esphomeyaml/components/sensor/max6675.py b/esphomeyaml/components/sensor/max6675.py index ae9ce0ee16..007acbd6b4 100644 --- a/esphomeyaml/components/sensor/max6675.py +++ b/esphomeyaml/components/sensor/max6675.py @@ -32,3 +32,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_MAX6675_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/mhz19.py b/esphomeyaml/components/sensor/mhz19.py index 7af15aa341..f2670356b3 100644 --- a/esphomeyaml/components/sensor/mhz19.py +++ b/esphomeyaml/components/sensor/mhz19.py @@ -36,3 +36,11 @@ def to_code(config): BUILD_FLAGS = '-DUSE_MHZ19' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_CO2, CONF_TEMPERATURE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/mpu6050.py b/esphomeyaml/components/sensor/mpu6050.py index 8aa989312e..dc42c6a637 100644 --- a/esphomeyaml/components/sensor/mpu6050.py +++ b/esphomeyaml/components/sensor/mpu6050.py @@ -69,3 +69,12 @@ def to_code(config): BUILD_FLAGS = '-DUSE_MPU6050' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z, CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z, + CONF_TEMPERATURE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/mqtt_subscribe.py b/esphomeyaml/components/sensor/mqtt_subscribe.py index 2bfe71d98d..8b8ce50317 100644 --- a/esphomeyaml/components/sensor/mqtt_subscribe.py +++ b/esphomeyaml/components/sensor/mqtt_subscribe.py @@ -27,3 +27,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ms5611.py b/esphomeyaml/components/sensor/ms5611.py index 92afc930dd..9dc25ca057 100644 --- a/esphomeyaml/components/sensor/ms5611.py +++ b/esphomeyaml/components/sensor/ms5611.py @@ -35,3 +35,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_MS5611' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_PRESSURE])] diff --git a/esphomeyaml/components/sensor/pmsx003.py b/esphomeyaml/components/sensor/pmsx003.py index aa61a00e38..70e3586e87 100644 --- a/esphomeyaml/components/sensor/pmsx003.py +++ b/esphomeyaml/components/sensor/pmsx003.py @@ -85,3 +85,12 @@ def to_code(config): BUILD_FLAGS = '-DUSE_PMSX003' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_PM_1_0, CONF_PM_2_5, CONF_PM_10_0, CONF_TEMPERATURE, CONF_HUMIDITY, + CONF_FORMALDEHYDE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/pulse_counter.py b/esphomeyaml/components/sensor/pulse_counter.py index 575d1a76c4..3b3200ea56 100644 --- a/esphomeyaml/components/sensor/pulse_counter.py +++ b/esphomeyaml/components/sensor/pulse_counter.py @@ -65,3 +65,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/rotary_encoder.py b/esphomeyaml/components/sensor/rotary_encoder.py index d872ed9c69..22c5552bfc 100644 --- a/esphomeyaml/components/sensor/rotary_encoder.py +++ b/esphomeyaml/components/sensor/rotary_encoder.py @@ -49,3 +49,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/sht3xd.py b/esphomeyaml/components/sensor/sht3xd.py index f78d988dc8..25688c31e1 100644 --- a/esphomeyaml/components/sensor/sht3xd.py +++ b/esphomeyaml/components/sensor/sht3xd.py @@ -36,3 +36,8 @@ def to_code(config): BUILD_FLAGS = '-DUSE_SHT3XD' + + +def to_hass_config(data, config): + return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]), + sensor.core_to_hass_config(data, config[CONF_HUMIDITY])] diff --git a/esphomeyaml/components/sensor/tcs34725.py b/esphomeyaml/components/sensor/tcs34725.py index 5da00be172..1e2ac88ac0 100644 --- a/esphomeyaml/components/sensor/tcs34725.py +++ b/esphomeyaml/components/sensor/tcs34725.py @@ -78,3 +78,12 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TCS34725' + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL, CONF_CLEAR_CHANNEL, + CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/template.py b/esphomeyaml/components/sensor/template.py index 6ae6138d5c..8c115a9239 100644 --- a/esphomeyaml/components/sensor/template.py +++ b/esphomeyaml/components/sensor/template.py @@ -27,3 +27,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/tsl2561.py b/esphomeyaml/components/sensor/tsl2561.py index b97919038d..0f973de264 100644 --- a/esphomeyaml/components/sensor/tsl2561.py +++ b/esphomeyaml/components/sensor/tsl2561.py @@ -55,3 +55,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TSL2561' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/ultrasonic.py b/esphomeyaml/components/sensor/ultrasonic.py index 31ba1f4027..75c56090ca 100644 --- a/esphomeyaml/components/sensor/ultrasonic.py +++ b/esphomeyaml/components/sensor/ultrasonic.py @@ -39,3 +39,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/uptime.py b/esphomeyaml/components/sensor/uptime.py index 1abb5ecd91..b58e595e27 100644 --- a/esphomeyaml/components/sensor/uptime.py +++ b/esphomeyaml/components/sensor/uptime.py @@ -20,3 +20,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_UPTIME_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/wifi_signal.py b/esphomeyaml/components/sensor/wifi_signal.py index 93c6172e08..f9b10a0a2f 100644 --- a/esphomeyaml/components/sensor/wifi_signal.py +++ b/esphomeyaml/components/sensor/wifi_signal.py @@ -20,3 +20,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_WIFI_SIGNAL_SENSOR' + + +def to_hass_config(data, config): + return sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/sensor/xiaomi_miflora.py b/esphomeyaml/components/sensor/xiaomi_miflora.py index dd9bd0f68a..888a5c7cf3 100644 --- a/esphomeyaml/components/sensor/xiaomi_miflora.py +++ b/esphomeyaml/components/sensor/xiaomi_miflora.py @@ -45,3 +45,12 @@ def to_code(config): if CONF_BATTERY_LEVEL in config: conf = config[CONF_BATTERY_LEVEL] sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf) + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_TEMPERATURE, CONF_MOISTURE, CONF_ILLUMINANCE, CONF_CONDUCTIVITY, + CONF_BATTERY_LEVEL): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/sensor/xiaomi_mijia.py b/esphomeyaml/components/sensor/xiaomi_mijia.py index add1010d20..50ea4dc7a8 100644 --- a/esphomeyaml/components/sensor/xiaomi_mijia.py +++ b/esphomeyaml/components/sensor/xiaomi_mijia.py @@ -37,3 +37,11 @@ def to_code(config): if CONF_BATTERY_LEVEL in config: conf = config[CONF_BATTERY_LEVEL] sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf) + + +def to_hass_config(data, config): + ret = [] + for key in (CONF_TEMPERATURE, CONF_HUMIDITY, CONF_BATTERY_LEVEL): + if key in config: + ret.append(sensor.core_to_hass_config(data, config[key])) + return ret diff --git a/esphomeyaml/components/switch/__init__.py b/esphomeyaml/components/switch/__init__.py index b5e18497a1..65e650f44e 100644 --- a/esphomeyaml/components/switch/__init__.py +++ b/esphomeyaml/components/switch/__init__.py @@ -1,8 +1,10 @@ import voluptuous as vol from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv -from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL +from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL, \ + CONF_OPTIMISTIC from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component, \ TemplateArguments, get_variable @@ -100,3 +102,14 @@ def switch_turn_on_to_code(config, action_id, arg_type): rhs = var.make_turn_on_action(template_arg) type = TurnOnAction.template(arg_type) yield Pvariable(action_id, rhs, type=type) + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'switch', config, include_state=True, include_command=True) + if ret is None: + return None + if CONF_ICON in config: + ret['icon'] = config[CONF_ICON] + if CONF_OPTIMISTIC in config: + ret['optimistic'] = config[CONF_OPTIMISTIC] + return ret diff --git a/esphomeyaml/components/switch/gpio.py b/esphomeyaml/components/switch/gpio.py index 3ee198ec05..dd58f4bc6e 100644 --- a/esphomeyaml/components/switch/gpio.py +++ b/esphomeyaml/components/switch/gpio.py @@ -29,3 +29,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_GPIO_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/output.py b/esphomeyaml/components/switch/output.py index 818225f74f..05076930b4 100644 --- a/esphomeyaml/components/switch/output.py +++ b/esphomeyaml/components/switch/output.py @@ -23,3 +23,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_OUTPUT_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/remote_transmitter.py b/esphomeyaml/components/switch/remote_transmitter.py index 058e84d44e..9daf53c42e 100644 --- a/esphomeyaml/components/switch/remote_transmitter.py +++ b/esphomeyaml/components/switch/remote_transmitter.py @@ -146,3 +146,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/restart.py b/esphomeyaml/components/switch/restart.py index 5edd01dab9..73f9182429 100644 --- a/esphomeyaml/components/switch/restart.py +++ b/esphomeyaml/components/switch/restart.py @@ -20,3 +20,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_RESTART_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/shutdown.py b/esphomeyaml/components/switch/shutdown.py index 641b84108c..6116136ca4 100644 --- a/esphomeyaml/components/switch/shutdown.py +++ b/esphomeyaml/components/switch/shutdown.py @@ -20,3 +20,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/template.py b/esphomeyaml/components/switch/template.py index a8f110bb6f..0d7f967322 100644 --- a/esphomeyaml/components/switch/template.py +++ b/esphomeyaml/components/switch/template.py @@ -42,3 +42,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/switch/uart.py b/esphomeyaml/components/switch/uart.py index 622b1df0b8..e64f05f1ab 100644 --- a/esphomeyaml/components/switch/uart.py +++ b/esphomeyaml/components/switch/uart.py @@ -43,3 +43,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_UART_SWITCH' + + +def to_hass_config(data, config): + return switch.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/__init__.py b/esphomeyaml/components/text_sensor/__init__.py index e5fd17760c..89c3e2fead 100644 --- a/esphomeyaml/components/text_sensor/__init__.py +++ b/esphomeyaml/components/text_sensor/__init__.py @@ -1,6 +1,7 @@ import voluptuous as vol from esphomeyaml import automation +from esphomeyaml.components import mqtt import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_ON_VALUE, \ CONF_TRIGGER_ID @@ -58,3 +59,12 @@ def register_text_sensor(var, config): BUILD_FLAGS = '-DUSE_TEXT_SENSOR' + + +def core_to_hass_config(data, config): + ret = mqtt.build_hass_config(data, 'sensor', config, include_state=True, include_command=False) + if ret is None: + return None + if CONF_ICON in config: + ret['icon'] = config[CONF_ICON] + return ret diff --git a/esphomeyaml/components/text_sensor/mqtt_subscribe.py b/esphomeyaml/components/text_sensor/mqtt_subscribe.py index 18082d1f6e..a4ba9705c1 100644 --- a/esphomeyaml/components/text_sensor/mqtt_subscribe.py +++ b/esphomeyaml/components/text_sensor/mqtt_subscribe.py @@ -25,3 +25,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/template.py b/esphomeyaml/components/text_sensor/template.py index 8e440b3fa5..3441ee5e6f 100644 --- a/esphomeyaml/components/text_sensor/template.py +++ b/esphomeyaml/components/text_sensor/template.py @@ -28,3 +28,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/components/text_sensor/version.py b/esphomeyaml/components/text_sensor/version.py index 593211c715..7e01302924 100644 --- a/esphomeyaml/components/text_sensor/version.py +++ b/esphomeyaml/components/text_sensor/version.py @@ -17,3 +17,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_VERSION_TEXT_SENSOR' + + +def to_hass_config(data, config): + return text_sensor.core_to_hass_config(data, config) diff --git a/esphomeyaml/dashboard/dashboard.py b/esphomeyaml/dashboard/dashboard.py index 4e8cb96b0b..d12dc0fec8 100644 --- a/esphomeyaml/dashboard/dashboard.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -132,6 +132,13 @@ class EsphomeyamlCleanHandler(EsphomeyamlCommandWebSocket): return ["esphomeyaml", config_file, "clean"] +class EsphomeyamlHassConfigHandler(EsphomeyamlCommandWebSocket): + def build_command(self, message): + js = json.loads(message) + config_file = os.path.join(CONFIG_DIR, js['configuration']) + return ["esphomeyaml", config_file, "hass-config"] + + class SerialPortRequestHandler(BaseHandler): def get(self): if not self.is_authenticated(): @@ -230,6 +237,7 @@ def make_app(debug=False): (r"/validate", EsphomeyamlValidateHandler), (r"/clean-mqtt", EsphomeyamlCleanMqttHandler), (r"/clean", EsphomeyamlCleanHandler), + (r"/hass-config", EsphomeyamlHassConfigHandler), (r"/download.bin", DownloadBinaryRequestHandler), (r"/serial-ports", SerialPortRequestHandler), (r"/wizard.html", WizardRequestHandler), diff --git a/esphomeyaml/dashboard/templates/index.html b/esphomeyaml/dashboard/templates/index.html index ccb231ab9c..9ac93d0a9d 100644 --- a/esphomeyaml/dashboard/templates/index.html +++ b/esphomeyaml/dashboard/templates/index.html @@ -215,6 +215,7 @@ @@ -491,6 +492,18 @@ + + add @@ -904,6 +917,54 @@ }); }); + const hassConfigModalElem = document.getElementById("modal-hass-config"); + + document.querySelectorAll(".action-hass-config").forEach((btn) => { + btn.addEventListener('click', (e) => { + configuration = e.target.getAttribute('data-node'); + const modalInstance = M.Modal.getInstance(hassConfigModalElem); + const log = hassConfigModalElem.querySelector(".log"); + log.innerHTML = ""; + const stopLogsButton = hassConfigModalElem.querySelector(".stop-logs"); + let stopped = false; + stopLogsButton.innerHTML = "Stop"; + modalInstance.open(); + + const filenameField = hassConfigModalElem.querySelector('.filename'); + filenameField.innerHTML = configuration; + + const logSocket = new WebSocket(wsUrl + "/hass-config"); + logSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.event === "line") { + const msg = data.data; + log.innerHTML += colorReplace(msg); + } else if (data.event === "exit") { + if (data.code === 0) { + M.toast({html: "Program exited successfully."}); + downloadButton.classList.remove('disabled'); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } + stopLogsButton.innerHTML = "Close"; + stopped = true; + } + }); + logSocket.addEventListener('open', () => { + const msg = JSON.stringify({configuration: configuration}); + logSocket.send(msg); + }); + logSocket.addEventListener('close', () => { + if (!stopped) { + M.toast({html: 'Terminated process.'}); + } + }); + modalInstance.options.onCloseStart = () => { + logSocket.close(); + }; + }); + }); + const modalSetupElem = document.getElementById("modal-wizard"); const setupWizardStart = document.getElementById('setup-wizard-start'); const startWizard = () => { diff --git a/esphomeyaml/espota2.py b/esphomeyaml/espota2.py index f9b7b21190..a2733c85c7 100755 --- a/esphomeyaml/espota2.py +++ b/esphomeyaml/espota2.py @@ -27,17 +27,24 @@ OTA_VERSION_1_0 = 1 MAGIC_BYTES = [0x6C, 0x26, 0xF7, 0x5C, 0x45] _LOGGER = logging.getLogger(__name__) +LAST_PROGRESS = -1 def update_progress(progress): + global LAST_PROGRESS + bar_length = 60 status = "" if progress >= 1: progress = 1 status = "Done...\r\n" + new_progress = int(progress * 100) + if new_progress == LAST_PROGRESS: + return + LAST_PROGRESS = new_progress block = int(round(bar_length * progress)) text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block), - int(progress * 100), status) + new_progress, status) sys.stderr.write(text) sys.stderr.flush() From 8a58ff91c341690136fa0be24c6e83a00e04bbe3 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 3 Nov 2018 16:24:26 +0100 Subject: [PATCH 4/5] Update Gitlab Build Script (#215) --- .gitlab-ci.yml | 166 +++++++++++++----- Dockerfile | 27 +-- docker/Dockerfile.aarch64 | 21 --- docker/Dockerfile.amd64 | 21 --- docker/Dockerfile.armhf | 31 ---- docker/Dockerfile.builder | 2 - docker/Dockerfile.hassio | 44 +++++ docker/Dockerfile.i386 | 21 --- docker/hassio-builder.sh | 318 ---------------------------------- docker/platformio-esp8266.ini | 7 - esphomeyaml-beta/config.json | 2 +- esphomeyaml-edge/Dockerfile | 59 ------- esphomeyaml-edge/build.json | 8 +- 13 files changed, 187 insertions(+), 540 deletions(-) delete mode 100644 docker/Dockerfile.aarch64 delete mode 100644 docker/Dockerfile.amd64 delete mode 100644 docker/Dockerfile.armhf create mode 100644 docker/Dockerfile.hassio delete mode 100644 docker/Dockerfile.i386 delete mode 100755 docker/hassio-builder.sh delete mode 100644 docker/platformio-esp8266.ini delete mode 100644 esphomeyaml-edge/Dockerfile diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5e03f1a67a..222d534cf6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,55 +61,84 @@ test2: <<: *docker-builder stage: build script: + - docker run --rm --privileged hassioaddons/qemu-user-static:latest + - BUILD_FROM=homeassistant/${ADDON_ARCH}-base-ubuntu:latest + - ADDON_VERSION="${CI_COMMIT_TAG#v}" + - ADDON_VERSION="${ADDON_VERSION:-${CI_COMMIT_SHA:0:7}}" + - ESPHOMELIB_VERSION="${ESPHOMELIB_VERSION:-''}" + - echo "Build from ${BUILD_FROM}" + - echo "Add-on version ${ADDON_VERSION}" + - echo "Esphomelib version ${ESPHOMELIB_VERSION}" + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" + - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - | - hassio-builder.sh \ - -t . \ - -i ottowinter/esphomeyaml-hassio-${ADDON_ARCH} \ - -d "$CI_REGISTRY" \ - --${ADDON_ARCH} + docker build \ + --build-arg "BUILD_FROM=${BUILD_FROM}" \ + --build-arg "ADDON_ARCH=${ADDON_ARCH}" \ + --build-arg "ADDON_VERSION=${ADDON_VERSION}" \ + --build-arg "ESPHOMELIB_VERSION=${ESPHOMELIB_VERSION}" \ + --tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" \ + --tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + --file "docker/Dockerfile.hassio" \ + . - | - docker tag \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" \ - "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" - retry: 2 + if [ "${DO_PUSH:-true}" = true ]; then + echo "Pushing to CI registry" + docker push ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA} + docker push ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev + fi # Generic deploy template .deploy-release: &deploy-release <<: *docker-builder stage: deploy script: - - version=${CI_COMMIT_TAG:1} + - version="${CI_COMMIT_TAG#v}" - echo "Publishing release version ${version}" - - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" + - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" + + - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - | + docker tag \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - | docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + + - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - | docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - | docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - | docker tag \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - | docker tag \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest" - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" only: - /^v\d+\.\d+\.\d+$/ except: @@ -119,24 +148,37 @@ test2: <<: *docker-builder stage: deploy script: - - version=${CI_COMMIT_TAG:1} + - version="${CI_COMMIT_TAG#v}" - echo "Publishing beta version ${version}" - - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" + - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" + + - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - | + docker tag \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - | docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - | docker tag \ "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" + + - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - | docker tag \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \ "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta" - - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc" only: - /^v\d+\.\d+\.\d+b\d+$/ @@ -149,30 +191,64 @@ build:normal: stage: build script: - docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" . - - | - docker tag \ - "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" \ - "${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}" - - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" -build:armhf: +.build-hassio-edge: &build-hassio-edge <<: *build-hassio + except: + - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+b\d+$/ + +.build-hassio-release: &build-hassio-release + <<: *build-hassio + only: + - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+b\d+$/ + +build:hassio-armhf-edge: + <<: *build-hassio-edge + variables: + ADDON_ARCH: armhf + DO_PUSH: "false" + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +build:hassio-armhf: + <<: *build-hassio-release variables: ADDON_ARCH: armhf -#build:aarch64: -# <<: *build -# variables: -# ADDON_ARCH: aarch64 +build:hassio-aarch64-edge: + <<: *build-hassio-edge + variables: + ADDON_ARCH: aarch64 + DO_PUSH: "false" + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" -build:i386: - <<: *build-hassio +build:hassio-aarch64: + <<: *build-hassio-release + variables: + ADDON_ARCH: aarch64 + +build:hassio-i386-edge: + <<: *build-hassio-edge + variables: + ADDON_ARCH: i386 + DO_PUSH: "false" + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +build:hassio-i386: + <<: *build-hassio-release variables: ADDON_ARCH: i386 -build:amd64: - <<: *build-hassio +build:hassio-amd64-edge: + <<: *build-hassio-edge + variables: + ADDON_ARCH: amd64 + DO_PUSH: "false" + ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}" + +build:hassio-amd64: + <<: *build-hassio-release variables: ADDON_ARCH: amd64 @@ -187,15 +263,15 @@ deploy-beta:armhf: variables: ADDON_ARCH: armhf -#deploy-release:aarch64: -# <<: *deploy-release -# variables: -# ADDON_ARCH: aarch64 +deploy-release:aarch64: + <<: *deploy-release + variables: + ADDON_ARCH: aarch64 -#deploy-beta:aarch64: -# <<: *deploy-beta -# variables: -# ADDON_ARCH: aarch64 +deploy-beta:aarch64: + <<: *deploy-beta + variables: + ADDON_ARCH: aarch64 deploy-release:i386: <<: *deploy-release diff --git a/Dockerfile b/Dockerfile index 81eefcd244..2c3d4da606 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,32 @@ -FROM python:2.7 +ARG BUILD_FROM=python:2.7 +FROM ${BUILD_FROM} MAINTAINER Otto Winter RUN apt-get update && apt-get install -y \ python-pil \ - && rm -rf /var/lib/apt/lists/* + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \ + pip install --no-cache-dir --no-binary :all: platformio && \ + platformio settings set enable_telemetry No && \ + platformio settings set check_libraries_interval 1000000 && \ + platformio settings set check_platformio_interval 1000000 && \ + platformio settings set check_platforms_interval 1000000 ENV ESPHOMEYAML_OTA_HOST_PORT=6123 EXPOSE 6123 VOLUME /config WORKDIR /usr/src/app -RUN pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /usr/src/app/ -RUN platformio settings set enable_telemetry No && \ - platformio run -e espressif32 -e espressif8266; exit 0 +COPY docker/platformio.ini /pio/platformio.ini +ARG ESPHOMELIB_VERSION="" +RUN platformio run -d /pio; rm -rf /pio && \ + /bin/bash -c "if [ ! -z '$ESPHOMELIB_VERSION']; then \ + platformio lib -g install '${ESPHOMELIB_VERSION}'; \ + fi" COPY . . -RUN pip install --no-cache-dir -e . && \ - pip install --no-cache-dir tzlocal pillow +RUN pip install --no-cache-dir --no-binary :all: -e . && \ + pip install --no-cache-dir --no-binary :all: tzlocal WORKDIR /config ENTRYPOINT ["esphomeyaml"] diff --git a/docker/Dockerfile.aarch64 b/docker/Dockerfile.aarch64 deleted file mode 100644 index e61ff0f92c..0000000000 --- a/docker/Dockerfile.aarch64 +++ /dev/null @@ -1,21 +0,0 @@ -# Dockerfile for aarch64 version of HassIO add-on -FROM arm64v8/ubuntu:bionic - -RUN apt-get update && apt-get install -y --no-install-recommends \ - python \ - python-pip \ - python-setuptools \ - python-pil \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir --no-binary :all: tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/Dockerfile.amd64 b/docker/Dockerfile.amd64 deleted file mode 100644 index 694e7c6762..0000000000 --- a/docker/Dockerfile.amd64 +++ /dev/null @@ -1,21 +0,0 @@ -# Dockerfile for amd64 version of HassIO add-on -FROM ubuntu:bionic - -RUN apt-get update && apt-get install -y --no-install-recommends \ - python \ - python-pip \ - python-setuptools \ - python-pil \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir --no-binary :all: tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf deleted file mode 100644 index 3b2cfedf8f..0000000000 --- a/docker/Dockerfile.armhf +++ /dev/null @@ -1,31 +0,0 @@ -# Dockerfile for armhf version of HassIO add-on -FROM homeassistant/armhf-base:latest - -RUN apk add --no-cache \ - python2 \ - python2-dev \ - py2-pip \ - git \ - gcc \ - openssh \ - libc6-compat \ - jpeg-dev \ - zlib-dev \ - freetype-dev \ - lcms2-dev \ - openjpeg-dev \ - tiff-dev \ - libc-dev \ - linux-headers \ - && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio-esp8266.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir pillow tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/Dockerfile.builder b/docker/Dockerfile.builder index d8148da3bd..2077b7bde1 100644 --- a/docker/Dockerfile.builder +++ b/docker/Dockerfile.builder @@ -27,6 +27,4 @@ RUN apt-get update && apt-get install -y \ binfmt-support \ && rm -rf /var/lib/apt/lists/* -COPY docker/hassio-builder.sh /usr/bin/ - WORKDIR /data diff --git a/docker/Dockerfile.hassio b/docker/Dockerfile.hassio new file mode 100644 index 0000000000..b0241f2d58 --- /dev/null +++ b/docker/Dockerfile.hassio @@ -0,0 +1,44 @@ +# Dockerfile for HassIO add-on +ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest +FROM ${BUILD_FROM} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python \ + python-pip \ + python-setuptools \ + python-pil \ + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \ + pip install --no-cache-dir --no-binary :all: platformio && \ + platformio settings set enable_telemetry No && \ + platformio settings set check_libraries_interval 1000000 && \ + platformio settings set check_platformio_interval 1000000 && \ + platformio settings set check_platforms_interval 1000000 + +COPY docker/platformio.ini /pio/platformio.ini +ARG ESPHOMELIB_VERSION="" +RUN platformio run -d /pio; rm -rf /pio && \ + /bin/bash -c "if [ ! -z '$ESPHOMELIB_VERSION']; then \ + platformio lib -g install '${ESPHOMELIB_VERSION}'; \ + fi" + + +COPY . . +RUN pip install --no-cache-dir --no-binary :all: -e . && \ + pip install --no-cache-dir --no-binary :all: tzlocal + +CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] + +# Build arugments +ARG ADDON_ARCH +ARG ADDON_VERSION + +# Labels +LABEL \ + io.hass.name="esphomeyaml" \ + io.hass.description="esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices." \ + io.hass.arch="${ADDON_ARCH}" \ + io.hass.type="addon" \ + io.hass.version="${ADDON_VERSION}" \ + io.hass.url="https://esphomelib.com/esphomeyaml/index.html" \ + maintainer="Otto Winter " diff --git a/docker/Dockerfile.i386 b/docker/Dockerfile.i386 deleted file mode 100644 index 06ef39f6ef..0000000000 --- a/docker/Dockerfile.i386 +++ /dev/null @@ -1,21 +0,0 @@ -# Dockerfile for i386 version of HassIO add-on -FROM i386/ubuntu:bionic - -RUN apt-get update && apt-get install -y --no-install-recommends \ - python \ - python-pip \ - python-setuptools \ - python-pil \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \ - pip install --no-cache-dir --no-binary :all: platformio && \ - platformio settings set enable_telemetry No - -COPY docker/platformio.ini /pio/platformio.ini -RUN platformio run -d /pio; rm -rf /pio - -COPY . . -RUN pip install --no-cache-dir --no-binary :all: -e . && \ - pip install --no-cache-dir --no-binary :all: tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/docker/hassio-builder.sh b/docker/hassio-builder.sh deleted file mode 100755 index 699e028748..0000000000 --- a/docker/hassio-builder.sh +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/env bash -# Based on Home Assistant's docker builder -###################### -# Hass.io Build-env -###################### -set -e - -echo -- "$@" - -#### Variable #### - -DOCKER_TIMEOUT=20 -DOCKER_PID=-1 -DOCKER_HUB="" -DOCKER_CACHE="true" -DOCKER_LOCAL="false" -TARGET="" -IMAGE="" -BUILD_LIST=() -BUILD_TASKS=() - -#### Misc functions #### - -function print_help() { - cat << EOF -Hass.io build-env for ecosystem: -docker run --rm homeassistant/{arch}-builder:latest [options] - -Options: - -h, --help - Display this help and exit. - - Repository / Data - -t, --target - Set local folder or path inside repository for build. - - Version/Image handling - -i, --image - Overwrite image name of build / support {arch} - - Architecture - --armhf - Build for arm. - --amd64 - Build for intel/amd 64bit. - --aarch64 - Build for arm 64bit. - --i386 - Build for intel/amd 32bit. - --all - Build all architecture. - - Build handling - --no-cache - Disable cache for the build (from latest). - -d, --docker-hub - Set or overwrite the docker repository. - - Use the host docker socket if mapped into container: - /var/run/docker.sock - -EOF - - exit 1 -} - -#### Docker functions #### - -function start_docker() { - local starttime - local endtime - - if [ -S "/var/run/docker.sock" ]; then - echo "[INFO] Use host docker setup with '/var/run/docker.sock'" - DOCKER_LOCAL="true" - return 0 - fi - - echo "[INFO] Starting docker." - dockerd 2> /dev/null & - DOCKER_PID=$! - - echo "[INFO] Waiting for docker to initialize..." - starttime="$(date +%s)" - endtime="$(date +%s)" - until docker info >/dev/null 2>&1; do - if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then - sleep 1 - endtime=$(date +%s) - else - echo "[ERROR] Timeout while waiting for docker to come up" - exit 1 - fi - done - echo "[INFO] Docker was initialized" -} - - -function stop_docker() { - local starttime - local endtime - - if [ "$DOCKER_LOCAL" == "true" ]; then - return 0 - fi - - echo "[INFO] Stopping in container docker..." - if [ "$DOCKER_PID" -gt 0 ] && kill -0 "$DOCKER_PID" 2> /dev/null; then - starttime="$(date +%s)" - endtime="$(date +%s)" - - # Now wait for it to die - kill "$DOCKER_PID" - while kill -0 "$DOCKER_PID" 2> /dev/null; do - if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then - sleep 1 - endtime=$(date +%s) - else - echo "[ERROR] Timeout while waiting for container docker to die" - exit 1 - fi - done - else - echo "[WARN] Your host might have been left with unreleased resources" - fi -} - -function run_build() { - local build_dir=$1 - local repository=$2 - local image=$3 - local version=$4 - local build_arch=$5 - local docker_cli=("${!6}") - - local push_images=() - - # Overwrites - if [ ! -z "$DOCKER_HUB" ]; then repository="$DOCKER_HUB"; fi - if [ ! -z "$IMAGE" ]; then image="$IMAGE"; fi - - # Init Cache - if [ "$DOCKER_CACHE" == "true" ]; then - echo "[INFO] Init cache for $repository/$image:$version" - if docker pull "$repository/$image:latest" > /dev/null 2>&1; then - docker_cli+=("--cache-from" "$repository/$image:latest") - else - docker_cli+=("--no-cache") - echo "[WARN] No cache image found. Cache is disabled for build" - fi - else - docker_cli+=("--no-cache") - fi - - # Build image - echo "[INFO] Run build for $repository/$image:$version" - docker build --pull -t "$repository/$image:$version" \ - --label "io.hass.version=$version" \ - --label "io.hass.arch=$build_arch" \ - -f "$TARGET/docker/Dockerfile.$build_arch" \ - "${docker_cli[@]}" \ - "$build_dir" - - echo "[INFO] Finish build for $repository/$image:$version" - docker tag "$repository/$image:$version" "$repository/$image:dev" -} - - -#### HassIO functions #### - -function build_addon() { - local build_arch=$1 - - local docker_cli=() - local image="" - local repository="" - local raw_image="" - local name="" - local description="" - local url="" - local args="" - - # Read addon config.json - name="$(jq --raw-output '.name // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")" - description="$(jq --raw-output '.description // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")" - url="$(jq --raw-output '.url // empty' "$TARGET/esphomeyaml/config.json")" - version="$(jq --raw-output '.version' "$TARGET/esphomeyaml/config.json")" - raw_image="$(jq --raw-output '.image // empty' "$TARGET/esphomeyaml/config.json")" - - # Read data from image - if [ ! -z "$raw_image" ]; then - repository="$(echo "$raw_image" | cut -f 1 -d '/')" - image="$(echo "$raw_image" | cut -f 2 -d '/')" - fi - - # Set additional labels - docker_cli+=("--label" "io.hass.name=$name") - docker_cli+=("--label" "io.hass.description=$description") - docker_cli+=("--label" "io.hass.type=addon") - - if [ ! -z "$url" ]; then - docker_cli+=("--label" "io.hass.url=$url") - fi - - # Start build - run_build "$TARGET" "$repository" "$image" "$version" \ - "$build_arch" docker_cli[@] -} - -#### initialized cross-build #### - -function init_crosscompile() { - echo "[INFO] Setup crosscompiling feature" - ( - mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc - update-binfmts --enable qemu-arm - update-binfmts --enable qemu-aarch64 - ) > /dev/null 2>&1 || echo "[WARN] Can't enable crosscompiling feature" -} - - -function clean_crosscompile() { - echo "[INFO] Clean crosscompiling feature" - if [ -f /proc/sys/fs/binfmt_misc ]; then - umount /proc/sys/fs/binfmt_misc || true - fi - - ( - update-binfmts --disable qemu-arm - update-binfmts --disable qemu-aarch64 - ) > /dev/null 2>&1 || echo "[WARN] No crosscompiling feature found for cleanup" -} - -#### Error handling #### - -function error_handling() { - stop_docker - clean_crosscompile - - exit 1 -} -trap 'error_handling' SIGINT SIGTERM - -#### Parse arguments #### - -while [[ $# -gt 0 ]]; do - key=$1 - case ${key} in - -h|--help) - print_help - ;; - -t|--target) - TARGET=$2 - shift - ;; - -i|--image) - IMAGE=$2 - shift - ;; - --no-cache) - DOCKER_CACHE="false" - ;; - -d|--docker-hub) - DOCKER_HUB=$2 - shift - ;; - --armhf) - BUILD_LIST+=("armhf") - ;; - --amd64) - BUILD_LIST+=("amd64") - ;; - --i386) - BUILD_LIST+=("i386") - ;; - --aarch64) - BUILD_LIST+=("aarch64") - ;; - --all) - BUILD_LIST=("armhf" "amd64" "i386" "aarch64") - ;; - - *) - echo "[WARN] $0 : Argument '$1' unknown will be Ignoring" - ;; - esac - shift -done - -# Check if an architecture is available -if [ "${#BUILD_LIST[@]}" -eq 0 ]; then - echo "[ERROR] You need select an architecture for build!" - exit 1 -fi - - -#### Main #### - -mkdir -p /data - -# Setup docker env -init_crosscompile -start_docker - -# Select arch build -for arch in "${BUILD_LIST[@]}"; do - (build_addon "$arch") & - BUILD_TASKS+=($!) -done - -# Wait until all build jobs are done -wait "${BUILD_TASKS[@]}" - -# Cleanup docker env -clean_crosscompile -stop_docker - -exit 0 diff --git a/docker/platformio-esp8266.ini b/docker/platformio-esp8266.ini deleted file mode 100644 index ed48f777cf..0000000000 --- a/docker/platformio-esp8266.ini +++ /dev/null @@ -1,7 +0,0 @@ -; This file allows the docker build file to install the required platformio -; platforms - -[env:espressif8266] -platform = espressif8266 -board = nodemcuv2 -framework = arduino diff --git a/esphomeyaml-beta/config.json b/esphomeyaml-beta/config.json index c0eeb19981..36afa8ab14 100644 --- a/esphomeyaml-beta/config.json +++ b/esphomeyaml-beta/config.json @@ -3,7 +3,7 @@ "version": "1.9.0b2", "slug": "esphomeyaml-beta", "description": "Beta version of esphomeyaml HassIO add-on.", - "url": "https://esphomelib.com/esphomeyaml/index.html", + "url": "https://beta.esphomelib.com/esphomeyaml/index.html", "startup": "application", "webui": "http://[HOST]:[PORT:6052]", "boot": "auto", diff --git a/esphomeyaml-edge/Dockerfile b/esphomeyaml-edge/Dockerfile deleted file mode 100644 index be1babac3b..0000000000 --- a/esphomeyaml-edge/Dockerfile +++ /dev/null @@ -1,59 +0,0 @@ -# Dockerfile for HassIO add-on -ARG BUILD_FROM=ubuntu:bionic -FROM ${BUILD_FROM} - -# Re-declare BUILD_FROM to fix weird docker issue -ARG BUILD_FROM - -# On amd64 and alike, using ubuntu as the base is better as building -# for the ESP32 only works with glibc (and ubuntu). However, on armhf -# the build toolchain frequently procudes segfaults under ubuntu. -# -> Use ubuntu for most architectures, except alpine for armhf -# -# * python and related required because this is a python project -# * git required for platformio library dependencies downloads -# * libc6-compat and openssh required on alpine for weird reasons -# * disable platformio telemetry on install -RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \ - apt-get update && apt-get install -y --no-install-recommends \ - python python-pip python-setuptools python-pil git && \ - rm -rf /var/lib/apt/lists/* /tmp/*; \ - else \ - apk add --no-cache \ - python2 \ - python2-dev \ - py2-pip \ - git \ - gcc \ - openssh \ - libc6-compat \ - jpeg-dev \ - zlib-dev \ - freetype-dev \ - lcms2-dev \ - openjpeg-dev \ - tiff-dev \ - libc-dev \ - linux-headers; \ - fi" && \ - pip install --no-cache-dir platformio && \ - platformio settings set enable_telemetry No - - -# Create fake project to make platformio install all depdencies. -# * Ignore build errors from platformio - empty project -# * On alpine, only install ESP8266 toolchain -COPY platformio.ini /pio/platformio.ini -RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \ - platformio run -e espressif32 -e espressif8266 -d /pio; exit 0; \ - else \ - echo \"\$(head -8 /pio/platformio.ini)\" >/pio/platformio.ini; \ - platformio run -e espressif8266 -d /pio; exit 0; \ - fi" - -# Install latest esphomeyaml from git -RUN pip install --no-cache-dir \ - git+git://github.com/OttoWinter/esphomeyaml.git && \ - pip install --no-cache-dir pillow tzlocal - -CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"] diff --git a/esphomeyaml-edge/build.json b/esphomeyaml-edge/build.json index a306cb0cc3..f60fa4f7fe 100644 --- a/esphomeyaml-edge/build.json +++ b/esphomeyaml-edge/build.json @@ -1,10 +1,10 @@ { "squash": false, "build_from": { - "aarch64": "arm64v8/ubuntu:bionic", - "amd64": "ubuntu:bionic", - "armhf": "homeassistant/armhf-base:latest", - "i386": "i386/ubuntu:bionic" + "aarch64": "homeassistant/aarch64-base-ubuntu:latest", + "amd64": "homeassistant/amd64-base-ubuntu:latest", + "armhf": "homeassistant/armhf-base-ubuntu:latest", + "i386": "homeassistant/i386-base-ubuntu:latest" }, "args": {} } From 611592170b4a0eaa4cbfdb829fc730fc937e9713 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 3 Nov 2018 17:10:56 +0100 Subject: [PATCH 5/5] Fix esphomeyaml-edge --- esphomeyaml-edge/Dockerfile | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 esphomeyaml-edge/Dockerfile diff --git a/esphomeyaml-edge/Dockerfile b/esphomeyaml-edge/Dockerfile new file mode 100644 index 0000000000..6c81bcaf29 --- /dev/null +++ b/esphomeyaml-edge/Dockerfile @@ -0,0 +1,24 @@ +# Dockerfile for HassIO edge add-on +ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest +FROM ${BUILD_FROM} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python \ + python-pip \ + python-setuptools \ + python-pil \ + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \ + pip install --no-cache-dir --no-binary :all: platformio && \ + platformio settings set enable_telemetry No && \ + platformio settings set check_libraries_interval 1000000 && \ + platformio settings set check_platformio_interval 1000000 && \ + platformio settings set check_platforms_interval 1000000 + +COPY platformio.ini /pio/platformio.ini +RUN platformio run -d /pio; rm -rf /pio + +RUN pip install --no-cache-dir git+https://github.com/OttoWinter/esphomeyaml.git@dev#egg=esphomeyaml && \ + pip install --no-cache-dir pillow tzlocal + +CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]