mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			51 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6a823f5777 | ||
| 
						 | 
					419c5afe27 | ||
| 
						 | 
					17798dee1e | ||
| 
						 | 
					ee2c53585f | ||
| 
						 | 
					2f05acfa5a | ||
| 
						 | 
					f4d393a59e | ||
| 
						 | 
					967aa53bad | ||
| 
						 | 
					4f3f460105 | ||
| 
						 | 
					6e85a741ae | ||
| 
						 | 
					2db45898e2 | ||
| 
						 | 
					eb62599a98 | ||
| 
						 | 
					9f0737e5b9 | ||
| 
						 | 
					7a393c1a3a | ||
| 
						 | 
					4fa7bc196a | ||
| 
						 | 
					65d0dd47f3 | ||
| 
						 | 
					d88634b196 | ||
| 
						 | 
					976627eb38 | ||
| 
						 | 
					5b995c0692 | ||
| 
						 | 
					2d4b475951 | ||
| 
						 | 
					93d962dd43 | ||
| 
						 | 
					2e7d8540fb | ||
| 
						 | 
					677fe8bacf | ||
| 
						 | 
					94d7ac4ef0 | ||
| 
						 | 
					ebb6d0d464 | ||
| 
						 | 
					e8fe653140 | ||
| 
						 | 
					374ea7044c | ||
| 
						 | 
					061798839d | ||
| 
						 | 
					b9b09a1763 | ||
| 
						 | 
					61b3ead2df | ||
| 
						 | 
					48e42cf478 | ||
| 
						 | 
					1a9ff55a61 | ||
| 
						 | 
					e04285581c | ||
| 
						 | 
					9af30061cb | ||
| 
						 | 
					19929fafa5 | ||
| 
						 | 
					3a9febaf85 | ||
| 
						 | 
					ebb5991889 | ||
| 
						 | 
					f18f8444c7 | ||
| 
						 | 
					10607f2a51 | ||
| 
						 | 
					d3ac5bfb27 | ||
| 
						 | 
					4b9bb2b731 | ||
| 
						 | 
					8639eb1b27 | ||
| 
						 | 
					9979ee6ddf | ||
| 
						 | 
					ee502a7aaa | ||
| 
						 | 
					dc516f7537 | ||
| 
						 | 
					44f2b582b5 | ||
| 
						 | 
					262855ff62 | ||
| 
						 | 
					eec163644d | ||
| 
						 | 
					e65a4d50e5 | ||
| 
						 | 
					a88aad2179 | ||
| 
						 | 
					49736c8c6d | ||
| 
						 | 
					7915e420f4 | 
@@ -4,7 +4,7 @@ python:
 | 
			
		||||
  - "2.7"
 | 
			
		||||
install:
 | 
			
		||||
  - pip install -r requirements.txt
 | 
			
		||||
  - pip install flake8==3.5.0 pylint==1.8.4
 | 
			
		||||
  - pip install tornado esptool flake8==3.5.0 pylint==1.8.4
 | 
			
		||||
script:
 | 
			
		||||
 - flake8 esphomeyaml
 | 
			
		||||
 - pylint esphomeyaml
 | 
			
		||||
 
 | 
			
		||||
@@ -7,16 +7,13 @@ VOLUME /config
 | 
			
		||||
WORKDIR /usr/src/app
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt /usr/src/app/
 | 
			
		||||
RUN pip install --no-cache-dir -r requirements.txt
 | 
			
		||||
RUN pip install --no-cache-dir -r requirements.txt && \
 | 
			
		||||
    pip install --no-cache-dir tornado esptool
 | 
			
		||||
 | 
			
		||||
COPY docker/platformio.ini /usr/src/app/
 | 
			
		||||
RUN platformio settings set enable_telemetry No && \
 | 
			
		||||
    platformio run -e espressif32 -e espressif8266; exit 0
 | 
			
		||||
 | 
			
		||||
# Fix issue with static IP on ESP32: https://github.com/espressif/arduino-esp32/issues/1081
 | 
			
		||||
RUN curl https://github.com/espressif/arduino-esp32/commit/144480637a718844b8f48f4392da8d4f622f2e5e.patch | \
 | 
			
		||||
    patch /root/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiGeneric.cpp
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN pip install -e .
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
include README.md
 | 
			
		||||
include esphomeyaml/dashboard/templates/index.html
 | 
			
		||||
include esphomeyaml/dashboard/static/materialize-stepper.min.css
 | 
			
		||||
include esphomeyaml/dashboard/static/materialize-stepper.min.js
 | 
			
		||||
							
								
								
									
										43
									
								
								esphomeyaml-edge/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphomeyaml-edge/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
# 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 git && \
 | 
			
		||||
      rm -rf /var/lib/apt/lists/* /tmp/*; \
 | 
			
		||||
    else \
 | 
			
		||||
       apk add --no-cache python2 py2-pip git openssh libc6-compat; \
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
 | 
			
		||||
							
								
								
									
										10
									
								
								esphomeyaml-edge/build.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								esphomeyaml-edge/build.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
    "squash": false,
 | 
			
		||||
    "build_from": {
 | 
			
		||||
        "aarch64": "arm64v8/ubuntu:bionic",
 | 
			
		||||
        "amd64": "ubuntu:bionic",
 | 
			
		||||
        "armhf": "homeassistant/armhf-base:latest",
 | 
			
		||||
        "i386": "i386/ubuntu:bionic"
 | 
			
		||||
    },
 | 
			
		||||
    "args": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								esphomeyaml-edge/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphomeyaml-edge/config.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "esphomeyaml-edge",
 | 
			
		||||
  "version": "dev",
 | 
			
		||||
  "slug": "esphomeyaml-edge",
 | 
			
		||||
  "description": "Development build of the esphomeyaml HassIO add-on.",
 | 
			
		||||
  "url": "https://esphomelib.com/esphomeyaml/index.html",
 | 
			
		||||
  "startup": "application",
 | 
			
		||||
  "webui": "http://[HOST]:[PORT:6052]",
 | 
			
		||||
  "boot": "auto",
 | 
			
		||||
  "ports": {
 | 
			
		||||
    "6052/tcp": 6052,
 | 
			
		||||
    "6053/tcp": 6053
 | 
			
		||||
  },
 | 
			
		||||
  "arch": [
 | 
			
		||||
    "aarch64",
 | 
			
		||||
    "amd64",
 | 
			
		||||
    "armhf",
 | 
			
		||||
    "i386"
 | 
			
		||||
  ],
 | 
			
		||||
  "auto_uart": true,
 | 
			
		||||
  "map": [
 | 
			
		||||
    "config:rw"
 | 
			
		||||
  ],
 | 
			
		||||
  "options": {},
 | 
			
		||||
  "environment": {
 | 
			
		||||
    "ESPHOMEYAML_OTA_HOST_PORT": "6053"
 | 
			
		||||
  },
 | 
			
		||||
  "schema": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								esphomeyaml-edge/platformio.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphomeyaml-edge/platformio.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
; This file allows the docker build file to install the required platformio
 | 
			
		||||
; platforms
 | 
			
		||||
 | 
			
		||||
[env:espressif8266]
 | 
			
		||||
platform = espressif8266
 | 
			
		||||
board = nodemcuv2
 | 
			
		||||
framework = arduino
 | 
			
		||||
 | 
			
		||||
[env:espressif32]
 | 
			
		||||
platform = espressif32
 | 
			
		||||
board = nodemcu-32s
 | 
			
		||||
framework = arduino
 | 
			
		||||
							
								
								
									
										44
									
								
								esphomeyaml/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphomeyaml/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
# 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
 | 
			
		||||
ARG BUILD_VERSION
 | 
			
		||||
 | 
			
		||||
# 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 git && \
 | 
			
		||||
      rm -rf /var/lib/apt/lists/* /tmp/*; \
 | 
			
		||||
    else \
 | 
			
		||||
       apk add --no-cache python2 py2-pip git openssh libc6-compat; \
 | 
			
		||||
    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 \
 | 
			
		||||
      esphomeyaml==${BUILD_VERSION}
 | 
			
		||||
 | 
			
		||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
 | 
			
		||||
@@ -5,13 +5,15 @@ import logging
 | 
			
		||||
import os
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const
 | 
			
		||||
from esphomeyaml import const, core, mqtt, wizard, writer, yaml_util
 | 
			
		||||
from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
 | 
			
		||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOGGER, \
 | 
			
		||||
    CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI
 | 
			
		||||
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \
 | 
			
		||||
    color, get_variable, indent, quote, statement, Expression
 | 
			
		||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_DOMAIN, CONF_ESPHOMEYAML, CONF_HOSTNAME, \
 | 
			
		||||
    CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, 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
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -26,28 +28,27 @@ def get_base_path(config):
 | 
			
		||||
    return os.path.join(os.path.dirname(core.CONFIG_PATH), get_name(config))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def discover_serial_ports():
 | 
			
		||||
def get_serial_ports():
 | 
			
		||||
    # from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
 | 
			
		||||
    try:
 | 
			
		||||
        from serial.tools.list_ports import comports
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    from serial.tools.list_ports import comports
 | 
			
		||||
    result = []
 | 
			
		||||
    descs = []
 | 
			
		||||
    for port, desc, info in comports():
 | 
			
		||||
        if not port:
 | 
			
		||||
            continue
 | 
			
		||||
        if "VID:PID" in info:
 | 
			
		||||
            result.append(port)
 | 
			
		||||
            descs.append(desc)
 | 
			
		||||
            result.append((port, desc))
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def choose_serial_port(config):
 | 
			
		||||
    result = get_serial_ports()
 | 
			
		||||
 | 
			
		||||
    if not result:
 | 
			
		||||
        return None
 | 
			
		||||
        return 'OTA'
 | 
			
		||||
    print(u"Found multiple serial port options, please choose one:")
 | 
			
		||||
    for i, (res, desc) in enumerate(zip(result, descs)):
 | 
			
		||||
    for i, (res, desc) in enumerate(result):
 | 
			
		||||
        print(u"  [{}] {} ({})".format(i, res, desc))
 | 
			
		||||
    print(u"  [{}] Over The Air".format(len(result)))
 | 
			
		||||
    print(u"  [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
 | 
			
		||||
    print()
 | 
			
		||||
    while True:
 | 
			
		||||
        opt = raw_input('(number): ')
 | 
			
		||||
@@ -62,11 +63,11 @@ def discover_serial_ports():
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            print(color('red', u"Invalid option: '{}'".format(opt)))
 | 
			
		||||
    if opt == len(result):
 | 
			
		||||
        return None
 | 
			
		||||
    return result[opt]
 | 
			
		||||
        return 'OTA'
 | 
			
		||||
    return result[opt][0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_platformio(*cmd):
 | 
			
		||||
def run_platformio(*cmd, **kwargs):
 | 
			
		||||
    def mock_exit(return_code):
 | 
			
		||||
        raise SystemExit(return_code)
 | 
			
		||||
 | 
			
		||||
@@ -75,10 +76,13 @@ def run_platformio(*cmd):
 | 
			
		||||
    full_cmd = u' '.join(quote(x) for x in cmd)
 | 
			
		||||
    _LOGGER.info(u"Running:  %s", full_cmd)
 | 
			
		||||
    try:
 | 
			
		||||
        import platformio.__main__
 | 
			
		||||
        func = kwargs.get('main')
 | 
			
		||||
        if func is None:
 | 
			
		||||
            import platformio.__main__
 | 
			
		||||
            func = platformio.__main__.main
 | 
			
		||||
        sys.argv = list(cmd)
 | 
			
		||||
        sys.exit = mock_exit
 | 
			
		||||
        return platformio.__main__.main()
 | 
			
		||||
        return func() or 0
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        return 1
 | 
			
		||||
    except SystemExit as err:
 | 
			
		||||
@@ -91,47 +95,46 @@ def run_platformio(*cmd):
 | 
			
		||||
        sys.exit = orig_exit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_miniterm(config, port):
 | 
			
		||||
    from serial.tools import miniterm
 | 
			
		||||
def run_miniterm(config, port, escape=False):
 | 
			
		||||
    import serial
 | 
			
		||||
    baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
 | 
			
		||||
    sys.argv = ['miniterm', '--raw', '--exit-char', '3']
 | 
			
		||||
    miniterm.main(
 | 
			
		||||
        default_port=port,
 | 
			
		||||
        default_baudrate=baud_rate)
 | 
			
		||||
    _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
 | 
			
		||||
 | 
			
		||||
    with serial.Serial(port, baudrate=baud_rate) as ser:
 | 
			
		||||
        while True:
 | 
			
		||||
            line = ser.readline()
 | 
			
		||||
            time = datetime.now().time().strftime('[%H:%M:%S]')
 | 
			
		||||
            message = time + line.decode('unicode-escape').replace('\r', '').replace('\n', '')
 | 
			
		||||
            if escape:
 | 
			
		||||
                message = message.replace('\033', '\\033').encode('ascii', 'replace')
 | 
			
		||||
            print(message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_cpp(config):
 | 
			
		||||
    _LOGGER.info("Generating C++ source...")
 | 
			
		||||
 | 
			
		||||
    add_task(core_to_code, config[CONF_ESPHOMEYAML])
 | 
			
		||||
    add_job(core_to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml')
 | 
			
		||||
    for domain in PRE_INITIALIZE:
 | 
			
		||||
        if domain == CONF_ESPHOMEYAML:
 | 
			
		||||
        if domain == CONF_ESPHOMEYAML or domain not in config:
 | 
			
		||||
            continue
 | 
			
		||||
        if domain in config:
 | 
			
		||||
            add_task(get_component(domain).to_code, config[domain])
 | 
			
		||||
 | 
			
		||||
    # Clear queue
 | 
			
		||||
    get_variable(None)
 | 
			
		||||
    add(RawStatement(''))
 | 
			
		||||
        add_job(get_component(domain).to_code, config[domain], domain=domain)
 | 
			
		||||
 | 
			
		||||
    for domain, component, conf in iter_components(config):
 | 
			
		||||
        if domain in PRE_INITIALIZE:
 | 
			
		||||
        if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'):
 | 
			
		||||
            continue
 | 
			
		||||
        if not hasattr(component, 'to_code'):
 | 
			
		||||
            continue
 | 
			
		||||
        add_task(component.to_code, conf)
 | 
			
		||||
        add_job(component.to_code, conf, domain=domain)
 | 
			
		||||
 | 
			
		||||
    # Clear queue
 | 
			
		||||
    get_variable(None)
 | 
			
		||||
    flush_tasks()
 | 
			
		||||
    add(RawStatement(''))
 | 
			
		||||
    add(RawStatement(''))
 | 
			
		||||
 | 
			
		||||
    all_code = []
 | 
			
		||||
    for exp in _EXPRESSIONS:
 | 
			
		||||
        if core.SIMPLIFY:
 | 
			
		||||
            if isinstance(exp, Expression) and not exp.required:
 | 
			
		||||
                continue
 | 
			
		||||
            if isinstance(exp, AssignmentExpression) and not exp.obj.required:
 | 
			
		||||
                if not exp.has_side_effects():
 | 
			
		||||
                    continue
 | 
			
		||||
                exp = exp.rhs
 | 
			
		||||
        all_code.append(unicode(statement(exp)))
 | 
			
		||||
 | 
			
		||||
@@ -139,29 +142,57 @@ def write_cpp(config):
 | 
			
		||||
    ini_path = os.path.join(get_base_path(config), 'platformio.ini')
 | 
			
		||||
    writer.write_platformio_ini(platformio_ini_s, ini_path)
 | 
			
		||||
 | 
			
		||||
    code_s = indent('\n'.join(all_code))
 | 
			
		||||
    code_s = indent('\n'.join(line.rstrip() for line in all_code))
 | 
			
		||||
    cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp')
 | 
			
		||||
    writer.write_cpp(code_s, cpp_path)
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def compile_program(config):
 | 
			
		||||
def compile_program(args, config):
 | 
			
		||||
    _LOGGER.info("Compiling app...")
 | 
			
		||||
    return run_platformio('platformio', 'run', '-d', get_base_path(config))
 | 
			
		||||
    command = ['platformio', 'run', '-d', get_base_path(config)]
 | 
			
		||||
    if args.verbose:
 | 
			
		||||
        command.append('-v')
 | 
			
		||||
    return run_platformio(*command)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_upload_host(config):
 | 
			
		||||
    if CONF_MANUAL_IP in config[CONF_WIFI]:
 | 
			
		||||
        host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
 | 
			
		||||
    elif CONF_HOSTNAME in config[CONF_WIFI]:
 | 
			
		||||
        host = config[CONF_WIFI][CONF_HOSTNAME] + config[CONF_WIFI][CONF_DOMAIN]
 | 
			
		||||
    else:
 | 
			
		||||
        host = config[CONF_ESPHOMEYAML][CONF_NAME] + config[CONF_WIFI][CONF_DOMAIN]
 | 
			
		||||
    return host
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_using_esptool(config, port):
 | 
			
		||||
    import esptool
 | 
			
		||||
 | 
			
		||||
    name = get_name(config)
 | 
			
		||||
    path = os.path.join(get_base_path(config), '.pioenvs', name, 'firmware.bin')
 | 
			
		||||
    # 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_program(config, args, port):
 | 
			
		||||
    _LOGGER.info("Uploading binary...")
 | 
			
		||||
    if port is not None:
 | 
			
		||||
        return run_platformio('platformio', 'run', '-d', get_base_path(config),
 | 
			
		||||
                              '-t', 'upload', '--upload-port', port)
 | 
			
		||||
    if port != 'OTA':
 | 
			
		||||
        if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy:
 | 
			
		||||
            return upload_using_esptool(config, port)
 | 
			
		||||
        command = ['platformio', 'run', '-d', get_base_path(config),
 | 
			
		||||
                   '-t', 'upload', '--upload-port', port]
 | 
			
		||||
        if args.verbose:
 | 
			
		||||
            command.append('-v')
 | 
			
		||||
        return run_platformio(*command)
 | 
			
		||||
 | 
			
		||||
    if CONF_MANUAL_IP in config[CONF_WIFI]:
 | 
			
		||||
        host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
 | 
			
		||||
    elif CONF_HOSTNAME in config[CONF_WIFI]:
 | 
			
		||||
        host = config[CONF_WIFI][CONF_HOSTNAME] + u'.local'
 | 
			
		||||
    else:
 | 
			
		||||
        host = config[CONF_ESPHOMEYAML][CONF_NAME] + u'.local'
 | 
			
		||||
    if 'ota' not in config:
 | 
			
		||||
        _LOGGER.error("No serial port found and OTA not enabled. Can't upload!")
 | 
			
		||||
        return -1
 | 
			
		||||
 | 
			
		||||
    host = get_upload_host(config)
 | 
			
		||||
 | 
			
		||||
    from esphomeyaml.components import ota
 | 
			
		||||
    from esphomeyaml import espota
 | 
			
		||||
@@ -177,19 +208,21 @@ def upload_program(config, args, port):
 | 
			
		||||
    return espota.main(espota_args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def show_logs(config, args, port):
 | 
			
		||||
    if port is not None and port != 'OTA':
 | 
			
		||||
        run_miniterm(config, port)
 | 
			
		||||
def show_logs(config, args, port, escape=False):
 | 
			
		||||
    if port != 'OTA':
 | 
			
		||||
        run_miniterm(config, port, escape=escape)
 | 
			
		||||
        return 0
 | 
			
		||||
    return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
 | 
			
		||||
    return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
 | 
			
		||||
                          escape=escape)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clean_mqtt(config, args):
 | 
			
		||||
    return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_log():
 | 
			
		||||
    logging.basicConfig(level=logging.INFO)
 | 
			
		||||
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"
 | 
			
		||||
    colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
 | 
			
		||||
    datefmt = '%H:%M:%S'
 | 
			
		||||
@@ -214,11 +247,121 @@ def setup_log():
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    setup_log()
 | 
			
		||||
def command_wizard(args):
 | 
			
		||||
    return wizard.wizard(args.configuration)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def strip_default_ids(config):
 | 
			
		||||
    value = config
 | 
			
		||||
    if isinstance(config, list):
 | 
			
		||||
        value = type(config)()
 | 
			
		||||
        for x in config:
 | 
			
		||||
            if isinstance(x, core.ID) and not x.is_manual:
 | 
			
		||||
                continue
 | 
			
		||||
            value.append(strip_default_ids(x))
 | 
			
		||||
        return value
 | 
			
		||||
    elif isinstance(config, dict):
 | 
			
		||||
        value = type(config)()
 | 
			
		||||
        for k, v in config.iteritems():
 | 
			
		||||
            if isinstance(v, core.ID) and not v.is_manual:
 | 
			
		||||
                continue
 | 
			
		||||
            value[k] = strip_default_ids(v)
 | 
			
		||||
        return value
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_config(args, config):
 | 
			
		||||
    if not args.verbose:
 | 
			
		||||
        config = strip_default_ids(config)
 | 
			
		||||
    print(yaml_util.dump(config))
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_compile(args, config):
 | 
			
		||||
    exit_code = write_cpp(config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    exit_code = compile_program(args, config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully compiled program.")
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_upload(args, config):
 | 
			
		||||
    port = args.upload_port or choose_serial_port(config)
 | 
			
		||||
    exit_code = upload_program(config, args, port)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully uploaded program.")
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_logs(args, config):
 | 
			
		||||
    port = args.serial_port or choose_serial_port(config)
 | 
			
		||||
    return show_logs(config, args, port, escape=args.escape)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_run(args, config):
 | 
			
		||||
    exit_code = write_cpp(config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    exit_code = compile_program(args, config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully compiled program.")
 | 
			
		||||
    port = args.upload_port or choose_serial_port(config)
 | 
			
		||||
    exit_code = upload_program(config, args, port)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully uploaded program.")
 | 
			
		||||
    if args.no_logs:
 | 
			
		||||
        return 0
 | 
			
		||||
    return show_logs(config, args, port, escape=args.escape)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_clean_mqtt(args, config):
 | 
			
		||||
    return clean_mqtt(config, args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_mqtt_fingerprint(args, config):
 | 
			
		||||
    return mqtt.get_fingerprint(config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_version(args):
 | 
			
		||||
    print(u"Version: {}".format(const.__version__))
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_dashboard(args):
 | 
			
		||||
    from esphomeyaml.dashboard import dashboard
 | 
			
		||||
 | 
			
		||||
    return dashboard.start_web_server(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PRE_CONFIG_ACTIONS = {
 | 
			
		||||
    'wizard': command_wizard,
 | 
			
		||||
    'version': command_version,
 | 
			
		||||
    'dashboard': command_dashboard
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
POST_CONFIG_ACTIONS = {
 | 
			
		||||
    'config': command_config,
 | 
			
		||||
    'compile': command_compile,
 | 
			
		||||
    'upload': command_upload,
 | 
			
		||||
    'logs': command_logs,
 | 
			
		||||
    'run': command_run,
 | 
			
		||||
    'clean-mqtt': command_clean_mqtt,
 | 
			
		||||
    'mqtt-fingerprint': command_mqtt_fingerprint,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_args(argv):
 | 
			
		||||
    parser = argparse.ArgumentParser(prog='esphomeyaml')
 | 
			
		||||
    parser.add_argument('-v', '--verbose', help="Enable verbose esphomeyaml logs.",
 | 
			
		||||
                        action='store_true')
 | 
			
		||||
    parser.add_argument('configuration', help='Your YAML configuration file.')
 | 
			
		||||
 | 
			
		||||
    subparsers = parser.add_subparsers(help='Commands', dest='command')
 | 
			
		||||
    subparsers.required = True
 | 
			
		||||
    subparsers.add_parser('config', help='Validate the configuration and spit it out.')
 | 
			
		||||
@@ -230,6 +373,9 @@ def main():
 | 
			
		||||
    parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
 | 
			
		||||
                                                     "For example /dev/cu.SLAB_USBtoUART.")
 | 
			
		||||
    parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
 | 
			
		||||
    parser_upload.add_argument('--use-esptoolpy',
 | 
			
		||||
                               help="Use esptool.py for the uploading (only for ESP8266)",
 | 
			
		||||
                               action='store_true')
 | 
			
		||||
 | 
			
		||||
    parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
 | 
			
		||||
                                                     'and show all MQTT logs.')
 | 
			
		||||
@@ -239,6 +385,8 @@ def main():
 | 
			
		||||
    parser_logs.add_argument('--client-id', help='Manually set the client id.')
 | 
			
		||||
    parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
 | 
			
		||||
                                                   "For example /dev/cu.SLAB_USBtoUART.")
 | 
			
		||||
    parser_logs.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
 | 
			
		||||
                             action='store_true')
 | 
			
		||||
 | 
			
		||||
    parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
 | 
			
		||||
                                                   'upload it, and start MQTT logs.')
 | 
			
		||||
@@ -251,6 +399,11 @@ def main():
 | 
			
		||||
    parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
 | 
			
		||||
    parser_run.add_argument('--password', help='Manually set the MQTT password for logs.')
 | 
			
		||||
    parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
 | 
			
		||||
    parser_run.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
 | 
			
		||||
                            action='store_true')
 | 
			
		||||
    parser_run.add_argument('--use-esptoolpy',
 | 
			
		||||
                            help="Use esptool.py for the uploading (only for ESP8266)",
 | 
			
		||||
                            action='store_true')
 | 
			
		||||
 | 
			
		||||
    parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
 | 
			
		||||
                                                            "retain messages.")
 | 
			
		||||
@@ -263,12 +416,26 @@ def main():
 | 
			
		||||
                                         "you through setting up esphomeyaml.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
 | 
			
		||||
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
    dashboard = subparsers.add_parser('dashboard',
 | 
			
		||||
                                      help="Create a simple webserver for a dashboard.")
 | 
			
		||||
    dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int,
 | 
			
		||||
                           default=6052)
 | 
			
		||||
 | 
			
		||||
    if args.command == 'wizard':
 | 
			
		||||
        return wizard.wizard(args.configuration)
 | 
			
		||||
    return parser.parse_args(argv[1:])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_esphomeyaml(argv):
 | 
			
		||||
    args = parse_args(argv)
 | 
			
		||||
    setup_log(args.verbose)
 | 
			
		||||
    if args.command in PRE_CONFIG_ACTIONS:
 | 
			
		||||
        try:
 | 
			
		||||
            return PRE_CONFIG_ACTIONS[args.command](args)
 | 
			
		||||
        except ESPHomeYAMLError as e:
 | 
			
		||||
            _LOGGER.error(e)
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
    core.CONFIG_PATH = args.configuration
 | 
			
		||||
 | 
			
		||||
@@ -276,54 +443,25 @@ def main():
 | 
			
		||||
    if config is None:
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    if args.command == 'config':
 | 
			
		||||
        print(yaml_util.dump(config))
 | 
			
		||||
        return 0
 | 
			
		||||
    elif args.command == 'compile':
 | 
			
		||||
        exit_code = write_cpp(config)
 | 
			
		||||
        if exit_code != 0:
 | 
			
		||||
            return exit_code
 | 
			
		||||
        exit_code = compile_program(config)
 | 
			
		||||
        if exit_code != 0:
 | 
			
		||||
            return exit_code
 | 
			
		||||
        _LOGGER.info(u"Successfully compiled program.")
 | 
			
		||||
        return 0
 | 
			
		||||
    elif args.command == 'upload':
 | 
			
		||||
        port = args.upload_port or discover_serial_ports()
 | 
			
		||||
        exit_code = upload_program(config, args, port)
 | 
			
		||||
        if exit_code != 0:
 | 
			
		||||
            return exit_code
 | 
			
		||||
        _LOGGER.info(u"Successfully uploaded program.")
 | 
			
		||||
        return 0
 | 
			
		||||
    elif args.command == 'logs':
 | 
			
		||||
        port = args.serial_port or discover_serial_ports()
 | 
			
		||||
        return show_logs(config, args, port)
 | 
			
		||||
    elif args.command == 'clean-mqtt':
 | 
			
		||||
        return clean_mqtt(config, args)
 | 
			
		||||
    elif args.command == 'mqtt-fingerprint':
 | 
			
		||||
        return mqtt.get_fingerprint(config)
 | 
			
		||||
    elif args.command == 'run':
 | 
			
		||||
        exit_code = write_cpp(config)
 | 
			
		||||
        if exit_code != 0:
 | 
			
		||||
            return exit_code
 | 
			
		||||
        exit_code = compile_program(config)
 | 
			
		||||
        if exit_code != 0:
 | 
			
		||||
            return exit_code
 | 
			
		||||
        _LOGGER.info(u"Successfully compiled program.")
 | 
			
		||||
        port = args.upload_port or discover_serial_ports()
 | 
			
		||||
        exit_code = upload_program(config, args, port)
 | 
			
		||||
        if exit_code != 0:
 | 
			
		||||
            return exit_code
 | 
			
		||||
        _LOGGER.info(u"Successfully uploaded program.")
 | 
			
		||||
        if args.no_logs:
 | 
			
		||||
            return 0
 | 
			
		||||
        return show_logs(config, args, port)
 | 
			
		||||
    elif args.command == 'version':
 | 
			
		||||
        print(u"Version: {}".format(const.__version__))
 | 
			
		||||
        return 0
 | 
			
		||||
    if args.command in POST_CONFIG_ACTIONS:
 | 
			
		||||
        try:
 | 
			
		||||
            return POST_CONFIG_ACTIONS[args.command](args, config)
 | 
			
		||||
        except ESPHomeYAMLError as e:
 | 
			
		||||
            _LOGGER.error(e)
 | 
			
		||||
            return 1
 | 
			
		||||
    print(u"Unknown command {}".format(args.command))
 | 
			
		||||
    return 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    try:
 | 
			
		||||
        return run_esphomeyaml(sys.argv)
 | 
			
		||||
    except ESPHomeYAMLError as e:
 | 
			
		||||
        _LOGGER.error(e)
 | 
			
		||||
        return 1
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    sys.exit(main())
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										409
									
								
								esphomeyaml/automation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										409
									
								
								esphomeyaml/automation.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,409 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import cover, fan
 | 
			
		||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \
 | 
			
		||||
    CONF_BELOW, \
 | 
			
		||||
    CONF_BLUE, CONF_BRIGHTNESS, CONF_CONDITION_ID, CONF_DELAY, CONF_EFFECT, CONF_FLASH_LENGTH, \
 | 
			
		||||
    CONF_GREEN, CONF_ID, CONF_IF, CONF_LAMBDA, CONF_OR, CONF_OSCILLATING, CONF_PAYLOAD, CONF_QOS, \
 | 
			
		||||
    CONF_RANGE, CONF_RED, CONF_RETAIN, CONF_SPEED, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, \
 | 
			
		||||
    CONF_TRIGGER_ID, CONF_WHITE
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, \
 | 
			
		||||
    bool_, esphomelib_ns, float_, get_variable, process_lambda, std_string, templatable, uint32, \
 | 
			
		||||
    uint8, add_job
 | 
			
		||||
 | 
			
		||||
CONF_MQTT_PUBLISH = 'mqtt.publish'
 | 
			
		||||
CONF_LIGHT_TOGGLE = 'light.toggle'
 | 
			
		||||
CONF_LIGHT_TURN_OFF = 'light.turn_off'
 | 
			
		||||
CONF_LIGHT_TURN_ON = 'light.turn_on'
 | 
			
		||||
CONF_SWITCH_TOGGLE = 'switch.toggle'
 | 
			
		||||
CONF_SWITCH_TURN_OFF = 'switch.turn_off'
 | 
			
		||||
CONF_SWITCH_TURN_ON = 'switch.turn_on'
 | 
			
		||||
CONF_COVER_OPEN = 'cover.open'
 | 
			
		||||
CONF_COVER_CLOSE = 'cover.close'
 | 
			
		||||
CONF_COVER_STOP = 'cover.stop'
 | 
			
		||||
CONF_FAN_TOGGLE = 'fan.toggle'
 | 
			
		||||
CONF_FAN_TURN_OFF = 'fan.turn_off'
 | 
			
		||||
CONF_FAN_TURN_ON = 'fan.turn_on'
 | 
			
		||||
 | 
			
		||||
ACTION_KEYS = [CONF_DELAY, CONF_MQTT_PUBLISH, CONF_LIGHT_TOGGLE, CONF_LIGHT_TURN_OFF,
 | 
			
		||||
               CONF_LIGHT_TURN_ON, CONF_SWITCH_TOGGLE, CONF_SWITCH_TURN_OFF, CONF_SWITCH_TURN_ON,
 | 
			
		||||
               CONF_LAMBDA, CONF_COVER_OPEN, CONF_COVER_CLOSE, CONF_COVER_STOP, CONF_FAN_TOGGLE,
 | 
			
		||||
               CONF_FAN_TURN_OFF, CONF_FAN_TURN_ON]
 | 
			
		||||
 | 
			
		||||
ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
 | 
			
		||||
    cv.GenerateID(CONF_ACTION_ID): cv.declare_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_DELAY): cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
    vol.Optional(CONF_MQTT_PUBLISH): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
 | 
			
		||||
        vol.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload),
 | 
			
		||||
        vol.Optional(CONF_QOS): cv.templatable(cv.mqtt_qos),
 | 
			
		||||
        vol.Optional(CONF_RETAIN): cv.templatable(cv.boolean),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_LIGHT_TOGGLE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
        vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_LIGHT_TURN_OFF): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
        vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_LIGHT_TURN_ON): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
        vol.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'):
 | 
			
		||||
            cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
        vol.Exclusive(CONF_FLASH_LENGTH, 'transformer'):
 | 
			
		||||
            cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
        vol.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
 | 
			
		||||
        vol.Optional(CONF_RED): cv.templatable(cv.percentage),
 | 
			
		||||
        vol.Optional(CONF_GREEN): cv.templatable(cv.percentage),
 | 
			
		||||
        vol.Optional(CONF_BLUE): cv.templatable(cv.percentage),
 | 
			
		||||
        vol.Optional(CONF_WHITE): cv.templatable(cv.percentage),
 | 
			
		||||
        vol.Optional(CONF_EFFECT): cv.templatable(cv.string),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_SWITCH_TOGGLE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_SWITCH_TURN_OFF): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_SWITCH_TURN_ON): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_COVER_OPEN): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_COVER_CLOSE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_COVER_STOP): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_COVER_OPEN): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_COVER_CLOSE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_COVER_STOP): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_FAN_TOGGLE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_FAN_TURN_OFF): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_FAN_TURN_ON): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ID): cv.use_variable_id(None),
 | 
			
		||||
        vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
 | 
			
		||||
        vol.Optional(CONF_SPEED): cv.templatable(fan.validate_fan_speed),
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
}, cv.has_exactly_one_key(*ACTION_KEYS))])
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
DelayAction = esphomelib_ns.DelayAction
 | 
			
		||||
LambdaAction = esphomelib_ns.LambdaAction
 | 
			
		||||
Automation = esphomelib_ns.Automation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_recursive_condition(value):
 | 
			
		||||
    return CONDITIONS_SCHEMA(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONDITION_KEYS = [CONF_AND, CONF_OR, CONF_RANGE, CONF_LAMBDA]
 | 
			
		||||
 | 
			
		||||
CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
 | 
			
		||||
    cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_AND): validate_recursive_condition,
 | 
			
		||||
    vol.Optional(CONF_OR): validate_recursive_condition,
 | 
			
		||||
    vol.Optional(CONF_RANGE): vol.All(vol.Schema({
 | 
			
		||||
        vol.Optional(CONF_ABOVE): vol.Coerce(float),
 | 
			
		||||
        vol.Optional(CONF_BELOW): vol.Coerce(float),
 | 
			
		||||
    }), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)),
 | 
			
		||||
    vol.Optional(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
}), cv.has_exactly_one_key(*CONDITION_KEYS)])
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
AndCondition = esphomelib_ns.AndCondition
 | 
			
		||||
OrCondition = esphomelib_ns.OrCondition
 | 
			
		||||
RangeCondition = esphomelib_ns.RangeCondition
 | 
			
		||||
LambdaCondition = esphomelib_ns.LambdaCondition
 | 
			
		||||
 | 
			
		||||
AUTOMATION_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(None),
 | 
			
		||||
    cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_IF): CONDITIONS_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_THEN): ACTIONS_SCHEMA,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_condition(config, arg_type):
 | 
			
		||||
    template_arg = TemplateArguments(arg_type)
 | 
			
		||||
    if CONF_AND in config:
 | 
			
		||||
        yield AndCondition.new(template_arg, build_conditions(config[CONF_AND], template_arg))
 | 
			
		||||
    elif CONF_OR in config:
 | 
			
		||||
        yield OrCondition.new(template_arg, build_conditions(config[CONF_OR], template_arg))
 | 
			
		||||
    elif CONF_LAMBDA in config:
 | 
			
		||||
        lambda_ = None
 | 
			
		||||
        for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]):
 | 
			
		||||
            yield
 | 
			
		||||
        yield LambdaCondition.new(template_arg, lambda_)
 | 
			
		||||
    elif CONF_RANGE in config:
 | 
			
		||||
        conf = config[CONF_RANGE]
 | 
			
		||||
        rhs = RangeCondition.new(template_arg)
 | 
			
		||||
        type = RangeCondition.template(template_arg)
 | 
			
		||||
        condition = Pvariable(config[CONF_CONDITION_ID], rhs, type=type)
 | 
			
		||||
        if CONF_ABOVE in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_ABOVE], arg_type, float_):
 | 
			
		||||
                yield
 | 
			
		||||
            condition.set_min(template_)
 | 
			
		||||
        if CONF_BELOW in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_BELOW], arg_type, float_):
 | 
			
		||||
                yield
 | 
			
		||||
            condition.set_max(template_)
 | 
			
		||||
        yield condition
 | 
			
		||||
    else:
 | 
			
		||||
        raise ESPHomeYAMLError(u"Unsupported condition {}".format(config))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_conditions(config, arg_type):
 | 
			
		||||
    conditions = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        condition = None
 | 
			
		||||
        for condition in build_condition(conf, arg_type):
 | 
			
		||||
            yield None
 | 
			
		||||
        conditions.append(condition)
 | 
			
		||||
    yield ArrayInitializer(*conditions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_action(config, arg_type):
 | 
			
		||||
    from esphomeyaml.components import light, mqtt, switch
 | 
			
		||||
 | 
			
		||||
    template_arg = TemplateArguments(arg_type)
 | 
			
		||||
    # Keep pylint from freaking out
 | 
			
		||||
    var = None
 | 
			
		||||
    if CONF_DELAY in config:
 | 
			
		||||
        rhs = App.register_component(DelayAction.new(template_arg))
 | 
			
		||||
        type = DelayAction.template(template_arg)
 | 
			
		||||
        action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
        template_ = None
 | 
			
		||||
        for template_ in templatable(config[CONF_DELAY], arg_type, uint32):
 | 
			
		||||
            yield
 | 
			
		||||
        add(action.set_delay(template_))
 | 
			
		||||
        yield action
 | 
			
		||||
    elif CONF_LAMBDA in config:
 | 
			
		||||
        lambda_ = None
 | 
			
		||||
        for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = LambdaAction.new(template_arg, lambda_)
 | 
			
		||||
        type = LambdaAction.template(template_arg)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_MQTT_PUBLISH in config:
 | 
			
		||||
        conf = config[CONF_MQTT_PUBLISH]
 | 
			
		||||
        rhs = App.Pget_mqtt_client().Pmake_publish_action()
 | 
			
		||||
        type = mqtt.MQTTPublishAction.template(template_arg)
 | 
			
		||||
        action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
        template_ = None
 | 
			
		||||
        for template_ in templatable(conf[CONF_TOPIC], arg_type, std_string):
 | 
			
		||||
            yield None
 | 
			
		||||
        add(action.set_topic(template_))
 | 
			
		||||
 | 
			
		||||
        template_ = None
 | 
			
		||||
        for template_ in templatable(conf[CONF_PAYLOAD], arg_type, std_string):
 | 
			
		||||
            yield None
 | 
			
		||||
        add(action.set_payload(template_))
 | 
			
		||||
        if CONF_QOS in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_QOS], arg_type, uint8):
 | 
			
		||||
                yield
 | 
			
		||||
            add(action.set_qos(template_))
 | 
			
		||||
        if CONF_RETAIN in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_RETAIN], arg_type, bool_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_retain(template_))
 | 
			
		||||
        yield action
 | 
			
		||||
    elif CONF_LIGHT_TOGGLE in config:
 | 
			
		||||
        conf = config[CONF_LIGHT_TOGGLE]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_toggle_action(template_arg)
 | 
			
		||||
        type = light.ToggleAction.template(template_arg)
 | 
			
		||||
        action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
        if CONF_TRANSITION_LENGTH in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_transition_length(template_))
 | 
			
		||||
        yield action
 | 
			
		||||
    elif CONF_LIGHT_TURN_OFF in config:
 | 
			
		||||
        conf = config[CONF_LIGHT_TURN_OFF]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_turn_off_action(template_arg)
 | 
			
		||||
        type = light.TurnOffAction.template(template_arg)
 | 
			
		||||
        action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
        if CONF_TRANSITION_LENGTH in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_transition_length(template_))
 | 
			
		||||
        yield action
 | 
			
		||||
    elif CONF_LIGHT_TURN_ON in config:
 | 
			
		||||
        conf = config[CONF_LIGHT_TURN_ON]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_turn_on_action(template_arg)
 | 
			
		||||
        type = light.TurnOnAction.template(template_arg)
 | 
			
		||||
        action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
        if CONF_TRANSITION_LENGTH in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_transition_length(template_))
 | 
			
		||||
        if CONF_FLASH_LENGTH in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_FLASH_LENGTH], arg_type, uint32):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_flash_length(template_))
 | 
			
		||||
        if CONF_BRIGHTNESS in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_BRIGHTNESS], arg_type, float_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_brightness(template_))
 | 
			
		||||
        if CONF_RED in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_RED], arg_type, float_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_red(template_))
 | 
			
		||||
        if CONF_GREEN in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_GREEN], arg_type, float_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_green(template_))
 | 
			
		||||
        if CONF_BLUE in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_BLUE], arg_type, float_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_blue(template_))
 | 
			
		||||
        if CONF_WHITE in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_WHITE], arg_type, float_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_white(template_))
 | 
			
		||||
        if CONF_EFFECT in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_EFFECT], arg_type, std_string):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_effect(template_))
 | 
			
		||||
        yield action
 | 
			
		||||
    elif CONF_SWITCH_TOGGLE in config:
 | 
			
		||||
        conf = config[CONF_SWITCH_TOGGLE]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_toggle_action(template_arg)
 | 
			
		||||
        type = switch.ToggleAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_SWITCH_TURN_OFF in config:
 | 
			
		||||
        conf = config[CONF_SWITCH_TURN_OFF]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_turn_off_action(template_arg)
 | 
			
		||||
        type = switch.TurnOffAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_SWITCH_TURN_ON in config:
 | 
			
		||||
        conf = config[CONF_SWITCH_TURN_ON]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_turn_on_action(template_arg)
 | 
			
		||||
        type = switch.TurnOnAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_COVER_OPEN in config:
 | 
			
		||||
        conf = config[CONF_COVER_OPEN]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_open_action(template_arg)
 | 
			
		||||
        type = cover.OpenAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_COVER_CLOSE in config:
 | 
			
		||||
        conf = config[CONF_COVER_CLOSE]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_close_action(template_arg)
 | 
			
		||||
        type = cover.CloseAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_COVER_STOP in config:
 | 
			
		||||
        conf = config[CONF_COVER_STOP]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_stop_action(template_arg)
 | 
			
		||||
        type = cover.StopAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_FAN_TOGGLE in config:
 | 
			
		||||
        conf = config[CONF_FAN_TOGGLE]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_toggle_action(template_arg)
 | 
			
		||||
        type = fan.ToggleAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_FAN_TURN_OFF in config:
 | 
			
		||||
        conf = config[CONF_FAN_TURN_OFF]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_turn_off_action(template_arg)
 | 
			
		||||
        type = fan.TurnOffAction.template(arg_type)
 | 
			
		||||
        yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
    elif CONF_FAN_TURN_ON in config:
 | 
			
		||||
        conf = config[CONF_FAN_TURN_ON]
 | 
			
		||||
        for var in get_variable(conf[CONF_ID]):
 | 
			
		||||
            yield None
 | 
			
		||||
        rhs = var.make_turn_on_action(template_arg)
 | 
			
		||||
        type = fan.TurnOnAction.template(arg_type)
 | 
			
		||||
        action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
 | 
			
		||||
        if CONF_OSCILLATING in config:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_OSCILLATING], arg_type, bool_):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_oscillating(template_))
 | 
			
		||||
        if CONF_SPEED in config:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_SPEED], arg_type, fan.FanSpeed):
 | 
			
		||||
                yield None
 | 
			
		||||
            add(action.set_speed(template_))
 | 
			
		||||
        yield action
 | 
			
		||||
    else:
 | 
			
		||||
        raise ESPHomeYAMLError(u"Unsupported action {}".format(config))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_actions(config, arg_type):
 | 
			
		||||
    actions = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        action = None
 | 
			
		||||
        for action in build_action(conf, arg_type):
 | 
			
		||||
            yield None
 | 
			
		||||
        actions.append(action)
 | 
			
		||||
    yield ArrayInitializer(*actions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_automation_(trigger, arg_type, config):
 | 
			
		||||
    rhs = App.make_automation(trigger)
 | 
			
		||||
    type = Automation.template(arg_type)
 | 
			
		||||
    obj = Pvariable(config[CONF_AUTOMATION_ID], rhs, type=type)
 | 
			
		||||
    if CONF_IF in config:
 | 
			
		||||
        conditions = None
 | 
			
		||||
        for conditions in build_conditions(config[CONF_IF], arg_type):
 | 
			
		||||
            yield
 | 
			
		||||
        add(obj.add_conditions(conditions))
 | 
			
		||||
    actions = None
 | 
			
		||||
    for actions in build_actions(config[CONF_THEN], arg_type):
 | 
			
		||||
        yield
 | 
			
		||||
    add(obj.add_actions(actions))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_automation(trigger, arg_type, config):
 | 
			
		||||
    add_job(build_automation_, trigger, arg_type, config)
 | 
			
		||||
							
								
								
									
										10
									
								
								esphomeyaml/build.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								esphomeyaml/build.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
    "squash": false,
 | 
			
		||||
    "build_from": {
 | 
			
		||||
        "aarch64": "arm64v8/ubuntu:bionic",
 | 
			
		||||
        "amd64": "ubuntu:bionic",
 | 
			
		||||
        "armhf": "homeassistant/armhf-base:latest",
 | 
			
		||||
        "i386": "i386/ubuntu:bionic"
 | 
			
		||||
    },
 | 
			
		||||
    "args": {}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +1,21 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, RawExpression, add, HexIntLiteral
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
ADS1115_COMPONENT_CLASS = 'sensor::ADS1115Component'
 | 
			
		||||
ADS1115Component = sensor.sensor_ns.ADS1115Component
 | 
			
		||||
 | 
			
		||||
RATES = {
 | 
			
		||||
    8: 'ADS1115_RATE_8',
 | 
			
		||||
    16: 'ADS1115_RATE_16',
 | 
			
		||||
    32: 'ADS1115_RATE_32',
 | 
			
		||||
    64: 'ADS1115_RATE_64',
 | 
			
		||||
    128: 'ADS1115_RATE_128',
 | 
			
		||||
    250: 'ADS1115_RATE_250',
 | 
			
		||||
    475: 'ADS1115_RATE_475',
 | 
			
		||||
    860: 'ADS1115_RATE_860',
 | 
			
		||||
}
 | 
			
		||||
RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required."""
 | 
			
		||||
 | 
			
		||||
ADS1115_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID('ads1115'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(ADS1115Component),
 | 
			
		||||
    vol.Required(CONF_ADDRESS): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_RATE): vol.All(vol.Coerce(int), vol.Any(*list(RATES.keys()))),
 | 
			
		||||
 | 
			
		||||
    vol.Optional(CONF_RATE): cv.invalid(RATE_REMOVE_MESSAGE)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
 | 
			
		||||
@@ -30,12 +23,8 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        address = HexIntLiteral(conf[CONF_ADDRESS])
 | 
			
		||||
        rhs = App.make_ads1115_component(address)
 | 
			
		||||
        ads1115 = Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
 | 
			
		||||
        if CONF_RATE in conf:
 | 
			
		||||
            add(ads1115.set_rate(RawExpression(RATES[conf[CONF_RATE]])))
 | 
			
		||||
        rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
 | 
			
		||||
        Pvariable(conf[CONF_ID], rhs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_ADS1115_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,12 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED
 | 
			
		||||
from esphomeyaml.helpers import add, setup_mqtt_component
 | 
			
		||||
from esphomeyaml import automation
 | 
			
		||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INVERTED, CONF_MAX_LENGTH, \
 | 
			
		||||
    CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, CONF_ON_DOUBLE_CLICK, CONF_ON_PRESS, \
 | 
			
		||||
    CONF_ON_RELEASE, CONF_TRIGGER_ID
 | 
			
		||||
from esphomeyaml.helpers import App, NoArg, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
 | 
			
		||||
    add_job
 | 
			
		||||
 | 
			
		||||
DEVICE_CLASSES = [
 | 
			
		||||
    '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
 | 
			
		||||
@@ -11,29 +15,86 @@ DEVICE_CLASSES = [
 | 
			
		||||
    'sound', 'vibration', 'window'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
DEVICE_CLASSES_MSG = "Unknown device class. Must be one of {}".format(', '.join(DEVICE_CLASSES))
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
binary_sensor_ns = esphomelib_ns.namespace('binary_sensor')
 | 
			
		||||
PressTrigger = binary_sensor_ns.PressTrigger
 | 
			
		||||
ReleaseTrigger = binary_sensor_ns.ReleaseTrigger
 | 
			
		||||
ClickTrigger = binary_sensor_ns.ClickTrigger
 | 
			
		||||
DoubleClickTrigger = binary_sensor_ns.DoubleClickTrigger
 | 
			
		||||
BinarySensor = binary_sensor_ns.BinarySensor
 | 
			
		||||
MQTTBinarySensorComponent = binary_sensor_ns.MQTTBinarySensorComponent
 | 
			
		||||
 | 
			
		||||
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent),
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(BinarySensor),
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower,
 | 
			
		||||
                                             vol.Any(*DEVICE_CLASSES, msg=DEVICE_CLASSES_MSG)),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
    vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)),
 | 
			
		||||
    vol.Optional(CONF_ON_PRESS): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger),
 | 
			
		||||
    })]),
 | 
			
		||||
    vol.Optional(CONF_ON_RELEASE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ReleaseTrigger),
 | 
			
		||||
    })]),
 | 
			
		||||
    vol.Optional(CONF_ON_CLICK): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ClickTrigger),
 | 
			
		||||
        vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
 | 
			
		||||
        vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
 | 
			
		||||
    })]),
 | 
			
		||||
    vol.Optional(CONF_ON_DOUBLE_CLICK):
 | 
			
		||||
        vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
            cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(DoubleClickTrigger),
 | 
			
		||||
            vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
 | 
			
		||||
            vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
 | 
			
		||||
        })]),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_binary_sensor(obj, config):
 | 
			
		||||
def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
 | 
			
		||||
    if CONF_DEVICE_CLASS in config:
 | 
			
		||||
        add(obj.set_device_class(config[CONF_DEVICE_CLASS]))
 | 
			
		||||
        add(binary_sensor_var.set_device_class(config[CONF_DEVICE_CLASS]))
 | 
			
		||||
    if CONF_INVERTED in config:
 | 
			
		||||
        add(obj.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
        add(binary_sensor_var.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_PRESS, []):
 | 
			
		||||
        rhs = binary_sensor_var.make_press_trigger()
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, NoArg, conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_RELEASE, []):
 | 
			
		||||
        rhs = binary_sensor_var.make_release_trigger()
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, NoArg, conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_CLICK, []):
 | 
			
		||||
        rhs = binary_sensor_var.make_click_trigger(conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH])
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, NoArg, conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
 | 
			
		||||
        rhs = binary_sensor_var.make_double_click_trigger(conf[CONF_MIN_LENGTH],
 | 
			
		||||
                                                          conf[CONF_MAX_LENGTH])
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, NoArg, conf)
 | 
			
		||||
 | 
			
		||||
    setup_mqtt_component(mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_mqtt_binary_sensor(obj, config):
 | 
			
		||||
    setup_mqtt_component(obj, config)
 | 
			
		||||
def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config):
 | 
			
		||||
    binary_sensor_var = Pvariable(config[CONF_ID], binary_sensor_obj,
 | 
			
		||||
                                  has_side_effects=False)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj,
 | 
			
		||||
                         has_side_effects=False)
 | 
			
		||||
    add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_BINARY_SENSOR'
 | 
			
		||||
def register_binary_sensor(var, config):
 | 
			
		||||
    binary_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
 | 
			
		||||
    rhs = App.register_binary_sensor(binary_sensor_var)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
 | 
			
		||||
    add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								esphomeyaml/components/binary_sensor/esp32_ble.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								esphomeyaml/components/binary_sensor/esp32_ble.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import binary_sensor
 | 
			
		||||
from esphomeyaml.components.esp32_ble import ESP32BLETracker
 | 
			
		||||
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.core import HexInt, MACAddress
 | 
			
		||||
from esphomeyaml.helpers import ArrayInitializer, get_variable
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
DEPENDENCIES = ['esp32_ble']
 | 
			
		||||
 | 
			
		||||
CONF_ESP32_BLE_ID = 'esp32_ble_id'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_mac(value):
 | 
			
		||||
    value = cv.string_strict(value)
 | 
			
		||||
    parts = value.split(':')
 | 
			
		||||
    if len(parts) != 6:
 | 
			
		||||
        raise vol.Invalid("MAC Address must consist of 6 : (colon) separated parts")
 | 
			
		||||
    parts_int = []
 | 
			
		||||
    if any(len(part) != 2 for part in parts):
 | 
			
		||||
        raise vol.Invalid("MAC Address must be format XX:XX:XX:XX:XX:XX")
 | 
			
		||||
    for part in parts:
 | 
			
		||||
        try:
 | 
			
		||||
            parts_int.append(int(part, 16))
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            raise vol.Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
 | 
			
		||||
 | 
			
		||||
    return MACAddress(*parts_int)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_MAC_ADDRESS): validate_mac,
 | 
			
		||||
    cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker)
 | 
			
		||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = None
 | 
			
		||||
    for hub in get_variable(CONF_ESP32_BLE_ID):
 | 
			
		||||
        yield
 | 
			
		||||
    addr = [HexInt(i) for i in config[CONF_MAC_ADDRESS].parts]
 | 
			
		||||
    rhs = hub.make_device(config[CONF_NAME], ArrayInitializer(*addr, multiline=False))
 | 
			
		||||
    binary_sensor.register_binary_sensor(rhs, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
 | 
			
		||||
							
								
								
									
										53
									
								
								esphomeyaml/components/binary_sensor/esp32_touch.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								esphomeyaml/components/binary_sensor/esp32_touch.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import binary_sensor
 | 
			
		||||
from esphomeyaml.components.esp32_touch import ESP32TouchComponent
 | 
			
		||||
from esphomeyaml.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.helpers import get_variable, global_ns
 | 
			
		||||
from esphomeyaml.pins import validate_gpio_pin
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['esp32_touch']
 | 
			
		||||
 | 
			
		||||
CONF_ESP32_TOUCH_ID = 'esp32_touch_id'
 | 
			
		||||
 | 
			
		||||
TOUCH_PADS = {
 | 
			
		||||
    4: global_ns.TOUCH_PAD_NUM0,
 | 
			
		||||
    0: global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
    2: global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
    15: global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
    13: global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
    12: global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
    14: global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
    27: global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
    33: global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
    32: global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_touch_pad(value):
 | 
			
		||||
    value = validate_gpio_pin(value)
 | 
			
		||||
    if value not in TOUCH_PADS:
 | 
			
		||||
        raise vol.Invalid("Pin {} does not support touch pads.".format(value))
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_PIN): validate_touch_pad,
 | 
			
		||||
    vol.Required(CONF_THRESHOLD): cv.uint16_t,
 | 
			
		||||
    cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_variable_id(ESP32TouchComponent),
 | 
			
		||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = None
 | 
			
		||||
    for hub in get_variable(config[CONF_ESP32_TOUCH_ID]):
 | 
			
		||||
        yield
 | 
			
		||||
    touch_pad = TOUCH_PADS[config[CONF_PIN]]
 | 
			
		||||
    rhs = hub.make_touch_pad(config[CONF_NAME], touch_pad, config[CONF_THRESHOLD])
 | 
			
		||||
    binary_sensor.register_binary_sensor(rhs, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'
 | 
			
		||||
@@ -3,23 +3,24 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import binary_sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, add, exp_gpio_input_pin, variable
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application
 | 
			
		||||
 | 
			
		||||
MakeGPIOBinarySensor = Application.MakeGPIOBinarySensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('gpio_binary_sensor'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOBinarySensor),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.GPIO_INPUT_PIN_SCHEMA
 | 
			
		||||
}).extend(binary_sensor.MQTT_BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_gpio_binary_sensor(config[CONF_NAME], exp_gpio_input_pin(config[CONF_PIN]))
 | 
			
		||||
    gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_INVERTED in config:
 | 
			
		||||
        add(gpio.Pgpio.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
    binary_sensor.setup_binary_sensor(gpio.Pgpio, config)
 | 
			
		||||
    binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config)
 | 
			
		||||
    pin = None
 | 
			
		||||
    for pin in gpio_input_pin_expression(config[CONF_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin)
 | 
			
		||||
    gpio = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    binary_sensor.setup_binary_sensor(gpio.Pgpio, gpio.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_GPIO_BINARY_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,21 @@
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import binary_sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NAME
 | 
			
		||||
from esphomeyaml.helpers import App, variable
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['mqtt']
 | 
			
		||||
 | 
			
		||||
MakeStatusBinarySensor = Application.MakeStatusBinarySensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('status_binary_sensor'): cv.register_variable_id,
 | 
			
		||||
}).extend(binary_sensor.MQTT_BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeStatusBinarySensor),
 | 
			
		||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_status_binary_sensor(config[CONF_NAME])
 | 
			
		||||
    status = variable('Application::MakeStatusBinarySensor', config[CONF_ID], rhs)
 | 
			
		||||
    binary_sensor.setup_binary_sensor(status.Pstatus, config)
 | 
			
		||||
    binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config)
 | 
			
		||||
    status = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    binary_sensor.setup_binary_sensor(status.Pstatus, status.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_STATUS_BINARY_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								esphomeyaml/components/binary_sensor/template.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphomeyaml/components/binary_sensor/template.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import binary_sensor
 | 
			
		||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME
 | 
			
		||||
from esphomeyaml.helpers import App, Application, process_lambda, variable
 | 
			
		||||
 | 
			
		||||
MakeTemplateBinarySensor = Application.MakeTemplateBinarySensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateBinarySensor),
 | 
			
		||||
    vol.Required(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    template_ = None
 | 
			
		||||
    for template_ in process_lambda(config[CONF_LAMBDA], []):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_template_binary_sensor(config[CONF_NAME], template_)
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR'
 | 
			
		||||
							
								
								
									
										35
									
								
								esphomeyaml/components/cover/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphomeyaml/components/cover/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID
 | 
			
		||||
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
cover_ns = esphomelib_ns.namespace('cover')
 | 
			
		||||
Cover = cover_ns.Cover
 | 
			
		||||
MQTTCoverComponent = cover_ns.MQTTCoverComponent
 | 
			
		||||
CoverState = cover_ns.CoverState
 | 
			
		||||
COVER_OPEN = cover_ns.COVER_OPEN
 | 
			
		||||
COVER_CLOSED = cover_ns.COVER_CLOSED
 | 
			
		||||
OpenAction = cover_ns.OpenAction
 | 
			
		||||
CloseAction = cover_ns.CloseAction
 | 
			
		||||
StopAction = cover_ns.StopAction
 | 
			
		||||
 | 
			
		||||
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(Cover),
 | 
			
		||||
    cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTCoverComponent),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_cover_core_(cover_var, mqtt_var, config):
 | 
			
		||||
    setup_mqtt_component(mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_cover(cover_obj, mqtt_obj, config):
 | 
			
		||||
    cover_var = Pvariable(config[CONF_ID], cover_obj, has_side_effects=False)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
 | 
			
		||||
    setup_cover_core_(cover_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_COVER'
 | 
			
		||||
							
								
								
									
										52
									
								
								esphomeyaml/components/cover/template.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								esphomeyaml/components/cover/template.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import automation
 | 
			
		||||
from esphomeyaml.components import cover
 | 
			
		||||
from esphomeyaml.const import CONF_CLOSE_ACTION, CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, \
 | 
			
		||||
    CONF_OPEN_ACTION, CONF_STOP_ACTION, CONF_OPTIMISTIC
 | 
			
		||||
from esphomeyaml.helpers import App, Application, NoArg, add, process_lambda, variable
 | 
			
		||||
 | 
			
		||||
MakeTemplateCover = Application.MakeTemplateCover
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(cover.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateCover),
 | 
			
		||||
    vol.Optional(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
    vol.Optional(CONF_OPTIMISTIC): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_OPEN_ACTION): automation.ACTIONS_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_CLOSE_ACTION): automation.ACTIONS_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_STOP_ACTION): automation.ACTIONS_SCHEMA,
 | 
			
		||||
}).extend(cover.COVER_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_template_cover(config[CONF_NAME])
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
        template_ = None
 | 
			
		||||
        for template_ in process_lambda(config[CONF_LAMBDA], []):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.set_state_lambda(template_))
 | 
			
		||||
    if CONF_OPEN_ACTION in config:
 | 
			
		||||
        actions = None
 | 
			
		||||
        for actions in automation.build_actions(config[CONF_OPEN_ACTION], NoArg):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.add_open_actions(actions))
 | 
			
		||||
    if CONF_CLOSE_ACTION in config:
 | 
			
		||||
        actions = None
 | 
			
		||||
        for actions in automation.build_actions(config[CONF_CLOSE_ACTION], NoArg):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.add_close_actions(actions))
 | 
			
		||||
    if CONF_STOP_ACTION in config:
 | 
			
		||||
        actions = None
 | 
			
		||||
        for actions in automation.build_actions(config[CONF_STOP_ACTION], NoArg):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.add_stop_actions(actions))
 | 
			
		||||
    if CONF_OPTIMISTIC in config:
 | 
			
		||||
        add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
 | 
			
		||||
 | 
			
		||||
    cover.setup_cover(make.Ptemplate_, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_TEMPLATE_COVER'
 | 
			
		||||
@@ -2,23 +2,23 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_PIN, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable
 | 
			
		||||
 | 
			
		||||
DALLAS_COMPONENT_CLASS = 'sensor::DallasComponent'
 | 
			
		||||
DallasComponent = sensor.sensor_ns.DallasComponent
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
 | 
			
		||||
    cv.GenerateID('dallas'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(DallasComponent),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.input_output_pin,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
        Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs)
 | 
			
		||||
        Pvariable(conf[CONF_ID], rhs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_DALLAS_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -11,5 +11,4 @@ def to_code(config):
 | 
			
		||||
    add(App.make_debug_component())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_DEBUG_COMPONENT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_DEBUG_COMPONENT'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +1,40 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import config_validation as cv, pins
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_RUN_CYCLES, CONF_RUN_DURATION, CONF_SLEEP_DURATION, \
 | 
			
		||||
    CONF_WAKEUP_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_input_pin
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \
 | 
			
		||||
    CONF_SLEEP_DURATION, CONF_WAKEUP_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression, esphomelib_ns
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_pin_number(value):
 | 
			
		||||
    valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 39]
 | 
			
		||||
    if value not in valid_pins:
 | 
			
		||||
    if value[CONF_NUMBER] not in valid_pins:
 | 
			
		||||
        raise vol.Invalid(u"Only pins {} support wakeup"
 | 
			
		||||
                          u"".format(', '.join(str(x) for x in valid_pins)))
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DeepSleepComponent = esphomelib_ns.DeepSleepComponent
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID('deep_sleep'): cv.register_variable_id,
 | 
			
		||||
    vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period,
 | 
			
		||||
    vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INPUT_PIN_SCHEMA,
 | 
			
		||||
                                           pins.schema_validate_number(validate_pin_number)),
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent),
 | 
			
		||||
    vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
 | 
			
		||||
                                           validate_pin_number),
 | 
			
		||||
    vol.Optional(CONF_RUN_CYCLES): cv.positive_int,
 | 
			
		||||
    vol.Optional(CONF_RUN_DURATION): cv.positive_time_period,
 | 
			
		||||
    vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_deep_sleep_component()
 | 
			
		||||
    deep_sleep = Pvariable('DeepSleepComponent', config[CONF_ID], rhs)
 | 
			
		||||
    deep_sleep = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_SLEEP_DURATION in config:
 | 
			
		||||
        add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION]))
 | 
			
		||||
    if CONF_WAKEUP_PIN in config:
 | 
			
		||||
        pin = exp_gpio_input_pin(config[CONF_WAKEUP_PIN])
 | 
			
		||||
        pin = None
 | 
			
		||||
        for pin in gpio_input_pin_expression(config[CONF_WAKEUP_PIN]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(deep_sleep.set_wakeup_pin(pin))
 | 
			
		||||
    if CONF_RUN_CYCLES in config:
 | 
			
		||||
        add(deep_sleep.set_run_cycles(config[CONF_RUN_CYCLES]))
 | 
			
		||||
@@ -38,5 +42,4 @@ def to_code(config):
 | 
			
		||||
        add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_DEEP_SLEEP'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_DEEP_SLEEP'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								esphomeyaml/components/esp32_ble.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphomeyaml/components/esp32_ble.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
ESP32BLETracker = esphomelib_ns.ESP32BLETracker
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker),
 | 
			
		||||
    vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_esp32_ble_tracker()
 | 
			
		||||
    ble = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_SCAN_INTERVAL in config:
 | 
			
		||||
        add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
 | 
			
		||||
							
								
								
									
										83
									
								
								esphomeyaml/components/esp32_touch.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								esphomeyaml/components/esp32_touch.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import config_validation as cv
 | 
			
		||||
from esphomeyaml.components import binary_sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_SETUP_MODE, CONF_IIR_FILTER, \
 | 
			
		||||
    CONF_SLEEP_DURATION, CONF_MEASUREMENT_DURATION, CONF_LOW_VOLTAGE_REFERENCE, \
 | 
			
		||||
    CONF_HIGH_VOLTAGE_REFERENCE, CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.core import TimePeriod
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, global_ns
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_voltage(values):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if isinstance(value, float) and value.is_integer():
 | 
			
		||||
            value = int(value)
 | 
			
		||||
        value = cv.string(value)
 | 
			
		||||
        if not value.endswith('V'):
 | 
			
		||||
            value += 'V'
 | 
			
		||||
        return cv.one_of(*values)(value)
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOW_VOLTAGE_REFERENCE = {
 | 
			
		||||
    '0.5V': global_ns.TOUCH_LVOLT_0V5,
 | 
			
		||||
    '0.6V': global_ns.TOUCH_LVOLT_0V6,
 | 
			
		||||
    '0.7V': global_ns.TOUCH_LVOLT_0V7,
 | 
			
		||||
    '0.8V': global_ns.TOUCH_LVOLT_0V8,
 | 
			
		||||
}
 | 
			
		||||
HIGH_VOLTAGE_REFERENCE = {
 | 
			
		||||
    '2.4V': global_ns.TOUCH_HVOLT_2V4,
 | 
			
		||||
    '2.5V': global_ns.TOUCH_HVOLT_2V5,
 | 
			
		||||
    '2.6V': global_ns.TOUCH_HVOLT_2V6,
 | 
			
		||||
    '2.7V': global_ns.TOUCH_HVOLT_2V7,
 | 
			
		||||
}
 | 
			
		||||
VOLTAGE_ATTENUATION = {
 | 
			
		||||
    '1.5V': global_ns.TOUCH_HVOLT_ATTEN_1V5,
 | 
			
		||||
    '1V': global_ns.TOUCH_HVOLT_ATTEN_1V,
 | 
			
		||||
    '0.5V': global_ns.TOUCH_HVOLT_ATTEN_0V5,
 | 
			
		||||
    '0V': global_ns.TOUCH_HVOLT_ATTEN_0V,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Optional(CONF_SETUP_MODE): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_IIR_FILTER): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_SLEEP_DURATION):
 | 
			
		||||
        vol.All(cv.positive_time_period, vol.Range(max=TimePeriod(microseconds=436906))),
 | 
			
		||||
    vol.Optional(CONF_MEASUREMENT_DURATION):
 | 
			
		||||
        vol.All(cv.positive_time_period, vol.Range(max=TimePeriod(microseconds=8192))),
 | 
			
		||||
    vol.Optional(CONF_LOW_VOLTAGE_REFERENCE): validate_voltage(LOW_VOLTAGE_REFERENCE),
 | 
			
		||||
    vol.Optional(CONF_HIGH_VOLTAGE_REFERENCE): validate_voltage(HIGH_VOLTAGE_REFERENCE),
 | 
			
		||||
    vol.Optional(CONF_VOLTAGE_ATTENUATION): validate_voltage(VOLTAGE_ATTENUATION),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
ESP32TouchComponent = binary_sensor.binary_sensor_ns.ESP32TouchComponent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_esp32_touch_component()
 | 
			
		||||
    touch = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_SETUP_MODE in config:
 | 
			
		||||
        add(touch.set_setup_mode(config[CONF_SETUP_MODE]))
 | 
			
		||||
    if CONF_IIR_FILTER in config:
 | 
			
		||||
        add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
 | 
			
		||||
    if CONF_SLEEP_DURATION in config:
 | 
			
		||||
        sleep_duration = int(config[CONF_SLEEP_DURATION].total_microseconds * 6.6667)
 | 
			
		||||
        add(touch.set_sleep_duration(sleep_duration))
 | 
			
		||||
    if CONF_MEASUREMENT_DURATION in config:
 | 
			
		||||
        measurement_duration = int(config[CONF_MEASUREMENT_DURATION].total_microseconds * 0.125)
 | 
			
		||||
        add(touch.set_measurement_duration(measurement_duration))
 | 
			
		||||
    if CONF_LOW_VOLTAGE_REFERENCE in config:
 | 
			
		||||
        value = LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]]
 | 
			
		||||
        add(touch.set_low_voltage_reference(value))
 | 
			
		||||
    if CONF_HIGH_VOLTAGE_REFERENCE in config:
 | 
			
		||||
        value = HIGH_VOLTAGE_REFERENCE[config[CONF_HIGH_VOLTAGE_REFERENCE]]
 | 
			
		||||
        add(touch.set_high_voltage_reference(value))
 | 
			
		||||
    if CONF_VOLTAGE_ATTENUATION in config:
 | 
			
		||||
        value = VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]]
 | 
			
		||||
        add(touch.set_voltage_attenuation(value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'
 | 
			
		||||
@@ -1,27 +1,63 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, \
 | 
			
		||||
    CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
 | 
			
		||||
from esphomeyaml.helpers import add, setup_mqtt_component
 | 
			
		||||
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
 | 
			
		||||
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
fan_ns = esphomelib_ns.namespace('fan')
 | 
			
		||||
FanState = fan_ns.FanState
 | 
			
		||||
MQTTFanComponent = fan_ns.MQTTFanComponent
 | 
			
		||||
MakeFan = Application.MakeFan
 | 
			
		||||
TurnOnAction = fan_ns.TurnOnAction
 | 
			
		||||
TurnOffAction = fan_ns.TurnOffAction
 | 
			
		||||
ToggleAction = fan_ns.ToggleAction
 | 
			
		||||
FanSpeed = fan_ns.FanSpeed
 | 
			
		||||
FAN_SPEED_OFF = fan_ns.FAN_SPEED_OFF
 | 
			
		||||
FAN_SPEED_LOW = fan_ns.FAN_SPEED_LOW
 | 
			
		||||
FAN_SPEED_MEDIUM = fan_ns.FAN_SPEED_MEDIUM
 | 
			
		||||
FAN_SPEED_HIGH = fan_ns.FAN_SPEED_HIGH
 | 
			
		||||
 | 
			
		||||
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(FanState),
 | 
			
		||||
    cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTFanComponent),
 | 
			
		||||
    vol.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.publish_topic,
 | 
			
		||||
    vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.subscribe_topic,
 | 
			
		||||
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_mqtt_fan(obj, config):
 | 
			
		||||
FAN_SPEEDS = {
 | 
			
		||||
    'OFF': FAN_SPEED_OFF,
 | 
			
		||||
    'LOW': FAN_SPEED_LOW,
 | 
			
		||||
    'MEDIUM': FAN_SPEED_MEDIUM,
 | 
			
		||||
    'HIGH': FAN_SPEED_HIGH,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_fan_speed(value):
 | 
			
		||||
    return vol.All(vol.Upper, cv.one_of(*FAN_SPEEDS))(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_fan_core_(fan_var, mqtt_var, config):
 | 
			
		||||
    if CONF_OSCILLATION_STATE_TOPIC in config:
 | 
			
		||||
        add(obj.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
 | 
			
		||||
        add(mqtt_var.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
 | 
			
		||||
    if CONF_OSCILLATION_COMMAND_TOPIC in config:
 | 
			
		||||
        add(obj.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
 | 
			
		||||
        add(mqtt_var.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
 | 
			
		||||
    if CONF_SPEED_STATE_TOPIC in config:
 | 
			
		||||
        add(obj.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
 | 
			
		||||
        add(mqtt_var.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
 | 
			
		||||
    if CONF_SPEED_COMMAND_TOPIC in config:
 | 
			
		||||
        add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
 | 
			
		||||
    setup_mqtt_component(obj, config)
 | 
			
		||||
        add(mqtt_var.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
 | 
			
		||||
    setup_mqtt_component(mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_FAN'
 | 
			
		||||
def setup_fan(fan_obj, mqtt_obj, config):
 | 
			
		||||
    fan_var = Pvariable(config[CONF_ID], fan_obj, has_side_effects=False)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
 | 
			
		||||
    setup_fan_core_(fan_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_FAN'
 | 
			
		||||
 
 | 
			
		||||
@@ -2,22 +2,28 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import fan
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
 | 
			
		||||
from esphomeyaml.helpers import App, add, get_variable, variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('binary_fan'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.variable_id,
 | 
			
		||||
    vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id,
 | 
			
		||||
})
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
}).extend(fan.FAN_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    output = get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    output = None
 | 
			
		||||
    for output in get_variable(config[CONF_OUTPUT]):
 | 
			
		||||
        yield
 | 
			
		||||
 | 
			
		||||
    rhs = App.make_fan(config[CONF_NAME])
 | 
			
		||||
    fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
 | 
			
		||||
    fan_struct = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    add(fan_struct.Poutput.set_binary(output))
 | 
			
		||||
    if CONF_OSCILLATION_OUTPUT in config:
 | 
			
		||||
        oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
 | 
			
		||||
        oscillation_output = None
 | 
			
		||||
        for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(fan_struct.Poutput.set_oscillation(oscillation_output))
 | 
			
		||||
    fan.setup_mqtt_fan(fan_struct.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
    fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,29 +2,31 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import fan
 | 
			
		||||
from esphomeyaml.const import CONF_HIGH, CONF_ID, CONF_LOW, \
 | 
			
		||||
    CONF_MEDIUM, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, \
 | 
			
		||||
    CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
 | 
			
		||||
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
 | 
			
		||||
from esphomeyaml.helpers import App, add, get_variable, variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('speed_fan'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_SPEED_STATE_TOPIC): cv.publish_topic,
 | 
			
		||||
    vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic,
 | 
			
		||||
    vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id,
 | 
			
		||||
    vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_SPEED): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_LOW): cv.zero_to_one_float,
 | 
			
		||||
        vol.Required(CONF_MEDIUM): cv.zero_to_one_float,
 | 
			
		||||
        vol.Required(CONF_HIGH): cv.zero_to_one_float,
 | 
			
		||||
        vol.Required(CONF_LOW): cv.percentage,
 | 
			
		||||
        vol.Required(CONF_MEDIUM): cv.percentage,
 | 
			
		||||
        vol.Required(CONF_HIGH): cv.percentage,
 | 
			
		||||
    }),
 | 
			
		||||
})
 | 
			
		||||
}).extend(fan.FAN_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    output = get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    output = None
 | 
			
		||||
    for output in get_variable(config[CONF_OUTPUT]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_fan(config[CONF_NAME])
 | 
			
		||||
    fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
 | 
			
		||||
    fan_struct = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    if CONF_SPEED in config:
 | 
			
		||||
        speeds = config[CONF_SPEED]
 | 
			
		||||
        add(fan_struct.Poutput.set_speed(output, 0.0,
 | 
			
		||||
@@ -35,6 +37,9 @@ def to_code(config):
 | 
			
		||||
        add(fan_struct.Poutput.set_speed(output))
 | 
			
		||||
 | 
			
		||||
    if CONF_OSCILLATION_OUTPUT in config:
 | 
			
		||||
        oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
 | 
			
		||||
        oscillation_output = None
 | 
			
		||||
        for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(fan_struct.Poutput.set_oscillation(oscillation_output))
 | 
			
		||||
    fan.setup_mqtt_fan(fan_struct.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
    fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,19 +2,31 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA
 | 
			
		||||
from esphomeyaml.helpers import App, add
 | 
			
		||||
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID, \
 | 
			
		||||
    CONF_RECEIVE_TIMEOUT
 | 
			
		||||
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
 | 
			
		||||
 | 
			
		||||
I2CComponent = esphomelib_ns.I2CComponent
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(I2CComponent),
 | 
			
		||||
    vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin,
 | 
			
		||||
    vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin,
 | 
			
		||||
    vol.Optional(CONF_FREQUENCY): vol.All(cv.only_on_esp32, cv.positive_int),
 | 
			
		||||
    vol.Optional(CONF_FREQUENCY): cv.positive_int,
 | 
			
		||||
    vol.Optional(CONF_RECEIVE_TIMEOUT): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_SCAN): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    add(App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_FREQUENCY)))
 | 
			
		||||
    rhs = App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_SCAN))
 | 
			
		||||
    i2c = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_FREQUENCY in config:
 | 
			
		||||
        add(i2c.set_frequency(config[CONF_FREQUENCY]))
 | 
			
		||||
    if CONF_RECEIVE_TIMEOUT in config:
 | 
			
		||||
        add(i2c.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_I2C'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_I2C'
 | 
			
		||||
 | 
			
		||||
LIB_DEPS = 'Wire'
 | 
			
		||||
 
 | 
			
		||||
@@ -2,26 +2,27 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent'
 | 
			
		||||
IRTransmitterComponent = switch.switch_ns.namespace('IRTransmitterComponent')
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
 | 
			
		||||
    cv.GenerateID('ir_transmitter'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(IRTransmitterComponent),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)),
 | 
			
		||||
    vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int),
 | 
			
		||||
                                                     vol.Range(min=1, max=100)),
 | 
			
		||||
})])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        pin = exp_gpio_output_pin(conf[CONF_PIN])
 | 
			
		||||
        pin = None
 | 
			
		||||
        for pin in gpio_output_pin_expression(conf[CONF_PIN]):
 | 
			
		||||
            yield
 | 
			
		||||
        rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT))
 | 
			
		||||
        Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs)
 | 
			
		||||
        Pvariable(conf[CONF_ID], rhs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_IR_TRANSMITTER'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,39 @@
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH
 | 
			
		||||
from esphomeyaml.helpers import add
 | 
			
		||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
 | 
			
		||||
    CONF_MQTT_ID
 | 
			
		||||
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
light_ns = esphomelib_ns.namespace('light')
 | 
			
		||||
LightState = light_ns.LightState
 | 
			
		||||
MQTTJSONLightComponent = light_ns.MQTTJSONLightComponent
 | 
			
		||||
ToggleAction = light_ns.ToggleAction
 | 
			
		||||
TurnOffAction = light_ns.TurnOffAction
 | 
			
		||||
TurnOnAction = light_ns.TurnOnAction
 | 
			
		||||
MakeLight = Application.MakeLight
 | 
			
		||||
 | 
			
		||||
LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(LightState),
 | 
			
		||||
    cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTJSONLightComponent),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_light_component(obj, config):
 | 
			
		||||
def setup_light_core_(light_var, mqtt_var, config):
 | 
			
		||||
    if CONF_DEFAULT_TRANSITION_LENGTH in config:
 | 
			
		||||
        add(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
 | 
			
		||||
        add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
 | 
			
		||||
    if CONF_GAMMA_CORRECT in config:
 | 
			
		||||
        add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
 | 
			
		||||
 | 
			
		||||
    setup_mqtt_component(mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_LIGHT'
 | 
			
		||||
def setup_light(light_obj, mqtt_obj, config):
 | 
			
		||||
    light_var = Pvariable(config[CONF_ID], light_obj, has_side_effects=False)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
 | 
			
		||||
    setup_light_core_(light_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_LIGHT'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,20 @@
 | 
			
		||||
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import light
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
 | 
			
		||||
from esphomeyaml.helpers import App, get_variable, variable, setup_mqtt_component
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
 | 
			
		||||
from esphomeyaml.helpers import App, get_variable, variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('binary_light'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.variable_id,
 | 
			
		||||
})
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
}).extend(light.LIGHT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    output = get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    output = None
 | 
			
		||||
    for output in get_variable(config[CONF_OUTPUT]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_binary_light(config[CONF_NAME], output)
 | 
			
		||||
    light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
 | 
			
		||||
    setup_mqtt_component(light_struct.Pmqtt, config)
 | 
			
		||||
    light.setup_light_component(light_struct.Pstate, config)
 | 
			
		||||
    light_struct = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								esphomeyaml/components/light/fastled_clockless.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								esphomeyaml/components/light/fastled_clockless.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import light
 | 
			
		||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
 | 
			
		||||
from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
 | 
			
		||||
    CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \
 | 
			
		||||
    CONF_RGB_ORDER
 | 
			
		||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
 | 
			
		||||
    get_variable, variable
 | 
			
		||||
 | 
			
		||||
TYPES = [
 | 
			
		||||
    'NEOPIXEL',
 | 
			
		||||
    'TM1829',
 | 
			
		||||
    'TM1809',
 | 
			
		||||
    'TM1804',
 | 
			
		||||
    'TM1803',
 | 
			
		||||
    'UCS1903',
 | 
			
		||||
    'UCS1903B',
 | 
			
		||||
    'UCS1904',
 | 
			
		||||
    'UCS2903',
 | 
			
		||||
    'WS2812',
 | 
			
		||||
    'WS2852',
 | 
			
		||||
    'WS2812B',
 | 
			
		||||
    'SK6812',
 | 
			
		||||
    'SK6822',
 | 
			
		||||
    'APA106',
 | 
			
		||||
    'PL9823',
 | 
			
		||||
    'WS2811',
 | 
			
		||||
    'WS2813',
 | 
			
		||||
    'APA104',
 | 
			
		||||
    'WS2811_400',
 | 
			
		||||
    'GW6205',
 | 
			
		||||
    'GW6205_400',
 | 
			
		||||
    'LPD1886',
 | 
			
		||||
    'LPD1886_8BIT',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
RGB_ORDERS = [
 | 
			
		||||
    'RGB',
 | 
			
		||||
    'RBG',
 | 
			
		||||
    'GRB',
 | 
			
		||||
    'GBR',
 | 
			
		||||
    'BRG',
 | 
			
		||||
    'BGR',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate(value):
 | 
			
		||||
    if value[CONF_CHIPSET] == 'NEOPIXEL' and CONF_RGB_ORDER in value:
 | 
			
		||||
        raise vol.Invalid("NEOPIXEL doesn't support RGB order")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MakeFastLEDLight = Application.MakeFastLEDLight
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(light.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight),
 | 
			
		||||
 | 
			
		||||
    vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*TYPES)),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.output_pin,
 | 
			
		||||
 | 
			
		||||
    vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
 | 
			
		||||
    vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
 | 
			
		||||
    vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)),
 | 
			
		||||
 | 
			
		||||
    vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
 | 
			
		||||
}).extend(light.LIGHT_SCHEMA.schema), validate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_fast_led_light(config[CONF_NAME])
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    fast_led = make.Pfast_led
 | 
			
		||||
 | 
			
		||||
    rgb_order = None
 | 
			
		||||
    if CONF_RGB_ORDER in config:
 | 
			
		||||
        rgb_order = RawExpression(config[CONF_RGB_ORDER])
 | 
			
		||||
    template_args = TemplateArguments(RawExpression(config[CONF_CHIPSET]),
 | 
			
		||||
                                      config[CONF_PIN], rgb_order)
 | 
			
		||||
    add(fast_led.add_leds(template_args, config[CONF_NUM_LEDS]))
 | 
			
		||||
 | 
			
		||||
    if CONF_MAX_REFRESH_RATE in config:
 | 
			
		||||
        add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
 | 
			
		||||
 | 
			
		||||
    if CONF_POWER_SUPPLY in config:
 | 
			
		||||
        power_supply = None
 | 
			
		||||
        for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(fast_led.set_power_supply(power_supply))
 | 
			
		||||
 | 
			
		||||
    light.setup_light(make.Pstate, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
 | 
			
		||||
							
								
								
									
										78
									
								
								esphomeyaml/components/light/fastled_spi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								esphomeyaml/components/light/fastled_spi.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import light
 | 
			
		||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
 | 
			
		||||
from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \
 | 
			
		||||
    CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \
 | 
			
		||||
    CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER
 | 
			
		||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
 | 
			
		||||
    get_variable, variable
 | 
			
		||||
 | 
			
		||||
CHIPSETS = [
 | 
			
		||||
    'LPD8806',
 | 
			
		||||
    'WS2801',
 | 
			
		||||
    'WS2803',
 | 
			
		||||
    'SM16716',
 | 
			
		||||
    'P9813',
 | 
			
		||||
    'APA102',
 | 
			
		||||
    'SK9822',
 | 
			
		||||
    'DOTSTAR',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
RGB_ORDERS = [
 | 
			
		||||
    'RGB',
 | 
			
		||||
    'RBG',
 | 
			
		||||
    'GRB',
 | 
			
		||||
    'GBR',
 | 
			
		||||
    'BRG',
 | 
			
		||||
    'BGR',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
MakeFastLEDLight = Application.MakeFastLEDLight
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight),
 | 
			
		||||
 | 
			
		||||
    vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*CHIPSETS)),
 | 
			
		||||
    vol.Required(CONF_DATA_PIN): pins.output_pin,
 | 
			
		||||
    vol.Required(CONF_CLOCK_PIN): pins.output_pin,
 | 
			
		||||
 | 
			
		||||
    vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
 | 
			
		||||
    vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)),
 | 
			
		||||
    vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
 | 
			
		||||
 | 
			
		||||
    vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
 | 
			
		||||
}).extend(light.LIGHT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_fast_led_light(config[CONF_NAME])
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    fast_led = make.Pfast_led
 | 
			
		||||
 | 
			
		||||
    rgb_order = None
 | 
			
		||||
    if CONF_RGB_ORDER in config:
 | 
			
		||||
        rgb_order = RawExpression(config[CONF_RGB_ORDER])
 | 
			
		||||
    template_args = TemplateArguments(RawExpression(config[CONF_CHIPSET]),
 | 
			
		||||
                                      config[CONF_DATA_PIN],
 | 
			
		||||
                                      config[CONF_CLOCK_PIN],
 | 
			
		||||
                                      rgb_order)
 | 
			
		||||
    add(fast_led.add_leds(template_args, config[CONF_NUM_LEDS]))
 | 
			
		||||
 | 
			
		||||
    if CONF_MAX_REFRESH_RATE in config:
 | 
			
		||||
        add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
 | 
			
		||||
 | 
			
		||||
    if CONF_POWER_SUPPLY in config:
 | 
			
		||||
        power_supply = None
 | 
			
		||||
        for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(fast_led.set_power_supply(power_supply))
 | 
			
		||||
 | 
			
		||||
        light.setup_light(make.Pstate, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
 | 
			
		||||
@@ -2,23 +2,22 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import light
 | 
			
		||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
 | 
			
		||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
 | 
			
		||||
    CONF_NAME, CONF_OUTPUT
 | 
			
		||||
from esphomeyaml.helpers import App, add, get_variable, variable, setup_mqtt_component
 | 
			
		||||
from esphomeyaml.helpers import App, get_variable, variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('monochromatic_light'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period,
 | 
			
		||||
})
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(light.LIGHT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    output = get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    output = None
 | 
			
		||||
    for output in get_variable(config[CONF_OUTPUT]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_monochromatic_light(config[CONF_NAME], output)
 | 
			
		||||
    light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_GAMMA_CORRECT in config:
 | 
			
		||||
        add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
 | 
			
		||||
    setup_mqtt_component(light_struct.Pmqtt, config)
 | 
			
		||||
    light.setup_light_component(light_struct.Pstate, config)
 | 
			
		||||
    light_struct = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,26 +3,29 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import light
 | 
			
		||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
 | 
			
		||||
    CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED
 | 
			
		||||
from esphomeyaml.helpers import App, add, get_variable, variable, setup_mqtt_component
 | 
			
		||||
    CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED
 | 
			
		||||
from esphomeyaml.helpers import App, get_variable, variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('rgb_light'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_RED): cv.variable_id,
 | 
			
		||||
    vol.Required(CONF_GREEN): cv.variable_id,
 | 
			
		||||
    vol.Required(CONF_BLUE): cv.variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
 | 
			
		||||
    vol.Required(CONF_RED): cv.use_variable_id(None),
 | 
			
		||||
    vol.Required(CONF_GREEN): cv.use_variable_id(None),
 | 
			
		||||
    vol.Required(CONF_BLUE): cv.use_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period,
 | 
			
		||||
})
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(light.LIGHT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    red = get_variable(config[CONF_RED])
 | 
			
		||||
    green = get_variable(config[CONF_GREEN])
 | 
			
		||||
    blue = get_variable(config[CONF_BLUE])
 | 
			
		||||
    red = None
 | 
			
		||||
    for red in get_variable(config[CONF_RED]):
 | 
			
		||||
        yield
 | 
			
		||||
    green = None
 | 
			
		||||
    for green in get_variable(config[CONF_GREEN]):
 | 
			
		||||
        yield
 | 
			
		||||
    blue = None
 | 
			
		||||
    for blue in get_variable(config[CONF_BLUE]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue)
 | 
			
		||||
    light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_GAMMA_CORRECT in config:
 | 
			
		||||
        add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
 | 
			
		||||
    setup_mqtt_component(light_struct.Pmqtt, config)
 | 
			
		||||
    light.setup_light_component(light_struct.Pstate, config)
 | 
			
		||||
    light_struct = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,28 +3,33 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import light
 | 
			
		||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
 | 
			
		||||
    CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE
 | 
			
		||||
from esphomeyaml.helpers import App, add, get_variable, setup_mqtt_component, variable
 | 
			
		||||
    CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE
 | 
			
		||||
from esphomeyaml.helpers import App, get_variable, variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('rgbw_light'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_RED): cv.variable_id,
 | 
			
		||||
    vol.Required(CONF_GREEN): cv.variable_id,
 | 
			
		||||
    vol.Required(CONF_BLUE): cv.variable_id,
 | 
			
		||||
    vol.Required(CONF_WHITE): cv.variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
 | 
			
		||||
    vol.Required(CONF_RED): cv.use_variable_id(None),
 | 
			
		||||
    vol.Required(CONF_GREEN): cv.use_variable_id(None),
 | 
			
		||||
    vol.Required(CONF_BLUE): cv.use_variable_id(None),
 | 
			
		||||
    vol.Required(CONF_WHITE): cv.use_variable_id(None),
 | 
			
		||||
    vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period,
 | 
			
		||||
})
 | 
			
		||||
    vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(light.LIGHT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    red = get_variable(config[CONF_RED])
 | 
			
		||||
    green = get_variable(config[CONF_GREEN])
 | 
			
		||||
    blue = get_variable(config[CONF_BLUE])
 | 
			
		||||
    white = get_variable(config[CONF_WHITE])
 | 
			
		||||
    red = None
 | 
			
		||||
    for red in get_variable(config[CONF_RED]):
 | 
			
		||||
        yield
 | 
			
		||||
    green = None
 | 
			
		||||
    for green in get_variable(config[CONF_GREEN]):
 | 
			
		||||
        yield
 | 
			
		||||
    blue = None
 | 
			
		||||
    for blue in get_variable(config[CONF_BLUE]):
 | 
			
		||||
        yield
 | 
			
		||||
    white = None
 | 
			
		||||
    for white in get_variable(config[CONF_WHITE]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white)
 | 
			
		||||
    light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_GAMMA_CORRECT in config:
 | 
			
		||||
        add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
 | 
			
		||||
    setup_mqtt_component(light_struct.Pmqtt, config)
 | 
			
		||||
    light.setup_light_component(light_struct.Pstate, config)
 | 
			
		||||
    light_struct = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,51 +1,61 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGGER, CONF_LOGS, \
 | 
			
		||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGS, \
 | 
			
		||||
    CONF_TX_BUFFER_SIZE
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, RawExpression, add
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, global_ns
 | 
			
		||||
 | 
			
		||||
LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE']
 | 
			
		||||
LOG_LEVELS = {
 | 
			
		||||
    'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE,
 | 
			
		||||
    'ERROR': global_ns.ESPHOMELIB_LOG_LEVEL_ERROR,
 | 
			
		||||
    'WARN': global_ns.ESPHOMELIB_LOG_LEVEL_WARN,
 | 
			
		||||
    'INFO': global_ns.ESPHOMELIB_LOG_LEVEL_INFO,
 | 
			
		||||
    'DEBUG': global_ns.ESPHOMELIB_LOG_LEVEL_DEBUG,
 | 
			
		||||
    'VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERBOSE,
 | 
			
		||||
    'VERY_VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERY_VERBOSE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS))
 | 
			
		||||
is_log_level = vol.All(vol.Upper, cv.one_of(*LOG_LEVELS))
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID(CONF_LOGGER): cv.register_variable_id,
 | 
			
		||||
 | 
			
		||||
def validate_local_no_higher_than_global(value):
 | 
			
		||||
    global_level = value.get(CONF_LEVEL, 'DEBUG')
 | 
			
		||||
    for tag, level in value.get(CONF_LOGS, {}).iteritems():
 | 
			
		||||
        if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
 | 
			
		||||
            raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the "
 | 
			
		||||
                                   u"global log level {}.".format(level, tag, global_level))
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LogComponent = esphomelib_ns.LogComponent
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(vol.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(LogComponent),
 | 
			
		||||
    vol.Optional(CONF_BAUD_RATE): cv.positive_int,
 | 
			
		||||
    vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int,
 | 
			
		||||
    vol.Optional(CONF_LEVEL): is_log_level,
 | 
			
		||||
    vol.Optional(CONF_LOGS): vol.Schema({
 | 
			
		||||
        cv.string: is_log_level,
 | 
			
		||||
    })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def esphomelib_log_level(level):
 | 
			
		||||
    return u'ESPHOMELIB_LOG_LEVEL_{}'.format(level)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_log_level(level):
 | 
			
		||||
    return RawExpression(esphomelib_log_level(level))
 | 
			
		||||
}), validate_local_no_higher_than_global)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.init_log(config.get(CONF_BAUD_RATE))
 | 
			
		||||
    log = Pvariable(u'LogComponent', config[CONF_ID], rhs)
 | 
			
		||||
    log = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_TX_BUFFER_SIZE in config:
 | 
			
		||||
        add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
 | 
			
		||||
    if CONF_LEVEL in config:
 | 
			
		||||
        add(log.set_global_log_level(exp_log_level(config[CONF_LEVEL])))
 | 
			
		||||
        add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]]))
 | 
			
		||||
    for tag, level in config.get(CONF_LOGS, {}).iteritems():
 | 
			
		||||
        global_level = config.get(CONF_LEVEL, 'DEBUG')
 | 
			
		||||
        if LOG_LEVELS.index(level) > LOG_LEVELS.index(global_level):
 | 
			
		||||
            raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the "
 | 
			
		||||
                                   u"global log level {}.".format(level, tag, global_level))
 | 
			
		||||
        add(log.set_log_level(tag, exp_log_level(level)))
 | 
			
		||||
        add(log.set_log_level(tag, LOG_LEVELS[level]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def required_build_flags(config):
 | 
			
		||||
    if CONF_LEVEL in config:
 | 
			
		||||
        return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL]))
 | 
			
		||||
        return u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]]))
 | 
			
		||||
    return None
 | 
			
		||||
 
 | 
			
		||||
@@ -3,26 +3,43 @@ import re
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import automation
 | 
			
		||||
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
 | 
			
		||||
    CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_SSL_FINGERPRINTS, CONF_ID, CONF_LOG_TOPIC, \
 | 
			
		||||
    CONF_MQTT, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, \
 | 
			
		||||
    CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE
 | 
			
		||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, StructInitializer, add, \
 | 
			
		||||
    exp_empty_optional, RawExpression
 | 
			
		||||
    CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LOG_TOPIC, \
 | 
			
		||||
    CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, \
 | 
			
		||||
    CONF_SSL_FINGERPRINTS, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, \
 | 
			
		||||
    CONF_WILL_MESSAGE
 | 
			
		||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
 | 
			
		||||
    StructInitializer, \
 | 
			
		||||
    TemplateArguments, add, esphomelib_ns, optional, std_string
 | 
			
		||||
 | 
			
		||||
MQTT_WILL_BIRTH_SCHEMA = vol.Any(None, vol.Schema({
 | 
			
		||||
 | 
			
		||||
def validate_message_just_topic(value):
 | 
			
		||||
    value = cv.publish_topic(value)
 | 
			
		||||
    return {CONF_TOPIC: value}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MQTT_MESSAGE_BASE = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_TOPIC): cv.publish_topic,
 | 
			
		||||
    vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
 | 
			
		||||
    vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
 | 
			
		||||
    vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
 | 
			
		||||
    vol.Optional(CONF_RETAIN, default=True): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MQTT_MESSAGE_TEMPLATE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE, validate_message_just_topic)
 | 
			
		||||
 | 
			
		||||
MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({
 | 
			
		||||
    vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
mqtt_ns = esphomelib_ns.namespace('mqtt')
 | 
			
		||||
MQTTMessage = mqtt_ns.MQTTMessage
 | 
			
		||||
MQTTClientComponent = mqtt_ns.MQTTClientComponent
 | 
			
		||||
MQTTPublishAction = mqtt_ns.MQTTPublishAction
 | 
			
		||||
MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_broker(value):
 | 
			
		||||
    value = cv.string_strict(value)
 | 
			
		||||
    if value.endswith(u'.local'):
 | 
			
		||||
        raise vol.Invalid(u"MQTT server addresses ending with '.local' are currently unsupported."
 | 
			
		||||
                          u" Please use the static IP instead.")
 | 
			
		||||
    if u':' in value:
 | 
			
		||||
        raise vol.Invalid(u"Please specify the port using the port: option")
 | 
			
		||||
    if not value:
 | 
			
		||||
@@ -38,7 +55,7 @@ def validate_fingerprint(value):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID(CONF_MQTT): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent),
 | 
			
		||||
    vol.Required(CONF_BROKER): validate_broker,
 | 
			
		||||
    vol.Optional(CONF_PORT, default=1883): cv.port,
 | 
			
		||||
    vol.Optional(CONF_USERNAME, default=''): cv.string,
 | 
			
		||||
@@ -47,22 +64,28 @@ CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Optional(CONF_DISCOVERY): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_DISCOVERY_RETAIN): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_DISCOVERY_PREFIX): cv.publish_topic,
 | 
			
		||||
    vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic,
 | 
			
		||||
    vol.Optional(CONF_LOG_TOPIC): cv.publish_topic,
 | 
			
		||||
    vol.Optional(CONF_LOG_TOPIC): MQTT_MESSAGE_TEMPLATE_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_SSL_FINGERPRINTS): vol.All(cv.only_on_esp8266,
 | 
			
		||||
                                                 cv.ensure_list, [validate_fingerprint]),
 | 
			
		||||
    vol.Optional(CONF_KEEPALIVE): cv.positive_time_period_seconds,
 | 
			
		||||
    vol.Optional(CONF_ON_MESSAGE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
 | 
			
		||||
        vol.Required(CONF_TOPIC): cv.publish_topic,
 | 
			
		||||
        vol.Optional(CONF_QOS, 0): cv.mqtt_qos,
 | 
			
		||||
    })])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_mqtt_message(config):
 | 
			
		||||
    if config is None:
 | 
			
		||||
        return exp_empty_optional('mqtt::MQTTMessage')
 | 
			
		||||
        return optional(TemplateArguments(MQTTMessage))
 | 
			
		||||
    exp = StructInitializer(
 | 
			
		||||
        'mqtt::MQTTMessage',
 | 
			
		||||
        MQTTMessage,
 | 
			
		||||
        ('topic', config[CONF_TOPIC]),
 | 
			
		||||
        ('payload', config[CONF_PAYLOAD]),
 | 
			
		||||
        ('payload', config.get(CONF_PAYLOAD, "")),
 | 
			
		||||
        ('qos', config[CONF_QOS]),
 | 
			
		||||
        ('retain', config[CONF_RETAIN])
 | 
			
		||||
    )
 | 
			
		||||
@@ -72,27 +95,46 @@ def exp_mqtt_message(config):
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.init_mqtt(config[CONF_BROKER], config[CONF_PORT],
 | 
			
		||||
                        config[CONF_USERNAME], config[CONF_PASSWORD])
 | 
			
		||||
    mqtt = Pvariable('mqtt::MQTTClientComponent', config[CONF_ID], rhs)
 | 
			
		||||
    mqtt = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if not config.get(CONF_DISCOVERY, True):
 | 
			
		||||
        add(mqtt.disable_discovery())
 | 
			
		||||
    if CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
 | 
			
		||||
        discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
 | 
			
		||||
        discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
 | 
			
		||||
        add(mqtt.set_discovery_info(discovery_prefix, discovery_retain))
 | 
			
		||||
    if CONF_BIRTH_MESSAGE in config:
 | 
			
		||||
        add(mqtt.set_birth_message(config[CONF_BIRTH_MESSAGE]))
 | 
			
		||||
    if CONF_WILL_MESSAGE in config:
 | 
			
		||||
        add(mqtt.set_last_will(config[CONF_WILL_MESSAGE]))
 | 
			
		||||
    if CONF_TOPIC_PREFIX in config:
 | 
			
		||||
        add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
 | 
			
		||||
    if CONF_BIRTH_MESSAGE in config:
 | 
			
		||||
        birth_message = config[CONF_BIRTH_MESSAGE]
 | 
			
		||||
        if birth_message is None:
 | 
			
		||||
            add(mqtt.disable_birth_message())
 | 
			
		||||
        else:
 | 
			
		||||
            add(mqtt.set_birth_message(exp_mqtt_message(birth_message)))
 | 
			
		||||
    if CONF_WILL_MESSAGE in config:
 | 
			
		||||
        will_message = config[CONF_WILL_MESSAGE]
 | 
			
		||||
        if will_message is None:
 | 
			
		||||
            add(mqtt.disable_last_will())
 | 
			
		||||
        else:
 | 
			
		||||
            add(mqtt.set_last_will(exp_mqtt_message(will_message)))
 | 
			
		||||
    if CONF_CLIENT_ID in config:
 | 
			
		||||
        add(mqtt.set_client_id(config[CONF_CLIENT_ID]))
 | 
			
		||||
    if CONF_LOG_TOPIC in config:
 | 
			
		||||
        add(mqtt.set_log_topic(config[CONF_LOG_TOPIC]))
 | 
			
		||||
        log_topic = config[CONF_LOG_TOPIC]
 | 
			
		||||
        if log_topic is None:
 | 
			
		||||
            add(mqtt.disable_log_message())
 | 
			
		||||
        else:
 | 
			
		||||
            add(mqtt.set_log_topic(exp_mqtt_message(log_topic)))
 | 
			
		||||
    if CONF_SSL_FINGERPRINTS in config:
 | 
			
		||||
        for fingerprint in config[CONF_SSL_FINGERPRINTS]:
 | 
			
		||||
            arr = [RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)]
 | 
			
		||||
            add(mqtt.add_ssl_fingerprint(ArrayInitializer(*arr, multiline=False)))
 | 
			
		||||
    if CONF_KEEPALIVE in config:
 | 
			
		||||
        add(mqtt.set_keep_alive(config[CONF_KEEPALIVE]))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_MESSAGE, []):
 | 
			
		||||
        rhs = mqtt.make_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS])
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, std_string, conf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def required_build_flags(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,14 @@ from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \
 | 
			
		||||
    ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
OTAComponent = esphomelib_ns.OTAComponent
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID(CONF_OTA): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(OTAComponent),
 | 
			
		||||
    vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
 | 
			
		||||
    # TODO Num attempts + wait time
 | 
			
		||||
    vol.Optional(CONF_PORT): cv.port,
 | 
			
		||||
@@ -23,7 +25,7 @@ CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.init_ota()
 | 
			
		||||
    ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
 | 
			
		||||
    ota = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_PASSWORD in config:
 | 
			
		||||
        hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
 | 
			
		||||
        add(ota.set_auth_password_hash(hash_))
 | 
			
		||||
@@ -45,5 +47,10 @@ def get_auth(config):
 | 
			
		||||
    return config[CONF_OTA].get(CONF_PASSWORD, '')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_OTA'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_OTA'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lib_deps(config):
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        return ['ArduinoOTA', 'Update', 'ESPmDNS']
 | 
			
		||||
    return ['Hash', 'ESP8266mDNS', 'ArduinoOTA']
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +1,36 @@
 | 
			
		||||
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_POWER_SUPPLY, CONF_INVERTED, CONF_MAX_POWER
 | 
			
		||||
from esphomeyaml.helpers import get_variable, add
 | 
			
		||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
 | 
			
		||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY
 | 
			
		||||
from esphomeyaml.helpers import add, esphomelib_ns, get_variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Optional(CONF_POWER_SUPPLY): cv.variable_id,
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
}).extend(cv.REQUIRED_ID_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
FLOAT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Optional(CONF_MAX_POWER): cv.zero_to_one_float,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
BINARY_OUTPUT_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({
 | 
			
		||||
    vol.Optional(CONF_MAX_POWER): cv.percentage,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
output_ns = esphomelib_ns.namespace('output')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_output_platform(obj, config, skip_power_supply=False):
 | 
			
		||||
    if CONF_INVERTED in config:
 | 
			
		||||
        add(obj.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
    if not skip_power_supply and CONF_POWER_SUPPLY in config:
 | 
			
		||||
        power_supply = get_variable(config[CONF_POWER_SUPPLY])
 | 
			
		||||
        power_supply = None
 | 
			
		||||
        for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(obj.set_power_supply(power_supply))
 | 
			
		||||
    if CONF_MAX_POWER in config:
 | 
			
		||||
        add(obj.set_max_power(config[CONF_MAX_POWER]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +1,36 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import output
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def valid_pwm_pin(value):
 | 
			
		||||
    if value >= 16:
 | 
			
		||||
    if value[CONF_NUMBER] >= 16:
 | 
			
		||||
        raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_PIN): vol.All(pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
                                    pins.schema_validate_number(valid_pwm_pin)),
 | 
			
		||||
})
 | 
			
		||||
ESP8266PWMOutput = output.output_ns.ESP8266PWMOutput
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_ID): cv.declare_variable_id(ESP8266PWMOutput),
 | 
			
		||||
    vol.Required(CONF_PIN): vol.All(pins.GPIO_INTERNAL_OUTPUT_PIN_SCHEMA, valid_pwm_pin),
 | 
			
		||||
}).extend(output.FLOAT_OUTPUT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    pin = exp_gpio_output_pin(config[CONF_PIN])
 | 
			
		||||
    pin = None
 | 
			
		||||
    for pin in gpio_output_pin_expression(config[CONF_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_esp8266_pwm_output(pin)
 | 
			
		||||
    gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs)
 | 
			
		||||
    gpio = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    output.setup_output_platform(gpio, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_ESP8266_PWM_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,26 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import output
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
 | 
			
		||||
 | 
			
		||||
GPIOBinaryOutputComponent = output.output_ns.GPIOBinaryOutputComponent
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_ID): cv.declare_variable_id(GPIOBinaryOutputComponent),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
})
 | 
			
		||||
}).extend(output.BINARY_OUTPUT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    pin = exp_gpio_output_pin(config[CONF_PIN])
 | 
			
		||||
    pin = None
 | 
			
		||||
    for pin in gpio_output_pin_expression(config[CONF_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_gpio_output(pin)
 | 
			
		||||
    gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs)
 | 
			
		||||
    gpio = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    output.setup_output_platform(gpio, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_GPIO_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_GPIO_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,15 @@ def validate_frequency_bit_depth(obj):
 | 
			
		||||
    return obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(output.FLOAT_PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_PIN): vol.All(pins.output_pin, vol.Range(min=0, max=33)),
 | 
			
		||||
LEDCOutputComponent = output.output_ns.LEDCOutputComponent
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(output.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_ID): cv.declare_variable_id(LEDCOutputComponent),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.output_pin,
 | 
			
		||||
    vol.Optional(CONF_FREQUENCY): cv.frequency,
 | 
			
		||||
    vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int), vol.Range(min=1, max=15)),
 | 
			
		||||
    vol.Optional(CONF_CHANNEL): vol.All(vol.Coerce(int), vol.Range(min=0, max=15))
 | 
			
		||||
}), validate_frequency_bit_depth)
 | 
			
		||||
}).extend(output.FLOAT_OUTPUT_SCHEMA.schema), validate_frequency_bit_depth)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
@@ -32,11 +35,10 @@ def to_code(config):
 | 
			
		||||
    if frequency is None and CONF_BIT_DEPTH in config:
 | 
			
		||||
        frequency = 1000
 | 
			
		||||
    rhs = App.make_ledc_output(config[CONF_PIN], frequency, config.get(CONF_BIT_DEPTH))
 | 
			
		||||
    ledc = Pvariable('output::LEDCOutputComponent', config[CONF_ID], rhs)
 | 
			
		||||
    ledc = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_CHANNEL in config:
 | 
			
		||||
        add(ledc.set_channel(config[CONF_CHANNEL]))
 | 
			
		||||
    output.setup_output_platform(ledc, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_LEDC_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_LEDC_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
@@ -2,28 +2,33 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import output
 | 
			
		||||
from esphomeyaml.components.pca9685 import PCA9685_COMPONENT_TYPE
 | 
			
		||||
from esphomeyaml.components.pca9685 import PCA9685OutputComponent
 | 
			
		||||
from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_PCA9685_ID, CONF_POWER_SUPPLY
 | 
			
		||||
from esphomeyaml.helpers import Pvariable, get_variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['pca9685']
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
 | 
			
		||||
Channel = PCA9685OutputComponent.Channel
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Required(CONF_ID): cv.declare_variable_id(Channel),
 | 
			
		||||
    vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int),
 | 
			
		||||
                                        vol.Range(min=0, max=15)),
 | 
			
		||||
    vol.Optional(CONF_PCA9685_ID): cv.variable_id,
 | 
			
		||||
})
 | 
			
		||||
    cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685OutputComponent),
 | 
			
		||||
}).extend(output.FLOAT_OUTPUT_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    power_supply = None
 | 
			
		||||
    if CONF_POWER_SUPPLY in config:
 | 
			
		||||
        power_supply = get_variable(config[CONF_POWER_SUPPLY])
 | 
			
		||||
    pca9685 = get_variable(config.get(CONF_PCA9685_ID), PCA9685_COMPONENT_TYPE)
 | 
			
		||||
        for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
 | 
			
		||||
            yield
 | 
			
		||||
    pca9685 = None
 | 
			
		||||
    for pca9685 in get_variable(config[CONF_PCA9685_ID]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
 | 
			
		||||
    out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs)
 | 
			
		||||
    out = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    output.setup_output_platform(out, config, skip_power_supply=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_PCA9685_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,24 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import output
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID, CONF_PHASE_BALANCER
 | 
			
		||||
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, RawExpression, add
 | 
			
		||||
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, add
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
PHASE_BALANCERS = ['None', 'Linear', 'Weaved']
 | 
			
		||||
PCA9685OutputComponent = output.output_ns.namespace('PCA9685OutputComponent')
 | 
			
		||||
 | 
			
		||||
PCA9685_COMPONENT_TYPE = 'output::PCA9685OutputComponent'
 | 
			
		||||
PHASE_BALANCER_MESSAGE = ("The phase_balancer option has been removed in version 1.5.0. "
 | 
			
		||||
                          "esphomelib will now automatically choose a suitable phase balancer.")
 | 
			
		||||
 | 
			
		||||
PCA9685_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID('pca9685'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(PCA9685OutputComponent),
 | 
			
		||||
    vol.Required(CONF_FREQUENCY): vol.All(cv.frequency,
 | 
			
		||||
                                          vol.Range(min=24, max=1526)),
 | 
			
		||||
    vol.Optional(CONF_PHASE_BALANCER): vol.All(vol.Title, vol.Any(*PHASE_BALANCERS)),
 | 
			
		||||
                                          vol.Range(min=23.84, max=1525.88)),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS): cv.i2c_address,
 | 
			
		||||
 | 
			
		||||
    vol.Optional(CONF_PHASE_BALANCER): cv.invalid(PHASE_BALANCER_MESSAGE),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA])
 | 
			
		||||
@@ -24,14 +27,9 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA])
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        rhs = App.make_pca9685_component(conf.get(CONF_FREQUENCY))
 | 
			
		||||
        pca9685 = Pvariable(PCA9685_COMPONENT_TYPE, conf[CONF_ID], rhs)
 | 
			
		||||
        pca9685 = Pvariable(conf[CONF_ID], rhs)
 | 
			
		||||
        if CONF_ADDRESS in conf:
 | 
			
		||||
            add(pca9685.set_address(HexIntLiteral(conf[CONF_ADDRESS])))
 | 
			
		||||
        if CONF_PHASE_BALANCER in conf:
 | 
			
		||||
            phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format(
 | 
			
		||||
                conf[CONF_PHASE_BALANCER]))
 | 
			
		||||
            add(pca9685.set_phase_balancer(phase_balancer))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_PCA9685_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								esphomeyaml/components/pcf8574.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphomeyaml/components/pcf8574.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_PCF8575
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
io_ns = esphomelib_ns.namespace('io')
 | 
			
		||||
PCF8574Component = io_ns.PCF8574Component
 | 
			
		||||
 | 
			
		||||
PCF8574_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_ID): cv.declare_variable_id(PCF8574Component),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_PCF8575, default=False): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575])
 | 
			
		||||
        Pvariable(conf[CONF_ID], rhs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_PCF8574'
 | 
			
		||||
@@ -3,12 +3,15 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_output_pin
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression
 | 
			
		||||
 | 
			
		||||
POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({
 | 
			
		||||
PowerSupplyComponent = esphomelib_ns.PowerSupplyComponent
 | 
			
		||||
 | 
			
		||||
POWER_SUPPLY_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_ID): cv.declare_variable_id(PowerSupplyComponent),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period,
 | 
			
		||||
    vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period,
 | 
			
		||||
    vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA])
 | 
			
		||||
@@ -16,14 +19,15 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA])
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        pin = exp_gpio_output_pin(conf[CONF_PIN])
 | 
			
		||||
        pin = None
 | 
			
		||||
        for pin in gpio_output_pin_expression(conf[CONF_PIN]):
 | 
			
		||||
            yield
 | 
			
		||||
        rhs = App.make_power_supply(pin)
 | 
			
		||||
        psu = Pvariable('PowerSupplyComponent', conf[CONF_ID], rhs)
 | 
			
		||||
        psu = Pvariable(conf[CONF_ID], rhs)
 | 
			
		||||
        if CONF_ENABLE_TIME in conf:
 | 
			
		||||
            add(psu.set_enable_time(conf[CONF_ENABLE_TIME]))
 | 
			
		||||
        if CONF_KEEP_ON_TIME in conf:
 | 
			
		||||
            add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_OUTPUT'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_OUTPUT'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,108 +1,196 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_EXPIRE_AFTER, \
 | 
			
		||||
    CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_ICON, \
 | 
			
		||||
    CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_SEND_EVERY, \
 | 
			
		||||
    CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, CONF_ID
 | 
			
		||||
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
 | 
			
		||||
    setup_mqtt_component
 | 
			
		||||
from esphomeyaml import automation
 | 
			
		||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \
 | 
			
		||||
    CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, \
 | 
			
		||||
    CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_LAMBDA, \
 | 
			
		||||
    CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE,\
 | 
			
		||||
    CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \
 | 
			
		||||
    CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
 | 
			
		||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, esphomelib_ns, float_, \
 | 
			
		||||
    process_lambda, setup_mqtt_component, templatable, add_job
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.Any(
 | 
			
		||||
    vol.Schema({vol.Required(CONF_OFFSET): vol.Coerce(float)}),
 | 
			
		||||
    vol.Schema({vol.Required(CONF_MULTIPLY): vol.Coerce(float)}),
 | 
			
		||||
    vol.Schema({vol.Required(CONF_FILTER_OUT): vol.Coerce(float)}),
 | 
			
		||||
    vol.Schema({vol.Required(CONF_FILTER_NAN): None}),
 | 
			
		||||
    vol.Schema({
 | 
			
		||||
        vol.Required(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
 | 
			
		||||
            vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
 | 
			
		||||
            vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
 | 
			
		||||
        })
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Schema({
 | 
			
		||||
        vol.Required(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
 | 
			
		||||
            vol.Required(CONF_ALPHA): cv.positive_float,
 | 
			
		||||
            vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
 | 
			
		||||
        })
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Schema({vol.Required(CONF_LAMBDA): cv.string_strict}),
 | 
			
		||||
)])
 | 
			
		||||
 | 
			
		||||
MQTT_SENSOR_SCHEMA = vol.Schema({
 | 
			
		||||
def validate_recursive_filter(value):
 | 
			
		||||
    return FILTERS_SCHEMA(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FILTER_KEYS = [CONF_OFFSET, CONF_MULTIPLY, CONF_FILTER_OUT, CONF_FILTER_NAN,
 | 
			
		||||
               CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_LAMBDA,
 | 
			
		||||
               CONF_THROTTLE, CONF_DELTA, CONF_UNIQUE, CONF_HEARTBEAT, CONF_DEBOUNCE, CONF_OR]
 | 
			
		||||
 | 
			
		||||
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
 | 
			
		||||
    vol.Optional(CONF_OFFSET): vol.Coerce(float),
 | 
			
		||||
    vol.Optional(CONF_MULTIPLY): vol.Coerce(float),
 | 
			
		||||
    vol.Optional(CONF_FILTER_OUT): vol.Coerce(float),
 | 
			
		||||
    vol.Optional(CONF_FILTER_NAN): None,
 | 
			
		||||
    vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
 | 
			
		||||
        vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ALPHA): cv.positive_float,
 | 
			
		||||
        vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
    vol.Optional(CONF_THROTTLE): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_DELTA): vol.Coerce(float),
 | 
			
		||||
    vol.Optional(CONF_UNIQUE): None,
 | 
			
		||||
    vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_DEBOUNCE): cv.positive_time_period_milliseconds,
 | 
			
		||||
    vol.Optional(CONF_OR): validate_recursive_filter,
 | 
			
		||||
}, cv.has_exactly_one_key(*FILTER_KEYS))])
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
sensor_ns = esphomelib_ns.namespace('sensor')
 | 
			
		||||
Sensor = sensor_ns.Sensor
 | 
			
		||||
MQTTSensorComponent = sensor_ns.MQTTSensorComponent
 | 
			
		||||
OffsetFilter = sensor_ns.OffsetFilter
 | 
			
		||||
MultiplyFilter = sensor_ns.MultiplyFilter
 | 
			
		||||
FilterOutValueFilter = sensor_ns.FilterOutValueFilter
 | 
			
		||||
FilterOutNANFilter = sensor_ns.FilterOutNANFilter
 | 
			
		||||
SlidingWindowMovingAverageFilter = sensor_ns.SlidingWindowMovingAverageFilter
 | 
			
		||||
ExponentialMovingAverageFilter = sensor_ns.ExponentialMovingAverageFilter
 | 
			
		||||
LambdaFilter = sensor_ns.LambdaFilter
 | 
			
		||||
ThrottleFilter = sensor_ns.ThrottleFilter
 | 
			
		||||
DeltaFilter = sensor_ns.DeltaFilter
 | 
			
		||||
OrFilter = sensor_ns.OrFilter
 | 
			
		||||
HeartbeatFilter = sensor_ns.HeartbeatFilter
 | 
			
		||||
DebounceFilter = sensor_ns.DebounceFilter
 | 
			
		||||
UniqueFilter = sensor_ns.UniqueFilter
 | 
			
		||||
 | 
			
		||||
SensorValueTrigger = sensor_ns.SensorValueTrigger
 | 
			
		||||
RawSensorValueTrigger = sensor_ns.RawSensorValueTrigger
 | 
			
		||||
ValueRangeTrigger = sensor_ns.ValueRangeTrigger
 | 
			
		||||
 | 
			
		||||
SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSensorComponent),
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(Sensor),
 | 
			
		||||
    vol.Required(CONF_NAME): cv.string,
 | 
			
		||||
    vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict,
 | 
			
		||||
    vol.Optional(CONF_ICON): cv.icon,
 | 
			
		||||
    vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int),
 | 
			
		||||
    vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period),
 | 
			
		||||
    vol.Optional(CONF_FILTERS): FILTERS_SCHEMA
 | 
			
		||||
    vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period_milliseconds),
 | 
			
		||||
    vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_ON_VALUE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorValueTrigger),
 | 
			
		||||
    })]),
 | 
			
		||||
    vol.Optional(CONF_ON_RAW_VALUE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(RawSensorValueTrigger),
 | 
			
		||||
    })]),
 | 
			
		||||
    vol.Optional(CONF_ON_VALUE_RANGE): vol.All(cv.ensure_list, [vol.All(
 | 
			
		||||
        automation.AUTOMATION_SCHEMA.extend({
 | 
			
		||||
            cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ValueRangeTrigger),
 | 
			
		||||
            vol.Optional(CONF_ABOVE): vol.Coerce(float),
 | 
			
		||||
            vol.Optional(CONF_BELOW): vol.Coerce(float),
 | 
			
		||||
        }), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW))]),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('mqtt_sensor', CONF_MQTT_ID): cv.register_variable_id,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
OffsetFilter = MockObj('new sensor::OffsetFilter')
 | 
			
		||||
MultiplyFilter = MockObj('new sensor::MultiplyFilter')
 | 
			
		||||
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
 | 
			
		||||
FilterOutNANFilter = MockObj('new sensor::FilterOutNANFilter')
 | 
			
		||||
SlidingWindowMovingAverageFilter = MockObj('new sensor::SlidingWindowMovingAverageFilter')
 | 
			
		||||
ExponentialMovingAverageFilter = MockObj('new sensor::ExponentialMovingAverageFilter')
 | 
			
		||||
LambdaFilter = MockObj('new sensor::LambdaFilter')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_filter(config):
 | 
			
		||||
    if CONF_OFFSET in config:
 | 
			
		||||
        return OffsetFilter(config[CONF_OFFSET])
 | 
			
		||||
    if CONF_MULTIPLY in config:
 | 
			
		||||
        return MultiplyFilter(config[CONF_MULTIPLY])
 | 
			
		||||
    if CONF_FILTER_OUT in config:
 | 
			
		||||
        return FilterOutValueFilter(config[CONF_FILTER_OUT])
 | 
			
		||||
    if CONF_FILTER_NAN in config:
 | 
			
		||||
        return FilterOutNANFilter()
 | 
			
		||||
    if CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
 | 
			
		||||
        yield OffsetFilter.new(config[CONF_OFFSET])
 | 
			
		||||
    elif CONF_MULTIPLY in config:
 | 
			
		||||
        yield MultiplyFilter.new(config[CONF_MULTIPLY])
 | 
			
		||||
    elif CONF_FILTER_OUT in config:
 | 
			
		||||
        yield FilterOutValueFilter.new(config[CONF_FILTER_OUT])
 | 
			
		||||
    elif CONF_FILTER_NAN in config:
 | 
			
		||||
        yield FilterOutNANFilter()
 | 
			
		||||
    elif CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
 | 
			
		||||
        conf = config[CONF_SLIDING_WINDOW_MOVING_AVERAGE]
 | 
			
		||||
        return SlidingWindowMovingAverageFilter(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
 | 
			
		||||
    if CONF_EXPONENTIAL_MOVING_AVERAGE in config:
 | 
			
		||||
        yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
 | 
			
		||||
    elif CONF_EXPONENTIAL_MOVING_AVERAGE in config:
 | 
			
		||||
        conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE]
 | 
			
		||||
        return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
        s = u'[](float x) -> Optional<float> {{ return {}; }}'.format(config[CONF_LAMBDA])
 | 
			
		||||
        return LambdaFilter(RawExpression(s))
 | 
			
		||||
    raise ValueError(u"Filter unsupported: {}".format(config))
 | 
			
		||||
        yield ExponentialMovingAverageFilter.new(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
 | 
			
		||||
    elif CONF_LAMBDA in config:
 | 
			
		||||
        lambda_ = None
 | 
			
		||||
        for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')]):
 | 
			
		||||
            yield None
 | 
			
		||||
        yield LambdaFilter.new(lambda_)
 | 
			
		||||
    elif CONF_THROTTLE in config:
 | 
			
		||||
        yield ThrottleFilter.new(config[CONF_THROTTLE])
 | 
			
		||||
    elif CONF_DELTA in config:
 | 
			
		||||
        yield DeltaFilter.new(config[CONF_DELTA])
 | 
			
		||||
    elif CONF_OR in config:
 | 
			
		||||
        yield OrFilter.new(setup_filters(config[CONF_OR]))
 | 
			
		||||
    elif CONF_HEARTBEAT in config:
 | 
			
		||||
        yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT]))
 | 
			
		||||
    elif CONF_DEBOUNCE in config:
 | 
			
		||||
        yield App.register_component(DebounceFilter.new(config[CONF_DEBOUNCE]))
 | 
			
		||||
    elif CONF_UNIQUE in config:
 | 
			
		||||
        yield UniqueFilter.new()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_mqtt_sensor_component(obj, config):
 | 
			
		||||
def setup_filters(config):
 | 
			
		||||
    filters = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        filter = None
 | 
			
		||||
        for filter in setup_filter(conf):
 | 
			
		||||
            yield
 | 
			
		||||
        filters.append(filter)
 | 
			
		||||
    yield ArrayInitializer(*filters)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_sensor_core_(sensor_var, mqtt_var, config):
 | 
			
		||||
    if CONF_UNIT_OF_MEASUREMENT in config:
 | 
			
		||||
        add(sensor_var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
 | 
			
		||||
    if CONF_ICON in config:
 | 
			
		||||
        add(sensor_var.set_icon(config[CONF_ICON]))
 | 
			
		||||
    if CONF_ACCURACY_DECIMALS in config:
 | 
			
		||||
        add(sensor_var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
 | 
			
		||||
    if CONF_FILTERS in config:
 | 
			
		||||
        filters = None
 | 
			
		||||
        for filters in setup_filters(config[CONF_FILTERS]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(sensor_var.set_filters(filters))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_VALUE, []):
 | 
			
		||||
        rhs = sensor_var.make_value_trigger()
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, float_, conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_RAW_VALUE, []):
 | 
			
		||||
        rhs = sensor_var.make_raw_value_trigger()
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        automation.build_automation(trigger, float_, conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_VALUE_RANGE, []):
 | 
			
		||||
        rhs = sensor_var.make_value_range_trigger()
 | 
			
		||||
        trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
 | 
			
		||||
        if CONF_ABOVE in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_ABOVE], float_, float_):
 | 
			
		||||
                yield
 | 
			
		||||
            trigger.set_min(template_)
 | 
			
		||||
        if CONF_BELOW in conf:
 | 
			
		||||
            template_ = None
 | 
			
		||||
            for template_ in templatable(conf[CONF_BELOW], float_, float_):
 | 
			
		||||
                yield
 | 
			
		||||
            trigger.set_max(template_)
 | 
			
		||||
        automation.build_automation(trigger, float_, conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_EXPIRE_AFTER in config:
 | 
			
		||||
        if config[CONF_EXPIRE_AFTER] is None:
 | 
			
		||||
            add(obj.disable_expire_after())
 | 
			
		||||
            add(mqtt_var.disable_expire_after())
 | 
			
		||||
        else:
 | 
			
		||||
            add(obj.set_expire_after(config[CONF_EXPIRE_AFTER]))
 | 
			
		||||
    setup_mqtt_component(obj, config)
 | 
			
		||||
            add(mqtt_var.set_expire_after(config[CONF_EXPIRE_AFTER]))
 | 
			
		||||
    setup_mqtt_component(mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_sensor(obj, config):
 | 
			
		||||
    if CONF_UNIT_OF_MEASUREMENT in config:
 | 
			
		||||
        add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
 | 
			
		||||
    if CONF_ICON in config:
 | 
			
		||||
        add(obj.set_icon(config[CONF_ICON]))
 | 
			
		||||
    if CONF_ACCURACY_DECIMALS in config:
 | 
			
		||||
        add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
 | 
			
		||||
    if CONF_FILTERS in config:
 | 
			
		||||
        filters = [setup_filter(x) for x in config[CONF_FILTERS]]
 | 
			
		||||
        add(obj.set_filters(ArrayInitializer(*filters)))
 | 
			
		||||
def setup_sensor(sensor_obj, mqtt_obj, config):
 | 
			
		||||
    sensor_var = Pvariable(config[CONF_ID], sensor_obj, has_side_effects=False)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
 | 
			
		||||
    add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_sensor(var, config):
 | 
			
		||||
    setup_sensor(var, config)
 | 
			
		||||
    rhs = App.register_sensor(var)
 | 
			
		||||
    mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_MQTT_ID], rhs)
 | 
			
		||||
    setup_mqtt_sensor_component(mqtt_sensor, config)
 | 
			
		||||
    sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
 | 
			
		||||
    rhs = App.register_sensor(sensor_var)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
 | 
			
		||||
    add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -3,38 +3,52 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ATTENUATION, CONF_ID, CONF_NAME, CONF_PIN, \
 | 
			
		||||
from esphomeyaml.const import CONF_ATTENUATION, CONF_MAKE_ID, CONF_NAME, CONF_PIN, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, RawExpression, add, variable
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, global_ns, variable
 | 
			
		||||
 | 
			
		||||
ATTENUATION_MODES = {
 | 
			
		||||
    '0db': 'ADC_0db',
 | 
			
		||||
    '2.5db': 'ADC_2_5db',
 | 
			
		||||
    '6db': 'ADC_6db',
 | 
			
		||||
    '11db': 'ADC_11db',
 | 
			
		||||
    '0db': global_ns.ADC_0db,
 | 
			
		||||
    '2.5db': global_ns.ADC_2_5db,
 | 
			
		||||
    '6db': global_ns.ADC_6db,
 | 
			
		||||
    '11db': global_ns.ADC_11db,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ATTENUATION_MODE_SCHEMA = vol.Any(*list(ATTENUATION_MODES.keys()))
 | 
			
		||||
 | 
			
		||||
def validate_adc_pin(value):
 | 
			
		||||
    vcc = str(value).upper()
 | 
			
		||||
    if vcc == 'VCC':
 | 
			
		||||
        return cv.only_on_esp8266(vcc)
 | 
			
		||||
    return pins.analog_pin(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MakeADCSensor = Application.MakeADCSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('adc'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_PIN): pins.analog_pin,
 | 
			
		||||
    vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, ATTENUATION_MODE_SCHEMA),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeADCSensor),
 | 
			
		||||
    vol.Required(CONF_PIN): validate_adc_pin,
 | 
			
		||||
    vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_adc_sensor(config[CONF_NAME], config[CONF_PIN],
 | 
			
		||||
    pin = config[CONF_PIN]
 | 
			
		||||
    if pin == 'VCC':
 | 
			
		||||
        pin = 0
 | 
			
		||||
    rhs = App.make_adc_sensor(config[CONF_NAME], pin,
 | 
			
		||||
                              config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable('Application::MakeADCSensor', config[CONF_ID], rhs)
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    adc = make.Padc
 | 
			
		||||
    if CONF_ATTENUATION in config:
 | 
			
		||||
        attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]]
 | 
			
		||||
        add(adc.set_attenuation(RawExpression(attenuation)))
 | 
			
		||||
    sensor.setup_sensor(adc, config)
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
 | 
			
		||||
        add(adc.set_attenuation(ATTENUATION_MODES[config[CONF_ATTENUATION]]))
 | 
			
		||||
    sensor.setup_sensor(make.Padc, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_ADC_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ADC_SENSOR'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def required_build_flags(config):
 | 
			
		||||
    if config[CONF_PIN] == 'VCC':
 | 
			
		||||
        return '-DUSE_ADC_SENSOR_VCC'
 | 
			
		||||
    return None
 | 
			
		||||
 
 | 
			
		||||
@@ -2,30 +2,31 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \
 | 
			
		||||
    CONF_NAME, CONF_ID
 | 
			
		||||
from esphomeyaml.helpers import RawExpression, get_variable, Pvariable
 | 
			
		||||
from esphomeyaml.components.ads1115 import ADS1115Component
 | 
			
		||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import get_variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['ads1115']
 | 
			
		||||
 | 
			
		||||
MUX = {
 | 
			
		||||
    'A0_A1': 'ADS1115_MUX_P0_N1',
 | 
			
		||||
    'A0_A3': 'ADS1115_MUX_P0_N3',
 | 
			
		||||
    'A1_A3': 'ADS1115_MUX_P1_N3',
 | 
			
		||||
    'A2_A3': 'ADS1115_MUX_P2_N3',
 | 
			
		||||
    'A0_GND': 'ADS1115_MUX_P0_NG',
 | 
			
		||||
    'A1_GND': 'ADS1115_MUX_P1_NG',
 | 
			
		||||
    'A2_GND': 'ADS1115_MUX_P2_NG',
 | 
			
		||||
    'A3_GND': 'ADS1115_MUX_P3_NG',
 | 
			
		||||
    'A0_A1': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N1,
 | 
			
		||||
    'A0_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N3,
 | 
			
		||||
    'A1_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_N3,
 | 
			
		||||
    'A2_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_N3,
 | 
			
		||||
    'A0_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_NG,
 | 
			
		||||
    'A1_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_NG,
 | 
			
		||||
    'A2_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_NG,
 | 
			
		||||
    'A3_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P3_NG,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GAIN = {
 | 
			
		||||
    '6.144': 'ADS1115_PGA_6P144',
 | 
			
		||||
    '4.096': 'ADS1115_PGA_6P096',
 | 
			
		||||
    '2.048': 'ADS1115_PGA_2P048',
 | 
			
		||||
    '1.024': 'ADS1115_PGA_1P024',
 | 
			
		||||
    '0.512': 'ADS1115_PGA_0P512',
 | 
			
		||||
    '0.256': 'ADS1115_PGA_0P256',
 | 
			
		||||
    '6.144': sensor.sensor_ns.ADS1115_GAIN_6P144,
 | 
			
		||||
    '4.096': sensor.sensor_ns.ADS1115_GAIN_6P096,
 | 
			
		||||
    '2.048': sensor.sensor_ns.ADS1115_GAIN_2P048,
 | 
			
		||||
    '1.024': sensor.sensor_ns.ADS1115_GAIN_1P024,
 | 
			
		||||
    '0.512': sensor.sensor_ns.ADS1115_GAIN_0P512,
 | 
			
		||||
    '0.256': sensor.sensor_ns.ADS1115_GAIN_0P256,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -35,29 +36,32 @@ def validate_gain(value):
 | 
			
		||||
    elif not isinstance(value, (str, unicode)):
 | 
			
		||||
        raise vol.Invalid('invalid gain "{}"'.format(value))
 | 
			
		||||
 | 
			
		||||
    if value not in GAIN:
 | 
			
		||||
        raise vol.Invalid("Invalid gain, options are {}".format(', '.join(GAIN.keys())))
 | 
			
		||||
    return value
 | 
			
		||||
    return cv.one_of(*GAIN)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_mux(value):
 | 
			
		||||
    value = cv.string(value).upper()
 | 
			
		||||
    value = value.replace(' ', '_')
 | 
			
		||||
    return cv.one_of(*MUX)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('ads1115_sensor'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))),
 | 
			
		||||
    vol.Required(CONF_MULTIPLEXER): validate_mux,
 | 
			
		||||
    vol.Required(CONF_GAIN): validate_gain,
 | 
			
		||||
    vol.Optional(CONF_ADS1115_ID): cv.variable_id,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema)
 | 
			
		||||
    cv.GenerateID(CONF_ADS1115_ID): cv.use_variable_id(ADS1115Component),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = get_variable(config.get(CONF_ADS1115_ID), u'sensor::ADS1115Component')
 | 
			
		||||
    hub = None
 | 
			
		||||
    for hub in get_variable(config[CONF_ADS1115_ID]):
 | 
			
		||||
        yield
 | 
			
		||||
 | 
			
		||||
    mux = RawExpression(MUX[config[CONF_MULTIPLEXER]])
 | 
			
		||||
    gain = RawExpression(GAIN[config[CONF_GAIN]])
 | 
			
		||||
    mux = MUX[config[CONF_MULTIPLEXER]]
 | 
			
		||||
    gain = GAIN[config[CONF_GAIN]]
 | 
			
		||||
    rhs = hub.get_sensor(config[CONF_NAME], mux, gain, config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    sensor_ = Pvariable('sensor::ADS1115Sensor', config[CONF_ID], rhs)
 | 
			
		||||
    sensor.register_sensor(sensor_, config)
 | 
			
		||||
    sensor.register_sensor(rhs, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_ADS1115_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								esphomeyaml/components/sensor/bh1750.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								esphomeyaml/components/sensor/bh1750.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
BH1750_RESOLUTIONS = {
 | 
			
		||||
    4.0: sensor.sensor_ns.BH1750_RESOLUTION_4P0_LX,
 | 
			
		||||
    1.0: sensor.sensor_ns.BH1750_RESOLUTION_1P0_LX,
 | 
			
		||||
    0.5: sensor.sensor_ns.BH1750_RESOLUTION_0P5_LX,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MakeBH1750Sensor = Application.MakeBH1750Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBH1750Sensor),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x23): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, cv.one_of(*BH1750_RESOLUTIONS)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_bh1750_sensor(config[CONF_NAME], config[CONF_ADDRESS],
 | 
			
		||||
                                 config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make_bh1750 = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    bh1750 = make_bh1750.Pbh1750
 | 
			
		||||
    if CONF_RESOLUTION in config:
 | 
			
		||||
        add(bh1750.set_resolution(BH1750_RESOLUTIONS[config[CONF_RESOLUTION]]))
 | 
			
		||||
    sensor.setup_sensor(bh1750, make_bh1750.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_BH1750'
 | 
			
		||||
							
								
								
									
										74
									
								
								esphomeyaml/components/sensor/bme280.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								esphomeyaml/components/sensor/bme280.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_IIR_FILTER, CONF_MAKE_ID, \
 | 
			
		||||
    CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
OVERSAMPLING_OPTIONS = {
 | 
			
		||||
    'NONE': sensor.sensor_ns.BME280_OVERSAMPLING_NONE,
 | 
			
		||||
    '1X': sensor.sensor_ns.BME280_OVERSAMPLING_1X,
 | 
			
		||||
    '2X': sensor.sensor_ns.BME280_OVERSAMPLING_2X,
 | 
			
		||||
    '4X': sensor.sensor_ns.BME280_OVERSAMPLING_4X,
 | 
			
		||||
    '8X': sensor.sensor_ns.BME280_OVERSAMPLING_8X,
 | 
			
		||||
    '16X': sensor.sensor_ns.BME280_OVERSAMPLING_16X,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIR_FILTER_OPTIONS = {
 | 
			
		||||
    'OFF': sensor.sensor_ns.BME280_IIR_FILTER_OFF,
 | 
			
		||||
    '2X': sensor.sensor_ns.BME280_IIR_FILTER_2X,
 | 
			
		||||
    '4X': sensor.sensor_ns.BME280_IIR_FILTER_4X,
 | 
			
		||||
    '8X': sensor.sensor_ns.BME280_IIR_FILTER_8X,
 | 
			
		||||
    '16X': sensor.sensor_ns.BME280_IIR_FILTER_16X,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BME280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
 | 
			
		||||
    vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MakeBME280Sensor = Application.MakeBME280Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME280Sensor),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): BME280_OVERSAMPLING_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_PRESSURE): BME280_OVERSAMPLING_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): BME280_OVERSAMPLING_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_bme280_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_PRESSURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                                 config[CONF_ADDRESS],
 | 
			
		||||
                                 config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    bme280 = make.Pbme280
 | 
			
		||||
    if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
 | 
			
		||||
        constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
 | 
			
		||||
        add(bme280.set_temperature_oversampling(constant))
 | 
			
		||||
    if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
 | 
			
		||||
        constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
 | 
			
		||||
        add(bme280.set_pressure_oversampling(constant))
 | 
			
		||||
    if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
 | 
			
		||||
        constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
 | 
			
		||||
        add(bme280.set_humidity_oversampling(constant))
 | 
			
		||||
    if CONF_IIR_FILTER in config:
 | 
			
		||||
        constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
 | 
			
		||||
        add(bme280.set_iir_filter(constant))
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(bme280.Pget_temperature_sensor(), make.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(bme280.Pget_pressure_sensor(), make.Pmqtt_pressure,
 | 
			
		||||
                        config[CONF_PRESSURE])
 | 
			
		||||
    sensor.setup_sensor(bme280.Pget_humidity_sensor(), make.Pmqtt_humidity,
 | 
			
		||||
                        config[CONF_HUMIDITY])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_BME280'
 | 
			
		||||
							
								
								
									
										83
									
								
								esphomeyaml/components/sensor/bme680.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								esphomeyaml/components/sensor/bme680.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_GAS_RESISTANCE, CONF_HUMIDITY, CONF_IIR_FILTER, \
 | 
			
		||||
    CONF_MAKE_ID, CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
OVERSAMPLING_OPTIONS = {
 | 
			
		||||
    'NONE': sensor.sensor_ns.BME680_OVERSAMPLING_NONE,
 | 
			
		||||
    '1X': sensor.sensor_ns.BME680_OVERSAMPLING_1X,
 | 
			
		||||
    '2X': sensor.sensor_ns.BME680_OVERSAMPLING_2X,
 | 
			
		||||
    '4X': sensor.sensor_ns.BME680_OVERSAMPLING_4X,
 | 
			
		||||
    '8X': sensor.sensor_ns.BME680_OVERSAMPLING_8X,
 | 
			
		||||
    '16X': sensor.sensor_ns.BME680_OVERSAMPLING_16X,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIR_FILTER_OPTIONS = {
 | 
			
		||||
    'OFF': sensor.sensor_ns.BME680_IIR_FILTER_OFF,
 | 
			
		||||
    '1X': sensor.sensor_ns.BME680_IIR_FILTER_1X,
 | 
			
		||||
    '3X': sensor.sensor_ns.BME680_IIR_FILTER_3X,
 | 
			
		||||
    '7X': sensor.sensor_ns.BME680_IIR_FILTER_7X,
 | 
			
		||||
    '15X': sensor.sensor_ns.BME680_IIR_FILTER_15X,
 | 
			
		||||
    '31X': sensor.sensor_ns.BME680_IIR_FILTER_31X,
 | 
			
		||||
    '63X': sensor.sensor_ns.BME680_IIR_FILTER_63X,
 | 
			
		||||
    '127X': sensor.sensor_ns.BME680_IIR_FILTER_127X,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BME680_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
 | 
			
		||||
    vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MakeBME680Sensor = Application.MakeBME680Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME680Sensor),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x76): cv.i2c_address,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_PRESSURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): BME680_OVERSAMPLING_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_GAS_RESISTANCE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
 | 
			
		||||
    # TODO: Heater
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_bme680_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_PRESSURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                                 config[CONF_GAS_RESISTANCE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_ADDRESS],
 | 
			
		||||
                                 config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    bme680 = make.Pbme680
 | 
			
		||||
    if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
 | 
			
		||||
        constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
 | 
			
		||||
        add(bme680.set_temperature_oversampling(constant))
 | 
			
		||||
    if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
 | 
			
		||||
        constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
 | 
			
		||||
        add(bme680.set_pressure_oversampling(constant))
 | 
			
		||||
    if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
 | 
			
		||||
        constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
 | 
			
		||||
        add(bme680.set_humidity_oversampling(constant))
 | 
			
		||||
    if CONF_IIR_FILTER in config:
 | 
			
		||||
        constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
 | 
			
		||||
        add(bme680.set_iir_filter(constant))
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(bme680.Pget_temperature_sensor(), make.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(bme680.Pget_pressure_sensor(), make.Pmqtt_pressure,
 | 
			
		||||
                        config[CONF_PRESSURE])
 | 
			
		||||
    sensor.setup_sensor(bme680.Pget_humidity_sensor(), make.Pmqtt_humidity,
 | 
			
		||||
                        config[CONF_HUMIDITY])
 | 
			
		||||
    sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), make.Pmqtt_gas_resistance,
 | 
			
		||||
                        config[CONF_GAS_RESISTANCE])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_BME680'
 | 
			
		||||
@@ -2,19 +2,20 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, \
 | 
			
		||||
    CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, HexIntLiteral, add, variable
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
 | 
			
		||||
    CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, HexIntLiteral, add, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
MakeBMP085Sensor = Application.MakeBMP085Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('bmp085_sensor'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_PRESSURE): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP085Sensor),
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_PRESSURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_ADDRESS): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -22,14 +23,14 @@ def to_code(config):
 | 
			
		||||
    rhs = App.make_bmp085_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_PRESSURE][CONF_NAME],
 | 
			
		||||
                                 config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    bmp = variable('Application::MakeBMP085Sensor', config[CONF_ID], rhs)
 | 
			
		||||
    bmp = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    if CONF_ADDRESS in config:
 | 
			
		||||
        add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS])))
 | 
			
		||||
    sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), config[CONF_PRESSURE])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE])
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), bmp.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), bmp.Pmqtt_pressure,
 | 
			
		||||
                        config[CONF_PRESSURE])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_BMP085_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,23 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS
 | 
			
		||||
from esphomeyaml.components.dallas import DallasComponent
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \
 | 
			
		||||
    CONF_RESOLUTION, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL, CONF_ID
 | 
			
		||||
from esphomeyaml.helpers import HexIntLiteral, get_variable, Pvariable
 | 
			
		||||
    CONF_RESOLUTION, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import HexIntLiteral, get_variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('dallas_sensor'): cv.register_variable_id,
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int,
 | 
			
		||||
    vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int,
 | 
			
		||||
    vol.Optional(CONF_DALLAS_ID): cv.variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_DALLAS_ID): cv.use_variable_id(DallasComponent),
 | 
			
		||||
    vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=8, max=12)),
 | 
			
		||||
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema)
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = get_variable(config.get(CONF_DALLAS_ID), DALLAS_COMPONENT_CLASS)
 | 
			
		||||
    hub = None
 | 
			
		||||
    for hub in get_variable(config[CONF_DALLAS_ID]):
 | 
			
		||||
        yield
 | 
			
		||||
    update_interval = config.get(CONF_UPDATE_INTERVAL)
 | 
			
		||||
    if CONF_RESOLUTION in config and update_interval is None:
 | 
			
		||||
        update_interval = 10000
 | 
			
		||||
@@ -30,9 +30,7 @@ def to_code(config):
 | 
			
		||||
    else:
 | 
			
		||||
        rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX],
 | 
			
		||||
                                       update_interval, config.get(CONF_RESOLUTION))
 | 
			
		||||
    sensor_ = Pvariable('sensor::DallasTemperatureSensor', config[CONF_ID], rhs)
 | 
			
		||||
    sensor.register_sensor(sensor_, config)
 | 
			
		||||
    sensor.register_sensor(rhs, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_DALLAS_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,38 +1,48 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
 | 
			
		||||
    CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, RawExpression, add, variable
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, gpio_output_pin_expression, variable
 | 
			
		||||
from esphomeyaml.pins import GPIO_OUTPUT_PIN_SCHEMA
 | 
			
		||||
 | 
			
		||||
DHT_MODELS = ['AUTO_DETECT', 'DHT11', 'DHT22', 'AM2302', 'RHT03']
 | 
			
		||||
DHT_MODELS = {
 | 
			
		||||
    'AUTO_DETECT': sensor.sensor_ns.DHT_MODEL_AUTO_DETECT,
 | 
			
		||||
    'DHT11': sensor.sensor_ns.DHT_MODEL_DHT11,
 | 
			
		||||
    'DHT22': sensor.sensor_ns.DHT_MODEL_DHT22,
 | 
			
		||||
    'AM2302': sensor.sensor_ns.DHT_MODEL_AM2302,
 | 
			
		||||
    'RHT03': sensor.sensor_ns.DHT_MODEL_RHT03,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MakeDHTSensor = Application.MakeDHTSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('dht_sensor'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_PIN): pins.input_output_pin,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_MODEL): vol.All(vol.Upper, vol.Any(*DHT_MODELS)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHTSensor),
 | 
			
		||||
    vol.Required(CONF_PIN): GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_MODEL): vol.All(vol.Upper, cv.one_of(*DHT_MODELS)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    pin = None
 | 
			
		||||
    for pin in gpio_output_pin_expression(config[CONF_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                              config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                              config[CONF_PIN], config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    dht = variable('Application::MakeDHTSensor', config[CONF_ID], rhs)
 | 
			
		||||
                              pin, config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    dht = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    if CONF_MODEL in config:
 | 
			
		||||
        model = RawExpression('DHT::{}'.format(config[CONF_MODEL]))
 | 
			
		||||
        add(dht.Pdht.set_dht_model(model))
 | 
			
		||||
    sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
 | 
			
		||||
        constant = DHT_MODELS[config[CONF_MODEL]]
 | 
			
		||||
        add(dht.Pdht.set_dht_model(constant))
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(),
 | 
			
		||||
                        dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(),
 | 
			
		||||
                        dht.Pmqtt_humidity, config[CONF_HUMIDITY])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_DHT_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_DHT_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								esphomeyaml/components/sensor/dht12.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphomeyaml/components/sensor/dht12.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
MakeDHT12Sensor = Application.MakeDHT12Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHT12Sensor),
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_dht12_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                                config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    dht = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), dht.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), dht.Pmqtt_humidity,
 | 
			
		||||
                        config[CONF_HUMIDITY])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_DHT12_SENSOR'
 | 
			
		||||
							
								
								
									
										24
									
								
								esphomeyaml/components/sensor/esp32_hall.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphomeyaml/components/sensor/esp32_hall.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
MakeESP32HallSensor = Application.MakeESP32HallSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeESP32HallSensor),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_esp32_hall_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    sensor.setup_sensor(make.Phall, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ESP32_HALL_SENSOR'
 | 
			
		||||
@@ -2,18 +2,19 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, variable
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
MakeHDC1080Sensor = Application.MakeHDC1080Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('dht_sensor'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHDC1080Sensor),
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -21,12 +22,13 @@ def to_code(config):
 | 
			
		||||
    rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                  config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                                  config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    hdc1080 = variable('Application::MakeHDC1080Sensor', config[CONF_ID], rhs)
 | 
			
		||||
    sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_temperature, config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), config[CONF_HUMIDITY])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY])
 | 
			
		||||
    hdc1080 = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(),
 | 
			
		||||
                        hdc1080.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), hdc1080.Pmqtt_humidity,
 | 
			
		||||
                        config[CONF_HUMIDITY])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_HDC1080_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -2,18 +2,19 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
 | 
			
		||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, variable
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
MakeHTU21DSensor = Application.MakeHTU21DSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('htu21d'): cv.register_variable_id,
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHTU21DSensor),
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -21,12 +22,11 @@ def to_code(config):
 | 
			
		||||
    rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                                 config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    htu21d = variable('Application::MakeHTU21DSensor', config[CONF_ID], rhs)
 | 
			
		||||
    sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_temperature, config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), config[CONF_HUMIDITY])
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY])
 | 
			
		||||
    htu21d = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), htu21d.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), htu21d.Pmqtt_humidity,
 | 
			
		||||
                        config[CONF_HUMIDITY])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_HTU21D_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								esphomeyaml/components/sensor/max6675.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								esphomeyaml/components/sensor/max6675.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN_CLOCK, CONF_PIN_CS, CONF_PIN_MISO, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, \
 | 
			
		||||
    gpio_output_pin_expression, variable
 | 
			
		||||
 | 
			
		||||
MakeMAX6675Sensor = Application.MakeMAX6675Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor),
 | 
			
		||||
    vol.Required(CONF_PIN_CS): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_PIN_CLOCK): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_PIN_MISO): pins.GPIO_INPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    pin_cs = None
 | 
			
		||||
    for pin_cs in gpio_output_pin_expression(config[CONF_PIN_CS]):
 | 
			
		||||
        yield
 | 
			
		||||
    pin_clock = None
 | 
			
		||||
    for pin_clock in gpio_output_pin_expression(config[CONF_PIN_CLOCK]):
 | 
			
		||||
        yield
 | 
			
		||||
    pin_miso = None
 | 
			
		||||
    for pin_miso in gpio_input_pin_expression(config[CONF_PIN_MISO]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_max6675_sensor(config[CONF_NAME], pin_cs, pin_clock, pin_miso,
 | 
			
		||||
                                  config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    sensor.setup_sensor(make.Pmax6675, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_MAX6675_SENSOR'
 | 
			
		||||
							
								
								
									
										71
									
								
								esphomeyaml/components/sensor/mpu6050.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								esphomeyaml/components/sensor/mpu6050.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
 | 
			
		||||
    CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
CONF_ACCEL_X = 'accel_x'
 | 
			
		||||
CONF_ACCEL_Y = 'accel_y'
 | 
			
		||||
CONF_ACCEL_Z = 'accel_z'
 | 
			
		||||
CONF_GYRO_X = 'gyro_x'
 | 
			
		||||
CONF_GYRO_Y = 'gyro_y'
 | 
			
		||||
CONF_GYRO_Z = 'gyro_z'
 | 
			
		||||
 | 
			
		||||
MPU6050Component = sensor.sensor_ns.MPU6050Component
 | 
			
		||||
MPU6050AccelSensor = sensor.sensor_ns.MPU6050AccelSensor
 | 
			
		||||
MPU6050GyroSensor = sensor.sensor_ns.MPU6050GyroSensor
 | 
			
		||||
MPU6050TemperatureSensor = sensor.sensor_ns.MPU6050TemperatureSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(MPU6050Component),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_ACCEL_X): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_ACCEL_Y): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_ACCEL_Z): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_GYRO_X): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_GYRO_Y): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_GYRO_Z): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}), cv.has_at_least_one_key(CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
 | 
			
		||||
                            CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_mpu6050_sensor(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    mpu = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_ACCEL_X in config:
 | 
			
		||||
        conf = config[CONF_ACCEL_X]
 | 
			
		||||
        rhs = mpu.Pmake_accel_x_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
    if CONF_ACCEL_Y in config:
 | 
			
		||||
        conf = config[CONF_ACCEL_Y]
 | 
			
		||||
        rhs = mpu.Pmake_accel_y_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
    if CONF_ACCEL_Z in config:
 | 
			
		||||
        conf = config[CONF_ACCEL_Z]
 | 
			
		||||
        rhs = mpu.Pmake_accel_z_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
    if CONF_GYRO_X in config:
 | 
			
		||||
        conf = config[CONF_GYRO_X]
 | 
			
		||||
        rhs = mpu.Pmake_gyro_x_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
    if CONF_GYRO_Y in config:
 | 
			
		||||
        conf = config[CONF_GYRO_Y]
 | 
			
		||||
        rhs = mpu.Pmake_gyro_y_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
    if CONF_GYRO_Z in config:
 | 
			
		||||
        conf = config[CONF_GYRO_Z]
 | 
			
		||||
        rhs = mpu.Pmake_gyro_z_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME])
 | 
			
		||||
        sensor.register_sensor(rhs, conf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_MPU6050'
 | 
			
		||||
@@ -3,32 +3,34 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_ID, CONF_INTERNAL_FILTER, \
 | 
			
		||||
    CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
 | 
			
		||||
from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_INTERNAL_FILTER, \
 | 
			
		||||
    CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
 | 
			
		||||
    ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.helpers import App, RawExpression, add, variable
 | 
			
		||||
from esphomeyaml.helpers import App, add, global_ns, variable, Application
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
 | 
			
		||||
 | 
			
		||||
GPIO_PULL_MODES = {
 | 
			
		||||
    'PULLUP': 'GPIO_PULLUP_ONLY',
 | 
			
		||||
    'PULLDOWN': 'GPIO_PULLDOWN_ONLY',
 | 
			
		||||
    'PULLUP_PULLDOWN': 'GPIO_PULLUP_PULLDOWN',
 | 
			
		||||
    'FLOATING': 'GPIO_FLOATING',
 | 
			
		||||
    'PULLUP': global_ns.GPIO_PULLUP_ONLY,
 | 
			
		||||
    'PULLDOWN': global_ns.GPIO_PULLDOWN_ONLY,
 | 
			
		||||
    'PULLUP_PULLDOWN': global_ns.GPIO_PULLUP_PULLDOWN,
 | 
			
		||||
    'FLOATING': global_ns.GPIO_FLOATING,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GPIO_PULL_MODE_SCHEMA = vol.All(vol.Upper, vol.Any(*list(GPIO_PULL_MODES.keys())))
 | 
			
		||||
GPIO_PULL_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*GPIO_PULL_MODES))
 | 
			
		||||
 | 
			
		||||
COUNT_MODES = {
 | 
			
		||||
    'DISABLE': 'PCNT_COUNT_DIS',
 | 
			
		||||
    'INCREMENT': 'PCNT_COUNT_INC',
 | 
			
		||||
    'DECREMENT': 'PCNT_COUNT_DEC',
 | 
			
		||||
    'DISABLE': global_ns.PCNT_COUNT_DIS,
 | 
			
		||||
    'INCREMENT': global_ns.PCNT_COUNT_INC,
 | 
			
		||||
    'DECREMENT': global_ns.PCNT_COUNT_DEC,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
COUNT_MODE_SCHEMA = vol.All(vol.Upper, vol.Any(*list(COUNT_MODES.keys())))
 | 
			
		||||
COUNT_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*COUNT_MODES))
 | 
			
		||||
 | 
			
		||||
MakePulseCounterSensor = Application.MakePulseCounterSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('pulse_counter'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakePulseCounterSensor),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.input_pin,
 | 
			
		||||
    vol.Optional(CONF_PULL_MODE): GPIO_PULL_MODE_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_COUNT_MODE): vol.Schema({
 | 
			
		||||
@@ -36,28 +38,26 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
        vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA,
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_INTERNAL_FILTER): vol.All(vol.Coerce(int), vol.Range(min=0, max=1023)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_pulse_counter_sensor(config[CONF_NAME], config[CONF_PIN],
 | 
			
		||||
                                        config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable('Application::MakePulseCounterSensor', config[CONF_ID], rhs)
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    pcnt = make.Ppcnt
 | 
			
		||||
    if CONF_PULL_MODE in config:
 | 
			
		||||
        pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]]
 | 
			
		||||
        add(pcnt.set_pull_mode(RawExpression(pull_mode)))
 | 
			
		||||
        add(pcnt.set_pull_mode(pull_mode))
 | 
			
		||||
    if CONF_COUNT_MODE in config:
 | 
			
		||||
        count_mode = config[CONF_COUNT_MODE]
 | 
			
		||||
        rising_edge = COUNT_MODES[count_mode[CONF_RISING_EDGE]]
 | 
			
		||||
        falling_edge = COUNT_MODES[count_mode[CONF_FALLING_EDGE]]
 | 
			
		||||
        add(pcnt.set_edge_mode(RawExpression(rising_edge), RawExpression(falling_edge)))
 | 
			
		||||
        add(pcnt.set_edge_mode(rising_edge, falling_edge))
 | 
			
		||||
    if CONF_INTERNAL_FILTER in config:
 | 
			
		||||
        add(pcnt.set_filter(config[CONF_INTERNAL_FILTER]))
 | 
			
		||||
    sensor.setup_sensor(pcnt, config)
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
 | 
			
		||||
    sensor.setup_sensor(make.Ppcnt, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_PULSE_COUNTER_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								esphomeyaml/components/sensor/rotary_encoder.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								esphomeyaml/components/sensor/rotary_encoder.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable
 | 
			
		||||
 | 
			
		||||
RESOLUTIONS = {
 | 
			
		||||
    '1': sensor.sensor_ns.ROTARY_ENCODER_1_PULSE_PER_CYCLE,
 | 
			
		||||
    '2': sensor.sensor_ns.ROTARY_ENCODER_2_PULSES_PER_CYCLE,
 | 
			
		||||
    '4': sensor.sensor_ns.ROTARY_ENCODER_4_PULSES_PER_CYCLE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONF_PIN_A = 'pin_a'
 | 
			
		||||
CONF_PIN_B = 'pin_b'
 | 
			
		||||
CONF_PIN_RESET = 'pin_reset'
 | 
			
		||||
 | 
			
		||||
MakeRotaryEncoderSensor = Application.MakeRotaryEncoderSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRotaryEncoderSensor),
 | 
			
		||||
    vol.Required(CONF_PIN_A): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_PIN_B): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_PIN_RESET): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_RESOLUTION): vol.All(cv.string, cv.one_of(*RESOLUTIONS)),
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    pin_a = None
 | 
			
		||||
    for pin_a in gpio_input_pin_expression(config[CONF_PIN_A]):
 | 
			
		||||
        yield
 | 
			
		||||
    pin_b = None
 | 
			
		||||
    for pin_b in gpio_input_pin_expression(config[CONF_PIN_B]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_rotary_encoder_sensor(config[CONF_NAME], pin_a, pin_b)
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    encoder = make.Protary_encoder
 | 
			
		||||
    if CONF_PIN_RESET in config:
 | 
			
		||||
        pin_i = None
 | 
			
		||||
        for pin_i in gpio_input_pin_expression(config[CONF_PIN_RESET]):
 | 
			
		||||
            yield
 | 
			
		||||
        add(encoder.set_reset_pin(pin_i))
 | 
			
		||||
    if CONF_RESOLUTION in config:
 | 
			
		||||
        resolution = RESOLUTIONS[config[CONF_RESOLUTION]]
 | 
			
		||||
        add(encoder.set_resolution(resolution))
 | 
			
		||||
    sensor.setup_sensor(encoder, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR'
 | 
			
		||||
							
								
								
									
										44
									
								
								esphomeyaml/components/sensor/sht3xd.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphomeyaml/components/sensor/sht3xd.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ACCURACY, CONF_ADDRESS, CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, \
 | 
			
		||||
    CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
SHT_ACCURACIES = {
 | 
			
		||||
    'LOW': sensor.sensor_ns.SHT3XD_ACCURACY_LOW,
 | 
			
		||||
    'MEDIUM': sensor.sensor_ns.SHT3XD_ACCURACY_MEDIUM,
 | 
			
		||||
    'HIGH': sensor.sensor_ns.SHT3XD_ACCURACY_HIGH,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MakeSHT3XDSensor = Application.MakeSHT3XDSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSHT3XDSensor),
 | 
			
		||||
    vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x44): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_ACCURACY): vol.All(vol.Upper, cv.one_of(*SHT_ACCURACIES)),
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_sht3xd_sensor(config[CONF_TEMPERATURE][CONF_NAME],
 | 
			
		||||
                                 config[CONF_HUMIDITY][CONF_NAME],
 | 
			
		||||
                                 config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    sht3xd = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
 | 
			
		||||
    if CONF_ACCURACY in config:
 | 
			
		||||
        add(sht3xd.Psht3xd.set_accuracy(SHT_ACCURACIES[config[CONF_ACCURACY]]))
 | 
			
		||||
 | 
			
		||||
    sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), sht3xd.Pmqtt_temperature,
 | 
			
		||||
                        config[CONF_TEMPERATURE])
 | 
			
		||||
    sensor.setup_sensor(sht3xd.Psht3xd.Pget_humidity_sensor(), sht3xd.Pmqtt_humidity,
 | 
			
		||||
                        config[CONF_HUMIDITY])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_SHT3XD'
 | 
			
		||||
							
								
								
									
										27
									
								
								esphomeyaml/components/sensor/template.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphomeyaml/components/sensor/template.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, process_lambda, variable, Application
 | 
			
		||||
 | 
			
		||||
MakeTemplateSensor = Application.MakeTemplateSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSensor),
 | 
			
		||||
    vol.Required(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    template_ = None
 | 
			
		||||
    for template_ in process_lambda(config[CONF_LAMBDA], []):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_template_sensor(config[CONF_NAME], template_,
 | 
			
		||||
                                   config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR'
 | 
			
		||||
							
								
								
									
										57
									
								
								esphomeyaml/components/sensor/tsl2561.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphomeyaml/components/sensor/tsl2561.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_INTEGRATION_TIME, CONF_MAKE_ID, \
 | 
			
		||||
    CONF_NAME, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, variable
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
INTEGRATION_TIMES = {
 | 
			
		||||
    14: sensor.sensor_ns.TSL2561_INTEGRATION_14MS,
 | 
			
		||||
    101: sensor.sensor_ns.TSL2561_INTEGRATION_101MS,
 | 
			
		||||
    402: sensor.sensor_ns.TSL2561_INTEGRATION_402MS,
 | 
			
		||||
}
 | 
			
		||||
GAINS = {
 | 
			
		||||
    '1X': sensor.sensor_ns.TSL2561_GAIN_1X,
 | 
			
		||||
    '16X': sensor.sensor_ns.TSL2561_GAIN_16X,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONF_IS_CS_PACKAGE = 'is_cs_package'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_integration_time(value):
 | 
			
		||||
    value = cv.positive_time_period_milliseconds(value).total_milliseconds
 | 
			
		||||
    if value not in INTEGRATION_TIMES:
 | 
			
		||||
        raise vol.Invalid(u"Unsupported integration time {}.".format(value))
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MakeTSL2561Sensor = Application.MakeTSL2561Sensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTSL2561Sensor),
 | 
			
		||||
    vol.Optional(CONF_ADDRESS, default=0x39): cv.i2c_address,
 | 
			
		||||
    vol.Optional(CONF_INTEGRATION_TIME): validate_integration_time,
 | 
			
		||||
    vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*GAINS)),
 | 
			
		||||
    vol.Optional(CONF_IS_CS_PACKAGE): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_tsl2561_sensor(config[CONF_NAME], config[CONF_ADDRESS],
 | 
			
		||||
                                  config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make_tsl = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    tsl2561 = make_tsl.Ptsl2561
 | 
			
		||||
    if CONF_INTEGRATION_TIME in config:
 | 
			
		||||
        add(tsl2561.set_integration_time(INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]))
 | 
			
		||||
    if CONF_GAIN in config:
 | 
			
		||||
        add(tsl2561.set_gain(GAINS[config[CONF_GAIN]]))
 | 
			
		||||
    if CONF_IS_CS_PACKAGE in config:
 | 
			
		||||
        add(tsl2561.set_is_cs_package(config[CONF_IS_CS_PACKAGE]))
 | 
			
		||||
    sensor.setup_sensor(tsl2561, make_tsl.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_TSL2561'
 | 
			
		||||
@@ -3,35 +3,39 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import sensor
 | 
			
		||||
from esphomeyaml.const import CONF_ECHO_PIN, CONF_ID, CONF_NAME, \
 | 
			
		||||
    CONF_TIMEOUT_METER, CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, add, exp_gpio_input_pin, exp_gpio_output_pin, \
 | 
			
		||||
    variable
 | 
			
		||||
from esphomeyaml.const import CONF_ECHO_PIN, CONF_MAKE_ID, CONF_NAME, CONF_TIMEOUT_METER, \
 | 
			
		||||
    CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL
 | 
			
		||||
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, \
 | 
			
		||||
    gpio_output_pin_expression, variable
 | 
			
		||||
 | 
			
		||||
MakeUltrasonicSensor = Application.MakeUltrasonicSensor
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('ultrasonic'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUltrasonicSensor),
 | 
			
		||||
    vol.Required(CONF_TRIGGER_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_ECHO_PIN): pins.GPIO_INPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Required(CONF_ECHO_PIN): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
 | 
			
		||||
    vol.Exclusive(CONF_TIMEOUT_METER, 'timeout'): cv.positive_float,
 | 
			
		||||
    vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_int,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
 | 
			
		||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
 | 
			
		||||
    vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_time_period_microseconds,
 | 
			
		||||
    vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
}).extend(sensor.SENSOR_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    trigger = exp_gpio_output_pin(config[CONF_TRIGGER_PIN])
 | 
			
		||||
    echo = exp_gpio_input_pin(config[CONF_ECHO_PIN])
 | 
			
		||||
    trigger = None
 | 
			
		||||
    for trigger in gpio_output_pin_expression(config[CONF_TRIGGER_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    echo = None
 | 
			
		||||
    for trigger in gpio_input_pin_expression(config[CONF_ECHO_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo,
 | 
			
		||||
                                     config.get(CONF_UPDATE_INTERVAL))
 | 
			
		||||
    make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs)
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    ultrasonic = make.Pultrasonic
 | 
			
		||||
    if CONF_TIMEOUT_TIME in config:
 | 
			
		||||
        add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME]))
 | 
			
		||||
    elif CONF_TIMEOUT_METER in config:
 | 
			
		||||
        add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER]))
 | 
			
		||||
    sensor.setup_sensor(ultrasonic, config)
 | 
			
		||||
    sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
 | 
			
		||||
    sensor.setup_sensor(ultrasonic, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_ULTRASONIC_SENSOR'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +1,48 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_NAME, CONF_MQTT_ID
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, setup_mqtt_component
 | 
			
		||||
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MQTT_SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
 | 
			
		||||
switch_ns = esphomelib_ns.namespace('switch_')
 | 
			
		||||
Switch = switch_ns.Switch
 | 
			
		||||
MQTTSwitchComponent = switch_ns.MQTTSwitchComponent
 | 
			
		||||
ToggleAction = switch_ns.ToggleAction
 | 
			
		||||
TurnOffAction = switch_ns.TurnOffAction
 | 
			
		||||
TurnOnAction = switch_ns.TurnOnAction
 | 
			
		||||
 | 
			
		||||
SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(Switch),
 | 
			
		||||
    cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent),
 | 
			
		||||
    vol.Optional(CONF_ICON): cv.icon,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
MQTT_SWITCH_ID_SCHEMA = MQTT_SWITCH_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('mqtt_switch', CONF_MQTT_ID): cv.register_variable_id,
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_mqtt_switch(obj, config):
 | 
			
		||||
    setup_mqtt_component(obj, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_switch(obj, config):
 | 
			
		||||
def setup_switch_core_(switch_var, mqtt_var, config):
 | 
			
		||||
    if CONF_ICON in config:
 | 
			
		||||
        add(obj.set_icon(config[CONF_ICON]))
 | 
			
		||||
        add(switch_var.set_icon(config[CONF_ICON]))
 | 
			
		||||
    if CONF_INVERTED in config:
 | 
			
		||||
        add(switch_var.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
 | 
			
		||||
    setup_mqtt_component(mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_switch(switch_obj, mqtt_obj, config):
 | 
			
		||||
    switch_var = Pvariable(config[CONF_ID], switch_obj, has_side_effects=False)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
 | 
			
		||||
    setup_switch_core_(switch_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_switch(var, config):
 | 
			
		||||
    setup_switch(var, config)
 | 
			
		||||
    rhs = App.register_switch(var)
 | 
			
		||||
    mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_MQTT_ID], rhs)
 | 
			
		||||
    setup_mqtt_switch(mqtt_switch, config)
 | 
			
		||||
    switch_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
 | 
			
		||||
    rhs = App.register_switch(switch_var)
 | 
			
		||||
    mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
 | 
			
		||||
    setup_switch_core_(switch_var, mqtt_var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_SWITCH'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_SWITCH'
 | 
			
		||||
 
 | 
			
		||||
@@ -3,21 +3,24 @@ import voluptuous as vol
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import pins
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, exp_gpio_output_pin, variable
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
 | 
			
		||||
from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable
 | 
			
		||||
 | 
			
		||||
MakeGPIOSwitch = Application.MakeGPIOSwitch
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('gpio_switch'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOSwitch),
 | 
			
		||||
    vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
 | 
			
		||||
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
 | 
			
		||||
}).extend(switch.SWITCH_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_gpio_switch(config[CONF_NAME], exp_gpio_output_pin(config[CONF_PIN]))
 | 
			
		||||
    gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs)
 | 
			
		||||
    switch.setup_switch(gpio.Pswitch_, config)
 | 
			
		||||
    switch.setup_mqtt_switch(gpio.Pmqtt, config)
 | 
			
		||||
    pin = None
 | 
			
		||||
    for pin in gpio_output_pin_expression(config[CONF_PIN]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_gpio_switch(config[CONF_NAME], pin)
 | 
			
		||||
    gpio = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_GPIO_SWITCH'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,21 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.components.ir_transmitter import IR_TRANSMITTER_COMPONENT_CLASS
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_COMMAND, CONF_DATA, CONF_IR_TRANSMITTER_ID, \
 | 
			
		||||
    CONF_LG, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_REPEAT, CONF_SONY, CONF_TIMES, \
 | 
			
		||||
    CONF_WAIT_TIME_US, CONF_RAW, CONF_CARRIER_FREQUENCY, CONF_NAME, CONF_ID
 | 
			
		||||
from esphomeyaml.components.ir_transmitter import IRTransmitterComponent
 | 
			
		||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \
 | 
			
		||||
    CONF_INVERTED, CONF_IR_TRANSMITTER_ID, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \
 | 
			
		||||
    CONF_PANASONIC, CONF_RAW, CONF_REPEAT, CONF_SONY, CONF_TIMES, CONF_WAIT_TIME
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml.helpers import HexIntLiteral, MockObj, get_variable, ArrayInitializer, Pvariable
 | 
			
		||||
from esphomeyaml.helpers import App, ArrayInitializer, HexIntLiteral, get_variable
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('ir_transmitter_switch'): cv.register_variable_id,
 | 
			
		||||
DEPENDENCIES = ['ir_transmitter']
 | 
			
		||||
 | 
			
		||||
IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW]
 | 
			
		||||
 | 
			
		||||
WAIT_TIME_MESSAGE = "The wait_time_us option has been renamed to wait_time in order to decrease " \
 | 
			
		||||
                    "ambiguity. "
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    vol.Exclusive(CONF_NEC, 'code'): vol.Schema({
 | 
			
		||||
        vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
 | 
			
		||||
        vol.Required(CONF_COMMAND): cv.hex_uint16_t,
 | 
			
		||||
@@ -33,14 +39,18 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
 | 
			
		||||
        vol.Required(CONF_TIMES): cv.positive_not_null_int,
 | 
			
		||||
        vol.Required(CONF_WAIT_TIME_US): cv.uint32_t,
 | 
			
		||||
    })),
 | 
			
		||||
    vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id,
 | 
			
		||||
}).extend(switch.MQTT_SWITCH_ID_SCHEMA.schema)
 | 
			
		||||
        vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds,
 | 
			
		||||
 | 
			
		||||
        vol.Optional('wait_time_us'): cv.invalid(WAIT_TIME_MESSAGE),
 | 
			
		||||
    })),
 | 
			
		||||
    cv.GenerateID(CONF_IR_TRANSMITTER_ID): cv.use_variable_id(IRTransmitterComponent),
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.invalid("IR Transmitters do not support inverted mode!"),
 | 
			
		||||
}).extend(switch.SWITCH_SCHEMA.schema), cv.has_at_least_one_key(*IR_KEYS))
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
SendData = MockObj('switch_::ir::SendData', '::')
 | 
			
		||||
ir_ns = switch.switch_ns.namespace('ir')
 | 
			
		||||
SendData = ir_ns.namespace('SendData')
 | 
			
		||||
DataTransmitter = IRTransmitterComponent.DataTransmitter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def safe_hex(value):
 | 
			
		||||
@@ -77,20 +87,18 @@ def exp_send_data(config):
 | 
			
		||||
            wait_us = None
 | 
			
		||||
        else:
 | 
			
		||||
            times = config[CONF_REPEAT][CONF_TIMES]
 | 
			
		||||
            wait_us = config[CONF_REPEAT][CONF_WAIT_TIME_US]
 | 
			
		||||
        base = MockObj(unicode(base), u'.')
 | 
			
		||||
            wait_us = config[CONF_REPEAT][CONF_WAIT_TIME]
 | 
			
		||||
        base = base.repeat(times, wait_us)
 | 
			
		||||
    return base
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS)
 | 
			
		||||
    ir = None
 | 
			
		||||
    for ir in get_variable(config[CONF_IR_TRANSMITTER_ID]):
 | 
			
		||||
        yield
 | 
			
		||||
    send_data = exp_send_data(config)
 | 
			
		||||
    rhs = ir.create_transmitter(config[CONF_NAME], send_data)
 | 
			
		||||
    switch_ = Pvariable(IR_TRANSMITTER_COMPONENT_CLASS + '::DataTransmitter', config[CONF_ID],
 | 
			
		||||
                        rhs)
 | 
			
		||||
    switch.register_switch(switch_, config)
 | 
			
		||||
    rhs = App.register_component(ir.create_transmitter(config[CONF_NAME], send_data))
 | 
			
		||||
    switch.register_switch(rhs, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_IR_TRANSMITTER'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								esphomeyaml/components/switch/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphomeyaml/components/switch/output.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
 | 
			
		||||
from esphomeyaml.helpers import App, Application, get_variable, variable
 | 
			
		||||
 | 
			
		||||
MakeSimpleSwitch = Application.MakeSimpleSwitch
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSimpleSwitch),
 | 
			
		||||
    vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
 | 
			
		||||
}).extend(switch.SWITCH_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    output = None
 | 
			
		||||
    for output in get_variable(config[CONF_OUTPUT]):
 | 
			
		||||
        yield
 | 
			
		||||
    rhs = App.make_simple_switch(config[CONF_NAME], output)
 | 
			
		||||
    gpio = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_SIMPLE_SWITCH'
 | 
			
		||||
@@ -1,19 +1,22 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.const import CONF_ID, CONF_NAME
 | 
			
		||||
from esphomeyaml.helpers import App, variable
 | 
			
		||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
MakeRestartSwitch = Application.MakeRestartSwitch
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID('restart_switch'): cv.register_variable_id,
 | 
			
		||||
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRestartSwitch),
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.invalid("Restart switches do not support inverted mode!"),
 | 
			
		||||
}).extend(switch.SWITCH_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_restart_switch(config[CONF_NAME])
 | 
			
		||||
    restart = variable('Application::MakeRestartSwitch', config[CONF_ID], rhs)
 | 
			
		||||
    switch.setup_switch(restart.Prestart, config)
 | 
			
		||||
    switch.setup_mqtt_switch(restart.Pmqtt, config)
 | 
			
		||||
    restart = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    switch.setup_switch(restart.Prestart, restart.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_RESTART_SWITCH'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								esphomeyaml/components/switch/shutdown.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphomeyaml/components/switch/shutdown.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
 | 
			
		||||
from esphomeyaml.helpers import App, Application, variable
 | 
			
		||||
 | 
			
		||||
MakeShutdownSwitch = Application.MakeShutdownSwitch
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeShutdownSwitch),
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"),
 | 
			
		||||
}).extend(switch.SWITCH_SCHEMA.schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_shutdown_switch(config[CONF_NAME])
 | 
			
		||||
    shutdown = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
    switch.setup_switch(shutdown.Pshutdown, shutdown.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH'
 | 
			
		||||
							
								
								
									
										46
									
								
								esphomeyaml/components/switch/template.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								esphomeyaml/components/switch/template.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import automation
 | 
			
		||||
from esphomeyaml.components import switch
 | 
			
		||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_TURN_OFF_ACTION, \
 | 
			
		||||
    CONF_TURN_ON_ACTION, CONF_OPTIMISTIC
 | 
			
		||||
from esphomeyaml.helpers import App, Application, process_lambda, variable, NoArg, add
 | 
			
		||||
 | 
			
		||||
MakeTemplateSwitch = Application.MakeTemplateSwitch
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSwitch),
 | 
			
		||||
    vol.Optional(CONF_LAMBDA): cv.lambda_,
 | 
			
		||||
    vol.Optional(CONF_OPTIMISTIC): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_TURN_OFF_ACTION): automation.ACTIONS_SCHEMA,
 | 
			
		||||
    vol.Optional(CONF_TURN_ON_ACTION): automation.ACTIONS_SCHEMA,
 | 
			
		||||
}).extend(switch.SWITCH_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.make_template_switch(config[CONF_NAME])
 | 
			
		||||
    make = variable(config[CONF_MAKE_ID], rhs)
 | 
			
		||||
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
        template_ = None
 | 
			
		||||
        for template_ in process_lambda(config[CONF_LAMBDA], []):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.set_state_lambda(template_))
 | 
			
		||||
    if CONF_TURN_OFF_ACTION in config:
 | 
			
		||||
        actions = None
 | 
			
		||||
        for actions in automation.build_actions(config[CONF_TURN_OFF_ACTION], NoArg):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.add_turn_off_actions(actions))
 | 
			
		||||
    if CONF_TURN_ON_ACTION in config:
 | 
			
		||||
        actions = None
 | 
			
		||||
        for actions in automation.build_actions(config[CONF_TURN_ON_ACTION], NoArg):
 | 
			
		||||
            yield
 | 
			
		||||
        add(make.Ptemplate_.add_turn_on_actions(actions))
 | 
			
		||||
    if CONF_OPTIMISTIC in config:
 | 
			
		||||
        add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
 | 
			
		||||
 | 
			
		||||
    switch.setup_switch(make.Ptemplate_, make.Pmqtt, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH'
 | 
			
		||||
@@ -3,13 +3,16 @@ import logging
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_PORT, CONF_JS_URL, CONF_CSS_URL, CONF_ID
 | 
			
		||||
from esphomeyaml.helpers import App, add, Pvariable
 | 
			
		||||
from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.const import CONF_PORT, CONF_JS_URL, CONF_CSS_URL, CONF_ID, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
WebServer = esphomelib_ns.WebServer
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID('web_server'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(WebServer),
 | 
			
		||||
    vol.Optional(CONF_PORT): cv.port,
 | 
			
		||||
    vol.Optional(CONF_CSS_URL): vol.Url,
 | 
			
		||||
    vol.Optional(CONF_JS_URL): vol.Url,
 | 
			
		||||
@@ -18,12 +21,17 @@ CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    rhs = App.init_web_server(config.get(CONF_PORT))
 | 
			
		||||
    web_server = Pvariable('WebServer', config[CONF_ID], rhs)
 | 
			
		||||
    web_server = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
    if CONF_CSS_URL in config:
 | 
			
		||||
        add(web_server.set_css_url(config[CONF_CSS_URL]))
 | 
			
		||||
    if CONF_JS_URL in config:
 | 
			
		||||
        add(web_server.set_js_url(config[CONF_JS_URL]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_flags(config):
 | 
			
		||||
    return '-DUSE_WEB_SERVER'
 | 
			
		||||
BUILD_FLAGS = '-DUSE_WEB_SERVER'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lib_deps(config):
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        return 'FS'
 | 
			
		||||
    return ''
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_GATEWAY, \
 | 
			
		||||
    CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET
 | 
			
		||||
from esphomeyaml.helpers import App, MockObj, Pvariable, StructInitializer, add
 | 
			
		||||
from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \
 | 
			
		||||
    CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, \
 | 
			
		||||
    CONF_STATIC_IP, CONF_SUBNET, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, esphomelib_ns, global_ns
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_password(value):
 | 
			
		||||
@@ -28,8 +30,13 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({
 | 
			
		||||
    vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
IPAddress = global_ns.IPAddress
 | 
			
		||||
ManualIP = esphomelib_ns.ManualIP
 | 
			
		||||
WiFiComponent = esphomelib_ns.WiFiComponent
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
    cv.GenerateID('wifi'): cv.register_variable_id,
 | 
			
		||||
    cv.GenerateID(): cv.declare_variable_id(WiFiComponent),
 | 
			
		||||
    vol.Optional(CONF_SSID): cv.ssid,
 | 
			
		||||
    vol.Optional(CONF_PASSWORD): validate_password,
 | 
			
		||||
    vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
 | 
			
		||||
@@ -40,11 +47,9 @@ CONFIG_SCHEMA = vol.Schema({
 | 
			
		||||
        vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
 | 
			
		||||
    }),
 | 
			
		||||
    vol.Optional(CONF_HOSTNAME): cv.hostname,
 | 
			
		||||
    vol.Required(CONF_DOMAIN, default='.local'): cv.domainname,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
IPAddress = MockObj('IPAddress')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def safe_ip(ip):
 | 
			
		||||
    if ip is None:
 | 
			
		||||
@@ -54,7 +59,7 @@ def safe_ip(ip):
 | 
			
		||||
 | 
			
		||||
def manual_ip(config):
 | 
			
		||||
    return StructInitializer(
 | 
			
		||||
        'ManualIP',
 | 
			
		||||
        ManualIP,
 | 
			
		||||
        ('static_ip', safe_ip(config[CONF_STATIC_IP])),
 | 
			
		||||
        ('gateway', safe_ip(config[CONF_GATEWAY])),
 | 
			
		||||
        ('subnet', safe_ip(config[CONF_SUBNET])),
 | 
			
		||||
@@ -70,7 +75,7 @@ def to_code(config):
 | 
			
		||||
        rhs = App.init_wifi(config[CONF_SSID], config.get(CONF_PASSWORD))
 | 
			
		||||
    else:
 | 
			
		||||
        rhs = App.init_wifi()
 | 
			
		||||
    wifi = Pvariable('WiFiComponent', config[CONF_ID], rhs)
 | 
			
		||||
    wifi = Pvariable(config[CONF_ID], rhs)
 | 
			
		||||
 | 
			
		||||
    if sta and CONF_MANUAL_IP in config:
 | 
			
		||||
        add(wifi.set_sta_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
 | 
			
		||||
@@ -87,3 +92,9 @@ def to_code(config):
 | 
			
		||||
 | 
			
		||||
    if CONF_HOSTNAME in config:
 | 
			
		||||
        add(wifi.set_hostname(config[CONF_HOSTNAME]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lib_deps(config):
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
 | 
			
		||||
        return 'ESP8266WiFi'
 | 
			
		||||
    return None
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								esphomeyaml/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphomeyaml/config.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "esphomeyaml",
 | 
			
		||||
  "version": "1.6.1",
 | 
			
		||||
  "slug": "esphomeyaml",
 | 
			
		||||
  "description": "esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices.",
 | 
			
		||||
  "url": "https://esphomelib.com/esphomeyaml/index.html",
 | 
			
		||||
  "startup": "application",
 | 
			
		||||
  "webui": "http://[HOST]:[PORT:6052]",
 | 
			
		||||
  "boot": "auto",
 | 
			
		||||
  "ports": {
 | 
			
		||||
    "6052/tcp": 6052,
 | 
			
		||||
    "6053/tcp": 6053
 | 
			
		||||
  },
 | 
			
		||||
  "auto_uart": true,
 | 
			
		||||
  "map": [
 | 
			
		||||
    "config:rw"
 | 
			
		||||
  ],
 | 
			
		||||
  "options": {},
 | 
			
		||||
  "environment": {
 | 
			
		||||
    "ESPHOMEYAML_OTA_HOST_PORT": "6053"
 | 
			
		||||
  },
 | 
			
		||||
  "schema": {},
 | 
			
		||||
  "image": "ottowinter/esphomeyaml-hassio-{arch}"
 | 
			
		||||
}
 | 
			
		||||
@@ -9,15 +9,18 @@ from voluptuous.humanize import humanize_error
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import core, yaml_util
 | 
			
		||||
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_NAME, \
 | 
			
		||||
    CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, ESP_PLATFORMS, \
 | 
			
		||||
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
 | 
			
		||||
    CONF_LIBRARY_URI, \
 | 
			
		||||
    CONF_NAME, CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, ESP_PLATFORMS, \
 | 
			
		||||
    ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml.helpers import App, add, color
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.3.0'
 | 
			
		||||
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.6.1'
 | 
			
		||||
 | 
			
		||||
BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout']
 | 
			
		||||
 | 
			
		||||
CORE_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_NAME): cv.valid_name,
 | 
			
		||||
@@ -25,7 +28,8 @@ CORE_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_BOARD): cv.string,
 | 
			
		||||
    vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string,
 | 
			
		||||
    vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_USE_BUILD_FLAGS, default=False): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_USE_BUILD_FLAGS, default=True): cv.boolean,
 | 
			
		||||
    vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, cv.one_of(*BUILD_FLASH_MODES)),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
REQUIRED_COMPONENTS = [
 | 
			
		||||
@@ -89,6 +93,50 @@ class Config(OrderedDict):
 | 
			
		||||
        self.errors.append((message, domain, config))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def iter_ids(config, prefix=None, parent=None):
 | 
			
		||||
    prefix = prefix or []
 | 
			
		||||
    parent = parent or {}
 | 
			
		||||
    if isinstance(config, core.ID):
 | 
			
		||||
        yield config, prefix, parent
 | 
			
		||||
    elif isinstance(config, core.Lambda):
 | 
			
		||||
        for id in config.requires_ids:
 | 
			
		||||
            yield id, prefix, parent
 | 
			
		||||
    elif isinstance(config, list):
 | 
			
		||||
        for i, item in enumerate(config):
 | 
			
		||||
            for result in iter_ids(item, prefix + [str(i)], config):
 | 
			
		||||
                yield result
 | 
			
		||||
    elif isinstance(config, dict):
 | 
			
		||||
        for key, value in config.iteritems():
 | 
			
		||||
            for result in iter_ids(value, prefix + [str(key)], config):
 | 
			
		||||
                yield result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def do_id_pass(result):
 | 
			
		||||
    declare_ids = []
 | 
			
		||||
    searching_ids = []
 | 
			
		||||
    for id, prefix, config in iter_ids(result):
 | 
			
		||||
        if id.is_declaration:
 | 
			
		||||
            if id.id is not None and any(v[0].id == id.id for v in declare_ids):
 | 
			
		||||
                result.add_error("ID {} redefined!".format(id.id), '.'.join(prefix), config)
 | 
			
		||||
                continue
 | 
			
		||||
            declare_ids.append((id, prefix, config))
 | 
			
		||||
        else:
 | 
			
		||||
            searching_ids.append((id, prefix, config))
 | 
			
		||||
    # Resolve default ids after manual IDs
 | 
			
		||||
    for id, _, _ in declare_ids:
 | 
			
		||||
        id.resolve([v[0].id for v in declare_ids])
 | 
			
		||||
 | 
			
		||||
    # Check searched IDs
 | 
			
		||||
    for id, prefix, config in searching_ids:
 | 
			
		||||
        if id.id is not None and not any(v[0].id == id.id for v in declare_ids):
 | 
			
		||||
            result.add_error("Couldn't find ID {}".format(id.id), '.'.join(prefix), config)
 | 
			
		||||
        if id.id is None and id.type is not None:
 | 
			
		||||
            id.id = next((v[0].id for v in declare_ids if v[0].type == id.type), None)
 | 
			
		||||
            if id.id is None:
 | 
			
		||||
                result.add_error("Couldn't resolve ID for type {}".format(id.type),
 | 
			
		||||
                                 '.'.join(prefix), config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_config(config):
 | 
			
		||||
    global _ALL_COMPONENTS
 | 
			
		||||
 | 
			
		||||
@@ -167,6 +215,12 @@ def validate_config(config):
 | 
			
		||||
            if not success:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            esp_platforms = getattr(platform, 'ESP_PLATFORMS', ESP_PLATFORMS)
 | 
			
		||||
            if core.ESP_PLATFORM not in esp_platforms:
 | 
			
		||||
                result.add_error(
 | 
			
		||||
                    u"Platform {}.{} doesn't support {}.".format(domain, p_name, core.ESP_PLATFORM))
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if hasattr(platform, u'PLATFORM_SCHEMA'):
 | 
			
		||||
                try:
 | 
			
		||||
                    p_validated = platform.PLATFORM_SCHEMA(p_config)
 | 
			
		||||
@@ -175,6 +229,8 @@ def validate_config(config):
 | 
			
		||||
                    continue
 | 
			
		||||
                platforms.append(p_validated)
 | 
			
		||||
        result[domain] = platforms
 | 
			
		||||
 | 
			
		||||
    do_id_pass(result)
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -190,6 +246,9 @@ def _format_config_error(ex, domain, config):
 | 
			
		||||
    else:
 | 
			
		||||
        message += u'{}.'.format(humanize_error(config, ex))
 | 
			
		||||
 | 
			
		||||
    if isinstance(config, list):
 | 
			
		||||
        return message
 | 
			
		||||
 | 
			
		||||
    domain_config = config.get(domain, config)
 | 
			
		||||
    message += u" (See {}, line {}). ".format(
 | 
			
		||||
        getattr(domain_config, '__config_file__', '?'),
 | 
			
		||||
@@ -203,24 +262,31 @@ def load_config(path):
 | 
			
		||||
        config = yaml_util.load_yaml(path)
 | 
			
		||||
    except OSError:
 | 
			
		||||
        raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path))
 | 
			
		||||
    core.RAW_CONFIG = config
 | 
			
		||||
 | 
			
		||||
    if CONF_ESPHOMEYAML not in config:
 | 
			
		||||
        raise ESPHomeYAMLError(u"No esphomeyaml section in config")
 | 
			
		||||
    core_conf = config[CONF_ESPHOMEYAML]
 | 
			
		||||
    esp_platform = unicode(core_conf.get(CONF_PLATFORM, u""))
 | 
			
		||||
    if CONF_PLATFORM not in core_conf:
 | 
			
		||||
        raise ESPHomeYAMLError("esphomeyaml.platform not specified.")
 | 
			
		||||
    esp_platform = unicode(core_conf[CONF_PLATFORM])
 | 
			
		||||
    esp_platform = esp_platform.upper()
 | 
			
		||||
    if '8266' in esp_platform:
 | 
			
		||||
        esp_platform = ESP_PLATFORM_ESP8266
 | 
			
		||||
    if '32' in esp_platform:
 | 
			
		||||
        esp_platform = ESP_PLATFORM_ESP32
 | 
			
		||||
    core.ESP_PLATFORM = esp_platform
 | 
			
		||||
    core.BOARD = unicode(core_conf.get(CONF_BOARD, u""))
 | 
			
		||||
    if CONF_BOARD not in core_conf:
 | 
			
		||||
        raise ESPHomeYAMLError("esphomeyaml.board not specified.")
 | 
			
		||||
    core.BOARD = unicode(core_conf[CONF_BOARD])
 | 
			
		||||
    core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True))
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        result = validate_config(config)
 | 
			
		||||
    except ESPHomeYAMLError:
 | 
			
		||||
        raise
 | 
			
		||||
    except Exception:
 | 
			
		||||
        print(u"Unexpected exception while reading configuration:")
 | 
			
		||||
        _LOGGER.error(u"Unexpected exception while reading configuration:")
 | 
			
		||||
        raise
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
@@ -261,18 +327,18 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_config(path):
 | 
			
		||||
    _LOGGER.debug("Reading configuration...")
 | 
			
		||||
    _LOGGER.info("Reading configuration...")
 | 
			
		||||
    try:
 | 
			
		||||
        res = load_config(path)
 | 
			
		||||
    except ESPHomeYAMLError as err:
 | 
			
		||||
        _LOGGER.error(u"Error while reading config: %s", err)
 | 
			
		||||
        return None
 | 
			
		||||
    excepts = {}
 | 
			
		||||
    for err in res.errors:
 | 
			
		||||
        domain = err[1] or u"General Error"
 | 
			
		||||
        excepts.setdefault(domain, []).append(err[0])
 | 
			
		||||
        if err[2] is not None:
 | 
			
		||||
            excepts[domain].append(err[2])
 | 
			
		||||
    for message, domain, config in res.errors:
 | 
			
		||||
        domain = domain or u"General Error"
 | 
			
		||||
        excepts.setdefault(domain, []).append(message)
 | 
			
		||||
        if config is not None:
 | 
			
		||||
            excepts[domain].append(config)
 | 
			
		||||
 | 
			
		||||
    if excepts:
 | 
			
		||||
        print(color('bold_white', u"Failed config"))
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
@@ -12,8 +12,8 @@ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOV
 | 
			
		||||
    CONF_NAME, CONF_PAYLOAD_AVAILABLE, \
 | 
			
		||||
    CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \
 | 
			
		||||
    ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.core import HexInt, IPAddress
 | 
			
		||||
from esphomeyaml.helpers import ensure_unique_string
 | 
			
		||||
from esphomeyaml.core import HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
 | 
			
		||||
    TimePeriodMilliseconds, TimePeriodSeconds
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +25,7 @@ zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
 | 
			
		||||
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
 | 
			
		||||
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
 | 
			
		||||
 | 
			
		||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
 | 
			
		||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
 | 
			
		||||
 | 
			
		||||
RESERVED_IDS = [
 | 
			
		||||
    # C++ keywords http://en.cppreference.com/w/cpp/keyword
 | 
			
		||||
@@ -56,8 +56,10 @@ def alphanumeric(value):
 | 
			
		||||
 | 
			
		||||
def valid_name(value):
 | 
			
		||||
    value = string_strict(value)
 | 
			
		||||
    if not all(c in ALLOWED_NAME_CHARS for c in value):
 | 
			
		||||
        raise vol.Invalid(u"Valid characters for name are {}".format(ALLOWED_NAME_CHARS))
 | 
			
		||||
    for c in value:
 | 
			
		||||
        if c not in ALLOWED_NAME_CHARS:
 | 
			
		||||
            raise vol.Invalid(u"'{}' is an invalid character for names. Valid characters are: {}"
 | 
			
		||||
                              u"".format(c, ALLOWED_NAME_CHARS))
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -133,16 +135,54 @@ def int_(value):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
hex_int = vol.Coerce(hex_int_)
 | 
			
		||||
match_cpp_var_ = vol.Match(r'^[a-zA-Z_][a-zA-Z0-9_]+$', msg=u"Must be a valid C++ variable name")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def variable_id(value):
 | 
			
		||||
    value = match_cpp_var_(value)
 | 
			
		||||
def variable_id_str_(value):
 | 
			
		||||
    value = string(value)
 | 
			
		||||
    if not value:
 | 
			
		||||
        raise vol.Invalid("ID must not be empty")
 | 
			
		||||
    if value[0].isdigit():
 | 
			
		||||
        raise vol.Invalid("First character in ID cannot be a digit.")
 | 
			
		||||
    if '-' in value:
 | 
			
		||||
        raise vol.Invalid("Dashes are not supported in IDs, please use underscores instead.")
 | 
			
		||||
    for char in value:
 | 
			
		||||
        if char != '_' and not char.isalnum():
 | 
			
		||||
            raise vol.Invalid(u"IDs must only consist of upper/lowercase characters and numbers."
 | 
			
		||||
                              u"The character '{}' cannot be used".format(char))
 | 
			
		||||
    if value in RESERVED_IDS:
 | 
			
		||||
        raise vol.Invalid(u"ID {} is reserved internally and cannot be used".format(value))
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def use_variable_id(type):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if value is None:
 | 
			
		||||
            return core.ID(None, is_declaration=False, type=type)
 | 
			
		||||
 | 
			
		||||
        return core.ID(variable_id_str_(value), is_declaration=False, type=type)
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def declare_variable_id(type):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if value is None:
 | 
			
		||||
            return core.ID(None, is_declaration=True, type=type)
 | 
			
		||||
 | 
			
		||||
        return core.ID(variable_id_str_(value), is_declaration=True, type=type)
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def templatable(other_validators):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if isinstance(value, Lambda):
 | 
			
		||||
            return value
 | 
			
		||||
        return other_validators(value)
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def only_on(platforms):
 | 
			
		||||
    if not isinstance(platforms, list):
 | 
			
		||||
        platforms = [platforms]
 | 
			
		||||
@@ -169,90 +209,161 @@ def has_at_least_one_key(*keys):
 | 
			
		||||
        if not isinstance(obj, dict):
 | 
			
		||||
            raise vol.Invalid('expected dictionary')
 | 
			
		||||
 | 
			
		||||
        for k in obj.keys():
 | 
			
		||||
            if k in keys:
 | 
			
		||||
                return obj
 | 
			
		||||
        raise vol.Invalid('must contain one of {}.'.format(', '.join(keys)))
 | 
			
		||||
        if not any(k in keys for k in obj):
 | 
			
		||||
            raise vol.Invalid('Must contain at least one of {}.'.format(', '.join(keys)))
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
    return validate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TIME_PERIOD_ERROR = "Time period {} should be format 5ms, 5s, 5min, 5h"
 | 
			
		||||
def has_exactly_one_key(*keys):
 | 
			
		||||
    def validate(obj):
 | 
			
		||||
        if not isinstance(obj, dict):
 | 
			
		||||
            raise vol.Invalid('expected dictionary')
 | 
			
		||||
 | 
			
		||||
        number = sum(k in keys for k in obj)
 | 
			
		||||
        if number > 1:
 | 
			
		||||
            raise vol.Invalid("Cannot specify more than one of {}.".format(', '.join(keys)))
 | 
			
		||||
        if number < 1:
 | 
			
		||||
            raise vol.Invalid('Must contain exactly one of {}.'.format(', '.join(keys)))
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
    return validate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h"
 | 
			
		||||
 | 
			
		||||
time_period_dict = vol.All(
 | 
			
		||||
    dict, vol.Schema({
 | 
			
		||||
        'days': vol.Coerce(int),
 | 
			
		||||
        'hours': vol.Coerce(int),
 | 
			
		||||
        'minutes': vol.Coerce(int),
 | 
			
		||||
        'seconds': vol.Coerce(int),
 | 
			
		||||
        'milliseconds': vol.Coerce(int),
 | 
			
		||||
        'days': vol.Coerce(float),
 | 
			
		||||
        'hours': vol.Coerce(float),
 | 
			
		||||
        'minutes': vol.Coerce(float),
 | 
			
		||||
        'seconds': vol.Coerce(float),
 | 
			
		||||
        'milliseconds': vol.Coerce(float),
 | 
			
		||||
        'microseconds': vol.Coerce(float),
 | 
			
		||||
    }),
 | 
			
		||||
    has_at_least_one_key('days', 'hours', 'minutes',
 | 
			
		||||
                         'seconds', 'milliseconds'),
 | 
			
		||||
    lambda value: timedelta(**value))
 | 
			
		||||
                         'seconds', 'milliseconds', 'microseconds'),
 | 
			
		||||
    lambda value: TimePeriod(**value))
 | 
			
		||||
 | 
			
		||||
TIME_PERIOD_EXPLICIT_MESSAGE = ("The old way of being able to write time values without a "
 | 
			
		||||
                                "time unit (like \"1000\" for 1000 milliseconds) has been "
 | 
			
		||||
                                "removed in 1.5.0 as it was ambiguous in some places. Please "
 | 
			
		||||
                                "now explicitly specify the time unit (like \"1000ms\"). See "
 | 
			
		||||
                                "https://esphomelib.com/esphomeyaml/configuration-types.html#time "
 | 
			
		||||
                                "for more information.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_period_str(value):
 | 
			
		||||
    """Validate and transform time offset."""
 | 
			
		||||
def time_period_str_colon(value):
 | 
			
		||||
    """Validate and transform time offset with format HH:MM[:SS]."""
 | 
			
		||||
    if isinstance(value, int):
 | 
			
		||||
        raise vol.Invalid("Make sure you wrap time values in quotes")
 | 
			
		||||
    elif not isinstance(value, (str, unicode)):
 | 
			
		||||
        raise vol.Invalid('Make sure you wrap time values in quotes')
 | 
			
		||||
    elif not isinstance(value, str):
 | 
			
		||||
        raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
 | 
			
		||||
 | 
			
		||||
    value = unicode(value)
 | 
			
		||||
    if value.endswith(u'ms'):
 | 
			
		||||
        return vol.Coerce(int)(value[:-2])
 | 
			
		||||
    elif value.endswith(u's'):
 | 
			
		||||
        return vol.Coerce(float)(value[:-1]) * 1000
 | 
			
		||||
    elif value.endswith(u'min'):
 | 
			
		||||
        return vol.Coerce(float)(value[:-3]) * 1000 * 60
 | 
			
		||||
    elif value.endswith(u'h'):
 | 
			
		||||
        return vol.Coerce(float)(value[:-1]) * 1000 * 60 * 60
 | 
			
		||||
    raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_period_milliseconds(value):
 | 
			
		||||
    try:
 | 
			
		||||
        return timedelta(milliseconds=int(value))
 | 
			
		||||
    except (ValueError, TypeError):
 | 
			
		||||
        raise vol.Invalid('Expected milliseconds, got {}'.format(value))
 | 
			
		||||
        parsed = [int(x) for x in value.split(':')]
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
 | 
			
		||||
 | 
			
		||||
    if len(parsed) == 2:
 | 
			
		||||
        hour, minute = parsed
 | 
			
		||||
        second = 0
 | 
			
		||||
    elif len(parsed) == 3:
 | 
			
		||||
        hour, minute, second = parsed
 | 
			
		||||
    else:
 | 
			
		||||
        raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
 | 
			
		||||
 | 
			
		||||
    return TimePeriod(hours=hour, minutes=minute, seconds=second)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_period_to_milliseconds(value):
 | 
			
		||||
    if isinstance(value, (int, long)):
 | 
			
		||||
        return value
 | 
			
		||||
    if isinstance(value, float):
 | 
			
		||||
        return int(value)
 | 
			
		||||
    return value / timedelta(milliseconds=1)
 | 
			
		||||
def time_period_str_unit(value):
 | 
			
		||||
    """Validate and transform time period with time unit and integer value."""
 | 
			
		||||
    if isinstance(value, int):
 | 
			
		||||
        value = str(value)
 | 
			
		||||
    elif not isinstance(value, (str, unicode)):
 | 
			
		||||
        raise vol.Invalid("Expected string for time period with unit.")
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        float(value)
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        pass
 | 
			
		||||
    else:
 | 
			
		||||
        raise vol.Invalid(TIME_PERIOD_EXPLICIT_MESSAGE)
 | 
			
		||||
 | 
			
		||||
    unit_to_kwarg = {
 | 
			
		||||
        'us': 'microseconds',
 | 
			
		||||
        'microseconds': 'microseconds',
 | 
			
		||||
        'ms': 'milliseconds',
 | 
			
		||||
        'milliseconds': 'milliseconds',
 | 
			
		||||
        's': 'seconds',
 | 
			
		||||
        'sec': 'seconds',
 | 
			
		||||
        'seconds': 'seconds',
 | 
			
		||||
        'min': 'minutes',
 | 
			
		||||
        'minutes': 'minutes',
 | 
			
		||||
        'h': 'hours',
 | 
			
		||||
        'hours': 'hours',
 | 
			
		||||
        'd': 'days',
 | 
			
		||||
        'days': 'days',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*)$", value)
 | 
			
		||||
 | 
			
		||||
    if match is None or match.group(2) not in unit_to_kwarg:
 | 
			
		||||
        raise vol.Invalid(u"Expected time period with unit, "
 | 
			
		||||
                          u"got {}".format(value))
 | 
			
		||||
 | 
			
		||||
    kwarg = unit_to_kwarg[match.group(2)]
 | 
			
		||||
    return TimePeriod(**{kwarg: float(match.group(1))})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
time_period = vol.All(vol.Any(time_period_str, timedelta, time_period_dict,
 | 
			
		||||
                              time_period_milliseconds), time_period_to_milliseconds)
 | 
			
		||||
positive_time_period = vol.All(time_period, vol.Range(min=0))
 | 
			
		||||
positive_not_null_time_period = vol.All(time_period, vol.Range(min=0, min_included=False))
 | 
			
		||||
def time_period_in_milliseconds_(value):
 | 
			
		||||
    if value.microseconds is not None and value.microseconds != 0:
 | 
			
		||||
        raise vol.Invalid("Maximum precision is milliseconds")
 | 
			
		||||
    return TimePeriodMilliseconds(**value.as_dict())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_period_in_microseconds_(value):
 | 
			
		||||
    return TimePeriodMicroseconds(**value.as_dict())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_period_in_seconds_(value):
 | 
			
		||||
    if value.microseconds is not None and value.microseconds != 0:
 | 
			
		||||
        raise vol.Invalid("Maximum precision is seconds")
 | 
			
		||||
    if value.milliseconds is not None and value.milliseconds != 0:
 | 
			
		||||
        raise vol.Invalid("Maximum precision is seconds")
 | 
			
		||||
    return TimePeriodSeconds(**value.as_dict())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
time_period = vol.Any(time_period_str_unit, time_period_str_colon, time_period_dict)
 | 
			
		||||
positive_time_period = vol.All(time_period, vol.Range(min=TimePeriod()))
 | 
			
		||||
positive_time_period_milliseconds = vol.All(positive_time_period, time_period_in_milliseconds_)
 | 
			
		||||
positive_time_period_seconds = vol.All(positive_time_period, time_period_in_seconds_)
 | 
			
		||||
positive_time_period_microseconds = vol.All(positive_time_period, time_period_in_microseconds_)
 | 
			
		||||
positive_not_null_time_period = vol.All(time_period,
 | 
			
		||||
                                        vol.Range(min=TimePeriod(), min_included=False))
 | 
			
		||||
 | 
			
		||||
METRIC_SUFFIXES = {
 | 
			
		||||
    'E': 1e18, 'P': 1e15, 'T': 1e12, 'G': 1e9, 'M': 1e6, 'k': 1e3, 'da': 10, 'd': 1e-1,
 | 
			
		||||
    'c': 1e-2, 'm': 0.001, u'µ': 1e-6, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12, 'f': 1e-15, 'a': 1e-18,
 | 
			
		||||
    '': 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def frequency(value):
 | 
			
		||||
    value = string(value).replace(' ', '').lower()
 | 
			
		||||
    if value.endswith('Hz') or value.endswith('hz') or value.endswith('HZ'):
 | 
			
		||||
        value = value[:-2]
 | 
			
		||||
    if not value:
 | 
			
		||||
        raise vol.Invalid(u"Frequency must have value")
 | 
			
		||||
    multiplier = 1
 | 
			
		||||
    if value[:-1] in METRIC_SUFFIXES:
 | 
			
		||||
        multiplier = METRIC_SUFFIXES[value[:-1]]
 | 
			
		||||
        value = value[:-1]
 | 
			
		||||
    elif len(value) >= 2 and value[:-2] in METRIC_SUFFIXES:
 | 
			
		||||
        multiplier = METRIC_SUFFIXES[value[:-2]]
 | 
			
		||||
        value = value[:-2]
 | 
			
		||||
    float_val = vol.Coerce(float)(value)
 | 
			
		||||
    return float_val * multiplier
 | 
			
		||||
    value = string(value)
 | 
			
		||||
    match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)(?:Hz|HZ|hz)?$", value)
 | 
			
		||||
 | 
			
		||||
    if match is None:
 | 
			
		||||
        raise vol.Invalid(u"Expected frequency with unit, "
 | 
			
		||||
                          u"got {}".format(value))
 | 
			
		||||
 | 
			
		||||
    mantissa = float(match.group(1))
 | 
			
		||||
    if match.group(2) not in METRIC_SUFFIXES:
 | 
			
		||||
        raise vol.Invalid(u"Invalid frequency suffix {}".format(match.group(2)))
 | 
			
		||||
 | 
			
		||||
    multiplier = METRIC_SUFFIXES[match.group(2)]
 | 
			
		||||
    return mantissa * multiplier
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def hostname(value):
 | 
			
		||||
@@ -265,6 +376,18 @@ def hostname(value):
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def domainname(value):
 | 
			
		||||
    value = string(value)
 | 
			
		||||
    if not value.startswith('.'):
 | 
			
		||||
        raise vol.Invalid("Domainname must start with .")
 | 
			
		||||
    if value.startswith('..'):
 | 
			
		||||
        raise vol.Invalid("Domainname must start with single .")
 | 
			
		||||
    for c in value:
 | 
			
		||||
        if not (c.isalnum() or c in '._-'):
 | 
			
		||||
            raise vol.Invalid("Domainname can only have alphanumeric characters and _ or -")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ssid(value):
 | 
			
		||||
    if value is None:
 | 
			
		||||
        raise vol.Invalid("SSID can not be None")
 | 
			
		||||
@@ -294,17 +417,70 @@ def ipv4(value):
 | 
			
		||||
    return IPAddress(*parts_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def publish_topic(value):
 | 
			
		||||
    value = string_strict(value)
 | 
			
		||||
    if value.endswith('/'):
 | 
			
		||||
        raise vol.Invalid("Publish topic can't end with '/'")
 | 
			
		||||
    if '+' in value or '#' in value:
 | 
			
		||||
        raise vol.Invalid("Publish topic can't contain '+' or '#'")
 | 
			
		||||
def _valid_topic(value):
 | 
			
		||||
    """Validate that this is a valid topic name/filter."""
 | 
			
		||||
    if isinstance(value, dict):
 | 
			
		||||
        raise vol.Invalid("Can't use dictionary with topic")
 | 
			
		||||
    value = string(value)
 | 
			
		||||
    try:
 | 
			
		||||
        raw_value = value.encode('utf-8')
 | 
			
		||||
    except UnicodeError:
 | 
			
		||||
        raise vol.Invalid("MQTT topic name/filter must be valid UTF-8 string.")
 | 
			
		||||
    if not raw_value:
 | 
			
		||||
        raise vol.Invalid("MQTT topic name/filter must not be empty.")
 | 
			
		||||
    if len(raw_value) > 65535:
 | 
			
		||||
        raise vol.Invalid("MQTT topic name/filter must not be longer than "
 | 
			
		||||
                          "65535 encoded bytes.")
 | 
			
		||||
    if '\0' in value:
 | 
			
		||||
        raise vol.Invalid("MQTT topic name/filter must not contain null "
 | 
			
		||||
                          "character.")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
subscribe_topic = string_strict  # TODO improve this
 | 
			
		||||
mqtt_payload = string  # TODO improve this
 | 
			
		||||
def subscribe_topic(value):
 | 
			
		||||
    """Validate that we can subscribe using this MQTT topic."""
 | 
			
		||||
    value = _valid_topic(value)
 | 
			
		||||
    for i in (i for i, c in enumerate(value) if c == '+'):
 | 
			
		||||
        if (i > 0 and value[i - 1] != '/') or \
 | 
			
		||||
                (i < len(value) - 1 and value[i + 1] != '/'):
 | 
			
		||||
            raise vol.Invalid("Single-level wildcard must occupy an entire "
 | 
			
		||||
                              "level of the filter")
 | 
			
		||||
 | 
			
		||||
    index = value.find('#')
 | 
			
		||||
    if index != -1:
 | 
			
		||||
        if index != len(value) - 1:
 | 
			
		||||
            # If there are multiple wildcards, this will also trigger
 | 
			
		||||
            raise vol.Invalid("Multi-level wildcard must be the last "
 | 
			
		||||
                              "character in the topic filter.")
 | 
			
		||||
        if len(value) > 1 and value[index - 1] != '/':
 | 
			
		||||
            raise vol.Invalid("Multi-level wildcard must be after a topic "
 | 
			
		||||
                              "level separator.")
 | 
			
		||||
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def publish_topic(value):
 | 
			
		||||
    """Validate that we can publish using this MQTT topic."""
 | 
			
		||||
    value = _valid_topic(value)
 | 
			
		||||
    if '+' in value or '#' in value:
 | 
			
		||||
        raise vol.Invalid("Wildcards can not be used in topic names")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mqtt_payload(value):
 | 
			
		||||
    if value is None:
 | 
			
		||||
        return ''
 | 
			
		||||
    return string(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mqtt_qos(value):
 | 
			
		||||
    try:
 | 
			
		||||
        value = int(value)
 | 
			
		||||
    except (TypeError, ValueError):
 | 
			
		||||
        raise vol.Invalid(u"MQTT Quality of Service must be integer, got {}".format(value))
 | 
			
		||||
    return one_of(0, 1, 2)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint8_t = vol.All(int_, vol.Range(min=0, max=255))
 | 
			
		||||
uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
 | 
			
		||||
uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295))
 | 
			
		||||
@@ -314,37 +490,47 @@ hex_uint32_t = vol.All(hex_int, vol.Range(min=0, max=4294967295))
 | 
			
		||||
i2c_address = hex_uint8_t
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def invalid(_):
 | 
			
		||||
    raise vol.Invalid("This shouldn't happen.")
 | 
			
		||||
def percentage(value):
 | 
			
		||||
    if isinstance(value, (str, unicode)) and value.endswith('%'):
 | 
			
		||||
        value = float(value[:-1].rstrip()) / 100.0
 | 
			
		||||
    return zero_to_one_float(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def invalid(message):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        raise vol.Invalid(message)
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def valid(value):
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def one_of(*values):
 | 
			
		||||
    options = u', '.join(u"'{}'".format(x) for x in values)
 | 
			
		||||
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if value not in values:
 | 
			
		||||
            raise vol.Invalid(u"Unknown value '{}', must be one of {}".format(value, options))
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lambda_(value):
 | 
			
		||||
    if isinstance(value, Lambda):
 | 
			
		||||
        return value
 | 
			
		||||
    return Lambda(string_strict(value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
REGISTERED_IDS = set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_variable_id(value):
 | 
			
		||||
    s = variable_id(value)
 | 
			
		||||
    if s in REGISTERED_IDS:
 | 
			
		||||
        raise vol.Invalid("This ID has already been used")
 | 
			
		||||
    REGISTERED_IDS.add(s)
 | 
			
		||||
    return s
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GenerateID(vol.Optional):
 | 
			
		||||
    def __init__(self, basename, key=CONF_ID):
 | 
			
		||||
        self._basename = basename
 | 
			
		||||
        super(GenerateID, self).__init__(key, default=self.default_variable_id)
 | 
			
		||||
    def __init__(self, key=CONF_ID):
 | 
			
		||||
        super(GenerateID, self).__init__(key, default=lambda: None)
 | 
			
		||||
 | 
			
		||||
    def default_variable_id(self):
 | 
			
		||||
        return ensure_unique_string(self._basename, REGISTERED_IDS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
REQUIRED_ID_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_ID): register_variable_id,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
PLATFORM_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_PLATFORM): valid,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
"""Constants used by esphomeyaml."""
 | 
			
		||||
 | 
			
		||||
MAJOR_VERSION = 1
 | 
			
		||||
MINOR_VERSION = 3
 | 
			
		||||
PATCH_VERSION = '0'
 | 
			
		||||
MINOR_VERSION = 6
 | 
			
		||||
PATCH_VERSION = '1'
 | 
			
		||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
 | 
			
		||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +34,13 @@ CONF_USERNAME = 'username'
 | 
			
		||||
CONF_POWER_SUPPLY = 'power_supply'
 | 
			
		||||
CONF_ID = 'id'
 | 
			
		||||
CONF_MQTT_ID = 'mqtt_id'
 | 
			
		||||
CONF_SENSOR_ID = 'sensor_id'
 | 
			
		||||
CONF_TRIGGER_ID = 'trigger_id'
 | 
			
		||||
CONF_ACTION_ID = 'action_id'
 | 
			
		||||
CONF_CONDITION_ID = 'condition_id'
 | 
			
		||||
CONF_MAKE_ID = 'make_id'
 | 
			
		||||
CONF_AUTOMATION_ID = 'automation_id'
 | 
			
		||||
CONF_DELAY = 'delay'
 | 
			
		||||
CONF_PIN = 'pin'
 | 
			
		||||
CONF_NUMBER = 'number'
 | 
			
		||||
CONF_INVERTED = 'inverted'
 | 
			
		||||
@@ -70,6 +77,15 @@ CONF_TOPIC = 'topic'
 | 
			
		||||
CONF_PAYLOAD_AVAILABLE = 'payload_available'
 | 
			
		||||
CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available'
 | 
			
		||||
CONF_DEFAULT_TRANSITION_LENGTH = 'default_transition_length'
 | 
			
		||||
CONF_TRANSITION_LENGTH = 'transition_length'
 | 
			
		||||
CONF_FLASH_LENGTH = 'flash_length'
 | 
			
		||||
CONF_BRIGHTNESS = 'brightness'
 | 
			
		||||
CONF_EFFECT = 'effect'
 | 
			
		||||
CONF_ABOVE = 'above'
 | 
			
		||||
CONF_BELOW = 'below'
 | 
			
		||||
CONF_ON = 'on'
 | 
			
		||||
CONF_IF = 'if'
 | 
			
		||||
CONF_THEN = 'then'
 | 
			
		||||
CONF_BINARY = 'binary'
 | 
			
		||||
CONF_WHITE = 'white'
 | 
			
		||||
CONF_RGBW = 'rgbw'
 | 
			
		||||
@@ -109,6 +125,14 @@ CONF_WINDOW_SIZE = 'window_size'
 | 
			
		||||
CONF_SEND_EVERY = 'send_every'
 | 
			
		||||
CONF_ALPHA = 'alpha'
 | 
			
		||||
CONF_LAMBDA = 'lambda'
 | 
			
		||||
CONF_THROTTLE = 'throttle'
 | 
			
		||||
CONF_DELTA = 'delta'
 | 
			
		||||
CONF_OR = 'or'
 | 
			
		||||
CONF_AND = 'and'
 | 
			
		||||
CONF_RANGE = 'range'
 | 
			
		||||
CONF_UNIQUE = 'unique'
 | 
			
		||||
CONF_HEARTBEAT = 'heartbeat'
 | 
			
		||||
CONF_DEBOUNCE = 'debounce'
 | 
			
		||||
CONF_UPDATE_INTERVAL = 'update_interval'
 | 
			
		||||
CONF_PULL_MODE = 'pull_mode'
 | 
			
		||||
CONF_COUNT_MODE = 'count_mode'
 | 
			
		||||
@@ -135,11 +159,12 @@ CONF_SONY = 'sony'
 | 
			
		||||
CONF_PANASONIC = 'panasonic'
 | 
			
		||||
CONF_REPEAT = 'repeat'
 | 
			
		||||
CONF_TIMES = 'times'
 | 
			
		||||
CONF_WAIT_TIME_US = 'wait_time_us'
 | 
			
		||||
CONF_WAIT_TIME = 'wait_time'
 | 
			
		||||
CONF_OSCILLATION_OUTPUT = 'oscillation_output'
 | 
			
		||||
CONF_SPEED = 'speed'
 | 
			
		||||
CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic'
 | 
			
		||||
CONF_OSCILLATION_COMMAND_TOPIC = 'oscillation_command_topic'
 | 
			
		||||
CONF_OSCILLATING = 'oscillating'
 | 
			
		||||
CONF_SPEED_STATE_TOPIC = 'speed_state_topic'
 | 
			
		||||
CONF_SPEED_COMMAND_TOPIC = 'speed_command_topic'
 | 
			
		||||
CONF_LOW = 'low'
 | 
			
		||||
@@ -161,6 +186,51 @@ CONF_AP = 'ap'
 | 
			
		||||
CONF_CSS_URL = 'css_url'
 | 
			
		||||
CONF_JS_URL = 'js_url'
 | 
			
		||||
CONF_SSL_FINGERPRINTS = 'ssl_fingerprints'
 | 
			
		||||
CONF_PCF8574 = 'pcf8574'
 | 
			
		||||
CONF_PCF8575 = 'pcf8575'
 | 
			
		||||
CONF_SCAN = 'scan'
 | 
			
		||||
CONF_KEEPALIVE = 'keepalive'
 | 
			
		||||
CONF_INTEGRATION_TIME = 'integration_time'
 | 
			
		||||
CONF_RECEIVE_TIMEOUT = 'receive_timeout'
 | 
			
		||||
CONF_SCAN_INTERVAL = 'scan_interval'
 | 
			
		||||
CONF_MAC_ADDRESS = 'mac_address'
 | 
			
		||||
CONF_SETUP_MODE = 'setup_mode'
 | 
			
		||||
CONF_IIR_FILTER = 'iir_filter'
 | 
			
		||||
CONF_MEASUREMENT_DURATION = 'measurement_duration'
 | 
			
		||||
CONF_LOW_VOLTAGE_REFERENCE = 'low_voltage_reference'
 | 
			
		||||
CONF_HIGH_VOLTAGE_REFERENCE = 'high_voltage_reference'
 | 
			
		||||
CONF_VOLTAGE_ATTENUATION = 'voltage_attenuation'
 | 
			
		||||
CONF_THRESHOLD = 'threshold'
 | 
			
		||||
CONF_OVERSAMPLING = 'oversampling'
 | 
			
		||||
CONF_GAS_RESISTANCE = 'gas_resistance'
 | 
			
		||||
CONF_NUM_LEDS = 'num_leds'
 | 
			
		||||
CONF_MAX_REFRESH_RATE = 'max_refresh_rate'
 | 
			
		||||
CONF_CHIPSET = 'chipset'
 | 
			
		||||
CONF_DATA_PIN = 'data_pin'
 | 
			
		||||
CONF_CLOCK_PIN = 'clock_pin'
 | 
			
		||||
CONF_RGB_ORDER = 'rgb_order'
 | 
			
		||||
CONF_ACCURACY = 'accuracy'
 | 
			
		||||
CONF_BOARD_FLASH_MODE = 'board_flash_mode'
 | 
			
		||||
CONF_ON_PRESS = 'on_press'
 | 
			
		||||
CONF_ON_RELEASE = 'on_release'
 | 
			
		||||
CONF_ON_CLICK = 'on_click'
 | 
			
		||||
CONF_ON_DOUBLE_CLICK = 'on_double_click'
 | 
			
		||||
CONF_MIN_LENGTH = 'min_length'
 | 
			
		||||
CONF_MAX_LENGTH = 'max_length'
 | 
			
		||||
CONF_ON_VALUE = 'on_value'
 | 
			
		||||
CONF_ON_RAW_VALUE = 'on_raw_value'
 | 
			
		||||
CONF_ON_VALUE_RANGE = 'on_value_range'
 | 
			
		||||
CONF_ON_MESSAGE = 'on_message'
 | 
			
		||||
CONF_PIN_CS = 'pin_cs'
 | 
			
		||||
CONF_PIN_CLOCK = 'pin_clock'
 | 
			
		||||
CONF_PIN_MISO = 'pin_miso'
 | 
			
		||||
CONF_TURN_ON_ACTION = 'turn_on_action'
 | 
			
		||||
CONF_TURN_OFF_ACTION = 'turn_off_action'
 | 
			
		||||
CONF_OPEN_ACTION = 'open_action'
 | 
			
		||||
CONF_CLOSE_ACTION = 'close_action'
 | 
			
		||||
CONF_STOP_ACTION = 'stop_action'
 | 
			
		||||
CONF_DOMAIN = 'domain'
 | 
			
		||||
CONF_OPTIMISTIC = 'optimistic'
 | 
			
		||||
 | 
			
		||||
ESP32_BOARDS = [
 | 
			
		||||
    'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',
 | 
			
		||||
@@ -183,3 +253,5 @@ ESP_BOARDS_FOR_PLATFORM = {
 | 
			
		||||
    ESP_PLATFORM_ESP32: ESP32_BOARDS,
 | 
			
		||||
    ESP_PLATFORM_ESP8266: ESP8266_BOARDS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,8 @@
 | 
			
		||||
import math
 | 
			
		||||
import re
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ESPHomeYAMLError(Exception):
 | 
			
		||||
    """General esphomeyaml exception occurred."""
 | 
			
		||||
    pass
 | 
			
		||||
@@ -18,7 +23,213 @@ class IPAddress(object):
 | 
			
		||||
        return '.'.join(str(x) for x in self.args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MACAddress(object):
 | 
			
		||||
    def __init__(self, *parts):
 | 
			
		||||
        if len(parts) != 6:
 | 
			
		||||
            raise ValueError(u"MAC Address must consist of 6 items")
 | 
			
		||||
        self.parts = parts
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return ':'.join('{:02X}'.format(part) for part in self.parts)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_approximately_integer(value):
 | 
			
		||||
    if isinstance(value, (int, long)):
 | 
			
		||||
        return True
 | 
			
		||||
    return abs(value - round(value)) < 0.001
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimePeriod(object):
 | 
			
		||||
    def __init__(self, microseconds=None, milliseconds=None, seconds=None,
 | 
			
		||||
                 minutes=None, hours=None, days=None):
 | 
			
		||||
        if days is not None:
 | 
			
		||||
            if not is_approximately_integer(days):
 | 
			
		||||
                frac_days, days = math.modf(days)
 | 
			
		||||
                hours = (hours or 0) + frac_days * 24
 | 
			
		||||
            self.days = int(round(days))
 | 
			
		||||
        else:
 | 
			
		||||
            self.days = None
 | 
			
		||||
 | 
			
		||||
        if hours is not None:
 | 
			
		||||
            if not is_approximately_integer(hours):
 | 
			
		||||
                frac_hours, hours = math.modf(hours)
 | 
			
		||||
                minutes = (minutes or 0) + frac_hours * 60
 | 
			
		||||
            self.hours = int(round(hours))
 | 
			
		||||
        else:
 | 
			
		||||
            self.hours = None
 | 
			
		||||
 | 
			
		||||
        if minutes is not None:
 | 
			
		||||
            if not is_approximately_integer(minutes):
 | 
			
		||||
                frac_minutes, minutes = math.modf(minutes)
 | 
			
		||||
                seconds = (seconds or 0) + frac_minutes * 60
 | 
			
		||||
            self.minutes = int(round(minutes))
 | 
			
		||||
        else:
 | 
			
		||||
            self.minutes = None
 | 
			
		||||
 | 
			
		||||
        if seconds is not None:
 | 
			
		||||
            if not is_approximately_integer(seconds):
 | 
			
		||||
                frac_seconds, seconds = math.modf(seconds)
 | 
			
		||||
                milliseconds = (milliseconds or 0) + frac_seconds * 1000
 | 
			
		||||
            self.seconds = int(round(seconds))
 | 
			
		||||
        else:
 | 
			
		||||
            self.seconds = None
 | 
			
		||||
 | 
			
		||||
        if milliseconds is not None:
 | 
			
		||||
            if not is_approximately_integer(milliseconds):
 | 
			
		||||
                frac_milliseconds, milliseconds = math.modf(milliseconds)
 | 
			
		||||
                microseconds = (microseconds or 0) + frac_milliseconds * 1000
 | 
			
		||||
            self.milliseconds = int(round(milliseconds))
 | 
			
		||||
        else:
 | 
			
		||||
            self.milliseconds = None
 | 
			
		||||
 | 
			
		||||
        if microseconds is not None:
 | 
			
		||||
            if not is_approximately_integer(microseconds):
 | 
			
		||||
                raise ValueError("Maximum precision is microseconds")
 | 
			
		||||
            self.microseconds = int(round(microseconds))
 | 
			
		||||
        else:
 | 
			
		||||
            self.microseconds = None
 | 
			
		||||
 | 
			
		||||
    def as_dict(self):
 | 
			
		||||
        out = OrderedDict()
 | 
			
		||||
        if self.microseconds is not None:
 | 
			
		||||
            out['microseconds'] = self.microseconds
 | 
			
		||||
        if self.milliseconds is not None:
 | 
			
		||||
            out['milliseconds'] = self.milliseconds
 | 
			
		||||
        if self.seconds is not None:
 | 
			
		||||
            out['seconds'] = self.seconds
 | 
			
		||||
        if self.minutes is not None:
 | 
			
		||||
            out['minutes'] = self.minutes
 | 
			
		||||
        if self.hours is not None:
 | 
			
		||||
            out['hours'] = self.hours
 | 
			
		||||
        if self.days is not None:
 | 
			
		||||
            out['days'] = self.days
 | 
			
		||||
        return out
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def total_microseconds(self):
 | 
			
		||||
        return self.total_milliseconds * 1000 + (self.microseconds or 0)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def total_milliseconds(self):
 | 
			
		||||
        return self.total_seconds * 1000 + (self.milliseconds or 0)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def total_seconds(self):
 | 
			
		||||
        return self.total_minutes * 60 + (self.seconds or 0)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def total_minutes(self):
 | 
			
		||||
        return self.total_hours * 60 + (self.minutes or 0)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def total_hours(self):
 | 
			
		||||
        return self.total_days * 24 + (self.hours or 0)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def total_days(self):
 | 
			
		||||
        return self.days or 0
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        if not isinstance(other, TimePeriod):
 | 
			
		||||
            raise ValueError("other must be TimePeriod")
 | 
			
		||||
        return self.total_microseconds == other.total_microseconds
 | 
			
		||||
 | 
			
		||||
    def __ne__(self, other):
 | 
			
		||||
        if not isinstance(other, TimePeriod):
 | 
			
		||||
            raise ValueError("other must be TimePeriod")
 | 
			
		||||
        return self.total_microseconds != other.total_microseconds
 | 
			
		||||
 | 
			
		||||
    def __lt__(self, other):
 | 
			
		||||
        if not isinstance(other, TimePeriod):
 | 
			
		||||
            raise ValueError("other must be TimePeriod")
 | 
			
		||||
        return self.total_microseconds < other.total_microseconds
 | 
			
		||||
 | 
			
		||||
    def __gt__(self, other):
 | 
			
		||||
        if not isinstance(other, TimePeriod):
 | 
			
		||||
            raise ValueError("other must be TimePeriod")
 | 
			
		||||
        return self.total_microseconds > other.total_microseconds
 | 
			
		||||
 | 
			
		||||
    def __le__(self, other):
 | 
			
		||||
        if not isinstance(other, TimePeriod):
 | 
			
		||||
            raise ValueError("other must be TimePeriod")
 | 
			
		||||
        return self.total_microseconds <= other.total_microseconds
 | 
			
		||||
 | 
			
		||||
    def __ge__(self, other):
 | 
			
		||||
        if not isinstance(other, TimePeriod):
 | 
			
		||||
            raise ValueError("other must be TimePeriod")
 | 
			
		||||
        return self.total_microseconds >= other.total_microseconds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimePeriodMicroseconds(TimePeriod):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimePeriodMilliseconds(TimePeriod):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimePeriodSeconds(TimePeriod):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Lambda(object):
 | 
			
		||||
    def __init__(self, value):
 | 
			
		||||
        self.value = value
 | 
			
		||||
        self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)\.', value)
 | 
			
		||||
        self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 2)]
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.value
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return u'Lambda<{}>'.format(self.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_unique_string(preferred_string, current_strings):
 | 
			
		||||
    test_string = preferred_string
 | 
			
		||||
    current_strings_set = set(current_strings)
 | 
			
		||||
 | 
			
		||||
    tries = 1
 | 
			
		||||
 | 
			
		||||
    while test_string in current_strings_set:
 | 
			
		||||
        tries += 1
 | 
			
		||||
        test_string = u"{}_{}".format(preferred_string, tries)
 | 
			
		||||
 | 
			
		||||
    return test_string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ID(object):
 | 
			
		||||
    def __init__(self, id, is_declaration=False, type=None):
 | 
			
		||||
        self.id = id
 | 
			
		||||
        self.is_manual = id is not None
 | 
			
		||||
        self.is_declaration = is_declaration
 | 
			
		||||
        self.type = type
 | 
			
		||||
 | 
			
		||||
    def resolve(self, registered_ids):
 | 
			
		||||
        if self.id is None:
 | 
			
		||||
            base = str(self.type).replace('::', '_').lower()
 | 
			
		||||
            name = ''.join(c for c in base if c.isalnum() or c == '_')
 | 
			
		||||
            self.id = ensure_unique_string(name, registered_ids)
 | 
			
		||||
        return self.id
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.id
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return u'ID<{} declaration={}, type={}, manual={}>'.format(
 | 
			
		||||
            self.id, self.is_declaration, self.type, self.is_manual)
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        if not isinstance(other, ID):
 | 
			
		||||
            raise ValueError("other must be ID")
 | 
			
		||||
        return self.id == other.id
 | 
			
		||||
 | 
			
		||||
    def __hash__(self):
 | 
			
		||||
        return hash(self.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_PATH = None
 | 
			
		||||
SIMPLIFY = True
 | 
			
		||||
ESP_PLATFORM = ''
 | 
			
		||||
BOARD = ''
 | 
			
		||||
RAW_CONFIG = None
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphomeyaml/dashboard/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphomeyaml/dashboard/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										197
									
								
								esphomeyaml/dashboard/dashboard.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								esphomeyaml/dashboard/dashboard.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
			
		||||
# pylint: disable=wrong-import-position
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import codecs
 | 
			
		||||
import json
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import random
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
from esphomeyaml import const, core, __main__
 | 
			
		||||
from esphomeyaml.__main__ import get_serial_ports, get_base_path, get_name
 | 
			
		||||
from esphomeyaml.helpers import quote
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import tornado
 | 
			
		||||
    import tornado.gen
 | 
			
		||||
    import tornado.ioloop
 | 
			
		||||
    import tornado.iostream
 | 
			
		||||
    import tornado.process
 | 
			
		||||
    import tornado.web
 | 
			
		||||
    import tornado.websocket
 | 
			
		||||
    import tornado.concurrent
 | 
			
		||||
except ImportError as err:
 | 
			
		||||
    tornado = None
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
CONFIG_DIR = ''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# pylint: disable=abstract-method, arguments-differ
 | 
			
		||||
class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler):
 | 
			
		||||
    def __init__(self, application, request, **kwargs):
 | 
			
		||||
        super(EsphomeyamlCommandWebSocket, self).__init__(application, request, **kwargs)
 | 
			
		||||
        self.proc = None
 | 
			
		||||
        self.closed = False
 | 
			
		||||
 | 
			
		||||
    def on_message(self, message):
 | 
			
		||||
        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])
 | 
			
		||||
        self.proc = tornado.process.Subprocess(command,
 | 
			
		||||
                                               stdout=tornado.process.Subprocess.STREAM,
 | 
			
		||||
                                               stderr=subprocess.STDOUT)
 | 
			
		||||
        self.proc.set_exit_callback(self.proc_on_exit)
 | 
			
		||||
        tornado.ioloop.IOLoop.current().spawn_callback(self.redirect_stream)
 | 
			
		||||
 | 
			
		||||
    @tornado.gen.coroutine
 | 
			
		||||
    def redirect_stream(self):
 | 
			
		||||
        while True:
 | 
			
		||||
            try:
 | 
			
		||||
                data = yield self.proc.stdout.read_until_regex('[\n\r]')
 | 
			
		||||
            except tornado.iostream.StreamClosedError:
 | 
			
		||||
                break
 | 
			
		||||
            if data.endswith('\r') and random.randrange(100) < 90:
 | 
			
		||||
                continue
 | 
			
		||||
            data = data.replace('\033', '\\033')
 | 
			
		||||
            self.write_message({'event': 'line', 'data': data})
 | 
			
		||||
 | 
			
		||||
    def proc_on_exit(self, returncode):
 | 
			
		||||
        if not self.closed:
 | 
			
		||||
            _LOGGER.debug("Process exited with return code %s", returncode)
 | 
			
		||||
            self.write_message({'event': 'exit', 'code': returncode})
 | 
			
		||||
 | 
			
		||||
    def on_close(self):
 | 
			
		||||
        self.closed = True
 | 
			
		||||
        if self.proc is not None and self.proc.returncode is None:
 | 
			
		||||
            _LOGGER.debug("Terminating process")
 | 
			
		||||
            self.proc.proc.terminate()
 | 
			
		||||
 | 
			
		||||
    def build_command(self, message):
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket):
 | 
			
		||||
    def build_command(self, message):
 | 
			
		||||
        js = json.loads(message)
 | 
			
		||||
        config_file = CONFIG_DIR + '/' + js['configuration']
 | 
			
		||||
        return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
 | 
			
		||||
    def build_command(self, message):
 | 
			
		||||
        js = json.loads(message)
 | 
			
		||||
        config_file = os.path.join(CONFIG_DIR, js['configuration'])
 | 
			
		||||
        return ["esphomeyaml", config_file, "run", '--upload-port', js["port"],
 | 
			
		||||
                '--escape', '--use-esptoolpy']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EsphomeyamlCompileHandler(EsphomeyamlCommandWebSocket):
 | 
			
		||||
    def build_command(self, message):
 | 
			
		||||
        js = json.loads(message)
 | 
			
		||||
        config_file = os.path.join(CONFIG_DIR, js['configuration'])
 | 
			
		||||
        return ["esphomeyaml", config_file, "compile"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket):
 | 
			
		||||
    def build_command(self, message):
 | 
			
		||||
        js = json.loads(message)
 | 
			
		||||
        config_file = os.path.join(CONFIG_DIR, js['configuration'])
 | 
			
		||||
        return ["esphomeyaml", config_file, "config"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SerialPortRequestHandler(tornado.web.RequestHandler):
 | 
			
		||||
    def get(self):
 | 
			
		||||
        ports = get_serial_ports()
 | 
			
		||||
        data = []
 | 
			
		||||
        for port, desc in ports:
 | 
			
		||||
            if port == '/dev/ttyAMA0':
 | 
			
		||||
                desc = 'UART pins on GPIO header'
 | 
			
		||||
            split_desc = desc.split(' - ')
 | 
			
		||||
            if len(split_desc) == 2 and split_desc[0] == split_desc[1]:
 | 
			
		||||
                # Some serial ports repeat their values
 | 
			
		||||
                desc = split_desc[0]
 | 
			
		||||
            data.append({'port': port, 'desc': desc})
 | 
			
		||||
        data.append({'port': 'OTA', 'desc': 'Over-The-Air'})
 | 
			
		||||
        self.write(json.dumps(sorted(data, reverse=True)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WizardRequestHandler(tornado.web.RequestHandler):
 | 
			
		||||
    def post(self):
 | 
			
		||||
        from esphomeyaml import wizard
 | 
			
		||||
 | 
			
		||||
        kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()}
 | 
			
		||||
        config = wizard.wizard_file(**kwargs)
 | 
			
		||||
        destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
 | 
			
		||||
        with codecs.open(destination, 'w') as f_handle:
 | 
			
		||||
            f_handle.write(config)
 | 
			
		||||
 | 
			
		||||
        self.redirect('/?begin=True')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DownloadBinaryRequestHandler(tornado.web.RequestHandler):
 | 
			
		||||
    def get(self):
 | 
			
		||||
        configuration = self.get_argument('configuration')
 | 
			
		||||
        config_file = os.path.join(CONFIG_DIR, configuration)
 | 
			
		||||
        core.CONFIG_PATH = config_file
 | 
			
		||||
        config = __main__.read_config(core.CONFIG_PATH)
 | 
			
		||||
        name = get_name(config)
 | 
			
		||||
        path = os.path.join(get_base_path(config), '.pioenvs', name, 'firmware.bin')
 | 
			
		||||
        self.set_header('Content-Type', 'application/octet-stream')
 | 
			
		||||
        self.set_header("Content-Disposition", 'attachment; filename="{}.bin"'.format(name))
 | 
			
		||||
        with open(path, 'rb') as f:
 | 
			
		||||
            while 1:
 | 
			
		||||
                data = f.read(16384)  # or some other nice-sized chunk
 | 
			
		||||
                if not data:
 | 
			
		||||
                    break
 | 
			
		||||
                self.write(data)
 | 
			
		||||
        self.finish()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MainRequestHandler(tornado.web.RequestHandler):
 | 
			
		||||
    def get(self):
 | 
			
		||||
        begin = bool(self.get_argument('begin', False))
 | 
			
		||||
        files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml') and
 | 
			
		||||
                        not f.startswith('.')])
 | 
			
		||||
        full_path_files = [os.path.join(CONFIG_DIR, f) for f in files]
 | 
			
		||||
        self.render("templates/index.html", files=files, full_path_files=full_path_files,
 | 
			
		||||
                    version=const.__version__, begin=begin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def make_app(debug=False):
 | 
			
		||||
    static_path = os.path.join(os.path.dirname(__file__), 'static')
 | 
			
		||||
    return tornado.web.Application([
 | 
			
		||||
        (r"/", MainRequestHandler),
 | 
			
		||||
        (r"/logs", EsphomeyamlLogsHandler),
 | 
			
		||||
        (r"/run", EsphomeyamlRunHandler),
 | 
			
		||||
        (r"/compile", EsphomeyamlCompileHandler),
 | 
			
		||||
        (r"/validate", EsphomeyamlValidateHandler),
 | 
			
		||||
        (r"/download.bin", DownloadBinaryRequestHandler),
 | 
			
		||||
        (r"/serial-ports", SerialPortRequestHandler),
 | 
			
		||||
        (r"/wizard.html", WizardRequestHandler),
 | 
			
		||||
        (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}),
 | 
			
		||||
    ], debug=debug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def start_web_server(args):
 | 
			
		||||
    global CONFIG_DIR
 | 
			
		||||
 | 
			
		||||
    if tornado is None:
 | 
			
		||||
        raise ESPHomeYAMLError("Attempted to load dashboard, but tornado is not installed! "
 | 
			
		||||
                               "Please run \"pip2 install tornado esptool\" in your terminal.")
 | 
			
		||||
 | 
			
		||||
    CONFIG_DIR = args.configuration
 | 
			
		||||
    if not os.path.exists(CONFIG_DIR):
 | 
			
		||||
        os.makedirs(CONFIG_DIR)
 | 
			
		||||
 | 
			
		||||
    _LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...",
 | 
			
		||||
                 args.port, CONFIG_DIR)
 | 
			
		||||
    app = make_app(args.verbose)
 | 
			
		||||
    app.listen(args.port)
 | 
			
		||||
    try:
 | 
			
		||||
        tornado.ioloop.IOLoop.current().start()
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        _LOGGER.info("Shutting down...")
 | 
			
		||||
							
								
								
									
										5
									
								
								esphomeyaml/dashboard/static/materialize-stepper.min.css
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								esphomeyaml/dashboard/static/materialize-stepper.min.css
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										5
									
								
								esphomeyaml/dashboard/static/materialize-stepper.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								esphomeyaml/dashboard/static/materialize-stepper.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										840
									
								
								esphomeyaml/dashboard/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										840
									
								
								esphomeyaml/dashboard/templates/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,840 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <title>esphomeyaml Dashboard</title>
 | 
			
		||||
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
 | 
			
		||||
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
 | 
			
		||||
 | 
			
		||||
  <link rel="stylesheet" href="/static/materialize-stepper.min.css">
 | 
			
		||||
 | 
			
		||||
  <!-- jQuery :( -->
 | 
			
		||||
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
 | 
			
		||||
  <script src="https://code.jquery.com/ui/1.8.5/jquery-ui.min.js" integrity="sha256-fOse6WapxTrUSJOJICXXYwHRJOPa6C1OUQXi7C9Ddy8=" crossorigin="anonymous"></script>
 | 
			
		||||
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
 | 
			
		||||
  <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/jquery.validate.min.js"></script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  <script src="/static/materialize-stepper.min.js"></script>
 | 
			
		||||
 | 
			
		||||
  <style>
 | 
			
		||||
    nav .brand-logo {
 | 
			
		||||
      margin-left: 48px;
 | 
			
		||||
      font-size: 20px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    main .container {
 | 
			
		||||
      margin-top: -12vh;
 | 
			
		||||
      flex-shrink: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .ribbon {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 17vh;
 | 
			
		||||
      background-color: #3F51B5;
 | 
			
		||||
      flex-shrink: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .ribbon-fab:not(.tap-target-origin) {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      right: 24px;
 | 
			
		||||
      top: calc(17vh + 34px);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i.very-large {
 | 
			
		||||
      font-size: 8rem;
 | 
			
		||||
      padding-top: 2px;
 | 
			
		||||
      color: #424242;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .card .card-content {
 | 
			
		||||
      padding-left: 18px;
 | 
			
		||||
      padding-bottom: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .chip {
 | 
			
		||||
      height: 26px;
 | 
			
		||||
      font-size: 12px;
 | 
			
		||||
      line-height: 26px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log {
 | 
			
		||||
      background-color: #1c1c1c;
 | 
			
		||||
      margin-top: 0;
 | 
			
		||||
      margin-bottom: 0;
 | 
			
		||||
      font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
 | 
			
		||||
      font-size: 12px;
 | 
			
		||||
      padding: 16px;
 | 
			
		||||
      overflow: auto;
 | 
			
		||||
      line-height: 1.45;
 | 
			
		||||
      border-radius: 3px;
 | 
			
		||||
      white-space: pre-wrap;
 | 
			
		||||
      overflow-wrap: break-word;
 | 
			
		||||
      color: #DDD;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .inlinecode {
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      padding: 0.2em 0.4em;
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      font-size: 85%;
 | 
			
		||||
      background-color: rgba(27,31,35,0.05);
 | 
			
		||||
      border-radius: 3px;
 | 
			
		||||
      font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log.bold {
 | 
			
		||||
      font-weight: bold;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .v {
 | 
			
		||||
      color: #888888;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .d {
 | 
			
		||||
      color: #00DDDD;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .c {
 | 
			
		||||
      color: magenta;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .i {
 | 
			
		||||
      color: limegreen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .w {
 | 
			
		||||
      color: yellow;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .e {
 | 
			
		||||
      color: red;
 | 
			
		||||
      font-weight: bold;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .e {
 | 
			
		||||
      color: red;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .log .ww {
 | 
			
		||||
      color: white;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .modal {
 | 
			
		||||
      width: 95%;
 | 
			
		||||
      max-height: 90%;
 | 
			
		||||
      height: 85% !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .page-footer {
 | 
			
		||||
      padding-top: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body {
 | 
			
		||||
      display: flex;
 | 
			
		||||
      min-height: 100vh;
 | 
			
		||||
      flex-direction: column;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    main {
 | 
			
		||||
      flex: 1 0 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ul.browser-default {
 | 
			
		||||
      padding-left: 30px;
 | 
			
		||||
      margin-top: 10px;
 | 
			
		||||
      margin-bottom: 15px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ul.browser-default li {
 | 
			
		||||
      list-style-type: initial;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .step.done::before, ul.stepper.horizontal .step.active .step-title::before, ul.stepper.horizontal .step.done .step-title::before {
 | 
			
		||||
      background-color: #3f51b5 !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .select-port-container {
 | 
			
		||||
      margin-top: 8px;
 | 
			
		||||
      margin-right: 24px;
 | 
			
		||||
      width: 350px;
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
<header>
 | 
			
		||||
<nav>
 | 
			
		||||
  <div class="nav-wrapper indigo">
 | 
			
		||||
    <a href="#" class="brand-logo left">esphomeyaml Dashboard</a>
 | 
			
		||||
    <div class="select-port-container right" id="select-port-target">
 | 
			
		||||
      <select></select>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<div class="tap-target pink lighten-1 select-port" data-target="select-port-target">
 | 
			
		||||
  <div class="tap-target-content">
 | 
			
		||||
    <h5>Select Upload Port</h5>
 | 
			
		||||
    <p>
 | 
			
		||||
      Here you can select where esphomeyaml will attempt to show logs and upload firmwares to.
 | 
			
		||||
      By default, this is "OTA", or Over-The-Air. Note that you might have to restart the HassIO add-on
 | 
			
		||||
      for new serial ports to be detected.
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="ribbon"></div>
 | 
			
		||||
</header>
 | 
			
		||||
 | 
			
		||||
<main>
 | 
			
		||||
<div class="container">
 | 
			
		||||
  {% for file, full_path in zip(files, full_path_files) %}
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col s8 offset-s2 m10 offset-m1 l12">
 | 
			
		||||
      <div class="card horizontal">
 | 
			
		||||
        <div class="card-image center-align">
 | 
			
		||||
          <i class="material-icons very-large icon-grey">memory</i>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="card-stacked">
 | 
			
		||||
          <div class="card-content">
 | 
			
		||||
            <span class="card-title">{{ escape(file) }}</span>
 | 
			
		||||
            <p>
 | 
			
		||||
              Full path: <code class="inlinecode">{{ escape(full_path) }}</code>
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-action">
 | 
			
		||||
            <a href="#" class="action-upload" data-node="{{ file }}">Upload</a>
 | 
			
		||||
            <a href="#" class="action-compile" data-node="{{ file }}">Compile</a>
 | 
			
		||||
            <a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a>
 | 
			
		||||
            <a href="#" class="action-validate" data-node="{{ file }}">Validate</a>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  {% end %}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="modal-logs" class="modal modal-fixed-footer">
 | 
			
		||||
  <div class="modal-content">
 | 
			
		||||
    <h4>Show Logs <code class="inlinecode filename"></code></h4>
 | 
			
		||||
    <div class="log-container">
 | 
			
		||||
      <pre class="log"></pre>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <a class="modal-close waves-effect waves-green btn-flat stop-logs">Close</a>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="modal-upload" class="modal modal-fixed-footer">
 | 
			
		||||
  <div class="modal-content">
 | 
			
		||||
    <h4>Compile And Upload <code class="inlinecode filename"></code></h4>
 | 
			
		||||
    <div class="log-container">
 | 
			
		||||
      <pre class="log"></pre>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="modal-compile" class="modal modal-fixed-footer">
 | 
			
		||||
  <div class="modal-content">
 | 
			
		||||
    <h4>Compile <code class="inlinecode filename"></code></h4>
 | 
			
		||||
    <div class="log-container">
 | 
			
		||||
      <pre class="log"></pre>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <a class="modal-close waves-effect waves-green btn-flat disabled download-binary">Download Binary</a>
 | 
			
		||||
    <a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="modal-validate" class="modal modal-fixed-footer">
 | 
			
		||||
  <div class="modal-content">
 | 
			
		||||
    <h4>Validate <code class="inlinecode filename"></code></h4>
 | 
			
		||||
    <div class="log-container">
 | 
			
		||||
      <pre class="log"></pre>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="modal-wizard" class="modal">
 | 
			
		||||
  <div class="modal-content">
 | 
			
		||||
    <form action="/wizard.html" method="POST">
 | 
			
		||||
    <ul class="stepper linear">
 | 
			
		||||
      <li class="step active">
 | 
			
		||||
        <div class="step-title waves-effect">Introduction And Name</div>
 | 
			
		||||
        <div class="step-content">
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <p>
 | 
			
		||||
              Hi there! I'm the esphomeyaml setup wizard and will guide you through setting up
 | 
			
		||||
              your first ESP8266 or ESP32-powered device using esphomeyaml.
 | 
			
		||||
            </p>
 | 
			
		||||
              <a href="https://www.espressif.com/en/products/hardware/esp8266ex/overview" target="_blank">ESP8266s</a> and
 | 
			
		||||
              their successors (the <a href="https://www.espressif.com/en/products/hardware/esp32/overview" target="_blank">ESP32s</a>)
 | 
			
		||||
              are great low-cost microcontrollers that can communicate with the outside world using WiFi.
 | 
			
		||||
              They're found in many devices such as the popular Sonoff/iTead, but also exist as development boards
 | 
			
		||||
              such as the <a href="https://esphomelib.com/esphomeyaml/devices/nodemcu_esp8266.html" target="_blank">NodeMCU</a>.
 | 
			
		||||
            <p>
 | 
			
		||||
            </p>
 | 
			
		||||
              <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml</a>,
 | 
			
		||||
              the tool you're using here, creates custom firmwares for these devices using YAML configuration
 | 
			
		||||
              files (similar to the ones you might be used to with Home Assistant).
 | 
			
		||||
            <p>
 | 
			
		||||
            </p>
 | 
			
		||||
              This wizard will create a basic YAML configuration file for your "node" (the microcontroller).
 | 
			
		||||
              Later, you will be able to customize this file and add some of
 | 
			
		||||
              <a href="https://github.com/OttoWinter/esphomelib" target="_blank">esphomelib's</a>
 | 
			
		||||
              many integrations.
 | 
			
		||||
            <p>
 | 
			
		||||
            <p>
 | 
			
		||||
              First, I need to know what this node should be called. Choose this name wisely, changing this
 | 
			
		||||
              later makes Over-The-Air Update attempts difficult.
 | 
			
		||||
              It may only contain the characters <code class="inlinecode">a-z</code>,
 | 
			
		||||
              <code class="inlinecode">0-9</code> and <code class="inlinecode">_</code>
 | 
			
		||||
            </p>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <input id="node_name" class="validate" type="text" name="name" required>
 | 
			
		||||
              <label for="node_name">Name of node</label>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="step-actions">
 | 
			
		||||
            <button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li class="step">
 | 
			
		||||
        <div class="step-title waves-effect">Device Type</div>
 | 
			
		||||
        <div class="step-content">
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <p>
 | 
			
		||||
              Great! Now I need to know what type of microcontroller you're using so that I can compile firmware for them.
 | 
			
		||||
              Please choose either ESP32 or ESP8266 (use ESP8266 for Sonoff devices).
 | 
			
		||||
            </p>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <select id="esp_type" name="platform" required>
 | 
			
		||||
                <option value="ESP8266">ESP8266</option>
 | 
			
		||||
                <option value="ESP32">ESP32</option>
 | 
			
		||||
              </select>
 | 
			
		||||
              <label>Microcontroller Type</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p>
 | 
			
		||||
              I'm also going to need to know which type of board you're using. Please go to
 | 
			
		||||
              <a href="http://docs.platformio.org/en/latest/platforms/espressif32.html#boards" target="_blank">ESP32 boards</a> or
 | 
			
		||||
              <a href="http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards" target="_blank">ESP8266 boards</a>,
 | 
			
		||||
              find your board and enter it here. For example, enter <code class="inlinecode">nodemcuv2</code>
 | 
			
		||||
              for ESP8266 NodeMCU boards. Note: Use <code class="inlinecode">esp01_1m</code> for Sonoff devices.
 | 
			
		||||
            </p>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <input id="board_type" class="validate" type="text" name="board" required>
 | 
			
		||||
              <label for="board_type">Board Type</label>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="step-actions">
 | 
			
		||||
            <button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li class="step">
 | 
			
		||||
        <div class="step-title waves-effect">WiFi And Over-The-Air Updates</div>
 | 
			
		||||
        <div class="step-content">
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <p>
 | 
			
		||||
              Thanks! Now I need to know what WiFi Access Point I should instruct the node to connect to.
 | 
			
		||||
              Please enter an SSID (name of the WiFi network) and password (leave empty for no password).
 | 
			
		||||
            </p>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <input id="wifi_ssid" class="validate" type="text" name="ssid" required>
 | 
			
		||||
              <label for="wifi_ssid">WiFi SSID</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <input id="wifi_password" name="psk" type="password">
 | 
			
		||||
              <label for="wifi_password">WiFi Password</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p>
 | 
			
		||||
              Esphomelib automatically sets up an Over-The-Air update server on the node
 | 
			
		||||
              so that you only need to flash a firmware via USB once.
 | 
			
		||||
              Optionally, you can set a password for this upload process here:
 | 
			
		||||
            </p>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <input id="ota_password" class="validate" name="ota_password" type="password">
 | 
			
		||||
              <label for="ota_password">OTA Password</label>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="step-actions">
 | 
			
		||||
            <button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li class="step">
 | 
			
		||||
        <div class="step-title waves-effect">MQTT</div>
 | 
			
		||||
        <div class="step-content">
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <p>
 | 
			
		||||
              esphomelib connects to your Home Assistant instance via
 | 
			
		||||
              <a href="https://www.home-assistant.io/docs/mqtt/">MQTT</a>. If you haven't already, please set up
 | 
			
		||||
              MQTT on your Home Assistant server, for example with the awesome
 | 
			
		||||
              <a href="https://www.home-assistant.io/addons/mosquitto/">Mosquitto Hass.io Add-on</a>.
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
              When you're done with that, please enter your MQTT broker here. For example
 | 
			
		||||
              <code class="inlinecode">192.168.1.100</code> (Note
 | 
			
		||||
              <code class="inlinecode">hassio.local</code> doesn't always work, please use a static IP).
 | 
			
		||||
              Please also specify the MQTT username and password you wish esphomelib to use
 | 
			
		||||
              (leave them empty if you're not using any authentication).
 | 
			
		||||
            </p>
 | 
			
		||||
            <div class="input-field col s12">
 | 
			
		||||
              <input id="mqtt_broker" class="validate" type="text" name="broker" required>
 | 
			
		||||
              <label for="mqtt_broker">MQTT Broker</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="input-field col s6">
 | 
			
		||||
              <input id="mqtt_username" class="validate" type="text" name="mqtt_username">
 | 
			
		||||
              <label for="mqtt_username">MQTT Username</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="input-field col s6">
 | 
			
		||||
              <input id="mqtt_password" class="validate" name="mqtt_password" type="password">
 | 
			
		||||
              <label for="mqtt_password">MQTT Password</label>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="step-actions">
 | 
			
		||||
            <button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li class="step">
 | 
			
		||||
        <div class="step-title waves-effect">Done!</div>
 | 
			
		||||
        <div class="step-content">
 | 
			
		||||
          <p>
 | 
			
		||||
            Hooray! 🎉🎉🎉 You've successfully created your first esphomeyaml configuration file.
 | 
			
		||||
            When you click Submit, I will save this configuration file under
 | 
			
		||||
            <code class="inlinecode"><HASS_CONFIG_FOLDER>/esphomeyaml/<NAME_OF_NODE>.yaml</code> and
 | 
			
		||||
            you will be able to edit this file with the
 | 
			
		||||
            <a href="https://www.home-assistant.io/addons/configurator/" target="_blank">HASS Configuratior add-on</a>.
 | 
			
		||||
          </p>
 | 
			
		||||
          <h5>Next steps</h5>
 | 
			
		||||
          <ul class="browser-default">
 | 
			
		||||
            <li>
 | 
			
		||||
              Flash the firmware. This can be done using the “UPLOAD” option in the dashboard. See
 | 
			
		||||
              <a href="https://esphomelib.com/esphomeyaml/index.html#devices" target="_blank">this</a>
 | 
			
		||||
              for guides on how to flash different types of devices. Note that you need to restart this add-on
 | 
			
		||||
              for newly plugged in serial devices to be detected.
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
              With the current configuration, your node will only connect to WiFi and MQTT. To make it actually <i>do</i>
 | 
			
		||||
              stuff, follow
 | 
			
		||||
              <a href="https://esphomelib.com/esphomeyaml/guides/getting_started_hassio.html#adding-some-basic-features">
 | 
			
		||||
                the rest of the getting started guide
 | 
			
		||||
              </a>.
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
              See the <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml index</a>
 | 
			
		||||
              for a list of supported sensors/devices.
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
              Join the <a href="https://discord.gg/KhAMKrd" target="_blank">Discord server</a> and say hi! When I
 | 
			
		||||
              have time, I would be happy to help with issues and discuss new features.
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
              Star <a href="https://github.com/OttoWinter/esphomelib" target="_blank">esphomelib</a> and
 | 
			
		||||
              <a href="https://github.com/OttoWinter/esphomeyaml" target="_blank">esphomeyaml</a> on GitHub
 | 
			
		||||
              if you find this software awesome and report issues using the bug trackers there.
 | 
			
		||||
            </li>
 | 
			
		||||
          </ul>
 | 
			
		||||
          <div class="step-actions">
 | 
			
		||||
            <button class="waves-effect waves-dark btn indigo" type="submit">SUBMIT</button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    </form>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <a href="#!" class="modal-close waves-effect waves-green btn-flat">Abort</a>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<a class="btn-floating btn-large ribbon-fab waves-effect waves-light pink accent-2" id="setup-wizard-start">
 | 
			
		||||
  <i class="material-icons">add</i>
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
<div class="tap-target pink lighten-1 setup-wizard" data-target="setup-wizard-start">
 | 
			
		||||
  <div class="tap-target-content">
 | 
			
		||||
    <h5>Set up your first Node</h5>
 | 
			
		||||
    <p>
 | 
			
		||||
      Huh... It seems like you you don't have any esphomeyaml configuration files yet...
 | 
			
		||||
      Fortunately, there's a setup wizard that will step you through setting up your first node 🎉
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
</main>
 | 
			
		||||
 | 
			
		||||
<footer class="page-footer indigo darken-1">
 | 
			
		||||
  <div class="container">
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="footer-copyright">
 | 
			
		||||
    <div class="container">
 | 
			
		||||
      © 2018 Copyright Otto Winter, Made with <a class="grey-text text-lighten-4" href="https://materializecss.com/" target="_blank">Materialize</a>
 | 
			
		||||
      <a class="grey-text text-lighten-4 right" href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml {{ version }} Documentation</a>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    M.AutoInit(document.body);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const colorReplace = (input) => {
 | 
			
		||||
    input = input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?31m/g, '<span class="e">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?31m/g, '<span class="e bold">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?32m/g, '<span class="i">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?32m/g, '<span class="i bold">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?33m/g, '<span class="w">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?33m/g, '<span class="w bold">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?35m/g, '<span class="c">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?35m/g, '<span class="c bold">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?36m/g, '<span class="d">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?36m/g, '<span class="d bold">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?37m/g, '<span class="v">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?37m/g, '<span class="v bold">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:0;)?38m/g, '<span class="vv">');
 | 
			
		||||
    input = input.replace(/\\033\[(?:1;)?38m/g, '<span class="vv bold">');
 | 
			
		||||
    input = input.replace(/\\033\[0m/g, '</span>');
 | 
			
		||||
 | 
			
		||||
    return input;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let configuration = "";
 | 
			
		||||
  let wsProtocol = "ws:";
 | 
			
		||||
  if (window.location.protocol === "https:") {
 | 
			
		||||
    wsProtocol = 'wss:';
 | 
			
		||||
  }
 | 
			
		||||
  const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port;
 | 
			
		||||
 | 
			
		||||
  const portSelect = document.querySelector('.nav-wrapper select');
 | 
			
		||||
  let ports = [];
 | 
			
		||||
 | 
			
		||||
  const fetchSerialPorts = (begin=false) => {
 | 
			
		||||
    fetch('/serial-ports').then(res => res.json())
 | 
			
		||||
      .then(response => {
 | 
			
		||||
        if (ports.length === response.length) {
 | 
			
		||||
          let allEqual = true;
 | 
			
		||||
          for (let i = 0; i < response.length; i++) {
 | 
			
		||||
            if (ports[i].port !== response[i].port) {
 | 
			
		||||
              allEqual = false;
 | 
			
		||||
              break;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          if (allEqual)
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ports = response;
 | 
			
		||||
 | 
			
		||||
        const inst = M.FormSelect.getInstance(portSelect);
 | 
			
		||||
        if (inst !== undefined) {
 | 
			
		||||
          inst.destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        portSelect.innerHTML = "";
 | 
			
		||||
        const prevSelected = getUploadPort();
 | 
			
		||||
        for (let i = 0; i < response.length; i++) {
 | 
			
		||||
          const val = response[i];
 | 
			
		||||
          if (val.port === prevSelected) {
 | 
			
		||||
            portSelect.innerHTML += `<option value="${val.port}" selected>${val.port} (${val.desc})</option>`;
 | 
			
		||||
          } else {
 | 
			
		||||
            portSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        M.FormSelect.init(portSelect, {});
 | 
			
		||||
        if (!begin)
 | 
			
		||||
          M.toast({html: "Discovered new serial port."});
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getUploadPort = () => {
 | 
			
		||||
    const inst = M.FormSelect.getInstance(portSelect);
 | 
			
		||||
    if (inst === undefined) {
 | 
			
		||||
      return "OTA";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inst._setSelectedStates();
 | 
			
		||||
    return inst.getSelectedValues()[0];
 | 
			
		||||
  };
 | 
			
		||||
  setInterval(fetchSerialPorts, 2500);
 | 
			
		||||
  fetchSerialPorts(true);
 | 
			
		||||
 | 
			
		||||
  const logsModalElem = document.getElementById("modal-logs");
 | 
			
		||||
 | 
			
		||||
  document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
 | 
			
		||||
    showLogs.addEventListener('click', (e) => {
 | 
			
		||||
      configuration = e.target.getAttribute('data-node');
 | 
			
		||||
      const modalInstance = M.Modal.getInstance(logsModalElem);
 | 
			
		||||
      const log = logsModalElem.querySelector(".log");
 | 
			
		||||
      log.innerHTML = "";
 | 
			
		||||
      const stopLogsButton = logsModalElem.querySelector(".stop-logs");
 | 
			
		||||
      let stopped = false;
 | 
			
		||||
      stopLogsButton.innerHTML = "Stop";
 | 
			
		||||
      modalInstance.open();
 | 
			
		||||
 | 
			
		||||
      const filenameField = logsModalElem.querySelector('.filename');
 | 
			
		||||
      filenameField.innerHTML = configuration;
 | 
			
		||||
 | 
			
		||||
      const logSocket = new WebSocket(wsUrl + "/logs");
 | 
			
		||||
      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."});
 | 
			
		||||
          } else {
 | 
			
		||||
            M.toast({html: `Program failed with code ${data.code}`});
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          stopLogsButton.innerHTML = "Close";
 | 
			
		||||
          stopped = true;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      logSocket.addEventListener('open', () => {
 | 
			
		||||
        const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
 | 
			
		||||
        logSocket.send(msg);
 | 
			
		||||
      });
 | 
			
		||||
      logSocket.addEventListener('close', () => {
 | 
			
		||||
        if (!stopped) {
 | 
			
		||||
          M.toast({html: 'Terminated process.'});
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      modalInstance.options.onCloseStart = () => {
 | 
			
		||||
        logSocket.close();
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const uploadModalElem = document.getElementById("modal-upload");
 | 
			
		||||
 | 
			
		||||
  document.querySelectorAll(".action-upload").forEach((upload) => {
 | 
			
		||||
    upload.addEventListener('click', (e) => {
 | 
			
		||||
      configuration = e.target.getAttribute('data-node');
 | 
			
		||||
      const modalInstance = M.Modal.getInstance(uploadModalElem);
 | 
			
		||||
      const log = uploadModalElem.querySelector(".log");
 | 
			
		||||
      log.innerHTML = "";
 | 
			
		||||
      const stopLogsButton = uploadModalElem.querySelector(".stop-logs");
 | 
			
		||||
      let stopped = false;
 | 
			
		||||
      stopLogsButton.innerHTML = "Stop";
 | 
			
		||||
      modalInstance.open();
 | 
			
		||||
 | 
			
		||||
      const filenameField = uploadModalElem.querySelector('.filename');
 | 
			
		||||
      filenameField.innerHTML = configuration;
 | 
			
		||||
 | 
			
		||||
      const logSocket = new WebSocket(wsUrl + "/run");
 | 
			
		||||
      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."});
 | 
			
		||||
          } else {
 | 
			
		||||
            M.toast({html: `Program failed with code ${data.code}`});
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          stopLogsButton.innerHTML = "Close";
 | 
			
		||||
          stopped = true;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      logSocket.addEventListener('open', () => {
 | 
			
		||||
        const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
 | 
			
		||||
        logSocket.send(msg);
 | 
			
		||||
      });
 | 
			
		||||
      logSocket.addEventListener('close', () => {
 | 
			
		||||
        if (!stopped) {
 | 
			
		||||
          M.toast({html: 'Terminated process.'});
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      modalInstance.options.onCloseStart = () => {
 | 
			
		||||
        logSocket.close();
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const validateModalElem = document.getElementById("modal-validate");
 | 
			
		||||
 | 
			
		||||
  document.querySelectorAll(".action-validate").forEach((upload) => {
 | 
			
		||||
    upload.addEventListener('click', (e) => {
 | 
			
		||||
      configuration = e.target.getAttribute('data-node');
 | 
			
		||||
      const modalInstance = M.Modal.getInstance(validateModalElem);
 | 
			
		||||
      const log = validateModalElem.querySelector(".log");
 | 
			
		||||
      log.innerHTML = "";
 | 
			
		||||
      const stopLogsButton = validateModalElem.querySelector(".stop-logs");
 | 
			
		||||
      let stopped = false;
 | 
			
		||||
      stopLogsButton.innerHTML = "Stop";
 | 
			
		||||
      modalInstance.open();
 | 
			
		||||
 | 
			
		||||
      const filenameField = validateModalElem.querySelector('.filename');
 | 
			
		||||
      filenameField.innerHTML = configuration;
 | 
			
		||||
 | 
			
		||||
      const logSocket = new WebSocket(wsUrl + "/validate");
 | 
			
		||||
      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: `<code class="inlinecode">${configuration}</code> is valid 👍`,
 | 
			
		||||
              displayLength: 5000,
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            M.toast({
 | 
			
		||||
              html: `<code class="inlinecode">${configuration}</code> is invalid 😕`,
 | 
			
		||||
              displayLength: 5000,
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          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 compileModalElem = document.getElementById("modal-compile");
 | 
			
		||||
  const downloadButton = compileModalElem.querySelector('.download-binary');
 | 
			
		||||
 | 
			
		||||
  document.querySelectorAll(".action-compile").forEach((upload) => {
 | 
			
		||||
    upload.addEventListener('click', (e) => {
 | 
			
		||||
      configuration = e.target.getAttribute('data-node');
 | 
			
		||||
      const modalInstance = M.Modal.getInstance(compileModalElem);
 | 
			
		||||
      const log = compileModalElem.querySelector(".log");
 | 
			
		||||
      log.innerHTML = "";
 | 
			
		||||
      const stopLogsButton = compileModalElem.querySelector(".stop-logs");
 | 
			
		||||
      let stopped = false;
 | 
			
		||||
      stopLogsButton.innerHTML = "Stop";
 | 
			
		||||
      downloadButton.classList.add('disabled');
 | 
			
		||||
 | 
			
		||||
      modalInstance.open();
 | 
			
		||||
 | 
			
		||||
      const filenameField = compileModalElem.querySelector('.filename');
 | 
			
		||||
      filenameField.innerHTML = configuration;
 | 
			
		||||
 | 
			
		||||
      const logSocket = new WebSocket(wsUrl + "/compile");
 | 
			
		||||
      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();
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  downloadButton.addEventListener('click', () => {
 | 
			
		||||
    const link = document.createElement("a");
 | 
			
		||||
    link.download = name;
 | 
			
		||||
    link.href = '/download.bin?configuration=' + encodeURIComponent(configuration);
 | 
			
		||||
    link.click();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const modalSetupElem = document.getElementById("modal-wizard");
 | 
			
		||||
  const setupWizardStart = document.getElementById('setup-wizard-start');
 | 
			
		||||
  const startWizard = () => {
 | 
			
		||||
    const modalInstance = M.Modal.getInstance(modalSetupElem);
 | 
			
		||||
    modalInstance.open();
 | 
			
		||||
 | 
			
		||||
    modalInstance.options.onCloseStart = () => {
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $('.stepper').activateStepper({
 | 
			
		||||
      linearStepsNavigation: false,
 | 
			
		||||
      autoFocusInput: true,
 | 
			
		||||
      autoFormCreation: true,
 | 
			
		||||
      showFeedbackLoader: true,
 | 
			
		||||
      parallel: false
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  setupWizardStart.addEventListener('click', startWizard);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{% if len(files) == 0 %}
 | 
			
		||||
<script>
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    const tapTargetElem = document.querySelector('.tap-target.setup-wizard');
 | 
			
		||||
    const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
 | 
			
		||||
    tapTargetInstance.options.onOpen = () => {
 | 
			
		||||
      $('.tap-target-origin').on('click', () => {
 | 
			
		||||
        startWizard();
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
    tapTargetInstance.open();
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
{% end %}
 | 
			
		||||
 | 
			
		||||
{% if begin %}
 | 
			
		||||
<script>
 | 
			
		||||
  window.history.replaceState({}, document.title, "/");
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    const tapTargetElem = document.querySelector('.tap-target.select-port');
 | 
			
		||||
    const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
 | 
			
		||||
    tapTargetInstance.open();
 | 
			
		||||
 | 
			
		||||
    tapTargetInstance.contentEl.style["top"] = "300px";
 | 
			
		||||
    tapTargetInstance.contentEl.style["padding"] = "250px";
 | 
			
		||||
    tapTargetInstance.waveEl.style["top"] = "250px";
 | 
			
		||||
    tapTargetInstance.waveEl.style["left"] = "250px";
 | 
			
		||||
    tapTargetInstance.waveEl.style["width"] = "300px";
 | 
			
		||||
    tapTargetInstance.waveEl.style["height"] = "300px";
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
{% end %}
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import inspect
 | 
			
		||||
import logging
 | 
			
		||||
import re
 | 
			
		||||
from collections import OrderedDict, deque
 | 
			
		||||
@@ -7,9 +8,10 @@ from collections import OrderedDict, deque
 | 
			
		||||
from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \
 | 
			
		||||
    CONF_INVERTED, \
 | 
			
		||||
    CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, \
 | 
			
		||||
    CONF_STATE_TOPIC, CONF_TOPIC
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt
 | 
			
		||||
    CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PCF8574, \
 | 
			
		||||
    CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, Lambda, TimePeriodMicroseconds, \
 | 
			
		||||
    TimePeriodMilliseconds, TimePeriodSeconds
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -57,6 +59,9 @@ class Expression(object):
 | 
			
		||||
                continue
 | 
			
		||||
            require.require()
 | 
			
		||||
 | 
			
		||||
    def has_side_effects(self):
 | 
			
		||||
        return self.required
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RawExpression(Expression):
 | 
			
		||||
    def __init__(self, text):
 | 
			
		||||
@@ -64,7 +69,7 @@ class RawExpression(Expression):
 | 
			
		||||
        self.text = text
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.text
 | 
			
		||||
        return str(self.text)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# pylint: disable=redefined-builtin
 | 
			
		||||
@@ -84,6 +89,9 @@ class AssignmentExpression(Expression):
 | 
			
		||||
            type_ = u'auto'
 | 
			
		||||
        return u"{} {}{} = {}".format(type_, self.modifier, self.name, self.rhs)
 | 
			
		||||
 | 
			
		||||
    def has_side_effects(self):
 | 
			
		||||
        return self.rhs.has_side_effects()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExpressionList(Expression):
 | 
			
		||||
    def __init__(self, *args):
 | 
			
		||||
@@ -99,18 +107,36 @@ class ExpressionList(Expression):
 | 
			
		||||
            self.args.append(exp)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        text = u", ".join(unicode(x) for x in self.args)
 | 
			
		||||
        text = u", ".join(str(x) for x in self.args)
 | 
			
		||||
        return indent_all_but_first_and_last(text)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TemplateArguments(Expression):
 | 
			
		||||
    def __init__(self, *args):
 | 
			
		||||
        super(TemplateArguments, self).__init__()
 | 
			
		||||
        self.args = ExpressionList(*args)
 | 
			
		||||
        self.requires.append(self.args)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return u'<{}>'.format(self.args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CallExpression(Expression):
 | 
			
		||||
    def __init__(self, base, *args):
 | 
			
		||||
        super(CallExpression, self).__init__()
 | 
			
		||||
        self.base = base
 | 
			
		||||
        if args and isinstance(args[0], TemplateArguments):
 | 
			
		||||
            self.template_args = args[0]
 | 
			
		||||
            self.requires.append(self.template_args)
 | 
			
		||||
            args = args[1:]
 | 
			
		||||
        else:
 | 
			
		||||
            self.template_args = None
 | 
			
		||||
        self.args = ExpressionList(*args)
 | 
			
		||||
        self.requires.append(self.args)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        if self.template_args is not None:
 | 
			
		||||
            return u'{}{}({})'.format(self.base, self.template_args, self.args)
 | 
			
		||||
        return u'{}({})'.format(self.base, self.args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -118,6 +144,8 @@ class StructInitializer(Expression):
 | 
			
		||||
    def __init__(self, base, *args):
 | 
			
		||||
        super(StructInitializer, self).__init__()
 | 
			
		||||
        self.base = base
 | 
			
		||||
        if isinstance(base, Expression):
 | 
			
		||||
            self.requires.append(base)
 | 
			
		||||
        if not isinstance(args, OrderedDict):
 | 
			
		||||
            args = OrderedDict(args)
 | 
			
		||||
        self.args = OrderedDict()
 | 
			
		||||
@@ -161,18 +189,82 @@ class ArrayInitializer(Expression):
 | 
			
		||||
        return cpp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
class ParameterExpression(Expression):
 | 
			
		||||
    def __init__(self, type, id):
 | 
			
		||||
        super(ParameterExpression, self).__init__()
 | 
			
		||||
        self.type = type
 | 
			
		||||
        self.id = id
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return u"{} {}".format(self.type, self.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ParameterListExpression(Expression):
 | 
			
		||||
    def __init__(self, *parameters):
 | 
			
		||||
        super(ParameterListExpression, self).__init__()
 | 
			
		||||
        self.parameters = []
 | 
			
		||||
        for parameter in parameters:
 | 
			
		||||
            if not isinstance(parameter, ParameterExpression):
 | 
			
		||||
                parameter = ParameterExpression(*parameter)
 | 
			
		||||
            self.parameters.append(parameter)
 | 
			
		||||
            self.requires.append(parameter)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return u", ".join(unicode(x) for x in self.parameters)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LambdaExpression(Expression):
 | 
			
		||||
    def __init__(self, parts, parameters, capture='=', return_type=None):
 | 
			
		||||
        super(LambdaExpression, self).__init__()
 | 
			
		||||
        self.parts = parts
 | 
			
		||||
        if not isinstance(parameters, ParameterListExpression):
 | 
			
		||||
            parameters = ParameterListExpression(*parameters)
 | 
			
		||||
        self.parameters = parameters
 | 
			
		||||
        self.requires.append(self.parameters)
 | 
			
		||||
        self.capture = capture
 | 
			
		||||
        self.return_type = return_type
 | 
			
		||||
        if return_type is not None:
 | 
			
		||||
            self.requires.append(return_type)
 | 
			
		||||
        for i in range(1, len(parts), 2):
 | 
			
		||||
            self.requires.append(parts[i])
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        cpp = u'[{}]({})'.format(self.capture, self.parameters)
 | 
			
		||||
        if self.return_type is not None:
 | 
			
		||||
            cpp += u' -> {}'.format(self.return_type)
 | 
			
		||||
        cpp += u' {\n'
 | 
			
		||||
        for part in self.parts:
 | 
			
		||||
            cpp += unicode(part)
 | 
			
		||||
        cpp += u'\n}'
 | 
			
		||||
        return indent_all_but_first_and_last(cpp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Literal(Expression):
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# From https://stackoverflow.com/a/14945195/8924614
 | 
			
		||||
def cpp_string_escape(string, encoding='utf-8'):
 | 
			
		||||
    if isinstance(string, unicode):
 | 
			
		||||
        string = string.encode(encoding)
 | 
			
		||||
    result = ''
 | 
			
		||||
    for character in string:
 | 
			
		||||
        if not (32 <= ord(character) < 127) or character in ('\\', '"'):
 | 
			
		||||
            result += '\\%03o' % ord(character)
 | 
			
		||||
        else:
 | 
			
		||||
            result += character
 | 
			
		||||
    return '"' + result + '"'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StringLiteral(Literal):
 | 
			
		||||
    def __init__(self, string):
 | 
			
		||||
        super(StringLiteral, self).__init__()
 | 
			
		||||
        self.string = string
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return u'"{}"'.format(self.string)
 | 
			
		||||
        return u'{}'.format(cpp_string_escape(self.string))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IntLiteral(Literal):
 | 
			
		||||
@@ -203,9 +295,9 @@ class HexIntLiteral(Literal):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FloatLiteral(Literal):
 | 
			
		||||
    def __init__(self, float_):
 | 
			
		||||
    def __init__(self, value):
 | 
			
		||||
        super(FloatLiteral, self).__init__()
 | 
			
		||||
        self.float_ = float_
 | 
			
		||||
        self.float_ = value
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return u"{:f}f".format(self.float_)
 | 
			
		||||
@@ -218,10 +310,18 @@ def safe_exp(obj):
 | 
			
		||||
        return BoolLiteral(obj)
 | 
			
		||||
    elif isinstance(obj, (str, unicode)):
 | 
			
		||||
        return StringLiteral(obj)
 | 
			
		||||
    elif isinstance(obj, HexInt):
 | 
			
		||||
        return HexIntLiteral(obj)
 | 
			
		||||
    elif isinstance(obj, (int, long)):
 | 
			
		||||
        return IntLiteral(obj)
 | 
			
		||||
    elif isinstance(obj, float):
 | 
			
		||||
        return FloatLiteral(obj)
 | 
			
		||||
    elif isinstance(obj, TimePeriodMicroseconds):
 | 
			
		||||
        return IntLiteral(int(obj.total_microseconds))
 | 
			
		||||
    elif isinstance(obj, TimePeriodMilliseconds):
 | 
			
		||||
        return IntLiteral(int(obj.total_milliseconds))
 | 
			
		||||
    elif isinstance(obj, TimePeriodSeconds):
 | 
			
		||||
        return IntLiteral(int(obj.total_seconds))
 | 
			
		||||
    raise ValueError(u"Object is not an expression", obj)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -257,82 +357,139 @@ def statement(expression):
 | 
			
		||||
    return ExpressionStatement(expression)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_variable(id, obj):
 | 
			
		||||
    _LOGGER.debug("Registered variable %s of type %s", id.id, id.type)
 | 
			
		||||
    _VARIABLES[id] = obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# pylint: disable=redefined-builtin, invalid-name
 | 
			
		||||
def variable(type, id, rhs):
 | 
			
		||||
def variable(id, rhs, type=None):
 | 
			
		||||
    rhs = safe_exp(rhs)
 | 
			
		||||
    obj = MockObj(id, u'.')
 | 
			
		||||
    assignment = AssignmentExpression(type, '', id, rhs, obj)
 | 
			
		||||
    id.type = type or id.type
 | 
			
		||||
    assignment = AssignmentExpression(id.type, '', id, rhs, obj)
 | 
			
		||||
    add(assignment)
 | 
			
		||||
    _VARIABLES[id] = obj, type
 | 
			
		||||
    register_variable(id, obj)
 | 
			
		||||
    obj.requires.append(assignment)
 | 
			
		||||
    return obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def Pvariable(type, id, rhs):
 | 
			
		||||
def Pvariable(id, rhs, has_side_effects=True, type=None):
 | 
			
		||||
    rhs = safe_exp(rhs)
 | 
			
		||||
    obj = MockObj(id, u'->')
 | 
			
		||||
    assignment = AssignmentExpression(type, '*', id, rhs, obj)
 | 
			
		||||
    if not has_side_effects and hasattr(rhs, '_has_side_effects'):
 | 
			
		||||
        # pylint: disable=attribute-defined-outside-init, protected-access
 | 
			
		||||
        rhs._has_side_effects = False
 | 
			
		||||
    obj = MockObj(id, u'->', has_side_effects=has_side_effects)
 | 
			
		||||
    id.type = type or id.type
 | 
			
		||||
    assignment = AssignmentExpression(id.type, '*', id, rhs, obj)
 | 
			
		||||
    add(assignment)
 | 
			
		||||
    _VARIABLES[id] = obj, type
 | 
			
		||||
    register_variable(id, obj)
 | 
			
		||||
    obj.requires.append(assignment)
 | 
			
		||||
    return obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_QUEUE = deque()
 | 
			
		||||
_TASKS = deque()
 | 
			
		||||
_VARIABLES = {}
 | 
			
		||||
_EXPRESSIONS = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_variable(id, type=None):
 | 
			
		||||
    result = None
 | 
			
		||||
    while _QUEUE:
 | 
			
		||||
        if id is not None:
 | 
			
		||||
            if id in _VARIABLES:
 | 
			
		||||
                result = _VARIABLES[id][0]
 | 
			
		||||
                break
 | 
			
		||||
        elif type is not None:
 | 
			
		||||
            result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
 | 
			
		||||
            if result is not None:
 | 
			
		||||
                break
 | 
			
		||||
        func, config = _QUEUE.popleft()
 | 
			
		||||
        func(config)
 | 
			
		||||
    if id is None and type is None:
 | 
			
		||||
        return None
 | 
			
		||||
    if result is None:
 | 
			
		||||
        if id is not None:
 | 
			
		||||
            result = _VARIABLES[id][0]
 | 
			
		||||
        elif type is not None:
 | 
			
		||||
            result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
 | 
			
		||||
 | 
			
		||||
        if result is None:
 | 
			
		||||
            raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type))
 | 
			
		||||
    return result
 | 
			
		||||
def get_variable(id):
 | 
			
		||||
    while True:
 | 
			
		||||
        if id in _VARIABLES:
 | 
			
		||||
            yield _VARIABLES[id]
 | 
			
		||||
            return
 | 
			
		||||
        _LOGGER.debug("Waiting for variable %s", id)
 | 
			
		||||
        yield None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_task(func, config):
 | 
			
		||||
    _QUEUE.append((func, config))
 | 
			
		||||
def process_lambda(value, parameters, capture='=', return_type=None):
 | 
			
		||||
    if value is None:
 | 
			
		||||
        yield
 | 
			
		||||
        return
 | 
			
		||||
    parts = value.parts[:]
 | 
			
		||||
    for i, id in enumerate(value.requires_ids):
 | 
			
		||||
        var = None
 | 
			
		||||
        for var in get_variable(id):
 | 
			
		||||
            yield
 | 
			
		||||
        parts[i*2 + 1] = var._
 | 
			
		||||
    yield LambdaExpression(parts, parameters, capture, return_type)
 | 
			
		||||
    return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def templatable(value, input_type, output_type):
 | 
			
		||||
    if isinstance(value, Lambda):
 | 
			
		||||
        lambda_ = None
 | 
			
		||||
        for lambda_ in process_lambda(value, [(input_type, 'x')], return_type=output_type):
 | 
			
		||||
            yield None
 | 
			
		||||
        yield lambda_
 | 
			
		||||
    else:
 | 
			
		||||
        yield value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_job(func, *args, **kwargs):
 | 
			
		||||
    domain = kwargs.get('domain')
 | 
			
		||||
    if inspect.isgeneratorfunction(func):
 | 
			
		||||
        def func_():
 | 
			
		||||
            yield
 | 
			
		||||
            for _ in func(*args):
 | 
			
		||||
                yield
 | 
			
		||||
    else:
 | 
			
		||||
        def func_():
 | 
			
		||||
            yield
 | 
			
		||||
            func(*args)
 | 
			
		||||
    gen = func_()
 | 
			
		||||
    _TASKS.append((gen, domain))
 | 
			
		||||
    return gen
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def flush_tasks():
 | 
			
		||||
    i = 0
 | 
			
		||||
    while _TASKS:
 | 
			
		||||
        i += 1
 | 
			
		||||
        if i > 1000000:
 | 
			
		||||
            raise ESPHomeYAMLError("Circular dependency detected!")
 | 
			
		||||
 | 
			
		||||
        task, domain = _TASKS.popleft()
 | 
			
		||||
        _LOGGER.debug("Executing task for domain=%s", domain)
 | 
			
		||||
        try:
 | 
			
		||||
            task.next()
 | 
			
		||||
            _TASKS.append((task, domain))
 | 
			
		||||
        except StopIteration:
 | 
			
		||||
            _LOGGER.debug(" -> %s finished", domain)
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add(expression, require=True):
 | 
			
		||||
    if require and isinstance(expression, Expression):
 | 
			
		||||
        expression.require()
 | 
			
		||||
    _EXPRESSIONS.append(expression)
 | 
			
		||||
    _LOGGER.debug("Adding: %s", statement(expression))
 | 
			
		||||
    return expression
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MockObj(Expression):
 | 
			
		||||
    def __init__(self, base, op=u'.'):
 | 
			
		||||
    def __init__(self, base, op=u'.', has_side_effects=True):
 | 
			
		||||
        self.base = base
 | 
			
		||||
        self.op = op
 | 
			
		||||
        self._has_side_effects = has_side_effects
 | 
			
		||||
        super(MockObj, self).__init__()
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, attr):
 | 
			
		||||
        if attr == u'_':
 | 
			
		||||
            obj = MockObj(u'{}{}'.format(self.base, self.op))
 | 
			
		||||
            obj.requires.append(self)
 | 
			
		||||
            return obj
 | 
			
		||||
        if attr == u'new':
 | 
			
		||||
            obj = MockObj(u'new {}'.format(self.base), u'->')
 | 
			
		||||
            obj.requires.append(self)
 | 
			
		||||
            return obj
 | 
			
		||||
        next_op = u'.'
 | 
			
		||||
        if attr.startswith(u'P'):
 | 
			
		||||
        if attr.startswith(u'P') and self.op != '::':
 | 
			
		||||
            attr = attr[1:]
 | 
			
		||||
            next_op = u'->'
 | 
			
		||||
        op = self.op
 | 
			
		||||
        obj = MockObj(u'{}{}{}'.format(self.base, op, attr), next_op)
 | 
			
		||||
        if attr.startswith(u'_'):
 | 
			
		||||
            attr = attr[1:]
 | 
			
		||||
        obj = MockObj(u'{}{}{}'.format(self.base, self.op, attr), next_op)
 | 
			
		||||
        obj.requires.append(self)
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
@@ -353,12 +510,41 @@ class MockObj(Expression):
 | 
			
		||||
                continue
 | 
			
		||||
            require.require()
 | 
			
		||||
 | 
			
		||||
    def template(self, args):
 | 
			
		||||
        if not isinstance(args, TemplateArguments):
 | 
			
		||||
            args = TemplateArguments(args)
 | 
			
		||||
        obj = MockObj(u'{}{}'.format(self.base, args))
 | 
			
		||||
        obj.requires.append(self)
 | 
			
		||||
        obj.requires.append(args)
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
App = MockObj(u'App')
 | 
			
		||||
    def namespace(self, name):
 | 
			
		||||
        obj = MockObj(u'{}{}{}'.format(self.base, self.op, name), u'::')
 | 
			
		||||
        obj.requires.append(self)
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
GPIOPin = MockObj(u'GPIOPin')
 | 
			
		||||
GPIOOutputPin = MockObj(u'GPIOOutputPin')
 | 
			
		||||
GPIOInputPin = MockObj(u'GPIOInputPin')
 | 
			
		||||
    def has_side_effects(self):
 | 
			
		||||
        return self._has_side_effects
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
global_ns = MockObj('', '')
 | 
			
		||||
float_ = global_ns.namespace('float')
 | 
			
		||||
bool_ = global_ns.namespace('bool')
 | 
			
		||||
std_ns = global_ns.namespace('std')
 | 
			
		||||
std_string = std_ns.string
 | 
			
		||||
uint8 = global_ns.namespace('uint8_t')
 | 
			
		||||
uint16 = global_ns.namespace('uint16_t')
 | 
			
		||||
uint32 = global_ns.namespace('uint32_t')
 | 
			
		||||
NAN = global_ns.namespace('NAN')
 | 
			
		||||
esphomelib_ns = global_ns  # using namespace esphomelib;
 | 
			
		||||
NoArg = esphomelib_ns.NoArg
 | 
			
		||||
App = esphomelib_ns.App
 | 
			
		||||
Application = esphomelib_ns.namespace('Application')
 | 
			
		||||
optional = esphomelib_ns.optional
 | 
			
		||||
 | 
			
		||||
GPIOPin = esphomelib_ns.GPIOPin
 | 
			
		||||
GPIOOutputPin = esphomelib_ns.GPIOOutputPin
 | 
			
		||||
GPIOInputPin = esphomelib_ns.GPIOInputPin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_gpio_pin_number(conf):
 | 
			
		||||
@@ -367,25 +553,45 @@ def get_gpio_pin_number(conf):
 | 
			
		||||
    return conf[CONF_NUMBER]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_gpio_pin_(obj, conf, default_mode):
 | 
			
		||||
    if isinstance(conf, int):
 | 
			
		||||
        return conf
 | 
			
		||||
    if conf.get(CONF_INVERTED) is None:
 | 
			
		||||
        return obj(conf[CONF_NUMBER], conf.get(CONF_MODE))
 | 
			
		||||
    return obj(conf[CONF_NUMBER], RawExpression(conf.get(CONF_MODE, default_mode)),
 | 
			
		||||
               conf[CONF_INVERTED])
 | 
			
		||||
def generic_gpio_pin_expression_(conf, mock_obj, default_mode):
 | 
			
		||||
    if conf is None:
 | 
			
		||||
        return
 | 
			
		||||
    number = conf[CONF_NUMBER]
 | 
			
		||||
    inverted = conf.get(CONF_INVERTED)
 | 
			
		||||
    if CONF_PCF8574 in conf:
 | 
			
		||||
        hub = None
 | 
			
		||||
        for hub in get_variable(conf[CONF_PCF8574]):
 | 
			
		||||
            yield None
 | 
			
		||||
        if default_mode == u'INPUT':
 | 
			
		||||
            mode = conf.get(CONF_MODE, u'INPUT')
 | 
			
		||||
            yield hub.make_input_pin(number,
 | 
			
		||||
                                     RawExpression('PCF8574_' + mode),
 | 
			
		||||
                                     inverted)
 | 
			
		||||
            return
 | 
			
		||||
        elif default_mode == u'OUTPUT':
 | 
			
		||||
            yield hub.make_output_pin(number, inverted)
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
            raise ESPHomeYAMLError(u"Unknown default mode {}".format(default_mode))
 | 
			
		||||
    if len(conf) == 1:
 | 
			
		||||
        yield IntLiteral(number)
 | 
			
		||||
        return
 | 
			
		||||
    mode = RawExpression(conf.get(CONF_MODE, default_mode))
 | 
			
		||||
    yield mock_obj(number, mode, inverted)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_gpio_pin(conf):
 | 
			
		||||
    return GPIOPin(conf[CONF_NUMBER], conf[CONF_MODE], conf.get(CONF_INVERTED))
 | 
			
		||||
def gpio_output_pin_expression(conf):
 | 
			
		||||
    exp = None
 | 
			
		||||
    for exp in generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT'):
 | 
			
		||||
        yield None
 | 
			
		||||
    yield exp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_gpio_output_pin(conf):
 | 
			
		||||
    return exp_gpio_pin_(GPIOOutputPin, conf, u'OUTPUT')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_gpio_input_pin(conf):
 | 
			
		||||
    return exp_gpio_pin_(GPIOInputPin, conf, u'INPUT')
 | 
			
		||||
def gpio_input_pin_expression(conf):
 | 
			
		||||
    exp = None
 | 
			
		||||
    for exp in generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT'):
 | 
			
		||||
        yield None
 | 
			
		||||
    yield exp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_mqtt_component(obj, config):
 | 
			
		||||
@@ -403,16 +609,6 @@ def setup_mqtt_component(obj, config):
 | 
			
		||||
                                 availability[CONF_PAYLOAD_NOT_AVAILABLE]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_empty_optional(type):
 | 
			
		||||
    return RawExpression(u'Optional<{}>()'.format(type))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exp_optional(type, value):
 | 
			
		||||
    if value is None:
 | 
			
		||||
        return exp_empty_optional(type)
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# shlex's quote for Python 2.7
 | 
			
		||||
_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def show_logs(config, topic=None, username=None, password=None, client_id=None):
 | 
			
		||||
def show_logs(config, topic=None, username=None, password=None, client_id=None, escape=False):
 | 
			
		||||
    if topic is not None:
 | 
			
		||||
        pass  # already have topic
 | 
			
		||||
    elif CONF_MQTT in config:
 | 
			
		||||
@@ -57,7 +57,10 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None):
 | 
			
		||||
 | 
			
		||||
    def on_message(client, userdata, msg):
 | 
			
		||||
        time = datetime.now().time().strftime(u'[%H:%M:%S]')
 | 
			
		||||
        print(time + msg.payload)
 | 
			
		||||
        message = msg.payload.decode('utf-8')
 | 
			
		||||
        if escape:
 | 
			
		||||
            message = message.replace('\033', '\\033')
 | 
			
		||||
        print(time + message)
 | 
			
		||||
 | 
			
		||||
    return initialize(config, [topic], on_message, username, password, client_id)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,9 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.const import ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_NUMBER, CONF_MODE, \
 | 
			
		||||
    CONF_INVERTED
 | 
			
		||||
from esphomeyaml.components import pcf8574
 | 
			
		||||
from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \
 | 
			
		||||
    ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -21,7 +22,7 @@ ESP8266_D1_PINS = dict(ESP8266_PINS, **{
 | 
			
		||||
    'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2, 'SDA': 4, 'SCL': 5,
 | 
			
		||||
})
 | 
			
		||||
ESP8266_D1_MINI_PINS = dict(ESP8266_PINS, **{
 | 
			
		||||
    'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 0, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'RX': 3,
 | 
			
		||||
    'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'RX': 3,
 | 
			
		||||
    'TX': 1, 'LED': 2, 'SDA': 4, 'SCL': 5,
 | 
			
		||||
})
 | 
			
		||||
ESP8266_THING_PINS = dict(ESP8266_PINS, **{
 | 
			
		||||
@@ -64,7 +65,8 @@ ESP32_BOARD_TO_PINS = {
 | 
			
		||||
 | 
			
		||||
def _translate_pin(value):
 | 
			
		||||
    if isinstance(value, dict) or value is None:
 | 
			
		||||
        raise vol.Invalid(u"This option doesn't allow more complicated options like inverted.")
 | 
			
		||||
        raise vol.Invalid(u"This variable only supports pin numbers, not full pin schemas "
 | 
			
		||||
                          u"(with inverted and mode).")
 | 
			
		||||
    if isinstance(value, int):
 | 
			
		||||
        return value
 | 
			
		||||
    try:
 | 
			
		||||
@@ -72,7 +74,7 @@ def _translate_pin(value):
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        pass
 | 
			
		||||
    if value.startswith('GPIO'):
 | 
			
		||||
        return vol.Coerce(int)(value[len('GPIO'):])
 | 
			
		||||
        return vol.Coerce(int)(value[len('GPIO'):].strip())
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        if value in ESP32_PINS:
 | 
			
		||||
            return ESP32_PINS[value]
 | 
			
		||||
@@ -80,7 +82,7 @@ def _translate_pin(value):
 | 
			
		||||
            raise vol.Invalid(u"ESP32: Unknown board {} with unknown "
 | 
			
		||||
                              u"pin {}.".format(core.BOARD, value))
 | 
			
		||||
        if value not in ESP32_BOARD_TO_PINS[core.BOARD]:
 | 
			
		||||
            raise vol.Invalid(u"ESP32: Board {} doesn't have"
 | 
			
		||||
            raise vol.Invalid(u"ESP32: Board {} doesn't have "
 | 
			
		||||
                              u"pin {}".format(core.BOARD, value))
 | 
			
		||||
        return ESP32_BOARD_TO_PINS[core.BOARD][value]
 | 
			
		||||
    elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
 | 
			
		||||
@@ -90,13 +92,13 @@ def _translate_pin(value):
 | 
			
		||||
            raise vol.Invalid(u"ESP8266: Unknown board {} with unknown "
 | 
			
		||||
                              u"pin {}.".format(core.BOARD, value))
 | 
			
		||||
        if value not in ESP8266_BOARD_TO_PINS[core.BOARD]:
 | 
			
		||||
            raise vol.Invalid(u"ESP8266: Board {} doesn't have"
 | 
			
		||||
            raise vol.Invalid(u"ESP8266: Board {} doesn't have "
 | 
			
		||||
                              u"pin {}".format(core.BOARD, value))
 | 
			
		||||
        return ESP8266_BOARD_TO_PINS[core.BOARD][value]
 | 
			
		||||
    raise vol.Invalid(u"Invalid ESP platform.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate_gpio_pin(value):
 | 
			
		||||
def validate_gpio_pin(value):
 | 
			
		||||
    value = _translate_pin(value)
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        if value < 0 or value > 39:
 | 
			
		||||
@@ -119,7 +121,7 @@ def _validate_gpio_pin(value):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def input_pin(value):
 | 
			
		||||
    value = _validate_gpio_pin(value)
 | 
			
		||||
    value = validate_gpio_pin(value)
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        return value
 | 
			
		||||
    elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
 | 
			
		||||
@@ -128,21 +130,19 @@ def input_pin(value):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def output_pin(value):
 | 
			
		||||
    value = _validate_gpio_pin(value)
 | 
			
		||||
    value = validate_gpio_pin(value)
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        if 34 <= value <= 39:
 | 
			
		||||
            raise vol.Invalid(u"ESP32: Pin {} (34-39) can only be used as "
 | 
			
		||||
                              u"input pins.".format(value))
 | 
			
		||||
        return value
 | 
			
		||||
    elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
 | 
			
		||||
        if value == 16:
 | 
			
		||||
            raise vol.Invalid(u"Pin {} doesn't support output mode".format(value))
 | 
			
		||||
        return value
 | 
			
		||||
    raise vol.Invalid("Invalid ESP platform.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def analog_pin(value):
 | 
			
		||||
    value = _validate_gpio_pin(value)
 | 
			
		||||
    value = validate_gpio_pin(value)
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        if 32 <= value <= 39:  # ADC1
 | 
			
		||||
            return value
 | 
			
		||||
@@ -173,37 +173,50 @@ PIN_MODES_ESP32 = [
 | 
			
		||||
def pin_mode(value):
 | 
			
		||||
    value = vol.All(vol.Coerce(str), vol.Upper)(value)
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        return vol.Any(*PIN_MODES_ESP32)(value)
 | 
			
		||||
        return cv.one_of(*PIN_MODES_ESP32)(value)
 | 
			
		||||
    elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
 | 
			
		||||
        return vol.Any(*PIN_MODES_ESP8266)(value)
 | 
			
		||||
        return cv.one_of(*PIN_MODES_ESP8266)(value)
 | 
			
		||||
    raise vol.Invalid(u"Invalid ESP platform.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GPIO_PIN_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_NUMBER): gpio_pin,
 | 
			
		||||
    vol.Required(CONF_MODE): pin_mode,
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(output_pin, vol.Schema({
 | 
			
		||||
GPIO_FULL_OUTPUT_PIN_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_NUMBER): output_pin,
 | 
			
		||||
    vol.Optional(CONF_MODE): pin_mode,
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
}))
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, vol.Schema({
 | 
			
		||||
    vol.Required(CONF_NUMBER): input_pin,
 | 
			
		||||
GPIO_FULL_INPUT_PIN_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_NUMBER): output_pin,
 | 
			
		||||
    vol.Optional(CONF_MODE): pin_mode,
 | 
			
		||||
    vol.Optional(CONF_INVERTED): cv.boolean,
 | 
			
		||||
}))
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def schema_validate_number(validator):
 | 
			
		||||
    def valid(value):
 | 
			
		||||
        if isinstance(value, dict):
 | 
			
		||||
            value[CONF_NUMBER] = validator(value[CONF_NUMBER])
 | 
			
		||||
        else:
 | 
			
		||||
            value = validator(value)
 | 
			
		||||
        return value
 | 
			
		||||
def shorthand_output_pin(value):
 | 
			
		||||
    value = output_pin(value)
 | 
			
		||||
    return {CONF_NUMBER: value}
 | 
			
		||||
 | 
			
		||||
    return valid
 | 
			
		||||
 | 
			
		||||
def shorthand_input_pin(value):
 | 
			
		||||
    value = input_pin(value)
 | 
			
		||||
    return {CONF_NUMBER: value}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({
 | 
			
		||||
    vol.Required(CONF_PCF8574): cv.use_variable_id(pcf8574.PCF8574Component),
 | 
			
		||||
    vol.Required(CONF_NUMBER): vol.Coerce(int),
 | 
			
		||||
    vol.Optional(CONF_MODE): vol.All(vol.Upper, "OUTPUT"),
 | 
			
		||||
    vol.Optional(CONF_INVERTED, default=False): cv.boolean,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
PCF8574_INPUT_PIN_SCHEMA = PCF8574_OUTPUT_PIN_SCHEMA.extend({
 | 
			
		||||
    vol.Optional(CONF_MODE): vol.All(vol.Upper, vol.Any("INPUT", "INPUT_PULLUP")),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
GPIO_INTERNAL_OUTPUT_PIN_SCHEMA = vol.Any(shorthand_output_pin, GPIO_FULL_OUTPUT_PIN_SCHEMA)
 | 
			
		||||
 | 
			
		||||
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(PCF8574_OUTPUT_PIN_SCHEMA, GPIO_INTERNAL_OUTPUT_PIN_SCHEMA)
 | 
			
		||||
 | 
			
		||||
GPIO_INTERNAL_INPUT_PIN_SCHEMA = vol.Any(shorthand_input_pin, GPIO_FULL_INPUT_PIN_SCHEMA)
 | 
			
		||||
 | 
			
		||||
GPIO_INPUT_PIN_SCHEMA = vol.Any(PCF8574_INPUT_PIN_SCHEMA, GPIO_INTERNAL_INPUT_PIN_SCHEMA)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								esphomeyaml/platformio.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphomeyaml/platformio.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
; This file allows the docker build file to install the required platformio
 | 
			
		||||
; platforms
 | 
			
		||||
 | 
			
		||||
[env:espressif8266]
 | 
			
		||||
platform = espressif8266
 | 
			
		||||
board = nodemcuv2
 | 
			
		||||
framework = arduino
 | 
			
		||||
 | 
			
		||||
[env:espressif32]
 | 
			
		||||
platform = espressif32
 | 
			
		||||
board = nodemcu-32s
 | 
			
		||||
framework = arduino
 | 
			
		||||
@@ -8,7 +8,8 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
import esphomeyaml.config_validation as cv
 | 
			
		||||
from esphomeyaml.components import mqtt
 | 
			
		||||
from esphomeyaml.const import ESP_BOARDS_FOR_PLATFORM, ESP_PLATFORMS, ESP_PLATFORM_ESP32
 | 
			
		||||
from esphomeyaml.const import ESP_BOARDS_FOR_PLATFORM, ESP_PLATFORMS, ESP_PLATFORM_ESP32, \
 | 
			
		||||
    ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.helpers import color
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +70,18 @@ logger:
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wizard_file(**kwargs):
 | 
			
		||||
    config = BASE_CONFIG.format(**kwargs)
 | 
			
		||||
 | 
			
		||||
    if kwargs['ota_password']:
 | 
			
		||||
        config += "ota:\n  password: '{}'\n".format(kwargs['ota_password'])
 | 
			
		||||
    else:
 | 
			
		||||
        config += "ota:\n"
 | 
			
		||||
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if os.getenv('ESPHOMEYAML_QUICKWIZARD', False):
 | 
			
		||||
    def sleep(time):
 | 
			
		||||
        pass
 | 
			
		||||
@@ -141,10 +154,10 @@ def wizard(path):
 | 
			
		||||
    print("Great! Your node is now called \"{}\".".format(color('cyan', name)))
 | 
			
		||||
    sleep(1)
 | 
			
		||||
    print_step(2, ESP_BIG)
 | 
			
		||||
    print("Now I'd like to know which *board* you're using so that I can compile "
 | 
			
		||||
    print("Now I'd like to know what microcontroller you're using so that I can compile "
 | 
			
		||||
          "firmwares for it.")
 | 
			
		||||
    print("Are you using an " + color('green', 'ESP32') + " or " +
 | 
			
		||||
          color('green', 'ESP8266') + " based board?")
 | 
			
		||||
          color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
 | 
			
		||||
    while True:
 | 
			
		||||
        sleep(0.5)
 | 
			
		||||
        print()
 | 
			
		||||
@@ -168,6 +181,8 @@ def wizard(path):
 | 
			
		||||
    print("Next, I need to know what " + color('green', 'board') + " you're using.")
 | 
			
		||||
    sleep(0.5)
 | 
			
		||||
    print("Please go to {} and choose a board.".format(color('green', board_link)))
 | 
			
		||||
    if platform == ESP_PLATFORM_ESP8266:
 | 
			
		||||
        print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
 | 
			
		||||
    print()
 | 
			
		||||
    # Don't sleep because user needs to copy link
 | 
			
		||||
    if platform == ESP_PLATFORM_ESP32:
 | 
			
		||||
@@ -217,7 +232,7 @@ def wizard(path):
 | 
			
		||||
    sleep(0.75)
 | 
			
		||||
 | 
			
		||||
    print("Now please state the " + color('green', 'password') +
 | 
			
		||||
          " of the WiFi network so that I can connect to it.")
 | 
			
		||||
          " of the WiFi network so that I can connect to it (Leave empty for no password)")
 | 
			
		||||
    print()
 | 
			
		||||
    print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
 | 
			
		||||
    sleep(0.5)
 | 
			
		||||
@@ -269,14 +284,10 @@ def wizard(path):
 | 
			
		||||
    print("Press ENTER for no password")
 | 
			
		||||
    ota_password = raw_input(color('bold_white', '(password): '))
 | 
			
		||||
 | 
			
		||||
    config = BASE_CONFIG.format(name=name, platform=platform, board=board,
 | 
			
		||||
                                ssid=ssid, psk=psk, broker=broker,
 | 
			
		||||
                                mqtt_username=mqtt_username, mqtt_password=mqtt_password)
 | 
			
		||||
 | 
			
		||||
    if ota_password:
 | 
			
		||||
        config += "ota:\n  password: '{}'\n".format(ota_password)
 | 
			
		||||
    else:
 | 
			
		||||
        config += "ota:\n"
 | 
			
		||||
    config = wizard_file(name=name, platform=platform, board=board,
 | 
			
		||||
                         ssid=ssid, psk=psk, broker=broker,
 | 
			
		||||
                         mqtt_username=mqtt_username, mqtt_password=mqtt_password,
 | 
			
		||||
                         ota_password=ota_password)
 | 
			
		||||
 | 
			
		||||
    with codecs.open(path, 'w') as f_handle:
 | 
			
		||||
        f_handle.write(config)
 | 
			
		||||
@@ -295,5 +306,5 @@ def wizard(path):
 | 
			
		||||
    print(color('bold_white', "  discovery: True"))
 | 
			
		||||
    print()
 | 
			
		||||
    print("  > Then follow the rest of the getting started guide:")
 | 
			
		||||
    print("  > https://esphomelib.com/esphomeyaml/getting-started.html")
 | 
			
		||||
    print("  > https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html")
 | 
			
		||||
    return 0
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,11 @@ import codecs
 | 
			
		||||
import errno
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.config import iter_components
 | 
			
		||||
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_NAME, \
 | 
			
		||||
    CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
 | 
			
		||||
    CONF_LIBRARY_URI, \
 | 
			
		||||
    CONF_NAME, CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError
 | 
			
		||||
 | 
			
		||||
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
 | 
			
		||||
@@ -28,7 +30,7 @@ void setup() {
 | 
			
		||||
 | 
			
		||||
void loop() {
 | 
			
		||||
  App.loop();
 | 
			
		||||
  delay(1);
 | 
			
		||||
  delay(16);
 | 
			
		||||
}
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +52,7 @@ platform = {platform}
 | 
			
		||||
board = {board}
 | 
			
		||||
framework = arduino
 | 
			
		||||
lib_deps =
 | 
			
		||||
    {esphomeyaml_uri}
 | 
			
		||||
    {lib_deps}
 | 
			
		||||
    ${{common.lib_deps}}
 | 
			
		||||
build_flags =
 | 
			
		||||
    {build_flags}
 | 
			
		||||
@@ -68,7 +70,9 @@ def get_build_flags(config, key):
 | 
			
		||||
    for _, component, conf in iter_components(config):
 | 
			
		||||
        if not hasattr(component, key):
 | 
			
		||||
            continue
 | 
			
		||||
        flags = getattr(component, key)(conf)
 | 
			
		||||
        flags = getattr(component, key)
 | 
			
		||||
        if callable(flags):
 | 
			
		||||
            flags = flags(conf)
 | 
			
		||||
        if flags is None:
 | 
			
		||||
            continue
 | 
			
		||||
        if isinstance(flags, (str, unicode)):
 | 
			
		||||
@@ -85,20 +89,39 @@ def get_ini_content(config):
 | 
			
		||||
        u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
 | 
			
		||||
        u'platform': platform,
 | 
			
		||||
        u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
 | 
			
		||||
        u'esphomeyaml_uri': config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI],
 | 
			
		||||
        u'build_flags': u'',
 | 
			
		||||
    }
 | 
			
		||||
    build_flags = set()
 | 
			
		||||
    if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]:
 | 
			
		||||
        build_flags |= get_build_flags(config, 'build_flags')
 | 
			
		||||
        build_flags |= get_build_flags(config, 'BUILD_FLAGS')
 | 
			
		||||
        build_flags.add(u"-DESPHOMEYAML_USE")
 | 
			
		||||
    build_flags |= get_build_flags(config, 'required_build_flags')
 | 
			
		||||
    build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS')
 | 
			
		||||
 | 
			
		||||
    # avoid changing build flags order
 | 
			
		||||
    build_flags = sorted(list(build_flags))
 | 
			
		||||
    if build_flags:
 | 
			
		||||
        options[u'build_flags'] = u'\n    '.join(build_flags)
 | 
			
		||||
    return INI_CONTENT_FORMAT.format(**options)
 | 
			
		||||
 | 
			
		||||
    lib_deps = set()
 | 
			
		||||
    lib_deps.add(config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI])
 | 
			
		||||
    lib_deps |= get_build_flags(config, 'LIB_DEPS')
 | 
			
		||||
    lib_deps |= get_build_flags(config, 'lib_deps')
 | 
			
		||||
    if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
 | 
			
		||||
        lib_deps |= {
 | 
			
		||||
            'Preferences',  # Preferences helper
 | 
			
		||||
        }
 | 
			
		||||
    # avoid changing build flags order
 | 
			
		||||
    lib_deps = sorted(x for x in lib_deps if x)
 | 
			
		||||
    if lib_deps:
 | 
			
		||||
        options[u'lib_deps'] = u'\n    '.join(lib_deps)
 | 
			
		||||
 | 
			
		||||
    content = INI_CONTENT_FORMAT.format(**options)
 | 
			
		||||
    if CONF_BOARD_FLASH_MODE in config[CONF_ESPHOMEYAML]:
 | 
			
		||||
        flash_mode = config[CONF_ESPHOMEYAML][CONF_BOARD_FLASH_MODE]
 | 
			
		||||
        content += "board_flash_mode = {}\n".format(flash_mode)
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mkdir_p(path):
 | 
			
		||||
@@ -144,7 +167,7 @@ def write_platformio_ini(content, path):
 | 
			
		||||
        mkdir_p(os.path.dirname(path))
 | 
			
		||||
        content_format = INI_BASE_FORMAT
 | 
			
		||||
    full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \
 | 
			
		||||
                content + INI_AUTO_GENERATE_END + content_format[1]
 | 
			
		||||
        content + INI_AUTO_GENERATE_END + content_format[1]
 | 
			
		||||
    if prev_file == full_file:
 | 
			
		||||
        return
 | 
			
		||||
    with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
 | 
			
		||||
@@ -166,7 +189,7 @@ def write_cpp(code_s, path):
 | 
			
		||||
        code_format = CPP_BASE_FORMAT
 | 
			
		||||
 | 
			
		||||
    full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \
 | 
			
		||||
                code_s + CPP_AUTO_GENERATE_END + code_format[1]
 | 
			
		||||
        code_s + CPP_AUTO_GENERATE_END + code_format[1]
 | 
			
		||||
    if prev_file == full_file:
 | 
			
		||||
        return
 | 
			
		||||
    with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,8 @@ from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
import yaml
 | 
			
		||||
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress
 | 
			
		||||
from esphomeyaml import core
 | 
			
		||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -194,6 +195,10 @@ def _secret_yaml(loader, node):
 | 
			
		||||
    return secrets[node.value]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _lambda(loader, node):
 | 
			
		||||
    return Lambda(unicode(node.value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict)
 | 
			
		||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
 | 
			
		||||
yaml.SafeLoader.add_constructor('!env_var', _env_var_yaml)
 | 
			
		||||
@@ -205,6 +210,7 @@ yaml.SafeLoader.add_constructor('!include_dir_merge_list',
 | 
			
		||||
yaml.SafeLoader.add_constructor('!include_dir_named', _include_dir_named_yaml)
 | 
			
		||||
yaml.SafeLoader.add_constructor('!include_dir_merge_named',
 | 
			
		||||
                                _include_dir_merge_named_yaml)
 | 
			
		||||
yaml.SafeLoader.add_constructor('!lambda', _lambda)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# From: https://gist.github.com/miracle2k/3184458
 | 
			
		||||
@@ -245,11 +251,39 @@ def hex_int_representer(_, data):
 | 
			
		||||
    return node
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ipaddress_representer(_, data):
 | 
			
		||||
def stringify_representer(_, data):
 | 
			
		||||
    node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=str(data))
 | 
			
		||||
    return node
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TIME_PERIOD_UNIT_MAP = {
 | 
			
		||||
    'microseconds': 'us',
 | 
			
		||||
    'milliseconds': 'ms',
 | 
			
		||||
    'seconds': 's',
 | 
			
		||||
    'minutes': 'min',
 | 
			
		||||
    'hours': 'h',
 | 
			
		||||
    'days': 'd',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def represent_time_period(dumper, data):
 | 
			
		||||
    dictionary = data.as_dict()
 | 
			
		||||
    if len(dictionary) == 1:
 | 
			
		||||
        unit, value = dictionary.popitem()
 | 
			
		||||
        out = '{}{}'.format(value, TIME_PERIOD_UNIT_MAP[unit])
 | 
			
		||||
        return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=out)
 | 
			
		||||
    return represent_odict(dumper, 'tag:yaml.org,2002:map', dictionary)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def represent_lambda(_, data):
 | 
			
		||||
    node = yaml.ScalarNode(tag='!lambda', value=data.value, style='>')
 | 
			
		||||
    return node
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def represent_id(_, data):
 | 
			
		||||
    return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=data.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
yaml.SafeDumper.add_representer(
 | 
			
		||||
    OrderedDict,
 | 
			
		||||
    lambda dumper, value:
 | 
			
		||||
@@ -264,4 +298,8 @@ yaml.SafeDumper.add_representer(
 | 
			
		||||
 | 
			
		||||
yaml.SafeDumper.add_representer(unicode, unicode_representer)
 | 
			
		||||
yaml.SafeDumper.add_representer(HexInt, hex_int_representer)
 | 
			
		||||
yaml.SafeDumper.add_representer(IPAddress, ipaddress_representer)
 | 
			
		||||
yaml.SafeDumper.add_representer(IPAddress, stringify_representer)
 | 
			
		||||
yaml.SafeDumper.add_representer(MACAddress, stringify_representer)
 | 
			
		||||
yaml.SafeDumper.add_multi_representer(TimePeriod, represent_time_period)
 | 
			
		||||
yaml.SafeDumper.add_multi_representer(Lambda, represent_lambda)
 | 
			
		||||
yaml.SafeDumper.add_multi_representer(core.ID, represent_id)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								examples/sonoff_4ch.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								examples/sonoff_4ch.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
esphomeyaml:
 | 
			
		||||
  name: <NAME_OF_NODE>
 | 
			
		||||
  platform: ESP8266
 | 
			
		||||
  board: esp01_1m
 | 
			
		||||
  board_flash_mode: dout
 | 
			
		||||
 | 
			
		||||
wifi:
 | 
			
		||||
  ssid: <YOUR_SSID>
 | 
			
		||||
  password: <YOUR_PASSWORD>
 | 
			
		||||
 | 
			
		||||
mqtt:
 | 
			
		||||
  broker: <YOUR_MQTT_BROKER>
 | 
			
		||||
  username: <YOUR_USERNAME>
 | 
			
		||||
  password: <YOUR_PASSWORD>
 | 
			
		||||
 | 
			
		||||
logger:
 | 
			
		||||
 | 
			
		||||
ota:
 | 
			
		||||
 | 
			
		||||
binary_sensor:
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin:
 | 
			
		||||
      number: GPIO0
 | 
			
		||||
      mode: INPUT_PULLUP
 | 
			
		||||
      inverted: True
 | 
			
		||||
    name: "Sonoff 4CH Button 1"
 | 
			
		||||
    on_press:
 | 
			
		||||
      then:
 | 
			
		||||
        switch.toggle:
 | 
			
		||||
          id: relay_1
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin:
 | 
			
		||||
      number: GPIO9
 | 
			
		||||
      mode: INPUT_PULLUP
 | 
			
		||||
      inverted: True
 | 
			
		||||
    name: "Sonoff 4CH Button 2"
 | 
			
		||||
    on_press:
 | 
			
		||||
      then:
 | 
			
		||||
        switch.toggle:
 | 
			
		||||
          id: relay_2
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin:
 | 
			
		||||
      number: GPIO10
 | 
			
		||||
      mode: INPUT_PULLUP
 | 
			
		||||
      inverted: True
 | 
			
		||||
    name: "Sonoff 4CH Button 3"
 | 
			
		||||
    on_press:
 | 
			
		||||
      then:
 | 
			
		||||
        switch.toggle:
 | 
			
		||||
          id: relay_3
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin:
 | 
			
		||||
      number: GPIO14
 | 
			
		||||
      mode: INPUT_PULLUP
 | 
			
		||||
      inverted: True
 | 
			
		||||
    name: "Sonoff 4CH Button 4"
 | 
			
		||||
    on_press:
 | 
			
		||||
      then:
 | 
			
		||||
        switch.toggle:
 | 
			
		||||
          id: relay_4
 | 
			
		||||
  - platform: status
 | 
			
		||||
    name: "Sonoff 4CH Status"
 | 
			
		||||
 | 
			
		||||
switch:
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: "Sonoff 4CH Relay 1"
 | 
			
		||||
    pin: GPIO12
 | 
			
		||||
    id: relay_1
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: "Sonoff 4CH Relay 2"
 | 
			
		||||
    pin: GPIO5
 | 
			
		||||
    id: relay_2
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: "Sonoff 4CH Relay 3"
 | 
			
		||||
    pin: GPIO4
 | 
			
		||||
    id: relay_3
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: "Sonoff 4CH Relay 4"
 | 
			
		||||
    pin: GPIO15
 | 
			
		||||
    id: relay_4
 | 
			
		||||
 | 
			
		||||
output:
 | 
			
		||||
  - platform: esp8266_pwm
 | 
			
		||||
    id: blue_led
 | 
			
		||||
    pin: GPIO13
 | 
			
		||||
    inverted: True
 | 
			
		||||
 | 
			
		||||
light:
 | 
			
		||||
  - platform: monochromatic
 | 
			
		||||
    name: "Sonoff 4CH Blue LED"
 | 
			
		||||
    output: blue_led
 | 
			
		||||
							
								
								
									
										50
									
								
								examples/sonoff_s20.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								examples/sonoff_s20.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
esphomeyaml:
 | 
			
		||||
  name: <NAME_OF_NODE>
 | 
			
		||||
  platform: ESP8266
 | 
			
		||||
  board: esp01_1m
 | 
			
		||||
  board_flash_mode: dout
 | 
			
		||||
 | 
			
		||||
wifi:
 | 
			
		||||
  ssid: <YOUR_SSID>
 | 
			
		||||
  password: <YOUR_PASSWORD>
 | 
			
		||||
 | 
			
		||||
mqtt:
 | 
			
		||||
  broker: <YOUR_MQTT_BROKER>
 | 
			
		||||
  username: <YOUR_USERNAME>
 | 
			
		||||
  password: <YOUR_PASSWORD>
 | 
			
		||||
 | 
			
		||||
logger:
 | 
			
		||||
 | 
			
		||||
ota:
 | 
			
		||||
 | 
			
		||||
binary_sensor:
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin:
 | 
			
		||||
      number: GPIO0
 | 
			
		||||
      mode: INPUT_PULLUP
 | 
			
		||||
      inverted: True
 | 
			
		||||
    name: "Sonoff S20 Button"
 | 
			
		||||
    on_press:
 | 
			
		||||
      then:
 | 
			
		||||
        - switch.toggle:
 | 
			
		||||
            id: relay
 | 
			
		||||
  - platform: status
 | 
			
		||||
    name: "Sonoff S20 Status"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
switch:
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: "Sonoff S20 Relay"
 | 
			
		||||
    pin: GPIO12
 | 
			
		||||
    id: relay
 | 
			
		||||
 | 
			
		||||
output:
 | 
			
		||||
  - platform: esp8266_pwm
 | 
			
		||||
    id: s20_green_led
 | 
			
		||||
    pin: GPIO13
 | 
			
		||||
    inverted: True
 | 
			
		||||
 | 
			
		||||
light:
 | 
			
		||||
  - platform: monochromatic
 | 
			
		||||
    name: "Sonoff S20 Green LED"
 | 
			
		||||
    output: s20_green_led
 | 
			
		||||
							
								
								
									
										3
									
								
								pylintrc
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								pylintrc
									
									
									
									
									
								
							@@ -14,6 +14,9 @@ disable=
 | 
			
		||||
  too-many-arguments,
 | 
			
		||||
  too-many-return-statements,
 | 
			
		||||
  duplicate-code,
 | 
			
		||||
  invalid-name,
 | 
			
		||||
  cyclic-import,
 | 
			
		||||
  redefined-builtin,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
additional-builtins=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								repository.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								repository.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "esphomeyaml HassIO Add-On Repository",
 | 
			
		||||
  "url": "https://github.com/OttoWinter/esphomeyaml",
 | 
			
		||||
  "maintainer": "Otto Winter <contact@otto-winter.com>"
 | 
			
		||||
}
 | 
			
		||||
@@ -3,3 +3,5 @@ platformio==3.5.2
 | 
			
		||||
pyyaml==3.12
 | 
			
		||||
paho-mqtt==1.3.1
 | 
			
		||||
colorlog==3.1.2
 | 
			
		||||
tornado==5.0.2
 | 
			
		||||
esptool==2.3.1
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user