Merge branch 'dev' into rc

This commit is contained in:
Otto Winter 2019-01-06 19:38:23 +01:00
parent 1ad65516cf
commit 4046a16d85
No known key found for this signature in database
GPG Key ID: DB66C0BE6013F97E
227 changed files with 11196 additions and 3511 deletions

View File

@ -1,6 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
about: Create a report to help esphomelib improve
---
@ -9,7 +9,9 @@ about: Create a report to help us improve
- esphomeyaml [here] - This is mostly for reporting bugs when compiling and when you get a long stack trace while compiling or if a configuration fails to validate.
- esphomelib [https://github.com/OttoWinter/esphomelib] - Report bugs there if the ESP is crashing or a feature is not working as expected.
- esphomedocs [https://github.com/OttoWinter/esphomedocs] - Report bugs there if the documentation is wrong/outdated.
- Provide as many details as possible. Paste logs, configuration sample and code into the backticks (```). Do not delete any text from this template!
- Provide as many details as possible. Paste logs, configuration sample and code into the backticks (```).
DO NOT DELETE ANY TEXT from this template! Otherwise the issue may be closed without a comment.
-->
**Operating environment (Hass.io/Docker/pip/etc.):**
@ -33,7 +35,7 @@ Please add the link to the documentation at https://esphomelib.com/esphomeyaml/i
**Problem-relevant YAML-configuration entries:**
```yaml
PASTE YAML FILE HERE
```
**Traceback (if applicable):**

View File

@ -7,16 +7,15 @@ about: Suggest an idea for this project
<!-- READ THIS FIRST:
- This is for feature requests only, if you want to have a certain new sensor/module supported, please use the "new integration" template.
- Please be as descriptive as possible, especially use-cases that can otherwise not be solved boost the problem's priority.
DO NOT DELETE ANY TEXT from this template! Otherwise the issue may be closed without a comment.
-->
**Is your feature request related to a problem? Please describe.**
<!--
A clear and concise description of what the problem is.
-->
Ex. I'm always frustrated when [...]
**Is your feature request related to a problem/use-case? Please describe.**
<!-- A clear and concise description of what the problem is. -->
**Describe the solution you'd like**
A description of what you want to happen.
**Describe the solution you'd like:**
<!-- A description of what you want to happen. -->
**Additional context**
Add any other context about the feature request here.
**Additional context:**
<!-- Add any other context about the feature request here. -->

View File

@ -4,17 +4,10 @@ about: Suggest a new integration for esphomelib
---
<!-- READ THIS FIRST:
- This is for new integrations (such as new sensors/modules) only, for new features within the environment please use the "feature request" template.
- Do not delete anything from this template and fill out the form as precisely as possible.
-->
DO NOT POST NEW INTEGRATION REQUESTS HERE!
**What new integration would you wish to have?**
<!-- A name/description of the new integration/board. -->
Please post all new integration requests in the esphomelib repository:
**If possible, provide a link to an existing library for the integration:**
https://github.com/OttoWinter/esphomelib/issues
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Additional context**
Thank you!

View File

@ -6,15 +6,9 @@
**Pull request in [esphomedocs](https://github.com/OttoWinter/esphomedocs) with documentation (if applicable):** OttoWinter/esphomedocs#<esphomedocs PR number goes here>
**Pull request in [esphomelib](https://github.com/OttoWinter/esphomelib) with C++ framework changes (if applicable):** OttoWinter/esphomelib#<esphomelib PR number goes here>
## Example entry for YAML configuration (if applicable):
```yaml
```
## Checklist:
- [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
- [ ] Check this box if you have read, understand, comply, and agree with the [Code of Conduct](https://github.com/OttoWinter/esphomeyaml/blob/master/CODE_OF_CONDUCT.md).
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).

1
.gitignore vendored
View File

@ -105,3 +105,4 @@ venv.bak/
config/
tests/build/
tests/.esphomeyaml/

View File

@ -11,6 +11,8 @@ stages:
.lint: &lint
stage: lint
before_script:
- pip install -e .
tags:
- python2.7
- esphomeyaml-lint
@ -24,9 +26,6 @@ stages:
- esphomeyaml-test
variables:
TZ: UTC
cache:
paths:
- tests/build
.docker-builder: &docker-builder
before_script:
@ -62,21 +61,20 @@ test2:
stage: build
script:
- docker run --rm --privileged hassioaddons/qemu-user-static:latest
- BUILD_FROM=homeassistant/${ADDON_ARCH}-base-ubuntu:latest
- BUILD_FROM=hassioaddons/ubuntu-base-${ADDON_ARCH}:2.2.0
- ADDON_VERSION="${CI_COMMIT_TAG#v}"
- ADDON_VERSION="${ADDON_VERSION:-${CI_COMMIT_SHA:0:7}}"
- ESPHOMELIB_VERSION="${ESPHOMELIB_VERSION:-dev}"
- echo "Build from ${BUILD_FROM}"
- echo "Add-on version ${ADDON_VERSION}"
- echo "Esphomelib version ${ESPHOMELIB_VERSION}"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "ADDON_ARCH=${ADDON_ARCH}" \
--build-arg "ADDON_VERSION=${ADDON_VERSION}" \
--build-arg "ESPHOMELIB_VERSION=${ESPHOMELIB_VERSION}" \
--build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")" \
--build-arg "BUILD_ARCH=${ADDON_ARCH}" \
--build-arg "BUILD_REF=${CI_COMMIT_SHA}" \
--build-arg "BUILD_VERSION=${ADDON_VERSION}" \
--tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" \
--tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
--file "docker/Dockerfile.hassio" \
@ -95,48 +93,48 @@ test2:
script:
- version="${CI_COMMIT_TAG#v}"
- echo "Publishing release version ${version}"
- docker pull "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:latest"
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc"
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
only:
@ -150,34 +148,34 @@ test2:
script:
- version="${CI_COMMIT_TAG#v}"
- echo "Publishing beta version ${version}"
- docker pull "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:rc"
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
only:
@ -190,7 +188,7 @@ build:normal:
<<: *docker-builder
stage: build
script:
- docker build -t "${CI_REGISTRY}/esphomeyaml:dev" .
- docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" .
.build-hassio-edge: &build-hassio-edge
<<: *build-hassio
@ -214,7 +212,6 @@ build:hassio-armhf:
<<: *build-hassio-release
variables:
ADDON_ARCH: armhf
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
#build:hassio-aarch64-edge:
# <<: *build-hassio-edge
@ -226,7 +223,6 @@ build:hassio-armhf:
# <<: *build-hassio-release
# variables:
# ADDON_ARCH: aarch64
# ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
build:hassio-i386-edge:
<<: *build-hassio-edge
@ -238,7 +234,6 @@ build:hassio-i386:
<<: *build-hassio-release
variables:
ADDON_ARCH: i386
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
build:hassio-amd64-edge:
<<: *build-hassio-edge
@ -250,7 +245,6 @@ build:hassio-amd64:
<<: *build-hassio-release
variables:
ADDON_ARCH: amd64
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
# Deploy jobs
deploy-release:armhf:
@ -267,7 +261,7 @@ deploy-beta:armhf:
# <<: *deploy-release
# variables:
# ADDON_ARCH: aarch64
#
#deploy-beta:aarch64:
# <<: *deploy-beta
# variables:

View File

@ -1,20 +1,30 @@
sudo: false
language: python
python:
- "2.7"
jobs:
matrix:
fast_finish: true
include:
- name: "Lint"
install:
- pip install -r requirements.txt
- pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow
- python: "2.7"
env: TARGET=Lint2.7
install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 pillow
script:
- flake8 esphomeyaml
- pylint esphomeyaml
- name: "Test"
install:
- pip install -e .
- pip install tzlocal pillow
- python: "3.5.3"
env: TARGET=Lint3.5
install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 pillow
script:
- flake8 esphomeyaml
- pylint esphomeyaml
- python: "2.7"
env: TARGET=Test2.7
install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 pillow
script:
- esphomeyaml tests/test1.yaml compile
- esphomeyaml tests/test2.yaml compile
- python: "3.5.3"
env: TARGET=Test3.5
install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 pillow
script:
- esphomeyaml tests/test1.yaml compile
- esphomeyaml tests/test2.yaml compile

View File

@ -21,8 +21,7 @@ COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
RUN pip install --no-cache-dir --no-binary :all: -e .
WORKDIR /config
ENTRYPOINT ["esphomeyaml"]

View File

@ -1,4 +1,17 @@
include README.md
include esphomeyaml/dashboard/templates/index.html
include esphomeyaml/dashboard/templates/login.html
include esphomeyaml/dashboard/static/ace.js
include esphomeyaml/dashboard/static/esphomeyaml.css
include esphomeyaml/dashboard/static/esphomeyaml.js
include esphomeyaml/dashboard/static/favicon.ico
include esphomeyaml/dashboard/static/jquery.min.js
include esphomeyaml/dashboard/static/jquery.validate.min.js
include esphomeyaml/dashboard/static/jquery-ui.min.js
include esphomeyaml/dashboard/static/materialize.min.css
include esphomeyaml/dashboard/static/materialize.min.js
include esphomeyaml/dashboard/static/materialize-stepper.min.css
include esphomeyaml/dashboard/static/materialize-stepper.min.js
include esphomeyaml/dashboard/static/mode-yaml.js
include esphomeyaml/dashboard/static/theme-dreamweaver.js
include esphomeyaml/dashboard/static/ext-searchbox.js

View File

@ -1,42 +1,75 @@
# Dockerfile for HassIO add-on
ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest
ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0
# hadolint ignore=DL3006
FROM ${BUILD_FROM}
RUN apt-get update && apt-get install -y --no-install-recommends \
# Set shell
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Copy root filesystem
COPY esphomeyaml-edge/rootfs /
COPY setup.py setup.cfg MANIFEST.in /opt/esphomeyaml/
COPY esphomeyaml /opt/esphomeyaml/esphomeyaml
RUN \
# Temporarily move nginx.conf (otherwise dpkg fails)
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bkp \
# Install add-on dependencies
&& apt-get update \
&& apt-get install -y --no-install-recommends \
# Python for esphomeyaml
python \
python-pip \
python-setuptools \
# Python Pillow for display component
python-pil \
# Git for esphomelib downloads
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No && \
platformio settings set check_libraries_interval 1000000 && \
platformio settings set check_platformio_interval 1000000 && \
platformio settings set check_platforms_interval 1000000
COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
ARG ESPHOMELIB_VERSION="dev"
RUN platformio lib -g install "https://github.com/OttoWinter/esphomelib.git#${ESPHOMELIB_VERSION}"
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
# Ping for dashboard online/offline status
iputils-ping \
# NGINX proxy
nginx \
\
&& mv /etc/nginx/nginx.conf.bkp /etc/nginx/nginx.conf \
\
&& pip2 install --no-cache-dir --no-binary :all: -e /opt/esphomeyaml \
\
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_libraries_interval 1000000 \
&& platformio settings set check_platformio_interval 1000000 \
&& platformio settings set check_platforms_interval 1000000 \
\
# Build an empty platformio project to force platformio to install all fw build dependencies
# The return-code will be non-zero since there's nothing to build.
&& (platformio run -d /opt/pio; echo "Done") \
\
# Cleanup
&& rm -fr \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/* \
/opt/pio/
# Build arugments
ARG ADDON_ARCH
ARG ADDON_VERSION
ARG BUILD_ARCH=amd64
ARG BUILD_DATE
ARG BUILD_REF
ARG BUILD_VERSION
# Labels
LABEL \
io.hass.name="esphomeyaml" \
io.hass.description="esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices." \
io.hass.arch="${ADDON_ARCH}" \
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
io.hass.arch="${BUILD_ARCH}" \
io.hass.type="addon" \
io.hass.version="${ADDON_VERSION}" \
io.hass.url="https://esphomelib.com/esphomeyaml/index.html" \
maintainer="Otto Winter <contact@otto-winter.com>"
io.hass.version=${BUILD_VERSION} \
maintainer="Otto Winter <contact@otto-winter.com>" \
org.label-schema.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
org.label-schema.build-date=${BUILD_DATE} \
org.label-schema.name="esphomeyaml" \
org.label-schema.schema-version="1.0" \
org.label-schema.url="https://esphomelib.com" \
org.label-schema.usage="https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml/README.md" \
org.label-schema.vcs-ref=${BUILD_REF} \
org.label-schema.vcs-url="https://github.com/OttoWinter/esphomeyaml" \
org.label-schema.vendor="esphomelib"

View File

@ -3,4 +3,4 @@ FROM python:2.7
COPY requirements.txt /requirements.txt
RUN pip install -r /requirements.txt && \
pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow
pip install flake8==3.6.0 pylint==1.9.4 pillow

View File

@ -8,12 +8,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
platformio settings set enable_telemetry No && \
platformio settings set check_libraries_interval 1000000 && \
platformio settings set check_platformio_interval 1000000 && \
platformio settings set check_platforms_interval 1000000
COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY requirements.txt /requirements.txt
RUN pip install --no-cache-dir -r /requirements.txt && \
pip install --no-cache-dir tzlocal pillow
RUN pip install --no-cache-dir -r /requirements.txt

View File

@ -1,8 +1,8 @@
{
"name": "esphomeyaml-beta",
"version": "1.9.0b5",
"version": "1.9.3",
"slug": "esphomeyaml-beta",
"description": "Beta version of esphomeyaml HassIO add-on.",
"description": "Beta version of esphomeyaml Hass.io add-on.",
"url": "https://beta.esphomelib.com/esphomeyaml/index.html",
"startup": "application",
"webui": "http://[HOST]:[PORT:6052]",

BIN
esphomeyaml-beta/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
esphomeyaml-beta/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -1,24 +1,73 @@
# Dockerfile for HassIO edge add-on
ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest
ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0
# hadolint ignore=DL3006
FROM ${BUILD_FROM}
RUN apt-get update && apt-get install -y --no-install-recommends \
# Set shell
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Copy root filesystem
COPY rootfs /
RUN \
# Temporarily move nginx.conf (otherwise dpkg fails)
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bkp \
# Install add-on dependencies
&& apt-get update \
&& apt-get install -y --no-install-recommends \
# Python for esphomeyaml
python \
python-pip \
python-setuptools \
# Python Pillow for display component
python-pil \
# Git for esphomelib downloads
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No && \
platformio settings set check_libraries_interval 1000000 && \
platformio settings set check_platformio_interval 1000000 && \
platformio settings set check_platforms_interval 1000000
# Ping for dashboard online/offline status
iputils-ping \
# NGINX proxy
nginx \
\
&& mv /etc/nginx/nginx.conf.bkp /etc/nginx/nginx.conf \
\
&& pip2 install --no-cache-dir --no-binary :all: https://github.com/OttoWinter/esphomeyaml/archive/dev.zip \
\
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_libraries_interval 1000000 \
&& platformio settings set check_platformio_interval 1000000 \
&& platformio settings set check_platforms_interval 1000000 \
\
# Build an empty platformio project to force platformio to install all fw build dependencies
# The return-code will be non-zero since there's nothing to build.
&& (platformio run -d /opt/pio; echo "Done") \
\
# Cleanup
&& rm -fr \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/* \
/opt/pio/
COPY platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
# Build arugments
ARG BUILD_ARCH=amd64
ARG BUILD_DATE
ARG BUILD_REF
ARG BUILD_VERSION
RUN pip install --no-cache-dir git+https://github.com/OttoWinter/esphomeyaml.git@dev#egg=esphomeyaml && \
pip install --no-cache-dir pillow tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
# Labels
LABEL \
io.hass.name="esphomeyaml-edge" \
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
io.hass.arch="${BUILD_ARCH}" \
io.hass.type="addon" \
io.hass.version=${BUILD_VERSION} \
maintainer="Otto Winter <contact@otto-winter.com>" \
org.label-schema.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
org.label-schema.build-date=${BUILD_DATE} \
org.label-schema.name="esphomeyaml-edge" \
org.label-schema.schema-version="1.0" \
org.label-schema.url="https://esphomelib.com" \
org.label-schema.usage="https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml-edge/README.md" \
org.label-schema.vcs-ref=${BUILD_REF} \
org.label-schema.vcs-url="https://github.com/OttoWinter/esphomeyaml" \
org.label-schema.vendor="esphomelib"

109
esphomeyaml-edge/README.md Normal file
View File

@ -0,0 +1,109 @@
# Esphomeyaml Hass.io Add-On
[![esphomeyaml logo](https://raw.githubusercontent.com/OttoWinter/esphomeyaml/dev/esphomeyaml-edge/logo.png)](https://esphomelib.com/esphomeyaml/index.html)
[![GitHub stars](https://img.shields.io/github/stars/OttoWinter/esphomelib.svg?style=social&label=Star&maxAge=2592000)](https://github.com/OttoWinter/esphomelib)
[![GitHub Release][releases-shield]][releases]
[![Discord][discord-shield]][discord]
## About
This add-on allows you to manage and program your ESP8266 and ESP32 based microcontrollers
directly through Hass.io **with no programming experience required**. All you need to do
is write YAML configuration files; the rest (over-the-air updates, compiling) is all
handled by esphomeyaml.
<p align="center">
<img title="esphomeyaml dashboard screenshot" src="https://raw.githubusercontent.com/OttoWinter/esphomeyaml/dev/esphomeyaml-edge/images/screenshot.png" width="700px"></img>
</p>
[_View the esphomeyaml documentation here_](https://esphomelib.com/esphomeyaml/index.html)
## Example
With esphomeyaml, you can go from a few lines of YAML straight to a custom-made
firmware. For example, to include a [DHT22][dht22].
temperature and humidity sensor, you just need to include 8 lines of YAML
in your configuration file:
<img title="esphomeyaml DHT configuration example" src="https://raw.githubusercontent.com/OttoWinter/esphomeyaml/dev/esphomeyaml-edge/images/dht-example.png" width="500px"></img>
Then just click UPLOAD and the sensor will magically appear in Home Assistant:
<img title="esphomelib Home Assistant MQTT discovery" src="https://raw.githubusercontent.com/OttoWinter/esphomeyaml/dev/esphomeyaml-edge/images/temperature-humidity.png" width="600px"></img>
## Installation
To install this Hass.io add-on you need to add the esphomeyaml add-on repository
first:
1. Add the epshomeyaml add-ons repository to your Hass.io instance. You can do this by navigating to the "Add-on Store" tab in the Hass.io panel and then entering https://github.com/OttoWinter/esphomeyaml in the "Add new repository by URL" field.
2. Now scroll down and select the "esphomeyaml" add-on.
3. Press install to download the add-on and unpack it on your machine. This can take some time.
4. Optional: If you're using SSL certificates and want to encrypt your communication to this add-on, please enter `true` into the `ssl` field and set the `fullchain` and `certfile` options accordingly.
5. Start the add-on, check the logs of the add-on to see if everything went well.
6. Click "OPEN WEB UI" to open the esphomeyaml dashboard. You will be asked for your Home Assistant credentials - esphomeyaml uses Hass.io's authentication system to log you in.
**NOTE**: Installation on RPis running in 64-bit mode is currently not possible. Please use the 32-bit variant of HassOS instead.
You can view the esphomeyaml docs here: https://esphomelib.com/esphomeyaml/index.html
## Configuration
**Note**: _Remember to restart the add-on when the configuration is changed._
Example add-on configuration:
```json
{
"ssl": false,
"certfile": "fullchain.pem",
"keyfile": "privkey.pem",
"port": 6052
}
```
### Option: `port`
The port to start the dashboard server on. Default is 6052.
### Option: `ssl`
Enables/Disables encrypted SSL (HTTPS) connections to the web server of this add-on.
Set it to `true` to encrypt communications, `false` otherwise.
Please note that if you set this to `true` you must also generate the key and certificate
files for encryption. For example using [Let's Encrypt](https://www.home-assistant.io/addons/lets_encrypt/)
or [Self-signed certificates](https://www.home-assistant.io/docs/ecosystem/certificates/tls_self_signed_certificate/).
### Option: `certfile`
The certificate file to use for SSL. If this file doesn't exist, the add-on start will fail.
**Note**: The file MUST be stored in `/ssl/`, which is the default for Hass.io
### Option: `keyfile`
The private key file to use for SSL. If this file doesn't exist, the add-on start will fail.
**Note**: The file MUST be stored in `/ssl/`, which is the default for Hass.io
### Option: `leave_front_door_open`
Adding this option to the add-on configuration allows you to disable
authentication by setting it to `true`.
### Option: `esphomeyaml_version`
Manually override which esphomeyaml version to use in the addon.
For example to install the latest development version, use `"esphomeyaml_version": "dev"`,
or for version 1.10.0: `"esphomeyaml_version": "v1.10.0""`.
Please note that this does not always work and is only meant for testing, usually the
esphomeyaml add-on and dashboard version must match to guarantee a working system.
[discord-shield]: https://img.shields.io/discord/429907082951524364.svg
[dht22]: https://esphomelib.com/esphomeyaml/components/sensor/dht.html
[discord]: https://discord.me/KhAMKrd
[releases-shield]: https://img.shields.io/github/release/OttoWinter/esphomeyaml.svg
[releases]: https://esphomelib.com/esphomeyaml/changelog/index.html
[repository]: https://github.com/OttoWinter/esphomeyaml

View File

@ -1,10 +1,10 @@
{
"squash": false,
"build_from": {
"aarch64": "homeassistant/aarch64-base-ubuntu:latest",
"amd64": "homeassistant/amd64-base-ubuntu:latest",
"armhf": "homeassistant/armhf-base-ubuntu:latest",
"i386": "homeassistant/i386-base-ubuntu:latest"
},
"args": {}
"squash": false,
"build_from": {
"aarch64": "hassioaddons/ubuntu-base-aarch64:2.2.0",
"amd64": "hassioaddons/ubuntu-base-amd64:2.2.0",
"armhf": "hassioaddons/ubuntu-base-armhf:2.2.0",
"i386": "hassioaddons/ubuntu-base-i386:2.2.0"
},
"args": {}
}

View File

@ -2,32 +2,38 @@
"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",
"description": "Development Version! Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files",
"url": "https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml-edge/README.md",
"webui": "http://[HOST]:[PORT:6052]",
"boot": "auto",
"ports": {
"6052/tcp": 6052,
"6053/tcp": 6053
},
"startup": "application",
"arch": [
"aarch64",
"amd64",
"armhf",
"i386"
],
"auto_uart": true,
"hassio_api": true,
"auth_api": true,
"hassio_role": "default",
"homeassistant_api": false,
"host_network": true,
"boot": "auto",
"map": [
"ssl",
"config:rw"
],
"options": {
"password": ""
"ssl": false,
"certfile": "fullchain.pem",
"keyfile": "privkey.pem",
"port": 6052
},
"schema": {
"password": "str?"
},
"environment": {
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
"ssl": "bool",
"certfile": "str",
"keyfile": "str",
"port": "int",
"leave_front_door_open": "bool?",
"esphomeyaml_version": "str?"
}
}

BIN
esphomeyaml-edge/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
esphomeyaml-edge/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,35 @@
#!/usr/bin/with-contenv bash
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# This files check if all user configuration requirements are met
# ==============================================================================
# shellcheck disable=SC1091
source /usr/lib/hassio-addons/base.sh
# Check SSL requirements, if enabled
if hass.config.true 'ssl'; then
if ! hass.config.has_value 'certfile'; then
hass.die 'SSL is enabled, but no certfile was specified.'
fi
if ! hass.config.has_value 'keyfile'; then
hass.die 'SSL is enabled, but no keyfile was specified'
fi
if ! hass.file_exists "/ssl/$(hass.config.get 'certfile')"; then
if ! hass.file_exists "/ssl/$(hass.config.get 'keyfile')"; then
# Both files are missing, let's print a friendlier error message
text="You enabled encrypted connections using the \"ssl\": true option.
However, the SSL files \"$(hass.config.get 'certfile')\" and \"$(hass.config.get 'keyfile')\"
were not found. If you're using Hass.io on your local network and don't want
to encrypt connections to the esphomeyaml dashboard, you can manually disable
SSL by setting \"ssl\" to false."
hass.die "${text}"
fi
hass.die 'The configured certfile is not found'
fi
if ! hass.file_exists "/ssl/$(hass.config.get 'keyfile')"; then
hass.die 'The configured keyfile is not found'
fi
fi

View File

@ -0,0 +1,28 @@
#!/usr/bin/with-contenv bash
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# Configures NGINX for use with esphomeyaml
# ==============================================================================
# shellcheck disable=SC1091
source /usr/lib/hassio-addons/base.sh
declare certfile
declare keyfile
declare port
mkdir -p /var/log/nginx
# Enable SSL
if hass.config.true 'ssl'; then
rm /etc/nginx/nginx.conf
mv /etc/nginx/nginx-ssl.conf /etc/nginx/nginx.conf
certfile=$(hass.config.get 'certfile')
keyfile=$(hass.config.get 'keyfile')
sed -i "s/%%certfile%%/${certfile}/g" /etc/nginx/nginx.conf
sed -i "s/%%keyfile%%/${keyfile}/g" /etc/nginx/nginx.conf
fi
port=$(hass.config.get 'port')
sed -i "s/%%port%%/${port}/g" /etc/nginx/nginx.conf

View File

@ -0,0 +1,14 @@
#!/usr/bin/with-contenv bash
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# This files installs the user esphomeyaml version if specified
# ==============================================================================
# shellcheck disable=SC1091
source /usr/lib/hassio-addons/base.sh
declare esphomeyaml_version
if hass.config.has_value 'esphomeyaml_version'; then
esphomeyaml_version=$(hass.config.get 'esphomeyaml_version')
pip2 install --no-cache-dir --no-binary :all: "https://github.com/OttoWinter/esphomeyaml/archive/${esphomeyaml_version}.zip"
fi

View File

@ -0,0 +1,62 @@
worker_processes 1;
pid /var/run/nginx.pid;
error_log stderr;
events {
worker_connections 1024;
}
http {
access_log stdout;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream esphomeyaml {
ip_hash;
server unix:/var/run/esphomeyaml.sock;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
server_name hassio.local;
listen %%port%% default_server ssl;
root /dev/null;
ssl_certificate /ssl/%%certfile%%;
ssl_certificate_key /ssl/%%keyfile%%;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
# Redirect http requests to https on the same port.
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
error_page 497 https://$http_host$request_uri;
location / {
proxy_redirect off;
proxy_pass http://esphomeyaml;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Authorization "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
}
}

View File

@ -0,0 +1,46 @@
worker_processes 1;
pid /var/run/nginx.pid;
error_log stderr;
events {
worker_connections 1024;
}
http {
access_log stdout;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream esphomeyaml {
ip_hash;
server unix:/var/run/esphomeyaml.sock;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
server_name hassio.local;
listen %%port%% default_server;
root /dev/null;
location / {
proxy_redirect off;
proxy_pass http://esphomeyaml;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Authorization "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
}
}

View File

@ -0,0 +1,9 @@
#!/usr/bin/execlineb -S0
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# Take down the S6 supervision tree when esphomeyaml fails
# ==============================================================================
if -n { s6-test $# -ne 0 }
if -n { s6-test ${1} -eq 256 }
s6-svscanctl -t /var/run/s6/services

View File

@ -0,0 +1,14 @@
#!/usr/bin/with-contenv bash
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# Runs the esphomeyaml dashboard
# ==============================================================================
# shellcheck disable=SC1091
source /usr/lib/hassio-addons/base.sh
if hass.config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true
fi
hass.log.info "Starting esphomeyaml dashboard..."
exec esphomeyaml /config/esphomeyaml dashboard --socket /var/run/esphomeyaml.sock --hassio

View File

@ -0,0 +1,9 @@
#!/usr/bin/execlineb -S0
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# Take down the S6 supervision tree when NGINX fails
# ==============================================================================
if -n { s6-test $# -ne 0 }
if -n { s6-test ${1} -eq 256 }
s6-svscanctl -t /var/run/s6/services

View File

@ -0,0 +1,10 @@
#!/usr/bin/with-contenv bash
# ==============================================================================
# Community Hass.io Add-ons: esphomeyaml
# Runs the NGINX proxy
# ==============================================================================
# shellcheck disable=SC1091
source /usr/lib/hassio-addons/base.sh
hass.log.info "Starting NGINX..."
exec nginx -g "daemon off;"

View 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

View File

@ -2,25 +2,29 @@ from __future__ import print_function
import argparse
from collections import OrderedDict
from datetime import datetime
import logging
import os
import random
import sys
from datetime import datetime
from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util, platformio_api
from esphomeyaml.config import get_component, iter_components, read_config
from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF_ESPHOMEYAML, \
CONF_HOSTNAME, CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_USE_CUSTOM_CODE, \
CONF_WIFI, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \
_EXPRESSIONS, add, add_job, color, flush_tasks, indent, statement, relative_path
from esphomeyaml.util import safe_print, run_external_command
from esphomeyaml import const, core_config, mqtt, platformio_api, wizard, writer, yaml_util
from esphomeyaml.api.client import run_logs
from esphomeyaml.config import get_component, iter_components, read_config, strip_default_ids
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_LOGGER, CONF_USE_CUSTOM_CODE, \
CONF_BROKER
from esphomeyaml.core import CORE, EsphomeyamlError
from esphomeyaml.cpp_generator import Expression, RawStatement, add, statement
from esphomeyaml.helpers import color, indent
from esphomeyaml.py_compat import safe_input, text_type, IS_PY2
from esphomeyaml.storage_json import StorageJSON, esphomeyaml_storage_path, \
start_update_check_thread, storage_path
from esphomeyaml.util import run_external_command, safe_print
_LOGGER = logging.getLogger(__name__)
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c']
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ethernet', 'ota', 'mqtt', 'web_server', 'api',
'i2c']
def get_serial_ports():
@ -32,37 +36,64 @@ def get_serial_ports():
continue
if "VID:PID" in info:
result.append((port, desc))
result.sort(key=lambda x: x[0])
return result
def choose_serial_port(config):
result = get_serial_ports()
def choose_prompt(options):
if not options:
raise ValueError
if len(options) == 1:
return options[0][1]
safe_print(u"Found multiple options, please choose one:")
for i, (desc, _) in enumerate(options):
safe_print(u" [{}] {}".format(i + 1, desc))
if not result:
return 'OTA'
safe_print(u"Found multiple serial port options, please choose one:")
for i, (res, desc) in enumerate(result):
safe_print(u" [{}] {} ({})".format(i, res, desc))
safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
safe_print()
while True:
opt = raw_input('(number): ')
if opt in result:
opt = result.index(opt)
opt = safe_input('(number): ')
if opt in options:
opt = options.index(opt)
break
try:
opt = int(opt)
if opt < 0 or opt > len(result):
if opt < 1 or opt > len(options):
raise ValueError
break
except ValueError:
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
if opt == len(result):
return 'OTA'
return result[opt][0]
return options[opt - 1][1]
def run_miniterm(config, port, escape=False):
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
options = []
for res, desc in get_serial_ports():
options.append((u"{} ({})".format(res, desc), res))
if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config):
options.append((u"Over The Air ({})".format(CORE.address), CORE.address))
if default == 'OTA':
return CORE.address
if show_mqtt and 'mqtt' in CORE.config:
options.append((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
if default == 'OTA':
return 'MQTT'
if default is not None:
return default
if check_default is not None and check_default in [opt[1] for opt in options]:
return check_default
return choose_prompt(options)
def get_port_type(port):
if port.startswith('/') or port.startswith('COM'):
return 'SERIAL'
if port == 'MQTT':
return 'MQTT'
return 'NETWORK'
def run_miniterm(config, port):
import serial
if CONF_LOGGER not in config:
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
@ -80,11 +111,13 @@ def run_miniterm(config, port, escape=False):
except serial.SerialException:
_LOGGER.error("Serial port closed!")
return
line = raw.replace('\r', '').replace('\n', '')
if IS_PY2:
line = raw.replace('\r', '').replace('\n', '')
else:
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8',
'backslashreplace')
time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line
if escape:
message = message.replace('\033', '\\033')
safe_print(message)
backtrace_state = platformio_api.process_stacktrace(
@ -94,91 +127,65 @@ def run_miniterm(config, port, escape=False):
def write_cpp(config):
_LOGGER.info("Generating C++ source...")
add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml')
CORE.add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml')
for domain in PRE_INITIALIZE:
if domain == CONF_ESPHOMEYAML or domain not in config:
continue
add_job(get_component(domain).to_code, config[domain], domain=domain)
CORE.add_job(get_component(domain).to_code, config[domain], domain=domain)
for domain, component, conf in iter_components(config):
if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'):
continue
add_job(component.to_code, conf, domain=domain)
CORE.add_job(component.to_code, conf, domain=domain)
flush_tasks()
CORE.flush_tasks()
add(RawStatement(''))
add(RawStatement(''))
all_code = []
for exp in _EXPRESSIONS:
for exp in CORE.expressions:
if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
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)))
all_code.append(text_type(statement(exp)))
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
writer.write_platformio_project(config, build_path)
writer.write_platformio_project()
code_s = indent('\n'.join(line.rstrip() for line in all_code))
cpp_path = os.path.join(build_path, 'src', 'main.cpp')
writer.write_cpp(code_s, cpp_path)
writer.write_cpp(code_s)
return 0
def compile_program(args, config):
_LOGGER.info("Compiling app...")
return platformio_api.run_compile(config, args.verbose)
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
update_check = not os.getenv('ESPHOMEYAML_NO_UPDATE_CHECK', '')
if update_check:
thread = start_update_check_thread(esphomeyaml_storage_path(CORE.config_dir))
rc = platformio_api.run_compile(config, args.verbose)
if update_check:
thread.join()
return rc
def upload_using_esptool(config, port):
import esptool
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
path = os.path.join(CORE.build_path, '.pioenvs', CORE.name, 'firmware.bin')
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
# pylint: disable=protected-access
return run_external_command(esptool._main, *cmd)
def upload_program(config, args, port):
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
def upload_program(config, args, host):
# if upload is to a serial port use platformio, otherwise assume ota
serial_port = port.startswith('/') or port.startswith('COM')
if port != 'OTA' and serial_port:
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy:
return upload_using_esptool(config, port)
return platformio_api.run_upload(config, args.verbose, port)
if 'ota' not in config:
_LOGGER.error("No serial port found and OTA not enabled. Can't upload!")
return -1
# If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf
# hostname. This is to support use cases where zeroconf (hostname.local) does not work.
if port != 'OTA':
host = port
else:
host = get_upload_host(config)
if get_port_type(host) == 'SERIAL':
if CORE.is_esp8266:
return upload_using_esptool(config, host)
return platformio_api.run_upload(config, args.verbose, host)
from esphomeyaml.components import ota
from esphomeyaml import espota2
bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
if args.host_port is not None:
host_port = args.host_port
else:
@ -188,20 +195,31 @@ def upload_program(config, args, port):
remote_port = ota.get_port(config)
password = ota.get_auth(config)
res = espota2.run_ota(host, remote_port, password, bin_file)
storage = StorageJSON.load(storage_path())
res = espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
if res == 0:
if storage is not None and storage.use_legacy_ota:
storage.use_legacy_ota = False
storage.save(storage_path())
return res
_LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...")
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, bin_file)
if storage is not None and not storage.use_legacy_ota:
return res
_LOGGER.warning("OTA v2 method failed. Trying with legacy OTA...")
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password,
CORE.firmware_bin)
def show_logs(config, args, port, escape=False):
serial_port = port.startswith('/') or port.startswith('COM')
if port != 'OTA' and serial_port:
run_miniterm(config, port, escape=escape)
def show_logs(config, args, port):
if get_port_type(port) == 'SERIAL':
run_miniterm(config, port)
return 0
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
escape=escape)
if get_port_type(port) == 'NETWORK':
return run_logs(config, port)
if get_port_type(port) == 'MQTT':
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
raise ValueError
def clean_mqtt(config, args):
@ -239,26 +257,8 @@ 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):
_LOGGER.info("Configuration is valid!")
if not args.verbose:
config = strip_default_ids(config)
safe_print(yaml_util.dump(config))
@ -280,7 +280,8 @@ def command_compile(args, config):
def command_upload(args, config):
port = args.upload_port or choose_serial_port(config)
port = choose_upload_log_host(default=args.upload_port, check_default=None,
show_ota=True, show_mqtt=False, show_api=False)
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
@ -289,8 +290,9 @@ def command_upload(args, config):
def command_logs(args, config):
port = args.serial_port or choose_serial_port(config)
return show_logs(config, args, port, escape=args.escape)
port = choose_upload_log_host(default=args.serial_port, check_default=None,
show_ota=False, show_mqtt=True, show_api=True)
return show_logs(config, args, port)
def command_run(args, config):
@ -301,14 +303,17 @@ def command_run(args, config):
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully compiled program.")
port = args.upload_port or choose_serial_port(config)
port = choose_upload_log_host(default=args.upload_port, check_default=None,
show_ota=True, show_mqtt=False, show_api=True)
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)
port = choose_upload_log_host(default=args.upload_port, check_default=port,
show_ota=False, show_mqtt=True, show_api=True)
return show_logs(config, args, port)
def command_clean_mqtt(args, config):
@ -325,9 +330,8 @@ def command_version(args):
def command_clean(args, config):
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
try:
writer.clean_build(build_path)
writer.clean_build()
except OSError as err:
_LOGGER.error("Error deleting build files: %s", err)
return 1
@ -386,6 +390,8 @@ 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('--dashboard', help="Internal flag to set if the command is run from the "
"dashboard.", action='store_true')
parser.add_argument('configuration', help='Your YAML configuration file.')
subparsers = parser.add_subparsers(help='Commands', dest='command')
@ -403,9 +409,6 @@ def parse_args(argv):
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.')
@ -415,8 +418,6 @@ def parse_args(argv):
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.')
@ -429,11 +430,6 @@ def parse_args(argv):
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.")
@ -453,39 +449,49 @@ def parse_args(argv):
dashboard = subparsers.add_parser('dashboard',
help="Create a simple web server for a dashboard.")
dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int,
default=6052)
dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
type=int, default=6052)
dashboard.add_argument("--password", help="The optional password to require for all requests.",
type=str, default='')
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
action='store_true')
dashboard.add_argument("--hassio",
help="Internal flag used to tell esphomeyaml is started as a Hass.io "
"add-on.",
action="store_true")
dashboard.add_argument("--socket",
help="Make the dashboard serve under a unix socket", type=str)
subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added"
"to Home Assistant when not using MQTT discovery.")
subparsers.add_parser('hass-config',
help="Dump the configuration entries that should be added "
"to Home Assistant when not using MQTT discovery.")
return parser.parse_args(argv[1:])
def run_esphomeyaml(argv):
args = parse_args(argv)
CORE.dashboard = args.dashboard
setup_log(args.verbose)
if args.command in PRE_CONFIG_ACTIONS:
try:
return PRE_CONFIG_ACTIONS[args.command](args)
except ESPHomeYAMLError as e:
except EsphomeyamlError as e:
_LOGGER.error(e)
return 1
core.CONFIG_PATH = args.configuration
CORE.config_path = args.configuration
config = read_config(core.CONFIG_PATH)
config = read_config(args.verbose)
if config is None:
return 1
CORE.config = config
if args.command in POST_CONFIG_ACTIONS:
try:
return POST_CONFIG_ACTIONS[args.command](args, config)
except ESPHomeYAMLError as e:
except EsphomeyamlError as e:
_LOGGER.error(e)
return 1
safe_print(u"Unknown command {}".format(args.command))
@ -495,7 +501,7 @@ def run_esphomeyaml(argv):
def main():
try:
return run_esphomeyaml(sys.argv)
except ESPHomeYAMLError as e:
except EsphomeyamlError as e:
_LOGGER.error(e)
return 1
except KeyboardInterrupt:

View File

330
esphomeyaml/api/api.proto Normal file
View File

@ -0,0 +1,330 @@
syntax = "proto3";
// The Home Assistant protocol is structured as a simple
// TCP socket with short binary messages encoded in the protocol buffers format
// First, a message in this protocol has a specific format:
// * VarInt denoting the size of the message object. (type is not part of this)
// * VarInt denoting the type of message.
// * The message object encoded as a ProtoBuf message
// The connection is established in 4 steps:
// * First, the client connects to the server and sends a "Hello Request" identifying itself
// * The server responds with a "Hello Response" and selects the protocol version
// * After receiving this message, the client attempts to authenticate itself using
// the password and a "Connect Request"
// * The server responds with a "Connect Response" and notifies of invalid password.
// If anything in this initial process fails, the connection must immediately closed
// by both sides and _no_ disconnection message is to be sent.
// Message sent at the beginning of each connection
// Can only be sent by the client and only at the beginning of the connection
message HelloRequest {
// Description of client (like User Agent)
// For example "Home Assistant"
// Not strictly necessary to send but nice for debugging
// purposes.
string client_info = 1;
}
// Confirmation of successful connection request.
// Can only be sent by the server and only at the beginning of the connection
message HelloResponse {
// The version of the API to use. The _client_ (for example Home Assistant) needs to check
// for compatibility and if necessary adopt to an older API.
// Major is for breaking changes in the base protocol - a mismatch will lead to immediate disconnect_client_
// Minor is for breaking changes in individual messages - a mismatch will lead to a warning message
uint32 api_version_major = 1;
uint32 api_version_minor = 2;
// A string identifying the server (ESP); like client info this may be empty
// and only exists for debugging/logging purposes.
// For example "ESPHome v1.10.0 on ESP8266"
string server_info = 3;
}
// Message sent at the beginning of each connection to authenticate the client
// Can only be sent by the client and only at the beginning of the connection
message ConnectRequest {
// The password to log in with
string password = 1;
}
// Confirmation of successful connection. After this the connection is available for all traffic.
// Can only be sent by the server and only at the beginning of the connection
message ConnectResponse {
bool invalid_password = 1;
}
// Request to close the connection.
// Can be sent by both the client and server
message DisconnectRequest {
// Do not close the connection before the acknowledgement arrives
}
message DisconnectResponse {
// Empty - Both parties are required to close the connection after this
// message has been received.
}
message PingRequest {
// Empty
}
message PingResponse {
// Empty
}
message DeviceInfoRequest {
// Empty
}
message DeviceInfoResponse {
bool uses_password = 1;
// The name of the node, given by "App.set_name()"
string name = 2;
// The mac address of the device. For example "AC:BC:32:89:0E:A9"
string mac_address = 3;
// A string describing the ESPHome version. For example "1.10.0"
string esphome_core_version = 4;
// A string describing the date of compilation, this is generated by the compiler
// and therefore may not be in the same format all the time.
// If the user isn't using esphomeyaml, this will also not be set.
string compilation_time = 5;
// The model of the board. For example NodeMCU
string model = 6;
bool has_deep_sleep = 7;
}
message ListEntitiesRequest {
// Empty
}
message ListEntitiesBinarySensorResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string device_class = 5;
bool is_status_binary_sensor = 6;
}
message ListEntitiesCoverResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
bool is_optimistic = 5;
}
message ListEntitiesFanResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
bool supports_oscillation = 5;
bool supports_speed = 6;
}
message ListEntitiesLightResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
bool supports_brightness = 5;
bool supports_rgb = 6;
bool supports_white_value = 7;
bool supports_color_temperature = 8;
float min_mireds = 9;
float max_mireds = 10;
repeated string effects = 11;
}
message ListEntitiesSensorResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
string unit_of_measurement = 6;
int32 accuracy_decimals = 7;
}
message ListEntitiesSwitchResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool optimistic = 6;
}
message ListEntitiesTextSensorResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
}
message ListEntitiesDoneResponse {
// Empty
}
message SubscribeStatesRequest {
// Empty
}
message BinarySensorStateResponse {
fixed32 key = 1;
bool state = 2;
}
message CoverStateResponse {
fixed32 key = 1;
enum CoverState {
OPEN = 0;
CLOSED = 1;
}
CoverState state = 2;
}
enum FanSpeed {
LOW = 0;
MEDIUM = 1;
HIGH = 2;
}
message FanStateResponse {
fixed32 key = 1;
bool state = 2;
bool oscillating = 3;
FanSpeed speed = 4;
}
message LightStateResponse {
fixed32 key = 1;
bool state = 2;
float brightness = 3;
float red = 4;
float green = 5;
float blue = 6;
float white = 7;
float color_temperature = 8;
string effect = 9;
}
message SensorStateResponse {
fixed32 key = 1;
float state = 2;
}
message SwitchStateResponse {
fixed32 key = 1;
bool state = 2;
}
message TextSensorStateResponse {
fixed32 key = 1;
string state = 2;
}
message CoverCommandRequest {
fixed32 key = 1;
enum CoverCommand {
OPEN = 0;
CLOSE = 1;
STOP = 2;
}
bool has_state = 2;
CoverCommand command = 3;
}
message FanCommandRequest {
fixed32 key = 1;
bool has_state = 2;
bool state = 3;
bool has_speed = 4;
FanSpeed speed = 5;
bool has_oscillating = 6;
bool oscillating = 7;
}
message LightCommandRequest {
fixed32 key = 1;
bool has_state = 2;
bool state = 3;
bool has_brightness = 4;
float brightness = 5;
bool has_rgb = 6;
float red = 7;
float green = 8;
float blue = 9;
bool has_white = 10;
float white = 11;
bool has_color_temperature = 12;
float color_temperature = 13;
bool has_transition_length = 14;
uint32 transition_length = 15;
bool has_flash_length = 16;
uint32 flash_length = 17;
bool has_effect = 18;
string effect = 19;
}
message SwitchCommandRequest {
fixed32 key = 1;
bool state = 2;
}
enum LogLevel {
NONE = 0;
ERROR = 1;
WARN = 2;
INFO = 3;
DEBUG = 4;
VERBOSE = 5;
VERY_VERBOSE = 6;
}
message SubscribeLogsRequest {
LogLevel level = 1;
bool dump_config = 2;
}
message SubscribeLogsResponse {
LogLevel level = 1;
string tag = 2;
string message = 3;
bool send_failed = 4;
}
message SubscribeServiceCallsRequest {
}
message ServiceCallResponse {
string service = 1;
map<string, string> data = 2;
map<string, string> data_template = 3;
map<string, string> variables = 4;
}
// 1. Client sends SubscribeHomeAssistantStatesRequest
// 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async)
// 3. Client sends HomeAssistantStateResponse for state changes.
message SubscribeHomeAssistantStatesRequest {
}
message SubscribeHomeAssistantStateResponse {
string entity_id = 1;
}
message HomeAssistantStateResponse {
string entity_id = 1;
string state = 2;
}
message GetTimeRequest {
}
message GetTimeResponse {
fixed32 epoch_seconds = 1;
}

2484
esphomeyaml/api/api_pb2.py Normal file

File diff suppressed because one or more lines are too long

490
esphomeyaml/api/client.py Normal file
View File

@ -0,0 +1,490 @@
from datetime import datetime
import functools
import logging
import socket
import threading
import time
# pylint: disable=unused-import
from typing import Optional # noqa
from google.protobuf import message # noqa
from esphomeyaml import const
import esphomeyaml.api.api_pb2 as pb
from esphomeyaml.const import CONF_PASSWORD, CONF_PORT
from esphomeyaml.core import EsphomeyamlError
from esphomeyaml.helpers import resolve_ip_address, indent, color
from esphomeyaml.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte, format_bytes
from esphomeyaml.util import safe_print
_LOGGER = logging.getLogger(__name__)
class APIConnectionError(EsphomeyamlError):
pass
MESSAGE_TYPE_TO_PROTO = {
1: pb.HelloRequest,
2: pb.HelloResponse,
3: pb.ConnectRequest,
4: pb.ConnectResponse,
5: pb.DisconnectRequest,
6: pb.DisconnectResponse,
7: pb.PingRequest,
8: pb.PingResponse,
9: pb.DeviceInfoRequest,
10: pb.DeviceInfoResponse,
11: pb.ListEntitiesRequest,
12: pb.ListEntitiesBinarySensorResponse,
13: pb.ListEntitiesCoverResponse,
14: pb.ListEntitiesFanResponse,
15: pb.ListEntitiesLightResponse,
16: pb.ListEntitiesSensorResponse,
17: pb.ListEntitiesSwitchResponse,
18: pb.ListEntitiesTextSensorResponse,
19: pb.ListEntitiesDoneResponse,
20: pb.SubscribeStatesRequest,
21: pb.BinarySensorStateResponse,
22: pb.CoverStateResponse,
23: pb.FanStateResponse,
24: pb.LightStateResponse,
25: pb.SensorStateResponse,
26: pb.SwitchStateResponse,
27: pb.TextSensorStateResponse,
28: pb.SubscribeLogsRequest,
29: pb.SubscribeLogsResponse,
30: pb.CoverCommandRequest,
31: pb.FanCommandRequest,
32: pb.LightCommandRequest,
33: pb.SwitchCommandRequest,
34: pb.SubscribeServiceCallsRequest,
35: pb.ServiceCallResponse,
36: pb.GetTimeRequest,
37: pb.GetTimeResponse,
}
def _varuint_to_bytes(value):
if value <= 0x7F:
return byte_to_bytes(value)
ret = bytes()
while value:
temp = value & 0x7F
value >>= 7
if value:
ret += byte_to_bytes(temp | 0x80)
else:
ret += byte_to_bytes(temp)
return ret
def _bytes_to_varuint(value):
result = 0
bitpos = 0
for c in value:
val = char_to_byte(c)
result |= (val & 0x7F) << bitpos
bitpos += 7
if (val & 0x80) == 0:
return result
return None
# pylint: disable=too-many-instance-attributes,not-callable
class APIClient(threading.Thread):
def __init__(self, address, port, password):
threading.Thread.__init__(self)
self._address = address # type: str
self._port = port # type: int
self._password = password # type: Optional[str]
self._socket = None # type: Optional[socket.socket]
self._socket_open_event = threading.Event()
self._socket_write_lock = threading.Lock()
self._connected = False
self._authenticated = False
self._message_handlers = []
self._keepalive = 5
self._ping_timer = None
self._refresh_ping()
self.on_disconnect = None
self.on_connect = None
self.on_login = None
self.auto_reconnect = False
self._running_event = threading.Event()
self._stop_event = threading.Event()
@property
def stopped(self):
return self._stop_event.is_set()
def _refresh_ping(self):
if self._ping_timer is not None:
self._ping_timer.cancel()
self._ping_timer = None
def func():
self._ping_timer = None
if self._connected:
try:
self.ping()
except APIConnectionError:
self._fatal_error()
else:
self._refresh_ping()
self._ping_timer = threading.Timer(self._keepalive, func)
self._ping_timer.start()
def _cancel_ping(self):
if self._ping_timer is not None:
self._ping_timer.cancel()
self._ping_timer = None
def _close_socket(self):
self._cancel_ping()
if self._socket is not None:
self._socket.close()
self._socket = None
self._socket_open_event.clear()
self._connected = False
self._authenticated = False
self._message_handlers = []
def stop(self, force=False):
if self.stopped:
raise ValueError
if self._connected and not force:
try:
self.disconnect()
except APIConnectionError:
pass
self._close_socket()
self._stop_event.set()
if not force:
self.join()
def connect(self):
if not self._running_event.wait(0.1):
raise APIConnectionError("You need to call start() first!")
if self._connected:
raise APIConnectionError("Already connected!")
try:
ip = resolve_ip_address(self._address)
except EsphomeyamlError as err:
_LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?",
self._address)
_LOGGER.warning("(If this error persists, please set a static IP address: "
"https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips)")
raise APIConnectionError(err)
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.settimeout(10.0)
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
self._socket.connect((ip, self._port))
except socket.error as err:
self._fatal_error()
raise APIConnectionError("Error connecting to {}: {}".format(ip, err))
self._socket.settimeout(0.1)
self._socket_open_event.set()
hello = pb.HelloRequest()
hello.client_info = 'esphomeyaml v{}'.format(const.__version__)
try:
resp = self._send_message_await_response(hello, pb.HelloResponse)
except APIConnectionError as err:
self._fatal_error()
raise err
_LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address,
resp.server_info, resp.api_version_major, resp.api_version_minor)
self._connected = True
if self.on_connect is not None:
self.on_connect()
def _check_connected(self):
if not self._connected:
self._fatal_error()
raise APIConnectionError("Must be connected!")
def login(self):
self._check_connected()
if self._authenticated:
raise APIConnectionError("Already logged in!")
connect = pb.ConnectRequest()
if self._password is not None:
connect.password = self._password
resp = self._send_message_await_response(connect, pb.ConnectResponse)
if resp.invalid_password:
raise APIConnectionError("Invalid password!")
self._authenticated = True
if self.on_login is not None:
self.on_login()
def _fatal_error(self):
was_connected = self._connected
self._close_socket()
if was_connected and self.on_disconnect is not None:
self.on_disconnect()
def _write(self, data): # type: (bytes) -> None
if self._socket is None:
raise APIConnectionError("Socket closed")
_LOGGER.debug("Write: %s", format_bytes(data))
with self._socket_write_lock:
try:
self._socket.sendall(data)
except socket.error as err:
self._fatal_error()
raise APIConnectionError("Error while writing data: {}".format(err))
def _send_message(self, msg):
# type: (message.Message) -> None
for message_type, klass in MESSAGE_TYPE_TO_PROTO.items():
if isinstance(msg, klass):
break
else:
raise ValueError
encoded = msg.SerializeToString()
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg)))
if IS_PY2:
req = chr(0x00)
else:
req = bytes([0])
req += _varuint_to_bytes(len(encoded))
req += _varuint_to_bytes(message_type)
req += encoded
self._write(req)
self._refresh_ping()
def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=1):
event = threading.Event()
responses = []
def on_message(resp):
if do_append(resp):
responses.append(resp)
if do_stop(resp):
event.set()
self._message_handlers.append(on_message)
self._send_message(send_msg)
ret = event.wait(timeout)
try:
self._message_handlers.remove(on_message)
except ValueError:
pass
if not ret:
raise APIConnectionError("Timeout while waiting for message response!")
return responses
def _send_message_await_response(self, send_msg, response_type, timeout=1):
def is_response(msg):
return isinstance(msg, response_type)
return self._send_message_await_response_complex(send_msg, is_response, is_response,
timeout)[0]
def device_info(self):
self._check_connected()
return self._send_message_await_response(pb.DeviceInfoRequest(), pb.DeviceInfoResponse)
def ping(self):
self._check_connected()
return self._send_message_await_response(pb.PingRequest(), pb.PingResponse)
def disconnect(self):
self._check_connected()
try:
self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse)
except APIConnectionError:
pass
self._close_socket()
if self.on_disconnect is not None:
self.on_disconnect()
def _check_authenticated(self):
if not self._authenticated:
raise APIConnectionError("Must login first!")
def subscribe_logs(self, on_log, log_level=None, dump_config=False):
self._check_authenticated()
def on_msg(msg):
if isinstance(msg, pb.SubscribeLogsResponse):
on_log(msg)
self._message_handlers.append(on_msg)
req = pb.SubscribeLogsRequest(dump_config=dump_config)
if log_level is not None:
req.level = log_level
self._send_message(req)
def _recv(self, amount):
ret = bytes()
if amount == 0:
return ret
while len(ret) < amount:
if self.stopped:
raise APIConnectionError("Stopped!")
if not self._socket_open_event.is_set():
raise APIConnectionError("No socket!")
try:
val = self._socket.recv(amount - len(ret))
except AttributeError:
raise APIConnectionError("Socket was closed")
except socket.timeout:
continue
except socket.error as err:
raise APIConnectionError("Error while receiving data: {}".format(err))
ret += val
return ret
def _recv_varint(self):
raw = bytes()
while not raw or char_to_byte(raw[-1]) & 0x80:
raw += self._recv(1)
return _bytes_to_varuint(raw)
def _run_once(self):
if not self._socket_open_event.wait(0.1):
return
# Preamble
if char_to_byte(self._recv(1)[0]) != 0x00:
raise APIConnectionError("Invalid preamble")
length = self._recv_varint()
msg_type = self._recv_varint()
raw_msg = self._recv(length)
if msg_type not in MESSAGE_TYPE_TO_PROTO:
_LOGGER.debug("Skipping message type %s", msg_type)
return
msg = MESSAGE_TYPE_TO_PROTO[msg_type]()
msg.ParseFromString(raw_msg)
_LOGGER.debug("Got message: %s:\n%s", type(msg), indent(str(msg)))
for msg_handler in self._message_handlers[:]:
msg_handler(msg)
self._handle_internal_messages(msg)
self._refresh_ping()
def run(self):
self._running_event.set()
while not self.stopped:
try:
self._run_once()
except APIConnectionError as err:
if self.stopped:
break
if self._connected:
_LOGGER.error("Error while reading incoming messages: %s", err)
self._fatal_error()
self._running_event.clear()
def _handle_internal_messages(self, msg):
if isinstance(msg, pb.DisconnectRequest):
self._send_message(pb.DisconnectResponse())
if self._socket is not None:
self._socket.close()
self._socket = None
self._connected = False
if self.on_disconnect is not None:
self.on_disconnect()
elif isinstance(msg, pb.PingRequest):
self._send_message(pb.PingResponse())
elif isinstance(msg, pb.GetTimeRequest):
resp = pb.GetTimeResponse()
resp.epoch_seconds = int(time.time())
self._send_message(resp)
def run_logs(config, address):
conf = config['api']
port = conf[CONF_PORT]
password = conf[CONF_PASSWORD]
_LOGGER.info("Starting log output from %s using esphomelib API", address)
cli = APIClient(address, port, password)
stopping = False
retry_timer = []
def try_connect(tries=0, is_disconnect=True):
if stopping:
return
if is_disconnect:
_LOGGER.warning(u"Disconnected from API.")
while retry_timer:
retry_timer.pop(0).cancel()
error = None
try:
cli.connect()
cli.login()
except APIConnectionError as err: # noqa
error = err
if error is None:
_LOGGER.info("Successfully connected to %s", address)
return
wait_time = min(2**tries, 300)
_LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
error, wait_time)
timer = threading.Timer(wait_time, functools.partial(try_connect, tries + 1, is_disconnect))
timer.start()
retry_timer.append(timer)
def on_log(msg):
time_ = datetime.now().time().strftime(u'[%H:%M:%S]')
text = msg.message
if msg.send_failed:
text = color('white', '(Message skipped because it was too big to fit in '
'TCP buffer - This is only cosmetic)')
safe_print(time_ + text)
has_connects = []
def on_login():
try:
cli.subscribe_logs(on_log, dump_config=not has_connects)
has_connects.append(True)
except APIConnectionError:
cli.disconnect()
cli.on_disconnect = try_connect
cli.on_login = on_login
cli.start()
try:
try_connect(is_disconnect=False)
while True:
time.sleep(1)
except KeyboardInterrupt:
stopping = True
cli.stop(True)
while retry_timer:
retry_timer.pop(0).cancel()
return 0

View File

@ -2,16 +2,15 @@ import copy
import voluptuous as vol
from esphomeyaml import core
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \
CONF_BELOW, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, \
CONF_ELSE, CONF_ID, CONF_IF, CONF_LAMBDA, \
CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, add_job, \
esphomelib_ns, float_, process_lambda, templatable, uint32, get_variable, PollingComponent, \
Action, Component, Trigger
CONF_BELOW, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, CONF_ELSE, CONF_ID, CONF_IF, \
CONF_LAMBDA, CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID, CONF_WHILE
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, TemplateArguments, add, \
get_variable, process_lambda, templatable
from esphomeyaml.cpp_types import Action, App, Component, PollingComponent, Trigger, \
esphomelib_ns, float_, uint32, void, bool_
from esphomeyaml.util import ServiceRegistry
@ -27,41 +26,82 @@ def maybe_simple_id(*validators):
def validate_recursive_condition(value):
return CONDITIONS_SCHEMA(value)
is_list = isinstance(value, list)
value = cv.ensure_list()(value)[:]
for i, item in enumerate(value):
path = [i] if is_list else []
item = copy.deepcopy(item)
if not isinstance(item, dict):
raise vol.Invalid(u"Condition must consist of key-value mapping! Got {}".format(item),
path)
key = next((x for x in item if x != CONF_CONDITION_ID), None)
if key is None:
raise vol.Invalid(u"Key missing from action! Got {}".format(item), path)
if key not in CONDITION_REGISTRY:
raise vol.Invalid(u"Unable to find condition with the name '{}', is the "
u"component loaded?".format(key), path + [key])
item.setdefault(CONF_CONDITION_ID, None)
key2 = next((x for x in item if x not in (CONF_CONDITION_ID, key)), None)
if key2 is not None:
raise vol.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! "
u"Did you forget to indent the block inside the condition?"
u"".format(key, key2), path)
validator = CONDITION_REGISTRY[key][0]
try:
condition = validator(item[key])
except vol.Invalid as err:
err.prepend(path)
raise err
value[i] = {
CONF_CONDITION_ID: cv.declare_variable_id(Condition)(item[CONF_CONDITION_ID]),
key: condition,
}
return value
def validate_recursive_action(value):
value = cv.ensure_list(value)[:]
is_list = isinstance(value, list)
if not is_list:
value = [value]
for i, item in enumerate(value):
path = [i] if is_list else []
item = copy.deepcopy(item)
if not isinstance(item, dict):
raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item))
raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item),
path)
key = next((x for x in item if x != CONF_ACTION_ID), None)
if key is None:
raise vol.Invalid(u"Key missing from action! Got {}".format(item))
raise vol.Invalid(u"Key missing from action! Got {}".format(item), path)
if key not in ACTION_REGISTRY:
raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?"
u"".format(key))
u"".format(key), path + [key])
item.setdefault(CONF_ACTION_ID, None)
key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None)
key2 = next((x for x in item if x not in (CONF_ACTION_ID, key)), None)
if key2 is not None:
raise vol.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! "
u"Did you forget to indent the action?"
u"".format(key, key2))
u"Did you forget to indent the block inside the action?"
u"".format(key, key2), path)
validator = ACTION_REGISTRY[key][0]
try:
action = validator(item[key])
except vol.Invalid as err:
err.prepend(path)
raise err
value[i] = {
CONF_ACTION_ID: cv.declare_variable_id(Action)(item[CONF_ACTION_ID]),
key: validator(item[key])
key: action,
}
return value
ACTION_REGISTRY = ServiceRegistry()
CONDITION_REGISTRY = ServiceRegistry()
# pylint: disable=invalid-name
DelayAction = esphomelib_ns.class_('DelayAction', Action, Component)
LambdaAction = esphomelib_ns.class_('LambdaAction', Action)
IfAction = esphomelib_ns.class_('IfAction', Action)
WhileAction = esphomelib_ns.class_('WhileAction', Action)
UpdateComponentAction = esphomelib_ns.class_('UpdateComponentAction', Action)
Automation = esphomelib_ns.class_('Automation')
@ -71,17 +111,6 @@ OrCondition = esphomelib_ns.class_('OrCondition', Condition)
RangeCondition = esphomelib_ns.class_('RangeCondition', Condition)
LambdaCondition = esphomelib_ns.class_('LambdaCondition', Condition)
CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [cv.templatable({
cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(Condition),
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_,
})])
def validate_automation(extra_schema=None, extra_validators=None, single=False):
schema = AUTOMATION_SCHEMA.extend(extra_schema or {})
@ -122,63 +151,63 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
AUTOMATION_SCHEMA = vol.Schema({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(Trigger),
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(Automation),
vol.Optional(CONF_IF): CONDITIONS_SCHEMA,
vol.Optional(CONF_IF): validate_recursive_condition,
vol.Required(CONF_THEN): validate_recursive_action,
})
AND_CONDITION_SCHEMA = validate_recursive_condition
def build_condition(config, arg_type):
template_arg = TemplateArguments(arg_type)
if isinstance(config, core.Lambda):
lambda_ = None
for lambda_ in process_lambda(config, [(arg_type, 'x')]):
@CONDITION_REGISTRY.register(CONF_AND, AND_CONDITION_SCHEMA)
def and_condition_to_code(config, condition_id, arg_type, template_arg):
for conditions in build_conditions(config, arg_type):
yield
rhs = AndCondition.new(template_arg, conditions)
type = AndCondition.template(template_arg)
yield Pvariable(condition_id, rhs, type=type)
OR_CONDITION_SCHEMA = validate_recursive_condition
@CONDITION_REGISTRY.register(CONF_OR, OR_CONDITION_SCHEMA)
def or_condition_to_code(config, condition_id, arg_type, template_arg):
for conditions in build_conditions(config, arg_type):
yield
rhs = OrCondition.new(template_arg, conditions)
type = OrCondition.template(template_arg)
yield Pvariable(condition_id, rhs, type=type)
RANGE_CONDITION_SCHEMA = vol.All(vol.Schema({
vol.Optional(CONF_ABOVE): cv.templatable(cv.float_),
vol.Optional(CONF_BELOW): cv.templatable(cv.float_),
}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW))
@CONDITION_REGISTRY.register(CONF_RANGE, RANGE_CONDITION_SCHEMA)
def range_condition_to_code(config, condition_id, arg_type, template_arg):
for conditions in build_conditions(config, arg_type):
yield
rhs = RangeCondition.new(template_arg, conditions)
type = RangeCondition.template(template_arg)
condition = Pvariable(condition_id, rhs, type=type)
if CONF_ABOVE in config:
for template_ in templatable(config[CONF_ABOVE], arg_type, float_):
yield
yield LambdaCondition.new(template_arg, lambda_)
elif 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')]):
condition.set_min(template_)
if CONF_BELOW in config:
for template_ in templatable(config[CONF_BELOW], arg_type, float_):
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)
condition.set_max(template_)
yield condition
DELAY_ACTION_SCHEMA = cv.templatable(cv.positive_time_period_milliseconds)
@ACTION_REGISTRY.register(CONF_DELAY, DELAY_ACTION_SCHEMA)
def delay_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def delay_action_to_code(config, action_id, arg_type, template_arg):
rhs = App.register_component(DelayAction.new(template_arg))
type = DelayAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
@ -196,8 +225,7 @@ IF_ACTION_SCHEMA = vol.All({
@ACTION_REGISTRY.register(CONF_IF, IF_ACTION_SCHEMA)
def if_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def if_action_to_code(config, action_id, arg_type, template_arg):
for conditions in build_conditions(config[CONF_CONDITION], arg_type):
yield None
rhs = IfAction.new(template_arg, conditions)
@ -214,19 +242,49 @@ def if_action_to_code(config, action_id, arg_type):
yield action
WHILE_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_CONDITION): validate_recursive_condition,
vol.Required(CONF_THEN): validate_recursive_action,
})
@ACTION_REGISTRY.register(CONF_WHILE, WHILE_ACTION_SCHEMA)
def while_action_to_code(config, action_id, arg_type, template_arg):
for conditions in build_conditions(config[CONF_CONDITION], arg_type):
yield None
rhs = WhileAction.new(template_arg, conditions)
type = WhileAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
for actions in build_actions(config[CONF_THEN], arg_type):
yield None
add(action.add_then(actions))
yield action
LAMBDA_ACTION_SCHEMA = cv.lambda_
@ACTION_REGISTRY.register(CONF_LAMBDA, LAMBDA_ACTION_SCHEMA)
def lambda_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for lambda_ in process_lambda(config, [(arg_type, 'x')]):
def lambda_action_to_code(config, action_id, arg_type, template_arg):
for lambda_ in process_lambda(config, [(arg_type, 'x')], return_type=void):
yield None
rhs = LambdaAction.new(template_arg, lambda_)
type = LambdaAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)
LAMBDA_CONDITION_SCHEMA = cv.lambda_
@CONDITION_REGISTRY.register(CONF_LAMBDA, LAMBDA_CONDITION_SCHEMA)
def lambda_condition_to_code(config, condition_id, arg_type, template_arg):
for lambda_ in process_lambda(config, [(arg_type, 'x')], return_type=bool_):
yield
rhs = LambdaCondition.new(template_arg, lambda_)
type = LambdaCondition.template(template_arg)
yield Pvariable(condition_id, rhs, type=type)
CONF_COMPONENT_UPDATE = 'component.update'
COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(PollingComponent),
@ -234,8 +292,7 @@ COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA)
def component_update_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def component_update_action_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = UpdateComponentAction.new(var)
@ -248,7 +305,8 @@ def build_action(full_config, arg_type):
key, config = next((k, v) for k, v in full_config.items() if k in ACTION_REGISTRY)
builder = ACTION_REGISTRY[key][1]
for result in builder(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for result in builder(config, action_id, arg_type, template_arg):
yield None
yield result
@ -263,6 +321,26 @@ def build_actions(config, arg_type):
yield ArrayInitializer(*actions, multiline=False)
def build_condition(full_config, arg_type):
action_id = full_config[CONF_CONDITION_ID]
key, config = next((k, v) for k, v in full_config.items() if k in CONDITION_REGISTRY)
builder = CONDITION_REGISTRY[key][1]
template_arg = TemplateArguments(arg_type)
for result in builder(config, action_id, arg_type, template_arg):
yield None
yield result
def build_conditions(config, arg_type):
conditions = []
for conf in config:
for condition in build_condition(conf, arg_type):
yield None
conditions.append(condition)
yield ArrayInitializer(*conditions, multiline=False)
def build_automation_(trigger, arg_type, config):
rhs = App.make_automation(TemplateArguments(arg_type), trigger)
type = Automation.template(arg_type)
@ -280,4 +358,4 @@ def build_automation_(trigger, arg_type, config):
def build_automation(trigger, arg_type, config):
add_job(build_automation_, trigger, arg_type, config)
CORE.add_job(build_automation_, trigger, arg_type, config)

View File

@ -1,27 +1,27 @@
import voluptuous as vol
from esphomeyaml.components import sensor, i2c
from esphomeyaml.components import i2c, sensor
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_ID
from esphomeyaml.helpers import App, Pvariable, setup_component, Component
from esphomeyaml.cpp_generator import Pvariable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component
DEPENDENCIES = ['i2c']
MULTI_CONF = True
ADS1115Component = sensor.sensor_ns.class_('ADS1115Component', Component, i2c.I2CDevice)
ADS1115_SCHEMA = vol.Schema({
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(ADS1115Component),
vol.Required(CONF_ADDRESS): cv.i2c_address,
}).extend(cv.COMPONENT_SCHEMA.schema)
CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
def to_code(config):
for conf in config:
rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
var = Pvariable(conf[CONF_ID], rhs)
setup_component(var, conf)
rhs = App.make_ads1115_component(config[CONF_ADDRESS])
var = Pvariable(config[CONF_ID], rhs)
setup_component(var, config)
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View File

@ -0,0 +1,33 @@
import voluptuous as vol
from esphomeyaml.components import i2c, sensor
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_UPDATE_INTERVAL
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, PollingComponent
DEPENDENCIES = ['i2c']
MULTI_CONF = True
CONF_APDS9960_ID = 'apds9960_id'
APDS9960 = sensor.sensor_ns.class_('APDS9960', PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(APDS9960),
vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}).extend(cv.COMPONENT_SCHEMA.schema)
def to_code(config):
rhs = App.make_apds9960(config.get(CONF_UPDATE_INTERVAL))
var = Pvariable(config[CONF_ID], rhs)
if CONF_ADDRESS in config:
add(var.set_address(config[CONF_ADDRESS]))
setup_component(var, config)
BUILD_FLAGS = '-DUSE_APDS9960'

View File

@ -0,0 +1,88 @@
import voluptuous as vol
from esphomeyaml.automation import ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
CONF_SERVICE, CONF_VARIABLES, CONF_REBOOT_TIMEOUT
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add, get_variable, process_lambda
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import Action, App, Component, StoringController, esphomelib_ns
api_ns = esphomelib_ns.namespace('api')
APIServer = api_ns.class_('APIServer', Component, StoringController)
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', Action)
KeyValuePair = api_ns.class_('KeyValuePair')
TemplatableKeyValuePair = api_ns.class_('TemplatableKeyValuePair')
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(APIServer),
vol.Optional(CONF_PORT, default=6053): cv.port,
vol.Optional(CONF_PASSWORD, default=''): cv.string_strict,
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA.schema)
def to_code(config):
rhs = App.init_api_server()
api = Pvariable(config[CONF_ID], rhs)
if config[CONF_PORT] != 6053:
add(api.set_port(config[CONF_PORT]))
if config.get(CONF_PASSWORD):
add(api.set_password(config[CONF_PASSWORD]))
if CONF_REBOOT_TIMEOUT in config:
add(api.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
setup_component(api, config)
BUILD_FLAGS = '-DUSE_API'
def lib_deps(config):
if CORE.is_esp32:
return 'AsyncTCP@1.0.1'
if CORE.is_esp8266:
return 'ESPAsyncTCP@1.1.3'
raise NotImplementedError
CONF_HOMEASSISTANT_SERVICE = 'homeassistant.service'
HOMEASSISTANT_SERVIC_ACTION_SCHEMA = vol.Schema({
cv.GenerateID(): cv.use_variable_id(APIServer),
vol.Required(CONF_SERVICE): cv.string,
vol.Optional(CONF_DATA): vol.Schema({
cv.string: cv.string,
}),
vol.Optional(CONF_DATA_TEMPLATE): vol.Schema({
cv.string: cv.string,
}),
vol.Optional(CONF_VARIABLES): vol.Schema({
cv.string: cv.lambda_,
}),
})
@ACTION_REGISTRY.register(CONF_HOMEASSISTANT_SERVICE, HOMEASSISTANT_SERVIC_ACTION_SCHEMA)
def homeassistant_service_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_home_assistant_service_call_action(template_arg)
type = HomeAssistantServiceCallAction.template(arg_type)
act = Pvariable(action_id, rhs, type=type)
add(act.set_service(config[CONF_SERVICE]))
if CONF_DATA in config:
datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA].items()]
add(act.set_data(ArrayInitializer(*datas)))
if CONF_DATA_TEMPLATE in config:
datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA_TEMPLATE].items()]
add(act.set_data_template(ArrayInitializer(*datas)))
if CONF_VARIABLES in config:
datas = []
for key, value in config[CONF_VARIABLES].items():
for value_ in process_lambda(value, []):
yield None
datas.append(TemplatableKeyValuePair(key, value_))
add(act.set_variables(ArrayInitializer(*datas)))
yield act

View File

@ -1,16 +1,21 @@
import voluptuous as vol
from esphomeyaml import automation, core
from esphomeyaml.automation import maybe_simple_id, CONDITION_REGISTRY, Condition
from esphomeyaml.components import mqtt
from esphomeyaml.components.mqtt import setup_mqtt_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DELAYED_OFF, CONF_DELAYED_ON, CONF_DEVICE_CLASS, CONF_FILTERS, \
CONF_HEARTBEAT, CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERT, CONF_INVERTED, \
CONF_LAMBDA, CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, \
CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_STATE, \
CONF_TIMING, CONF_TRIGGER_ID
from esphomeyaml.helpers import App, ArrayInitializer, NoArg, Pvariable, StructInitializer, add, \
add_job, bool_, esphomelib_ns, process_lambda, setup_mqtt_component, Nameable, Trigger, \
Component
CONF_TIMING, CONF_TRIGGER_ID, CONF_ON_STATE
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import process_lambda, ArrayInitializer, add, Pvariable, \
StructInitializer, get_variable
from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, NoArg, Component, App, bool_, \
optional
from esphomeyaml.py_compat import string_types
DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
@ -25,6 +30,7 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
binary_sensor_ns = esphomelib_ns.namespace('binary_sensor')
BinarySensor = binary_sensor_ns.class_('BinarySensor', Nameable)
BinarySensorPtr = BinarySensor.operator('ptr')
MQTTBinarySensorComponent = binary_sensor_ns.class_('MQTTBinarySensorComponent', mqtt.MQTTComponent)
# Triggers
@ -34,6 +40,10 @@ ClickTrigger = binary_sensor_ns.class_('ClickTrigger', Trigger.template(NoArg))
DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', Trigger.template(NoArg))
MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', Trigger.template(NoArg), Component)
MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent')
StateTrigger = binary_sensor_ns.class_('StateTrigger', Trigger.template(bool_))
# Condition
BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition)
# Filters
Filter = binary_sensor_ns.class_('Filter')
@ -46,13 +56,13 @@ LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter)
FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT]
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
FILTERS_SCHEMA = cv.ensure_list({
vol.Optional(CONF_INVERT): None,
vol.Optional(CONF_DELAYED_ON): cv.positive_time_period_milliseconds,
vol.Optional(CONF_DELAYED_OFF): cv.positive_time_period_milliseconds,
vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_LAMBDA): cv.lambda_,
}, cv.has_exactly_one_key(*FILTER_KEYS))])
}, cv.has_exactly_one_key(*FILTER_KEYS))
MULTI_CLICK_TIMING_SCHEMA = vol.Schema({
vol.Optional(CONF_STATE): cv.boolean,
@ -62,7 +72,7 @@ MULTI_CLICK_TIMING_SCHEMA = vol.Schema({
def parse_multi_click_timing_str(value):
if not isinstance(value, basestring):
if not isinstance(value, string_types):
return value
parts = value.lower().split(' ')
@ -150,7 +160,7 @@ def validate_multi_click_timing(value):
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent),
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)),
vol.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
vol.Optional(CONF_ON_PRESS): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger),
@ -174,6 +184,9 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
validate_multi_click_timing),
vol.Optional(CONF_INVALID_COOLDOWN): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_ON_STATE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StateTrigger),
}),
vol.Optional(CONF_INVERTED): cv.invalid(
"The inverted binary_sensor property has been replaced by the "
@ -195,8 +208,8 @@ def setup_filter(config):
elif CONF_HEARTBEAT in config:
yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT]))
elif CONF_LAMBDA in config:
lambda_ = None
for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')]):
for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')],
return_type=optional.template(bool_)):
yield None
yield LambdaFilter.new(lambda_)
@ -261,6 +274,11 @@ def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN]))
automation.build_automation(trigger, NoArg, conf)
for conf in config.get(CONF_ON_STATE, []):
rhs = binary_sensor_var.make_state_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, bool_, conf)
setup_mqtt_component(mqtt_var, config)
@ -269,14 +287,14 @@ def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config):
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)
CORE.add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
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)
CORE.add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
def core_to_hass_config(data, config):
@ -290,3 +308,33 @@ def core_to_hass_config(data, config):
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'
CONF_BINARY_SENSOR_IS_ON = 'binary_sensor.is_on'
BINARY_SENSOR_IS_ON_CONDITION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(BinarySensor),
})
@CONDITION_REGISTRY.register(CONF_BINARY_SENSOR_IS_ON, BINARY_SENSOR_IS_ON_CONDITION_SCHEMA)
def binary_sensor_is_on_to_code(config, condition_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_binary_sensor_is_on_condition(template_arg)
type = BinarySensorCondition.template(arg_type)
yield Pvariable(condition_id, rhs, type=type)
CONF_BINARY_SENSOR_IS_OFF = 'binary_sensor.is_off'
BINARY_SENSOR_IS_OFF_CONDITION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(BinarySensor),
})
@CONDITION_REGISTRY.register(CONF_BINARY_SENSOR_IS_OFF, BINARY_SENSOR_IS_OFF_CONDITION_SCHEMA)
def binary_sensor_is_off_to_code(config, condition_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_binary_sensor_is_off_condition(template_arg)
type = BinarySensorCondition.template(arg_type)
yield Pvariable(condition_id, rhs, type=type)

View File

@ -0,0 +1,36 @@
import voluptuous as vol
from esphomeyaml.components import binary_sensor, sensor
from esphomeyaml.components.apds9960 import APDS9960, CONF_APDS9960_ID
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DIRECTION, CONF_NAME
from esphomeyaml.cpp_generator import get_variable
DEPENDENCIES = ['apds9960']
APDS9960GestureDirectionBinarySensor = sensor.sensor_ns.class_(
'APDS9960GestureDirectionBinarySensor', binary_sensor.BinarySensor)
DIRECTIONS = {
'UP': 'make_up_direction',
'DOWN': 'make_down_direction',
'LEFT': 'make_left_direction',
'RIGHT': 'make_right_direction',
}
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(APDS9960GestureDirectionBinarySensor),
vol.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
cv.GenerateID(CONF_APDS9960_ID): cv.use_variable_id(APDS9960)
}))
def to_code(config):
for hub in get_variable(config[CONF_APDS9960_ID]):
yield
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
rhs = func(config[CONF_NAME])
binary_sensor.register_binary_sensor(rhs, config)
def to_hass_config(data, config):
return binary_sensor.core_to_hass_config(data, config)

View File

@ -0,0 +1,37 @@
import voluptuous as vol
from esphomeyaml.components import binary_sensor
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA
from esphomeyaml.cpp_generator import process_lambda, variable
from esphomeyaml.cpp_types import std_vector
CustomBinarySensorConstructor = binary_sensor.binary_sensor_ns.class_(
'CustomBinarySensorConstructor')
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(CustomBinarySensorConstructor),
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Required(CONF_BINARY_SENSORS):
cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(binary_sensor.BinarySensor),
})),
})
def to_code(config):
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=std_vector.template(binary_sensor.BinarySensorPtr)):
yield
rhs = CustomBinarySensorConstructor(template_)
custom = variable(config[CONF_ID], rhs)
for i, sens in enumerate(config[CONF_BINARY_SENSORS]):
binary_sensor.register_binary_sensor(custom.get_binary_sensor(i), sens)
BUILD_FLAGS = '-DUSE_CUSTOM_BINARY_SENSOR'
def to_hass_config(data, config):
return [binary_sensor.core_to_hass_config(data, sens) for sens in config[CONF_BINARY_SENSORS]]

View File

@ -5,7 +5,8 @@ from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLE
make_address_array
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
from esphomeyaml.helpers import esphomelib_ns, get_variable
from esphomeyaml.cpp_generator import get_variable
from esphomeyaml.cpp_types import esphomelib_ns
DEPENDENCIES = ['esp32_ble_tracker']
ESP32BLEPresenceDevice = esphomelib_ns.class_('ESP32BLEPresenceDevice', binary_sensor.BinarySensor)

View File

@ -4,7 +4,8 @@ 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.cpp_generator import get_variable
from esphomeyaml.cpp_types import global_ns
from esphomeyaml.pins import validate_gpio_pin
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]

View File

@ -4,8 +4,9 @@ import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application, \
setup_component, Component
from esphomeyaml.cpp_generator import variable
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
from esphomeyaml.cpp_types import Application, Component, App
MakeGPIOBinarySensor = Application.struct('MakeGPIOBinarySensor')
GPIOBinarySensorComponent = binary_sensor.binary_sensor_ns.class_('GPIOBinarySensorComponent',

View File

@ -4,7 +4,7 @@ from esphomeyaml.components import binary_sensor, display
from esphomeyaml.components.display.nextion import Nextion
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_COMPONENT_ID, CONF_NAME, CONF_PAGE_ID
from esphomeyaml.helpers import get_variable
from esphomeyaml.cpp_generator import get_variable
DEPENDENCIES = ['display']
@ -22,7 +22,6 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
def to_code(config):
hub = None
for hub in get_variable(config[CONF_NEXTION_ID]):
yield
rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID],

View File

@ -5,7 +5,7 @@ from esphomeyaml.components import binary_sensor
from esphomeyaml.components.pn532 import PN532Component
from esphomeyaml.const import CONF_NAME, CONF_UID
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import ArrayInitializer, get_variable
from esphomeyaml.cpp_generator import get_variable, ArrayInitializer
DEPENDENCIES = ['pn532']

View File

@ -3,7 +3,7 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor, rdm6300
from esphomeyaml.const import CONF_NAME, CONF_UID
from esphomeyaml.helpers import get_variable
from esphomeyaml.cpp_generator import get_variable
DEPENDENCIES = ['rdm6300']

View File

@ -11,7 +11,7 @@ from esphomeyaml.const import CONF_ADDRESS, CONF_CHANNEL, CONF_CODE, CONF_COMMAN
CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, CONF_RC_SWITCH_TYPE_A, \
CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, CONF_SAMSUNG, CONF_SONY, \
CONF_STATE
from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable
from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, Pvariable
DEPENDENCIES = ['remote_receiver']
@ -39,7 +39,7 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
cv.GenerateID(): cv.declare_variable_id(RemoteReceiver),
vol.Optional(CONF_LG): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)),
vol.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True),
}),
vol.Optional(CONF_NEC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
@ -50,7 +50,7 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
}),
vol.Optional(CONF_SONY): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)),
vol.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True),
}),
vol.Optional(CONF_PANASONIC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
@ -73,40 +73,40 @@ def receiver_base(full_config):
key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS)
if key == CONF_LG:
return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_NEC:
if key == CONF_NEC:
return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_PANASONIC:
if key == CONF_PANASONIC:
return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_SAMSUNG:
if key == CONF_SAMSUNG:
return SamsungReceiver.new(name, config[CONF_DATA])
elif key == CONF_SONY:
if key == CONF_SONY:
return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_RAW:
if key == CONF_RAW:
data = ArrayInitializer(*config, multiline=False)
return RawReceiver.new(name, data)
elif key == CONF_RC_SWITCH_RAW:
if key == CONF_RC_SWITCH_RAW:
return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
elif key == CONF_RC_SWITCH_TYPE_A:
if key == CONF_RC_SWITCH_TYPE_A:
return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_GROUP]),
binary_code(config[CONF_DEVICE]),
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_B:
if key == CONF_RC_SWITCH_TYPE_B:
return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
config[CONF_ADDRESS], config[CONF_CHANNEL],
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_C:
if key == CONF_RC_SWITCH_TYPE_C:
return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_FAMILY][0]) - ord('a'),
config[CONF_GROUP], config[CONF_DEVICE],
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_D:
if key == CONF_RC_SWITCH_TYPE_D:
return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_GROUP][0]) - ord('a'),
config[CONF_DEVICE], config[CONF_STATE])
else:
raise NotImplementedError("Unknown receiver type {}".format(config))
raise NotImplementedError("Unknown receiver type {}".format(config))
def to_code(config):

View File

@ -1,9 +1,10 @@
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, variable, setup_component, Component
from esphomeyaml.cpp_generator import variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import Application, Component, App
DEPENDENCIES = ['mqtt']
MakeStatusBinarySensor = Application.struct('MakeStatusBinarySensor')
StatusBinarySensor = binary_sensor.binary_sensor_ns.class_('StatusBinarySensor',

View File

@ -3,8 +3,9 @@ import voluptuous as vol
from esphomeyaml.components import binary_sensor
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, add, bool_, optional, process_lambda, variable, \
setup_component, Component
from esphomeyaml.cpp_generator import variable, process_lambda, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import Application, Component, App, optional, bool_
MakeTemplateBinarySensor = Application.struct('MakeTemplateBinarySensor')
TemplateBinarySensor = binary_sensor.binary_sensor_ns.class_('TemplateBinarySensor',

View File

@ -1,11 +1,12 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.components import mqtt
from esphomeyaml.components.mqtt import setup_mqtt_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_INTERNAL
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \
TemplateArguments, get_variable, Action, Nameable
from esphomeyaml.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID
from esphomeyaml.cpp_generator import Pvariable, add, get_variable
from esphomeyaml.cpp_types import Action, Nameable, esphomelib_ns
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@ -54,8 +55,7 @@ COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA)
def cover_open_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def cover_open_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_open_action(template_arg)
@ -70,8 +70,7 @@ COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA)
def cover_close_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def cover_close_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_close_action(template_arg)
@ -86,8 +85,7 @@ COVER_STOP_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA)
def cover_stop_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def cover_stop_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_stop_action(template_arg)

View File

@ -5,8 +5,9 @@ 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, optional, \
setup_component
from esphomeyaml.cpp_generator import variable, process_lambda, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import Application, App, optional, NoArg
MakeTemplateCover = Application.struct('MakeTemplateCover')
TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover)

View File

@ -0,0 +1,32 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_COMPONENTS
from esphomeyaml.cpp_generator import process_lambda, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import Component, ComponentPtr, esphomelib_ns, std_vector
CustomComponentConstructor = esphomelib_ns.class_('CustomComponentConstructor')
MULTI_CONF = True
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(CustomComponentConstructor),
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_COMPONENTS): cv.ensure_list(vol.Schema({
cv.GenerateID(): cv.declare_variable_id(Component)
}).extend(cv.COMPONENT_SCHEMA.schema)),
})
def to_code(config):
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=std_vector.template(ComponentPtr)):
yield
rhs = CustomComponentConstructor(template_)
custom = variable(config[CONF_ID], rhs)
for i, comp in enumerate(config.get(CONF_COMPONENTS, [])):
setup_component(custom.get_component(i), comp)
BUILD_FLAGS = '-DUSE_CUSTOM_COMPONENT'

View File

@ -1,25 +1,27 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_PIN, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable, setup_component, PollingComponent
from esphomeyaml.cpp_generator import Pvariable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, PollingComponent
DallasComponent = sensor.sensor_ns.class_('DallasComponent', PollingComponent)
MULTI_CONF = True
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(DallasComponent),
vol.Required(CONF_PIN): pins.input_output_pin,
vol.Required(CONF_PIN): pins.input_pullup_pin,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}).extend(cv.COMPONENT_SCHEMA.schema)])
}).extend(cv.COMPONENT_SCHEMA.schema)
def to_code(config):
for conf in config:
rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL))
var = Pvariable(conf[CONF_ID], rhs)
setup_component(var, conf)
rhs = App.make_dallas_component(config[CONF_PIN], config.get(CONF_UPDATE_INTERVAL))
var = Pvariable(config[CONF_ID], rhs)
setup_component(var, config)
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View File

@ -1,6 +1,7 @@
import voluptuous as vol
from esphomeyaml.helpers import App, add
from esphomeyaml.cpp_generator import add
from esphomeyaml.cpp_types import App
DEPENDENCIES = ['logger']

View File

@ -2,11 +2,11 @@ import voluptuous as vol
from esphomeyaml import config_validation as cv, pins
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \
CONF_SLEEP_DURATION, CONF_WAKEUP_PIN, CONF_MODE, CONF_PINS
from esphomeyaml.helpers import Action, App, Component, Pvariable, TemplateArguments, add, \
esphomelib_ns, get_variable, gpio_input_pin_expression, setup_component, global_ns, \
StructInitializer
from esphomeyaml.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \
CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN
from esphomeyaml.cpp_generator import Pvariable, StructInitializer, add, get_variable
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
from esphomeyaml.cpp_types import Action, App, Component, esphomelib_ns, global_ns
def validate_pin_number(value):
@ -43,12 +43,11 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema,
validate_pin_number),
vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32, vol.Upper,
cv.one_of(*WAKEUP_PIN_MODES)),
vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32,
cv.one_of(*WAKEUP_PIN_MODES), upper=True),
vol.Optional(CONF_ESP32_EXT1_WAKEUP): vol.All(cv.only_on_esp32, vol.Schema({
vol.Required(CONF_PINS): vol.All(cv.ensure_list, [pins.shorthand_input_pin],
[validate_pin_number]),
vol.Required(CONF_MODE): vol.All(vol.Upper, cv.one_of(*EXT1_WAKEUP_MODES)),
vol.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number),
vol.Required(CONF_MODE): cv.one_of(*EXT1_WAKEUP_MODES, upper=True),
})),
vol.Optional(CONF_RUN_CYCLES): cv.positive_int,
vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
@ -95,8 +94,7 @@ DEEP_SLEEP_ENTER_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_DEEP_SLEEP_ENTER, DEEP_SLEEP_ENTER_ACTION_SCHEMA)
def deep_sleep_enter_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def deep_sleep_enter_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_enter_deep_sleep_action(template_arg)
@ -111,8 +109,7 @@ DEEP_SLEEP_PREVENT_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_DEEP_SLEEP_PREVENT, DEEP_SLEEP_PREVENT_ACTION_SCHEMA)
def deep_sleep_prevent_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def deep_sleep_prevent_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_prevent_deep_sleep_action(template_arg)

View File

@ -3,7 +3,9 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import add, add_job, esphomelib_ns
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import add
from esphomeyaml.cpp_types import esphomelib_ns
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@ -50,7 +52,7 @@ def setup_display_core_(display_var, config):
def setup_display(display_var, config):
add_job(setup_display_core_, display_var, config)
CORE.add_job(setup_display_core_, display_var, config)
BUILD_FLAGS = '-DUSE_DISPLAY'

View File

@ -1,12 +1,13 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DATA_PINS, CONF_DIMENSIONS, CONF_ENABLE_PIN, CONF_ID, \
CONF_LAMBDA, CONF_RS_PIN, CONF_RW_PIN
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, process_lambda, \
setup_component, PollingComponent
from esphomeyaml.cpp_generator import Pvariable, add, process_lambda
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, PollingComponent, void
LCDDisplay = display.display_ns.class_('LCDDisplay', PollingComponent)
LCDDisplayRef = LCDDisplay.operator('ref')
@ -63,7 +64,8 @@ def to_code(config):
add(lcd.set_rw_pin(rw))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]):
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')],
return_type=void):
yield
add(lcd.set_writer(lambda_))

View File

@ -1,11 +1,13 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import display, i2c
from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions, \
LCDDisplay
from esphomeyaml.components.display.lcd_gpio import LCDDisplay, LCDDisplayRef, \
validate_lcd_dimensions
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_DIMENSIONS, CONF_ID, CONF_LAMBDA
from esphomeyaml.helpers import App, Pvariable, add, process_lambda, setup_component
from esphomeyaml.cpp_generator import Pvariable, add, process_lambda
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, void
DEPENDENCIES = ['i2c']
@ -26,7 +28,8 @@ def to_code(config):
add(lcd.set_address(config[CONF_ADDRESS]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]):
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')],
return_type=void):
yield
add(lcd.set_writer(lambda_))

View File

@ -1,13 +1,14 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display, spi
from esphomeyaml.components.spi import SPIComponent
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS, \
CONF_SPI_ID
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression, \
process_lambda, setup_component, PollingComponent
from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, PollingComponent, void
DEPENDENCIES = ['spi']
@ -38,7 +39,8 @@ def to_code(config):
add(max7219.set_intensity(config[CONF_INTENSITY]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')]):
for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')],
return_type=void):
yield
add(max7219.set_writer(lambda_))

View File

@ -2,9 +2,9 @@ from esphomeyaml.components import display, uart
from esphomeyaml.components.uart import UARTComponent
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_UART_ID
from esphomeyaml.helpers import App, PollingComponent, Pvariable, add, get_variable, \
process_lambda, \
setup_component
from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, PollingComponent, void
DEPENDENCIES = ['uart']
@ -24,7 +24,8 @@ def to_code(config):
nextion = Pvariable(config[CONF_ID], rhs)
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')]):
for lambda_ in process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')],
return_type=void):
yield
add(nextion.set_writer(lambda_))

View File

@ -1,13 +1,14 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
from esphomeyaml.components.display import ssd1306_spi
from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, \
CONF_MODEL, CONF_RESET_PIN, CONF_LAMBDA
from esphomeyaml.helpers import App, Pvariable, add, \
gpio_output_pin_expression, process_lambda, setup_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, CONF_LAMBDA, CONF_MODEL, \
CONF_RESET_PIN
from esphomeyaml.cpp_generator import Pvariable, add, process_lambda
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, void
DEPENDENCIES = ['i2c']
@ -36,7 +37,7 @@ def to_code(config):
add(ssd.set_address(config[CONF_ADDRESS]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA],
[(display.DisplayBufferRef, 'it')]):
[(display.DisplayBufferRef, 'it')], return_type=void):
yield
add(ssd.set_writer(lambda_))

View File

@ -1,14 +1,14 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display, spi
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, \
CONF_ID, CONF_MODEL, \
CONF_RESET_PIN, CONF_SPI_ID, CONF_LAMBDA
from esphomeyaml.helpers import App, Pvariable, add, get_variable, \
gpio_output_pin_expression, process_lambda, setup_component, PollingComponent
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, CONF_ID, CONF_LAMBDA, \
CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID
from esphomeyaml.cpp_generator import Pvariable, add, get_variable, process_lambda
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, PollingComponent, void
DEPENDENCIES = ['spi']
@ -27,7 +27,7 @@ MODELS = {
'SH1106_64X48': SSD1306Model.SH1106_MODEL_64_48,
}
SSD1306_MODEL = vol.All(vol.Upper, vol.Replace(' ', '_'), cv.one_of(*MODELS))
SSD1306_MODEL = cv.one_of(*MODELS, upper=True, space="_")
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(SPISSD1306),
@ -60,7 +60,7 @@ def to_code(config):
add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA],
[(display.DisplayBufferRef, 'it')]):
[(display.DisplayBufferRef, 'it')], return_type=void):
yield
add(ssd.set_writer(lambda_))

View File

@ -6,8 +6,10 @@ from esphomeyaml.components import display, spi
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_BUSY_PIN, CONF_CS_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \
CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_input_pin_expression, \
gpio_output_pin_expression, process_lambda, setup_component, PollingComponent
from esphomeyaml.cpp_generator import get_variable, Pvariable, process_lambda, add
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, gpio_input_pin_expression, \
setup_component
from esphomeyaml.cpp_types import PollingComponent, App, void
DEPENDENCIES = ['spi']
@ -43,7 +45,7 @@ PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_MODEL): vol.All(vol.Lower, cv.one_of(*MODELS)),
vol.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True),
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
vol.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t,
@ -69,7 +71,8 @@ def to_code(config):
raise NotImplementedError()
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')]):
for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')],
return_type=void):
yield
add(epaper.set_writer(lambda_))
if CONF_RESET_PIN in config:

View File

@ -2,8 +2,9 @@ import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, ArrayInitializer, Component, Pvariable, RawExpression, add, \
esphomelib_ns, setup_component
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, RawExpression, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
@ -14,7 +15,7 @@ CONF_MINOR = 'minor'
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon),
vol.Required(CONF_TYPE): vol.All(vol.Upper, cv.one_of('IBEACON')),
vol.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True),
vol.Required(CONF_UUID): cv.uuid,
vol.Optional(CONF_MAJOR): cv.uint16_t,
vol.Optional(CONF_MINOR): cv.uint16_t,

View File

@ -4,8 +4,9 @@ from esphomeyaml import config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, ArrayInitializer, \
setup_component, Component
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]

View File

@ -2,11 +2,13 @@ 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.const import CONF_HIGH_VOLTAGE_REFERENCE, CONF_ID, CONF_IIR_FILTER, \
CONF_LOW_VOLTAGE_REFERENCE, CONF_MEASUREMENT_DURATION, CONF_SETUP_MODE, CONF_SLEEP_DURATION, \
CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
from esphomeyaml.core import TimePeriod
from esphomeyaml.helpers import App, Pvariable, add, global_ns, setup_component, Component
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component, global_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
@ -19,6 +21,7 @@ def validate_voltage(values):
if not value.endswith('V'):
value += 'V'
return cv.one_of(*values)(value)
return validator

View File

@ -0,0 +1,73 @@
import voluptuous as vol
from esphomeyaml import pins
from esphomeyaml.components import wifi
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DOMAIN, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_TYPE, \
ESP_PLATFORM_ESP32
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import gpio_output_pin_expression
from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns
CONFLICTS_WITH = ['wifi']
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONF_PHY_ADDR = 'phy_addr'
CONF_MDC_PIN = 'mdc_pin'
CONF_MDIO_PIN = 'mdio_pin'
CONF_CLK_MODE = 'clk_mode'
CONF_POWER_PIN = 'power_pin'
EthernetType = esphomelib_ns.enum('EthernetType')
ETHERNET_TYPES = {
'LAN8720': EthernetType.ETHERNET_TYPE_LAN8720,
'TLK110': EthernetType.ETHERNET_TYPE_TLK110,
}
eth_clock_mode_t = global_ns.enum('eth_clock_mode_t')
CLK_MODES = {
'GPIO0_IN': eth_clock_mode_t.ETH_CLOCK_GPIO0_IN,
'GPIO0_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT,
'GPIO16_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT,
'GPIO17_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT,
}
EthernetComponent = esphomelib_ns.class_('EthernetComponent', Component)
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(EthernetComponent),
vol.Required(CONF_TYPE): cv.one_of(*ETHERNET_TYPES, upper=True),
vol.Required(CONF_MDC_PIN): pins.output_pin,
vol.Required(CONF_MDIO_PIN): pins.input_output_pin,
vol.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.one_of(*CLK_MODES, upper=True, space='_'),
vol.Optional(CONF_PHY_ADDR, default=0): vol.All(cv.int_, vol.Range(min=0, max=31)),
vol.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_MANUAL_IP): wifi.STA_MANUAL_IP_SCHEMA,
vol.Optional(CONF_HOSTNAME): cv.hostname,
vol.Optional(CONF_DOMAIN, default='.local'): cv.domain_name,
})
def to_code(config):
rhs = App.init_ethernet()
eth = Pvariable(config[CONF_ID], rhs)
add(eth.set_phy_addr(config[CONF_PHY_ADDR]))
add(eth.set_mdc_pin(config[CONF_MDC_PIN]))
add(eth.set_mdio_pin(config[CONF_MDIO_PIN]))
add(eth.set_type(ETHERNET_TYPES[config[CONF_TYPE]]))
add(eth.set_clk_mode(CLK_MODES[config[CONF_CLK_MODE]]))
if CONF_POWER_PIN in config:
for pin in gpio_output_pin_expression(config[CONF_POWER_PIN]):
yield
add(eth.set_power_pin(pin))
if CONF_HOSTNAME in config:
add(eth.set_hostname(config[CONF_HOSTNAME]))
if CONF_MANUAL_IP in config:
add(eth.set_manual_ip(wifi.manual_ip(config[CONF_MANUAL_IP])))
REQUIRED_BUILD_FLAGS = '-DUSE_ETHERNET'

View File

@ -1,13 +1,14 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.components import mqtt
from esphomeyaml.components.mqtt import setup_mqtt_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_OSCILLATION_COMMAND_TOPIC, \
CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_INTERNAL, \
CONF_SPEED, CONF_OSCILLATING, CONF_OSCILLATION_OUTPUT, CONF_NAME
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
TemplateArguments, get_variable, templatable, bool_, Action, Nameable, Component
from esphomeyaml.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_NAME, CONF_OSCILLATING, \
CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_OUTPUT, CONF_OSCILLATION_STATE_TOPIC, \
CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
from esphomeyaml.cpp_generator import add, Pvariable, get_variable, templatable
from esphomeyaml.cpp_types import Application, Component, Nameable, esphomelib_ns, Action, bool_
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@ -32,13 +33,14 @@ FAN_SPEED_HIGH = FanSpeed.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,
vol.Optional(CONF_OSCILLATION_STATE_TOPIC): vol.All(cv.requires_component('mqtt'),
cv.publish_topic),
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): vol.All(cv.requires_component('mqtt'),
cv.subscribe_topic),
})
FAN_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FAN_SCHEMA.schema)
FAN_SPEEDS = {
'OFF': FAN_SPEED_OFF,
'LOW': FAN_SPEED_LOW,
@ -47,10 +49,6 @@ FAN_SPEEDS = {
}
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_INTERNAL in config:
add(fan_var.set_internal(config[CONF_INTERNAL]))
@ -74,7 +72,6 @@ def setup_fan(fan_obj, mqtt_obj, config):
BUILD_FLAGS = '-DUSE_FAN'
CONF_FAN_TOGGLE = 'fan.toggle'
FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(FanState),
@ -82,8 +79,7 @@ FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA)
def fan_toggle_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def fan_toggle_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_toggle_action(template_arg)
@ -98,8 +94,7 @@ FAN_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_FAN_TURN_OFF, FAN_TURN_OFF_ACTION_SCHEMA)
def fan_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def fan_turn_off_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
@ -111,13 +106,12 @@ CONF_FAN_TURN_ON = 'fan.turn_on'
FAN_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(FanState),
vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
vol.Optional(CONF_SPEED): cv.templatable(validate_fan_speed),
vol.Optional(CONF_SPEED): cv.templatable(cv.one_of(*FAN_SPEEDS, upper=True)),
})
@ACTION_REGISTRY.register(CONF_FAN_TURN_ON, FAN_TURN_ON_ACTION_SCHEMA)
def fan_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def fan_turn_on_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)

View File

@ -3,7 +3,9 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import fan, output
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
from esphomeyaml.helpers import App, add, get_variable, variable, setup_component
from esphomeyaml.cpp_generator import get_variable, variable, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),

View File

@ -5,7 +5,8 @@ from esphomeyaml.components import fan, mqtt, output
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
from esphomeyaml.cpp_generator import get_variable, variable, add
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),

View File

@ -1,15 +1,17 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.components import display
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
relative_path
from esphomeyaml.core import CORE, HexInt
from esphomeyaml.cpp_generator import ArrayInitializer, MockObj, Pvariable, RawExpression, add
from esphomeyaml.cpp_types import App
from esphomeyaml.py_compat import sort_by_cmp
DEPENDENCIES = ['display']
MULTI_CONF = True
Font = display.display_ns.class_('Font')
Glyph = display.display_ns.class_('Glyph')
@ -32,12 +34,11 @@ def validate_glyphs(value):
if len(x_) < len(y_):
return -1
elif len(x_) > len(y_):
if len(x_) > len(y_):
return 1
else:
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
value.sort(cmp=comparator)
sort_by_cmp(value, comparator)
return value
@ -46,11 +47,11 @@ def validate_pillow_installed(value):
import PIL
except ImportError:
raise vol.Invalid("Please install the pillow python package to use this feature. "
"(pip2 install pillow)")
"(pip install pillow)")
if PIL.__version__[0] < '4':
raise vol.Invalid("Please update your pillow installation to at least 4.0.x. "
"(pip2 install -U pillow)")
"(pip install -U pillow)")
return value
@ -76,46 +77,45 @@ FONT_SCHEMA = vol.Schema({
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
})
CONFIG_SCHEMA = vol.All(validate_pillow_installed, cv.ensure_list, [FONT_SCHEMA])
CONFIG_SCHEMA = vol.All(validate_pillow_installed, FONT_SCHEMA)
def to_code(config):
from PIL import ImageFont
for conf in config:
path = relative_path(conf[CONF_FILE])
try:
font = ImageFont.truetype(path, conf[CONF_SIZE])
except Exception as e:
raise core.ESPHomeYAMLError(u"Could not load truetype file {}: {}".format(path, e))
path = CORE.relative_path(config[CONF_FILE])
try:
font = ImageFont.truetype(path, config[CONF_SIZE])
except Exception as e:
raise core.EsphomeyamlError(u"Could not load truetype file {}: {}".format(path, e))
ascent, descent = font.getmetrics()
ascent, descent = font.getmetrics()
glyph_args = {}
data = []
for glyph in conf[CONF_GLYPHS]:
mask = font.getmask(glyph, mode='1')
_, (offset_x, offset_y) = font.font.getsize(glyph)
width, height = mask.size
width8 = ((width + 7) // 8) * 8
glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812
for y in range(height):
for x in range(width):
if not mask.getpixel((x, y)):
continue
pos = x + y * width8
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
glyph_args[glyph] = (len(data), offset_x, offset_y, width, height)
data += glyph_data
glyph_args = {}
data = []
for glyph in config[CONF_GLYPHS]:
mask = font.getmask(glyph, mode='1')
_, (offset_x, offset_y) = font.font.getsize(glyph)
width, height = mask.size
width8 = ((width + 7) // 8) * 8
glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812
for y in range(height):
for x in range(width):
if not mask.getpixel((x, y)):
continue
pos = x + y * width8
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
glyph_args[glyph] = (len(data), offset_x, offset_y, width, height)
data += glyph_data
raw_data = MockObj(conf[CONF_RAW_DATA_ID])
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
raw_data, len(data),
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
raw_data = MockObj(config[CONF_RAW_DATA_ID])
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
raw_data, len(data),
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
glyphs = []
for glyph in conf[CONF_GLYPHS]:
glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))
glyphs = []
for glyph in config[CONF_GLYPHS]:
glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))
rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
Pvariable(conf[CONF_ID], rhs)
rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
Pvariable(config[CONF_ID], rhs)

View File

@ -2,34 +2,34 @@ import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_INITIAL_VALUE, CONF_RESTORE_VALUE, CONF_TYPE
from esphomeyaml.helpers import App, Component, Pvariable, RawExpression, TemplateArguments, add, \
esphomelib_ns, setup_component
from esphomeyaml.cpp_generator import Pvariable, RawExpression, TemplateArguments, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
GlobalVariableComponent = esphomelib_ns.class_('GlobalVariableComponent', Component)
GLOBAL_VAR_SCHEMA = vol.Schema({
MULTI_CONF = True
CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.declare_variable_id(GlobalVariableComponent),
vol.Required(CONF_TYPE): cv.string_strict,
vol.Optional(CONF_INITIAL_VALUE): cv.string_strict,
vol.Optional(CONF_RESTORE_VALUE): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA.schema)
CONFIG_SCHEMA = vol.All(cv.ensure_list, [GLOBAL_VAR_SCHEMA])
def to_code(config):
for conf in config:
type_ = RawExpression(conf[CONF_TYPE])
template_args = TemplateArguments(type_)
res_type = GlobalVariableComponent.template(template_args)
initial_value = None
if CONF_INITIAL_VALUE in conf:
initial_value = RawExpression(conf[CONF_INITIAL_VALUE])
rhs = App.Pmake_global_variable(template_args, initial_value)
glob = Pvariable(conf[CONF_ID], rhs, type=res_type)
type_ = RawExpression(config[CONF_TYPE])
template_args = TemplateArguments(type_)
res_type = GlobalVariableComponent.template(template_args)
initial_value = None
if CONF_INITIAL_VALUE in config:
initial_value = RawExpression(config[CONF_INITIAL_VALUE])
rhs = App.Pmake_global_variable(template_args, initial_value)
glob = Pvariable(config[CONF_ID], rhs, type=res_type)
if conf.get(CONF_RESTORE_VALUE, False):
hash_ = hash(conf[CONF_ID].id) % 2**32
add(glob.set_restore_value(hash_))
if config.get(CONF_RESTORE_VALUE, False):
hash_ = hash(config[CONF_ID].id) % 2**32
add(glob.set_restore_value(hash_))
setup_component(glob, conf)
setup_component(glob, config)

View File

@ -1,18 +1,20 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
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, setup_component, Component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_FREQUENCY, CONF_ID, CONF_RECEIVE_TIMEOUT, CONF_SCAN, CONF_SCL, \
CONF_SDA
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
I2CComponent = esphomelib_ns.class_('I2CComponent', Component)
I2CDevice = pins.I2CDevice
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_SDA, default='SDA'): pins.input_pullup_pin,
vol.Optional(CONF_SCL, default='SCL'): pins.input_pullup_pin,
vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=0, min_included=False)),
vol.Optional(CONF_SCAN): cv.boolean,

View File

@ -3,17 +3,18 @@ import logging
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.components import display, font
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_FILE, CONF_ID, CONF_RESIZE
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
relative_path
from esphomeyaml.core import CORE, HexInt
from esphomeyaml.cpp_generator import ArrayInitializer, MockObj, Pvariable, RawExpression, add
from esphomeyaml.cpp_types import App
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['display']
MULTI_CONF = True
Image_ = display.display_ns.class_('Image')
@ -26,40 +27,39 @@ IMAGE_SCHEMA = vol.Schema({
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
})
CONFIG_SCHEMA = vol.All(font.validate_pillow_installed, cv.ensure_list, [IMAGE_SCHEMA])
CONFIG_SCHEMA = vol.All(font.validate_pillow_installed, IMAGE_SCHEMA)
def to_code(config):
from PIL import Image
for conf in config:
path = relative_path(conf[CONF_FILE])
try:
image = Image.open(path)
except Exception as e:
raise core.ESPHomeYAMLError(u"Could not load image file {}: {}".format(path, e))
path = CORE.relative_path(config[CONF_FILE])
try:
image = Image.open(path)
except Exception as e:
raise core.EsphomeyamlError(u"Could not load image file {}: {}".format(path, e))
if CONF_RESIZE in conf:
image.thumbnail(conf[CONF_RESIZE])
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
image = image.convert('1', dither=Image.NONE)
width, height = image.size
if width > 500 or height > 500:
_LOGGER.warning("The image you requested is very big. Please consider using the resize "
"parameter")
width8 = ((width + 7) // 8) * 8
data = [0 for _ in range(height * width8 // 8)]
for y in range(height):
for x in range(width):
if image.getpixel((x, y)):
continue
pos = x + y * width8
data[pos // 8] |= 0x80 >> (pos % 8)
image = image.convert('1', dither=Image.NONE)
width, height = image.size
if width > 500 or height > 500:
_LOGGER.warning("The image you requested is very big. Please consider using the resize "
"parameter")
width8 = ((width + 7) // 8) * 8
data = [0 for _ in range(height * width8 // 8)]
for y in range(height):
for x in range(width):
if image.getpixel((x, y)):
continue
pos = x + y * width8
data[pos // 8] |= 0x80 >> (pos % 8)
raw_data = MockObj(conf[CONF_RAW_DATA_ID])
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
raw_data, len(data),
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
raw_data = MockObj(config[CONF_RAW_DATA_ID])
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
raw_data, len(data),
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
rhs = App.make_image(raw_data, width, height)
Pvariable(conf[CONF_ID], rhs)
rhs = App.make_image(raw_data, width, height)
Pvariable(config[CONF_ID], rhs)

View File

@ -0,0 +1,24 @@
import voluptuous as vol
from esphomeyaml import automation
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_INTERVAL
from esphomeyaml.cpp_generator import Pvariable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, NoArg, PollingComponent, Trigger, esphomelib_ns
IntervalTrigger = esphomelib_ns.class_('IntervalTrigger', Trigger.template(NoArg), PollingComponent)
CONFIG_SCHEMA = automation.validate_automation(vol.Schema({
vol.Required(CONF_ID): cv.declare_variable_id(IntervalTrigger),
vol.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config):
for conf in config:
rhs = App.register_component(IntervalTrigger.new(config[CONF_INTERVAL]))
trigger = Pvariable(conf[CONF_ID], rhs)
setup_component(trigger, conf)
automation.build_automation(trigger, NoArg, conf)

View File

@ -1,7 +1,8 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.components import mqtt
from esphomeyaml.components.mqtt import setup_mqtt_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \
@ -9,10 +10,11 @@ from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLOR
CONF_NUM_LEDS, CONF_RANDOM, CONF_RED, CONF_SPEED, CONF_STATE, CONF_TRANSITION_LENGTH, \
CONF_UPDATE_INTERVAL, CONF_WHITE, CONF_WIDTH, CONF_FLASH_LENGTH, CONF_COLOR_TEMPERATURE, \
CONF_EFFECT
from esphomeyaml.helpers import Application, ArrayInitializer, Pvariable, RawExpression, \
StructInitializer, add, add_job, esphomelib_ns, process_lambda, setup_mqtt_component, \
get_variable, TemplateArguments, templatable, uint32, float_, std_string, Nameable, Component, \
Action
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import process_lambda, Pvariable, add, StructInitializer, \
ArrayInitializer, get_variable, templatable
from esphomeyaml.cpp_types import esphomelib_ns, Application, Component, Nameable, Action, uint32, \
float_, std_string, void
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@ -23,7 +25,8 @@ light_ns = esphomelib_ns.namespace('light')
LightState = light_ns.class_('LightState', Nameable, Component)
MakeLight = Application.struct('MakeLight')
LightOutput = light_ns.class_('LightOutput')
FastLEDLightOutputComponent = light_ns.class_('FastLEDLightOutputComponent', LightOutput)
AddressableLight = light_ns.class_('AddressableLight')
AddressableLightRef = AddressableLight.operator('ref')
# Actions
ToggleAction = light_ns.class_('ToggleAction', Action)
@ -32,7 +35,6 @@ TurnOnAction = light_ns.class_('TurnOnAction', Action)
LightColorValues = light_ns.class_('LightColorValues')
MQTTJSONLightComponent = light_ns.class_('MQTTJSONLightComponent', mqtt.MQTTComponent)
# Effects
@ -42,28 +44,30 @@ LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect)
StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect)
StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect)
FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect)
BaseFastLEDLightEffect = light_ns.class_('BaseFastLEDLightEffect', LightEffect)
FastLEDLambdaLightEffect = light_ns.class_('FastLEDLambdaLightEffect', BaseFastLEDLightEffect)
FastLEDRainbowLightEffect = light_ns.class_('FastLEDRainbowLightEffect', BaseFastLEDLightEffect)
FastLEDColorWipeEffect = light_ns.class_('FastLEDColorWipeEffect', BaseFastLEDLightEffect)
FastLEDColorWipeEffectColor = light_ns.class_('FastLEDColorWipeEffectColor', BaseFastLEDLightEffect)
FastLEDScanEffect = light_ns.class_('FastLEDScanEffect', BaseFastLEDLightEffect)
FastLEDScanEffectColor = light_ns.class_('FastLEDScanEffectColor', BaseFastLEDLightEffect)
FastLEDTwinkleEffect = light_ns.class_('FastLEDTwinkleEffect', BaseFastLEDLightEffect)
FastLEDRandomTwinkleEffect = light_ns.class_('FastLEDRandomTwinkleEffect', BaseFastLEDLightEffect)
FastLEDFireworksEffect = light_ns.class_('FastLEDFireworksEffect', BaseFastLEDLightEffect)
FastLEDFlickerEffect = light_ns.class_('FastLEDFlickerEffect', BaseFastLEDLightEffect)
AddressableLightEffect = light_ns.class_('AddressableLightEffect', LightEffect)
AddressableLambdaLightEffect = light_ns.class_('AddressableLambdaLightEffect',
AddressableLightEffect)
AddressableRainbowLightEffect = light_ns.class_('AddressableRainbowLightEffect',
AddressableLightEffect)
AddressableColorWipeEffect = light_ns.class_('AddressableColorWipeEffect', AddressableLightEffect)
AddressableColorWipeEffectColor = light_ns.struct('AddressableColorWipeEffectColor')
AddressableScanEffect = light_ns.class_('AddressableScanEffect', AddressableLightEffect)
AddressableTwinkleEffect = light_ns.class_('AddressableTwinkleEffect', AddressableLightEffect)
AddressableRandomTwinkleEffect = light_ns.class_('AddressableRandomTwinkleEffect',
AddressableLightEffect)
AddressableFireworksEffect = light_ns.class_('AddressableFireworksEffect', AddressableLightEffect)
AddressableFlickerEffect = light_ns.class_('AddressableFlickerEffect', AddressableLightEffect)
CONF_STROBE = 'strobe'
CONF_FLICKER = 'flicker'
CONF_FASTLED_LAMBDA = 'fastled_lambda'
CONF_FASTLED_RAINBOW = 'fastled_rainbow'
CONF_FASTLED_COLOR_WIPE = 'fastled_color_wipe'
CONF_FASTLED_SCAN = 'fastled_scan'
CONF_FASTLED_TWINKLE = 'fastled_twinkle'
CONF_FASTLED_RANDOM_TWINKLE = 'fastled_random_twinkle'
CONF_FASTLED_FIREWORKS = 'fastled_fireworks'
CONF_FASTLED_FLICKER = 'fastled_flicker'
CONF_ADDRESSABLE_LAMBDA = 'addressable_lambda'
CONF_ADDRESSABLE_RAINBOW = 'addressable_rainbow'
CONF_ADDRESSABLE_COLOR_WIPE = 'addressable_color_wipe'
CONF_ADDRESSABLE_SCAN = 'addressable_scan'
CONF_ADDRESSABLE_TWINKLE = 'addressable_twinkle'
CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle'
CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks'
CONF_ADDRESSABLE_FLICKER = 'addressable_flicker'
CONF_ADD_LED_INTERVAL = 'add_led_interval'
CONF_REVERSE = 'reverse'
@ -78,10 +82,10 @@ CONF_INTENSITY = 'intensity'
BINARY_EFFECTS = [CONF_LAMBDA, CONF_STROBE]
MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + [CONF_FLICKER]
RGB_EFFECTS = MONOCHROMATIC_EFFECTS + [CONF_RANDOM]
FASTLED_EFFECTS = RGB_EFFECTS + [CONF_FASTLED_LAMBDA, CONF_FASTLED_RAINBOW, CONF_FASTLED_COLOR_WIPE,
CONF_FASTLED_SCAN, CONF_FASTLED_TWINKLE,
CONF_FASTLED_RANDOM_TWINKLE, CONF_FASTLED_FIREWORKS,
CONF_FASTLED_FLICKER]
ADDRESSABLE_EFFECTS = RGB_EFFECTS + [CONF_ADDRESSABLE_LAMBDA, CONF_ADDRESSABLE_RAINBOW,
CONF_ADDRESSABLE_COLOR_WIPE, CONF_ADDRESSABLE_SCAN,
CONF_ADDRESSABLE_TWINKLE, CONF_ADDRESSABLE_RANDOM_TWINKLE,
CONF_ADDRESSABLE_FIREWORKS, CONF_ADDRESSABLE_FLICKER]
EFFECTS_SCHEMA = vol.Schema({
vol.Optional(CONF_LAMBDA): vol.Schema({
@ -98,7 +102,7 @@ EFFECTS_SCHEMA = vol.Schema({
vol.Optional(CONF_STROBE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(StrobeLightEffect),
vol.Optional(CONF_NAME, default="Strobe"): cv.string,
vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.All(vol.Schema({
vol.Optional(CONF_COLORS): vol.All(cv.ensure_list(vol.Schema({
vol.Optional(CONF_STATE, default=True): cv.boolean,
vol.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
vol.Optional(CONF_RED, default=1.0): cv.percentage,
@ -107,7 +111,7 @@ EFFECTS_SCHEMA = vol.Schema({
vol.Optional(CONF_WHITE, default=1.0): cv.percentage,
vol.Required(CONF_DURATION): cv.positive_time_period_milliseconds,
}), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE,
CONF_WHITE))], vol.Length(min=2)),
CONF_WHITE)), vol.Length(min=2)),
}),
vol.Optional(CONF_FLICKER): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FlickerLightEffect),
@ -115,58 +119,59 @@ EFFECTS_SCHEMA = vol.Schema({
vol.Optional(CONF_ALPHA): cv.percentage,
vol.Optional(CONF_INTENSITY): cv.percentage,
}),
vol.Optional(CONF_FASTLED_LAMBDA): vol.Schema({
vol.Optional(CONF_ADDRESSABLE_LAMBDA): vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_RAINBOW): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRainbowLightEffect),
vol.Optional(CONF_ADDRESSABLE_RAINBOW): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableRainbowLightEffect),
vol.Optional(CONF_NAME, default="Rainbow"): cv.string,
vol.Optional(CONF_SPEED): cv.uint32_t,
vol.Optional(CONF_WIDTH): cv.uint32_t,
}),
vol.Optional(CONF_FASTLED_COLOR_WIPE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDColorWipeEffect),
vol.Optional(CONF_ADDRESSABLE_COLOR_WIPE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableColorWipeEffect),
vol.Optional(CONF_NAME, default="Color Wipe"): cv.string,
vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.Schema({
vol.Optional(CONF_COLORS): cv.ensure_list({
vol.Optional(CONF_RED, default=1.0): cv.percentage,
vol.Optional(CONF_GREEN, default=1.0): cv.percentage,
vol.Optional(CONF_BLUE, default=1.0): cv.percentage,
vol.Optional(CONF_WHITE, default=1.0): cv.percentage,
vol.Optional(CONF_RANDOM, default=False): cv.boolean,
vol.Required(CONF_NUM_LEDS): vol.All(cv.uint32_t, vol.Range(min=1)),
})]),
}),
vol.Optional(CONF_ADD_LED_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_REVERSE): cv.boolean,
}),
vol.Optional(CONF_FASTLED_SCAN): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDScanEffect),
vol.Optional(CONF_ADDRESSABLE_SCAN): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableScanEffect),
vol.Optional(CONF_NAME, default="Scan"): cv.string,
vol.Optional(CONF_MOVE_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_TWINKLE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDTwinkleEffect),
vol.Optional(CONF_ADDRESSABLE_TWINKLE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableTwinkleEffect),
vol.Optional(CONF_NAME, default="Twinkle"): cv.string,
vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage,
vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_RANDOM_TWINKLE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRandomTwinkleEffect),
vol.Optional(CONF_ADDRESSABLE_RANDOM_TWINKLE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableRandomTwinkleEffect),
vol.Optional(CONF_NAME, default="Random Twinkle"): cv.string,
vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage,
vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_FIREWORKS): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFireworksEffect),
vol.Optional(CONF_ADDRESSABLE_FIREWORKS): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableFireworksEffect),
vol.Optional(CONF_NAME, default="Fireworks"): cv.string,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_SPARK_PROBABILITY): cv.percentage,
vol.Optional(CONF_USE_RANDOM_COLOR): cv.boolean,
vol.Optional(CONF_FADE_OUT_RATE): cv.uint8_t,
}),
vol.Optional(CONF_FASTLED_FLICKER): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFlickerEffect),
vol.Optional(CONF_NAME, default="FastLED Flicker"): cv.string,
vol.Optional(CONF_ADDRESSABLE_FLICKER): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableFlickerEffect),
vol.Optional(CONF_NAME, default="Addressable Flicker"): cv.string,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_INTENSITY): cv.percentage,
}),
@ -175,28 +180,64 @@ EFFECTS_SCHEMA = vol.Schema({
def validate_effects(allowed_effects):
def validator(value):
value = cv.ensure_list(value)
is_list = isinstance(value, list)
if not is_list:
value = [value]
names = set()
ret = []
errors = []
for i, effect in enumerate(value):
path = [i] if is_list else []
if not isinstance(effect, dict):
raise vol.Invalid("Each effect must be a dictionary, not {}".format(type(value)))
errors.append(
vol.Invalid("Each effect must be a dictionary, not {}".format(type(value)),
path)
)
continue
if len(effect) > 1:
raise vol.Invalid("Each entry in the 'effects:' option must be a single effect.")
errors.append(
vol.Invalid("Each entry in the 'effects:' option must be a single effect.",
path)
)
continue
if not effect:
raise vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i))
errors.append(
vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i),
path)
)
continue
key = next(iter(effect.keys()))
if key.startswith('fastled'):
errors.append(
vol.Invalid("FastLED effects have been renamed to addressable effects. "
"Please use '{}'".format(key.replace('fastled', 'addressable')),
path)
)
continue
if key not in allowed_effects:
raise vol.Invalid("The effect '{}' does not exist or is not allowed for this "
"light type".format(key))
errors.append(
vol.Invalid("The effect '{}' does not exist or is not allowed for this "
"light type".format(key), path)
)
continue
effect[key] = effect[key] or {}
conf = EFFECTS_SCHEMA(effect)
try:
conf = EFFECTS_SCHEMA(effect)
except vol.Invalid as err:
err.prepend(path)
errors.append(err)
continue
name = conf[key][CONF_NAME]
if name in names:
raise vol.Invalid(u"Found the effect name '{}' twice. All effects must have "
u"unique names".format(name))
errors.append(
vol.Invalid(u"Found the effect name '{}' twice. All effects must have "
u"unique names".format(name), [i])
)
continue
names.add(name)
ret.append(conf)
if errors:
raise vol.MultipleInvalid(errors)
return ret
return validator
@ -214,7 +255,7 @@ def build_effect(full_config):
key, config = next(iter(full_config.items()))
if key == CONF_LAMBDA:
lambda_ = None
for lambda_ in process_lambda(config[CONF_LAMBDA], []):
for lambda_ in process_lambda(config[CONF_LAMBDA], [], return_type=void):
yield None
yield LambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL])
elif key == CONF_RANDOM:
@ -248,22 +289,22 @@ def build_effect(full_config):
if CONF_INTENSITY in config:
add(effect.set_intensity(config[CONF_INTENSITY]))
yield effect
elif key == CONF_FASTLED_LAMBDA:
lambda_ = None
args = [(RawExpression('FastLEDLightOutputComponent &'), 'it')]
for lambda_ in process_lambda(config[CONF_LAMBDA], args):
elif key == CONF_ADDRESSABLE_LAMBDA:
args = [(AddressableLightRef, 'it')]
for lambda_ in process_lambda(config[CONF_LAMBDA], args, return_type=void):
yield None
yield FastLEDLambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL])
elif key == CONF_FASTLED_RAINBOW:
rhs = FastLEDRainbowLightEffect.new(config[CONF_NAME])
yield AddressableLambdaLightEffect.new(config[CONF_NAME], lambda_,
config[CONF_UPDATE_INTERVAL])
elif key == CONF_ADDRESSABLE_RAINBOW:
rhs = AddressableRainbowLightEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_SPEED in config:
add(effect.set_speed(config[CONF_SPEED]))
if CONF_WIDTH in config:
add(effect.set_width(config[CONF_WIDTH]))
yield effect
elif key == CONF_FASTLED_COLOR_WIPE:
rhs = FastLEDColorWipeEffect.new(config[CONF_NAME])
elif key == CONF_ADDRESSABLE_COLOR_WIPE:
rhs = AddressableColorWipeEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_ADD_LED_INTERVAL in config:
add(effect.set_add_led_interval(config[CONF_ADD_LED_INTERVAL]))
@ -272,40 +313,41 @@ def build_effect(full_config):
colors = []
for color in config.get(CONF_COLORS, []):
colors.append(StructInitializer(
FastLEDColorWipeEffectColor,
('r', color[CONF_RED]),
('g', color[CONF_GREEN]),
('b', color[CONF_BLUE]),
AddressableColorWipeEffectColor,
('r', int(round(color[CONF_RED] * 255))),
('g', int(round(color[CONF_GREEN] * 255))),
('b', int(round(color[CONF_BLUE] * 255))),
('w', int(round(color[CONF_WHITE] * 255))),
('random', color[CONF_RANDOM]),
('num_leds', color[CONF_NUM_LEDS]),
))
if colors:
add(effect.set_colors(ArrayInitializer(*colors)))
yield effect
elif key == CONF_FASTLED_SCAN:
rhs = FastLEDScanEffect.new(config[CONF_NAME])
elif key == CONF_ADDRESSABLE_SCAN:
rhs = AddressableScanEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_MOVE_INTERVAL in config:
add(effect.set_move_interval(config[CONF_MOVE_INTERVAL]))
yield effect
elif key == CONF_FASTLED_TWINKLE:
rhs = FastLEDTwinkleEffect.new(config[CONF_NAME])
elif key == CONF_ADDRESSABLE_TWINKLE:
rhs = AddressableTwinkleEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_TWINKLE_PROBABILITY in config:
add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
if CONF_PROGRESS_INTERVAL in config:
add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL]))
yield effect
elif key == CONF_FASTLED_RANDOM_TWINKLE:
rhs = FastLEDRandomTwinkleEffect.new(config[CONF_NAME])
elif key == CONF_ADDRESSABLE_RANDOM_TWINKLE:
rhs = AddressableRandomTwinkleEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_TWINKLE_PROBABILITY in config:
add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
if CONF_PROGRESS_INTERVAL in config:
add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL]))
yield effect
elif key == CONF_FASTLED_FIREWORKS:
rhs = FastLEDFireworksEffect.new(config[CONF_NAME])
elif key == CONF_ADDRESSABLE_FIREWORKS:
rhs = AddressableFireworksEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_UPDATE_INTERVAL in config:
add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
@ -316,8 +358,8 @@ def build_effect(full_config):
if CONF_FADE_OUT_RATE in config:
add(effect.set_spark_probability(config[CONF_FADE_OUT_RATE]))
yield effect
elif key == CONF_FASTLED_FLICKER:
rhs = FastLEDFlickerEffect.new(config[CONF_NAME])
elif key == CONF_ADDRESSABLE_FLICKER:
rhs = AddressableFlickerEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_UPDATE_INTERVAL in config:
add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
@ -349,12 +391,11 @@ def setup_light_core_(light_var, mqtt_var, config):
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)
add_job(setup_light_core_, light_var, mqtt_var, config)
CORE.add_job(setup_light_core_, light_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_LIGHT'
CONF_LIGHT_TOGGLE = 'light.toggle'
LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(LightState),
@ -363,8 +404,7 @@ LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_LIGHT_TOGGLE, LIGHT_TOGGLE_ACTION_SCHEMA)
def light_toggle_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def light_toggle_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_toggle_action(template_arg)
@ -385,8 +425,7 @@ LIGHT_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_LIGHT_TURN_OFF, LIGHT_TURN_OFF_ACTION_SCHEMA)
def light_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def light_turn_off_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
@ -417,8 +456,7 @@ LIGHT_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_LIGHT_TURN_ON, LIGHT_TURN_ON_ACTION_SCHEMA)
def light_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def light_turn_on_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)
@ -465,10 +503,10 @@ def light_turn_on_to_code(config, action_id, arg_type):
def core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True,
white_value=True):
ret = mqtt.build_hass_config(data, 'light', config, include_state=True, include_command=True,
platform='mqtt_json')
ret = mqtt.build_hass_config(data, 'light', config, include_state=True, include_command=True)
if ret is None:
return None
ret['schema'] = 'json'
if brightness:
ret['brightness'] = True
if rgb:

View File

@ -3,7 +3,9 @@ import voluptuous as vol
from esphomeyaml.components import light, output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_EFFECTS, CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, setup_component, variable
from esphomeyaml.cpp_generator import get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),

View File

@ -7,7 +7,9 @@ from esphomeyaml.components.light.rgbww import validate_cold_white_colder, \
from esphomeyaml.const import CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
CONF_NAME, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
from esphomeyaml.helpers import App, get_variable, variable, setup_component
from esphomeyaml.cpp_generator import get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),

View File

@ -1,14 +1,15 @@
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, CONF_EFFECTS, CONF_COLOR_CORRECT
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
get_variable, variable, setup_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_CHIPSET, CONF_COLOR_CORRECT, CONF_DEFAULT_TRANSITION_LENGTH, \
CONF_EFFECTS, 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.cpp_generator import RawExpression, TemplateArguments, add, get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Application
TYPES = [
'NEOPIXEL',
@ -58,18 +59,18 @@ MakeFastLEDLight = Application.struct('MakeFastLEDLight')
PLATFORM_SCHEMA = cv.nameable(light.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_CHIPSET): cv.one_of(*TYPES, upper=True),
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_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)),
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
}).extend(cv.COMPONENT_SCHEMA.schema), validate)
@ -103,6 +104,8 @@ def to_code(config):
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
LIB_DEPS = 'FastLED@3.2.0'
def to_hass_config(data, config):
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False,

View File

@ -1,14 +1,15 @@
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, CONF_EFFECTS, CONF_COLOR_CORRECT
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
get_variable, variable, setup_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_COLOR_CORRECT, CONF_DATA_PIN, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER
from esphomeyaml.cpp_generator import RawExpression, TemplateArguments, add, get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Application
CHIPSETS = [
'LPD8806',
@ -35,19 +36,19 @@ MakeFastLEDLight = Application.struct('MakeFastLEDLight')
PLATFORM_SCHEMA = cv.nameable(light.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_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
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_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)),
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
}).extend(cv.COMPONENT_SCHEMA.schema))
@ -83,6 +84,8 @@ def to_code(config):
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
LIB_DEPS = 'FastLED@3.2.0'
def to_hass_config(data, config):
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False,

View File

@ -4,7 +4,9 @@ from esphomeyaml.components import light, output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, \
CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, setup_component, variable
from esphomeyaml.cpp_generator import get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),

View File

@ -0,0 +1,170 @@
import voluptuous as vol
from esphomeyaml import pins
from esphomeyaml.components import light
from esphomeyaml.components.light import AddressableLight
from esphomeyaml.components.power_supply import PowerSupplyComponent
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_CLOCK_PIN, CONF_COLOR_CORRECT, CONF_DATA_PIN, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_METHOD, \
CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, CONF_TYPE, CONF_VARIANT
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import TemplateArguments, add, get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Application, Component, global_ns
NeoPixelBusLightOutputBase = light.light_ns.class_('NeoPixelBusLightOutputBase', Component,
AddressableLight)
ESPNeoPixelOrder = light.light_ns.namespace('ESPNeoPixelOrder')
def validate_type(value):
value = cv.string(value).upper()
if 'R' not in value:
raise vol.Invalid("Must have R in type")
if 'G' not in value:
raise vol.Invalid("Must have G in type")
if 'B' not in value:
raise vol.Invalid("Must have B in type")
rest = set(value) - set('RGBW')
if rest:
raise vol.Invalid("Type has invalid color: {}".format(', '.join(rest)))
if len(set(value)) != len(value):
raise vol.Invalid("Type has duplicate color!")
return value
def validate_variant(value):
value = cv.string(value).upper()
if value == 'WS2813':
value = 'WS2812X'
if value == 'WS2812':
value = '800KBPS'
if value == 'LC8812':
value = 'SK6812'
return cv.one_of(*VARIANTS)(value)
def validate_method(value):
if value is None:
if CORE.is_esp32:
return 'ESP32_I2S_1'
if CORE.is_esp8266:
return 'ESP8266_DMA'
raise NotImplementedError
if CORE.is_esp32:
return cv.one_of(*ESP32_METHODS, upper=True, space='_')(value)
if CORE.is_esp8266:
return cv.one_of(*ESP8266_METHODS, upper=True, space='_')(value)
raise NotImplementedError
VARIANTS = {
'WS2812X': 'Ws2812x',
'SK6812': 'Sk6812',
'800KBPS': '800Kbps',
'400KBPS': '400Kbps',
}
ESP8266_METHODS = {
'ESP8266_DMA': 'NeoEsp8266Dma{}Method',
'ESP8266_UART0': 'NeoEsp8266Uart0{}Method',
'ESP8266_UART1': 'NeoEsp8266Uart1{}Method',
'ESP8266_ASYNC_UART0': 'NeoEsp8266AsyncUart0{}Method',
'ESP8266_ASYNC_UART1': 'NeoEsp8266AsyncUart1{}Method',
'BIT_BANG': 'NeoEsp8266BitBang{}Method',
}
ESP32_METHODS = {
'ESP32_I2S_0': 'NeoEsp32I2s0{}Method',
'ESP32_I2S_1': 'NeoEsp32I2s1{}Method',
'BIT_BANG': 'NeoEsp32BitBang{}Method',
}
def format_method(config):
variant = VARIANTS[config[CONF_VARIANT]]
method = config[CONF_METHOD]
if CORE.is_esp8266:
return ESP8266_METHODS[method].format(variant)
if CORE.is_esp32:
return ESP32_METHODS[method].format(variant)
raise NotImplementedError
def validate(config):
if CONF_PIN in config:
if CONF_CLOCK_PIN in config or CONF_DATA_PIN in config:
raise vol.Invalid("Cannot specify both 'pin' and 'clock_pin'+'data_pin'")
return config
if CONF_CLOCK_PIN in config:
if CONF_DATA_PIN not in config:
raise vol.Invalid("If you give clock_pin, you must also specify data_pin")
return config
raise vol.Invalid("Must specify at least one of 'pin' or 'clock_pin'+'data_pin'")
MakeNeoPixelBusLight = Application.struct('MakeNeoPixelBusLight')
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeNeoPixelBusLight),
vol.Optional(CONF_TYPE, default='GRB'): validate_type,
vol.Optional(CONF_VARIANT, default='800KBPS'): validate_variant,
vol.Optional(CONF_METHOD, default=None): validate_method,
vol.Optional(CONF_PIN): pins.output_pin,
vol.Optional(CONF_CLOCK_PIN): pins.output_pin,
vol.Optional(CONF_DATA_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=4)),
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
}).extend(cv.COMPONENT_SCHEMA.schema), validate)
def to_code(config):
type_ = config[CONF_TYPE]
has_white = 'W' in type_
if has_white:
func = App.make_neo_pixel_bus_rgbw_light
color_feat = global_ns.NeoRgbwFeature
else:
func = App.make_neo_pixel_bus_rgb_light
color_feat = global_ns.NeoRgbFeature
template = TemplateArguments(getattr(global_ns, format_method(config)), color_feat)
rhs = func(template, config[CONF_NAME])
make = variable(config[CONF_MAKE_ID], rhs, type=MakeNeoPixelBusLight.template(template))
output = make.Poutput
if CONF_PIN in config:
add(output.add_leds(config[CONF_NUM_LEDS], config[CONF_PIN]))
else:
add(output.add_leds(config[CONF_NUM_LEDS], config[CONF_CLOCK_PIN], config[CONF_DATA_PIN]))
add(output.set_pixel_order(getattr(ESPNeoPixelOrder, type_)))
if CONF_POWER_SUPPLY in config:
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
yield
add(output.set_power_supply(power_supply))
if CONF_COLOR_CORRECT in config:
add(output.set_correction(*config[CONF_COLOR_CORRECT]))
light.setup_light(make.Pstate, make.Pmqtt, config)
setup_component(output, config)
BUILD_FLAGS = '-DUSE_NEO_PIXEL_BUS_LIGHT'
LIB_DEPS = 'NeoPixelBus@2.4.1'
def to_hass_config(data, config):
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False,
white_value='W' in config[CONF_TYPE])

View File

@ -1,10 +1,12 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light, output
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_EFFECTS
from esphomeyaml.helpers import App, get_variable, variable, setup_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, \
CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED
from esphomeyaml.cpp_generator import get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),

View File

@ -1,10 +1,12 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light, output
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE, CONF_EFFECTS
from esphomeyaml.helpers import App, get_variable, variable, setup_component
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, \
CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE
from esphomeyaml.cpp_generator import get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),

View File

@ -1,11 +1,13 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light, output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BLUE, CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, \
CONF_NAME, CONF_RED, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
from esphomeyaml.helpers import App, get_variable, variable, setup_component
from esphomeyaml.cpp_generator import get_variable, variable
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App
def validate_color_temperature(value):

View File

@ -6,9 +6,11 @@ from esphomeyaml.automation import ACTION_REGISTRY, LambdaAction
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_ID, CONF_LEVEL, \
CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE
from esphomeyaml.core import ESPHomeYAMLError, Lambda
from esphomeyaml.helpers import App, Pvariable, RawExpression, TemplateArguments, add, \
esphomelib_ns, global_ns, process_lambda, statement, Component
from esphomeyaml.core import EsphomeyamlError, Lambda, CORE
from esphomeyaml.cpp_generator import Pvariable, RawExpression, add, process_lambda, statement
from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns, void
from esphomeyaml.py_compat import text_type
LOG_LEVELS = {
'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE,
@ -32,14 +34,14 @@ LOG_LEVEL_TO_ESP_LOG = {
LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
# pylint: disable=invalid-name
is_log_level = vol.All(vol.Upper, cv.one_of(*LOG_LEVELS))
is_log_level = cv.one_of(*LOG_LEVELS, upper=True)
def validate_local_no_higher_than_global(value):
global_level = value.get(CONF_LEVEL, 'DEBUG')
for tag, level in value.get(CONF_LOGS, {}).iteritems():
for tag, level in value.get(CONF_LOGS, {}).items():
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 "
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
@ -64,14 +66,37 @@ def to_code(config):
add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
if CONF_LEVEL in config:
add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]]))
for tag, level in config.get(CONF_LOGS, {}).iteritems():
for tag, level in config.get(CONF_LOGS, {}).items():
add(log.set_log_level(tag, LOG_LEVELS[level]))
def required_build_flags(config):
flags = []
if CONF_LEVEL in config:
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]]))
return None
flags.append(u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]])))
this_severity = LOG_LEVEL_SEVERITY.index(config[CONF_LEVEL])
verbose_severity = LOG_LEVEL_SEVERITY.index('VERBOSE')
is_at_least_verbose = this_severity >= verbose_severity
has_serial_logging = config.get(CONF_BAUD_RATE) != 0
if CORE.is_esp8266 and has_serial_logging and is_at_least_verbose:
flags.append(u"-DDEBUG_ESP_PORT=Serial")
flags.append(u"-DLWIP_DEBUG")
DEBUG_COMPONENTS = {
'HTTP_CLIENT',
'HTTP_SERVER',
'HTTP_UPDATE',
'OTA',
'SSL',
'TLS_MEM',
'UPDATER',
'WIFI',
}
for comp in DEBUG_COMPONENTS:
flags.append(u"-DDEBUG_ESP_{}".format(comp))
if CORE.is_esp32 and is_at_least_verbose:
flags.append('-DCORE_DEBUG_LEVEL=5')
return flags
def maybe_simple_message(schema):
@ -97,7 +122,7 @@ def validate_printf(value):
[cCdiouxXeEfgGaAnpsSZ] # type
) | # OR
%%) # literal "%%"
"""
""" # noqa
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X)
if len(matches) != len(value[CONF_ARGS]):
raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!"
@ -108,21 +133,20 @@ def validate_printf(value):
CONF_LOGGER_LOG = 'logger.log'
LOGGER_LOG_ACTION_SCHEMA = vol.All(maybe_simple_message({
vol.Required(CONF_FORMAT): cv.string,
vol.Optional(CONF_ARGS, default=list): vol.All(cv.ensure_list, [cv.lambda_]),
vol.Optional(CONF_LEVEL, default="DEBUG"): vol.All(vol.Upper, cv.one_of(*LOG_LEVEL_TO_ESP_LOG)),
vol.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
vol.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(*LOG_LEVEL_TO_ESP_LOG, upper=True),
vol.Optional(CONF_TAG, default="main"): cv.string,
}), validate_printf)
@ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA)
def logger_log_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def logger_log_action_to_code(config, action_id, arg_type, template_arg):
esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]]
args = [RawExpression(unicode(x)) for x in config[CONF_ARGS]]
args = [RawExpression(text_type(x)) for x in config[CONF_ARGS]]
text = unicode(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
text = text_type(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]):
for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')], return_type=void):
yield None
rhs = LambdaAction.new(template_arg, lambda_)
type = LambdaAction.template(template_arg)

View File

@ -7,17 +7,18 @@ from esphomeyaml import automation
from esphomeyaml.automation import ACTION_REGISTRY
from esphomeyaml.components import logger
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \
CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \
CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \
CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE, \
CONF_STATE_TOPIC, CONF_MQTT, CONF_ESPHOMEYAML, CONF_NAME, CONF_AVAILABILITY, \
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_INTERNAL
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \
uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef, Component, Action, Trigger
from esphomeyaml.const import CONF_AVAILABILITY, CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, \
CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, \
CONF_ESPHOMEYAML, CONF_ID, CONF_INTERNAL, CONF_KEEPALIVE, CONF_LEVEL, CONF_LOG_TOPIC, \
CONF_MQTT, CONF_NAME, CONF_ON_JSON_MESSAGE, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, \
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PORT, CONF_QOS, CONF_REBOOT_TIMEOUT, \
CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_STATE_TOPIC, CONF_TOPIC, \
CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE
from esphomeyaml.core import EsphomeyamlError
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, RawExpression, \
StructInitializer, TemplateArguments, add, process_lambda, templatable
from esphomeyaml.cpp_types import Action, App, Component, JsonObjectConstRef, JsonObjectRef, \
Trigger, bool_, esphomelib_ns, optional, std_string, uint8, void
def validate_message_just_topic(value):
@ -48,12 +49,14 @@ MQTTJsonMessageTrigger = mqtt_ns.class_('MQTTJsonMessageTrigger',
MQTTComponent = mqtt_ns.class_('MQTTComponent', Component)
def validate_broker(value):
value = cv.string_strict(value)
if u':' in value:
raise vol.Invalid(u"Please specify the port using the port: option")
if not value:
raise vol.Invalid(u"Broker cannot be empty")
def validate_config(value):
if CONF_PORT not in value:
parts = value[CONF_BROKER].split(u':')
if len(parts) == 2:
value[CONF_BROKER] = parts[0]
value[CONF_PORT] = cv.port(parts[1])
else:
value[CONF_PORT] = 1883
return value
@ -64,14 +67,14 @@ def validate_fingerprint(value):
return value
CONFIG_SCHEMA = vol.Schema({
CONFIG_SCHEMA = vol.All(vol.Schema({
cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent),
vol.Required(CONF_BROKER): validate_broker,
vol.Optional(CONF_PORT, default=1883): cv.port,
vol.Required(CONF_BROKER): cv.string_strict,
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_USERNAME, default=''): cv.string,
vol.Optional(CONF_PASSWORD, default=''): cv.string,
vol.Optional(CONF_CLIENT_ID): vol.All(cv.string, vol.Length(max=23)),
vol.Optional(CONF_DISCOVERY): cv.boolean,
vol.Optional(CONF_DISCOVERY): vol.Any(cv.boolean, cv.one_of("CLEAN", upper=True)),
vol.Optional(CONF_DISCOVERY_RETAIN): cv.boolean,
vol.Optional(CONF_DISCOVERY_PREFIX): cv.publish_topic,
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
@ -82,20 +85,21 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_LEVEL): logger.is_log_level,
}), validate_message_just_topic),
vol.Optional(CONF_SSL_FINGERPRINTS): vol.All(cv.only_on_esp8266,
cv.ensure_list, [validate_fingerprint]),
cv.ensure_list(validate_fingerprint)),
vol.Optional(CONF_KEEPALIVE): cv.positive_time_period_seconds,
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_ON_MESSAGE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
vol.Required(CONF_TOPIC): cv.subscribe_topic,
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
vol.Optional(CONF_QOS): cv.mqtt_qos,
vol.Optional(CONF_PAYLOAD): cv.string_strict,
}),
vol.Optional(CONF_ON_JSON_MESSAGE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTJsonMessageTrigger),
vol.Required(CONF_TOPIC): cv.subscribe_topic,
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
}),
})
}), validate_config)
def exp_mqtt_message(config):
@ -116,12 +120,17 @@ def to_code(config):
config[CONF_USERNAME], config[CONF_PASSWORD])
mqtt = Pvariable(config[CONF_ID], rhs)
if not config.get(CONF_DISCOVERY, True):
discovery = config.get(CONF_DISCOVERY, True)
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
if not discovery:
add(mqtt.disable_discovery())
elif discovery == "CLEAN":
add(mqtt.set_discovery_info(discovery_prefix, discovery_retain, True))
elif 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_TOPIC_PREFIX in config:
add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
@ -169,8 +178,12 @@ def to_code(config):
add(mqtt.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
for conf in config.get(CONF_ON_MESSAGE, []):
rhs = mqtt.make_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS])
rhs = App.register_component(mqtt.make_message_trigger(conf[CONF_TOPIC]))
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
if CONF_QOS in conf:
add(trigger.set_qos(conf[CONF_QOS]))
if CONF_PAYLOAD in conf:
add(trigger.set_payload(conf[CONF_PAYLOAD]))
automation.build_automation(trigger, std_string, conf)
for conf in config.get(CONF_ON_JSON_MESSAGE, []):
@ -189,8 +202,7 @@ MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({
@ACTION_REGISTRY.register(CONF_MQTT_PUBLISH, MQTT_PUBLISH_ACTION_SCHEMA)
def mqtt_publish_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def mqtt_publish_action_to_code(config, action_id, arg_type, template_arg):
rhs = App.Pget_mqtt_client().Pmake_publish_action(template_arg)
type = MQTTPublishAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
@ -222,8 +234,7 @@ MQTT_PUBLISH_JSON_ACTION_SCHEMA = vol.Schema({
@ACTION_REGISTRY.register(CONF_MQTT_PUBLISH_JSON, MQTT_PUBLISH_JSON_ACTION_SCHEMA)
def mqtt_publish_json_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def mqtt_publish_json_action_to_code(config, action_id, arg_type, template_arg):
rhs = App.Pget_mqtt_client().Pmake_publish_json_action(template_arg)
type = MQTTPublishJsonAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
@ -231,7 +242,8 @@ def mqtt_publish_json_action_to_code(config, action_id, arg_type):
yield None
add(action.set_topic(template_))
for lambda_ in process_lambda(config[CONF_PAYLOAD], [(arg_type, 'x'), (JsonObjectRef, 'root')]):
for lambda_ in process_lambda(config[CONF_PAYLOAD], [(arg_type, 'x'), (JsonObjectRef, 'root')],
return_type=void):
yield None
add(action.set_payload(lambda_))
if CONF_QOS in config:
@ -254,12 +266,11 @@ def get_default_topic_for(data, component_type, name, suffix):
sanitized_name, suffix)
def build_hass_config(data, component_type, config, include_state=True, include_command=True,
platform='mqtt'):
def build_hass_config(data, component_type, config, include_state=True, include_command=True):
if config.get(CONF_INTERNAL, False):
return None
ret = OrderedDict()
ret['platform'] = platform
ret['platform'] = 'mqtt'
ret['name'] = config[CONF_NAME]
if include_state:
default = get_default_topic_for(data, component_type, config[CONF_NAME], 'state')
@ -282,7 +293,7 @@ def build_hass_config(data, component_type, config, include_state=True, include_
class GenerateHassConfigData(object):
def __init__(self, config):
if 'mqtt' not in config:
raise ESPHomeYAMLError("Cannot generate Home Assistant MQTT config if MQTT is not "
raise EsphomeyamlError("Cannot generate Home Assistant MQTT config if MQTT is not "
"used!")
mqtt = config[CONF_MQTT]
self.topic_prefix = mqtt.get(CONF_TOPIC_PREFIX, config[CONF_ESPHOMEYAML][CONF_NAME])
@ -308,3 +319,21 @@ class GenerateHassConfigData(object):
CONF_PAYLOAD_AVAILABLE: birth_message[CONF_PAYLOAD],
CONF_PAYLOAD_NOT_AVAILABLE: will_message[CONF_PAYLOAD],
}
def setup_mqtt_component(obj, config):
if CONF_RETAIN in config:
add(obj.set_retain(config[CONF_RETAIN]))
if not config.get(CONF_DISCOVERY, True):
add(obj.disable_discovery())
if CONF_STATE_TOPIC in config:
add(obj.set_custom_state_topic(config[CONF_STATE_TOPIC]))
if CONF_COMMAND_TOPIC in config:
add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC]))
if CONF_AVAILABILITY in config:
availability = config[CONF_AVAILABILITY]
if not availability:
add(obj.disable_availability())
else:
add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
availability[CONF_PAYLOAD_NOT_AVAILABLE]))

View File

@ -1,18 +1,18 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import output
from esphomeyaml.const import (CONF_DATA_PIN, CONF_CLOCK_PIN, CONF_NUM_CHANNELS,
CONF_NUM_CHIPS, CONF_BIT_DEPTH, CONF_ID,
CONF_UPDATE_ON_BOOT)
from esphomeyaml.helpers import (gpio_output_pin_expression, App, Pvariable,
add, setup_component, Component)
import esphomeyaml.config_validation as cv
from esphomeyaml.const import (CONF_BIT_DEPTH, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_ID,
CONF_NUM_CHANNELS, CONF_NUM_CHIPS, CONF_UPDATE_ON_BOOT)
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, Component
MY9231OutputComponent = output.output_ns.class_('MY9231OutputComponent', Component)
MULTI_CONF = True
MY9231_SCHEMA = vol.Schema({
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(MY9231OutputComponent),
vol.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
@ -20,33 +20,27 @@ MY9231_SCHEMA = vol.Schema({
vol.Range(3, 1020)),
vol.Optional(CONF_NUM_CHIPS): vol.All(vol.Coerce(int),
vol.Range(1, 255)),
vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int),
cv.one_of(8, 12, 14, 16)),
vol.Optional(CONF_BIT_DEPTH): cv.one_of(8, 12, 14, 16, int=True),
vol.Optional(CONF_UPDATE_ON_BOOT): vol.Coerce(bool),
}).extend(cv.COMPONENT_SCHEMA.schema)
CONFIG_SCHEMA = vol.All(cv.ensure_list, [MY9231_SCHEMA])
def to_code(config):
for conf in config:
di = None
for di in gpio_output_pin_expression(conf[CONF_DATA_PIN]):
yield
dcki = None
for dcki in gpio_output_pin_expression(conf[CONF_CLOCK_PIN]):
yield
rhs = App.make_my9231_component(di, dcki)
my9231 = Pvariable(conf[CONF_ID], rhs)
if CONF_NUM_CHANNELS in conf:
add(my9231.set_num_channels(conf[CONF_NUM_CHANNELS]))
if CONF_NUM_CHIPS in conf:
add(my9231.set_num_chips(conf[CONF_NUM_CHIPS]))
if CONF_BIT_DEPTH in conf:
add(my9231.set_bit_depth(conf[CONF_BIT_DEPTH]))
if CONF_UPDATE_ON_BOOT in conf:
add(my9231.set_update(conf[CONF_UPDATE_ON_BOOT]))
setup_component(my9231, conf)
for di in gpio_output_pin_expression(config[CONF_DATA_PIN]):
yield
for dcki in gpio_output_pin_expression(config[CONF_CLOCK_PIN]):
yield
rhs = App.make_my9231_component(di, dcki)
my9231 = Pvariable(config[CONF_ID], rhs)
if CONF_NUM_CHANNELS in config:
add(my9231.set_num_channels(config[CONF_NUM_CHANNELS]))
if CONF_NUM_CHIPS in config:
add(my9231.set_num_chips(config[CONF_NUM_CHIPS]))
if CONF_BIT_DEPTH in config:
add(my9231.set_bit_depth(config[CONF_BIT_DEPTH]))
if CONF_UPDATE_ON_BOOT in config:
add(my9231.set_update(config[CONF_UPDATE_ON_BOOT]))
setup_component(my9231, config)
BUILD_FLAGS = '-DUSE_MY9231_OUTPUT'

View File

@ -2,12 +2,11 @@ import logging
import voluptuous as vol
from esphomeyaml import core
import esphomeyaml.config_validation as cv
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, esphomelib_ns, Component
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
_LOGGER = logging.getLogger(__name__)
@ -35,11 +34,11 @@ def to_code(config):
def get_port(config):
if CONF_PORT in config[CONF_OTA]:
return config[CONF_OTA][CONF_PORT]
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if CORE.is_esp32:
return 3232
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if CORE.is_esp8266:
return 8266
raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.")
raise NotImplementedError
def get_auth(config):
@ -51,6 +50,8 @@ REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA'
def lib_deps(config):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return ['ArduinoOTA', 'Update', 'ESPmDNS']
return ['Hash', 'ESP8266mDNS', 'ArduinoOTA']
if CORE.is_esp32:
return ['Update', 'ESPmDNS']
if CORE.is_esp8266:
return ['Hash', 'ESP8266mDNS']
raise NotImplementedError

View File

@ -4,8 +4,9 @@ from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.components.power_supply import PowerSupplyComponent
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY, CONF_ID, CONF_LEVEL
from esphomeyaml.helpers import add, esphomelib_ns, get_variable, TemplateArguments, Pvariable, \
templatable, float_, add_job, Action
from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import add, get_variable, Pvariable, templatable
from esphomeyaml.cpp_types import esphomelib_ns, Action, float_
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@ -26,7 +27,9 @@ FLOAT_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FLOAT_OUTPUT_SCHEMA.schema
output_ns = esphomelib_ns.namespace('output')
BinaryOutput = output_ns.class_('BinaryOutput')
BinaryOutputPtr = BinaryOutput.operator('ptr')
FloatOutput = output_ns.class_('FloatOutput', BinaryOutput)
FloatOutputPtr = FloatOutput.operator('ptr')
# Actions
TurnOffAction = output_ns.class_('TurnOffAction', Action)
@ -47,7 +50,12 @@ def setup_output_platform_(obj, config, skip_power_supply=False):
def setup_output_platform(obj, config, skip_power_supply=False):
add_job(setup_output_platform_, obj, config, skip_power_supply)
CORE.add_job(setup_output_platform_, obj, config, skip_power_supply)
def register_output(var, config):
output_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
CORE.add_job(setup_output_platform_, output_var, config)
BUILD_FLAGS = '-DUSE_OUTPUT'
@ -60,8 +68,7 @@ OUTPUT_TURN_ON_ACTION = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_ON_ACTION)
def output_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def output_turn_on_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)
@ -76,8 +83,7 @@ OUTPUT_TURN_OFF_ACTION = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_OFF_ACTION)
def output_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def output_turn_off_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
@ -93,8 +99,7 @@ OUTPUT_SET_LEVEL_ACTION = vol.Schema({
@ACTION_REGISTRY.register(CONF_OUTPUT_SET_LEVEL, OUTPUT_SET_LEVEL_ACTION)
def output_set_level_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
def output_set_level_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_set_level_action(template_arg)

View File

@ -0,0 +1,66 @@
import voluptuous as vol
from esphomeyaml.components import output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE
from esphomeyaml.cpp_generator import process_lambda, variable
from esphomeyaml.cpp_types import std_vector
CustomBinaryOutputConstructor = output.output_ns.class_('CustomBinaryOutputConstructor')
CustomFloatOutputConstructor = output.output_ns.class_('CustomFloatOutputConstructor')
BINARY_SCHEMA = output.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(CustomBinaryOutputConstructor),
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Required(CONF_OUTPUTS):
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(output.BinaryOutput),
})),
})
FLOAT_SCHEMA = output.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(CustomFloatOutputConstructor),
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Required(CONF_OUTPUTS):
cv.ensure_list(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(output.FloatOutput),
})),
})
def validate_custom_output(value):
if not isinstance(value, dict):
raise vol.Invalid("Value must be dict")
if CONF_TYPE not in value:
raise vol.Invalid("type not specified!")
type = cv.string_strict(value[CONF_TYPE]).lower()
value[CONF_TYPE] = type
if type == 'binary':
return BINARY_SCHEMA(value)
if type == 'float':
return FLOAT_SCHEMA(value)
raise vol.Invalid("type must either be binary or float, not {}!".format(type))
PLATFORM_SCHEMA = validate_custom_output
def to_code(config):
type = config[CONF_TYPE]
if type == 'binary':
ret_type = output.BinaryOutputPtr
klass = CustomBinaryOutputConstructor
else:
ret_type = output.FloatOutputPtr
klass = CustomFloatOutputConstructor
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=std_vector.template(ret_type)):
yield
rhs = klass(template_)
custom = variable(config[CONF_ID], rhs)
for i, sens in enumerate(config[CONF_OUTPUTS]):
output.register_output(custom.get_output(i), sens)
BUILD_FLAGS = '-DUSE_CUSTOM_OUTPUT'

View File

@ -3,9 +3,10 @@ import voluptuous as vol
from esphomeyaml import pins
from esphomeyaml.components import output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266, CONF_FREQUENCY
from esphomeyaml.helpers import App, Component, Pvariable, gpio_output_pin_expression, \
setup_component, add
from esphomeyaml.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, Component
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]

View File

@ -1,11 +1,12 @@
import voluptuous as vol
from esphomeyaml import pins
import esphomeyaml.config_validation as cv
from esphomeyaml.components import output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_PIN
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression, setup_component, \
Component
from esphomeyaml.cpp_generator import Pvariable
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, Component
GPIOBinaryOutputComponent = output.output_ns.class_('GPIOBinaryOutputComponent',
output.BinaryOutput, Component)

View File

@ -1,11 +1,13 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import output
import esphomeyaml.config_validation as cv
from esphomeyaml.const import APB_CLOCK_FREQ, CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \
CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, Pvariable, add, setup_component, Component
from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import setup_component
from esphomeyaml.cpp_types import App, Component
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]

Some files were not shown because too many files have changed in this diff Show More