Compare commits
182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b71b14cd06 | ||
|
|
145e7b00ee | ||
|
|
07c80dfcda | ||
|
|
0e52c9a778 | ||
|
|
94bd179256 | ||
|
|
11c38ca4e8 | ||
|
|
dc71d11a21 | ||
|
|
e385c8435b | ||
|
|
13eca6012d | ||
|
|
cb3e3e024d | ||
|
|
79bdec32b8 | ||
|
|
c153dba5bc | ||
|
|
fa2c2917c1 | ||
|
|
2a06f4dbf4 | ||
|
|
49b618bb0c | ||
|
|
20ec3900be | ||
|
|
605be1a6ec | ||
|
|
33b67de32e | ||
|
|
1ffedb291c | ||
|
|
8f251848ef | ||
|
|
4046a16d85 | ||
|
|
5ed987adcd | ||
|
|
a463c59733 | ||
|
|
1726c4237b | ||
|
|
f1241af91d | ||
|
|
7ae6777fd6 | ||
|
|
a169d37557 | ||
|
|
be21aa786d | ||
|
|
9a881100e6 | ||
|
|
c2f88776c7 | ||
|
|
846fcb8ccd | ||
|
|
44495c919c | ||
|
|
d4ce7699d4 | ||
|
|
318bb8b254 | ||
|
|
1ad65516cf | ||
|
|
d4c7e6c634 | ||
|
|
9b07bb6608 | ||
|
|
f368255739 | ||
|
|
083c2fce05 | ||
|
|
c99d4e2815 | ||
|
|
39457f7b8c | ||
|
|
01aaf14078 | ||
|
|
9a939d2d27 | ||
|
|
7f91141df2 | ||
|
|
06eeed9ee9 | ||
|
|
5655b5fe10 | ||
|
|
15331edb78 | ||
|
|
4f375757a5 | ||
|
|
0dec7cfbf8 | ||
|
|
f51d301d53 | ||
|
|
c3b3ba4923 | ||
|
|
30e7797577 | ||
|
|
0e5cabadc1 | ||
|
|
58f4fa53d0 | ||
|
|
04dc848620 | ||
|
|
8203b8fcd3 | ||
|
|
0ad61f4a95 | ||
|
|
9fd4076ab8 | ||
|
|
a9e799cb06 | ||
|
|
3ec931ffa4 | ||
|
|
e3094d9689 | ||
|
|
3594779401 | ||
|
|
94978d0063 | ||
|
|
415e12309b | ||
|
|
aa5f887ff3 | ||
|
|
28561ea6a4 | ||
|
|
3b3ff4fea9 | ||
|
|
6b8125f5f2 | ||
|
|
ab43390983 | ||
|
|
611592170b | ||
|
|
8a58ff91c3 | ||
|
|
27b86d89b0 | ||
|
|
36da3b85d5 | ||
|
|
d7d3a4aa36 | ||
|
|
4e4ffc3a24 | ||
|
|
4dce7fa103 | ||
|
|
467ef9902f | ||
|
|
486174073b | ||
|
|
e9d9de448e | ||
|
|
08c16020c6 | ||
|
|
0ade9baf65 | ||
|
|
2fab7e73b9 | ||
|
|
af4e2bf61d | ||
|
|
6a2e9a8503 | ||
|
|
74fefea5bb | ||
|
|
21c22fe04a | ||
|
|
70206df8b5 | ||
|
|
15732ca465 | ||
|
|
8e0f4f93d4 | ||
|
|
361baea17f | ||
|
|
8bbfbc4cc1 | ||
|
|
8c5d12df51 | ||
|
|
25c66ed8ca | ||
|
|
7a55521807 | ||
|
|
d04de7baeb | ||
|
|
4aeb756388 | ||
|
|
92b6ed4180 | ||
|
|
9efd9f4fe8 | ||
|
|
34fc2147a4 | ||
|
|
629f2b128e | ||
|
|
81bc400340 | ||
|
|
75628b96a1 | ||
|
|
b1f7ed4fdc | ||
|
|
b8d7185d99 | ||
|
|
2d20a1c0fb | ||
|
|
820067ae5a | ||
|
|
db8313e0d5 | ||
|
|
27a77c685d | ||
|
|
e34365dc7c | ||
|
|
8d395e5338 | ||
|
|
6f54afec00 | ||
|
|
6a24145be6 | ||
|
|
4a2cdbf31c | ||
|
|
1d75ed1ff4 | ||
|
|
76b1c6f47b | ||
|
|
06371c9e2d | ||
|
|
a9c130dd50 | ||
|
|
1f82c1a483 | ||
|
|
cb28429231 | ||
|
|
f2cd2ec178 | ||
|
|
37360bb797 | ||
|
|
6c1dc0f2b3 | ||
|
|
f7671f0c90 | ||
|
|
639b97ccb2 | ||
|
|
0374b3a0b3 | ||
|
|
7ce753b76f | ||
|
|
05a1089ed2 | ||
|
|
71947bb6ac | ||
|
|
ef54e33b70 | ||
|
|
12f20fc3cf | ||
|
|
ffdcddc18e | ||
|
|
85d70eb5a0 | ||
|
|
ffb793177a | ||
|
|
d3f2fab88a | ||
|
|
0fa52d0ce6 | ||
|
|
cf264a2743 | ||
|
|
433b605bef | ||
|
|
6e60c6493a | ||
|
|
4e63bc96d5 | ||
|
|
ce4b339d16 | ||
|
|
ab6d293d0d | ||
|
|
5e5137960d | ||
|
|
8dba37846b | ||
|
|
5cd82d7c25 | ||
|
|
bc8354bad5 | ||
|
|
2abbe1bca3 | ||
|
|
74c70509c2 | ||
|
|
1576e1847e | ||
|
|
9ea9b4b102 | ||
|
|
490743c26e | ||
|
|
1c7bddd005 | ||
|
|
5c39f73fda | ||
|
|
2fc78a1b33 | ||
|
|
03249780fd | ||
|
|
5170a7cdf4 | ||
|
|
5680de79a9 | ||
|
|
61bd2a3a44 | ||
|
|
7802c63f5a | ||
|
|
94bef2a5a4 | ||
|
|
12c4b0788c | ||
|
|
b13cc3a4a5 | ||
|
|
c5d9bc5452 | ||
|
|
6c6d21a7ab | ||
|
|
9bb06782b2 | ||
|
|
a827b51887 | ||
|
|
2c30d80490 | ||
|
|
e0acdc3ae0 | ||
|
|
36a3f96011 | ||
|
|
6ae8e3495f | ||
|
|
6aa449115f | ||
|
|
0be29d27d5 | ||
|
|
68fa7489a2 | ||
|
|
d7699c93d6 | ||
|
|
a04438e924 | ||
|
|
e063f2aaea | ||
|
|
7b630bfb8b | ||
|
|
ec3366cce0 | ||
|
|
135117714b | ||
|
|
91e6304505 | ||
|
|
361a9da868 | ||
|
|
31d7656c07 | ||
|
|
d1a7751dc9 |
@@ -106,3 +106,5 @@ venv.bak/
|
||||
config/
|
||||
examples/
|
||||
Dockerfile
|
||||
.git/
|
||||
tests/build/
|
||||
|
||||
49
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help esphomelib improve
|
||||
|
||||
---
|
||||
|
||||
<!-- Thanks for reporting a bug for this project. READ THIS FIRST:
|
||||
- Please make sure to submit issues in the right GitHub repository, if unsure just post it here:
|
||||
- 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! Otherwise the issue may be closed without a comment.
|
||||
-->
|
||||
|
||||
**Operating environment (Hass.io/Docker/pip/etc.):**
|
||||
<!--
|
||||
Please provide details about your environment.
|
||||
-->
|
||||
|
||||
**ESP (ESP32/ESP8266/Board/Sonoff):**
|
||||
<!--
|
||||
Please provide details about which ESP you're using.
|
||||
-->
|
||||
|
||||
**Affected component:**
|
||||
<!--
|
||||
Please add the link to the documentation at https://esphomelib.com/esphomeyaml/index.html of the component in question.
|
||||
-->
|
||||
|
||||
|
||||
**Description of problem:**
|
||||
|
||||
|
||||
**Problem-relevant YAML-configuration entries:**
|
||||
```yaml
|
||||
PASTE YAML FILE HERE
|
||||
```
|
||||
|
||||
**Traceback (if applicable):**
|
||||
<!--
|
||||
Please copy the traceback here if compilation is failing. If possible, also connect to the ESP and copy its logs into the backticks.
|
||||
-->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
**Additional information:**
|
||||
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Feature request
|
||||
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/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. -->
|
||||
|
||||
**Additional context:**
|
||||
<!-- Add any other context about the feature request here. -->
|
||||
13
.github/ISSUE_TEMPLATE/new-integration.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: New integration
|
||||
about: Suggest a new integration for esphomelib
|
||||
|
||||
---
|
||||
|
||||
DO NOT POST NEW INTEGRATION REQUESTS HERE!
|
||||
|
||||
Please post all new integration requests in the esphomelib repository:
|
||||
|
||||
https://github.com/OttoWinter/esphomelib/issues
|
||||
|
||||
Thank you!
|
||||
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
## Description:
|
||||
|
||||
|
||||
**Related issue (if applicable):** fixes <link to issue>
|
||||
|
||||
**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>
|
||||
|
||||
## Checklist:
|
||||
- [ ] The code change is tested and works locally.
|
||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
|
||||
If user exposed functionality or configuration variables are added/changed:
|
||||
- [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
|
||||
2
.gitignore
vendored
@@ -104,3 +104,5 @@ venv.bak/
|
||||
.mypy_cache/
|
||||
|
||||
config/
|
||||
tests/build/
|
||||
tests/.esphomeyaml/
|
||||
|
||||
314
.gitlab-ci.yml
Normal file
@@ -0,0 +1,314 @@
|
||||
---
|
||||
# Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
|
||||
stages:
|
||||
- lint
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
||||
.lint: &lint
|
||||
stage: lint
|
||||
before_script:
|
||||
- pip install -e .
|
||||
tags:
|
||||
- python2.7
|
||||
- esphomeyaml-lint
|
||||
|
||||
.test: &test
|
||||
stage: test
|
||||
before_script:
|
||||
- pip install -e .
|
||||
tags:
|
||||
- python2.7
|
||||
- esphomeyaml-test
|
||||
variables:
|
||||
TZ: UTC
|
||||
|
||||
.docker-builder: &docker-builder
|
||||
before_script:
|
||||
- docker info
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
|
||||
services:
|
||||
- docker:dind
|
||||
tags:
|
||||
- hassio-builder
|
||||
|
||||
flake8:
|
||||
<<: *lint
|
||||
script:
|
||||
- flake8 esphomeyaml
|
||||
|
||||
pylint:
|
||||
<<: *lint
|
||||
script:
|
||||
- pylint esphomeyaml
|
||||
|
||||
test1:
|
||||
<<: *test
|
||||
script:
|
||||
- esphomeyaml tests/test1.yaml compile
|
||||
|
||||
test2:
|
||||
<<: *test
|
||||
script:
|
||||
- esphomeyaml tests/test2.yaml compile
|
||||
|
||||
.build-hassio: &build-hassio
|
||||
<<: *docker-builder
|
||||
stage: build
|
||||
script:
|
||||
- docker run --rm --privileged hassioaddons/qemu-user-static: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}}"
|
||||
- echo "Build from ${BUILD_FROM}"
|
||||
- echo "Add-on version ${ADDON_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 "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}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" \
|
||||
--tag "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
--file "docker/Dockerfile.hassio" \
|
||||
.
|
||||
- |
|
||||
if [ "${DO_PUSH:-true}" = true ]; then
|
||||
echo "Pushing to CI registry"
|
||||
docker push ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}
|
||||
docker push ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev
|
||||
fi
|
||||
|
||||
# Generic deploy template
|
||||
.deploy-release: &deploy-release
|
||||
<<: *docker-builder
|
||||
stage: deploy
|
||||
script:
|
||||
- version="${CI_COMMIT_TAG#v}"
|
||||
- echo "Publishing release version ${version}"
|
||||
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
|
||||
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
|
||||
|
||||
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
|
||||
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
|
||||
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
|
||||
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
|
||||
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- |
|
||||
docker tag \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
|
||||
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- |
|
||||
docker tag \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
|
||||
.deploy-beta: &deploy-beta
|
||||
<<: *docker-builder
|
||||
stage: deploy
|
||||
script:
|
||||
- version="${CI_COMMIT_TAG#v}"
|
||||
- echo "Publishing beta version ${version}"
|
||||
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
|
||||
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
|
||||
|
||||
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
|
||||
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
|
||||
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
|
||||
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- |
|
||||
docker tag \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+b\d+$/
|
||||
except:
|
||||
- /^(?!rc).+@/
|
||||
|
||||
# Build jobs
|
||||
build:normal:
|
||||
<<: *docker-builder
|
||||
stage: build
|
||||
script:
|
||||
- docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" .
|
||||
|
||||
.build-hassio-edge: &build-hassio-edge
|
||||
<<: *build-hassio
|
||||
except:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- /^v\d+\.\d+\.\d+b\d+$/
|
||||
|
||||
.build-hassio-release: &build-hassio-release
|
||||
<<: *build-hassio
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- /^v\d+\.\d+\.\d+b\d+$/
|
||||
|
||||
build:hassio-armhf-edge:
|
||||
<<: *build-hassio-edge
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
DO_PUSH: "false"
|
||||
|
||||
build:hassio-armhf:
|
||||
<<: *build-hassio-release
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
|
||||
#build:hassio-aarch64-edge:
|
||||
# <<: *build-hassio-edge
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
# DO_PUSH: "false"
|
||||
|
||||
#build:hassio-aarch64:
|
||||
# <<: *build-hassio-release
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
|
||||
build:hassio-i386-edge:
|
||||
<<: *build-hassio-edge
|
||||
variables:
|
||||
ADDON_ARCH: i386
|
||||
DO_PUSH: "false"
|
||||
|
||||
build:hassio-i386:
|
||||
<<: *build-hassio-release
|
||||
variables:
|
||||
ADDON_ARCH: i386
|
||||
|
||||
build:hassio-amd64-edge:
|
||||
<<: *build-hassio-edge
|
||||
variables:
|
||||
ADDON_ARCH: amd64
|
||||
DO_PUSH: "false"
|
||||
|
||||
build:hassio-amd64:
|
||||
<<: *build-hassio-release
|
||||
variables:
|
||||
ADDON_ARCH: amd64
|
||||
|
||||
# Deploy jobs
|
||||
deploy-release:armhf:
|
||||
<<: *deploy-release
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
|
||||
deploy-beta:armhf:
|
||||
<<: *deploy-beta
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
|
||||
#deploy-release:aarch64:
|
||||
# <<: *deploy-release
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
|
||||
#deploy-beta:aarch64:
|
||||
# <<: *deploy-beta
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
|
||||
deploy-release:i386:
|
||||
<<: *deploy-release
|
||||
variables:
|
||||
ADDON_ARCH: i386
|
||||
|
||||
deploy-beta:i386:
|
||||
<<: *deploy-beta
|
||||
variables:
|
||||
ADDON_ARCH: i386
|
||||
|
||||
deploy-release:amd64:
|
||||
<<: *deploy-release
|
||||
variables:
|
||||
ADDON_ARCH: amd64
|
||||
|
||||
deploy-beta:amd64:
|
||||
<<: *deploy-beta
|
||||
variables:
|
||||
ADDON_ARCH: amd64
|
||||
|
||||
.deploy-pypi: &deploy-pypi
|
||||
stage: deploy
|
||||
before_script:
|
||||
- pip install -e .
|
||||
- pip install twine
|
||||
script:
|
||||
- python setup.py sdist
|
||||
- twine upload dist/*
|
||||
tags:
|
||||
- python2.7
|
||||
- esphomeyaml-test
|
||||
|
||||
deploy-release:pypi:
|
||||
<<: *deploy-pypi
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
|
||||
deploy-beta:pypi:
|
||||
<<: *deploy-pypi
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+b\d+$/
|
||||
except:
|
||||
- /^(?!rc).+@/
|
||||
36
.travis.yml
@@ -1,10 +1,30 @@
|
||||
sudo: false
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- pip install tornado esptool flake8==3.5.0 pylint==1.8.4
|
||||
script:
|
||||
- flake8 esphomeyaml
|
||||
- pylint esphomeyaml
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- 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
|
||||
- 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
|
||||
|
||||
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@otto-winter.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
18
CONTRIBUTING.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Contributing to esphomeyaml
|
||||
|
||||
esphomeyaml is a part of esphomelib and is responsible for reading in YAML configuration files,
|
||||
converting them to C++ code. This code is then converted to a platformio project and compiled
|
||||
with [esphomelib](https://github.com/OttoWinter/esphomelib), the C++ framework behind the project.
|
||||
|
||||
For a detailed guide, please see https://esphomelib.com/esphomeyaml/guides/contributing.html#contributing-to-esphomeyaml
|
||||
|
||||
Things to note when contributing:
|
||||
|
||||
- Please test your changes :)
|
||||
- If a new feature is added or an existing user-facing feature is changed, you should also
|
||||
update the [docs](https://github.com/OttoWinter/esphomedocs). See [contributing to esphomedocs](https://esphomelib.com/esphomeyaml/guides/contributing.html#contributing-to-esphomedocs)
|
||||
for more information.
|
||||
- Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
|
||||
which checks if your new feature compiles correctly.
|
||||
- Sometimes I will let pull requests linger because I'm not 100% sure about them. Please feel free to ping
|
||||
me after some time.
|
||||
25
Dockerfile
@@ -1,21 +1,28 @@
|
||||
FROM python:2.7
|
||||
ARG BUILD_FROM=python:2.7
|
||||
FROM ${BUILD_FROM}
|
||||
MAINTAINER Otto Winter <contact@otto-winter.com>
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python-pil \
|
||||
git \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No && \
|
||||
platformio settings set check_libraries_interval 1000000 && \
|
||||
platformio settings set check_platformio_interval 1000000 && \
|
||||
platformio settings set check_platforms_interval 1000000
|
||||
|
||||
ENV ESPHOMEYAML_OTA_HOST_PORT=6123
|
||||
EXPOSE 6123
|
||||
VOLUME /config
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt /usr/src/app/
|
||||
RUN pip install --no-cache-dir -r requirements.txt && \
|
||||
pip install --no-cache-dir tornado esptool
|
||||
|
||||
COPY docker/platformio.ini /usr/src/app/
|
||||
RUN platformio settings set enable_telemetry No && \
|
||||
platformio run -e espressif32 -e espressif8266; exit 0
|
||||
COPY docker/platformio.ini /pio/platformio.ini
|
||||
RUN platformio run -d /pio; rm -rf /pio
|
||||
|
||||
COPY . .
|
||||
RUN pip install -e .
|
||||
RUN pip install --no-cache-dir --no-binary :all: -e .
|
||||
|
||||
WORKDIR /config
|
||||
ENTRYPOINT ["esphomeyaml"]
|
||||
CMD ["/config", "dashboard"]
|
||||
|
||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Otto Winter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
13
MANIFEST.in
@@ -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
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# esphomeyaml for [esphomelib](https://github.com/OttoWinter/esphomelib)
|
||||
|
||||
### Getting Started Guide: https://esphomelib.com/esphomeyaml/getting-started.html
|
||||
### Getting Started Guide: https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html
|
||||
|
||||
### Available Components: https://esphomelib.com/esphomeyaml/index.html
|
||||
|
||||
esphomeyaml is the solution for your ESP8266/ESP32 projects with Home Assistant. It allows you to create **custom firmwares** for your microcontrollers with no programming experience required. All you need to know is the YAML configuration format which is also used by Home Assistant.
|
||||
esphomeyaml is the solution for your ESP8266/ESP32 projects with Home Assistant. It allows you to create **custom firmwares** for your microcontrollers with no programming experience required. All you need to know is the YAML configuration format which is also used by [Home Assistant](https://www.home-assistant.io).
|
||||
|
||||
esphomeyaml will:
|
||||
|
||||
@@ -26,7 +26,7 @@ esphomeyaml configuration.yaml run
|
||||
files like you're used to with Home Assistant.
|
||||
* **Flexible:** Use [esphomelib](https://github.com/OttoWinter/esphomelib)'s powerful core to create custom sensors/outputs.
|
||||
* **Fast and efficient:** Written in C++ and keeps memory consumption to a minimum.
|
||||
* **Made for Home Assistant:** Almost all Home Assistant features are supported out of the box. Including RGB lights and many more.
|
||||
* **Made for [Home Assistant](https://www.home-assistant.io):** Almost all [Home Assistant](https://www.home-assistant.io) features are supported out of the box. Including RGB lights and many more.
|
||||
* **Easy reproducible configuration:** No need to go through a long setup process for every single node. Just copy a configuration file and run a single command.
|
||||
* **Smart Over The Air Updates:** esphomeyaml has OTA updates deeply integrated into the system. It even automatically enters a recovery mode if a boot loop is detected.
|
||||
* **Powerful logging engine:** View colorful logs and debug issues remotely.
|
||||
|
||||
30
docker/Dockerfile.builder
Normal file
@@ -0,0 +1,30 @@
|
||||
FROM multiarch/ubuntu-core:amd64-xenial
|
||||
|
||||
# setup locals
|
||||
RUN apt-get update && apt-get install -y \
|
||||
jq \
|
||||
git \
|
||||
python3-setuptools \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
# Install docker
|
||||
# https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apt-transport-https \
|
||||
ca-certificates \
|
||||
curl \
|
||||
software-properties-common \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
|
||||
&& add-apt-repository "deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
|
||||
&& apt-get update && apt-get install -y docker-ce \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# setup arm binary support
|
||||
RUN apt-get update && apt-get install -y \
|
||||
qemu-user-static \
|
||||
binfmt-support \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /data
|
||||
75
docker/Dockerfile.hassio
Normal file
@@ -0,0 +1,75 @@
|
||||
ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0
|
||||
# hadolint ignore=DL3006
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# 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 \
|
||||
# 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 BUILD_ARCH=amd64
|
||||
ARG BUILD_DATE
|
||||
ARG BUILD_REF
|
||||
ARG BUILD_VERSION
|
||||
|
||||
# Labels
|
||||
LABEL \
|
||||
io.hass.name="esphomeyaml" \
|
||||
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" \
|
||||
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"
|
||||
6
docker/Dockerfile.lint
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM python:2.7
|
||||
|
||||
COPY requirements.txt /requirements.txt
|
||||
|
||||
RUN pip install -r /requirements.txt && \
|
||||
pip install flake8==3.6.0 pylint==1.9.4 pillow
|
||||
21
docker/Dockerfile.test
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python \
|
||||
python-pip \
|
||||
python-setuptools \
|
||||
python-pil \
|
||||
git \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No && \
|
||||
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
|
||||
@@ -1,12 +1,12 @@
|
||||
; This file allows the docker build file to install the required platformio
|
||||
; platforms
|
||||
|
||||
[env:espressif32]
|
||||
platform = espressif32
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
|
||||
[env:espressif8266]
|
||||
platform = espressif8266
|
||||
platform = espressif8266@1.8.0
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
|
||||
[env:espressif32]
|
||||
platform = espressif32@1.5.0
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
|
||||
109
esphomeyaml-beta/README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Esphomeyaml Hass.io Add-On
|
||||
|
||||
[](https://esphomelib.com/esphomeyaml/index.html)
|
||||
|
||||
[](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
|
||||
39
esphomeyaml-beta/config.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "esphomeyaml-beta",
|
||||
"version": "1.10.1",
|
||||
"slug": "esphomeyaml-beta",
|
||||
"description": "Beta version of esphomeyaml Hass.io add-on.",
|
||||
"url": "https://beta.esphomelib.com/esphomeyaml/index.html",
|
||||
"webui": "http://[HOST]:[PORT:6052]",
|
||||
"startup": "application",
|
||||
"arch": [
|
||||
"amd64",
|
||||
"armhf",
|
||||
"i386"
|
||||
],
|
||||
"hassio_api": true,
|
||||
"auth_api": true,
|
||||
"hassio_role": "default",
|
||||
"homeassistant_api": false,
|
||||
"host_network": true,
|
||||
"boot": "auto",
|
||||
"auto_uart": true,
|
||||
"map": [
|
||||
"ssl",
|
||||
"config:rw"
|
||||
],
|
||||
"options": {
|
||||
"ssl": false,
|
||||
"certfile": "fullchain.pem",
|
||||
"keyfile": "privkey.pem",
|
||||
"port": 6052
|
||||
},
|
||||
"schema": {
|
||||
"ssl": "bool",
|
||||
"certfile": "str",
|
||||
"keyfile": "str",
|
||||
"port": "int",
|
||||
"leave_front_door_open": "bool?",
|
||||
"esphomeyaml_version": "str?"
|
||||
}
|
||||
}
|
||||
BIN
esphomeyaml-beta/icon.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
esphomeyaml-beta/logo.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
@@ -1,43 +1,73 @@
|
||||
# Dockerfile for HassIO add-on
|
||||
ARG BUILD_FROM=ubuntu:bionic
|
||||
ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0
|
||||
# hadolint ignore=DL3006
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# Re-declare BUILD_FROM to fix weird docker issue
|
||||
ARG BUILD_FROM
|
||||
# Set shell
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
# On amd64 and alike, using ubuntu as the base is better as building
|
||||
# for the ESP32 only works with glibc (and ubuntu). However, on armhf
|
||||
# the build toolchain frequently procudes segfaults under ubuntu.
|
||||
# -> Use ubuntu for most architectures, except alpine for armhf
|
||||
#
|
||||
# * python and related required because this is a python project
|
||||
# * git required for platformio library dependencies downloads
|
||||
# * libc6-compat and openssh required on alpine for weird reasons
|
||||
# * disable platformio telemetry on install
|
||||
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
python python-pip python-setuptools git && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/*; \
|
||||
else \
|
||||
apk add --no-cache python2 py2-pip git openssh libc6-compat; \
|
||||
fi" && \
|
||||
pip install --no-cache-dir platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
# 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 \
|
||||
# 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/
|
||||
|
||||
# Create fake project to make platformio install all depdencies.
|
||||
# * Ignore build errors from platformio - empty project
|
||||
# * On alpine, only install ESP8266 toolchain
|
||||
COPY platformio.ini /pio/platformio.ini
|
||||
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
|
||||
platformio run -e espressif32 -e espressif8266 -d /pio; exit 0; \
|
||||
else \
|
||||
echo \"\$(head -8 /pio/platformio.ini)\" >/pio/platformio.ini; \
|
||||
platformio run -e espressif8266 -d /pio; exit 0; \
|
||||
fi"
|
||||
# Build arugments
|
||||
ARG BUILD_ARCH=amd64
|
||||
ARG BUILD_DATE
|
||||
ARG BUILD_REF
|
||||
ARG BUILD_VERSION
|
||||
|
||||
# Install latest esphomeyaml from git
|
||||
RUN pip install --no-cache-dir \
|
||||
git+git://github.com/OttoWinter/esphomeyaml.git
|
||||
|
||||
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
@@ -0,0 +1,109 @@
|
||||
# Esphomeyaml Hass.io Add-On
|
||||
|
||||
[](https://esphomelib.com/esphomeyaml/index.html)
|
||||
|
||||
[](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
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"squash": false,
|
||||
"build_from": {
|
||||
"aarch64": "arm64v8/ubuntu:bionic",
|
||||
"amd64": "ubuntu:bionic",
|
||||
"armhf": "homeassistant/armhf-base:latest",
|
||||
"i386": "i386/ubuntu:bionic"
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,39 @@
|
||||
"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"
|
||||
],
|
||||
"hassio_api": true,
|
||||
"auth_api": true,
|
||||
"hassio_role": "default",
|
||||
"homeassistant_api": false,
|
||||
"host_network": true,
|
||||
"boot": "auto",
|
||||
"auto_uart": true,
|
||||
"map": [
|
||||
"ssl",
|
||||
"config:rw"
|
||||
],
|
||||
"options": {},
|
||||
"environment": {
|
||||
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
|
||||
"options": {
|
||||
"ssl": false,
|
||||
"certfile": "fullchain.pem",
|
||||
"keyfile": "privkey.pem",
|
||||
"port": 6052
|
||||
},
|
||||
"schema": {}
|
||||
"schema": {
|
||||
"ssl": "bool",
|
||||
"certfile": "str",
|
||||
"keyfile": "str",
|
||||
"port": "int",
|
||||
"leave_front_door_open": "bool?",
|
||||
"esphomeyaml_version": "str?"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
esphomeyaml-edge/icon.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
esphomeyaml-edge/images/dht-example.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
esphomeyaml-edge/images/screenshot.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
esphomeyaml-edge/images/temperature-humidity.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
esphomeyaml-edge/logo.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
35
esphomeyaml-edge/rootfs/etc/cont-init.d/10-requirements.sh
Executable 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
|
||||
28
esphomeyaml-edge/rootfs/etc/cont-init.d/20-nginx.sh
Executable 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
|
||||
14
esphomeyaml-edge/rootfs/etc/cont-init.d/30-esphomeyaml.sh
Normal 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
|
||||
62
esphomeyaml-edge/rootfs/etc/nginx/nginx-ssl.conf
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
esphomeyaml-edge/rootfs/etc/nginx/nginx.conf
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/finish
Executable 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
|
||||
14
esphomeyaml-edge/rootfs/etc/services.d/esphomeyaml/run
Executable 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
|
||||
9
esphomeyaml-edge/rootfs/etc/services.d/nginx/finish
Executable 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
|
||||
10
esphomeyaml-edge/rootfs/etc/services.d/nginx/run
Executable 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;"
|
||||
@@ -2,11 +2,11 @@
|
||||
; platforms
|
||||
|
||||
[env:espressif8266]
|
||||
platform = espressif8266
|
||||
platform = espressif8266@1.8.0
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
|
||||
[env:espressif32]
|
||||
platform = espressif32
|
||||
platform = espressif32@1.5.0
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
@@ -1,44 +0,0 @@
|
||||
# Dockerfile for HassIO add-on
|
||||
ARG BUILD_FROM=ubuntu:bionic
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# Re-declare BUILD_FROM to fix weird docker issue
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_VERSION
|
||||
|
||||
# On amd64 and alike, using ubuntu as the base is better as building
|
||||
# for the ESP32 only works with glibc (and ubuntu). However, on armhf
|
||||
# the build toolchain frequently procudes segfaults under ubuntu.
|
||||
# -> Use ubuntu for most architectures, except alpine for armhf
|
||||
#
|
||||
# * python and related required because this is a python project
|
||||
# * git required for platformio library dependencies downloads
|
||||
# * libc6-compat and openssh required on alpine for weird reasons
|
||||
# * disable platformio telemetry on install
|
||||
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
python python-pip python-setuptools git && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/*; \
|
||||
else \
|
||||
apk add --no-cache python2 py2-pip git openssh libc6-compat; \
|
||||
fi" && \
|
||||
pip install --no-cache-dir platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
|
||||
# Create fake project to make platformio install all depdencies.
|
||||
# * Ignore build errors from platformio - empty project
|
||||
# * On alpine, only install ESP8266 toolchain
|
||||
COPY platformio.ini /pio/platformio.ini
|
||||
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
|
||||
platformio run -e espressif32 -e espressif8266 -d /pio; \
|
||||
else \
|
||||
echo \"\$(head -8 /pio/platformio.ini)\" >/pio/platformio.ini; \
|
||||
platformio run -e espressif8266 -d /pio; \
|
||||
fi"; exit 0
|
||||
|
||||
# Install latest esphomeyaml from git
|
||||
RUN pip install --no-cache-dir \
|
||||
esphomeyaml==${BUILD_VERSION}
|
||||
|
||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
|
||||
109
esphomeyaml/README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Esphomeyaml Hass.io Add-On
|
||||
|
||||
[](https://esphomelib.com/esphomeyaml/index.html)
|
||||
|
||||
[](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
|
||||
@@ -1,31 +1,30 @@
|
||||
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, mqtt, wizard, writer, yaml_util
|
||||
from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_DOMAIN, CONF_ESPHOMEYAML, CONF_HOSTNAME, \
|
||||
CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, _EXPRESSIONS, add, \
|
||||
add_job, color, flush_tasks, indent, quote, statement
|
||||
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']
|
||||
|
||||
|
||||
def get_name(config):
|
||||
return config[CONF_ESPHOMEYAML][CONF_NAME]
|
||||
|
||||
|
||||
def get_base_path(config):
|
||||
return os.path.join(os.path.dirname(core.CONFIG_PATH), get_name(config))
|
||||
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ethernet', 'ota', 'mqtt', 'web_server', 'api',
|
||||
'i2c']
|
||||
|
||||
|
||||
def get_serial_ports():
|
||||
@@ -37,188 +36,192 @@ 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'
|
||||
print(u"Found multiple serial port options, please choose one:")
|
||||
for i, (res, desc) in enumerate(result):
|
||||
print(u" [{}] {} ({})".format(i, res, desc))
|
||||
print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
|
||||
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:
|
||||
print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||
if opt == len(result):
|
||||
return 'OTA'
|
||||
return result[opt][0]
|
||||
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||
return options[opt - 1][1]
|
||||
|
||||
|
||||
def run_platformio(*cmd, **kwargs):
|
||||
def mock_exit(return_code):
|
||||
raise SystemExit(return_code)
|
||||
|
||||
orig_argv = sys.argv
|
||||
orig_exit = sys.exit # mock sys.exit
|
||||
full_cmd = u' '.join(quote(x) for x in cmd)
|
||||
_LOGGER.info(u"Running: %s", full_cmd)
|
||||
try:
|
||||
func = kwargs.get('main')
|
||||
if func is None:
|
||||
import platformio.__main__
|
||||
func = platformio.__main__.main
|
||||
sys.argv = list(cmd)
|
||||
sys.exit = mock_exit
|
||||
return func() or 0
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
except SystemExit as err:
|
||||
return err.args[0]
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
_LOGGER.error(u"Running platformio failed: %s", err)
|
||||
_LOGGER.error(u"Please try running %s locally.", full_cmd)
|
||||
finally:
|
||||
sys.argv = orig_argv
|
||||
sys.exit = orig_exit
|
||||
def 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 run_miniterm(config, port, escape=False):
|
||||
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
|
||||
baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
|
||||
if CONF_LOGGER not in config:
|
||||
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
|
||||
return
|
||||
baud_rate = config['logger'][CONF_BAUD_RATE]
|
||||
if baud_rate == 0:
|
||||
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||
|
||||
backtrace_state = False
|
||||
with serial.Serial(port, baudrate=baud_rate) as ser:
|
||||
while True:
|
||||
line = ser.readline().replace('\r', '').replace('\n', '')
|
||||
try:
|
||||
raw = ser.readline()
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return
|
||||
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')
|
||||
try:
|
||||
print(message)
|
||||
except UnicodeEncodeError:
|
||||
print(message.encode('ascii', 'backslashreplace'))
|
||||
safe_print(message)
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state)
|
||||
|
||||
|
||||
def write_cpp(config):
|
||||
_LOGGER.info("Generating C++ source...")
|
||||
|
||||
add_job(core_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:
|
||||
if core.SIMPLIFY:
|
||||
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)))
|
||||
|
||||
platformio_ini_s = writer.get_ini_content(config)
|
||||
ini_path = os.path.join(get_base_path(config), 'platformio.ini')
|
||||
writer.write_platformio_ini(platformio_ini_s, ini_path)
|
||||
writer.write_platformio_project()
|
||||
|
||||
code_s = indent('\n'.join(line.rstrip() for line in all_code))
|
||||
cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp')
|
||||
writer.write_cpp(code_s, cpp_path)
|
||||
writer.write_cpp(code_s)
|
||||
return 0
|
||||
|
||||
|
||||
def compile_program(args, config):
|
||||
_LOGGER.info("Compiling app...")
|
||||
command = ['platformio', 'run', '-d', get_base_path(config)]
|
||||
if args.verbose:
|
||||
command.append('-v')
|
||||
return run_platformio(*command)
|
||||
|
||||
|
||||
def get_upload_host(config):
|
||||
if CONF_MANUAL_IP in config[CONF_WIFI]:
|
||||
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
|
||||
elif CONF_HOSTNAME in config[CONF_WIFI]:
|
||||
host = config[CONF_WIFI][CONF_HOSTNAME] + config[CONF_WIFI][CONF_DOMAIN]
|
||||
else:
|
||||
host = config[CONF_ESPHOMEYAML][CONF_NAME] + config[CONF_WIFI][CONF_DOMAIN]
|
||||
return host
|
||||
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
|
||||
|
||||
name = get_name(config)
|
||||
path = os.path.join(get_base_path(config), '.pioenvs', 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_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
|
||||
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0',
|
||||
path, main=esptool._main)
|
||||
return run_external_command(esptool._main, *cmd)
|
||||
|
||||
|
||||
def upload_program(config, args, port):
|
||||
_LOGGER.info("Uploading binary...")
|
||||
if port != 'OTA':
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy:
|
||||
return upload_using_esptool(config, port)
|
||||
command = ['platformio', 'run', '-d', get_base_path(config),
|
||||
'-t', 'upload', '--upload-port', port]
|
||||
if args.verbose:
|
||||
command.append('-v')
|
||||
return run_platformio(*command)
|
||||
|
||||
if 'ota' not in config:
|
||||
_LOGGER.error("No serial port found and OTA not enabled. Can't upload!")
|
||||
return -1
|
||||
|
||||
host = get_upload_host(config)
|
||||
def upload_program(config, args, host):
|
||||
# if upload is to a serial port use platformio, otherwise assume ota
|
||||
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 espota
|
||||
from esphomeyaml import espota2
|
||||
|
||||
bin_file = os.path.join(get_base_path(config), '.pioenvs', get_name(config), 'firmware.bin')
|
||||
if args.host_port is not None:
|
||||
host_port = args.host_port
|
||||
else:
|
||||
host_port = int(os.getenv('ESPHOMEYAML_OTA_HOST_PORT', random.randint(10000, 60000)))
|
||||
espota_args = ['espota.py', '--debug', '--progress', '-i', host,
|
||||
'-p', str(ota.get_port(config)), '-f', bin_file,
|
||||
'-a', ota.get_auth(config), '-P', str(host_port)]
|
||||
if args.verbose:
|
||||
espota_args.append('-d')
|
||||
return espota.main(espota_args)
|
||||
|
||||
verbose = args.verbose
|
||||
remote_port = ota.get_port(config)
|
||||
password = ota.get_auth(config)
|
||||
|
||||
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
|
||||
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):
|
||||
if port != 'OTA':
|
||||
run_miniterm(config, port, escape=escape)
|
||||
def show_logs(config, args, port):
|
||||
if 'logger' not in config:
|
||||
raise EsphomeyamlError("Logger is not configured!")
|
||||
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' and 'api' in config:
|
||||
return run_logs(config, port)
|
||||
if get_port_type(port) == 'MQTT' and 'mqtt' in config:
|
||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
|
||||
|
||||
raise ValueError
|
||||
|
||||
|
||||
def clean_mqtt(config, args):
|
||||
@@ -228,7 +231,7 @@ def clean_mqtt(config, args):
|
||||
def setup_log(debug=False):
|
||||
log_level = logging.DEBUG if debug else logging.INFO
|
||||
logging.basicConfig(level=log_level)
|
||||
fmt = "%(levelname)s [%(name)s] %(message)s"
|
||||
fmt = "%(levelname)s %(message)s"
|
||||
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
|
||||
datefmt = '%H:%M:%S'
|
||||
|
||||
@@ -256,29 +259,11 @@ 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)
|
||||
print(yaml_util.dump(config))
|
||||
safe_print(yaml_util.dump(config))
|
||||
return 0
|
||||
|
||||
|
||||
@@ -286,6 +271,9 @@ def command_compile(args, config):
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
if args.only_generate:
|
||||
_LOGGER.info(u"Successfully generated source code.")
|
||||
return 0
|
||||
exit_code = compile_program(args, config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
@@ -294,7 +282,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
|
||||
@@ -303,8 +292,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):
|
||||
@@ -315,14 +305,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):
|
||||
@@ -334,7 +327,39 @@ def command_mqtt_fingerprint(args, config):
|
||||
|
||||
|
||||
def command_version(args):
|
||||
print(u"Version: {}".format(const.__version__))
|
||||
safe_print(u"Version: {}".format(const.__version__))
|
||||
return 0
|
||||
|
||||
|
||||
def command_clean(args, config):
|
||||
try:
|
||||
writer.clean_build()
|
||||
except OSError as err:
|
||||
_LOGGER.error("Error deleting build files: %s", err)
|
||||
return 1
|
||||
_LOGGER.info("Done!")
|
||||
return 0
|
||||
|
||||
|
||||
def command_hass_config(args, config):
|
||||
from esphomeyaml.components import mqtt as mqtt_component
|
||||
|
||||
_LOGGER.info("This is what you should put in your Home Assistant YAML configuration.")
|
||||
_LOGGER.info("Please note this is only necessary if you're not using MQTT discovery.")
|
||||
data = mqtt_component.GenerateHassConfigData(config)
|
||||
hass_config = OrderedDict()
|
||||
for domain, component, conf in iter_components(config):
|
||||
if not hasattr(component, 'to_hass_config'):
|
||||
continue
|
||||
func = getattr(component, 'to_hass_config')
|
||||
ret = func(data, conf)
|
||||
if not isinstance(ret, (list, tuple)):
|
||||
ret = [ret]
|
||||
ret = [x for x in ret if x is not None]
|
||||
domain_conf = hass_config.setdefault(domain.split('.')[0], [])
|
||||
domain_conf += ret
|
||||
|
||||
safe_print(yaml_util.dump(hass_config))
|
||||
return 0
|
||||
|
||||
|
||||
@@ -358,6 +383,8 @@ POST_CONFIG_ACTIONS = {
|
||||
'run': command_run,
|
||||
'clean-mqtt': command_clean_mqtt,
|
||||
'mqtt-fingerprint': command_mqtt_fingerprint,
|
||||
'clean': command_clean,
|
||||
'hass-config': command_hass_config,
|
||||
}
|
||||
|
||||
|
||||
@@ -365,22 +392,25 @@ 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')
|
||||
subparsers.required = True
|
||||
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
||||
|
||||
subparsers.add_parser('compile', help='Read the configuration and compile a program.')
|
||||
parser_compile = subparsers.add_parser('compile',
|
||||
help='Read the configuration and compile a program.')
|
||||
parser_compile.add_argument('--only-generate',
|
||||
help="Only generate source code, do not compile.",
|
||||
action='store_true')
|
||||
|
||||
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
||||
'and upload the latest binary.')
|
||||
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.')
|
||||
@@ -390,12 +420,10 @@ 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.')
|
||||
parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
||||
parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.")
|
||||
parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
|
||||
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
|
||||
@@ -404,11 +432,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.")
|
||||
@@ -424,44 +447,63 @@ def parse_args(argv):
|
||||
|
||||
subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
|
||||
|
||||
subparsers.add_parser('clean', help="Delete all temporary build files.")
|
||||
|
||||
dashboard = subparsers.add_parser('dashboard',
|
||||
help="Create a simple webserver for a dashboard.")
|
||||
dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int,
|
||||
default=6052)
|
||||
help="Create a simple web server for a dashboard.")
|
||||
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.")
|
||||
|
||||
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
|
||||
print(u"Unknown command {}".format(args.command))
|
||||
safe_print(u"Unknown command {}".format(args.command))
|
||||
return 1
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
return run_esphomeyaml(sys.argv)
|
||||
except ESPHomeYAMLError as e:
|
||||
except EsphomeyamlError as e:
|
||||
_LOGGER.error(e)
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
|
||||
0
esphomeyaml/api/__init__.py
Normal file
330
esphomeyaml/api/api.proto
Normal 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
490
esphomeyaml/api/client.py
Normal 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
|
||||
@@ -1,383 +1,318 @@
|
||||
import copy
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import cover, fan
|
||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \
|
||||
CONF_BELOW, \
|
||||
CONF_BLUE, CONF_BRIGHTNESS, CONF_CONDITION_ID, CONF_DELAY, CONF_EFFECT, CONF_FLASH_LENGTH, \
|
||||
CONF_GREEN, CONF_ID, CONF_IF, CONF_LAMBDA, CONF_OR, CONF_OSCILLATING, CONF_PAYLOAD, CONF_QOS, \
|
||||
CONF_RANGE, CONF_RED, CONF_RETAIN, CONF_SPEED, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, \
|
||||
CONF_TRIGGER_ID, CONF_WHITE
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, \
|
||||
bool_, esphomelib_ns, float_, get_variable, process_lambda, std_string, templatable, uint32, \
|
||||
uint8, add_job
|
||||
CONF_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
|
||||
|
||||
CONF_MQTT_PUBLISH = 'mqtt.publish'
|
||||
CONF_LIGHT_TOGGLE = 'light.toggle'
|
||||
CONF_LIGHT_TURN_OFF = 'light.turn_off'
|
||||
CONF_LIGHT_TURN_ON = 'light.turn_on'
|
||||
CONF_SWITCH_TOGGLE = 'switch.toggle'
|
||||
CONF_SWITCH_TURN_OFF = 'switch.turn_off'
|
||||
CONF_SWITCH_TURN_ON = 'switch.turn_on'
|
||||
CONF_COVER_OPEN = 'cover.open'
|
||||
CONF_COVER_CLOSE = 'cover.close'
|
||||
CONF_COVER_STOP = 'cover.stop'
|
||||
CONF_FAN_TOGGLE = 'fan.toggle'
|
||||
CONF_FAN_TURN_OFF = 'fan.turn_off'
|
||||
CONF_FAN_TURN_ON = 'fan.turn_on'
|
||||
|
||||
ACTION_KEYS = [CONF_DELAY, CONF_MQTT_PUBLISH, CONF_LIGHT_TOGGLE, CONF_LIGHT_TURN_OFF,
|
||||
CONF_LIGHT_TURN_ON, CONF_SWITCH_TOGGLE, CONF_SWITCH_TURN_OFF, CONF_SWITCH_TURN_ON,
|
||||
CONF_LAMBDA, CONF_COVER_OPEN, CONF_COVER_CLOSE, CONF_COVER_STOP, CONF_FAN_TOGGLE,
|
||||
CONF_FAN_TURN_OFF, CONF_FAN_TURN_ON]
|
||||
def maybe_simple_id(*validators):
|
||||
validator = vol.All(*validators)
|
||||
|
||||
ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
|
||||
cv.GenerateID(CONF_ACTION_ID): cv.declare_variable_id(None),
|
||||
vol.Optional(CONF_DELAY): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
vol.Optional(CONF_MQTT_PUBLISH): vol.Schema({
|
||||
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
|
||||
vol.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload),
|
||||
vol.Optional(CONF_QOS): cv.templatable(cv.mqtt_qos),
|
||||
vol.Optional(CONF_RETAIN): cv.templatable(cv.boolean),
|
||||
}),
|
||||
vol.Optional(CONF_LIGHT_TOGGLE): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
}),
|
||||
vol.Optional(CONF_LIGHT_TURN_OFF): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
}),
|
||||
vol.Optional(CONF_LIGHT_TURN_ON): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'):
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
vol.Exclusive(CONF_FLASH_LENGTH, 'transformer'):
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
vol.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_RED): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_GREEN): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_BLUE): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_WHITE): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_EFFECT): cv.templatable(cv.string),
|
||||
}),
|
||||
vol.Optional(CONF_SWITCH_TOGGLE): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_SWITCH_TURN_OFF): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_SWITCH_TURN_ON): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_OPEN): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_CLOSE): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_STOP): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_OPEN): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_CLOSE): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_STOP): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_FAN_TOGGLE): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_FAN_TURN_OFF): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_FAN_TURN_ON): vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
|
||||
vol.Optional(CONF_SPEED): cv.templatable(fan.validate_fan_speed),
|
||||
}),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
}, cv.has_exactly_one_key(*ACTION_KEYS))])
|
||||
def validate(value):
|
||||
if isinstance(value, dict):
|
||||
return validator(value)
|
||||
return validator({CONF_ID: value})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
DelayAction = esphomelib_ns.DelayAction
|
||||
LambdaAction = esphomelib_ns.LambdaAction
|
||||
Automation = esphomelib_ns.Automation
|
||||
return validate
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
CONDITION_KEYS = [CONF_AND, CONF_OR, CONF_RANGE, CONF_LAMBDA]
|
||||
def validate_recursive_action(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),
|
||||
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), 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), path + [key])
|
||||
item.setdefault(CONF_ACTION_ID, 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 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: action,
|
||||
}
|
||||
return value
|
||||
|
||||
CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
|
||||
cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(None),
|
||||
vol.Optional(CONF_AND): validate_recursive_condition,
|
||||
vol.Optional(CONF_OR): validate_recursive_condition,
|
||||
vol.Optional(CONF_RANGE): vol.All(vol.Schema({
|
||||
vol.Optional(CONF_ABOVE): vol.Coerce(float),
|
||||
vol.Optional(CONF_BELOW): vol.Coerce(float),
|
||||
}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
}), cv.has_exactly_one_key(*CONDITION_KEYS)])
|
||||
|
||||
ACTION_REGISTRY = ServiceRegistry()
|
||||
CONDITION_REGISTRY = ServiceRegistry()
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
AndCondition = esphomelib_ns.AndCondition
|
||||
OrCondition = esphomelib_ns.OrCondition
|
||||
RangeCondition = esphomelib_ns.RangeCondition
|
||||
LambdaCondition = esphomelib_ns.LambdaCondition
|
||||
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')
|
||||
|
||||
Condition = esphomelib_ns.class_('Condition')
|
||||
AndCondition = esphomelib_ns.class_('AndCondition', Condition)
|
||||
OrCondition = esphomelib_ns.class_('OrCondition', Condition)
|
||||
RangeCondition = esphomelib_ns.class_('RangeCondition', Condition)
|
||||
LambdaCondition = esphomelib_ns.class_('LambdaCondition', Condition)
|
||||
|
||||
|
||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||
if extra_schema is None:
|
||||
extra_schema = {}
|
||||
if isinstance(extra_schema, vol.Schema):
|
||||
extra_schema = extra_schema.schema
|
||||
schema = AUTOMATION_SCHEMA.extend(extra_schema)
|
||||
|
||||
def validator_(value):
|
||||
if isinstance(value, list):
|
||||
try:
|
||||
# First try as a sequence of actions
|
||||
return [schema({CONF_THEN: value})]
|
||||
except vol.Invalid as err:
|
||||
# Next try as a sequence of automations
|
||||
try:
|
||||
return vol.Schema([schema])(value)
|
||||
except vol.Invalid as err2:
|
||||
if 'Unable to find action' in str(err):
|
||||
raise err2
|
||||
raise vol.MultipleInvalid([err, err2])
|
||||
elif isinstance(value, dict):
|
||||
if CONF_THEN in value:
|
||||
return [schema(value)]
|
||||
return [schema({CONF_THEN: value})]
|
||||
# This should only happen with invalid configs, but let's have a nice error message.
|
||||
return [schema(value)]
|
||||
|
||||
def validator(value):
|
||||
value = validator_(value)
|
||||
if extra_validators is not None:
|
||||
value = vol.Schema([extra_validators])(value)
|
||||
if single:
|
||||
if len(value) != 1:
|
||||
raise vol.Invalid("Cannot have more than 1 automation for templates")
|
||||
return value[0]
|
||||
return value
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
AUTOMATION_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(None),
|
||||
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(None),
|
||||
vol.Optional(CONF_IF): CONDITIONS_SCHEMA,
|
||||
vol.Required(CONF_THEN): ACTIONS_SCHEMA,
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(Trigger),
|
||||
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(Automation),
|
||||
vol.Optional(CONF_IF): validate_recursive_condition,
|
||||
vol.Required(CONF_THEN): validate_recursive_action,
|
||||
})
|
||||
|
||||
AND_CONDITION_SCHEMA = validate_recursive_condition
|
||||
|
||||
|
||||
@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
|
||||
condition.set_min(template_)
|
||||
if CONF_BELOW in config:
|
||||
for template_ in templatable(config[CONF_BELOW], arg_type, float_):
|
||||
yield
|
||||
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):
|
||||
rhs = App.register_component(DelayAction.new(template_arg))
|
||||
type = DelayAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
for template_ in templatable(config, arg_type, uint32):
|
||||
yield
|
||||
add(action.set_delay(template_))
|
||||
yield action
|
||||
|
||||
|
||||
IF_ACTION_SCHEMA = vol.All({
|
||||
vol.Required(CONF_CONDITION): validate_recursive_condition,
|
||||
vol.Optional(CONF_THEN): validate_recursive_action,
|
||||
vol.Optional(CONF_ELSE): validate_recursive_action,
|
||||
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_IF, IF_ACTION_SCHEMA)
|
||||
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)
|
||||
type = IfAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
if CONF_THEN in config:
|
||||
for actions in build_actions(config[CONF_THEN], arg_type):
|
||||
yield None
|
||||
add(action.add_then(actions))
|
||||
if CONF_ELSE in config:
|
||||
for actions in build_actions(config[CONF_ELSE], arg_type):
|
||||
yield None
|
||||
add(action.add_else(actions))
|
||||
yield action
|
||||
|
||||
|
||||
WHILE_ACTION_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_CONDITION): validate_recursive_condition,
|
||||
vol.Required(CONF_THEN): validate_recursive_action,
|
||||
})
|
||||
|
||||
|
||||
def build_condition(config, arg_type):
|
||||
@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):
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA)
|
||||
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(template_arg, var)
|
||||
type = UpdateComponentAction.template(template_arg)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
def build_action(full_config, arg_type):
|
||||
action_id = full_config[CONF_ACTION_ID]
|
||||
key, config = next((k, v) for k, v in full_config.items() if k in ACTION_REGISTRY)
|
||||
|
||||
builder = ACTION_REGISTRY[key][1]
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
if CONF_AND in config:
|
||||
yield AndCondition.new(template_arg, build_conditions(config[CONF_AND], template_arg))
|
||||
elif CONF_OR in config:
|
||||
yield OrCondition.new(template_arg, build_conditions(config[CONF_OR], template_arg))
|
||||
elif CONF_LAMBDA in config:
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]):
|
||||
yield
|
||||
yield LambdaCondition.new(template_arg, lambda_)
|
||||
elif CONF_RANGE in config:
|
||||
conf = config[CONF_RANGE]
|
||||
rhs = RangeCondition.new(template_arg)
|
||||
type = RangeCondition.template(template_arg)
|
||||
condition = Pvariable(config[CONF_CONDITION_ID], rhs, type=type)
|
||||
if CONF_ABOVE in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_ABOVE], arg_type, float_):
|
||||
yield
|
||||
condition.set_min(template_)
|
||||
if CONF_BELOW in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_BELOW], arg_type, float_):
|
||||
yield
|
||||
condition.set_max(template_)
|
||||
yield condition
|
||||
else:
|
||||
raise ESPHomeYAMLError(u"Unsupported condition {}".format(config))
|
||||
|
||||
|
||||
def build_conditions(config, arg_type):
|
||||
conditions = []
|
||||
for conf in config:
|
||||
condition = None
|
||||
for condition in build_condition(conf, arg_type):
|
||||
yield None
|
||||
conditions.append(condition)
|
||||
yield ArrayInitializer(*conditions)
|
||||
|
||||
|
||||
def build_action(config, arg_type):
|
||||
from esphomeyaml.components import light, mqtt, switch
|
||||
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
# Keep pylint from freaking out
|
||||
var = None
|
||||
if CONF_DELAY in config:
|
||||
rhs = App.register_component(DelayAction.new(template_arg))
|
||||
type = DelayAction.template(template_arg)
|
||||
action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
template_ = None
|
||||
for template_ in templatable(config[CONF_DELAY], arg_type, uint32):
|
||||
yield
|
||||
add(action.set_delay(template_))
|
||||
yield action
|
||||
elif CONF_LAMBDA in config:
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]):
|
||||
yield None
|
||||
rhs = LambdaAction.new(template_arg, lambda_)
|
||||
type = LambdaAction.template(template_arg)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_MQTT_PUBLISH in config:
|
||||
conf = config[CONF_MQTT_PUBLISH]
|
||||
rhs = App.Pget_mqtt_client().Pmake_publish_action(template_arg)
|
||||
type = mqtt.MQTTPublishAction.template(template_arg)
|
||||
action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_TOPIC], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_topic(template_))
|
||||
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_PAYLOAD], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_payload(template_))
|
||||
if CONF_QOS in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_QOS], arg_type, uint8):
|
||||
yield
|
||||
add(action.set_qos(template_))
|
||||
if CONF_RETAIN in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_RETAIN], arg_type, bool_):
|
||||
yield None
|
||||
add(action.set_retain(template_))
|
||||
yield action
|
||||
elif CONF_LIGHT_TOGGLE in config:
|
||||
conf = config[CONF_LIGHT_TOGGLE]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_toggle_action(template_arg)
|
||||
type = light.ToggleAction.template(template_arg)
|
||||
action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
if CONF_TRANSITION_LENGTH in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_transition_length(template_))
|
||||
yield action
|
||||
elif CONF_LIGHT_TURN_OFF in config:
|
||||
conf = config[CONF_LIGHT_TURN_OFF]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = light.TurnOffAction.template(template_arg)
|
||||
action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
if CONF_TRANSITION_LENGTH in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_transition_length(template_))
|
||||
yield action
|
||||
elif CONF_LIGHT_TURN_ON in config:
|
||||
conf = config[CONF_LIGHT_TURN_ON]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = light.TurnOnAction.template(template_arg)
|
||||
action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
if CONF_TRANSITION_LENGTH in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_TRANSITION_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_transition_length(template_))
|
||||
if CONF_FLASH_LENGTH in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_FLASH_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_flash_length(template_))
|
||||
if CONF_BRIGHTNESS in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_BRIGHTNESS], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_brightness(template_))
|
||||
if CONF_RED in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_RED], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_red(template_))
|
||||
if CONF_GREEN in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_GREEN], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_green(template_))
|
||||
if CONF_BLUE in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_BLUE], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_blue(template_))
|
||||
if CONF_WHITE in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_WHITE], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_white(template_))
|
||||
if CONF_EFFECT in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_EFFECT], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_effect(template_))
|
||||
yield action
|
||||
elif CONF_SWITCH_TOGGLE in config:
|
||||
conf = config[CONF_SWITCH_TOGGLE]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_toggle_action(template_arg)
|
||||
type = switch.ToggleAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_SWITCH_TURN_OFF in config:
|
||||
conf = config[CONF_SWITCH_TURN_OFF]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = switch.TurnOffAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_SWITCH_TURN_ON in config:
|
||||
conf = config[CONF_SWITCH_TURN_ON]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = switch.TurnOnAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_COVER_OPEN in config:
|
||||
conf = config[CONF_COVER_OPEN]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_open_action(template_arg)
|
||||
type = cover.OpenAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_COVER_CLOSE in config:
|
||||
conf = config[CONF_COVER_CLOSE]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_close_action(template_arg)
|
||||
type = cover.CloseAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_COVER_STOP in config:
|
||||
conf = config[CONF_COVER_STOP]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_stop_action(template_arg)
|
||||
type = cover.StopAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_FAN_TOGGLE in config:
|
||||
conf = config[CONF_FAN_TOGGLE]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_toggle_action(template_arg)
|
||||
type = fan.ToggleAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_FAN_TURN_OFF in config:
|
||||
conf = config[CONF_FAN_TURN_OFF]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = fan.TurnOffAction.template(arg_type)
|
||||
yield Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
elif CONF_FAN_TURN_ON in config:
|
||||
conf = config[CONF_FAN_TURN_ON]
|
||||
for var in get_variable(conf[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = fan.TurnOnAction.template(arg_type)
|
||||
action = Pvariable(config[CONF_ACTION_ID], rhs, type=type)
|
||||
if CONF_OSCILLATING in config:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_OSCILLATING], arg_type, bool_):
|
||||
yield None
|
||||
add(action.set_oscillating(template_))
|
||||
if CONF_SPEED in config:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_SPEED], arg_type, fan.FanSpeed):
|
||||
yield None
|
||||
add(action.set_speed(template_))
|
||||
yield action
|
||||
else:
|
||||
raise ESPHomeYAMLError(u"Unsupported action {}".format(config))
|
||||
for result in builder(config, action_id, arg_type, template_arg):
|
||||
yield None
|
||||
yield result
|
||||
|
||||
|
||||
def build_actions(config, arg_type):
|
||||
@@ -390,20 +325,41 @@ 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(trigger)
|
||||
rhs = App.make_automation(TemplateArguments(arg_type), trigger)
|
||||
type = Automation.template(arg_type)
|
||||
obj = Pvariable(config[CONF_AUTOMATION_ID], rhs, type=type)
|
||||
if CONF_IF in config:
|
||||
conditions = None
|
||||
for conditions in build_conditions(config[CONF_IF], arg_type):
|
||||
yield
|
||||
yield None
|
||||
add(obj.add_conditions(conditions))
|
||||
actions = None
|
||||
for actions in build_actions(config[CONF_THEN], arg_type):
|
||||
yield
|
||||
yield None
|
||||
add(obj.add_actions(actions))
|
||||
yield obj
|
||||
|
||||
|
||||
def build_automation(trigger, arg_type, config):
|
||||
add_job(build_automation_, trigger, arg_type, config)
|
||||
CORE.add_job(build_automation_, trigger, arg_type, config)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"squash": false,
|
||||
"build_from": {
|
||||
"aarch64": "arm64v8/ubuntu:bionic",
|
||||
"amd64": "ubuntu:bionic",
|
||||
"armhf": "homeassistant/armhf-base:latest",
|
||||
"i386": "i386/ubuntu:bionic"
|
||||
},
|
||||
"args": {}
|
||||
}
|
||||
@@ -1,30 +1,27 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID
|
||||
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.ADS1115Component
|
||||
ADS1115Component = sensor.sensor_ns.class_('ADS1115Component', Component, i2c.I2CDevice)
|
||||
|
||||
RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required."""
|
||||
|
||||
ADS1115_SCHEMA = vol.Schema({
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(ADS1115Component),
|
||||
vol.Required(CONF_ADDRESS): cv.i2c_address,
|
||||
|
||||
vol.Optional(CONF_RATE): cv.invalid(RATE_REMOVE_MESSAGE)
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
rhs = App.make_ads1115_component(config[CONF_ADDRESS])
|
||||
var = Pvariable(config[CONF_ID], rhs)
|
||||
setup_component(var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
|
||||
|
||||
33
esphomeyaml/components/apds9960.py
Normal 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'
|
||||
88
esphomeyaml/components/api.py
Normal 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
|
||||
@@ -1,12 +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 import automation
|
||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INVERTED, CONF_MAX_LENGTH, \
|
||||
CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, CONF_ON_DOUBLE_CLICK, CONF_ON_PRESS, \
|
||||
CONF_ON_RELEASE, CONF_TRIGGER_ID
|
||||
from esphomeyaml.helpers import App, NoArg, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
|
||||
add_job
|
||||
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, 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',
|
||||
@@ -20,43 +29,213 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
})
|
||||
|
||||
binary_sensor_ns = esphomelib_ns.namespace('binary_sensor')
|
||||
PressTrigger = binary_sensor_ns.PressTrigger
|
||||
ReleaseTrigger = binary_sensor_ns.ReleaseTrigger
|
||||
ClickTrigger = binary_sensor_ns.ClickTrigger
|
||||
DoubleClickTrigger = binary_sensor_ns.DoubleClickTrigger
|
||||
BinarySensor = binary_sensor_ns.BinarySensor
|
||||
MQTTBinarySensorComponent = binary_sensor_ns.MQTTBinarySensorComponent
|
||||
BinarySensor = binary_sensor_ns.class_('BinarySensor', Nameable)
|
||||
BinarySensorPtr = BinarySensor.operator('ptr')
|
||||
MQTTBinarySensorComponent = binary_sensor_ns.class_('MQTTBinarySensorComponent', mqtt.MQTTComponent)
|
||||
|
||||
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent),
|
||||
cv.GenerateID(): cv.declare_variable_id(BinarySensor),
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)),
|
||||
vol.Optional(CONF_ON_PRESS): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_RELEASE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ReleaseTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_CLICK): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ClickTrigger),
|
||||
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
|
||||
})]),
|
||||
vol.Optional(CONF_ON_DOUBLE_CLICK):
|
||||
vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(DoubleClickTrigger),
|
||||
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
|
||||
})]),
|
||||
# Triggers
|
||||
PressTrigger = binary_sensor_ns.class_('PressTrigger', Trigger.template(NoArg))
|
||||
ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', Trigger.template(NoArg))
|
||||
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')
|
||||
DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, Component)
|
||||
DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, Component)
|
||||
HeartbeatFilter = binary_sensor_ns.class_('HeartbeatFilter', Filter, Component)
|
||||
InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter)
|
||||
LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter)
|
||||
|
||||
|
||||
FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT]
|
||||
|
||||
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))
|
||||
|
||||
MULTI_CLICK_TIMING_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_STATE): cv.boolean,
|
||||
vol.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
|
||||
|
||||
def parse_multi_click_timing_str(value):
|
||||
if not isinstance(value, string_types):
|
||||
return value
|
||||
|
||||
parts = value.lower().split(' ')
|
||||
if len(parts) != 5:
|
||||
raise vol.Invalid("Multi click timing grammar consists of exactly 5 words, not {}"
|
||||
"".format(len(parts)))
|
||||
try:
|
||||
state = cv.boolean(parts[0])
|
||||
except vol.Invalid:
|
||||
raise vol.Invalid(u"First word must either be ON or OFF, not {}".format(parts[0]))
|
||||
|
||||
if parts[1] != 'for':
|
||||
raise vol.Invalid(u"Second word must be 'for', got {}".format(parts[1]))
|
||||
|
||||
if parts[2] == 'at':
|
||||
if parts[3] == 'least':
|
||||
key = CONF_MIN_LENGTH
|
||||
elif parts[3] == 'most':
|
||||
key = CONF_MAX_LENGTH
|
||||
else:
|
||||
raise vol.Invalid(u"Third word after at must either be 'least' or 'most', got {}"
|
||||
u"".format(parts[3]))
|
||||
try:
|
||||
length = cv.positive_time_period_milliseconds(parts[4])
|
||||
except vol.Invalid as err:
|
||||
raise vol.Invalid(u"Multi Click Grammar Parsing length failed: {}".format(err))
|
||||
return {
|
||||
CONF_STATE: state,
|
||||
key: str(length)
|
||||
}
|
||||
|
||||
if parts[3] != 'to':
|
||||
raise vol.Invalid("Multi click grammar: 4th word must be 'to'")
|
||||
|
||||
try:
|
||||
min_length = cv.positive_time_period_milliseconds(parts[2])
|
||||
except vol.Invalid as err:
|
||||
raise vol.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err))
|
||||
|
||||
try:
|
||||
max_length = cv.positive_time_period_milliseconds(parts[4])
|
||||
except vol.Invalid as err:
|
||||
raise vol.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err))
|
||||
|
||||
return {
|
||||
CONF_STATE: state,
|
||||
CONF_MIN_LENGTH: str(min_length),
|
||||
CONF_MAX_LENGTH: str(max_length)
|
||||
}
|
||||
|
||||
|
||||
def validate_multi_click_timing(value):
|
||||
if not isinstance(value, list):
|
||||
raise vol.Invalid("Timing option must be a *list* of times!")
|
||||
timings = []
|
||||
state = None
|
||||
for i, v_ in enumerate(value):
|
||||
v_ = MULTI_CLICK_TIMING_SCHEMA(v_)
|
||||
min_length = v_.get(CONF_MIN_LENGTH)
|
||||
max_length = v_.get(CONF_MAX_LENGTH)
|
||||
if min_length is None and max_length is None:
|
||||
raise vol.Invalid("At least one of min_length and max_length is required!")
|
||||
if min_length is None and max_length is not None:
|
||||
min_length = core.TimePeriodMilliseconds(milliseconds=0)
|
||||
|
||||
new_state = v_.get(CONF_STATE, not state)
|
||||
if new_state == state:
|
||||
raise vol.Invalid("Timings must have alternating state. Indices {} and {} have "
|
||||
"the same state {}".format(i, i + 1, state))
|
||||
if max_length is not None and max_length < min_length:
|
||||
raise vol.Invalid("Max length ({}) must be larger than min length ({})."
|
||||
"".format(max_length, min_length))
|
||||
|
||||
state = new_state
|
||||
tim = {
|
||||
CONF_STATE: new_state,
|
||||
CONF_MIN_LENGTH: min_length,
|
||||
}
|
||||
if max_length is not None:
|
||||
tim[CONF_MAX_LENGTH] = max_length
|
||||
timings.append(tim)
|
||||
return timings
|
||||
|
||||
|
||||
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent),
|
||||
|
||||
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),
|
||||
}),
|
||||
vol.Optional(CONF_ON_RELEASE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ReleaseTrigger),
|
||||
}),
|
||||
vol.Optional(CONF_ON_CLICK): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ClickTrigger),
|
||||
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
vol.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(DoubleClickTrigger),
|
||||
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
vol.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MultiClickTrigger),
|
||||
vol.Required(CONF_TIMING): vol.All([parse_multi_click_timing_str],
|
||||
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 "
|
||||
"new 'invert' binary sensor filter. Please see "
|
||||
"https://esphomelib.com/esphomeyaml/components/binary_sensor/index.html."
|
||||
),
|
||||
})
|
||||
|
||||
BINARY_SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BINARY_SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_filter(config):
|
||||
if CONF_INVERT in config:
|
||||
yield InvertFilter.new()
|
||||
elif CONF_DELAYED_OFF in config:
|
||||
yield App.register_component(DelayedOffFilter.new(config[CONF_DELAYED_OFF]))
|
||||
elif CONF_DELAYED_ON in config:
|
||||
yield App.register_component(DelayedOnFilter.new(config[CONF_DELAYED_ON]))
|
||||
elif CONF_HEARTBEAT in config:
|
||||
yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT]))
|
||||
elif CONF_LAMBDA in config:
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')],
|
||||
return_type=optional.template(bool_)):
|
||||
yield None
|
||||
yield LambdaFilter.new(lambda_)
|
||||
|
||||
|
||||
def setup_filters(config):
|
||||
filters = []
|
||||
for conf in config:
|
||||
filter = None
|
||||
for filter in setup_filter(conf):
|
||||
yield None
|
||||
filters.append(filter)
|
||||
yield ArrayInitializer(*filters)
|
||||
|
||||
|
||||
def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(binary_sensor_var.set_internal(CONF_INTERNAL))
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
add(binary_sensor_var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
if CONF_INVERTED in config:
|
||||
add(binary_sensor_var.set_inverted(config[CONF_INVERTED]))
|
||||
if CONF_FILTERS in config:
|
||||
filters = None
|
||||
for filters in setup_filters(config[CONF_FILTERS]):
|
||||
yield
|
||||
add(binary_sensor_var.add_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_PRESS, []):
|
||||
rhs = binary_sensor_var.make_press_trigger()
|
||||
@@ -79,6 +258,27 @@ def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
for conf in config.get(CONF_ON_MULTI_CLICK, []):
|
||||
timings = []
|
||||
for tim in conf[CONF_TIMING]:
|
||||
timings.append(StructInitializer(
|
||||
MultiClickTriggerEvent,
|
||||
('state', tim[CONF_STATE]),
|
||||
('min_length', tim[CONF_MIN_LENGTH]),
|
||||
('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)),
|
||||
))
|
||||
timings = ArrayInitializer(*timings, multiline=False)
|
||||
rhs = App.register_component(binary_sensor_var.make_multi_click_trigger(timings))
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
if CONF_INVALID_COOLDOWN in conf:
|
||||
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)
|
||||
|
||||
|
||||
@@ -87,14 +287,54 @@ 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):
|
||||
ret = mqtt.build_hass_config(data, 'binary_sensor', config,
|
||||
include_state=True, include_command=False)
|
||||
if ret is None:
|
||||
return None
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
ret['device_class'] = config[CONF_DEVICE_CLASS]
|
||||
return ret
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'
|
||||
|
||||
|
||||
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)
|
||||
|
||||
36
esphomeyaml/components/binary_sensor/apds9960.py
Normal 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)
|
||||
37
esphomeyaml/components/binary_sensor/custom.py
Normal 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]]
|
||||
@@ -1,48 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.esp32_ble import ESP32BLETracker
|
||||
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.core import HexInt, MACAddress
|
||||
from esphomeyaml.helpers import ArrayInitializer, get_variable
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
DEPENDENCIES = ['esp32_ble']
|
||||
|
||||
CONF_ESP32_BLE_ID = 'esp32_ble_id'
|
||||
|
||||
|
||||
def validate_mac(value):
|
||||
value = cv.string_strict(value)
|
||||
parts = value.split(':')
|
||||
if len(parts) != 6:
|
||||
raise vol.Invalid("MAC Address must consist of 6 : (colon) separated parts")
|
||||
parts_int = []
|
||||
if any(len(part) != 2 for part in parts):
|
||||
raise vol.Invalid("MAC Address must be format XX:XX:XX:XX:XX:XX")
|
||||
for part in parts:
|
||||
try:
|
||||
parts_int.append(int(part, 16))
|
||||
except ValueError:
|
||||
raise vol.Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
|
||||
|
||||
return MACAddress(*parts_int)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_MAC_ADDRESS): validate_mac,
|
||||
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker)
|
||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
addr = [HexInt(i) for i in config[CONF_MAC_ADDRESS].parts]
|
||||
rhs = hub.make_device(config[CONF_NAME], ArrayInitializer(*addr, multiline=False))
|
||||
binary_sensor.register_binary_sensor(rhs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
|
||||
30
esphomeyaml/components/binary_sensor/esp32_ble_tracker.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
|
||||
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)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32BLEPresenceDevice),
|
||||
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
binary_sensor.register_binary_sensor(rhs, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
@@ -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]
|
||||
@@ -34,11 +35,15 @@ def validate_touch_pad(value):
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
ESP32TouchBinarySensor = binary_sensor.binary_sensor_ns.class_('ESP32TouchBinarySensor',
|
||||
binary_sensor.BinarySensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32TouchBinarySensor),
|
||||
vol.Required(CONF_PIN): validate_touch_pad,
|
||||
vol.Required(CONF_THRESHOLD): cv.uint16_t,
|
||||
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_variable_id(ESP32TouchComponent),
|
||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -51,3 +56,7 @@ def to_code(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -4,14 +4,20 @@ 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
|
||||
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.MakeGPIOBinarySensor
|
||||
MakeGPIOBinarySensor = Application.struct('MakeGPIOBinarySensor')
|
||||
GPIOBinarySensorComponent = binary_sensor.binary_sensor_ns.class_('GPIOBinarySensorComponent',
|
||||
binary_sensor.BinarySensor,
|
||||
Component)
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(GPIOBinarySensorComponent),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOBinarySensor),
|
||||
vol.Required(CONF_PIN): pins.gpio_input_pin_schema
|
||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -21,6 +27,11 @@ def to_code(config):
|
||||
rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin)
|
||||
gpio = variable(config[CONF_MAKE_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(gpio.Pgpio, gpio.Pmqtt, config)
|
||||
setup_component(gpio.Pgpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
|
||||
33
esphomeyaml/components/binary_sensor/nextion.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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.cpp_generator import get_variable
|
||||
|
||||
DEPENDENCIES = ['display']
|
||||
|
||||
CONF_NEXTION_ID = 'nextion_id'
|
||||
|
||||
NextionTouchComponent = display.display_ns.class_('NextionTouchComponent',
|
||||
binary_sensor.BinarySensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(NextionTouchComponent),
|
||||
vol.Required(CONF_PAGE_ID): cv.uint8_t,
|
||||
vol.Required(CONF_COMPONENT_ID): cv.uint8_t,
|
||||
cv.GenerateID(CONF_NEXTION_ID): cv.use_variable_id(Nextion)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for hub in get_variable(config[CONF_NEXTION_ID]):
|
||||
yield
|
||||
rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID],
|
||||
config[CONF_COMPONENT_ID])
|
||||
binary_sensor.register_binary_sensor(rhs, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
49
esphomeyaml/components/binary_sensor/pn532.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
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.cpp_generator import get_variable, ArrayInitializer
|
||||
|
||||
DEPENDENCIES = ['pn532']
|
||||
|
||||
CONF_PN532_ID = 'pn532_id'
|
||||
|
||||
|
||||
def validate_uid(value):
|
||||
value = cv.string_strict(value)
|
||||
for x in value.split('-'):
|
||||
if len(x) != 2:
|
||||
raise vol.Invalid("Each part (separated by '-') of the UID must be two characters "
|
||||
"long.")
|
||||
try:
|
||||
x = int(x, 16)
|
||||
except ValueError:
|
||||
raise vol.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.")
|
||||
if x < 0 or x > 255:
|
||||
raise vol.Invalid("Valid values for UID parts (separated by '-') are 00 to FF")
|
||||
return value
|
||||
|
||||
|
||||
PN532BinarySensor = binary_sensor.binary_sensor_ns.class_('PN532BinarySensor',
|
||||
binary_sensor.BinarySensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(PN532BinarySensor),
|
||||
vol.Required(CONF_UID): validate_uid,
|
||||
cv.GenerateID(CONF_PN532_ID): cv.use_variable_id(PN532Component)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for hub in get_variable(config[CONF_PN532_ID]):
|
||||
yield
|
||||
addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')]
|
||||
rhs = hub.make_tag(config[CONF_NAME], ArrayInitializer(*addr, multiline=False))
|
||||
binary_sensor.register_binary_sensor(rhs, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
30
esphomeyaml/components/binary_sensor/rdm6300.py
Normal file
@@ -0,0 +1,30 @@
|
||||
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.cpp_generator import get_variable
|
||||
|
||||
DEPENDENCIES = ['rdm6300']
|
||||
|
||||
CONF_RDM6300_ID = 'rdm6300_id'
|
||||
|
||||
RDM6300BinarySensor = binary_sensor.binary_sensor_ns.class_('RDM6300BinarySensor',
|
||||
binary_sensor.BinarySensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(RDM6300BinarySensor),
|
||||
vol.Required(CONF_UID): cv.uint32_t,
|
||||
cv.GenerateID(CONF_RDM6300_ID): cv.use_variable_id(rdm6300.RDM6300Component)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for hub in get_variable(config[CONF_RDM6300_ID]):
|
||||
yield
|
||||
rhs = hub.make_card(config[CONF_NAME], config[CONF_UID])
|
||||
binary_sensor.register_binary_sensor(rhs, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
125
esphomeyaml/components/binary_sensor/remote_receiver.py
Normal file
@@ -0,0 +1,125 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.remote_receiver import RemoteReceiverComponent, remote_ns
|
||||
from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \
|
||||
RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \
|
||||
RC_SWITCH_TYPE_D_SCHEMA, binary_code, build_rc_switch_protocol
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CHANNEL, CONF_CODE, CONF_COMMAND, CONF_DATA, \
|
||||
CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \
|
||||
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.cpp_generator import ArrayInitializer, get_variable, Pvariable
|
||||
|
||||
DEPENDENCIES = ['remote_receiver']
|
||||
|
||||
REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_SAMSUNG, 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_REMOTE_RECEIVER_ID = 'remote_receiver_id'
|
||||
CONF_RECEIVER_ID = 'receiver_id'
|
||||
|
||||
RemoteReceiver = remote_ns.class_('RemoteReceiver', binary_sensor.BinarySensor)
|
||||
LGReceiver = remote_ns.class_('LGReceiver', RemoteReceiver)
|
||||
NECReceiver = remote_ns.class_('NECReceiver', RemoteReceiver)
|
||||
PanasonicReceiver = remote_ns.class_('PanasonicReceiver', RemoteReceiver)
|
||||
RawReceiver = remote_ns.class_('RawReceiver', RemoteReceiver)
|
||||
SamsungReceiver = remote_ns.class_('SamsungReceiver', RemoteReceiver)
|
||||
SonyReceiver = remote_ns.class_('SonyReceiver', RemoteReceiver)
|
||||
RCSwitchRawReceiver = remote_ns.class_('RCSwitchRawReceiver', RemoteReceiver)
|
||||
RCSwitchTypeAReceiver = remote_ns.class_('RCSwitchTypeAReceiver', RCSwitchRawReceiver)
|
||||
RCSwitchTypeBReceiver = remote_ns.class_('RCSwitchTypeBReceiver', RCSwitchRawReceiver)
|
||||
RCSwitchTypeCReceiver = remote_ns.class_('RCSwitchTypeCReceiver', RCSwitchRawReceiver)
|
||||
RCSwitchTypeDReceiver = remote_ns.class_('RCSwitchTypeDReceiver', RCSwitchRawReceiver)
|
||||
|
||||
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): cv.one_of(28, 32, int=True),
|
||||
}),
|
||||
vol.Optional(CONF_NEC): vol.Schema({
|
||||
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
|
||||
}),
|
||||
vol.Optional(CONF_SAMSUNG): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
}),
|
||||
vol.Optional(CONF_SONY): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
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,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint32_t,
|
||||
}),
|
||||
vol.Optional(CONF_RAW): [vol.Any(vol.Coerce(int), cv.time_period_microseconds)],
|
||||
vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_A): RC_SWITCH_TYPE_A_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA,
|
||||
|
||||
cv.GenerateID(CONF_REMOTE_RECEIVER_ID): cv.use_variable_id(RemoteReceiverComponent),
|
||||
cv.GenerateID(CONF_RECEIVER_ID): cv.declare_variable_id(RemoteReceiver),
|
||||
}), cv.has_exactly_one_key(*REMOTE_KEYS))
|
||||
|
||||
|
||||
def receiver_base(full_config):
|
||||
name = full_config[CONF_NAME]
|
||||
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])
|
||||
if key == CONF_NEC:
|
||||
return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
if key == CONF_PANASONIC:
|
||||
return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
if key == CONF_SAMSUNG:
|
||||
return SamsungReceiver.new(name, config[CONF_DATA])
|
||||
if key == CONF_SONY:
|
||||
return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
|
||||
if key == CONF_RAW:
|
||||
data = ArrayInitializer(*config, multiline=False)
|
||||
return RawReceiver.new(name, data)
|
||||
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]))
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
|
||||
raise NotImplementedError("Unknown receiver type {}".format(config))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for remote in get_variable(config[CONF_REMOTE_RECEIVER_ID]):
|
||||
yield
|
||||
rhs = receiver_base(config)
|
||||
receiver = Pvariable(config[CONF_RECEIVER_ID], rhs)
|
||||
|
||||
binary_sensor.register_binary_sensor(remote.add_decoder(receiver), config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
@@ -1,21 +1,31 @@
|
||||
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
|
||||
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.MakeStatusBinarySensor
|
||||
MakeStatusBinarySensor = Application.struct('MakeStatusBinarySensor')
|
||||
StatusBinarySensor = binary_sensor.binary_sensor_ns.class_('StatusBinarySensor',
|
||||
binary_sensor.BinarySensor,
|
||||
Component)
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeStatusBinarySensor),
|
||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
|
||||
cv.GenerateID(): cv.declare_variable_id(StatusBinarySensor),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_status_binary_sensor(config[CONF_NAME])
|
||||
status = variable(config[CONF_MAKE_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(status.Pstatus, status.Pmqtt, config)
|
||||
setup_component(status.Pstatus, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
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, process_lambda, variable, optional, bool_
|
||||
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.MakeTemplateBinarySensor
|
||||
MakeTemplateBinarySensor = Application.struct('MakeTemplateBinarySensor')
|
||||
TemplateBinarySensor = binary_sensor.binary_sensor_ns.class_('TemplateBinarySensor',
|
||||
binary_sensor.BinarySensor,
|
||||
Component)
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TemplateBinarySensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateBinarySensor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
}).extend(binary_sensor.BINARY_SENSOR_SCHEMA.schema)
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_template_binary_sensor(config[CONF_NAME])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config)
|
||||
setup_component(make.Ptemplate_, config)
|
||||
|
||||
template_ = None
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(bool_)):
|
||||
yield
|
||||
rhs = App.make_template_binary_sensor(config[CONF_NAME], template_)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config)
|
||||
add(make.Ptemplate_.set_template(template_))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return binary_sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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
|
||||
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component
|
||||
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({
|
||||
|
||||
})
|
||||
|
||||
cover_ns = esphomelib_ns.namespace('cover')
|
||||
Cover = cover_ns.Cover
|
||||
MQTTCoverComponent = cover_ns.MQTTCoverComponent
|
||||
CoverState = cover_ns.CoverState
|
||||
|
||||
Cover = cover_ns.class_('Cover', Nameable)
|
||||
MQTTCoverComponent = cover_ns.class_('MQTTCoverComponent', mqtt.MQTTComponent)
|
||||
|
||||
CoverState = cover_ns.class_('CoverState')
|
||||
COVER_OPEN = cover_ns.COVER_OPEN
|
||||
COVER_CLOSED = cover_ns.COVER_CLOSED
|
||||
OpenAction = cover_ns.OpenAction
|
||||
CloseAction = cover_ns.CloseAction
|
||||
StopAction = cover_ns.StopAction
|
||||
|
||||
# Actions
|
||||
OpenAction = cover_ns.class_('OpenAction', Action)
|
||||
CloseAction = cover_ns.class_('CloseAction', Action)
|
||||
StopAction = cover_ns.class_('StopAction', Action)
|
||||
|
||||
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(Cover),
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTCoverComponent),
|
||||
})
|
||||
|
||||
COVER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(COVER_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_cover_core_(cover_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(cover_var.set_internal(config[CONF_INTERNAL]))
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
|
||||
@@ -33,3 +47,54 @@ def setup_cover(cover_obj, mqtt_obj, config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_COVER'
|
||||
|
||||
CONF_COVER_OPEN = 'cover.open'
|
||||
COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Cover),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA)
|
||||
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)
|
||||
type = OpenAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_COVER_CLOSE = 'cover.close'
|
||||
COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Cover),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA)
|
||||
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)
|
||||
type = CloseAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_COVER_STOP = 'cover.stop'
|
||||
COVER_STOP_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Cover),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA)
|
||||
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)
|
||||
type = StopAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
def core_to_hass_config(data, config):
|
||||
ret = mqtt.build_hass_config(data, 'cover', config, include_state=True, include_command=True)
|
||||
if ret is None:
|
||||
return None
|
||||
return ret
|
||||
|
||||
@@ -5,49 +5,56 @@ 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
|
||||
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.MakeTemplateCover
|
||||
MakeTemplateCover = Application.struct('MakeTemplateCover')
|
||||
TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(cover.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateCover),
|
||||
cv.GenerateID(): cv.declare_variable_id(TemplateCover),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_OPEN_ACTION): automation.ACTIONS_SCHEMA,
|
||||
vol.Optional(CONF_CLOSE_ACTION): automation.ACTIONS_SCHEMA,
|
||||
vol.Optional(CONF_STOP_ACTION): automation.ACTIONS_SCHEMA,
|
||||
}).extend(cover.COVER_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
|
||||
vol.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_template_cover(config[CONF_NAME])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
|
||||
cover.setup_cover(make.Ptemplate_, make.Pmqtt, config)
|
||||
setup_component(make.Ptemplate_, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = None
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(cover.CoverState)):
|
||||
yield
|
||||
add(make.Ptemplate_.set_state_lambda(template_))
|
||||
if CONF_OPEN_ACTION in config:
|
||||
actions = None
|
||||
for actions in automation.build_actions(config[CONF_OPEN_ACTION], NoArg):
|
||||
yield
|
||||
add(make.Ptemplate_.add_open_actions(actions))
|
||||
automation.build_automation(make.Ptemplate_.get_open_trigger(), NoArg,
|
||||
config[CONF_OPEN_ACTION])
|
||||
if CONF_CLOSE_ACTION in config:
|
||||
actions = None
|
||||
for actions in automation.build_actions(config[CONF_CLOSE_ACTION], NoArg):
|
||||
yield
|
||||
add(make.Ptemplate_.add_close_actions(actions))
|
||||
automation.build_automation(make.Ptemplate_.get_close_trigger(), NoArg,
|
||||
config[CONF_CLOSE_ACTION])
|
||||
if CONF_STOP_ACTION in config:
|
||||
actions = None
|
||||
for actions in automation.build_actions(config[CONF_STOP_ACTION], NoArg):
|
||||
yield
|
||||
add(make.Ptemplate_.add_stop_actions(actions))
|
||||
automation.build_automation(make.Ptemplate_.get_stop_trigger(), NoArg,
|
||||
config[CONF_STOP_ACTION])
|
||||
if CONF_OPTIMISTIC in config:
|
||||
add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
|
||||
cover.setup_cover(make.Ptemplate_, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_COVER'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = cover.core_to_hass_config(data, config)
|
||||
if ret is None:
|
||||
return None
|
||||
if CONF_OPTIMISTIC in config:
|
||||
ret['optimistic'] = config[CONF_OPTIMISTIC]
|
||||
return ret
|
||||
|
||||
32
esphomeyaml/components/custom_component.py
Normal 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'
|
||||
@@ -1,24 +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
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, PollingComponent
|
||||
|
||||
DallasComponent = sensor.sensor_ns.DallasComponent
|
||||
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.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})])
|
||||
vol.Required(CONF_PIN): pins.input_pullup_pin,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).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))
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
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'
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import config_validation as cv, pins
|
||||
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \
|
||||
CONF_SLEEP_DURATION, CONF_WAKEUP_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression, esphomelib_ns
|
||||
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id
|
||||
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):
|
||||
@@ -14,26 +17,41 @@ def validate_pin_number(value):
|
||||
return value
|
||||
|
||||
|
||||
DeepSleepComponent = esphomelib_ns.DeepSleepComponent
|
||||
DeepSleepComponent = esphomelib_ns.class_('DeepSleepComponent', Component)
|
||||
EnterDeepSleepAction = esphomelib_ns.class_('EnterDeepSleepAction', Action)
|
||||
PreventDeepSleepAction = esphomelib_ns.class_('PreventDeepSleepAction', Action)
|
||||
|
||||
WakeupPinMode = esphomelib_ns.enum('WakeupPinMode')
|
||||
WAKEUP_PIN_MODES = {
|
||||
'IGNORE': esphomelib_ns.WAKEUP_PIN_MODE_IGNORE,
|
||||
'KEEP_AWAKE': esphomelib_ns.WAKEUP_PIN_MODE_KEEP_AWAKE,
|
||||
'INVERT_WAKEUP': esphomelib_ns.WAKEUP_PIN_MODE_INVERT_WAKEUP,
|
||||
'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE,
|
||||
'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE,
|
||||
'INVERT_WAKEUP': WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP,
|
||||
}
|
||||
|
||||
esp_sleep_ext1_wakeup_mode_t = global_ns.enum('esp_sleep_ext1_wakeup_mode_t')
|
||||
Ext1Wakeup = esphomelib_ns.struct('Ext1Wakeup')
|
||||
EXT1_WAKEUP_MODES = {
|
||||
'ALL_LOW': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW,
|
||||
'ANY_HIGH': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH,
|
||||
}
|
||||
|
||||
CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode'
|
||||
CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent),
|
||||
vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.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): 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,
|
||||
})
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -42,7 +60,6 @@ def to_code(config):
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION]))
|
||||
if CONF_WAKEUP_PIN in config:
|
||||
pin = None
|
||||
for pin in gpio_input_pin_expression(config[CONF_WAKEUP_PIN]):
|
||||
yield
|
||||
add(deep_sleep.set_wakeup_pin(pin))
|
||||
@@ -53,5 +70,48 @@ def to_code(config):
|
||||
if CONF_RUN_DURATION in config:
|
||||
add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION]))
|
||||
|
||||
if CONF_ESP32_EXT1_WAKEUP in config:
|
||||
conf = config[CONF_ESP32_EXT1_WAKEUP]
|
||||
mask = 0
|
||||
for pin in conf[CONF_PINS]:
|
||||
mask |= 1 << pin[CONF_NUMBER]
|
||||
struct = StructInitializer(
|
||||
Ext1Wakeup,
|
||||
('mask', mask),
|
||||
('wakeup_mode', EXT1_WAKEUP_MODES[conf[CONF_MODE]])
|
||||
)
|
||||
add(deep_sleep.set_ext1_wakeup(struct))
|
||||
|
||||
setup_component(deep_sleep, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DEEP_SLEEP'
|
||||
|
||||
CONF_DEEP_SLEEP_ENTER = 'deep_sleep.enter'
|
||||
DEEP_SLEEP_ENTER_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(DeepSleepComponent),
|
||||
})
|
||||
|
||||
|
||||
@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):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_enter_deep_sleep_action(template_arg)
|
||||
type = EnterDeepSleepAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_DEEP_SLEEP_PREVENT = 'deep_sleep.prevent'
|
||||
DEEP_SLEEP_PREVENT_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(DeepSleepComponent),
|
||||
})
|
||||
|
||||
|
||||
@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):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_prevent_deep_sleep_action(template_arg)
|
||||
type = PreventDeepSleepAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
58
esphomeyaml/components/display/__init__.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import add
|
||||
from esphomeyaml.cpp_types import esphomelib_ns
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
display_ns = esphomelib_ns.namespace('display')
|
||||
DisplayBuffer = display_ns.class_('DisplayBuffer')
|
||||
DisplayBufferRef = DisplayBuffer.operator('ref')
|
||||
|
||||
DISPLAY_ROTATIONS = {
|
||||
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
|
||||
90: display_ns.DISPLAY_ROTATION_90_DEGREES,
|
||||
180: display_ns.DISPLAY_ROTATION_180_DEGREES,
|
||||
270: display_ns.DISPLAY_ROTATION_270_DEGREES,
|
||||
}
|
||||
|
||||
|
||||
def validate_rotation(value):
|
||||
value = cv.string(value)
|
||||
if value.endswith(u"°"):
|
||||
value = value[:-1]
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
raise vol.Invalid(u"Expected integer for rotation")
|
||||
return cv.one_of(*DISPLAY_ROTATIONS)(value)
|
||||
|
||||
|
||||
BASIC_DISPLAY_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
})
|
||||
|
||||
FULL_DISPLAY_PLATFORM_SCHEMA = BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_ROTATION): validate_rotation,
|
||||
})
|
||||
|
||||
|
||||
def setup_display_core_(display_var, config):
|
||||
if CONF_UPDATE_INTERVAL in config:
|
||||
add(display_var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
|
||||
if CONF_ROTATION in config:
|
||||
add(display_var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
|
||||
|
||||
|
||||
def setup_display(display_var, config):
|
||||
CORE.add_job(setup_display_core_, display_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DISPLAY'
|
||||
76
esphomeyaml/components/display/lcd_gpio.py
Normal file
@@ -0,0 +1,76 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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.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')
|
||||
GPIOLCDDisplay = display.display_ns.class_('GPIOLCDDisplay', LCDDisplay)
|
||||
|
||||
|
||||
def validate_lcd_dimensions(value):
|
||||
value = cv.dimensions(value)
|
||||
if value[0] > 0x40:
|
||||
raise vol.Invalid("LCD displays can't have more than 64 columns")
|
||||
if value[1] > 4:
|
||||
raise vol.Invalid("LCD displays can't have more than 4 rows")
|
||||
return value
|
||||
|
||||
|
||||
def validate_pin_length(value):
|
||||
if len(value) != 4 and len(value) != 8:
|
||||
raise vol.Invalid("LCD Displays can either operate in 4-pin or 8-pin mode,"
|
||||
"not {}-pin mode".format(len(value)))
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(GPIOLCDDisplay),
|
||||
vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
|
||||
|
||||
vol.Required(CONF_DATA_PINS): vol.All([pins.gpio_output_pin_schema], validate_pin_length),
|
||||
vol.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_RS_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_gpio_lcd_display(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
|
||||
lcd = Pvariable(config[CONF_ID], rhs)
|
||||
pins_ = []
|
||||
for conf in config[CONF_DATA_PINS]:
|
||||
for pin in gpio_output_pin_expression(conf):
|
||||
yield
|
||||
pins_.append(pin)
|
||||
add(lcd.set_data_pins(*pins_))
|
||||
for enable in gpio_output_pin_expression(config[CONF_ENABLE_PIN]):
|
||||
yield
|
||||
add(lcd.set_enable_pin(enable))
|
||||
|
||||
for rs in gpio_output_pin_expression(config[CONF_RS_PIN]):
|
||||
yield
|
||||
add(lcd.set_rs_pin(rs))
|
||||
|
||||
if CONF_RW_PIN in config:
|
||||
for rw in gpio_output_pin_expression(config[CONF_RW_PIN]):
|
||||
yield
|
||||
add(lcd.set_rw_pin(rw))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')],
|
||||
return_type=void):
|
||||
yield
|
||||
add(lcd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(lcd, config)
|
||||
setup_component(lcd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_LCD_DISPLAY'
|
||||
40
esphomeyaml/components/display/lcd_pcf8574.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import display, i2c
|
||||
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.cpp_generator import Pvariable, add, process_lambda
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, void
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
PCF8574LCDDisplay = display.display_ns.class_('PCF8574LCDDisplay', LCDDisplay, i2c.I2CDevice)
|
||||
|
||||
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(PCF8574LCDDisplay),
|
||||
vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_pcf8574_lcd_display(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
|
||||
lcd = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_ADDRESS in config:
|
||||
add(lcd.set_address(config[CONF_ADDRESS]))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')],
|
||||
return_type=void):
|
||||
yield
|
||||
add(lcd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(lcd, config)
|
||||
setup_component(lcd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = ['-DUSE_LCD_DISPLAY', '-DUSE_LCD_DISPLAY_PCF8574']
|
||||
51
esphomeyaml/components/display/max7219.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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.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']
|
||||
|
||||
MAX7219Component = display.display_ns.class_('MAX7219Component', PollingComponent, spi.SPIDevice)
|
||||
MAX7219ComponentRef = MAX7219Component.operator('ref')
|
||||
|
||||
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MAX7219Component),
|
||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||
|
||||
vol.Optional(CONF_NUM_CHIPS): vol.All(cv.uint8_t, vol.Range(min=1)),
|
||||
vol.Optional(CONF_INTENSITY): vol.All(cv.uint8_t, vol.Range(min=0, max=15)),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for spi_ in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
rhs = App.make_max7219(spi_, cs)
|
||||
max7219 = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_NUM_CHIPS in config:
|
||||
add(max7219.set_num_chips(config[CONF_NUM_CHIPS]))
|
||||
if CONF_INTENSITY in config:
|
||||
add(max7219.set_intensity(config[CONF_INTENSITY]))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')],
|
||||
return_type=void):
|
||||
yield
|
||||
add(max7219.set_writer(lambda_))
|
||||
|
||||
display.setup_display(max7219, config)
|
||||
setup_component(max7219, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MAX7219'
|
||||
36
esphomeyaml/components/display/nextion.py
Normal file
@@ -0,0 +1,36 @@
|
||||
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.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']
|
||||
|
||||
Nextion = display.display_ns.class_('Nextion', PollingComponent, uart.UARTDevice)
|
||||
NextionRef = Nextion.operator('ref')
|
||||
|
||||
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(Nextion),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for uart_ in get_variable(config[CONF_UART_ID]):
|
||||
yield
|
||||
rhs = App.make_nextion(uart_)
|
||||
nextion = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')],
|
||||
return_type=void):
|
||||
yield
|
||||
add(nextion.set_writer(lambda_))
|
||||
|
||||
display.setup_display(nextion, config)
|
||||
setup_component(nextion, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_NEXTION'
|
||||
48
esphomeyaml/components/display/ssd1306_i2c.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.display import ssd1306_spi
|
||||
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']
|
||||
|
||||
I2CSSD1306 = display.display_ns.class_('I2CSSD1306', ssd1306_spi.SSD1306)
|
||||
|
||||
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(I2CSSD1306),
|
||||
vol.Required(CONF_MODEL): ssd1306_spi.SSD1306_MODEL,
|
||||
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
ssd = Pvariable(config[CONF_ID], App.make_i2c_ssd1306())
|
||||
add(ssd.set_model(ssd1306_spi.MODELS[config[CONF_MODEL]]))
|
||||
|
||||
if CONF_RESET_PIN in config:
|
||||
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
|
||||
yield
|
||||
add(ssd.set_reset_pin(reset))
|
||||
if CONF_EXTERNAL_VCC in config:
|
||||
add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC]))
|
||||
if CONF_ADDRESS in config:
|
||||
add(ssd.set_address(config[CONF_ADDRESS]))
|
||||
if CONF_LAMBDA in config:
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA],
|
||||
[(display.DisplayBufferRef, 'it')], return_type=void):
|
||||
yield
|
||||
add(ssd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(ssd, config)
|
||||
setup_component(ssd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SSD1306'
|
||||
71
esphomeyaml/components/display/ssd1306_spi.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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_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']
|
||||
|
||||
SSD1306 = display.display_ns.class_('SSD1306', PollingComponent, display.DisplayBuffer)
|
||||
SPISSD1306 = display.display_ns.class_('SPISSD1306', SSD1306, spi.SPIDevice)
|
||||
SSD1306Model = display.display_ns.enum('SSD1306Model')
|
||||
|
||||
MODELS = {
|
||||
'SSD1306_128X32': SSD1306Model.SSD1306_MODEL_128_32,
|
||||
'SSD1306_128X64': SSD1306Model.SSD1306_MODEL_128_64,
|
||||
'SSD1306_96X16': SSD1306Model.SSD1306_MODEL_96_16,
|
||||
'SSD1306_64X48': SSD1306Model.SSD1306_MODEL_64_48,
|
||||
'SH1106_128X32': SSD1306Model.SH1106_MODEL_128_32,
|
||||
'SH1106_128X64': SSD1306Model.SH1106_MODEL_128_64,
|
||||
'SH1106_96X16': SSD1306Model.SH1106_MODEL_96_16,
|
||||
'SH1106_64X48': SSD1306Model.SH1106_MODEL_64_48,
|
||||
}
|
||||
|
||||
SSD1306_MODEL = cv.one_of(*MODELS, upper=True, space="_")
|
||||
|
||||
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(SPISSD1306),
|
||||
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): SSD1306_MODEL,
|
||||
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for spi_ in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
for dc in gpio_output_pin_expression(config[CONF_DC_PIN]):
|
||||
yield
|
||||
|
||||
rhs = App.make_spi_ssd1306(spi_, cs, dc)
|
||||
ssd = Pvariable(config[CONF_ID], rhs)
|
||||
add(ssd.set_model(MODELS[config[CONF_MODEL]]))
|
||||
|
||||
if CONF_RESET_PIN in config:
|
||||
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
|
||||
yield
|
||||
add(ssd.set_reset_pin(reset))
|
||||
if CONF_EXTERNAL_VCC in 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')], return_type=void):
|
||||
yield
|
||||
add(ssd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(ssd, config)
|
||||
setup_component(ssd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SSD1306'
|
||||
93
esphomeyaml/components/display/waveshare_epaper.py
Normal file
@@ -0,0 +1,93 @@
|
||||
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_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.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']
|
||||
|
||||
WaveshareEPaperTypeA = display.display_ns.WaveshareEPaperTypeA
|
||||
WaveshareEPaper = display.display_ns.class_('WaveshareEPaper',
|
||||
PollingComponent, spi.SPIDevice, display.DisplayBuffer)
|
||||
|
||||
|
||||
WaveshareEPaperTypeAModel = display.display_ns.enum('WaveshareEPaperTypeAModel')
|
||||
WaveshareEPaperTypeBModel = display.display_ns.enum('WaveshareEPaperTypeBModel')
|
||||
|
||||
MODELS = {
|
||||
'1.54in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN),
|
||||
'2.13in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN),
|
||||
'2.90in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
|
||||
'2.70in': ('b', WaveshareEPaperTypeBModel.WAVESHARE_EPAPER_2_7_IN),
|
||||
'4.20in': ('b', WaveshareEPaperTypeBModel.WAVESHARE_EPAPER_4_2_IN),
|
||||
'7.50in': ('b', WaveshareEPaperTypeBModel.WAVESHARE_EPAPER_7_5_IN),
|
||||
}
|
||||
|
||||
|
||||
def validate_full_update_every_only_type_a(value):
|
||||
if CONF_FULL_UPDATE_EVERY not in value:
|
||||
return value
|
||||
if MODELS[value[CONF_MODEL]][0] != 'a':
|
||||
raise vol.Invalid("The 'full_update_every' option is only available for models "
|
||||
"'1.54in', '2.13in' and '2.90in'.")
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(WaveshareEPaper),
|
||||
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): 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,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), validate_full_update_every_only_type_a)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for spi_ in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
for dc in gpio_output_pin_expression(config[CONF_DC_PIN]):
|
||||
yield
|
||||
|
||||
model_type, model = MODELS[config[CONF_MODEL]]
|
||||
if model_type == 'a':
|
||||
rhs = App.make_waveshare_epaper_type_a(spi_, cs, dc, model)
|
||||
epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaperTypeA)
|
||||
elif model_type == 'b':
|
||||
rhs = App.make_waveshare_epaper_type_b(spi_, cs, dc, model)
|
||||
epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaper)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
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:
|
||||
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
|
||||
yield
|
||||
add(epaper.set_reset_pin(reset))
|
||||
if CONF_BUSY_PIN in config:
|
||||
for reset in gpio_input_pin_expression(config[CONF_BUSY_PIN]):
|
||||
yield
|
||||
add(epaper.set_busy_pin(reset))
|
||||
if CONF_FULL_UPDATE_EVERY in config:
|
||||
add(epaper.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||
|
||||
display.setup_display(epaper, config)
|
||||
setup_component(epaper, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_WAVESHARE_EPAPER'
|
||||
@@ -1,24 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
ESP32BLETracker = esphomelib_ns.ESP32BLETracker
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker),
|
||||
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_esp32_ble_tracker()
|
||||
ble = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_SCAN_INTERVAL in config:
|
||||
add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
|
||||
39
esphomeyaml/components/esp32_ble_beacon.py
Normal file
@@ -0,0 +1,39 @@
|
||||
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.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]
|
||||
|
||||
ESP32BLEBeacon = esphomelib_ns.class_('ESP32BLEBeacon', Component)
|
||||
|
||||
CONF_MAJOR = 'major'
|
||||
CONF_MINOR = 'minor'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon),
|
||||
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,
|
||||
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
uuid = config[CONF_UUID].hex
|
||||
uuid_arr = [RawExpression('0x{}'.format(uuid[i:i + 2])) for i in range(0, len(uuid), 2)]
|
||||
rhs = App.make_esp32_ble_beacon(ArrayInitializer(*uuid_arr, multiline=False))
|
||||
ble = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_MAJOR in config:
|
||||
add(ble.set_major(config[CONF_MAJOR]))
|
||||
if CONF_MINOR in config:
|
||||
add(ble.set_minor(config[CONF_MINOR]))
|
||||
|
||||
setup_component(ble, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_BEACON'
|
||||
41
esphomeyaml/components/esp32_ble_tracker.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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.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]
|
||||
|
||||
CONF_ESP32_BLE_ID = 'esp32_ble_id'
|
||||
ESP32BLETracker = esphomelib_ns.class_('ESP32BLETracker', Component)
|
||||
XiaomiSensor = esphomelib_ns.class_('XiaomiSensor', sensor.Sensor)
|
||||
XiaomiDevice = esphomelib_ns.class_('XiaomiDevice')
|
||||
XIAOMI_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(XiaomiSensor)
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker),
|
||||
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def make_address_array(address):
|
||||
addr = [HexInt(i) for i in address.parts]
|
||||
return ArrayInitializer(*addr, multiline=False)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_esp32_ble_tracker()
|
||||
ble = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_SCAN_INTERVAL in config:
|
||||
add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
|
||||
|
||||
setup_component(ble, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
|
||||
@@ -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
|
||||
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
|
||||
|
||||
|
||||
@@ -41,7 +44,7 @@ VOLTAGE_ATTENUATION = {
|
||||
'0V': global_ns.TOUCH_HVOLT_ATTEN_0V,
|
||||
}
|
||||
|
||||
ESP32TouchComponent = binary_sensor.binary_sensor_ns.ESP32TouchComponent
|
||||
ESP32TouchComponent = binary_sensor.binary_sensor_ns.class_('ESP32TouchComponent', Component)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32TouchComponent),
|
||||
@@ -54,7 +57,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_LOW_VOLTAGE_REFERENCE): validate_voltage(LOW_VOLTAGE_REFERENCE),
|
||||
vol.Optional(CONF_HIGH_VOLTAGE_REFERENCE): validate_voltage(HIGH_VOLTAGE_REFERENCE),
|
||||
vol.Optional(CONF_VOLTAGE_ATTENUATION): validate_voltage(VOLTAGE_ATTENUATION),
|
||||
})
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -80,5 +83,7 @@ def to_code(config):
|
||||
value = VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]]
|
||||
add(touch.set_voltage_attenuation(value))
|
||||
|
||||
setup_component(touch, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'
|
||||
|
||||
73
esphomeyaml/components/ethernet.py
Normal 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'
|
||||
@@ -1,34 +1,45 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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
|
||||
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_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({
|
||||
|
||||
})
|
||||
|
||||
fan_ns = esphomelib_ns.namespace('fan')
|
||||
FanState = fan_ns.FanState
|
||||
MQTTFanComponent = fan_ns.MQTTFanComponent
|
||||
MakeFan = Application.MakeFan
|
||||
TurnOnAction = fan_ns.TurnOnAction
|
||||
TurnOffAction = fan_ns.TurnOffAction
|
||||
ToggleAction = fan_ns.ToggleAction
|
||||
FanSpeed = fan_ns.FanSpeed
|
||||
FAN_SPEED_OFF = fan_ns.FAN_SPEED_OFF
|
||||
FAN_SPEED_LOW = fan_ns.FAN_SPEED_LOW
|
||||
FAN_SPEED_MEDIUM = fan_ns.FAN_SPEED_MEDIUM
|
||||
FAN_SPEED_HIGH = fan_ns.FAN_SPEED_HIGH
|
||||
FanState = fan_ns.class_('FanState', Nameable, Component)
|
||||
MQTTFanComponent = fan_ns.class_('MQTTFanComponent', mqtt.MQTTComponent)
|
||||
MakeFan = Application.struct('MakeFan')
|
||||
|
||||
# Actions
|
||||
TurnOnAction = fan_ns.class_('TurnOnAction', Action)
|
||||
TurnOffAction = fan_ns.class_('TurnOffAction', Action)
|
||||
ToggleAction = fan_ns.class_('ToggleAction', Action)
|
||||
|
||||
FanSpeed = fan_ns.enum('FanSpeed')
|
||||
FAN_SPEED_OFF = FanSpeed.FAN_SPEED_OFF
|
||||
FAN_SPEED_LOW = FanSpeed.FAN_SPEED_LOW
|
||||
FAN_SPEED_MEDIUM = FanSpeed.FAN_SPEED_MEDIUM
|
||||
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,
|
||||
@@ -38,11 +49,10 @@ 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]))
|
||||
|
||||
if CONF_OSCILLATION_STATE_TOPIC in config:
|
||||
add(mqtt_var.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
|
||||
if CONF_OSCILLATION_COMMAND_TOPIC in config:
|
||||
@@ -61,3 +71,70 @@ 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),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA)
|
||||
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)
|
||||
type = ToggleAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_FAN_TURN_OFF = 'fan.turn_off'
|
||||
FAN_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(FanState),
|
||||
})
|
||||
|
||||
|
||||
@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):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = TurnOffAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
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(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):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = TurnOnAction.template(arg_type)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
if CONF_OSCILLATING in config:
|
||||
for template_ in templatable(config[CONF_OSCILLATING], arg_type, bool_):
|
||||
yield None
|
||||
add(action.set_oscillating(template_))
|
||||
if CONF_SPEED in config:
|
||||
for template_ in templatable(config[CONF_SPEED], arg_type, FanSpeed):
|
||||
yield None
|
||||
add(action.set_speed(template_))
|
||||
yield action
|
||||
|
||||
|
||||
def core_to_hass_config(data, config):
|
||||
ret = mqtt.build_hass_config(data, 'fan', config, include_state=True, include_command=True)
|
||||
if ret is None:
|
||||
return None
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'oscillation/state')
|
||||
ret['oscillation_state_topic'] = config.get(CONF_OSCILLATION_STATE_TOPIC, default)
|
||||
default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'oscillation/command')
|
||||
ret['oscillation_command__topic'] = config.get(CONF_OSCILLATION_COMMAND_TOPIC, default)
|
||||
return ret
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import fan
|
||||
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
|
||||
from esphomeyaml.cpp_generator import get_variable, variable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App
|
||||
|
||||
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
|
||||
}).extend(fan.FAN_SCHEMA.schema)
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput),
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
for output_ in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
|
||||
rhs = App.make_fan(config[CONF_NAME])
|
||||
fan_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
add(fan_struct.Poutput.set_binary(output))
|
||||
add(fan_struct.Poutput.set_binary(output_))
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = None
|
||||
for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
|
||||
yield
|
||||
add(fan_struct.Poutput.set_oscillation(oscillation_output))
|
||||
|
||||
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)
|
||||
setup_component(fan_struct.Poutput, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return fan.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,45 +1,55 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import fan
|
||||
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 = fan.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Optional(CONF_SPEED_STATE_TOPIC): cv.publish_topic,
|
||||
vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput),
|
||||
vol.Optional(CONF_SPEED): vol.Schema({
|
||||
vol.Required(CONF_LOW): cv.percentage,
|
||||
vol.Required(CONF_MEDIUM): cv.percentage,
|
||||
vol.Required(CONF_HIGH): cv.percentage,
|
||||
}),
|
||||
}).extend(fan.FAN_SCHEMA.schema)
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
for output_ in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_fan(config[CONF_NAME])
|
||||
fan_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
if CONF_SPEED in config:
|
||||
speeds = config[CONF_SPEED]
|
||||
add(fan_struct.Poutput.set_speed(output, 0.0,
|
||||
add(fan_struct.Poutput.set_speed(output_,
|
||||
speeds[CONF_LOW],
|
||||
speeds[CONF_MEDIUM],
|
||||
speeds[CONF_HIGH]))
|
||||
else:
|
||||
add(fan_struct.Poutput.set_speed(output))
|
||||
add(fan_struct.Poutput.set_speed(output_))
|
||||
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = None
|
||||
for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
|
||||
yield
|
||||
add(fan_struct.Poutput.set_oscillation(oscillation_output))
|
||||
|
||||
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = fan.core_to_hass_config(data, config)
|
||||
if ret is None:
|
||||
return None
|
||||
default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'speed/state')
|
||||
ret['speed_state_topic'] = config.get(CONF_SPEED_STATE_TOPIC, default)
|
||||
default = mqtt.get_default_topic_for(data, 'fan', config[CONF_NAME], 'speed/command')
|
||||
ret['speed_command__topic'] = config.get(CONF_SPEED_COMMAND_TOPIC, default)
|
||||
return ret
|
||||
|
||||
121
esphomeyaml/components/font.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
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 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')
|
||||
|
||||
|
||||
def validate_glyphs(value):
|
||||
if isinstance(value, list):
|
||||
value = vol.Schema([cv.string])(value)
|
||||
value = vol.Schema([cv.string])(list(value))
|
||||
|
||||
def comparator(x, y):
|
||||
x_ = x.encode('utf-8')
|
||||
y_ = y.encode('utf-8')
|
||||
|
||||
for c in range(min(len(x_), len(y_))):
|
||||
if x_[c] < y_[c]:
|
||||
return -1
|
||||
if x_[c] > y_[c]:
|
||||
return 1
|
||||
|
||||
if len(x_) < len(y_):
|
||||
return -1
|
||||
if len(x_) > len(y_):
|
||||
return 1
|
||||
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
|
||||
|
||||
sort_by_cmp(value, comparator)
|
||||
return value
|
||||
|
||||
|
||||
def validate_pillow_installed(value):
|
||||
try:
|
||||
import PIL
|
||||
except ImportError:
|
||||
raise vol.Invalid("Please install the pillow python package to use this feature. "
|
||||
"(pip install pillow)")
|
||||
|
||||
if PIL.__version__[0] < '4':
|
||||
raise vol.Invalid("Please update your pillow installation to at least 4.0.x. "
|
||||
"(pip install -U pillow)")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def validate_truetype_file(value):
|
||||
if value.endswith('.zip'): # for Google Fonts downloads
|
||||
raise vol.Invalid(u"Please unzip the font archive '{}' first and then use the .ttf files "
|
||||
u"inside.".format(value))
|
||||
if not value.endswith('.ttf'):
|
||||
raise vol.Invalid(u"Only truetype (.ttf) files are supported. Please make sure you're "
|
||||
u"using the correct format or rename the extension to .ttf")
|
||||
return cv.file_(value)
|
||||
|
||||
|
||||
DEFAULT_GLYPHS = u' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
|
||||
CONF_RAW_DATA_ID = 'raw_data_id'
|
||||
|
||||
FONT_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Font),
|
||||
vol.Required(CONF_FILE): validate_truetype_file,
|
||||
vol.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
|
||||
vol.Optional(CONF_SIZE, default=20): vol.All(cv.int_, vol.Range(min=1)),
|
||||
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.All(validate_pillow_installed, FONT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
from PIL import ImageFont
|
||||
|
||||
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()
|
||||
|
||||
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(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 config[CONF_GLYPHS]:
|
||||
glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))
|
||||
|
||||
rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
|
||||
Pvariable(config[CONF_ID], rhs)
|
||||
35
esphomeyaml/components/globals.py
Normal file
@@ -0,0 +1,35 @@
|
||||
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.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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
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 config.get(CONF_RESTORE_VALUE, False):
|
||||
hash_ = hash(config[CONF_ID].id) % 2**32
|
||||
add(glob.set_restore_value(hash_))
|
||||
|
||||
setup_component(glob, config)
|
||||
@@ -1,21 +1,27 @@
|
||||
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
|
||||
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.I2CComponent
|
||||
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_RECEIVE_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_SCAN): cv.boolean,
|
||||
})
|
||||
|
||||
vol.Optional(CONF_RECEIVE_TIMEOUT): cv.invalid("The receive_timeout option has been removed "
|
||||
"because timeouts are already handled by the "
|
||||
"low-level i2c interface.")
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -23,8 +29,8 @@ def to_code(config):
|
||||
i2c = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_FREQUENCY in config:
|
||||
add(i2c.set_frequency(config[CONF_FREQUENCY]))
|
||||
if CONF_RECEIVE_TIMEOUT in config:
|
||||
add(i2c.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT]))
|
||||
|
||||
setup_component(i2c, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_I2C'
|
||||
|
||||
65
esphomeyaml/components/image.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
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 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')
|
||||
|
||||
CONF_RAW_DATA_ID = 'raw_data_id'
|
||||
|
||||
IMAGE_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Image_),
|
||||
vol.Required(CONF_FILE): cv.file_,
|
||||
vol.Optional(CONF_RESIZE): cv.dimensions,
|
||||
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.All(font.validate_pillow_installed, IMAGE_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
from PIL import Image
|
||||
|
||||
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 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)
|
||||
|
||||
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(config[CONF_ID], rhs)
|
||||
24
esphomeyaml/components/interval.py
Normal 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({
|
||||
cv.GenerateID(): 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(conf[CONF_INTERVAL]))
|
||||
trigger = Pvariable(conf[CONF_ID], rhs)
|
||||
setup_component(trigger, conf)
|
||||
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
@@ -1,28 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
|
||||
|
||||
IRTransmitterComponent = switch.switch_ns.namespace('IRTransmitterComponent')
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(IRTransmitterComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int),
|
||||
vol.Range(min=1, max=100)),
|
||||
})])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(conf[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT))
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
|
||||
@@ -1,31 +1,389 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
|
||||
CONF_MQTT_ID
|
||||
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component
|
||||
from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \
|
||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \
|
||||
CONF_GAMMA_CORRECT, CONF_GREEN, CONF_ID, CONF_INTERNAL, CONF_LAMBDA, CONF_MQTT_ID, CONF_NAME, \
|
||||
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.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({
|
||||
|
||||
})
|
||||
|
||||
# Base
|
||||
light_ns = esphomelib_ns.namespace('light')
|
||||
LightState = light_ns.LightState
|
||||
MQTTJSONLightComponent = light_ns.MQTTJSONLightComponent
|
||||
ToggleAction = light_ns.ToggleAction
|
||||
TurnOffAction = light_ns.TurnOffAction
|
||||
TurnOnAction = light_ns.TurnOnAction
|
||||
MakeLight = Application.MakeLight
|
||||
LightState = light_ns.class_('LightState', Nameable, Component)
|
||||
MakeLight = Application.struct('MakeLight')
|
||||
LightOutput = light_ns.class_('LightOutput')
|
||||
AddressableLight = light_ns.class_('AddressableLight')
|
||||
AddressableLightRef = AddressableLight.operator('ref')
|
||||
|
||||
# Actions
|
||||
ToggleAction = light_ns.class_('ToggleAction', Action)
|
||||
TurnOffAction = light_ns.class_('TurnOffAction', Action)
|
||||
TurnOnAction = light_ns.class_('TurnOnAction', Action)
|
||||
|
||||
LightColorValues = light_ns.class_('LightColorValues')
|
||||
|
||||
MQTTJSONLightComponent = light_ns.class_('MQTTJSONLightComponent', mqtt.MQTTComponent)
|
||||
|
||||
# Effects
|
||||
LightEffect = light_ns.class_('LightEffect')
|
||||
RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect)
|
||||
LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect)
|
||||
StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect)
|
||||
StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect)
|
||||
FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect)
|
||||
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_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'
|
||||
CONF_MOVE_INTERVAL = 'move_interval'
|
||||
CONF_TWINKLE_PROBABILITY = 'twinkle_probability'
|
||||
CONF_PROGRESS_INTERVAL = 'progress_interval'
|
||||
CONF_SPARK_PROBABILITY = 'spark_probability'
|
||||
CONF_USE_RANDOM_COLOR = 'use_random_color'
|
||||
CONF_FADE_OUT_RATE = 'fade_out_rate'
|
||||
CONF_INTENSITY = 'intensity'
|
||||
|
||||
BINARY_EFFECTS = [CONF_LAMBDA, CONF_STROBE]
|
||||
MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + [CONF_FLICKER]
|
||||
RGB_EFFECTS = MONOCHROMATIC_EFFECTS + [CONF_RANDOM]
|
||||
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({
|
||||
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_RANDOM): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(RandomLightEffect),
|
||||
vol.Optional(CONF_NAME, default="Random"): cv.string,
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
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.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,
|
||||
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.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)),
|
||||
}),
|
||||
vol.Optional(CONF_FLICKER): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FlickerLightEffect),
|
||||
vol.Optional(CONF_NAME, default="Flicker"): cv.string,
|
||||
vol.Optional(CONF_ALPHA): cv.percentage,
|
||||
vol.Optional(CONF_INTENSITY): cv.percentage,
|
||||
}),
|
||||
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_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_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): 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_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_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_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_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_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,
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
def validate_effects(allowed_effects):
|
||||
def validator(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):
|
||||
errors.append(
|
||||
vol.Invalid("Each effect must be a dictionary, not {}".format(type(value)),
|
||||
path)
|
||||
)
|
||||
continue
|
||||
if len(effect) > 1:
|
||||
errors.append(
|
||||
vol.Invalid("Each entry in the 'effects:' option must be a single effect.",
|
||||
path)
|
||||
)
|
||||
continue
|
||||
if not effect:
|
||||
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:
|
||||
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 {}
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(LightState),
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTJSONLightComponent),
|
||||
})
|
||||
|
||||
LIGHT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(LIGHT_SCHEMA.schema)
|
||||
|
||||
|
||||
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], [], return_type=void):
|
||||
yield None
|
||||
yield LambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL])
|
||||
elif key == CONF_RANDOM:
|
||||
rhs = RandomLightEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
if CONF_TRANSITION_LENGTH in config:
|
||||
add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH]))
|
||||
if CONF_UPDATE_INTERVAL in config:
|
||||
add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
|
||||
yield effect
|
||||
elif key == CONF_STROBE:
|
||||
rhs = StrobeLightEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
colors = []
|
||||
for color in config.get(CONF_COLORS, []):
|
||||
colors.append(StructInitializer(
|
||||
StrobeLightEffectColor,
|
||||
('color', LightColorValues(color[CONF_STATE], color[CONF_BRIGHTNESS],
|
||||
color[CONF_RED], color[CONF_GREEN], color[CONF_BLUE],
|
||||
color[CONF_WHITE])),
|
||||
('duration', color[CONF_DURATION]),
|
||||
))
|
||||
if colors:
|
||||
add(effect.set_colors(ArrayInitializer(*colors)))
|
||||
yield effect
|
||||
elif key == CONF_FLICKER:
|
||||
rhs = FlickerLightEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
if CONF_ALPHA in config:
|
||||
add(effect.set_alpha(config[CONF_ALPHA]))
|
||||
if CONF_INTENSITY in config:
|
||||
add(effect.set_intensity(config[CONF_INTENSITY]))
|
||||
yield effect
|
||||
elif key == CONF_ADDRESSABLE_LAMBDA:
|
||||
args = [(AddressableLightRef, 'it')]
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], args, return_type=void):
|
||||
yield None
|
||||
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_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]))
|
||||
if CONF_REVERSE in config:
|
||||
add(effect.set_reverse(config[CONF_REVERSE]))
|
||||
colors = []
|
||||
for color in config.get(CONF_COLORS, []):
|
||||
colors.append(StructInitializer(
|
||||
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_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_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_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_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]))
|
||||
if CONF_SPARK_PROBABILITY in config:
|
||||
add(effect.set_spark_probability(config[CONF_SPARK_PROBABILITY]))
|
||||
if CONF_USE_RANDOM_COLOR in config:
|
||||
add(effect.set_spark_probability(config[CONF_USE_RANDOM_COLOR]))
|
||||
if CONF_FADE_OUT_RATE in config:
|
||||
add(effect.set_spark_probability(config[CONF_FADE_OUT_RATE]))
|
||||
yield effect
|
||||
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]))
|
||||
if CONF_INTENSITY in config:
|
||||
add(effect.set_intensity(config[CONF_INTENSITY]))
|
||||
yield effect
|
||||
else:
|
||||
raise NotImplementedError("Effect {} not implemented".format(next(config.keys())))
|
||||
|
||||
|
||||
def setup_light_core_(light_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(light_var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_DEFAULT_TRANSITION_LENGTH in config:
|
||||
add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
|
||||
if CONF_GAMMA_CORRECT in config:
|
||||
add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
|
||||
effects = []
|
||||
for conf in config.get(CONF_EFFECTS, []):
|
||||
for effect in build_effect(conf):
|
||||
yield
|
||||
effects.append(effect)
|
||||
if effects:
|
||||
add(light_var.add_effects(ArrayInitializer(*effects)))
|
||||
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
@@ -33,7 +391,132 @@ 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)
|
||||
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),
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_LIGHT_TOGGLE, LIGHT_TOGGLE_ACTION_SCHEMA)
|
||||
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)
|
||||
type = ToggleAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
if CONF_TRANSITION_LENGTH in config:
|
||||
for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_transition_length(template_))
|
||||
yield action
|
||||
|
||||
|
||||
CONF_LIGHT_TURN_OFF = 'light.turn_off'
|
||||
LIGHT_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(LightState),
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
})
|
||||
|
||||
|
||||
@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):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = TurnOffAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
if CONF_TRANSITION_LENGTH in config:
|
||||
for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_transition_length(template_))
|
||||
yield action
|
||||
|
||||
|
||||
CONF_LIGHT_TURN_ON = 'light.turn_on'
|
||||
LIGHT_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(LightState),
|
||||
vol.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'):
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
vol.Exclusive(CONF_FLASH_LENGTH, 'transformer'):
|
||||
cv.templatable(cv.positive_time_period_milliseconds),
|
||||
vol.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_RED): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_GREEN): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_BLUE): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_WHITE): cv.templatable(cv.percentage),
|
||||
vol.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.positive_float),
|
||||
vol.Optional(CONF_EFFECT): cv.templatable(cv.string),
|
||||
})
|
||||
|
||||
|
||||
@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):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = TurnOnAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
if CONF_TRANSITION_LENGTH in config:
|
||||
for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_transition_length(template_))
|
||||
if CONF_FLASH_LENGTH in config:
|
||||
for template_ in templatable(config[CONF_FLASH_LENGTH], arg_type, uint32):
|
||||
yield None
|
||||
add(action.set_flash_length(template_))
|
||||
if CONF_BRIGHTNESS in config:
|
||||
for template_ in templatable(config[CONF_BRIGHTNESS], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_brightness(template_))
|
||||
if CONF_RED in config:
|
||||
for template_ in templatable(config[CONF_RED], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_red(template_))
|
||||
if CONF_GREEN in config:
|
||||
for template_ in templatable(config[CONF_GREEN], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_green(template_))
|
||||
if CONF_BLUE in config:
|
||||
for template_ in templatable(config[CONF_BLUE], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_blue(template_))
|
||||
if CONF_WHITE in config:
|
||||
for template_ in templatable(config[CONF_WHITE], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_white(template_))
|
||||
if CONF_COLOR_TEMPERATURE in config:
|
||||
for template_ in templatable(config[CONF_COLOR_TEMPERATURE], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_color_temperature(template_))
|
||||
if CONF_EFFECT in config:
|
||||
for template_ in templatable(config[CONF_EFFECT], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_effect(template_))
|
||||
yield action
|
||||
|
||||
|
||||
def core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True,
|
||||
white_value=True):
|
||||
ret = mqtt.build_hass_config(data, 'light', config, include_state=True, include_command=True)
|
||||
if ret is None:
|
||||
return None
|
||||
ret['schema'] = 'json'
|
||||
if brightness:
|
||||
ret['brightness'] = True
|
||||
if rgb:
|
||||
ret['rgb'] = True
|
||||
if color_temp:
|
||||
ret['color_temp'] = True
|
||||
if white_value:
|
||||
ret['white_value'] = True
|
||||
for effect in config.get(CONF_EFFECTS, []):
|
||||
ret["effect"] = True
|
||||
effects = ret.setdefault("effect_list", [])
|
||||
effects.append(next(x for x in effect.values())[CONF_NAME])
|
||||
return ret
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import light, output
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
from esphomeyaml.const import CONF_EFFECTS, CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
}).extend(light.LIGHT_SCHEMA.schema)
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput),
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.BINARY_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
for output_ in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_binary_light(config[CONF_NAME], output)
|
||||
rhs = App.make_binary_light(config[CONF_NAME], output_)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
setup_component(light_struct.Pstate, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return light.core_to_hass_config(data, config, brightness=False, rgb=False, color_temp=False,
|
||||
white_value=False)
|
||||
|
||||
42
esphomeyaml/components/light/cwww.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light, output
|
||||
from esphomeyaml.components.light.rgbww import validate_cold_white_colder, \
|
||||
validate_color_temperature
|
||||
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.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),
|
||||
vol.Required(CONF_COLD_WHITE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_WARM_WHITE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
|
||||
vol.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
|
||||
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), validate_cold_white_colder)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for cold_white in get_variable(config[CONF_COLD_WHITE]):
|
||||
yield
|
||||
for warm_white in get_variable(config[CONF_WARM_WHITE]):
|
||||
yield
|
||||
rhs = App.make_cwww_light(config[CONF_NAME], config[CONF_COLD_WHITE_COLOR_TEMPERATURE],
|
||||
config[CONF_WARM_WHITE_COLOR_TEMPERATURE],
|
||||
cold_white, warm_white)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
setup_component(light_struct.Pstate, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return light.core_to_hass_config(data, config, brightness=True, rgb=False, color_temp=True,
|
||||
white_value=False)
|
||||
@@ -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
|
||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
|
||||
get_variable, variable
|
||||
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',
|
||||
@@ -53,22 +54,24 @@ def validate(value):
|
||||
return value
|
||||
|
||||
|
||||
MakeFastLEDLight = Application.MakeFastLEDLight
|
||||
MakeFastLEDLight = Application.struct('MakeFastLEDLight')
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(light.PLATFORM_SCHEMA.extend({
|
||||
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),
|
||||
}).extend(light.LIGHT_SCHEMA.schema), validate)
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), validate)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -87,12 +90,23 @@ def to_code(config):
|
||||
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = None
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
add(fast_led.set_power_supply(power_supply))
|
||||
|
||||
if CONF_COLOR_CORRECT in config:
|
||||
r, g, b = config[CONF_COLOR_CORRECT]
|
||||
add(fast_led.set_correction(r, g, b))
|
||||
|
||||
light.setup_light(make.Pstate, make.Pmqtt, config)
|
||||
setup_component(fast_led, 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,
|
||||
white_value=False)
|
||||
|
||||
@@ -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
|
||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
|
||||
get_variable, variable
|
||||
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',
|
||||
@@ -30,23 +31,25 @@ RGB_ORDERS = [
|
||||
'BGR',
|
||||
]
|
||||
|
||||
MakeFastLEDLight = Application.MakeFastLEDLight
|
||||
MakeFastLEDLight = Application.struct('MakeFastLEDLight')
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
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),
|
||||
}).extend(light.LIGHT_SCHEMA.schema)
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -67,12 +70,23 @@ def to_code(config):
|
||||
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = None
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
add(fast_led.set_power_supply(power_supply))
|
||||
|
||||
light.setup_light(make.Pstate, make.Pmqtt, config)
|
||||
if CONF_COLOR_CORRECT in config:
|
||||
r, g, b = config[CONF_COLOR_CORRECT]
|
||||
add(fast_led.set_correction(r, g, b))
|
||||
|
||||
light.setup_light(make.Pstate, make.Pmqtt, config)
|
||||
setup_component(fast_led, 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,
|
||||
white_value=False)
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import light, output
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, \
|
||||
CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
}).extend(light.LIGHT_SCHEMA.schema)
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
for output_ in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_monochromatic_light(config[CONF_NAME], output)
|
||||
rhs = App.make_monochromatic_light(config[CONF_NAME], output_)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
setup_component(light_struct.Pstate, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return light.core_to_hass_config(data, config, brightness=True, rgb=False, color_temp=False,
|
||||
white_value=False)
|
||||
|
||||
170
esphomeyaml/components/light/neopixelbus.py
Normal 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])
|
||||
@@ -1,31 +1,37 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import light, output
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
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 = light.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
|
||||
vol.Required(CONF_RED): cv.use_variable_id(None),
|
||||
vol.Required(CONF_GREEN): cv.use_variable_id(None),
|
||||
vol.Required(CONF_BLUE): cv.use_variable_id(None),
|
||||
vol.Required(CONF_RED): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
}).extend(light.LIGHT_SCHEMA.schema)
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
red = None
|
||||
for red in get_variable(config[CONF_RED]):
|
||||
yield
|
||||
green = None
|
||||
for green in get_variable(config[CONF_GREEN]):
|
||||
yield
|
||||
blue = None
|
||||
for blue in get_variable(config[CONF_BLUE]):
|
||||
yield
|
||||
rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
setup_component(light_struct.Pstate, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False,
|
||||
white_value=False)
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import light, output
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
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 = light.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
|
||||
vol.Required(CONF_RED): cv.use_variable_id(None),
|
||||
vol.Required(CONF_GREEN): cv.use_variable_id(None),
|
||||
vol.Required(CONF_BLUE): cv.use_variable_id(None),
|
||||
vol.Required(CONF_WHITE): cv.use_variable_id(None),
|
||||
vol.Required(CONF_RED): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_WHITE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
}).extend(light.LIGHT_SCHEMA.schema)
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
red = None
|
||||
for red in get_variable(config[CONF_RED]):
|
||||
yield
|
||||
green = None
|
||||
for green in get_variable(config[CONF_GREEN]):
|
||||
yield
|
||||
blue = None
|
||||
for blue in get_variable(config[CONF_BLUE]):
|
||||
yield
|
||||
white = None
|
||||
for white in get_variable(config[CONF_WHITE]):
|
||||
yield
|
||||
rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
setup_component(light_struct.Pstate, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False,
|
||||
white_value=True)
|
||||
|
||||
70
esphomeyaml/components/light/rgbww.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App
|
||||
|
||||
|
||||
def validate_color_temperature(value):
|
||||
try:
|
||||
val = cv.float_with_unit('Color Temperature', 'mireds')(value)
|
||||
except vol.Invalid:
|
||||
val = 1000000.0 / cv.float_with_unit('Color Temperature', 'K')(value)
|
||||
if val < 0:
|
||||
raise vol.Invalid("Color temperature cannot be negative")
|
||||
return val
|
||||
|
||||
|
||||
def validate_cold_white_colder(value):
|
||||
cw = value[CONF_COLD_WHITE_COLOR_TEMPERATURE]
|
||||
ww = value[CONF_WARM_WHITE_COLOR_TEMPERATURE]
|
||||
if cw > ww:
|
||||
raise vol.Invalid("Cold white color temperature cannot be higher than warm white")
|
||||
if cw == ww:
|
||||
raise vol.Invalid("Cold white color temperature cannot be the same as warm white")
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
|
||||
vol.Required(CONF_RED): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_COLD_WHITE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_WARM_WHITE): cv.use_variable_id(output.FloatOutput),
|
||||
vol.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
|
||||
vol.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
|
||||
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), validate_cold_white_colder)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for red in get_variable(config[CONF_RED]):
|
||||
yield
|
||||
for green in get_variable(config[CONF_GREEN]):
|
||||
yield
|
||||
for blue in get_variable(config[CONF_BLUE]):
|
||||
yield
|
||||
for cold_white in get_variable(config[CONF_COLD_WHITE]):
|
||||
yield
|
||||
for warm_white in get_variable(config[CONF_WARM_WHITE]):
|
||||
yield
|
||||
rhs = App.make_rgbww_light(config[CONF_NAME], config[CONF_COLD_WHITE_COLOR_TEMPERATURE],
|
||||
config[CONF_WARM_WHITE_COLOR_TEMPERATURE],
|
||||
red, green, blue, cold_white, warm_white)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
setup_component(light_struct.Pstate, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=True,
|
||||
white_value=True)
|
||||
@@ -1,10 +1,16 @@
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import ACTION_REGISTRY, LambdaAction
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGS, \
|
||||
CONF_TX_BUFFER_SIZE
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, global_ns
|
||||
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, 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,
|
||||
@@ -16,27 +22,36 @@ LOG_LEVELS = {
|
||||
'VERY_VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERY_VERBOSE,
|
||||
}
|
||||
|
||||
LOG_LEVEL_TO_ESP_LOG = {
|
||||
'ERROR': global_ns.ESP_LOGE,
|
||||
'WARN': global_ns.ESP_LOGW,
|
||||
'INFO': global_ns.ESP_LOGI,
|
||||
'DEBUG': global_ns.ESP_LOGD,
|
||||
'VERBOSE': global_ns.ESP_LOGV,
|
||||
'VERY_VERBOSE': global_ns.ESP_LOGVV,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
LogComponent = esphomelib_ns.LogComponent
|
||||
LogComponent = esphomelib_ns.class_('LogComponent', Component)
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(LogComponent),
|
||||
vol.Optional(CONF_BAUD_RATE): cv.positive_int,
|
||||
vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int,
|
||||
vol.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
|
||||
vol.Optional(CONF_TX_BUFFER_SIZE): cv.validate_bytes,
|
||||
vol.Optional(CONF_LEVEL): is_log_level,
|
||||
vol.Optional(CONF_LOGS): vol.Schema({
|
||||
cv.string: is_log_level,
|
||||
@@ -51,11 +66,88 @@ 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):
|
||||
def validator(value):
|
||||
if isinstance(value, dict):
|
||||
return vol.Schema(schema)(value)
|
||||
return vol.Schema(schema)({CONF_FORMAT: value})
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def validate_printf(value):
|
||||
# https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
cfmt = u"""\
|
||||
( # start of capture group 1
|
||||
% # literal "%"
|
||||
(?: # first option
|
||||
(?:[-+0 #]{0,5}) # optional flags
|
||||
(?:\d+|\*)? # width
|
||||
(?:\.(?:\d+|\*))? # precision
|
||||
(?:h|l|ll|w|I|I32|I64)? # size
|
||||
[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!"
|
||||
u"".format(len(matches), u', '.join(matches), len(value[CONF_ARGS])))
|
||||
return 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): 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):
|
||||
esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]]
|
||||
args = [RawExpression(text_type(x)) for x in config[CONF_ARGS]]
|
||||
|
||||
text = text_type(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
|
||||
|
||||
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)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
from collections import OrderedDict
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
|
||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LOG_TOPIC, \
|
||||
CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, \
|
||||
CONF_SSL_FINGERPRINTS, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, \
|
||||
CONF_WILL_MESSAGE
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
|
||||
StructInitializer, \
|
||||
TemplateArguments, add, esphomelib_ns, optional, std_string
|
||||
from esphomeyaml.automation import ACTION_REGISTRY
|
||||
from esphomeyaml.components import logger
|
||||
import esphomeyaml.config_validation as cv
|
||||
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):
|
||||
@@ -32,18 +39,24 @@ MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({
|
||||
}))
|
||||
|
||||
mqtt_ns = esphomelib_ns.namespace('mqtt')
|
||||
MQTTMessage = mqtt_ns.MQTTMessage
|
||||
MQTTClientComponent = mqtt_ns.MQTTClientComponent
|
||||
MQTTPublishAction = mqtt_ns.MQTTPublishAction
|
||||
MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger
|
||||
MQTTMessage = mqtt_ns.struct('MQTTMessage')
|
||||
MQTTClientComponent = mqtt_ns.class_('MQTTClientComponent', Component)
|
||||
MQTTPublishAction = mqtt_ns.class_('MQTTPublishAction', Action)
|
||||
MQTTPublishJsonAction = mqtt_ns.class_('MQTTPublishJsonAction', Action)
|
||||
MQTTMessageTrigger = mqtt_ns.class_('MQTTMessageTrigger', Trigger.template(std_string))
|
||||
MQTTJsonMessageTrigger = mqtt_ns.class_('MQTTJsonMessageTrigger',
|
||||
Trigger.template(JsonObjectConstRef))
|
||||
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
|
||||
|
||||
|
||||
@@ -54,29 +67,39 @@ 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,
|
||||
vol.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
|
||||
vol.Optional(CONF_SHUTDOWN_MESSAGE): MQTT_MESSAGE_SCHEMA,
|
||||
vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic,
|
||||
vol.Optional(CONF_LOG_TOPIC): MQTT_MESSAGE_TEMPLATE_SCHEMA,
|
||||
vol.Optional(CONF_LOG_TOPIC): vol.Any(None, MQTT_MESSAGE_BASE.extend({
|
||||
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_ON_MESSAGE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
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.publish_topic,
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
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):
|
||||
@@ -96,14 +119,21 @@ def to_code(config):
|
||||
rhs = App.init_mqtt(config[CONF_BROKER], config[CONF_PORT],
|
||||
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())
|
||||
if CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
|
||||
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
|
||||
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
|
||||
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:
|
||||
add(mqtt.set_discovery_info(discovery_prefix, discovery_retain))
|
||||
|
||||
if CONF_TOPIC_PREFIX in config:
|
||||
add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
|
||||
|
||||
if CONF_BIRTH_MESSAGE in config:
|
||||
birth_message = config[CONF_BIRTH_MESSAGE]
|
||||
if not birth_message:
|
||||
@@ -116,28 +146,194 @@ def to_code(config):
|
||||
add(mqtt.disable_last_will())
|
||||
else:
|
||||
add(mqtt.set_last_will(exp_mqtt_message(will_message)))
|
||||
if CONF_SHUTDOWN_MESSAGE in config:
|
||||
shutdown_message = config[CONF_SHUTDOWN_MESSAGE]
|
||||
if not shutdown_message:
|
||||
add(mqtt.disable_shutdown_message())
|
||||
else:
|
||||
add(mqtt.set_shutdown_message(exp_mqtt_message(shutdown_message)))
|
||||
|
||||
if CONF_CLIENT_ID in config:
|
||||
add(mqtt.set_client_id(config[CONF_CLIENT_ID]))
|
||||
|
||||
if CONF_LOG_TOPIC in config:
|
||||
log_topic = config[CONF_LOG_TOPIC]
|
||||
if not log_topic:
|
||||
add(mqtt.disable_log_message())
|
||||
else:
|
||||
add(mqtt.set_log_message_template(exp_mqtt_message(log_topic)))
|
||||
|
||||
if CONF_LEVEL in config:
|
||||
add(mqtt.set_log_level(logger.LOG_LEVELS[config[CONF_LEVEL]]))
|
||||
|
||||
if CONF_SSL_FINGERPRINTS in config:
|
||||
for fingerprint in config[CONF_SSL_FINGERPRINTS]:
|
||||
arr = [RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)]
|
||||
add(mqtt.add_ssl_fingerprint(ArrayInitializer(*arr, multiline=False)))
|
||||
|
||||
if CONF_KEEPALIVE in config:
|
||||
add(mqtt.set_keep_alive(config[CONF_KEEPALIVE]))
|
||||
|
||||
if CONF_REBOOT_TIMEOUT in 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, []):
|
||||
rhs = mqtt.make_json_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS])
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, JsonObjectConstRef, conf)
|
||||
|
||||
|
||||
CONF_MQTT_PUBLISH = 'mqtt.publish'
|
||||
MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
|
||||
vol.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload),
|
||||
vol.Optional(CONF_QOS): cv.templatable(cv.mqtt_qos),
|
||||
vol.Optional(CONF_RETAIN): cv.templatable(cv.boolean),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_MQTT_PUBLISH, MQTT_PUBLISH_ACTION_SCHEMA)
|
||||
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)
|
||||
for template_ in templatable(config[CONF_TOPIC], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_topic(template_))
|
||||
|
||||
for template_ in templatable(config[CONF_PAYLOAD], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_payload(template_))
|
||||
if CONF_QOS in config:
|
||||
for template_ in templatable(config[CONF_QOS], arg_type, uint8):
|
||||
yield
|
||||
add(action.set_qos(template_))
|
||||
if CONF_RETAIN in config:
|
||||
for template_ in templatable(config[CONF_RETAIN], arg_type, bool_):
|
||||
yield None
|
||||
add(action.set_retain(template_))
|
||||
yield action
|
||||
|
||||
|
||||
CONF_MQTT_PUBLISH_JSON = 'mqtt.publish_json'
|
||||
MQTT_PUBLISH_JSON_ACTION_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
|
||||
vol.Required(CONF_PAYLOAD): cv.lambda_,
|
||||
vol.Optional(CONF_QOS): cv.mqtt_qos,
|
||||
vol.Optional(CONF_RETAIN): cv.boolean,
|
||||
})
|
||||
|
||||
|
||||
@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):
|
||||
rhs = App.Pget_mqtt_client().Pmake_publish_json_action(template_arg)
|
||||
type = MQTTPublishJsonAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
for template_ in templatable(config[CONF_TOPIC], arg_type, std_string):
|
||||
yield None
|
||||
add(action.set_topic(template_))
|
||||
|
||||
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:
|
||||
add(action.set_qos(config[CONF_QOS]))
|
||||
if CONF_RETAIN in config:
|
||||
add(action.set_retain(config[CONF_RETAIN]))
|
||||
yield action
|
||||
|
||||
|
||||
def required_build_flags(config):
|
||||
if CONF_SSL_FINGERPRINTS in config:
|
||||
return '-DASYNC_TCP_SSL_ENABLED=1'
|
||||
return None
|
||||
|
||||
|
||||
def get_default_topic_for(data, component_type, name, suffix):
|
||||
whitelist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_'
|
||||
sanitized_name = ''.join(x for x in name.lower().replace(' ', '_') if x in whitelist)
|
||||
return '{}/{}/{}/{}'.format(data.topic_prefix, component_type,
|
||||
sanitized_name, suffix)
|
||||
|
||||
|
||||
def build_hass_config(data, component_type, config, include_state=True, include_command=True):
|
||||
if config.get(CONF_INTERNAL, False):
|
||||
return None
|
||||
ret = OrderedDict()
|
||||
ret['platform'] = 'mqtt'
|
||||
ret['name'] = config[CONF_NAME]
|
||||
if include_state:
|
||||
default = get_default_topic_for(data, component_type, config[CONF_NAME], 'state')
|
||||
ret['state_topic'] = config.get(CONF_STATE_TOPIC, default)
|
||||
if include_command:
|
||||
default = get_default_topic_for(data, component_type, config[CONF_NAME], 'command')
|
||||
ret['command_topic'] = config.get(CONF_STATE_TOPIC, default)
|
||||
avail = config.get(CONF_AVAILABILITY, data.availability)
|
||||
if avail:
|
||||
ret['availability_topic'] = avail[CONF_TOPIC]
|
||||
payload_available = avail[CONF_PAYLOAD_AVAILABLE]
|
||||
if payload_available != 'online':
|
||||
ret['payload_available'] = payload_available
|
||||
payload_not_available = avail[CONF_PAYLOAD_NOT_AVAILABLE]
|
||||
if payload_not_available != 'offline':
|
||||
ret['payload_not_available'] = payload_not_available
|
||||
return ret
|
||||
|
||||
|
||||
class GenerateHassConfigData(object):
|
||||
def __init__(self, config):
|
||||
if 'mqtt' not in config:
|
||||
raise EsphomeyamlError("Cannot generate Home Assistant MQTT config if MQTT is not "
|
||||
"used!")
|
||||
mqtt = config[CONF_MQTT]
|
||||
self.topic_prefix = mqtt.get(CONF_TOPIC_PREFIX, config[CONF_ESPHOMEYAML][CONF_NAME])
|
||||
birth_message = mqtt.get(CONF_BIRTH_MESSAGE)
|
||||
if CONF_BIRTH_MESSAGE not in mqtt:
|
||||
birth_message = {
|
||||
CONF_TOPIC: self.topic_prefix + '/status',
|
||||
CONF_PAYLOAD: 'online',
|
||||
}
|
||||
will_message = mqtt.get(CONF_WILL_MESSAGE)
|
||||
if CONF_WILL_MESSAGE not in mqtt:
|
||||
will_message = {
|
||||
CONF_TOPIC: self.topic_prefix + '/status',
|
||||
CONF_PAYLOAD: 'offline'
|
||||
}
|
||||
if not birth_message or not will_message:
|
||||
self.availability = None
|
||||
elif birth_message[CONF_TOPIC] != will_message[CONF_TOPIC]:
|
||||
self.availability = None
|
||||
else:
|
||||
self.availability = {
|
||||
CONF_TOPIC: birth_message[CONF_TOPIC],
|
||||
CONF_PAYLOAD_AVAILABLE: birth_message[CONF_PAYLOAD],
|
||||
CONF_PAYLOAD_NOT_AVAILABLE: will_message[CONF_PAYLOAD],
|
||||
}
|
||||
|
||||
|
||||
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]))
|
||||
|
||||
46
esphomeyaml/components/my9231.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import output
|
||||
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
|
||||
|
||||
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,
|
||||
vol.Optional(CONF_NUM_CHANNELS): vol.All(vol.Coerce(int),
|
||||
vol.Range(3, 1020)),
|
||||
vol.Optional(CONF_NUM_CHIPS): vol.All(vol.Coerce(int),
|
||||
vol.Range(1, 255)),
|
||||
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)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
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'
|
||||
@@ -1,23 +1,20 @@
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns
|
||||
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__)
|
||||
|
||||
OTAComponent = esphomelib_ns.OTAComponent
|
||||
OTAComponent = esphomelib_ns.class_('OTAComponent', Component)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(OTAComponent),
|
||||
vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
|
||||
# TODO Num attempts + wait time
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
vol.Optional(CONF_PASSWORD): cv.string,
|
||||
})
|
||||
@@ -27,8 +24,7 @@ def to_code(config):
|
||||
rhs = App.init_ota()
|
||||
ota = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_PASSWORD in config:
|
||||
hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
||||
add(ota.set_auth_password_hash(hash_))
|
||||
add(ota.set_auth_password(config[CONF_PASSWORD]))
|
||||
if CONF_PORT in config:
|
||||
add(ota.set_port(config[CONF_PORT]))
|
||||
if config[CONF_SAFE_MODE]:
|
||||
@@ -38,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):
|
||||
@@ -50,9 +46,12 @@ def get_auth(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OTA'
|
||||
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
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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
|
||||
from esphomeyaml.helpers import add, esphomelib_ns, get_variable
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY, CONF_ID, CONF_LEVEL
|
||||
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({
|
||||
|
||||
@@ -14,14 +17,27 @@ BINARY_OUTPUT_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
})
|
||||
|
||||
BINARY_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BINARY_OUTPUT_SCHEMA.schema)
|
||||
|
||||
FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({
|
||||
vol.Optional(CONF_MAX_POWER): cv.percentage,
|
||||
})
|
||||
|
||||
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)
|
||||
TurnOnAction = output_ns.class_('TurnOnAction', Action)
|
||||
SetLevelAction = output_ns.class_('SetLevelAction', Action)
|
||||
|
||||
|
||||
def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
def setup_output_platform_(obj, config, skip_power_supply=False):
|
||||
if CONF_INVERTED in config:
|
||||
add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
if not skip_power_supply and CONF_POWER_SUPPLY in config:
|
||||
@@ -33,4 +49,63 @@ def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
add(obj.set_max_power(config[CONF_MAX_POWER]))
|
||||
|
||||
|
||||
def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
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'
|
||||
|
||||
|
||||
CONF_OUTPUT_TURN_ON = 'output.turn_on'
|
||||
OUTPUT_TURN_ON_ACTION = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(BinaryOutput),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_ON_ACTION)
|
||||
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)
|
||||
type = TurnOnAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_OUTPUT_TURN_OFF = 'output.turn_off'
|
||||
OUTPUT_TURN_OFF_ACTION = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(BinaryOutput)
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_OFF_ACTION)
|
||||
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)
|
||||
type = TurnOffAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_OUTPUT_SET_LEVEL = 'output.set_level'
|
||||
OUTPUT_SET_LEVEL_ACTION = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(FloatOutput),
|
||||
vol.Required(CONF_LEVEL): cv.templatable(cv.percentage),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_SET_LEVEL, OUTPUT_SET_LEVEL_ACTION)
|
||||
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)
|
||||
type = SetLevelAction.template(arg_type)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
for template_ in templatable(config[CONF_LEVEL], arg_type, float_):
|
||||
yield None
|
||||
add(action.set_level(template_))
|
||||
yield action
|
||||
|
||||
66
esphomeyaml/components/output/custom.py
Normal 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'
|
||||
@@ -1,36 +1,42 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
|
||||
import esphomeyaml.config_validation as cv
|
||||
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]
|
||||
|
||||
|
||||
def valid_pwm_pin(value):
|
||||
if value[CONF_NUMBER] > 16:
|
||||
raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.")
|
||||
num = value[CONF_NUMBER]
|
||||
cv.one_of(0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16)(num)
|
||||
return value
|
||||
|
||||
|
||||
ESP8266PWMOutput = output.output_ns.ESP8266PWMOutput
|
||||
ESP8266PWMOutput = output.output_ns.class_('ESP8266PWMOutput', output.FloatOutput, Component)
|
||||
|
||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(ESP8266PWMOutput),
|
||||
vol.Required(CONF_PIN): vol.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin),
|
||||
}).extend(output.FLOAT_OUTPUT_SCHEMA.schema)
|
||||
vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=1.0e-6)),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_esp8266_pwm_output(pin)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_FREQUENCY in config:
|
||||
add(gpio.set_frequency(config[CONF_FREQUENCY]))
|
||||
|
||||
output.setup_output_platform(gpio, config)
|
||||
setup_component(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT'
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
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
|
||||
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.GPIOBinaryOutputComponent
|
||||
GPIOBinaryOutputComponent = output.output_ns.class_('GPIOBinaryOutputComponent',
|
||||
output.BinaryOutput, Component)
|
||||
|
||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = output.BINARY_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(GPIOBinaryOutputComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
}).extend(output.BINARY_OUTPUT_SCHEMA.schema)
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_gpio_output(pin)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(gpio, config)
|
||||
setup_component(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_OUTPUT'
|
||||
|
||||
@@ -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
|
||||
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]
|
||||
|
||||
@@ -19,15 +21,15 @@ def validate_frequency_bit_depth(obj):
|
||||
return obj
|
||||
|
||||
|
||||
LEDCOutputComponent = output.output_ns.LEDCOutputComponent
|
||||
LEDCOutputComponent = output.output_ns.class_('LEDCOutputComponent', output.FloatOutput, Component)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(output.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = vol.All(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(LEDCOutputComponent),
|
||||
vol.Required(CONF_PIN): pins.output_pin,
|
||||
vol.Optional(CONF_FREQUENCY): cv.frequency,
|
||||
vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int), vol.Range(min=1, max=15)),
|
||||
vol.Optional(CONF_CHANNEL): vol.All(vol.Coerce(int), vol.Range(min=0, max=15))
|
||||
}).extend(output.FLOAT_OUTPUT_SCHEMA.schema), validate_frequency_bit_depth)
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), validate_frequency_bit_depth)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -39,6 +41,7 @@ def to_code(config):
|
||||
if CONF_CHANNEL in config:
|
||||
add(ledc.set_channel(config[CONF_CHANNEL]))
|
||||
output.setup_output_platform(ledc, config)
|
||||
setup_component(ledc, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_LEDC_OUTPUT'
|
||||
|
||||
36
esphomeyaml/components/output/my9231.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.components.my9231 import MY9231OutputComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_MY9231_ID, CONF_POWER_SUPPLY
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
|
||||
DEPENDENCIES = ['my9231']
|
||||
|
||||
Channel = MY9231OutputComponent.class_('Channel', output.FloatOutput)
|
||||
|
||||
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Channel),
|
||||
vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int),
|
||||
vol.Range(min=0, max=65535)),
|
||||
cv.GenerateID(CONF_MY9231_ID): cv.use_variable_id(MY9231OutputComponent),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
power_supply = None
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
my9231 = None
|
||||
for my9231 in get_variable(config[CONF_MY9231_ID]):
|
||||
yield
|
||||
rhs = my9231.create_channel(config[CONF_CHANNEL], power_supply)
|
||||
out = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(out, config, skip_power_supply=True)
|
||||
setup_component(out, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MY9231_OUTPUT'
|
||||
@@ -1,21 +1,21 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.components.pca9685 import PCA9685OutputComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_PCA9685_ID, CONF_POWER_SUPPLY
|
||||
from esphomeyaml.helpers import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
|
||||
DEPENDENCIES = ['pca9685']
|
||||
|
||||
Channel = PCA9685OutputComponent.Channel
|
||||
Channel = PCA9685OutputComponent.class_('Channel', output.FloatOutput)
|
||||
|
||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Channel),
|
||||
vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int),
|
||||
vol.Range(min=0, max=15)),
|
||||
cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685OutputComponent),
|
||||
}).extend(output.FLOAT_OUTPUT_SCHEMA.schema)
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -23,7 +23,6 @@ def to_code(config):
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
pca9685 = None
|
||||
for pca9685 in get_variable(config[CONF_PCA9685_ID]):
|
||||
yield
|
||||
rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, output
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID, CONF_PHASE_BALANCER
|
||||
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, add
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Component
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
MULTI_CONF = True
|
||||
|
||||
PCA9685OutputComponent = output.output_ns.namespace('PCA9685OutputComponent')
|
||||
PCA9685OutputComponent = output.output_ns.class_('PCA9685OutputComponent',
|
||||
Component, i2c.I2CDevice)
|
||||
|
||||
PHASE_BALANCER_MESSAGE = ("The phase_balancer option has been removed in version 1.5.0. "
|
||||
"esphomelib will now automatically choose a suitable phase balancer.")
|
||||
|
||||
PCA9685_SCHEMA = vol.Schema({
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(PCA9685OutputComponent),
|
||||
vol.Required(CONF_FREQUENCY): vol.All(cv.frequency,
|
||||
vol.Range(min=23.84, max=1525.88)),
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
|
||||
vol.Optional(CONF_PHASE_BALANCER): cv.invalid(PHASE_BALANCER_MESSAGE),
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA])
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_pca9685_component(conf.get(CONF_FREQUENCY))
|
||||
pca9685 = Pvariable(conf[CONF_ID], rhs)
|
||||
if CONF_ADDRESS in conf:
|
||||
add(pca9685.set_address(HexIntLiteral(conf[CONF_ADDRESS])))
|
||||
rhs = App.make_pca9685_component(config.get(CONF_FREQUENCY))
|
||||
pca9685 = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_ADDRESS in config:
|
||||
add(pca9685.set_address(config[CONF_ADDRESS]))
|
||||
setup_component(pca9685, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_PCF8575
|
||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, GPIOInputPin, GPIOOutputPin, io_ns
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
MULTI_CONF = True
|
||||
|
||||
io_ns = esphomelib_ns.namespace('io')
|
||||
PCF8574Component = io_ns.PCF8574Component
|
||||
PCF8574GPIOMode = io_ns.enum('PCF8574GPIOMode')
|
||||
PCF8675_GPIO_MODES = {
|
||||
'INPUT': PCF8574GPIOMode.PCF8574_INPUT,
|
||||
'INPUT_PULLUP': PCF8574GPIOMode.PCF8574_INPUT_PULLUP,
|
||||
'OUTPUT': PCF8574GPIOMode.PCF8574_OUTPUT,
|
||||
}
|
||||
|
||||
PCF8574_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(PCF8574Component),
|
||||
PCF8574GPIOInputPin = io_ns.class_('PCF8574GPIOInputPin', GPIOInputPin)
|
||||
PCF8574GPIOOutputPin = io_ns.class_('PCF8574GPIOOutputPin', GPIOOutputPin)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(pins.PCF8574Component),
|
||||
vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address,
|
||||
vol.Optional(CONF_PCF8575, default=False): cv.boolean,
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA])
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575])
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
rhs = App.make_pcf8574_component(config[CONF_ADDRESS], config[CONF_PCF8575])
|
||||
var = Pvariable(config[CONF_ID], rhs)
|
||||
setup_component(var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCF8574'
|
||||
|
||||
46
esphomeyaml/components/pn532.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import automation, pins
|
||||
from esphomeyaml.components import binary_sensor, spi
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_ON_TAG, CONF_SPI_ID, CONF_TRIGGER_ID, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, PollingComponent, Trigger, std_string
|
||||
|
||||
DEPENDENCIES = ['spi']
|
||||
MULTI_CONF = True
|
||||
|
||||
PN532Component = binary_sensor.binary_sensor_ns.class_('PN532Component', PollingComponent,
|
||||
spi.SPIDevice)
|
||||
PN532Trigger = binary_sensor.binary_sensor_ns.class_('PN532Trigger', Trigger.template(std_string))
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(PN532Component),
|
||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
vol.Optional(CONF_ON_TAG): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PN532Trigger),
|
||||
}),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for spi_ in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
rhs = App.make_pn532_component(spi_, cs, config.get(CONF_UPDATE_INTERVAL))
|
||||
pn532 = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
for conf_ in config.get(CONF_ON_TAG, []):
|
||||
trigger = Pvariable(conf_[CONF_TRIGGER_ID], pn532.make_trigger())
|
||||
automation.build_automation(trigger, std_string, conf_)
|
||||
|
||||
setup_component(pn532, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PN532'
|
||||
@@ -1,33 +1,36 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression
|
||||
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, esphomelib_ns
|
||||
|
||||
PowerSupplyComponent = esphomelib_ns.PowerSupplyComponent
|
||||
PowerSupplyComponent = esphomelib_ns.class_('PowerSupplyComponent', Component)
|
||||
|
||||
POWER_SUPPLY_SCHEMA = vol.Schema({
|
||||
MULTI_CONF = True
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(PowerSupplyComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA])
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(conf[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_power_supply(pin)
|
||||
psu = Pvariable(conf[CONF_ID], rhs)
|
||||
if CONF_ENABLE_TIME in conf:
|
||||
add(psu.set_enable_time(conf[CONF_ENABLE_TIME]))
|
||||
if CONF_KEEP_ON_TIME in conf:
|
||||
add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME]))
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
|
||||
rhs = App.make_power_supply(pin)
|
||||
psu = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_ENABLE_TIME in config:
|
||||
add(psu.set_enable_time(config[CONF_ENABLE_TIME]))
|
||||
if CONF_KEEP_ON_TIME in config:
|
||||
add(psu.set_keep_on_time(config[CONF_KEEP_ON_TIME]))
|
||||
|
||||
setup_component(psu, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT'
|
||||
|
||||
29
esphomeyaml/components/rdm6300.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import binary_sensor, uart
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_UART_ID
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Component
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
RDM6300Component = binary_sensor.binary_sensor_ns.class_('RDM6300Component', Component,
|
||||
uart.UARTDevice)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(RDM6300Component),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(uart.UARTComponent),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for uart_ in get_variable(config[CONF_UART_ID]):
|
||||
yield
|
||||
rhs = App.make_rdm6300_component(uart_)
|
||||
var = Pvariable(config[CONF_ID], rhs)
|
||||
setup_component(var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_RDM6300'
|
||||
73
esphomeyaml/components/remote_receiver.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, \
|
||||
CONF_PIN, CONF_TOLERANCE
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
|
||||
from esphomeyaml.py_compat import string_types
|
||||
|
||||
remote_ns = esphomelib_ns.namespace('remote')
|
||||
MULTI_CONF = True
|
||||
|
||||
RemoteControlComponentBase = remote_ns.class_('RemoteControlComponentBase')
|
||||
RemoteReceiverComponent = remote_ns.class_('RemoteReceiverComponent',
|
||||
RemoteControlComponentBase,
|
||||
Component)
|
||||
|
||||
RemoteReceiveDumper = remote_ns.class_('RemoteReceiveDumper')
|
||||
|
||||
DUMPERS = {
|
||||
'lg': remote_ns.class_('LGDumper', RemoteReceiveDumper),
|
||||
'nec': remote_ns.class_('NECDumper', RemoteReceiveDumper),
|
||||
'panasonic': remote_ns.class_('PanasonicDumper', RemoteReceiveDumper),
|
||||
'raw': remote_ns.class_('RawDumper', RemoteReceiveDumper),
|
||||
'samsung': remote_ns.class_('SamsungDumper', RemoteReceiveDumper),
|
||||
'sony': remote_ns.class_('SonyDumper', RemoteReceiveDumper),
|
||||
'rc_switch': remote_ns.class_('RCSwitchDumper', RemoteReceiveDumper),
|
||||
}
|
||||
|
||||
|
||||
def validate_dumpers_all(value):
|
||||
if not isinstance(value, string_types):
|
||||
raise vol.Invalid("Not valid dumpers")
|
||||
if value.upper() == "ALL":
|
||||
return list(sorted(list(DUMPERS)))
|
||||
raise vol.Invalid("Not valid dumpers")
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(RemoteReceiverComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_input_pin_schema,
|
||||
vol.Optional(CONF_DUMP, default=[]):
|
||||
vol.Any(validate_dumpers_all, cv.ensure_list(cv.one_of(*DUMPERS, lower=True))),
|
||||
vol.Optional(CONF_TOLERANCE): vol.All(cv.percentage_int, vol.Range(min=0)),
|
||||
vol.Optional(CONF_BUFFER_SIZE): cv.validate_bytes,
|
||||
vol.Optional(CONF_FILTER): cv.positive_time_period_microseconds,
|
||||
vol.Optional(CONF_IDLE): cv.positive_time_period_microseconds,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for pin in gpio_input_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_remote_receiver_component(pin)
|
||||
receiver = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
for dumper in config[CONF_DUMP]:
|
||||
add(receiver.add_dumper(DUMPERS[dumper].new()))
|
||||
if CONF_TOLERANCE in config:
|
||||
add(receiver.set_tolerance(config[CONF_TOLERANCE]))
|
||||
if CONF_BUFFER_SIZE in config:
|
||||
add(receiver.set_buffer_size(config[CONF_BUFFER_SIZE]))
|
||||
if CONF_FILTER in config:
|
||||
add(receiver.set_filter_us(config[CONF_FILTER]))
|
||||
if CONF_IDLE in config:
|
||||
add(receiver.set_idle_us(config[CONF_IDLE]))
|
||||
|
||||
setup_component(receiver, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER'
|
||||
120
esphomeyaml/components/remote_transmitter.py
Normal file
@@ -0,0 +1,120 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components.remote_receiver import RemoteControlComponentBase, remote_ns
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_DUTY_PERCENT, CONF_CHANNEL, CONF_CODE, \
|
||||
CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_ID, CONF_INVERTED, CONF_ONE, CONF_PIN, \
|
||||
CONF_PROTOCOL, CONF_PULSE_LENGTH, CONF_STATE, CONF_SYNC, CONF_ZERO
|
||||
from esphomeyaml.core import HexInt
|
||||
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
|
||||
from esphomeyaml.py_compat import text_type
|
||||
|
||||
RemoteTransmitterComponent = remote_ns.class_('RemoteTransmitterComponent',
|
||||
RemoteControlComponentBase, Component)
|
||||
RCSwitchProtocol = remote_ns.class_('RCSwitchProtocol')
|
||||
rc_switch_protocols = remote_ns.rc_switch_protocols
|
||||
|
||||
MULTI_CONF = True
|
||||
|
||||
|
||||
def validate_rc_switch_code(value):
|
||||
if not isinstance(value, (str, text_type)):
|
||||
raise vol.Invalid("All RCSwitch codes must be in quotes ('')")
|
||||
for c in value:
|
||||
if c not in ('0', '1'):
|
||||
raise vol.Invalid(u"Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed"
|
||||
u"".format(c))
|
||||
if len(value) > 32:
|
||||
raise vol.Invalid("Maximum length for RCSwitch codes is 32, code '{}' has length {}"
|
||||
"".format(value, len(value)))
|
||||
if not value:
|
||||
raise vol.Invalid("RCSwitch code must not be empty")
|
||||
return value
|
||||
|
||||
|
||||
RC_SWITCH_TIMING_SCHEMA = vol.All([cv.uint8_t], vol.Length(min=2, max=2))
|
||||
|
||||
RC_SWITCH_PROTOCOL_SCHEMA = vol.Any(
|
||||
vol.All(vol.Coerce(int), vol.Range(min=1, max=7)),
|
||||
vol.Schema({
|
||||
vol.Required(CONF_PULSE_LENGTH): cv.uint32_t,
|
||||
vol.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA,
|
||||
vol.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA,
|
||||
vol.Optional(CONF_ONE, default=[3, 1]): RC_SWITCH_TIMING_SCHEMA,
|
||||
vol.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
})
|
||||
)
|
||||
|
||||
RC_SWITCH_RAW_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_CODE): validate_rc_switch_code,
|
||||
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_A_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_GROUP): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)),
|
||||
vol.Required(CONF_DEVICE): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)),
|
||||
vol.Required(CONF_STATE): cv.boolean,
|
||||
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_B_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ADDRESS): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
|
||||
vol.Required(CONF_CHANNEL): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
|
||||
vol.Required(CONF_STATE): cv.boolean,
|
||||
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_C_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
|
||||
'l', 'm', 'n', 'o', 'p', lower=True),
|
||||
vol.Required(CONF_GROUP): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
|
||||
vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
|
||||
vol.Required(CONF_STATE): cv.boolean,
|
||||
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
RC_SWITCH_TYPE_D_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True),
|
||||
vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=3)),
|
||||
vol.Required(CONF_STATE): cv.boolean,
|
||||
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(RemoteTransmitterComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(cv.percentage_int,
|
||||
vol.Range(min=1, max=100)),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def build_rc_switch_protocol(config):
|
||||
if isinstance(config, int):
|
||||
return rc_switch_protocols[config]
|
||||
pl = config[CONF_PULSE_LENGTH]
|
||||
return RCSwitchProtocol(config[CONF_SYNC][0] * pl, config[CONF_SYNC][1] * pl,
|
||||
config[CONF_ZERO][0] * pl, config[CONF_ZERO][1] * pl,
|
||||
config[CONF_ONE][0] * pl, config[CONF_ONE][1] * pl,
|
||||
config[CONF_INVERTED])
|
||||
|
||||
|
||||
def binary_code(value):
|
||||
code = 0
|
||||
for val in value:
|
||||
code <<= 1
|
||||
code |= val == '1'
|
||||
return HexInt(code)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_remote_transmitter_component(pin)
|
||||
transmitter = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_CARRIER_DUTY_PERCENT in config:
|
||||
add(transmitter.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT]))
|
||||
|
||||
setup_component(transmitter, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER'
|
||||
52
esphomeyaml/components/script.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_types import Action, NoArg, Trigger, esphomelib_ns
|
||||
|
||||
Script = esphomelib_ns.class_('Script', Trigger.template(NoArg))
|
||||
ScriptExecuteAction = esphomelib_ns.class_('ScriptExecuteAction', Action)
|
||||
ScriptStopAction = esphomelib_ns.class_('ScriptStopAction', Action)
|
||||
|
||||
CONFIG_SCHEMA = automation.validate_automation({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Script),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
trigger = Pvariable(conf[CONF_ID], Script.new())
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
|
||||
CONF_SCRIPT_EXECUTE = 'script.execute'
|
||||
SCRIPT_EXECUTE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Script),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_SCRIPT_EXECUTE, SCRIPT_EXECUTE_ACTION_SCHEMA)
|
||||
def script_execute_action_to_code(config, action_id, arg_type, template_arg):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_execute_action(template_arg)
|
||||
type = ScriptExecuteAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_SCRIPT_STOP = 'script.stop'
|
||||
SCRIPT_STOP_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Script),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_SCRIPT_STOP, SCRIPT_STOP_ACTION_SCHEMA)
|
||||
def script_stop_action_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)
|
||||
type = ScriptStopAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
@@ -1,15 +1,22 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.automation import CONDITION_REGISTRY
|
||||
from esphomeyaml.components import mqtt
|
||||
from esphomeyaml.components.mqtt import setup_mqtt_component
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \
|
||||
CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, \
|
||||
CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_LAMBDA, \
|
||||
CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE,\
|
||||
CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \
|
||||
CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, esphomelib_ns, float_, \
|
||||
process_lambda, setup_mqtt_component, templatable, add_job
|
||||
CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_INTERNAL, \
|
||||
CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE, \
|
||||
CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SEND_FIRST_AT, \
|
||||
CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, \
|
||||
CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add, process_lambda, \
|
||||
templatable, get_variable
|
||||
from esphomeyaml.cpp_types import App, Component, Nameable, PollingComponent, Trigger, \
|
||||
esphomelib_ns, float_, optional
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
@@ -20,77 +27,96 @@ def validate_recursive_filter(value):
|
||||
return FILTERS_SCHEMA(value)
|
||||
|
||||
|
||||
def validate_send_first_at(value):
|
||||
send_first_at = value.get(CONF_SEND_FIRST_AT)
|
||||
send_every = value[CONF_SEND_EVERY]
|
||||
if send_first_at is not None and send_first_at > send_every:
|
||||
raise vol.Invalid("send_first_at must be smaller than or equal to send_every! {} <= {}"
|
||||
"".format(send_first_at, send_every))
|
||||
return value
|
||||
|
||||
|
||||
FILTER_KEYS = [CONF_OFFSET, CONF_MULTIPLY, CONF_FILTER_OUT, CONF_FILTER_NAN,
|
||||
CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_LAMBDA,
|
||||
CONF_THROTTLE, CONF_DELTA, CONF_UNIQUE, CONF_HEARTBEAT, CONF_DEBOUNCE, CONF_OR]
|
||||
|
||||
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
|
||||
vol.Optional(CONF_OFFSET): vol.Coerce(float),
|
||||
vol.Optional(CONF_MULTIPLY): vol.Coerce(float),
|
||||
vol.Optional(CONF_FILTER_OUT): vol.Coerce(float),
|
||||
FILTERS_SCHEMA = cv.ensure_list({
|
||||
vol.Optional(CONF_OFFSET): cv.float_,
|
||||
vol.Optional(CONF_MULTIPLY): cv.float_,
|
||||
vol.Optional(CONF_FILTER_OUT): cv.float_,
|
||||
vol.Optional(CONF_FILTER_NAN): None,
|
||||
vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
|
||||
vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
|
||||
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
|
||||
}),
|
||||
vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.All(vol.Schema({
|
||||
vol.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int,
|
||||
vol.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int,
|
||||
vol.Optional(CONF_SEND_FIRST_AT): cv.positive_not_null_int,
|
||||
}), validate_send_first_at),
|
||||
vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
|
||||
vol.Required(CONF_ALPHA): cv.positive_float,
|
||||
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
|
||||
vol.Optional(CONF_ALPHA, default=0.1): cv.positive_float,
|
||||
vol.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int,
|
||||
}),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_THROTTLE): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_DELTA): vol.Coerce(float),
|
||||
vol.Optional(CONF_DELTA): cv.float_,
|
||||
vol.Optional(CONF_UNIQUE): None,
|
||||
vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_DEBOUNCE): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_OR): validate_recursive_filter,
|
||||
}, cv.has_exactly_one_key(*FILTER_KEYS))])
|
||||
}, cv.has_exactly_one_key(*FILTER_KEYS))
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
# Base
|
||||
sensor_ns = esphomelib_ns.namespace('sensor')
|
||||
Sensor = sensor_ns.Sensor
|
||||
MQTTSensorComponent = sensor_ns.MQTTSensorComponent
|
||||
OffsetFilter = sensor_ns.OffsetFilter
|
||||
MultiplyFilter = sensor_ns.MultiplyFilter
|
||||
FilterOutValueFilter = sensor_ns.FilterOutValueFilter
|
||||
FilterOutNANFilter = sensor_ns.FilterOutNANFilter
|
||||
SlidingWindowMovingAverageFilter = sensor_ns.SlidingWindowMovingAverageFilter
|
||||
ExponentialMovingAverageFilter = sensor_ns.ExponentialMovingAverageFilter
|
||||
LambdaFilter = sensor_ns.LambdaFilter
|
||||
ThrottleFilter = sensor_ns.ThrottleFilter
|
||||
DeltaFilter = sensor_ns.DeltaFilter
|
||||
OrFilter = sensor_ns.OrFilter
|
||||
HeartbeatFilter = sensor_ns.HeartbeatFilter
|
||||
DebounceFilter = sensor_ns.DebounceFilter
|
||||
UniqueFilter = sensor_ns.UniqueFilter
|
||||
Sensor = sensor_ns.class_('Sensor', Nameable)
|
||||
SensorPtr = Sensor.operator('ptr')
|
||||
MQTTSensorComponent = sensor_ns.class_('MQTTSensorComponent', mqtt.MQTTComponent)
|
||||
|
||||
SensorValueTrigger = sensor_ns.SensorValueTrigger
|
||||
RawSensorValueTrigger = sensor_ns.RawSensorValueTrigger
|
||||
ValueRangeTrigger = sensor_ns.ValueRangeTrigger
|
||||
PollingSensorComponent = sensor_ns.class_('PollingSensorComponent', PollingComponent, Sensor)
|
||||
EmptySensor = sensor_ns.class_('EmptySensor', Sensor)
|
||||
EmptyPollingParentSensor = sensor_ns.class_('EmptyPollingParentSensor', EmptySensor)
|
||||
|
||||
# Triggers
|
||||
SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', Trigger.template(float_))
|
||||
SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', Trigger.template(float_))
|
||||
ValueRangeTrigger = sensor_ns.class_('ValueRangeTrigger', Trigger.template(float_), Component)
|
||||
|
||||
# Filters
|
||||
Filter = sensor_ns.class_('Filter')
|
||||
SlidingWindowMovingAverageFilter = sensor_ns.class_('SlidingWindowMovingAverageFilter', Filter)
|
||||
ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter)
|
||||
LambdaFilter = sensor_ns.class_('LambdaFilter', Filter)
|
||||
OffsetFilter = sensor_ns.class_('OffsetFilter', Filter)
|
||||
MultiplyFilter = sensor_ns.class_('MultiplyFilter', Filter)
|
||||
FilterOutValueFilter = sensor_ns.class_('FilterOutValueFilter', Filter)
|
||||
FilterOutNANFilter = sensor_ns.class_('FilterOutNANFilter', Filter)
|
||||
ThrottleFilter = sensor_ns.class_('ThrottleFilter', Filter)
|
||||
DebounceFilter = sensor_ns.class_('DebounceFilter', Filter, Component)
|
||||
HeartbeatFilter = sensor_ns.class_('HeartbeatFilter', Filter, Component)
|
||||
DeltaFilter = sensor_ns.class_('DeltaFilter', Filter)
|
||||
OrFilter = sensor_ns.class_('OrFilter', Filter)
|
||||
UniqueFilter = sensor_ns.class_('UniqueFilter', Filter)
|
||||
SensorInRangeCondition = sensor_ns.class_('SensorInRangeCondition', Filter)
|
||||
|
||||
SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSensorComponent),
|
||||
cv.GenerateID(): cv.declare_variable_id(Sensor),
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict,
|
||||
vol.Optional(CONF_ICON): cv.icon,
|
||||
vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int),
|
||||
vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period_milliseconds),
|
||||
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
|
||||
vol.Optional(CONF_ON_VALUE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorValueTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_RAW_VALUE): vol.All(cv.ensure_list, [automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(RawSensorValueTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_VALUE_RANGE): vol.All(cv.ensure_list, [vol.All(
|
||||
automation.AUTOMATION_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ValueRangeTrigger),
|
||||
vol.Optional(CONF_ABOVE): vol.Coerce(float),
|
||||
vol.Optional(CONF_BELOW): vol.Coerce(float),
|
||||
}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW))]),
|
||||
vol.Optional(CONF_ON_VALUE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorStateTrigger),
|
||||
}),
|
||||
vol.Optional(CONF_ON_RAW_VALUE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorRawStateTrigger),
|
||||
}),
|
||||
vol.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ValueRangeTrigger),
|
||||
vol.Optional(CONF_ABOVE): cv.float_,
|
||||
vol.Optional(CONF_BELOW): cv.float_,
|
||||
}, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)),
|
||||
})
|
||||
|
||||
SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_filter(config):
|
||||
if CONF_OFFSET in config:
|
||||
@@ -103,13 +129,15 @@ def setup_filter(config):
|
||||
yield FilterOutNANFilter.new()
|
||||
elif CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
|
||||
conf = config[CONF_SLIDING_WINDOW_MOVING_AVERAGE]
|
||||
yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
|
||||
yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY],
|
||||
conf.get(CONF_SEND_FIRST_AT))
|
||||
elif CONF_EXPONENTIAL_MOVING_AVERAGE in config:
|
||||
conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE]
|
||||
yield ExponentialMovingAverageFilter.new(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
|
||||
elif CONF_LAMBDA in config:
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')]):
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')],
|
||||
return_type=optional.template(float_)):
|
||||
yield None
|
||||
yield LambdaFilter.new(lambda_)
|
||||
elif CONF_THROTTLE in config:
|
||||
@@ -140,6 +168,8 @@ def setup_filters(config):
|
||||
|
||||
|
||||
def setup_sensor_core_(sensor_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(sensor_var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_UNIT_OF_MEASUREMENT in config:
|
||||
add(sensor_var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||
if CONF_ICON in config:
|
||||
@@ -153,26 +183,25 @@ def setup_sensor_core_(sensor_var, mqtt_var, config):
|
||||
add(sensor_var.set_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
rhs = sensor_var.make_value_trigger()
|
||||
rhs = sensor_var.make_state_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
for conf in config.get(CONF_ON_RAW_VALUE, []):
|
||||
rhs = sensor_var.make_raw_value_trigger()
|
||||
rhs = sensor_var.make_raw_state_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
rhs = sensor_var.make_value_range_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
add(App.register_component(trigger))
|
||||
if CONF_ABOVE in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_ABOVE], float_, float_):
|
||||
yield
|
||||
trigger.set_min(template_)
|
||||
add(trigger.set_min(template_))
|
||||
if CONF_BELOW in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_BELOW], float_, float_):
|
||||
yield
|
||||
trigger.set_max(template_)
|
||||
add(trigger.set_max(template_))
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
|
||||
if CONF_EXPIRE_AFTER in config:
|
||||
@@ -186,14 +215,53 @@ def setup_sensor_core_(sensor_var, mqtt_var, config):
|
||||
def setup_sensor(sensor_obj, mqtt_obj, config):
|
||||
sensor_var = Pvariable(config[CONF_ID], sensor_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
|
||||
CORE.add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
def register_sensor(var, config):
|
||||
sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
|
||||
rhs = App.register_sensor(sensor_var)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
|
||||
add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
|
||||
CORE.add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SENSOR'
|
||||
|
||||
|
||||
CONF_SENSOR_IN_RANGE = 'sensor.in_range'
|
||||
SENSOR_IN_RANGE_CONDITION_SCHEMA = vol.All({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Sensor),
|
||||
vol.Optional(CONF_ABOVE): cv.float_,
|
||||
vol.Optional(CONF_BELOW): cv.float_,
|
||||
}, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW))
|
||||
|
||||
|
||||
@CONDITION_REGISTRY.register(CONF_SENSOR_IN_RANGE, SENSOR_IN_RANGE_CONDITION_SCHEMA)
|
||||
def sensor_in_range_to_code(config, condition_id, arg_type, template_arg):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_sensor_in_range_condition(template_arg)
|
||||
type = SensorInRangeCondition.template(arg_type)
|
||||
cond = Pvariable(condition_id, rhs, type=type)
|
||||
|
||||
if CONF_ABOVE in config:
|
||||
add(cond.set_min(config[CONF_ABOVE]))
|
||||
if CONF_BELOW in config:
|
||||
add(cond.set_max(config[CONF_BELOW]))
|
||||
|
||||
yield cond
|
||||
|
||||
|
||||
def core_to_hass_config(data, config):
|
||||
ret = mqtt.build_hass_config(data, 'sensor', config, include_state=True, include_command=False)
|
||||
if ret is None:
|
||||
return None
|
||||
if CONF_UNIT_OF_MEASUREMENT in config:
|
||||
ret['unit_of_measurement'] = config[CONF_UNIT_OF_MEASUREMENT]
|
||||
if CONF_EXPIRE_AFTER in config:
|
||||
expire = config[CONF_EXPIRE_AFTER]
|
||||
if expire is not None:
|
||||
ret['expire_after'] = expire.total_seconds
|
||||
if CONF_ICON in config:
|
||||
ret['icon'] = config[CONF_ICON]
|
||||
return ret
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
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_ATTENUATION, CONF_MAKE_ID, CONF_NAME, CONF_PIN, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, global_ns, variable
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, global_ns
|
||||
|
||||
ATTENUATION_MODES = {
|
||||
'0db': global_ns.ADC_0db,
|
||||
@@ -22,14 +24,17 @@ def validate_adc_pin(value):
|
||||
return pins.analog_pin(value)
|
||||
|
||||
|
||||
MakeADCSensor = Application.MakeADCSensor
|
||||
MakeADCSensor = Application.struct('MakeADCSensor')
|
||||
ADCSensorComponent = sensor.sensor_ns.class_('ADCSensorComponent', sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ADCSensorComponent),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeADCSensor),
|
||||
vol.Required(CONF_PIN): validate_adc_pin,
|
||||
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES,
|
||||
lower=True)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -42,7 +47,8 @@ def to_code(config):
|
||||
adc = make.Padc
|
||||
if CONF_ATTENUATION in config:
|
||||
add(adc.set_attenuation(ATTENUATION_MODES[config[CONF_ATTENUATION]]))
|
||||
sensor.setup_sensor(make.Padc, make.Pmqtt, config)
|
||||
sensor.setup_sensor(adc, make.Pmqtt, config)
|
||||
setup_component(adc, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADC_SENSOR'
|
||||
@@ -52,3 +58,13 @@ def required_build_flags(config):
|
||||
if config[CONF_PIN] == 'VCC':
|
||||
return '-DUSE_ADC_SENSOR_VCC'
|
||||
return None
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
|
||||
def includes(config):
|
||||
if config[CONF_PIN] == 'VCC':
|
||||
return 'ADC_MODE(ADC_VCC);'
|
||||
return None
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.ads1115 import ADS1115Component
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import get_variable
|
||||
from esphomeyaml.cpp_generator import get_variable
|
||||
from esphomeyaml.py_compat import string_types
|
||||
|
||||
DEPENDENCIES = ['ads1115']
|
||||
|
||||
ADS1115Multiplexer = sensor.sensor_ns.enum('ADS1115Multiplexer')
|
||||
MUX = {
|
||||
'A0_A1': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N1,
|
||||
'A0_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N3,
|
||||
'A1_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_N3,
|
||||
'A2_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_N3,
|
||||
'A0_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_NG,
|
||||
'A1_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_NG,
|
||||
'A2_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_NG,
|
||||
'A3_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P3_NG,
|
||||
'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
|
||||
'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
|
||||
'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
|
||||
'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
|
||||
'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
|
||||
'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
|
||||
'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
|
||||
'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
|
||||
}
|
||||
|
||||
ADS1115Gain = sensor.sensor_ns.enum('ADS1115Gain')
|
||||
GAIN = {
|
||||
'6.144': sensor.sensor_ns.ADS1115_GAIN_6P144,
|
||||
'4.096': sensor.sensor_ns.ADS1115_GAIN_6P096,
|
||||
'2.048': sensor.sensor_ns.ADS1115_GAIN_2P048,
|
||||
'1.024': sensor.sensor_ns.ADS1115_GAIN_1P024,
|
||||
'0.512': sensor.sensor_ns.ADS1115_GAIN_0P512,
|
||||
'0.256': sensor.sensor_ns.ADS1115_GAIN_0P256,
|
||||
'6.144': ADS1115Gain.ADS1115_GAIN_6P144,
|
||||
'4.096': ADS1115Gain.ADS1115_GAIN_6P096,
|
||||
'2.048': ADS1115Gain.ADS1115_GAIN_2P048,
|
||||
'1.024': ADS1115Gain.ADS1115_GAIN_1P024,
|
||||
'0.512': ADS1115Gain.ADS1115_GAIN_0P512,
|
||||
'0.256': ADS1115Gain.ADS1115_GAIN_0P256,
|
||||
}
|
||||
|
||||
|
||||
def validate_gain(value):
|
||||
if isinstance(value, float):
|
||||
value = u'{:0.03f}'.format(value)
|
||||
elif not isinstance(value, (str, unicode)):
|
||||
elif not isinstance(value, string_types):
|
||||
raise vol.Invalid('invalid gain "{}"'.format(value))
|
||||
|
||||
return cv.one_of(*GAIN)(value)
|
||||
@@ -45,16 +48,18 @@ def validate_mux(value):
|
||||
return cv.one_of(*MUX)(value)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
ADS1115Sensor = sensor.sensor_ns.class_('ADS1115Sensor', sensor.EmptySensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ADS1115Sensor),
|
||||
vol.Required(CONF_MULTIPLEXER): validate_mux,
|
||||
vol.Required(CONF_GAIN): validate_gain,
|
||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_variable_id(ADS1115Component),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ADS1115_ID]):
|
||||
yield
|
||||
|
||||
@@ -65,3 +70,7 @@ def to_code(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
35
esphomeyaml/components/sensor/apds9960.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.apds9960 import APDS9960, CONF_APDS9960_ID
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_NAME, CONF_TYPE
|
||||
from esphomeyaml.cpp_generator import get_variable
|
||||
|
||||
DEPENDENCIES = ['apds9960']
|
||||
|
||||
TYPES = {
|
||||
'CLEAR': 'make_clear_channel',
|
||||
'RED': 'make_red_channel',
|
||||
'GREEN': 'make_green_channel',
|
||||
'BLUE': 'make_blue_channel',
|
||||
'PROXIMITY': 'make_proximity',
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(sensor.Sensor),
|
||||
vol.Required(CONF_TYPE): cv.one_of(*TYPES, 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, TYPES[config[CONF_TYPE]])
|
||||
rhs = func(config[CONF_NAME])
|
||||
sensor.register_sensor(rhs, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
@@ -1,27 +1,33 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
BH1750Resolution = sensor.sensor_ns.enum('BH1750Resolution')
|
||||
BH1750_RESOLUTIONS = {
|
||||
4.0: sensor.sensor_ns.BH1750_RESOLUTION_4P0_LX,
|
||||
1.0: sensor.sensor_ns.BH1750_RESOLUTION_1P0_LX,
|
||||
0.5: sensor.sensor_ns.BH1750_RESOLUTION_0P5_LX,
|
||||
4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
|
||||
1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX,
|
||||
0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX,
|
||||
}
|
||||
|
||||
MakeBH1750Sensor = Application.MakeBH1750Sensor
|
||||
MakeBH1750Sensor = Application.struct('MakeBH1750Sensor')
|
||||
BH1750Sensor = sensor.sensor_ns.class_('BH1750Sensor', sensor.PollingSensorComponent,
|
||||
i2c.I2CDevice)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BH1750Sensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBH1750Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x23): cv.i2c_address,
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, cv.one_of(*BH1750_RESOLUTIONS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -32,6 +38,11 @@ def to_code(config):
|
||||
if CONF_RESOLUTION in config:
|
||||
add(bh1750.set_resolution(BH1750_RESOLUTIONS[config[CONF_RESOLUTION]]))
|
||||
sensor.setup_sensor(bh1750, make_bh1750.Pmqtt, config)
|
||||
setup_component(bh1750, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BH1750'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
30
esphomeyaml/components/sensor/ble_rssi.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
|
||||
from esphomeyaml.cpp_generator import get_variable
|
||||
from esphomeyaml.cpp_types import esphomelib_ns
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
ESP32BLERSSISensor = esphomelib_ns.class_('ESP32BLERSSISensor', sensor.Sensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32BLERSSISensor),
|
||||
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
rhs = hub.make_rssi_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
sensor.register_sensor(rhs, config)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
@@ -4,42 +4,58 @@ import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_IIR_FILTER, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
from esphomeyaml.cpp_generator import variable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
BME280Oversampling = sensor.sensor_ns.enum('BME280Oversampling')
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': sensor.sensor_ns.BME280_OVERSAMPLING_NONE,
|
||||
'1X': sensor.sensor_ns.BME280_OVERSAMPLING_1X,
|
||||
'2X': sensor.sensor_ns.BME280_OVERSAMPLING_2X,
|
||||
'4X': sensor.sensor_ns.BME280_OVERSAMPLING_4X,
|
||||
'8X': sensor.sensor_ns.BME280_OVERSAMPLING_8X,
|
||||
'16X': sensor.sensor_ns.BME280_OVERSAMPLING_16X,
|
||||
'NONE': BME280Oversampling.BME280_OVERSAMPLING_NONE,
|
||||
'1X': BME280Oversampling.BME280_OVERSAMPLING_1X,
|
||||
'2X': BME280Oversampling.BME280_OVERSAMPLING_2X,
|
||||
'4X': BME280Oversampling.BME280_OVERSAMPLING_4X,
|
||||
'8X': BME280Oversampling.BME280_OVERSAMPLING_8X,
|
||||
'16X': BME280Oversampling.BME280_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
BME280IIRFilter = sensor.sensor_ns.enum('BME280IIRFilter')
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': sensor.sensor_ns.BME280_IIR_FILTER_OFF,
|
||||
'2X': sensor.sensor_ns.BME280_IIR_FILTER_2X,
|
||||
'4X': sensor.sensor_ns.BME280_IIR_FILTER_4X,
|
||||
'8X': sensor.sensor_ns.BME280_IIR_FILTER_8X,
|
||||
'16X': sensor.sensor_ns.BME280_IIR_FILTER_16X,
|
||||
'OFF': BME280IIRFilter.BME280_IIR_FILTER_OFF,
|
||||
'2X': BME280IIRFilter.BME280_IIR_FILTER_2X,
|
||||
'4X': BME280IIRFilter.BME280_IIR_FILTER_4X,
|
||||
'8X': BME280IIRFilter.BME280_IIR_FILTER_8X,
|
||||
'16X': BME280IIRFilter.BME280_IIR_FILTER_16X,
|
||||
}
|
||||
|
||||
BME280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
|
||||
vol.Optional(CONF_OVERSAMPLING): cv.one_of(*OVERSAMPLING_OPTIONS, upper=True),
|
||||
})
|
||||
|
||||
MakeBME280Sensor = Application.MakeBME280Sensor
|
||||
MakeBME280Sensor = Application.struct('MakeBME280Sensor')
|
||||
BME280TemperatureSensor = sensor.sensor_ns.class_('BME280TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BME280PressureSensor = sensor.sensor_ns.class_('BME280PressureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BME280HumiditySensor = sensor.sensor_ns.class_('BME280HumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME280Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address,
|
||||
vol.Required(CONF_TEMPERATURE): BME280_OVERSAMPLING_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_PRESSURE): BME280_OVERSAMPLING_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): BME280_OVERSAMPLING_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME280TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME280PressureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME280HumiditySensor),
|
||||
})),
|
||||
vol.Optional(CONF_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -69,6 +85,13 @@ def to_code(config):
|
||||
config[CONF_PRESSURE])
|
||||
sensor.setup_sensor(bme280.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
setup_component(bme280, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BME280'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_PRESSURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY])]
|
||||
|
||||
@@ -1,51 +1,76 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_GAS_RESISTANCE, CONF_HUMIDITY, CONF_IIR_FILTER, \
|
||||
CONF_MAKE_ID, CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
CONF_UPDATE_INTERVAL, CONF_HEATER, CONF_DURATION
|
||||
from esphomeyaml.cpp_generator import variable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
BME680Oversampling = sensor.sensor_ns.enum('BME680Oversampling')
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': sensor.sensor_ns.BME680_OVERSAMPLING_NONE,
|
||||
'1X': sensor.sensor_ns.BME680_OVERSAMPLING_1X,
|
||||
'2X': sensor.sensor_ns.BME680_OVERSAMPLING_2X,
|
||||
'4X': sensor.sensor_ns.BME680_OVERSAMPLING_4X,
|
||||
'8X': sensor.sensor_ns.BME680_OVERSAMPLING_8X,
|
||||
'16X': sensor.sensor_ns.BME680_OVERSAMPLING_16X,
|
||||
'NONE': BME680Oversampling.BME680_OVERSAMPLING_NONE,
|
||||
'1X': BME680Oversampling.BME680_OVERSAMPLING_1X,
|
||||
'2X': BME680Oversampling.BME680_OVERSAMPLING_2X,
|
||||
'4X': BME680Oversampling.BME680_OVERSAMPLING_4X,
|
||||
'8X': BME680Oversampling.BME680_OVERSAMPLING_8X,
|
||||
'16X': BME680Oversampling.BME680_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
BME680IIRFilter = sensor.sensor_ns.enum('BME680IIRFilter')
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': sensor.sensor_ns.BME680_IIR_FILTER_OFF,
|
||||
'1X': sensor.sensor_ns.BME680_IIR_FILTER_1X,
|
||||
'3X': sensor.sensor_ns.BME680_IIR_FILTER_3X,
|
||||
'7X': sensor.sensor_ns.BME680_IIR_FILTER_7X,
|
||||
'15X': sensor.sensor_ns.BME680_IIR_FILTER_15X,
|
||||
'31X': sensor.sensor_ns.BME680_IIR_FILTER_31X,
|
||||
'63X': sensor.sensor_ns.BME680_IIR_FILTER_63X,
|
||||
'127X': sensor.sensor_ns.BME680_IIR_FILTER_127X,
|
||||
'OFF': BME680IIRFilter.BME680_IIR_FILTER_OFF,
|
||||
'1X': BME680IIRFilter.BME680_IIR_FILTER_1X,
|
||||
'3X': BME680IIRFilter.BME680_IIR_FILTER_3X,
|
||||
'7X': BME680IIRFilter.BME680_IIR_FILTER_7X,
|
||||
'15X': BME680IIRFilter.BME680_IIR_FILTER_15X,
|
||||
'31X': BME680IIRFilter.BME680_IIR_FILTER_31X,
|
||||
'63X': BME680IIRFilter.BME680_IIR_FILTER_63X,
|
||||
'127X': BME680IIRFilter.BME680_IIR_FILTER_127X,
|
||||
}
|
||||
|
||||
BME680_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
|
||||
vol.Optional(CONF_OVERSAMPLING): cv.one_of(*OVERSAMPLING_OPTIONS, upper=True),
|
||||
})
|
||||
|
||||
MakeBME680Sensor = Application.MakeBME680Sensor
|
||||
MakeBME680Sensor = Application.struct('MakeBME680Sensor')
|
||||
BME680TemperatureSensor = sensor.sensor_ns.class_('BME680TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BME680PressureSensor = sensor.sensor_ns.class_('BME680PressureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BME680HumiditySensor = sensor.sensor_ns.class_('BME680HumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BME680GasResistanceSensor = sensor.sensor_ns.class_('BME680GasResistanceSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME680Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x76): cv.i2c_address,
|
||||
vol.Required(CONF_TEMPERATURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_PRESSURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): BME680_OVERSAMPLING_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_GAS_RESISTANCE): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
|
||||
# TODO: Heater
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME680TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME680PressureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME680HumiditySensor),
|
||||
})),
|
||||
vol.Required(CONF_GAS_RESISTANCE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME680GasResistanceSensor),
|
||||
})),
|
||||
vol.Optional(CONF_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True),
|
||||
vol.Optional(CONF_HEATER): vol.Any(None, vol.All(vol.Schema({
|
||||
vol.Optional(CONF_TEMPERATURE, default=320): vol.All(vol.Coerce(int), vol.Range(200, 400)),
|
||||
vol.Optional(CONF_DURATION, default='150ms'): vol.All(
|
||||
cv.positive_time_period_milliseconds, vol.Range(max=core.TimePeriod(milliseconds=4032)))
|
||||
}, cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION)))),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -69,6 +94,12 @@ def to_code(config):
|
||||
if CONF_IIR_FILTER in config:
|
||||
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
|
||||
add(bme680.set_iir_filter(constant))
|
||||
if CONF_HEATER in config:
|
||||
conf = config[CONF_HEATER]
|
||||
if not conf:
|
||||
add(bme680.set_heater(0, 0))
|
||||
else:
|
||||
add(bme680.set_heater(conf[CONF_TEMPERATURE], conf[CONF_DURATION]))
|
||||
|
||||
sensor.setup_sensor(bme680.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
@@ -78,6 +109,14 @@ def to_code(config):
|
||||
config[CONF_HUMIDITY])
|
||||
sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), make.Pmqtt_gas_resistance,
|
||||
config[CONF_GAS_RESISTANCE])
|
||||
setup_component(bme680, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BME680'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_PRESSURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY]),
|
||||
sensor.core_to_hass_config(data, config[CONF_GAS_RESISTANCE])]
|
||||
|
||||
@@ -4,19 +4,29 @@ import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, HexIntLiteral, add, variable
|
||||
from esphomeyaml.cpp_generator import variable, add, HexIntLiteral
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeBMP085Sensor = Application.MakeBMP085Sensor
|
||||
MakeBMP085Sensor = Application.struct('MakeBMP085Sensor')
|
||||
BMP085TemperatureSensor = sensor.sensor_ns.class_('BMP085TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BMP085PressureSensor = sensor.sensor_ns.class_('BMP085PressureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP085Sensor),
|
||||
vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_PRESSURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BMP085TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BMP085PressureSensor),
|
||||
})),
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -31,6 +41,12 @@ def to_code(config):
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), bmp.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
setup_component(bmp.Pbmp, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_PRESSURE])]
|
||||
|
||||
85
esphomeyaml/components/sensor/bmp280.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_IIR_FILTER, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
BMP280Oversampling = sensor.sensor_ns.enum('BMP280Oversampling')
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': BMP280Oversampling.BMP280_OVERSAMPLING_NONE,
|
||||
'1X': BMP280Oversampling.BMP280_OVERSAMPLING_1X,
|
||||
'2X': BMP280Oversampling.BMP280_OVERSAMPLING_2X,
|
||||
'4X': BMP280Oversampling.BMP280_OVERSAMPLING_4X,
|
||||
'8X': BMP280Oversampling.BMP280_OVERSAMPLING_8X,
|
||||
'16X': BMP280Oversampling.BMP280_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
BMP280IIRFilter = sensor.sensor_ns.enum('BMP280IIRFilter')
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': BMP280IIRFilter.BMP280_IIR_FILTER_OFF,
|
||||
'2X': BMP280IIRFilter.BMP280_IIR_FILTER_2X,
|
||||
'4X': BMP280IIRFilter.BMP280_IIR_FILTER_4X,
|
||||
'8X': BMP280IIRFilter.BMP280_IIR_FILTER_8X,
|
||||
'16X': BMP280IIRFilter.BMP280_IIR_FILTER_16X,
|
||||
}
|
||||
|
||||
BMP280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): cv.one_of(*OVERSAMPLING_OPTIONS, upper=True),
|
||||
})
|
||||
|
||||
MakeBMP280Sensor = Application.struct('MakeBMP280Sensor')
|
||||
BMP280TemperatureSensor = sensor.sensor_ns.class_('BMP280TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
BMP280PressureSensor = sensor.sensor_ns.class_('BMP280PressureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP280Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BMP280TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(BMP280PressureSensor),
|
||||
})),
|
||||
vol.Optional(CONF_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_bmp280_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_PRESSURE][CONF_NAME],
|
||||
config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
bmp280 = make.Pbmp280
|
||||
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
|
||||
add(bmp280.set_temperature_oversampling(constant))
|
||||
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
|
||||
add(bmp280.set_pressure_oversampling(constant))
|
||||
if CONF_IIR_FILTER in config:
|
||||
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
|
||||
add(bmp280.set_iir_filter(constant))
|
||||
|
||||
sensor.setup_sensor(bmp280.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bmp280.Pget_pressure_sensor(), make.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
setup_component(bmp280, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BMP280'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_PRESSURE])]
|
||||
67
esphomeyaml/components/sensor/cse7766.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor, uart
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CURRENT, CONF_ID, CONF_NAME, CONF_POWER, CONF_UART_ID, \
|
||||
CONF_UPDATE_INTERVAL, CONF_VOLTAGE
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, PollingComponent
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
CSE7766Component = sensor.sensor_ns.class_('CSE7766Component', PollingComponent, uart.UARTDevice)
|
||||
CSE7766VoltageSensor = sensor.sensor_ns.class_('CSE7766VoltageSensor',
|
||||
sensor.EmptySensor)
|
||||
CSE7766CurrentSensor = sensor.sensor_ns.class_('CSE7766CurrentSensor',
|
||||
sensor.EmptySensor)
|
||||
CSE7766PowerSensor = sensor.sensor_ns.class_('CSE7766PowerSensor',
|
||||
sensor.EmptySensor)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CSE7766Component),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
|
||||
vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CSE7766VoltageSensor),
|
||||
})),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CSE7766CurrentSensor),
|
||||
})),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CSE7766PowerSensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT,
|
||||
CONF_POWER))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for uart_ in get_variable(config[CONF_UART_ID]):
|
||||
yield
|
||||
|
||||
rhs = App.make_cse7766(uart_, config.get(CONF_UPDATE_INTERVAL))
|
||||
cse = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_VOLTAGE in config:
|
||||
conf = config[CONF_VOLTAGE]
|
||||
sensor.register_sensor(cse.make_voltage_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_CURRENT in config:
|
||||
conf = config[CONF_CURRENT]
|
||||
sensor.register_sensor(cse.make_current_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_POWER in config:
|
||||
conf = config[CONF_POWER]
|
||||
sensor.register_sensor(cse.make_power_sensor(conf[CONF_NAME]), conf)
|
||||
setup_component(cse, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_CSE7766'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_VOLTAGE, CONF_CURRENT, CONF_POWER):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
35
esphomeyaml/components/sensor/custom.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS
|
||||
from esphomeyaml.cpp_generator import process_lambda, variable
|
||||
from esphomeyaml.cpp_types import std_vector
|
||||
|
||||
CustomSensorConstructor = sensor.sensor_ns.class_('CustomSensorConstructor')
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CustomSensorConstructor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(sensor.Sensor),
|
||||
})),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=std_vector.template(sensor.SensorPtr)):
|
||||
yield
|
||||
|
||||
rhs = CustomSensorConstructor(template_)
|
||||
custom = variable(config[CONF_ID], rhs)
|
||||
for i, sens in enumerate(config[CONF_SENSORS]):
|
||||
sensor.register_sensor(custom.get_sensor(i), sens)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_CUSTOM_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, sens) for sens in config[CONF_SENSORS]]
|
||||
@@ -1,22 +1,25 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.dallas import DallasComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \
|
||||
CONF_RESOLUTION
|
||||
from esphomeyaml.helpers import HexIntLiteral, get_variable
|
||||
from esphomeyaml.cpp_generator import HexIntLiteral, get_variable
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
DallasTemperatureSensor = sensor.sensor_ns.class_('DallasTemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(DallasTemperatureSensor),
|
||||
vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int,
|
||||
vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int,
|
||||
cv.GenerateID(CONF_DALLAS_ID): cv.use_variable_id(DallasComponent),
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=9, max=12)),
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
|
||||
}), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_DALLAS_ID]):
|
||||
yield
|
||||
if CONF_ADDRESS in config:
|
||||
@@ -29,3 +32,7 @@ def to_code(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,48 +1,69 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, gpio_output_pin_expression, variable
|
||||
from esphomeyaml.pins import gpio_output_pin_schema
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, \
|
||||
CONF_PIN, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import Pvariable, add, variable
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, PollingComponent
|
||||
from esphomeyaml.pins import gpio_input_pullup_pin_schema
|
||||
|
||||
DHTModel = sensor.sensor_ns.enum('DHTModel')
|
||||
DHT_MODELS = {
|
||||
'AUTO_DETECT': sensor.sensor_ns.DHT_MODEL_AUTO_DETECT,
|
||||
'DHT11': sensor.sensor_ns.DHT_MODEL_DHT11,
|
||||
'DHT22': sensor.sensor_ns.DHT_MODEL_DHT22,
|
||||
'AM2302': sensor.sensor_ns.DHT_MODEL_AM2302,
|
||||
'RHT03': sensor.sensor_ns.DHT_MODEL_RHT03,
|
||||
'AUTO_DETECT': DHTModel.DHT_MODEL_AUTO_DETECT,
|
||||
'DHT11': DHTModel.DHT_MODEL_DHT11,
|
||||
'DHT22': DHTModel.DHT_MODEL_DHT22,
|
||||
'AM2302': DHTModel.DHT_MODEL_AM2302,
|
||||
'RHT03': DHTModel.DHT_MODEL_RHT03,
|
||||
}
|
||||
|
||||
MakeDHTSensor = Application.MakeDHTSensor
|
||||
MakeDHTSensor = Application.struct('MakeDHTSensor')
|
||||
DHTComponent = sensor.sensor_ns.class_('DHTComponent', PollingComponent)
|
||||
DHTTemperatureSensor = sensor.sensor_ns.class_('DHTTemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
DHTHumiditySensor = sensor.sensor_ns.class_('DHTHumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHTSensor),
|
||||
vol.Required(CONF_PIN): gpio_output_pin_schema,
|
||||
vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_MODEL): vol.All(vol.Upper, cv.one_of(*DHT_MODELS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
cv.GenerateID(): cv.declare_variable_id(DHTComponent),
|
||||
vol.Required(CONF_PIN): gpio_input_pullup_pin_schema,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(DHTTemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(DHTHumiditySensor),
|
||||
})),
|
||||
vol.Optional(CONF_MODEL): cv.one_of(*DHT_MODELS, upper=True),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
pin, config.get(CONF_UPDATE_INTERVAL))
|
||||
dht = variable(config[CONF_MAKE_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
dht = make.Pdht
|
||||
Pvariable(config[CONF_ID], dht)
|
||||
|
||||
if CONF_MODEL in config:
|
||||
constant = DHT_MODELS[config[CONF_MODEL]]
|
||||
add(dht.Pdht.set_dht_model(constant))
|
||||
add(dht.set_dht_model(constant))
|
||||
|
||||
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(),
|
||||
dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(),
|
||||
dht.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
sensor.setup_sensor(dht.Pget_temperature_sensor(),
|
||||
make.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pget_humidity_sensor(),
|
||||
make.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
setup_component(dht, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DHT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY])]
|
||||
|
||||
@@ -1,33 +1,53 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
from esphomeyaml.cpp_generator import Pvariable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, PollingComponent
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeDHT12Sensor = Application.MakeDHT12Sensor
|
||||
MakeDHT12Sensor = Application.struct('MakeDHT12Sensor')
|
||||
DHT12Component = sensor.sensor_ns.class_('DHT12Component', PollingComponent, i2c.I2CDevice)
|
||||
DHT12TemperatureSensor = sensor.sensor_ns.class_('DHT12TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
DHT12HumiditySensor = sensor.sensor_ns.class_('DHT12HumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHT12Sensor),
|
||||
vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
cv.GenerateID(): cv.declare_variable_id(DHT12Component),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(DHT12TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(DHT12HumiditySensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_dht12_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
dht = variable(config[CONF_MAKE_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
dht = make.Pdht12
|
||||
Pvariable(config[CONF_ID], dht)
|
||||
|
||||
sensor.setup_sensor(dht.Pdht12.Pget_temperature_sensor(), dht.Pmqtt_temperature,
|
||||
sensor.setup_sensor(dht.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht12.Pget_humidity_sensor(), dht.Pmqtt_humidity,
|
||||
sensor.setup_sensor(dht.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
setup_component(dht, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DHT12_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY])]
|
||||
|
||||
36
esphomeyaml/components/sensor/duty_cycle.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
MakeDutyCycleSensor = Application.struct('MakeDutyCycleSensor')
|
||||
DutyCycleSensor = sensor.sensor_ns.class_('DutyCycleSensor', sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(DutyCycleSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDutyCycleSensor),
|
||||
vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for pin in gpio_input_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_duty_cycle_sensor(config[CONF_NAME], pin,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Pduty, make.Pmqtt, config)
|
||||
setup_component(make.Pduty, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DUTY_CYCLE_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
@@ -3,22 +3,31 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
MakeESP32HallSensor = Application.MakeESP32HallSensor
|
||||
MakeESP32HallSensor = Application.struct('MakeESP32HallSensor')
|
||||
ESP32HallSensor = sensor.sensor_ns.class_('ESP32HallSensor', sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32HallSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeESP32HallSensor),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_esp32_hall_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Phall, make.Pmqtt, config)
|
||||
setup_component(make.Phall, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_HALL_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,34 +1,54 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components import sensor, i2c
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
CONF_UPDATE_INTERVAL, CONF_ID
|
||||
from esphomeyaml.cpp_generator import variable, Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, PollingComponent, App
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeHDC1080Sensor = Application.MakeHDC1080Sensor
|
||||
MakeHDC1080Sensor = Application.struct('MakeHDC1080Sensor')
|
||||
HDC1080Component = sensor.sensor_ns.class_('HDC1080Component', PollingComponent, i2c.I2CDevice)
|
||||
HDC1080TemperatureSensor = sensor.sensor_ns.class_('HDC1080TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
HDC1080HumiditySensor = sensor.sensor_ns.class_('HDC1080HumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHDC1080Sensor),
|
||||
vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
cv.GenerateID(): cv.declare_variable_id(HDC1080Component),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HDC1080TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HDC1080HumiditySensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
hdc1080 = variable(config[CONF_MAKE_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
hdc1080 = make.Phdc1080
|
||||
Pvariable(config[CONF_ID], hdc1080)
|
||||
|
||||
sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(),
|
||||
hdc1080.Pmqtt_temperature,
|
||||
sensor.setup_sensor(hdc1080.Pget_temperature_sensor(),
|
||||
make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), hdc1080.Pmqtt_humidity,
|
||||
sensor.setup_sensor(hdc1080.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
setup_component(hdc1080, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY])]
|
||||
|
||||
76
esphomeyaml/components/sensor/hlw8012.py
Normal file
@@ -0,0 +1,76 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CF1_PIN, CONF_CF_PIN, CONF_CHANGE_MODE_EVERY, CONF_CURRENT, \
|
||||
CONF_CURRENT_RESISTOR, CONF_ID, CONF_NAME, CONF_POWER, CONF_SEL_PIN, CONF_UPDATE_INTERVAL, \
|
||||
CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import PollingComponent, App
|
||||
|
||||
HLW8012Component = sensor.sensor_ns.class_('HLW8012Component', PollingComponent)
|
||||
HLW8012VoltageSensor = sensor.sensor_ns.class_('HLW8012VoltageSensor', sensor.EmptySensor)
|
||||
HLW8012CurrentSensor = sensor.sensor_ns.class_('HLW8012CurrentSensor', sensor.EmptySensor)
|
||||
HLW8012PowerSensor = sensor.sensor_ns.class_('HLW8012PowerSensor', sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HLW8012Component),
|
||||
vol.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_CF_PIN): pins.input_pin,
|
||||
vol.Required(CONF_CF1_PIN): pins.input_pin,
|
||||
|
||||
vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HLW8012VoltageSensor),
|
||||
})),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HLW8012CurrentSensor),
|
||||
})),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HLW8012PowerSensor),
|
||||
})),
|
||||
|
||||
vol.Optional(CONF_CURRENT_RESISTOR): cv.resistance,
|
||||
vol.Optional(CONF_VOLTAGE_DIVIDER): cv.positive_float,
|
||||
vol.Optional(CONF_CHANGE_MODE_EVERY): vol.All(cv.uint32_t, vol.Range(min=1)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT,
|
||||
CONF_POWER))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for sel in gpio_output_pin_expression(config[CONF_SEL_PIN]):
|
||||
yield
|
||||
|
||||
rhs = App.make_hlw8012(sel, config[CONF_CF_PIN], config[CONF_CF1_PIN],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
hlw = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_VOLTAGE in config:
|
||||
conf = config[CONF_VOLTAGE]
|
||||
sensor.register_sensor(hlw.make_voltage_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_CURRENT in config:
|
||||
conf = config[CONF_CURRENT]
|
||||
sensor.register_sensor(hlw.make_current_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_POWER in config:
|
||||
conf = config[CONF_POWER]
|
||||
sensor.register_sensor(hlw.make_power_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_CURRENT_RESISTOR in config:
|
||||
add(hlw.set_current_resistor(config[CONF_CURRENT_RESISTOR]))
|
||||
if CONF_VOLTAGE_DIVIDER in config:
|
||||
add(hlw.set_voltage_divider(config[CONF_VOLTAGE_DIVIDER]))
|
||||
if CONF_CHANGE_MODE_EVERY in config:
|
||||
add(hlw.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY]))
|
||||
setup_component(hlw, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HLW8012'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_VOLTAGE, CONF_CURRENT, CONF_POWER):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
98
esphomeyaml/components/sensor/hmc5883l.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor, i2c
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_RANGE
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import PollingComponent, App
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
CONF_FIELD_STRENGTH_X = 'field_strength_x'
|
||||
CONF_FIELD_STRENGTH_Y = 'field_strength_y'
|
||||
CONF_FIELD_STRENGTH_Z = 'field_strength_z'
|
||||
CONF_HEADING = 'heading'
|
||||
|
||||
HMC5883LComponent = sensor.sensor_ns.class_('HMC5883LComponent', PollingComponent, i2c.I2CDevice)
|
||||
HMC5883LFieldStrengthSensor = sensor.sensor_ns.class_('HMC5883LFieldStrengthSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
HMC5883LHeadingSensor = sensor.sensor_ns.class_('HMC5883LHeadingSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
|
||||
HMC5883LRange = sensor.sensor_ns.enum('HMC5883LRange')
|
||||
HMC5883L_RANGES = {
|
||||
88: HMC5883LRange.HMC5883L_RANGE_88_UT,
|
||||
130: HMC5883LRange.HMC5883L_RANGE_130_UT,
|
||||
190: HMC5883LRange.HMC5883L_RANGE_190_UT,
|
||||
250: HMC5883LRange.HMC5883L_RANGE_250_UT,
|
||||
400: HMC5883LRange.HMC5883L_RANGE_400_UT,
|
||||
470: HMC5883LRange.HMC5883L_RANGE_470_UT,
|
||||
560: HMC5883LRange.HMC5883L_RANGE_560_UT,
|
||||
810: HMC5883LRange.HMC5883L_RANGE_810_UT,
|
||||
}
|
||||
|
||||
|
||||
def validate_range(value):
|
||||
value = cv.string(value)
|
||||
if value.endswith(u'µT') or value.endswith('uT'):
|
||||
value = value[:-2]
|
||||
return cv.one_of(*HMC5883L_RANGES, int=True)(value)
|
||||
|
||||
|
||||
SENSOR_KEYS = [CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z,
|
||||
CONF_HEADING]
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HMC5883LComponent),
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
vol.Optional(CONF_FIELD_STRENGTH_X): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HMC5883LFieldStrengthSensor),
|
||||
})),
|
||||
vol.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HMC5883LFieldStrengthSensor),
|
||||
})),
|
||||
vol.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HMC5883LFieldStrengthSensor),
|
||||
})),
|
||||
vol.Optional(CONF_HEADING): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HMC5883LHeadingSensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
vol.Optional(CONF_RANGE): validate_range,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_hmc5883l(config.get(CONF_UPDATE_INTERVAL))
|
||||
hmc = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_ADDRESS in config:
|
||||
add(hmc.set_address(config[CONF_ADDRESS]))
|
||||
if CONF_RANGE in config:
|
||||
add(hmc.set_range(HMC5883L_RANGES[config[CONF_RANGE]]))
|
||||
if CONF_FIELD_STRENGTH_X in config:
|
||||
conf = config[CONF_FIELD_STRENGTH_X]
|
||||
sensor.register_sensor(hmc.Pmake_x_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_FIELD_STRENGTH_Y in config:
|
||||
conf = config[CONF_FIELD_STRENGTH_Y]
|
||||
sensor.register_sensor(hmc.Pmake_y_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_FIELD_STRENGTH_Z in config:
|
||||
conf = config[CONF_FIELD_STRENGTH_Z]
|
||||
sensor.register_sensor(hmc.Pmake_z_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_HEADING in config:
|
||||
conf = config[CONF_HEADING]
|
||||
sensor.register_sensor(hmc.Pmake_heading_sensor(conf[CONF_NAME]), conf)
|
||||
setup_component(hmc, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HMC5883L'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z, CONF_HEADING):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
32
esphomeyaml/components/sensor/homeassistant.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ENTITY_ID, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
DEPENDENCIES = ['api']
|
||||
|
||||
MakeHomeassistantSensor = Application.struct('MakeHomeassistantSensor')
|
||||
HomeassistantSensor = sensor.sensor_ns.class_('HomeassistantSensor', sensor.Sensor)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HomeassistantSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHomeassistantSensor),
|
||||
vol.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_homeassistant_sensor(config[CONF_NAME], config[CONF_ENTITY_ID])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
subs = make.Psensor
|
||||
sensor.setup_sensor(subs, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HOMEASSISTANT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
@@ -1,32 +1,53 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
CONF_UPDATE_INTERVAL, CONF_ID
|
||||
from esphomeyaml.cpp_generator import variable, Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, PollingComponent, App
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeHTU21DSensor = Application.MakeHTU21DSensor
|
||||
MakeHTU21DSensor = Application.struct('MakeHTU21DSensor')
|
||||
HTU21DComponent = sensor.sensor_ns.class_('HTU21DComponent', PollingComponent, i2c.I2CDevice)
|
||||
HTU21DTemperatureSensor = sensor.sensor_ns.class_('HTU21DTemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
HTU21DHumiditySensor = sensor.sensor_ns.class_('HTU21DHumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HTU21DComponent),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHTU21DSensor),
|
||||
vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HTU21DTemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HTU21DHumiditySensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
htu21d = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), htu21d.Pmqtt_temperature,
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
htu21d = make.Phtu21d
|
||||
Pvariable(config[CONF_ID], htu21d)
|
||||
|
||||
sensor.setup_sensor(htu21d.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), htu21d.Pmqtt_humidity,
|
||||
sensor.setup_sensor(htu21d.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
setup_component(htu21d, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY])]
|
||||
|
||||
55
esphomeyaml/components/sensor/hx711.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CLK_PIN, CONF_GAIN, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
MakeHX711Sensor = Application.struct('MakeHX711Sensor')
|
||||
HX711Sensor = sensor.sensor_ns.class_('HX711Sensor', sensor.PollingSensorComponent)
|
||||
|
||||
CONF_DOUT_PIN = 'dout_pin'
|
||||
|
||||
HX711Gain = sensor.sensor_ns.enum('HX711Gain')
|
||||
GAINS = {
|
||||
128: HX711Gain.HX711_GAIN_128,
|
||||
32: HX711Gain.HX711_GAIN_32,
|
||||
64: HX711Gain.HX711_GAIN_64,
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HX711Sensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHX711Sensor),
|
||||
vol.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema,
|
||||
vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_GAIN): cv.one_of(*GAINS, int=True),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for dout_pin in gpio_input_pin_expression(config[CONF_DOUT_PIN]):
|
||||
yield
|
||||
for sck_pin in gpio_input_pin_expression(config[CONF_CLK_PIN]):
|
||||
yield
|
||||
|
||||
rhs = App.make_hx711_sensor(config[CONF_NAME], dout_pin, sck_pin,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
hx711 = make.Phx711
|
||||
|
||||
if CONF_GAIN in config:
|
||||
add(hx711.set_gain(GAINS[config[CONF_GAIN]]))
|
||||
|
||||
sensor.setup_sensor(hx711, make.Pmqtt, config)
|
||||
setup_component(hx711, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HX711'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
76
esphomeyaml/components/sensor/ina219.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \
|
||||
CONF_MAX_CURRENT, CONF_MAX_VOLTAGE, CONF_NAME, CONF_POWER, CONF_SHUNT_RESISTANCE, \
|
||||
CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, PollingComponent
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
INA219Component = sensor.sensor_ns.class_('INA219Component', PollingComponent, i2c.I2CDevice)
|
||||
INA219VoltageSensor = sensor.sensor_ns.class_('INA219VoltageSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
INA219CurrentSensor = sensor.sensor_ns.class_('INA219CurrentSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
INA219PowerSensor = sensor.sensor_ns.class_('INA219PowerSensor', sensor.EmptyPollingParentSensor)
|
||||
|
||||
SENSOR_KEYS = [CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT,
|
||||
CONF_POWER]
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA219Component),
|
||||
vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address,
|
||||
vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA219VoltageSensor),
|
||||
})),
|
||||
vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA219VoltageSensor),
|
||||
})),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA219CurrentSensor),
|
||||
})),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA219PowerSensor),
|
||||
})),
|
||||
vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance,
|
||||
vol.Range(min=0.0, max=32.0)),
|
||||
vol.Optional(CONF_MAX_VOLTAGE, default=32.0): vol.All(cv.voltage, vol.Range(min=0.0, max=32.0)),
|
||||
vol.Optional(CONF_MAX_CURRENT, default=3.2): vol.All(cv.current, vol.Range(min=0.0)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_ina219(config[CONF_SHUNT_RESISTANCE],
|
||||
config[CONF_MAX_CURRENT], config[CONF_MAX_VOLTAGE],
|
||||
config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
|
||||
ina = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_BUS_VOLTAGE in config:
|
||||
conf = config[CONF_BUS_VOLTAGE]
|
||||
sensor.register_sensor(ina.Pmake_bus_voltage_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_SHUNT_VOLTAGE in config:
|
||||
conf = config[CONF_SHUNT_VOLTAGE]
|
||||
sensor.register_sensor(ina.Pmake_shunt_voltage_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_CURRENT in config:
|
||||
conf = config[CONF_CURRENT]
|
||||
sensor.register_sensor(ina.Pmake_current_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_POWER in config:
|
||||
conf = config[CONF_POWER]
|
||||
sensor.register_sensor(ina.Pmake_power_sensor(conf[CONF_NAME]), conf)
|
||||
setup_component(ina, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_INA219'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
91
esphomeyaml/components/sensor/ina3221.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_NAME, \
|
||||
CONF_POWER, CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, 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']
|
||||
|
||||
CONF_CHANNEL_1 = 'channel_1'
|
||||
CONF_CHANNEL_2 = 'channel_2'
|
||||
CONF_CHANNEL_3 = 'channel_3'
|
||||
|
||||
INA3221Component = sensor.sensor_ns.class_('INA3221Component', PollingComponent, i2c.I2CDevice)
|
||||
INA3221VoltageSensor = sensor.sensor_ns.class_('INA3221VoltageSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
INA3221CurrentSensor = sensor.sensor_ns.class_('INA3221CurrentSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
INA3221PowerSensor = sensor.sensor_ns.class_('INA3221PowerSensor', sensor.EmptyPollingParentSensor)
|
||||
|
||||
SENSOR_KEYS = [CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER]
|
||||
|
||||
INA3221_CHANNEL_SCHEMA = vol.All(vol.Schema({
|
||||
vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA3221VoltageSensor),
|
||||
})),
|
||||
vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA3221VoltageSensor),
|
||||
})),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA3221CurrentSensor),
|
||||
})),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA3221PowerSensor),
|
||||
})),
|
||||
vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance,
|
||||
vol.Range(min=0.0, max=32.0)),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS))
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(INA3221Component),
|
||||
vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address,
|
||||
vol.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA,
|
||||
vol.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA,
|
||||
vol.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_ina3221(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
|
||||
ina = Pvariable(config[CONF_ID], rhs)
|
||||
for i, channel in enumerate([CONF_CHANNEL_1, CONF_CHANNEL_2, CONF_CHANNEL_3]):
|
||||
if channel not in config:
|
||||
continue
|
||||
conf = config[channel]
|
||||
if CONF_SHUNT_RESISTANCE in conf:
|
||||
add(ina.set_shunt_resistance(i, conf[CONF_SHUNT_RESISTANCE]))
|
||||
if CONF_BUS_VOLTAGE in conf:
|
||||
c = conf[CONF_BUS_VOLTAGE]
|
||||
sensor.register_sensor(ina.Pmake_bus_voltage_sensor(i, c[CONF_NAME]), c)
|
||||
if CONF_SHUNT_VOLTAGE in conf:
|
||||
c = conf[CONF_SHUNT_VOLTAGE]
|
||||
sensor.register_sensor(ina.Pmake_shunt_voltage_sensor(i, c[CONF_NAME]), c)
|
||||
if CONF_CURRENT in conf:
|
||||
c = conf[CONF_CURRENT]
|
||||
sensor.register_sensor(ina.Pmake_current_sensor(i, c[CONF_NAME]), c)
|
||||
if CONF_POWER in conf:
|
||||
c = conf[CONF_POWER]
|
||||
sensor.register_sensor(ina.Pmake_power_sensor(i, c[CONF_NAME]), c)
|
||||
|
||||
setup_component(ina, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_INA3221'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for channel in (CONF_CHANNEL_1, CONF_CHANNEL_2, CONF_CHANNEL_3):
|
||||
if channel not in config:
|
||||
continue
|
||||
conf = config[channel]
|
||||
for key in (CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER):
|
||||
if key in conf:
|
||||
ret.append(sensor.core_to_hass_config(data, conf[key]))
|
||||
return ret
|
||||
43
esphomeyaml/components/sensor/max31855.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor, spi
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
from esphomeyaml.const import CONF_CS_PIN, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
MakeMAX31855Sensor = Application.struct('MakeMAX31855Sensor')
|
||||
MAX31855Sensor = sensor.sensor_ns.class_('MAX31855Sensor', sensor.PollingSensorComponent,
|
||||
spi.SPIDevice)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MAX31855Sensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX31855Sensor),
|
||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for spi_ in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
rhs = App.make_max31855_sensor(config[CONF_NAME], spi_, cs,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
max31855 = make.Pmax31855
|
||||
sensor.setup_sensor(max31855, make.Pmqtt, config)
|
||||
setup_component(max31855, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MAX31855_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
@@ -1,38 +1,43 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN_CLOCK, CONF_PIN_CS, CONF_PIN_MISO, \
|
||||
from esphomeyaml.components import sensor, spi
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CS_PIN, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, \
|
||||
gpio_output_pin_expression, variable
|
||||
from esphomeyaml.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
MakeMAX6675Sensor = Application.MakeMAX6675Sensor
|
||||
MakeMAX6675Sensor = Application.struct('MakeMAX6675Sensor')
|
||||
MAX6675Sensor = sensor.sensor_ns.class_('MAX6675Sensor', sensor.PollingSensorComponent,
|
||||
spi.SPIDevice)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MAX6675Sensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor),
|
||||
vol.Required(CONF_PIN_CS): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_PIN_CLOCK): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_PIN_MISO): pins.gpio_input_pin_schema,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin_cs = None
|
||||
for pin_cs in gpio_output_pin_expression(config[CONF_PIN_CS]):
|
||||
for spi_ in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
pin_clock = None
|
||||
for pin_clock in gpio_output_pin_expression(config[CONF_PIN_CLOCK]):
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
pin_miso = None
|
||||
for pin_miso in gpio_input_pin_expression(config[CONF_PIN_MISO]):
|
||||
yield
|
||||
rhs = App.make_max6675_sensor(config[CONF_NAME], pin_cs, pin_clock, pin_miso,
|
||||
rhs = App.make_max6675_sensor(config[CONF_NAME], spi_, cs,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Pmax6675, make.Pmqtt, config)
|
||||
max6675 = make.Pmax6675
|
||||
sensor.setup_sensor(max6675, make.Pmqtt, config)
|
||||
setup_component(max6675, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MAX6675_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
59
esphomeyaml/components/sensor/mhz19.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor, uart
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CO2, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, CONF_UART_ID, \
|
||||
CONF_UPDATE_INTERVAL, CONF_ID
|
||||
from esphomeyaml.cpp_generator import get_variable, variable, Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, PollingComponent, App
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
MakeMHZ19Sensor = Application.struct('MakeMHZ19Sensor')
|
||||
MHZ19Component = sensor.sensor_ns.class_('MHZ19Component', PollingComponent, uart.UARTDevice)
|
||||
MHZ19TemperatureSensor = sensor.sensor_ns.class_('MHZ19TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
MHZ19CO2Sensor = sensor.sensor_ns.class_('MHZ19CO2Sensor', sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MHZ19Component),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMHZ19Sensor),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
vol.Required(CONF_CO2): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MHZ19CO2Sensor),
|
||||
})),
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MHZ19TemperatureSensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for uart_ in get_variable(config[CONF_UART_ID]):
|
||||
yield
|
||||
rhs = App.make_mhz19_sensor(uart_, config[CONF_CO2][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
mhz19 = make.Pmhz19
|
||||
Pvariable(config[CONF_ID], mhz19)
|
||||
sensor.setup_sensor(mhz19.Pget_co2_sensor(), make.Pmqtt, config[CONF_CO2])
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sensor.register_sensor(mhz19.Pmake_temperature_sensor(config[CONF_TEMPERATURE][CONF_NAME]),
|
||||
config[CONF_TEMPERATURE])
|
||||
|
||||
setup_component(mhz19, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MHZ19'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_CO2, CONF_TEMPERATURE):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
@@ -1,10 +1,12 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, PollingComponent
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
@@ -15,24 +17,41 @@ CONF_GYRO_X = 'gyro_x'
|
||||
CONF_GYRO_Y = 'gyro_y'
|
||||
CONF_GYRO_Z = 'gyro_z'
|
||||
|
||||
MPU6050Component = sensor.sensor_ns.MPU6050Component
|
||||
MPU6050AccelSensor = sensor.sensor_ns.MPU6050AccelSensor
|
||||
MPU6050GyroSensor = sensor.sensor_ns.MPU6050GyroSensor
|
||||
MPU6050TemperatureSensor = sensor.sensor_ns.MPU6050TemperatureSensor
|
||||
MPU6050Component = sensor.sensor_ns.class_('MPU6050Component', PollingComponent, i2c.I2CDevice)
|
||||
MPU6050AccelSensor = sensor.sensor_ns.class_('MPU6050AccelSensor', sensor.EmptyPollingParentSensor)
|
||||
MPU6050GyroSensor = sensor.sensor_ns.class_('MPU6050GyroSensor', sensor.EmptyPollingParentSensor)
|
||||
MPU6050TemperatureSensor = sensor.sensor_ns.class_('MPU6050TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
SENSOR_KEYS = [CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
|
||||
CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z]
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050Component),
|
||||
vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address,
|
||||
vol.Optional(CONF_ACCEL_X): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_ACCEL_Y): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_ACCEL_Z): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_GYRO_X): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_GYRO_Y): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_GYRO_Z): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}), cv.has_at_least_one_key(CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
|
||||
CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z))
|
||||
vol.Optional(CONF_ACCEL_X): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050AccelSensor),
|
||||
})),
|
||||
vol.Optional(CONF_ACCEL_Y): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050AccelSensor),
|
||||
})),
|
||||
vol.Optional(CONF_ACCEL_Z): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050AccelSensor),
|
||||
})),
|
||||
vol.Optional(CONF_GYRO_X): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050GyroSensor),
|
||||
})),
|
||||
vol.Optional(CONF_GYRO_Y): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050GyroSensor),
|
||||
})),
|
||||
vol.Optional(CONF_GYRO_Z): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050GyroSensor),
|
||||
})),
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050TemperatureSensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -67,5 +86,16 @@ def to_code(config):
|
||||
rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME])
|
||||
sensor.register_sensor(rhs, conf)
|
||||
|
||||
setup_component(mpu, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MPU6050'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z, CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z,
|
||||
CONF_TEMPERATURE):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
|
||||
39
esphomeyaml/components/sensor/mqtt_subscribe.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_QOS, CONF_TOPIC
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
MakeMQTTSubscribeSensor = Application.struct('MakeMQTTSubscribeSensor')
|
||||
MQTTSubscribeSensor = sensor.sensor_ns.class_('MQTTSubscribeSensor', sensor.Sensor, Component)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MQTTSubscribeSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMQTTSubscribeSensor),
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_QOS): cv.mqtt_qos,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_mqtt_subscribe_sensor(config[CONF_NAME], config[CONF_TOPIC])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
subs = make.Psensor
|
||||
|
||||
if CONF_QOS in config:
|
||||
add(subs.set_qos(config[CONF_QOS]))
|
||||
|
||||
sensor.setup_sensor(subs, make.Pmqtt, config)
|
||||
setup_component(subs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
57
esphomeyaml/components/sensor/ms5611.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import Pvariable, add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, PollingComponent
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeMS5611Sensor = Application.struct('MakeMS5611Sensor')
|
||||
MS5611Component = sensor.sensor_ns.class_('MS5611Component', PollingComponent, i2c.I2CDevice)
|
||||
MS5611TemperatureSensor = sensor.sensor_ns.class_('MS5611TemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
MS5611PressureSensor = sensor.sensor_ns.class_('MS5611PressureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MS5611Component),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMS5611Sensor),
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MS5611TemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MS5611PressureSensor),
|
||||
})),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_ms5611_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_PRESSURE][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
ms5611 = make.Pms5611
|
||||
Pvariable(config[CONF_ID], ms5611)
|
||||
|
||||
if CONF_ADDRESS in config:
|
||||
add(ms5611.set_address(config[CONF_ADDRESS]))
|
||||
|
||||
sensor.setup_sensor(ms5611.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(ms5611.Pget_pressure_sensor(), make.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
setup_component(ms5611, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MS5611'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_PRESSURE])]
|
||||
106
esphomeyaml/components/sensor/pmsx003.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor, uart
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_FORMALDEHYDE, CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_PM_10_0, \
|
||||
CONF_PM_1_0, CONF_PM_2_5, CONF_TEMPERATURE, CONF_TYPE, CONF_UART_ID
|
||||
from esphomeyaml.cpp_generator import Pvariable, get_variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Component
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
PMSX003Component = sensor.sensor_ns.class_('PMSX003Component', uart.UARTDevice, Component)
|
||||
PMSX003Sensor = sensor.sensor_ns.class_('PMSX003Sensor', sensor.Sensor)
|
||||
|
||||
CONF_PMSX003 = 'PMSX003'
|
||||
CONF_PMS5003T = 'PMS5003T'
|
||||
CONF_PMS5003ST = 'PMS5003ST'
|
||||
|
||||
PMSX003Type = sensor.sensor_ns.enum('PMSX003Type')
|
||||
PMSX003_TYPES = {
|
||||
CONF_PMSX003: PMSX003Type.PMSX003_TYPE_X003,
|
||||
CONF_PMS5003T: PMSX003Type.PMSX003_TYPE_5003T,
|
||||
CONF_PMS5003ST: PMSX003Type.PMSX003_TYPE_5003ST,
|
||||
}
|
||||
|
||||
SENSORS_TO_TYPE = {
|
||||
CONF_PM_1_0: [CONF_PMSX003],
|
||||
CONF_PM_2_5: [CONF_PMSX003, CONF_PMS5003T, CONF_PMS5003ST],
|
||||
CONF_PM_10_0: [CONF_PMSX003],
|
||||
CONF_TEMPERATURE: [CONF_PMS5003T, CONF_PMS5003ST],
|
||||
CONF_HUMIDITY: [CONF_PMS5003T, CONF_PMS5003ST],
|
||||
CONF_FORMALDEHYDE: [CONF_PMS5003ST],
|
||||
}
|
||||
|
||||
|
||||
def validate_pmsx003_sensors(value):
|
||||
for key, types in SENSORS_TO_TYPE.items():
|
||||
if key in value and value[CONF_TYPE] not in types:
|
||||
raise vol.Invalid(u"{} does not have {} sensor!".format(value[CONF_TYPE], key))
|
||||
return value
|
||||
|
||||
|
||||
PMSX003_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(PMSX003Sensor),
|
||||
})
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(PMSX003Component),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
vol.Required(CONF_TYPE): cv.one_of(*PMSX003_TYPES, upper=True),
|
||||
|
||||
vol.Optional(CONF_PM_1_0): cv.nameable(PMSX003_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_PM_2_5): cv.nameable(PMSX003_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_PM_10_0): cv.nameable(PMSX003_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(PMSX003_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_HUMIDITY): cv.nameable(PMSX003_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_FORMALDEHYDE): cv.nameable(PMSX003_SENSOR_SCHEMA),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSORS_TO_TYPE))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for uart_ in get_variable(config[CONF_UART_ID]):
|
||||
yield
|
||||
|
||||
rhs = App.make_pmsx003(uart_, PMSX003_TYPES[config[CONF_TYPE]])
|
||||
pms = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_PM_1_0 in config:
|
||||
conf = config[CONF_PM_1_0]
|
||||
sensor.register_sensor(pms.make_pm_1_0_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
if CONF_PM_2_5 in config:
|
||||
conf = config[CONF_PM_2_5]
|
||||
sensor.register_sensor(pms.make_pm_2_5_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
if CONF_PM_10_0 in config:
|
||||
conf = config[CONF_PM_10_0]
|
||||
sensor.register_sensor(pms.make_pm_10_0_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sensor.register_sensor(pms.make_temperature_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
conf = config[CONF_HUMIDITY]
|
||||
sensor.register_sensor(pms.make_humidity_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
if CONF_FORMALDEHYDE in config:
|
||||
conf = config[CONF_FORMALDEHYDE]
|
||||
sensor.register_sensor(pms.make_formaldehyde_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
setup_component(pms, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PMSX003'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_PM_1_0, CONF_PM_2_5, CONF_PM_10_0, CONF_TEMPERATURE, CONF_HUMIDITY,
|
||||
CONF_FORMALDEHYDE):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
@@ -1,63 +1,78 @@
|
||||
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_COUNT_MODE, CONF_FALLING_EDGE, CONF_INTERNAL_FILTER, \
|
||||
CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
|
||||
ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import App, add, global_ns, variable, Application
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
GPIO_PULL_MODES = {
|
||||
'PULLUP': global_ns.GPIO_PULLUP_ONLY,
|
||||
'PULLDOWN': global_ns.GPIO_PULLDOWN_ONLY,
|
||||
'PULLUP_PULLDOWN': global_ns.GPIO_PULLUP_PULLDOWN,
|
||||
'FLOATING': global_ns.GPIO_FLOATING,
|
||||
}
|
||||
|
||||
GPIO_PULL_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*GPIO_PULL_MODES))
|
||||
CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
PulseCounterCountMode = sensor.sensor_ns.enum('PulseCounterCountMode')
|
||||
COUNT_MODES = {
|
||||
'DISABLE': global_ns.PCNT_COUNT_DIS,
|
||||
'INCREMENT': global_ns.PCNT_COUNT_INC,
|
||||
'DECREMENT': global_ns.PCNT_COUNT_DEC,
|
||||
'DISABLE': PulseCounterCountMode.PULSE_COUNTER_DISABLE,
|
||||
'INCREMENT': PulseCounterCountMode.PULSE_COUNTER_INCREMENT,
|
||||
'DECREMENT': PulseCounterCountMode.PULSE_COUNTER_DECREMENT,
|
||||
}
|
||||
|
||||
COUNT_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*COUNT_MODES))
|
||||
COUNT_MODE_SCHEMA = cv.one_of(*COUNT_MODES, upper=True)
|
||||
|
||||
MakePulseCounterSensor = Application.MakePulseCounterSensor
|
||||
PulseCounterBase = sensor.sensor_ns.class_('PulseCounterBase')
|
||||
MakePulseCounterSensor = Application.struct('MakePulseCounterSensor')
|
||||
PulseCounterSensorComponent = sensor.sensor_ns.class_('PulseCounterSensorComponent',
|
||||
sensor.PollingSensorComponent,
|
||||
PulseCounterBase)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
|
||||
def validate_internal_filter(value):
|
||||
if CORE.is_esp32:
|
||||
if isinstance(value, int):
|
||||
raise vol.Invalid("Please specify the internal filter in microseconds now "
|
||||
"(since 1.7.0). For example '17ms'")
|
||||
value = cv.positive_time_period_microseconds(value)
|
||||
if value.total_microseconds > 13:
|
||||
raise vol.Invalid("Maximum internal filter value for ESP32 is 13us")
|
||||
return value
|
||||
|
||||
return cv.positive_time_period_microseconds(value)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(PulseCounterSensorComponent),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakePulseCounterSensor),
|
||||
vol.Required(CONF_PIN): pins.input_pin,
|
||||
vol.Optional(CONF_PULL_MODE): GPIO_PULL_MODE_SCHEMA,
|
||||
vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema,
|
||||
vol.Optional(CONF_COUNT_MODE): vol.Schema({
|
||||
vol.Required(CONF_RISING_EDGE): COUNT_MODE_SCHEMA,
|
||||
vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA,
|
||||
}),
|
||||
vol.Optional(CONF_INTERNAL_FILTER): vol.All(vol.Coerce(int), vol.Range(min=0, max=1023)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_INTERNAL_FILTER): validate_internal_filter,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_pulse_counter_sensor(config[CONF_NAME], config[CONF_PIN],
|
||||
for pin in gpio_input_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_pulse_counter_sensor(config[CONF_NAME], pin,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
pcnt = make.Ppcnt
|
||||
if CONF_PULL_MODE in config:
|
||||
pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]]
|
||||
add(pcnt.set_pull_mode(pull_mode))
|
||||
|
||||
if CONF_COUNT_MODE in config:
|
||||
count_mode = config[CONF_COUNT_MODE]
|
||||
rising_edge = COUNT_MODES[count_mode[CONF_RISING_EDGE]]
|
||||
falling_edge = COUNT_MODES[count_mode[CONF_FALLING_EDGE]]
|
||||
rising_edge = COUNT_MODES[config[CONF_COUNT_MODE][CONF_RISING_EDGE]]
|
||||
falling_edge = COUNT_MODES[config[CONF_COUNT_MODE][CONF_FALLING_EDGE]]
|
||||
add(pcnt.set_edge_mode(rising_edge, falling_edge))
|
||||
if CONF_INTERNAL_FILTER in config:
|
||||
add(pcnt.set_filter(config[CONF_INTERNAL_FILTER]))
|
||||
sensor.setup_sensor(make.Ppcnt, make.Pmqtt, config)
|
||||
add(pcnt.set_filter_us(config[CONF_INTERNAL_FILTER]))
|
||||
|
||||
sensor.setup_sensor(pcnt, make.Pmqtt, config)
|
||||
setup_component(pcnt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,42 +1,46 @@
|
||||
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_MAKE_ID, CONF_NAME, CONF_RESOLUTION
|
||||
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
RotaryEncoderResolution = sensor.sensor_ns.enum('RotaryEncoderResolution')
|
||||
RESOLUTIONS = {
|
||||
'1': sensor.sensor_ns.ROTARY_ENCODER_1_PULSE_PER_CYCLE,
|
||||
'2': sensor.sensor_ns.ROTARY_ENCODER_2_PULSES_PER_CYCLE,
|
||||
'4': sensor.sensor_ns.ROTARY_ENCODER_4_PULSES_PER_CYCLE,
|
||||
'1': RotaryEncoderResolution.ROTARY_ENCODER_1_PULSE_PER_CYCLE,
|
||||
'2': RotaryEncoderResolution.ROTARY_ENCODER_2_PULSES_PER_CYCLE,
|
||||
'4': RotaryEncoderResolution.ROTARY_ENCODER_4_PULSES_PER_CYCLE,
|
||||
}
|
||||
|
||||
CONF_PIN_A = 'pin_a'
|
||||
CONF_PIN_B = 'pin_b'
|
||||
CONF_PIN_RESET = 'pin_reset'
|
||||
|
||||
MakeRotaryEncoderSensor = Application.MakeRotaryEncoderSensor
|
||||
MakeRotaryEncoderSensor = Application.struct('MakeRotaryEncoderSensor')
|
||||
RotaryEncoderSensor = sensor.sensor_ns.class_('RotaryEncoderSensor', sensor.Sensor, Component)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(RotaryEncoderSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRotaryEncoderSensor),
|
||||
vol.Required(CONF_PIN_A): pins.internal_gpio_input_pin_schema,
|
||||
vol.Required(CONF_PIN_B): pins.internal_gpio_input_pin_schema,
|
||||
vol.Optional(CONF_PIN_RESET): pins.internal_gpio_input_pin_schema,
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(cv.string, cv.one_of(*RESOLUTIONS)),
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_RESOLUTION): cv.one_of(*RESOLUTIONS, string=True),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin_a = None
|
||||
for pin_a in gpio_input_pin_expression(config[CONF_PIN_A]):
|
||||
yield
|
||||
pin_b = None
|
||||
for pin_b in gpio_input_pin_expression(config[CONF_PIN_B]):
|
||||
yield
|
||||
rhs = App.make_rotary_encoder_sensor(config[CONF_NAME], pin_a, pin_b)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
encoder = make.Protary_encoder
|
||||
|
||||
if CONF_PIN_RESET in config:
|
||||
pin_i = None
|
||||
for pin_i in gpio_input_pin_expression(config[CONF_PIN_RESET]):
|
||||
@@ -45,7 +49,13 @@ def to_code(config):
|
||||
if CONF_RESOLUTION in config:
|
||||
resolution = RESOLUTIONS[config[CONF_RESOLUTION]]
|
||||
add(encoder.set_resolution(resolution))
|
||||
|
||||
sensor.setup_sensor(encoder, make.Pmqtt, config)
|
||||
setup_component(encoder, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ACCURACY, CONF_ADDRESS, CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, \
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_MAKE_ID, CONF_NAME, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
from esphomeyaml.cpp_generator import Pvariable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, PollingComponent
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
SHT_ACCURACIES = {
|
||||
'LOW': sensor.sensor_ns.SHT3XD_ACCURACY_LOW,
|
||||
'MEDIUM': sensor.sensor_ns.SHT3XD_ACCURACY_MEDIUM,
|
||||
'HIGH': sensor.sensor_ns.SHT3XD_ACCURACY_HIGH,
|
||||
}
|
||||
|
||||
MakeSHT3XDSensor = Application.MakeSHT3XDSensor
|
||||
MakeSHT3XDSensor = Application.struct('MakeSHT3XDSensor')
|
||||
SHT3XDComponent = sensor.sensor_ns.class_('SHT3XDComponent', PollingComponent, i2c.I2CDevice)
|
||||
SHT3XDTemperatureSensor = sensor.sensor_ns.class_('SHT3XDTemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
SHT3XDHumiditySensor = sensor.sensor_ns.class_('SHT3XDHumiditySensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(SHT3XDComponent),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSHT3XDSensor),
|
||||
vol.Required(CONF_TEMPERATURE): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): sensor.SENSOR_SCHEMA,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(SHT3XDTemperatureSensor),
|
||||
})),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(SHT3XDHumiditySensor),
|
||||
})),
|
||||
vol.Optional(CONF_ADDRESS, default=0x44): cv.i2c_address,
|
||||
vol.Optional(CONF_ACCURACY): vol.All(vol.Upper, cv.one_of(*SHT_ACCURACIES)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -31,15 +36,20 @@ def to_code(config):
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
sht3xd = variable(config[CONF_MAKE_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sht3xd = make.Psht3xd
|
||||
Pvariable(config[CONF_ID], sht3xd)
|
||||
|
||||
if CONF_ACCURACY in config:
|
||||
add(sht3xd.Psht3xd.set_accuracy(SHT_ACCURACIES[config[CONF_ACCURACY]]))
|
||||
|
||||
sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), sht3xd.Pmqtt_temperature,
|
||||
sensor.setup_sensor(sht3xd.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(sht3xd.Psht3xd.Pget_humidity_sensor(), sht3xd.Pmqtt_humidity,
|
||||
sensor.setup_sensor(sht3xd.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
setup_component(sht3xd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SHT3XD'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [sensor.core_to_hass_config(data, config[CONF_TEMPERATURE]),
|
||||
sensor.core_to_hass_config(data, config[CONF_HUMIDITY])]
|
||||
|
||||
113
esphomeyaml/components/sensor/tcs34725.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, \
|
||||
CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, CONF_NAME, 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']
|
||||
|
||||
CONF_RED_CHANNEL = 'red_channel'
|
||||
CONF_GREEN_CHANNEL = 'green_channel'
|
||||
CONF_BLUE_CHANNEL = 'blue_channel'
|
||||
CONF_CLEAR_CHANNEL = 'clear_channel'
|
||||
|
||||
TCS34725Component = sensor.sensor_ns.class_('TCS34725Component', PollingComponent,
|
||||
i2c.I2CDevice)
|
||||
|
||||
TCS34725IntegrationTime = sensor.sensor_ns.enum('TCS34725IntegrationTime')
|
||||
TCS34725_INTEGRATION_TIMES = {
|
||||
'2.4ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_2_4MS,
|
||||
'24ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_24MS,
|
||||
'50ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_50MS,
|
||||
'101ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_101MS,
|
||||
'154ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_154MS,
|
||||
'700ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_700MS,
|
||||
}
|
||||
|
||||
TCS34725Gain = sensor.sensor_ns.enum('TCS34725Gain')
|
||||
TCS34725_GAINS = {
|
||||
'1X': TCS34725Gain.TCS34725_GAIN_1X,
|
||||
'4X': TCS34725Gain.TCS34725_GAIN_4X,
|
||||
'16X': TCS34725Gain.TCS34725_GAIN_16X,
|
||||
'60X': TCS34725Gain.TCS34725_GAIN_60X,
|
||||
}
|
||||
|
||||
TCS35725IlluminanceSensor = sensor.sensor_ns.class_('TCS35725IlluminanceSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
TCS35725ColorTemperatureSensor = sensor.sensor_ns.class_('TCS35725ColorTemperatureSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
TCS35725ColorChannelSensor = sensor.sensor_ns.class_('TCS35725ColorChannelSensor',
|
||||
sensor.EmptyPollingParentSensor)
|
||||
|
||||
COLOR_CHANNEL_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TCS35725ColorChannelSensor),
|
||||
})
|
||||
|
||||
SENSOR_KEYS = [CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL,
|
||||
CONF_CLEAR_CHANNEL, CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE]
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TCS34725Component),
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
vol.Optional(CONF_RED_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_GREEN_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_BLUE_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_CLEAR_CHANNEL): cv.nameable(COLOR_CHANNEL_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TCS35725IlluminanceSensor),
|
||||
})),
|
||||
vol.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TCS35725ColorTemperatureSensor),
|
||||
})),
|
||||
vol.Optional(CONF_INTEGRATION_TIME): cv.one_of(*TCS34725_INTEGRATION_TIMES, lower=True),
|
||||
vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*TCS34725_GAINS), upper=True),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(*SENSOR_KEYS))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_tcs34725(config.get(CONF_UPDATE_INTERVAL))
|
||||
tcs = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_ADDRESS in config:
|
||||
add(tcs.set_address(config[CONF_ADDRESS]))
|
||||
if CONF_INTEGRATION_TIME in config:
|
||||
add(tcs.set_integration_time(TCS34725_INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]))
|
||||
if CONF_GAIN in config:
|
||||
add(tcs.set_gain(TCS34725_GAINS[config[CONF_GAIN]]))
|
||||
if CONF_RED_CHANNEL in config:
|
||||
conf = config[CONF_RED_CHANNEL]
|
||||
sensor.register_sensor(tcs.Pmake_red_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_GREEN_CHANNEL in config:
|
||||
conf = config[CONF_GREEN_CHANNEL]
|
||||
sensor.register_sensor(tcs.Pmake_green_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_BLUE_CHANNEL in config:
|
||||
conf = config[CONF_BLUE_CHANNEL]
|
||||
sensor.register_sensor(tcs.Pmake_blue_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_CLEAR_CHANNEL in config:
|
||||
conf = config[CONF_CLEAR_CHANNEL]
|
||||
sensor.register_sensor(tcs.Pmake_clear_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_ILLUMINANCE in config:
|
||||
conf = config[CONF_ILLUMINANCE]
|
||||
sensor.register_sensor(tcs.Pmake_illuminance_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_COLOR_TEMPERATURE in config:
|
||||
conf = config[CONF_COLOR_TEMPERATURE]
|
||||
sensor.register_sensor(tcs.Pmake_color_temperature_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
setup_component(tcs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TCS34725'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL, CONF_CLEAR_CHANNEL,
|
||||
CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
@@ -1,28 +1,39 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional
|
||||
from esphomeyaml.cpp_generator import add, process_lambda, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, float_, optional
|
||||
|
||||
MakeTemplateSensor = Application.MakeTemplateSensor
|
||||
MakeTemplateSensor = Application.struct('MakeTemplateSensor')
|
||||
TemplateSensor = sensor.sensor_ns.class_('TemplateSensor', sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TemplateSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSensor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
template_ = None
|
||||
rhs = App.make_template_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
template = make.Ptemplate_
|
||||
|
||||
sensor.setup_sensor(template, make.Pmqtt, config)
|
||||
setup_component(template, config)
|
||||
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(float_)):
|
||||
yield
|
||||
rhs = App.make_template_sensor(config[CONF_NAME], template_,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config)
|
||||
add(template.set_template(template_))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
41
esphomeyaml/components/sensor/total_daily_energy.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor, time
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_TIME_ID
|
||||
from esphomeyaml.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import Application, Component, App
|
||||
|
||||
DEPENDENCIES = ['time']
|
||||
|
||||
CONF_POWER_ID = 'power_id'
|
||||
MakeTotalDailyEnergySensor = Application.struct('MakeTotalDailyEnergySensor')
|
||||
TotalDailyEnergy = sensor.sensor_ns.class_('TotalDailyEnergy', sensor.Sensor, Component)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TotalDailyEnergy),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTotalDailyEnergySensor),
|
||||
cv.GenerateID(CONF_TIME_ID): cv.use_variable_id(time.RealTimeClockComponent),
|
||||
vol.Required(CONF_POWER_ID): cv.use_variable_id(sensor.Sensor),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for time_ in get_variable(config[CONF_TIME_ID]):
|
||||
yield
|
||||
for sens in get_variable(config[CONF_POWER_ID]):
|
||||
yield
|
||||
rhs = App.make_total_daily_energy_sensor(config[CONF_NAME], time_, sens)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
total_energy = make.Ptotal_energy
|
||||
|
||||
sensor.setup_sensor(total_energy, make.Pmqtt, config)
|
||||
setup_component(total_energy, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TOTAL_DAILY_ENERGY_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
@@ -1,21 +1,26 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import i2c, sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_INTEGRATION_TIME, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
TSL2561IntegrationTime = sensor.sensor_ns.enum('TSL2561IntegrationTime')
|
||||
INTEGRATION_TIMES = {
|
||||
14: sensor.sensor_ns.TSL2561_INTEGRATION_14MS,
|
||||
101: sensor.sensor_ns.TSL2561_INTEGRATION_101MS,
|
||||
402: sensor.sensor_ns.TSL2561_INTEGRATION_402MS,
|
||||
14: TSL2561IntegrationTime.TSL2561_INTEGRATION_14MS,
|
||||
101: TSL2561IntegrationTime.TSL2561_INTEGRATION_101MS,
|
||||
402: TSL2561IntegrationTime.TSL2561_INTEGRATION_402MS,
|
||||
}
|
||||
|
||||
TSL2561Gain = sensor.sensor_ns.enum('TSL2561Gain')
|
||||
GAINS = {
|
||||
'1X': sensor.sensor_ns.TSL2561_GAIN_1X,
|
||||
'16X': sensor.sensor_ns.TSL2561_GAIN_16X,
|
||||
'1X': TSL2561Gain.TSL2561_GAIN_1X,
|
||||
'16X': TSL2561Gain.TSL2561_GAIN_16X,
|
||||
}
|
||||
|
||||
CONF_IS_CS_PACKAGE = 'is_cs_package'
|
||||
@@ -28,16 +33,19 @@ def validate_integration_time(value):
|
||||
return value
|
||||
|
||||
|
||||
MakeTSL2561Sensor = Application.MakeTSL2561Sensor
|
||||
MakeTSL2561Sensor = Application.struct('MakeTSL2561Sensor')
|
||||
TSL2561Sensor = sensor.sensor_ns.class_('TSL2561Sensor', sensor.PollingSensorComponent,
|
||||
i2c.I2CDevice)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TSL2561Sensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTSL2561Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x39): cv.i2c_address,
|
||||
vol.Optional(CONF_INTEGRATION_TIME): validate_integration_time,
|
||||
vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*GAINS)),
|
||||
vol.Optional(CONF_GAIN): cv.one_of(*GAINS, upper=True),
|
||||
vol.Optional(CONF_IS_CS_PACKAGE): cv.boolean,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -45,13 +53,20 @@ def to_code(config):
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make_tsl = variable(config[CONF_MAKE_ID], rhs)
|
||||
tsl2561 = make_tsl.Ptsl2561
|
||||
|
||||
if CONF_INTEGRATION_TIME in config:
|
||||
add(tsl2561.set_integration_time(INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]))
|
||||
if CONF_GAIN in config:
|
||||
add(tsl2561.set_gain(GAINS[config[CONF_GAIN]]))
|
||||
if CONF_IS_CS_PACKAGE in config:
|
||||
add(tsl2561.set_is_cs_package(config[CONF_IS_CS_PACKAGE]))
|
||||
|
||||
sensor.setup_sensor(tsl2561, make_tsl.Pmqtt, config)
|
||||
setup_component(tsl2561, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TSL2561'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
@@ -5,37 +5,47 @@ from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ECHO_PIN, CONF_MAKE_ID, CONF_NAME, CONF_TIMEOUT_METER, \
|
||||
CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, \
|
||||
gpio_output_pin_expression, variable
|
||||
from esphomeyaml.cpp_generator import variable, add
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, gpio_input_pin_expression, \
|
||||
setup_component
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
MakeUltrasonicSensor = Application.MakeUltrasonicSensor
|
||||
MakeUltrasonicSensor = Application.struct('MakeUltrasonicSensor')
|
||||
UltrasonicSensorComponent = sensor.sensor_ns.class_('UltrasonicSensorComponent',
|
||||
sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(UltrasonicSensorComponent),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUltrasonicSensor),
|
||||
vol.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema,
|
||||
vol.Exclusive(CONF_TIMEOUT_METER, 'timeout'): cv.positive_float,
|
||||
vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_time_period_microseconds,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
trigger = None
|
||||
for trigger in gpio_output_pin_expression(config[CONF_TRIGGER_PIN]):
|
||||
yield
|
||||
echo = None
|
||||
for echo in gpio_input_pin_expression(config[CONF_ECHO_PIN]):
|
||||
yield
|
||||
rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
ultrasonic = make.Pultrasonic
|
||||
|
||||
if CONF_TIMEOUT_TIME in config:
|
||||
add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME]))
|
||||
elif CONF_TIMEOUT_METER in config:
|
||||
add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER]))
|
||||
|
||||
sensor.setup_sensor(ultrasonic, make.Pmqtt, config)
|
||||
setup_component(ultrasonic, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
|
||||
33
esphomeyaml/components/sensor/uptime.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
MakeUptimeSensor = Application.struct('MakeUptimeSensor')
|
||||
UptimeSensor = sensor.sensor_ns.class_('UptimeSensor', sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(UptimeSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUptimeSensor),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_uptime_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
uptime = make.Puptime
|
||||
|
||||
sensor.setup_sensor(uptime, make.Pmqtt, config)
|
||||
setup_component(uptime, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_UPTIME_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
33
esphomeyaml/components/sensor/wifi_signal.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
MakeWiFiSignalSensor = Application.struct('MakeWiFiSignalSensor')
|
||||
WiFiSignalSensor = sensor.sensor_ns.class_('WiFiSignalSensor', sensor.PollingSensorComponent)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(WiFiSignalSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeWiFiSignalSensor),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_wifi_signal_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
wifi = make.Pwifi
|
||||
|
||||
sensor.setup_sensor(wifi, make.Pmqtt, config)
|
||||
setup_component(wifi, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_WIFI_SIGNAL_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return sensor.core_to_hass_config(data, config)
|
||||
53
esphomeyaml/components/sensor/xiaomi_miflora.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import esp32_ble_tracker, sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_CONDUCTIVITY, CONF_ID, CONF_ILLUMINANCE, \
|
||||
CONF_MAC_ADDRESS, CONF_MOISTURE, CONF_NAME, CONF_TEMPERATURE
|
||||
from esphomeyaml.cpp_generator import get_variable, Pvariable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(esp32_ble_tracker.XiaomiDevice),
|
||||
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker),
|
||||
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_MOISTURE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ILLUMINANCE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_CONDUCTIVITY): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
dev = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sensor.register_sensor(dev.Pmake_temperature_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_MOISTURE in config:
|
||||
conf = config[CONF_MOISTURE]
|
||||
sensor.register_sensor(dev.Pmake_moisture_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_ILLUMINANCE in config:
|
||||
conf = config[CONF_ILLUMINANCE]
|
||||
sensor.register_sensor(dev.Pmake_illuminance_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_CONDUCTIVITY in config:
|
||||
conf = config[CONF_CONDUCTIVITY]
|
||||
sensor.register_sensor(dev.Pmake_conductivity_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_BATTERY_LEVEL in config:
|
||||
conf = config[CONF_BATTERY_LEVEL]
|
||||
sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_TEMPERATURE, CONF_MOISTURE, CONF_ILLUMINANCE, CONF_CONDUCTIVITY,
|
||||
CONF_BATTERY_LEVEL):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
44
esphomeyaml/components/sensor/xiaomi_mijia.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import esp32_ble_tracker, sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_TEMPERATURE
|
||||
from esphomeyaml.cpp_generator import get_variable, Pvariable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(esp32_ble_tracker.XiaomiDevice),
|
||||
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker),
|
||||
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_HUMIDITY): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(esp32_ble_tracker.XIAOMI_SENSOR_SCHEMA),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
dev = Pvariable(config[CONF_MAKE_ID], rhs)
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sensor.register_sensor(dev.Pmake_temperature_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_HUMIDITY in config:
|
||||
conf = config[CONF_HUMIDITY]
|
||||
sensor.register_sensor(dev.Pmake_humidity_sensor(conf[CONF_NAME]), conf)
|
||||
if CONF_BATTERY_LEVEL in config:
|
||||
conf = config[CONF_BATTERY_LEVEL]
|
||||
sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf)
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
ret = []
|
||||
for key in (CONF_TEMPERATURE, CONF_HUMIDITY, CONF_BATTERY_LEVEL):
|
||||
if key in config:
|
||||
ret.append(sensor.core_to_hass_config(data, config[key]))
|
||||
return ret
|
||||
40
esphomeyaml/components/spi.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, gpio_output_pin_expression, \
|
||||
setup_component
|
||||
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
|
||||
|
||||
SPIComponent = esphomelib_ns.class_('SPIComponent', Component)
|
||||
SPIDevice = esphomelib_ns.class_('SPIDevice')
|
||||
MULTI_CONF = True
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(SPIComponent),
|
||||
vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema,
|
||||
vol.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema,
|
||||
}), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for clk in gpio_output_pin_expression(config[CONF_CLK_PIN]):
|
||||
yield
|
||||
rhs = App.init_spi(clk)
|
||||
spi = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_MISO_PIN in config:
|
||||
for miso in gpio_input_pin_expression(config[CONF_MISO_PIN]):
|
||||
yield
|
||||
add(spi.set_miso(miso))
|
||||
if CONF_MOSI_PIN in config:
|
||||
for mosi in gpio_input_pin_expression(config[CONF_MOSI_PIN]):
|
||||
yield
|
||||
add(spi.set_mosi(mosi))
|
||||
|
||||
setup_component(spi, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SPI'
|
||||
26
esphomeyaml/components/status_led.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import config_validation as cv, pins
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
|
||||
|
||||
StatusLEDComponent = esphomelib_ns.class_('StatusLEDComponent', Component)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(StatusLEDComponent),
|
||||
vol.Optional(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_status_led(pin)
|
||||
var = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
setup_component(var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_STATUS_LED'
|
||||
124
esphomeyaml/components/stepper/__init__.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ACCELERATION, CONF_DECELERATION, CONF_ID, CONF_MAX_SPEED, \
|
||||
CONF_POSITION, CONF_TARGET
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import Pvariable, add, get_variable, templatable
|
||||
from esphomeyaml.cpp_types import Action, esphomelib_ns, int32
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
stepper_ns = esphomelib_ns.namespace('stepper')
|
||||
Stepper = stepper_ns.class_('Stepper')
|
||||
|
||||
SetTargetAction = stepper_ns.class_('SetTargetAction', Action)
|
||||
ReportPositionAction = stepper_ns.class_('ReportPositionAction', Action)
|
||||
|
||||
|
||||
def validate_acceleration(value):
|
||||
value = cv.string(value)
|
||||
for suffix in ('steps/s^2', 'steps/s*s', 'steps/s/s', 'steps/ss', 'steps/(s*s)'):
|
||||
if value.endswith(suffix):
|
||||
value = value[:-len(suffix)]
|
||||
|
||||
if value == 'inf':
|
||||
return 1e6
|
||||
|
||||
try:
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
raise vol.Invalid("Expected acceleration as floating point number, got {}".format(value))
|
||||
|
||||
if value <= 0:
|
||||
raise vol.Invalid("Acceleration must be larger than 0 steps/s^2!")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def validate_speed(value):
|
||||
value = cv.string(value)
|
||||
for suffix in ('steps/s', 'steps/s'):
|
||||
if value.endswith(suffix):
|
||||
value = value[:-len(suffix)]
|
||||
|
||||
if value == 'inf':
|
||||
return 1e6
|
||||
|
||||
try:
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
raise vol.Invalid("Expected speed as floating point number, got {}".format(value))
|
||||
|
||||
if value <= 0:
|
||||
raise vol.Invalid("Speed must be larger than 0 steps/s!")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
STEPPER_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_MAX_SPEED): validate_speed,
|
||||
vol.Optional(CONF_ACCELERATION): validate_acceleration,
|
||||
vol.Optional(CONF_DECELERATION): validate_acceleration,
|
||||
})
|
||||
|
||||
STEPPER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(STEPPER_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_stepper_core_(stepper_var, config):
|
||||
if CONF_ACCELERATION in config:
|
||||
add(stepper_var.set_acceleration(config[CONF_ACCELERATION]))
|
||||
if CONF_DECELERATION in config:
|
||||
add(stepper_var.set_deceleration(config[CONF_DECELERATION]))
|
||||
if CONF_MAX_SPEED in config:
|
||||
add(stepper_var.set_max_speed(config[CONF_MAX_SPEED]))
|
||||
|
||||
|
||||
def setup_stepper(stepper_var, config):
|
||||
CORE.add_job(setup_stepper_core_, stepper_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_STEPPER'
|
||||
|
||||
CONF_STEPPER_SET_TARGET = 'stepper.set_target'
|
||||
STEPPER_SET_TARGET_ACTION_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Stepper),
|
||||
vol.Required(CONF_TARGET): cv.templatable(cv.int_),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_STEPPER_SET_TARGET, STEPPER_SET_TARGET_ACTION_SCHEMA)
|
||||
def stepper_set_target_to_code(config, action_id, arg_type, template_arg):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_set_target_action(template_arg)
|
||||
type = SetTargetAction.template(arg_type)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
for template_ in templatable(config[CONF_TARGET], arg_type, int32):
|
||||
yield None
|
||||
add(action.set_target(template_))
|
||||
yield action
|
||||
|
||||
|
||||
CONF_STEPPER_REPORT_POSITION = 'stepper.report_position'
|
||||
STEPPER_REPORT_POSITION_ACTION_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Stepper),
|
||||
vol.Required(CONF_POSITION): cv.templatable(cv.int_),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_STEPPER_REPORT_POSITION, STEPPER_REPORT_POSITION_ACTION_SCHEMA)
|
||||
def stepper_report_position_to_code(config, action_id, arg_type, template_arg):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_report_position_action(template_arg)
|
||||
type = ReportPositionAction.template(arg_type)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
for template_ in templatable(config[CONF_POSITION], arg_type, int32):
|
||||
yield None
|
||||
add(action.set_target(template_))
|
||||
yield action
|
||||
38
esphomeyaml/components/stepper/a4988.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import stepper
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import Component, App
|
||||
|
||||
A4988 = stepper.stepper_ns.class_('A4988', stepper.Stepper, Component)
|
||||
|
||||
PLATFORM_SCHEMA = stepper.STEPPER_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(A4988),
|
||||
vol.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for step_pin in gpio_output_pin_expression(config[CONF_STEP_PIN]):
|
||||
yield
|
||||
for dir_pin in gpio_output_pin_expression(config[CONF_DIR_PIN]):
|
||||
yield
|
||||
rhs = App.make_a4988(step_pin, dir_pin)
|
||||
a4988 = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_SLEEP_PIN in config:
|
||||
for sleep_pin in gpio_output_pin_expression(config[CONF_SLEEP_PIN]):
|
||||
yield
|
||||
add(a4988.set_sleep_pin(sleep_pin))
|
||||
|
||||
stepper.setup_stepper(a4988, config)
|
||||
setup_component(a4988, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_A4988'
|
||||
55
esphomeyaml/components/stepper/uln2003.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import stepper
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN_A, CONF_PIN_B, CONF_PIN_C, CONF_PIN_D, \
|
||||
CONF_SLEEP_WHEN_DONE, CONF_STEP_MODE
|
||||
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
|
||||
|
||||
ULN2003StepMode = stepper.stepper_ns.enum('ULN2003StepMode')
|
||||
|
||||
STEP_MODES = {
|
||||
'FULL_STEP': ULN2003StepMode.ULN2003_STEP_MODE_FULL_STEP,
|
||||
'HALF_STEP': ULN2003StepMode.ULN2003_STEP_MODE_HALF_STEP,
|
||||
'WAVE_DRIVE': ULN2003StepMode.ULN2003_STEP_MODE_WAVE_DRIVE,
|
||||
}
|
||||
|
||||
ULN2003 = stepper.stepper_ns.class_('ULN2003', stepper.Stepper, Component)
|
||||
|
||||
PLATFORM_SCHEMA = stepper.STEPPER_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(ULN2003),
|
||||
vol.Required(CONF_PIN_A): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_PIN_B): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_PIN_C): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_PIN_D): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_SLEEP_WHEN_DONE): cv.boolean,
|
||||
vol.Optional(CONF_STEP_MODE): cv.one_of(*STEP_MODES, upper=True, space='_')
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for pin_a in gpio_output_pin_expression(config[CONF_PIN_A]):
|
||||
yield
|
||||
for pin_b in gpio_output_pin_expression(config[CONF_PIN_B]):
|
||||
yield
|
||||
for pin_c in gpio_output_pin_expression(config[CONF_PIN_C]):
|
||||
yield
|
||||
for pin_d in gpio_output_pin_expression(config[CONF_PIN_D]):
|
||||
yield
|
||||
rhs = App.make_uln2003(pin_a, pin_b, pin_c, pin_d)
|
||||
uln = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_SLEEP_WHEN_DONE in config:
|
||||
add(uln.set_sleep_when_done(config[CONF_SLEEP_WHEN_DONE]))
|
||||
|
||||
if CONF_STEP_MODE in config:
|
||||
add(uln.set_step_mode(STEP_MODES[config[CONF_STEP_MODE]]))
|
||||
|
||||
stepper.setup_stepper(uln, config)
|
||||
setup_component(uln, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ULN2003'
|
||||
135
esphomeyaml/components/substitutions.py
Normal file
@@ -0,0 +1,135 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import core
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.core import EsphomeyamlError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_SUBSTITUTIONS = 'substitutions'
|
||||
|
||||
VALID_SUBSTITUTIONS_CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
|
||||
'0123456789_'
|
||||
|
||||
|
||||
def validate_substitution_key(value):
|
||||
value = cv.string(value)
|
||||
if not value:
|
||||
raise vol.Invalid("Substitution key must not be empty")
|
||||
if value[0] == '$':
|
||||
value = value[1:]
|
||||
if value[0].isdigit():
|
||||
raise vol.Invalid("First character in substitutions cannot be a digit.")
|
||||
for char in value:
|
||||
if char not in VALID_SUBSTITUTIONS_CHARACTERS:
|
||||
raise vol.Invalid(
|
||||
u"Substitution must only consist of upper/lowercase characters, the underscore "
|
||||
u"and numbers. The character '{}' cannot be used".format(char))
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
validate_substitution_key: cv.string_strict,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pass
|
||||
|
||||
|
||||
VARIABLE_PROG = re.compile('\\$([{0}]+|\\{{[{0}]*\\}})'.format(VALID_SUBSTITUTIONS_CHARACTERS))
|
||||
|
||||
|
||||
def _expand_substitutions(substitutions, value, path):
|
||||
if u'$' not in value:
|
||||
return value
|
||||
|
||||
orig_value = value
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
m = VARIABLE_PROG.search(value, i)
|
||||
if not m:
|
||||
# Nothing more to match. Done
|
||||
break
|
||||
|
||||
i, j = m.span(0)
|
||||
name = m.group(1)
|
||||
if name.startswith(u'{') and name.endswith(u'}'):
|
||||
name = name[1:-1]
|
||||
if name not in substitutions:
|
||||
_LOGGER.warning(u"Found '%s' (see %s) which looks like a substitution, but '%s' was "
|
||||
u"not declared", orig_value, u'->'.join(str(x) for x in path), name)
|
||||
i = j
|
||||
continue
|
||||
|
||||
sub = substitutions[name]
|
||||
tail = value[j:]
|
||||
value = value[:i] + sub
|
||||
i = len(value)
|
||||
value += tail
|
||||
return value
|
||||
|
||||
|
||||
def _substitute_item(substitutions, item, path):
|
||||
if isinstance(item, list):
|
||||
for i, it in enumerate(item):
|
||||
sub = _substitute_item(substitutions, it, path + [i])
|
||||
if sub is not None:
|
||||
item[i] = sub
|
||||
elif isinstance(item, dict):
|
||||
replace_keys = []
|
||||
for k, v in item.items():
|
||||
if path or k != CONF_SUBSTITUTIONS:
|
||||
sub = _substitute_item(substitutions, k, path + [k])
|
||||
if sub is not None:
|
||||
replace_keys.append((k, sub))
|
||||
sub = _substitute_item(substitutions, v, path + [k])
|
||||
if sub is not None:
|
||||
item[k] = sub
|
||||
for old, new in replace_keys:
|
||||
item[new] = item[old]
|
||||
del item[old]
|
||||
elif isinstance(item, str):
|
||||
sub = _expand_substitutions(substitutions, item, path)
|
||||
if sub != item:
|
||||
return sub
|
||||
elif isinstance(item, core.Lambda):
|
||||
sub = _expand_substitutions(substitutions, item.value, path)
|
||||
if sub != item:
|
||||
item.value = sub
|
||||
return None
|
||||
|
||||
|
||||
def do_substitution_pass(config):
|
||||
if CONF_SUBSTITUTIONS not in config:
|
||||
return config
|
||||
|
||||
substitutions = config[CONF_SUBSTITUTIONS]
|
||||
if not isinstance(substitutions, dict):
|
||||
raise EsphomeyamlError(u"Substitutions must be a key to value mapping, got {}"
|
||||
u"".format(type(substitutions)))
|
||||
|
||||
key = ''
|
||||
try:
|
||||
replace_keys = []
|
||||
for key, value in substitutions.items():
|
||||
sub = validate_substitution_key(key)
|
||||
if sub != key:
|
||||
replace_keys.append((key, sub))
|
||||
substitutions[key] = cv.string_strict(value)
|
||||
for old, new in replace_keys:
|
||||
substitutions[new] = substitutions[old]
|
||||
del substitutions[old]
|
||||
except vol.Invalid as err:
|
||||
err.path.append(key)
|
||||
|
||||
raise EsphomeyamlError(u"Error while parsing substitutions: {}".format(err))
|
||||
|
||||
config[CONF_SUBSTITUTIONS] = substitutions
|
||||
_substitute_item(substitutions, config, [])
|
||||
|
||||
return config
|
||||
@@ -1,29 +1,41 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY, 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_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component
|
||||
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL, \
|
||||
CONF_OPTIMISTIC
|
||||
from esphomeyaml.cpp_generator import add, Pvariable, get_variable
|
||||
from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Action, App
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
switch_ns = esphomelib_ns.namespace('switch_')
|
||||
Switch = switch_ns.Switch
|
||||
MQTTSwitchComponent = switch_ns.MQTTSwitchComponent
|
||||
ToggleAction = switch_ns.ToggleAction
|
||||
TurnOffAction = switch_ns.TurnOffAction
|
||||
TurnOnAction = switch_ns.TurnOnAction
|
||||
Switch = switch_ns.class_('Switch', Nameable)
|
||||
SwitchPtr = Switch.operator('ptr')
|
||||
MQTTSwitchComponent = switch_ns.class_('MQTTSwitchComponent', mqtt.MQTTComponent)
|
||||
|
||||
ToggleAction = switch_ns.class_('ToggleAction', Action)
|
||||
TurnOffAction = switch_ns.class_('TurnOffAction', Action)
|
||||
TurnOnAction = switch_ns.class_('TurnOnAction', Action)
|
||||
|
||||
SwitchCondition = switch_ns.class_('SwitchCondition', Condition)
|
||||
|
||||
SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(Switch),
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent),
|
||||
vol.Optional(CONF_ICON): cv.icon,
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
})
|
||||
|
||||
SWITCH_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SWITCH_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_switch_core_(switch_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(switch_var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_ICON in config:
|
||||
add(switch_var.set_icon(config[CONF_ICON]))
|
||||
if CONF_INVERTED in config:
|
||||
@@ -46,3 +58,89 @@ def register_switch(var, config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SWITCH'
|
||||
|
||||
|
||||
CONF_SWITCH_TOGGLE = 'switch.toggle'
|
||||
SWITCH_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Switch),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_SWITCH_TOGGLE, SWITCH_TOGGLE_ACTION_SCHEMA)
|
||||
def switch_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)
|
||||
type = ToggleAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_SWITCH_TURN_OFF = 'switch.turn_off'
|
||||
SWITCH_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Switch),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_SWITCH_TURN_OFF, SWITCH_TURN_OFF_ACTION_SCHEMA)
|
||||
def switch_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)
|
||||
type = TurnOffAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_SWITCH_TURN_ON = 'switch.turn_on'
|
||||
SWITCH_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Switch),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_SWITCH_TURN_ON, SWITCH_TURN_ON_ACTION_SCHEMA)
|
||||
def switch_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)
|
||||
type = TurnOnAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_SWITCH_IS_ON = 'switch.is_on'
|
||||
SWITCH_IS_ON_CONDITION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Switch),
|
||||
})
|
||||
|
||||
|
||||
@CONDITION_REGISTRY.register(CONF_SWITCH_IS_ON, SWITCH_IS_ON_CONDITION_SCHEMA)
|
||||
def switch_is_on_to_code(config, condition_id, arg_type, template_arg):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_switch_is_on_condition(template_arg)
|
||||
type = SwitchCondition.template(arg_type)
|
||||
yield Pvariable(condition_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_SWITCH_IS_OFF = 'switch.is_off'
|
||||
SWITCH_IS_OFF_CONDITION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(Switch),
|
||||
})
|
||||
|
||||
|
||||
@CONDITION_REGISTRY.register(CONF_SWITCH_IS_OFF, SWITCH_IS_OFF_CONDITION_SCHEMA)
|
||||
def switch_is_off_to_code(config, condition_id, arg_type, template_arg):
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_switch_is_off_condition(template_arg)
|
||||
type = SwitchCondition.template(arg_type)
|
||||
yield Pvariable(condition_id, rhs, type=type)
|
||||
|
||||
|
||||
def core_to_hass_config(data, config):
|
||||
ret = mqtt.build_hass_config(data, 'switch', config, include_state=True, include_command=True)
|
||||
if ret is None:
|
||||
return None
|
||||
if CONF_ICON in config:
|
||||
ret['icon'] = config[CONF_ICON]
|
||||
if CONF_OPTIMISTIC in config:
|
||||
ret['optimistic'] = config[CONF_OPTIMISTIC]
|
||||
return ret
|
||||
|
||||
36
esphomeyaml/components/switch/custom.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import switch
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES
|
||||
from esphomeyaml.cpp_generator import process_lambda, variable
|
||||
from esphomeyaml.cpp_types import std_vector
|
||||
|
||||
CustomSwitchConstructor = switch.switch_ns.class_('CustomSwitchConstructor')
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CustomSwitchConstructor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Required(CONF_SWITCHES):
|
||||
cv.ensure_list(switch.SWITCH_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(switch.Switch),
|
||||
})),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=std_vector.template(switch.SwitchPtr)):
|
||||
yield
|
||||
|
||||
rhs = CustomSwitchConstructor(template_)
|
||||
custom = variable(config[CONF_ID], rhs)
|
||||
for i, sens in enumerate(config[CONF_SWITCHES]):
|
||||
switch.register_switch(custom.get_switch(i), sens)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_CUSTOM_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [switch.core_to_hass_config(data, swi) for swi in config[CONF_SWITCHES]]
|
||||
@@ -1,26 +1,48 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_RESTORE_MODE
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
MakeGPIOSwitch = Application.MakeGPIOSwitch
|
||||
MakeGPIOSwitch = Application.struct('MakeGPIOSwitch')
|
||||
GPIOSwitch = switch.switch_ns.class_('GPIOSwitch', switch.Switch, Component)
|
||||
GPIOSwitchRestoreMode = switch.switch_ns.enum('GPIOSwitchRestoreMode')
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
RESTORE_MODES = {
|
||||
'RESTORE_DEFAULT_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF,
|
||||
'RESTORE_DEFAULT_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON,
|
||||
'ALWAYS_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF,
|
||||
'ALWAYS_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON,
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(GPIOSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOSwitch),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
}).extend(switch.SWITCH_SCHEMA.schema)
|
||||
vol.Optional(CONF_RESTORE_MODE): cv.one_of(*RESTORE_MODES, upper=True, space='_'),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_gpio_switch(config[CONF_NAME], pin)
|
||||
gpio = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
gpio = make.Pswitch_
|
||||
|
||||
if CONF_RESTORE_MODE in config:
|
||||
add(gpio.set_restore_mode(RESTORE_MODES[config[CONF_RESTORE_MODE]]))
|
||||
|
||||
switch.setup_switch(gpio, make.Pmqtt, config)
|
||||
setup_component(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.components.ir_transmitter import IRTransmitterComponent
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \
|
||||
CONF_INVERTED, CONF_IR_TRANSMITTER_ID, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \
|
||||
CONF_PANASONIC, CONF_RAW, CONF_REPEAT, CONF_SONY, CONF_TIMES, CONF_WAIT_TIME
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, HexIntLiteral, get_variable
|
||||
|
||||
DEPENDENCIES = ['ir_transmitter']
|
||||
|
||||
IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW]
|
||||
|
||||
WAIT_TIME_MESSAGE = "The wait_time_us option has been renamed to wait_time in order to decrease " \
|
||||
"ambiguity. "
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
|
||||
vol.Exclusive(CONF_NEC, 'code'): vol.Schema({
|
||||
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
|
||||
}),
|
||||
vol.Exclusive(CONF_LG, 'code'): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), vol.Range(min=0, max=32)),
|
||||
}),
|
||||
vol.Exclusive(CONF_SONY, 'code'): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), vol.Range(min=0, max=32)),
|
||||
}),
|
||||
vol.Exclusive(CONF_PANASONIC, 'code'): vol.Schema({
|
||||
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint32_t,
|
||||
}),
|
||||
vol.Exclusive(CONF_RAW, 'code'): vol.Schema({
|
||||
vol.Required(CONF_CARRIER_FREQUENCY): vol.All(cv.frequency, vol.Coerce(int)),
|
||||
vol.Required(CONF_DATA): [vol.Coerce(int)],
|
||||
}),
|
||||
vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
|
||||
vol.Required(CONF_TIMES): cv.positive_not_null_int,
|
||||
vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds,
|
||||
|
||||
vol.Optional('wait_time_us'): cv.invalid(WAIT_TIME_MESSAGE),
|
||||
})),
|
||||
cv.GenerateID(CONF_IR_TRANSMITTER_ID): cv.use_variable_id(IRTransmitterComponent),
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("IR Transmitters do not support inverted mode!"),
|
||||
}).extend(switch.SWITCH_SCHEMA.schema), cv.has_at_least_one_key(*IR_KEYS))
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
ir_ns = switch.switch_ns.namespace('ir')
|
||||
SendData = ir_ns.namespace('SendData')
|
||||
DataTransmitter = IRTransmitterComponent.DataTransmitter
|
||||
|
||||
|
||||
def safe_hex(value):
|
||||
if value is None:
|
||||
return None
|
||||
return HexIntLiteral(value)
|
||||
|
||||
|
||||
def exp_send_data(config):
|
||||
if CONF_NEC in config:
|
||||
conf = config[CONF_NEC]
|
||||
base = SendData.from_nec(safe_hex(conf[CONF_ADDRESS]),
|
||||
safe_hex(conf[CONF_COMMAND]))
|
||||
elif CONF_LG in config:
|
||||
conf = config[CONF_LG]
|
||||
base = SendData.from_lg(safe_hex(conf[CONF_DATA]), conf.get(CONF_NBITS))
|
||||
elif CONF_SONY in config:
|
||||
conf = config[CONF_SONY]
|
||||
base = SendData.from_sony(safe_hex(conf[CONF_DATA]), conf.get(CONF_NBITS))
|
||||
elif CONF_PANASONIC in config:
|
||||
conf = config[CONF_PANASONIC]
|
||||
base = SendData.from_panasonic(safe_hex(conf[CONF_ADDRESS]),
|
||||
safe_hex(conf[CONF_COMMAND]))
|
||||
elif CONF_RAW in config:
|
||||
conf = config[CONF_RAW]
|
||||
data = ArrayInitializer(*conf[CONF_DATA])
|
||||
base = SendData.from_raw(data, conf[CONF_CARRIER_FREQUENCY])
|
||||
else:
|
||||
raise ESPHomeYAMLError(u"Unsupported IR mode {}".format(config))
|
||||
|
||||
if CONF_REPEAT in config:
|
||||
if isinstance(config[CONF_REPEAT], int):
|
||||
times = config[CONF_REPEAT]
|
||||
wait_us = None
|
||||
else:
|
||||
times = config[CONF_REPEAT][CONF_TIMES]
|
||||
wait_us = config[CONF_REPEAT][CONF_WAIT_TIME]
|
||||
base = base.repeat(times, wait_us)
|
||||
return base
|
||||
|
||||
|
||||
def to_code(config):
|
||||
ir = None
|
||||
for ir in get_variable(config[CONF_IR_TRANSMITTER_ID]):
|
||||
yield
|
||||
send_data = exp_send_data(config)
|
||||
rhs = App.register_component(ir.create_transmitter(config[CONF_NAME], send_data))
|
||||
switch.register_switch(rhs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
|
||||
@@ -1,25 +1,35 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import output, switch
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, Application, get_variable, variable
|
||||
from esphomeyaml.cpp_generator import get_variable, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
MakeSimpleSwitch = Application.MakeSimpleSwitch
|
||||
MakeOutputSwitch = Application.struct('MakeOutputSwitch')
|
||||
OutputSwitch = switch.switch_ns.class_('OutputSwitch', switch.Switch, Component)
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSimpleSwitch),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
}).extend(switch.SWITCH_SCHEMA.schema)
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(OutputSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeOutputSwitch),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
for output_ in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_simple_switch(config[CONF_NAME], output)
|
||||
gpio = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
|
||||
rhs = App.make_output_switch(config[CONF_NAME], output_)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch_ = make.Pswitch_
|
||||
|
||||
switch.setup_switch(switch_, make.Pmqtt, config)
|
||||
setup_component(switch, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SIMPLE_SWITCH'
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
|
||||
152
esphomeyaml/components/switch/remote_transmitter.py
Normal file
@@ -0,0 +1,152 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \
|
||||
RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \
|
||||
RC_SWITCH_TYPE_D_SCHEMA, RemoteTransmitterComponent, binary_code, build_rc_switch_protocol, \
|
||||
remote_ns
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_CHANNEL, CONF_CODE, \
|
||||
CONF_COMMAND, CONF_DATA, CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_INVERTED, CONF_LG, \
|
||||
CONF_NAME, CONF_NBITS, CONF_NEC, 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_REPEAT, CONF_SAMSUNG, CONF_SONY, CONF_STATE, CONF_TIMES, \
|
||||
CONF_WAIT_TIME
|
||||
from esphomeyaml.cpp_generator import ArrayInitializer, Pvariable, add, get_variable
|
||||
|
||||
DEPENDENCIES = ['remote_transmitter']
|
||||
|
||||
REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SAMSUNG, CONF_SONY, CONF_PANASONIC, 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_REMOTE_TRANSMITTER_ID = 'remote_transmitter_id'
|
||||
CONF_TRANSMITTER_ID = 'transmitter_id'
|
||||
|
||||
RemoteTransmitter = remote_ns.class_('RemoteTransmitter', switch.Switch)
|
||||
LGTransmitter = remote_ns.class_('LGTransmitter', RemoteTransmitter)
|
||||
NECTransmitter = remote_ns.class_('NECTransmitter', RemoteTransmitter)
|
||||
PanasonicTransmitter = remote_ns.class_('PanasonicTransmitter', RemoteTransmitter)
|
||||
RawTransmitter = remote_ns.class_('RawTransmitter', RemoteTransmitter)
|
||||
SamsungTransmitter = remote_ns.class_('SamsungTransmitter', RemoteTransmitter)
|
||||
SonyTransmitter = remote_ns.class_('SonyTransmitter', RemoteTransmitter)
|
||||
RCSwitchRawTransmitter = remote_ns.class_('RCSwitchRawTransmitter', RemoteTransmitter)
|
||||
RCSwitchTypeATransmitter = remote_ns.class_('RCSwitchTypeATransmitter', RCSwitchRawTransmitter)
|
||||
RCSwitchTypeBTransmitter = remote_ns.class_('RCSwitchTypeBTransmitter', RCSwitchRawTransmitter)
|
||||
RCSwitchTypeCTransmitter = remote_ns.class_('RCSwitchTypeCTransmitter', RCSwitchRawTransmitter)
|
||||
RCSwitchTypeDTransmitter = remote_ns.class_('RCSwitchTypeDTransmitter', RCSwitchRawTransmitter)
|
||||
|
||||
validate_raw_data = [vol.Any(vol.Coerce(int), cv.time_period_microseconds)]
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(RemoteTransmitter),
|
||||
vol.Optional(CONF_LG): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
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,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
|
||||
}),
|
||||
vol.Optional(CONF_SAMSUNG): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
}),
|
||||
vol.Optional(CONF_SONY): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
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,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint32_t,
|
||||
}),
|
||||
vol.Optional(CONF_RAW): vol.Any(validate_raw_data, vol.Schema({
|
||||
vol.Required(CONF_DATA): validate_raw_data,
|
||||
vol.Optional(CONF_CARRIER_FREQUENCY): vol.All(cv.frequency, vol.Coerce(int)),
|
||||
})),
|
||||
vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_A): RC_SWITCH_TYPE_A_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA,
|
||||
vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA,
|
||||
|
||||
vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
|
||||
vol.Required(CONF_TIMES): cv.positive_not_null_int,
|
||||
vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds,
|
||||
})),
|
||||
cv.GenerateID(CONF_REMOTE_TRANSMITTER_ID): cv.use_variable_id(RemoteTransmitterComponent),
|
||||
cv.GenerateID(CONF_TRANSMITTER_ID): cv.declare_variable_id(RemoteTransmitter),
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("Remote Transmitters do not support inverted mode!"),
|
||||
}), cv.has_exactly_one_key(*REMOTE_KEYS))
|
||||
|
||||
|
||||
def transmitter_base(full_config):
|
||||
name = full_config[CONF_NAME]
|
||||
key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS)
|
||||
|
||||
if key == CONF_LG:
|
||||
return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
|
||||
if key == CONF_NEC:
|
||||
return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
if key == CONF_PANASONIC:
|
||||
return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
if key == CONF_SAMSUNG:
|
||||
return SamsungTransmitter.new(name, config[CONF_DATA])
|
||||
if key == CONF_SONY:
|
||||
return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
|
||||
if key == CONF_RAW:
|
||||
if isinstance(config, dict):
|
||||
data = config[CONF_DATA]
|
||||
carrier_frequency = config.get(CONF_CARRIER_FREQUENCY)
|
||||
else:
|
||||
data = config
|
||||
carrier_frequency = None
|
||||
return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False),
|
||||
carrier_frequency)
|
||||
if key == CONF_RC_SWITCH_RAW:
|
||||
return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
|
||||
if key == CONF_RC_SWITCH_TYPE_A:
|
||||
return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
binary_code(config[CONF_GROUP]),
|
||||
binary_code(config[CONF_DEVICE]),
|
||||
config[CONF_STATE])
|
||||
if key == CONF_RC_SWITCH_TYPE_B:
|
||||
return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
config[CONF_ADDRESS], config[CONF_CHANNEL],
|
||||
config[CONF_STATE])
|
||||
if key == CONF_RC_SWITCH_TYPE_C:
|
||||
return RCSwitchTypeCTransmitter.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])
|
||||
if key == CONF_RC_SWITCH_TYPE_D:
|
||||
return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
ord(config[CONF_GROUP][0]) - ord('a'),
|
||||
config[CONF_DEVICE], config[CONF_STATE])
|
||||
|
||||
raise NotImplementedError("Unknown transmitter type {}".format(config))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for remote in get_variable(config[CONF_REMOTE_TRANSMITTER_ID]):
|
||||
yield
|
||||
rhs = transmitter_base(config)
|
||||
transmitter = Pvariable(config[CONF_TRANSMITTER_ID], rhs)
|
||||
|
||||
if CONF_REPEAT in config:
|
||||
if isinstance(config[CONF_REPEAT], int):
|
||||
times = config[CONF_REPEAT]
|
||||
wait_us = 1000
|
||||
else:
|
||||
times = config[CONF_REPEAT][CONF_TIMES]
|
||||
wait_us = config[CONF_REPEAT][CONF_WAIT_TIME]
|
||||
add(transmitter.set_repeat(times, wait_us))
|
||||
|
||||
switch.register_switch(remote.add_transmitter(transmitter), config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
@@ -1,16 +1,19 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
|
||||
MakeRestartSwitch = Application.MakeRestartSwitch
|
||||
MakeRestartSwitch = Application.struct('MakeRestartSwitch')
|
||||
RestartSwitch = switch.switch_ns.class_('RestartSwitch', switch.Switch)
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(RestartSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRestartSwitch),
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("Restart switches do not support inverted mode!"),
|
||||
}).extend(switch.SWITCH_SCHEMA.schema)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -20,3 +23,7 @@ def to_code(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
|
||||
@@ -3,14 +3,17 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_types import Application, App
|
||||
|
||||
MakeShutdownSwitch = Application.MakeShutdownSwitch
|
||||
MakeShutdownSwitch = Application.struct('MakeShutdownSwitch')
|
||||
ShutdownSwitch = switch.switch_ns.class_('ShutdownSwitch', switch.Switch)
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(ShutdownSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeShutdownSwitch),
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"),
|
||||
}).extend(switch.SWITCH_SCHEMA.schema)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -20,3 +23,7 @@ def to_code(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
|
||||
@@ -1,48 +1,57 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_TURN_OFF_ACTION, \
|
||||
CONF_TURN_ON_ACTION, CONF_OPTIMISTIC
|
||||
from esphomeyaml.helpers import App, Application, process_lambda, variable, NoArg, add, bool_, \
|
||||
optional
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_OPTIMISTIC, \
|
||||
CONF_RESTORE_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION
|
||||
from esphomeyaml.cpp_generator import add, process_lambda, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component, NoArg, bool_, optional
|
||||
|
||||
MakeTemplateSwitch = Application.MakeTemplateSwitch
|
||||
MakeTemplateSwitch = Application.struct('MakeTemplateSwitch')
|
||||
TemplateSwitch = switch.switch_ns.class_('TemplateSwitch', switch.Switch, Component)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TemplateSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSwitch),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_TURN_OFF_ACTION): automation.ACTIONS_SCHEMA,
|
||||
vol.Optional(CONF_TURN_ON_ACTION): automation.ACTIONS_SCHEMA,
|
||||
}).extend(switch.SWITCH_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
|
||||
vol.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_RESTORE_STATE): cv.boolean,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_template_switch(config[CONF_NAME])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
template = make.Ptemplate_
|
||||
|
||||
switch.setup_switch(template, make.Pmqtt, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = None
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(bool_)):
|
||||
yield
|
||||
add(make.Ptemplate_.set_state_lambda(template_))
|
||||
add(template.set_state_lambda(template_))
|
||||
if CONF_TURN_OFF_ACTION in config:
|
||||
actions = None
|
||||
for actions in automation.build_actions(config[CONF_TURN_OFF_ACTION], NoArg):
|
||||
yield
|
||||
add(make.Ptemplate_.add_turn_off_actions(actions))
|
||||
automation.build_automation(template.get_turn_off_trigger(), NoArg,
|
||||
config[CONF_TURN_OFF_ACTION])
|
||||
if CONF_TURN_ON_ACTION in config:
|
||||
actions = None
|
||||
for actions in automation.build_actions(config[CONF_TURN_ON_ACTION], NoArg):
|
||||
yield
|
||||
add(make.Ptemplate_.add_turn_on_actions(actions))
|
||||
automation.build_automation(template.get_turn_on_trigger(), NoArg,
|
||||
config[CONF_TURN_ON_ACTION])
|
||||
if CONF_OPTIMISTIC in config:
|
||||
add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
add(template.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
|
||||
switch.setup_switch(make.Ptemplate_, make.Pmqtt, config)
|
||||
if CONF_RESTORE_STATE in config:
|
||||
add(template.set_restore_state(config[CONF_RESTORE_STATE]))
|
||||
|
||||
setup_component(template, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
|
||||
52
esphomeyaml/components/switch/uart.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import switch, uart
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, CONF_UART_ID
|
||||
from esphomeyaml.core import HexInt
|
||||
from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, variable
|
||||
from esphomeyaml.cpp_types import App, Application
|
||||
from esphomeyaml.py_compat import text_type
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
MakeUARTSwitch = Application.struct('MakeUARTSwitch')
|
||||
UARTSwitch = switch.switch_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice)
|
||||
|
||||
|
||||
def validate_data(value):
|
||||
if isinstance(value, text_type):
|
||||
return value.encode('utf-8')
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, list):
|
||||
return vol.Schema([cv.hex_uint8_t])(value)
|
||||
raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes")
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(UARTSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUARTSwitch),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
vol.Required(CONF_DATA): validate_data,
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for uart_ in get_variable(config[CONF_UART_ID]):
|
||||
yield
|
||||
data = config[CONF_DATA]
|
||||
if isinstance(data, str):
|
||||
data = [HexInt(ord(x)) for x in data]
|
||||
rhs = App.make_uart_switch(uart_, config[CONF_NAME], ArrayInitializer(*data, multiline=False))
|
||||
restart = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch.setup_switch(restart.Puart, restart.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_UART_SWITCH'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return switch.core_to_hass_config(data, config)
|
||||
73
esphomeyaml/components/text_sensor/__init__.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.components import mqtt
|
||||
from esphomeyaml.components.mqtt import setup_mqtt_component
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_ON_VALUE, \
|
||||
CONF_TRIGGER_ID
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, std_string, App
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
text_sensor_ns = esphomelib_ns.namespace('text_sensor')
|
||||
TextSensor = text_sensor_ns.class_('TextSensor', Nameable)
|
||||
TextSensorPtr = TextSensor.operator('ptr')
|
||||
MQTTTextSensor = text_sensor_ns.class_('MQTTTextSensor', mqtt.MQTTComponent)
|
||||
|
||||
TextSensorStateTrigger = text_sensor_ns.class_('TextSensorStateTrigger',
|
||||
Trigger.template(std_string))
|
||||
|
||||
TEXT_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTTextSensor),
|
||||
vol.Optional(CONF_ICON): cv.icon,
|
||||
vol.Optional(CONF_ON_VALUE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(TextSensorStateTrigger),
|
||||
}),
|
||||
})
|
||||
|
||||
TEXT_SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(TEXT_SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_text_sensor_core_(text_sensor_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(text_sensor_var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_ICON in config:
|
||||
add(text_sensor_var.set_icon(config[CONF_ICON]))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
rhs = text_sensor_var.make_state_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, std_string, conf)
|
||||
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
|
||||
def setup_text_sensor(text_sensor_obj, mqtt_obj, config):
|
||||
sensor_var = Pvariable(config[CONF_ID], text_sensor_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
CORE.add_job(setup_text_sensor_core_, sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
def register_text_sensor(var, config):
|
||||
text_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
|
||||
rhs = App.register_text_sensor(text_sensor_var)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
|
||||
CORE.add_job(setup_text_sensor_core_, text_sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEXT_SENSOR'
|
||||
|
||||
|
||||
def core_to_hass_config(data, config):
|
||||
ret = mqtt.build_hass_config(data, 'sensor', config, include_state=True, include_command=False)
|
||||
if ret is None:
|
||||
return None
|
||||
if CONF_ICON in config:
|
||||
ret['icon'] = config[CONF_ICON]
|
||||
return ret
|
||||
36
esphomeyaml/components/text_sensor/custom.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import text_sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS
|
||||
from esphomeyaml.cpp_generator import process_lambda, variable
|
||||
from esphomeyaml.cpp_types import std_vector
|
||||
|
||||
CustomTextSensorConstructor = text_sensor.text_sensor_ns.class_('CustomTextSensorConstructor')
|
||||
|
||||
PLATFORM_SCHEMA = text_sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CustomTextSensorConstructor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Required(CONF_TEXT_SENSORS):
|
||||
cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(text_sensor.TextSensor),
|
||||
})),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=std_vector.template(text_sensor.TextSensorPtr)):
|
||||
yield
|
||||
|
||||
rhs = CustomTextSensorConstructor(template_)
|
||||
custom = variable(config[CONF_ID], rhs)
|
||||
for i, sens in enumerate(config[CONF_TEXT_SENSORS]):
|
||||
text_sensor.register_text_sensor(custom.get_text_sensor(i), sens)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_CUSTOM_TEXT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return [text_sensor.core_to_hass_config(data, sens) for sens in config[CONF_TEXT_SENSORS]]
|
||||
33
esphomeyaml/components/text_sensor/homeassistant.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import text_sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ENTITY_ID, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
DEPENDENCIES = ['api']
|
||||
|
||||
MakeHomeassistantTextSensor = Application.struct('MakeHomeassistantTextSensor')
|
||||
HomeassistantTextSensor = text_sensor.text_sensor_ns.class_('HomeassistantTextSensor',
|
||||
text_sensor.TextSensor, Component)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HomeassistantTextSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHomeassistantTextSensor),
|
||||
vol.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_homeassistant_text_sensor(config[CONF_NAME], config[CONF_ENTITY_ID])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor_ = make.Psensor
|
||||
text_sensor.setup_text_sensor(sensor_, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HOMEASSISTANT_TEXT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return text_sensor.core_to_hass_config(data, config)
|
||||
40
esphomeyaml/components/text_sensor/mqtt_subscribe.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import text_sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_QOS, CONF_TOPIC
|
||||
from esphomeyaml.cpp_generator import add, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
MakeMQTTSubscribeTextSensor = Application.struct('MakeMQTTSubscribeTextSensor')
|
||||
MQTTSubscribeTextSensor = text_sensor.text_sensor_ns.class_('MQTTSubscribeTextSensor',
|
||||
text_sensor.TextSensor, Component)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MQTTSubscribeTextSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMQTTSubscribeTextSensor),
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_QOS): cv.mqtt_qos,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_mqtt_subscribe_text_sensor(config[CONF_NAME], config[CONF_TOPIC])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor_ = make.Psensor
|
||||
|
||||
if CONF_QOS in config:
|
||||
add(sensor_.set_qos(config[CONF_QOS]))
|
||||
|
||||
text_sensor.setup_text_sensor(sensor_, make.Pmqtt, config)
|
||||
setup_component(sensor_, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_TEXT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return text_sensor.core_to_hass_config(data, config)
|
||||
39
esphomeyaml/components/text_sensor/template.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import text_sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.cpp_generator import add, process_lambda, variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, PollingComponent, optional, std_string
|
||||
|
||||
MakeTemplateTextSensor = Application.struct('MakeTemplateTextSensor')
|
||||
TemplateTextSensor = text_sensor.text_sensor_ns.class_('TemplateTextSensor',
|
||||
text_sensor.TextSensor, PollingComponent)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(TemplateTextSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateTextSensor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_template_text_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
template = make.Ptemplate_
|
||||
text_sensor.setup_text_sensor(template, make.Pmqtt, config)
|
||||
setup_component(template, config)
|
||||
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(std_string)):
|
||||
yield
|
||||
add(template.set_template(template_))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_TEXT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return text_sensor.core_to_hass_config(data, config)
|
||||
29
esphomeyaml/components/text_sensor/version.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from esphomeyaml.components import text_sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.cpp_generator import variable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Application, Component
|
||||
|
||||
MakeVersionTextSensor = Application.struct('MakeVersionTextSensor')
|
||||
VersionTextSensor = text_sensor.text_sensor_ns.class_('VersionTextSensor',
|
||||
text_sensor.TextSensor, Component)
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(VersionTextSensor),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeVersionTextSensor),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_version_text_sensor(config[CONF_NAME])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
text_sensor.setup_text_sensor(make.Psensor, make.Pmqtt, config)
|
||||
setup_component(make.Psensor, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_VERSION_TEXT_SENSOR'
|
||||
|
||||
|
||||
def to_hass_config(data, config):
|
||||
return text_sensor.core_to_hass_config(data, config)
|
||||
305
esphomeyaml/components/time/__init__.py
Normal file
@@ -0,0 +1,305 @@
|
||||
import datetime
|
||||
import logging
|
||||
import math
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.const import CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \
|
||||
CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import add, Pvariable, ArrayInitializer
|
||||
from esphomeyaml.cpp_types import esphomelib_ns, Component, NoArg, Trigger, App
|
||||
from esphomeyaml.py_compat import string_types
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
time_ns = esphomelib_ns.namespace('time')
|
||||
RealTimeClockComponent = time_ns.class_('RealTimeClockComponent', Component)
|
||||
CronTrigger = time_ns.class_('CronTrigger', Trigger.template(NoArg), Component)
|
||||
EsphomelibTime = time_ns.struct('EsphomelibTime')
|
||||
|
||||
|
||||
def _tz_timedelta(td):
|
||||
offset_hour = int(td.total_seconds() / (60 * 60))
|
||||
offset_minute = int(abs(td.total_seconds() / 60)) % 60
|
||||
offset_second = int(abs(td.total_seconds())) % 60
|
||||
if offset_hour == 0 and offset_minute == 0 and offset_second == 0:
|
||||
return '0'
|
||||
if offset_minute == 0 and offset_second == 0:
|
||||
return '{}'.format(offset_hour)
|
||||
if offset_second == 0:
|
||||
return '{}:{}'.format(offset_hour, offset_minute)
|
||||
return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second)
|
||||
|
||||
|
||||
# https://stackoverflow.com/a/16804556/8924614
|
||||
def _week_of_month(dt):
|
||||
first_day = dt.replace(day=1)
|
||||
dom = dt.day
|
||||
adjusted_dom = dom + first_day.weekday()
|
||||
return int(math.ceil(adjusted_dom / 7.0))
|
||||
|
||||
|
||||
def _tz_dst_str(dt):
|
||||
td = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second)
|
||||
return 'M{}.{}.{}/{}'.format(dt.month, _week_of_month(dt), dt.isoweekday() % 7,
|
||||
_tz_timedelta(td))
|
||||
|
||||
|
||||
def convert_tz(pytz_obj):
|
||||
tz = pytz_obj
|
||||
|
||||
def _dst(dt, is_dst):
|
||||
try:
|
||||
return tz.dst(dt, is_dst=is_dst)
|
||||
except TypeError: # stupid pytz...
|
||||
return tz.dst(dt)
|
||||
|
||||
def _tzname(dt, is_dst):
|
||||
try:
|
||||
return tz.tzname(dt, is_dst=is_dst)
|
||||
except TypeError: # stupid pytz...
|
||||
return tz.tzname(dt)
|
||||
|
||||
def _utcoffset(dt, is_dst):
|
||||
try:
|
||||
return tz.utcoffset(dt, is_dst=is_dst)
|
||||
except TypeError: # stupid pytz...
|
||||
return tz.utcoffset(dt)
|
||||
|
||||
dst_begins = None
|
||||
dst_tzname = None
|
||||
dst_utcoffset = None
|
||||
dst_ends = None
|
||||
norm_tzname = None
|
||||
norm_utcoffset = None
|
||||
|
||||
hour = datetime.timedelta(hours=1)
|
||||
this_year = datetime.datetime.now().year
|
||||
dt = datetime.datetime(year=this_year, month=1, day=1)
|
||||
last_dst = None
|
||||
while dt.year == this_year:
|
||||
current_dst = _dst(dt, not last_dst)
|
||||
is_dst = bool(current_dst)
|
||||
if is_dst != last_dst:
|
||||
if is_dst:
|
||||
dst_begins = dt
|
||||
dst_tzname = _tzname(dt, True)
|
||||
dst_utcoffset = _utcoffset(dt, True)
|
||||
else:
|
||||
dst_ends = dt + hour
|
||||
norm_tzname = _tzname(dt, False)
|
||||
norm_utcoffset = _utcoffset(dt, False)
|
||||
last_dst = is_dst
|
||||
dt += hour
|
||||
|
||||
tzbase = '{}{}'.format(norm_tzname, _tz_timedelta(-1 * norm_utcoffset))
|
||||
if dst_begins is None:
|
||||
# No DST in this timezone
|
||||
_LOGGER.info("Detected timezone '%s' with UTC offset %s",
|
||||
norm_tzname, _tz_timedelta(norm_utcoffset))
|
||||
return tzbase
|
||||
tzext = '{}{},{},{}'.format(dst_tzname, _tz_timedelta(-1 * dst_utcoffset),
|
||||
_tz_dst_str(dst_begins), _tz_dst_str(dst_ends))
|
||||
_LOGGER.info("Detected timezone '%s' with UTC offset %s and daylight savings time from "
|
||||
"%s to %s",
|
||||
norm_tzname, _tz_timedelta(norm_utcoffset), dst_begins.strftime("%x %X"),
|
||||
dst_ends.strftime("%x %X"))
|
||||
return tzbase + tzext
|
||||
|
||||
|
||||
def detect_tz():
|
||||
try:
|
||||
import tzlocal
|
||||
import pytz
|
||||
except ImportError:
|
||||
raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically "
|
||||
"detect the timezone please install tzlocal (pip install tzlocal)")
|
||||
try:
|
||||
tz = tzlocal.get_localzone()
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
_LOGGER.warning("Could not auto-detect timezone. Using UTC...")
|
||||
return 'UTC'
|
||||
|
||||
return convert_tz(tz)
|
||||
|
||||
|
||||
def _parse_cron_int(value, special_mapping, message):
|
||||
special_mapping = special_mapping or {}
|
||||
if isinstance(value, string_types) and value in special_mapping:
|
||||
return special_mapping[value]
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
raise vol.Invalid(message.format(value))
|
||||
|
||||
|
||||
def _parse_cron_part(part, min_value, max_value, special_mapping):
|
||||
if part in ('*', '?'):
|
||||
return set(x for x in range(min_value, max_value + 1))
|
||||
if '/' in part:
|
||||
data = part.split('/')
|
||||
if len(data) > 2:
|
||||
raise vol.Invalid(u"Can't have more than two '/' in one time expression, got {}"
|
||||
.format(part))
|
||||
offset, repeat = data
|
||||
offset_n = 0
|
||||
if offset:
|
||||
offset_n = _parse_cron_int(offset, special_mapping,
|
||||
u"Offset for '/' time expression must be an integer, got {}")
|
||||
|
||||
try:
|
||||
repeat_n = int(repeat)
|
||||
except ValueError:
|
||||
raise vol.Invalid(u"Repeat for '/' time expression must be an integer, got {}"
|
||||
.format(repeat))
|
||||
return set(x for x in range(offset_n, max_value + 1, repeat_n))
|
||||
if '-' in part:
|
||||
data = part.split('-')
|
||||
if len(data) > 2:
|
||||
raise vol.Invalid(u"Can't have more than two '-' in range time expression '{}'"
|
||||
.format(part))
|
||||
begin, end = data
|
||||
begin_n = _parse_cron_int(begin, special_mapping, u"Number for time range must be integer, "
|
||||
u"got {}")
|
||||
end_n = _parse_cron_int(end, special_mapping, u"Number for time range must be integer, "
|
||||
u"got {}")
|
||||
if end_n < begin_n:
|
||||
return set(x for x in range(end_n, max_value + 1)) | \
|
||||
set(x for x in range(min_value, begin_n + 1))
|
||||
return set(x for x in range(begin_n, end_n + 1))
|
||||
|
||||
return {_parse_cron_int(part, special_mapping, u"Number for time expression must be an "
|
||||
u"integer, got {}")}
|
||||
|
||||
|
||||
def cron_expression_validator(name, min_value, max_value, special_mapping=None):
|
||||
def validator(value):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
if not isinstance(v, int):
|
||||
raise vol.Invalid(
|
||||
"Expected integer for {} '{}', got {}".format(v, name, type(v)))
|
||||
if v < min_value or v > max_value:
|
||||
raise vol.Invalid(
|
||||
"{} {} is out of range (min={} max={}).".format(name, v, min_value,
|
||||
max_value))
|
||||
return list(sorted(value))
|
||||
value = cv.string(value)
|
||||
values = set()
|
||||
for part in value.split(','):
|
||||
values |= _parse_cron_part(part, min_value, max_value, special_mapping)
|
||||
return validator(list(values))
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
validate_cron_seconds = cron_expression_validator('seconds', 0, 60)
|
||||
validate_cron_minutes = cron_expression_validator('minutes', 0, 59)
|
||||
validate_cron_hours = cron_expression_validator('hours', 0, 23)
|
||||
validate_cron_days_of_month = cron_expression_validator('days of month', 1, 31)
|
||||
validate_cron_months = cron_expression_validator('months', 1, 12, {
|
||||
'JAN': 1, 'FEB': 2, 'MAR': 3, 'APR': 4, 'MAY': 5, 'JUN': 6, 'JUL': 7, 'AUG': 8,
|
||||
'SEP': 9, 'OCT': 10, 'NOV': 11, 'DEC': 12
|
||||
})
|
||||
validate_cron_days_of_week = cron_expression_validator('days of week', 1, 7, {
|
||||
'SUN': 1, 'MON': 2, 'TUE': 3, 'WED': 4, 'THU': 5, 'FRI': 6, 'SAT': 7
|
||||
})
|
||||
CRON_KEYS = [CONF_SECONDS, CONF_MINUTES, CONF_HOURS, CONF_DAYS_OF_MONTH, CONF_MONTHS,
|
||||
CONF_DAYS_OF_WEEK]
|
||||
|
||||
|
||||
def validate_cron_raw(value):
|
||||
value = cv.string(value)
|
||||
value = value.split(' ')
|
||||
if len(value) != 6:
|
||||
raise vol.Invalid("Cron expression must consist of exactly 6 space-separated parts, "
|
||||
"not {}".format(len(value)))
|
||||
seconds, minutes, hours, days_of_month, months, days_of_week = value
|
||||
return {
|
||||
CONF_SECONDS: validate_cron_seconds(seconds),
|
||||
CONF_MINUTES: validate_cron_minutes(minutes),
|
||||
CONF_HOURS: validate_cron_hours(hours),
|
||||
CONF_DAYS_OF_MONTH: validate_cron_days_of_month(days_of_month),
|
||||
CONF_MONTHS: validate_cron_months(months),
|
||||
CONF_DAYS_OF_WEEK: validate_cron_days_of_week(days_of_week),
|
||||
}
|
||||
|
||||
|
||||
def validate_cron_keys(value):
|
||||
if CONF_CRON in value:
|
||||
for key in value.keys():
|
||||
if key in CRON_KEYS:
|
||||
raise vol.Invalid("Cannot use option {} when cron: is specified.".format(key))
|
||||
cron_ = value[CONF_CRON]
|
||||
value = {x: value[x] for x in value if x != CONF_CRON}
|
||||
value.update(cron_)
|
||||
return value
|
||||
return cv.has_at_least_one_key(*CRON_KEYS)(value)
|
||||
|
||||
|
||||
def validate_tz(value):
|
||||
value = cv.string_strict(value)
|
||||
|
||||
try:
|
||||
import pytz
|
||||
|
||||
return convert_tz(pytz.timezone(value))
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return value
|
||||
|
||||
|
||||
TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_TIMEZONE, default=detect_tz): validate_tz,
|
||||
vol.Optional(CONF_ON_TIME): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(CronTrigger),
|
||||
vol.Optional(CONF_SECONDS): validate_cron_seconds,
|
||||
vol.Optional(CONF_MINUTES): validate_cron_minutes,
|
||||
vol.Optional(CONF_HOURS): validate_cron_hours,
|
||||
vol.Optional(CONF_DAYS_OF_MONTH): validate_cron_days_of_month,
|
||||
vol.Optional(CONF_MONTHS): validate_cron_months,
|
||||
vol.Optional(CONF_DAYS_OF_WEEK): validate_cron_days_of_week,
|
||||
vol.Optional(CONF_CRON): validate_cron_raw,
|
||||
}, validate_cron_keys),
|
||||
})
|
||||
|
||||
|
||||
def setup_time_core_(time_var, config):
|
||||
add(time_var.set_timezone(config[CONF_TIMEZONE]))
|
||||
|
||||
for conf in config.get(CONF_ON_TIME, []):
|
||||
rhs = App.register_component(time_var.Pmake_cron_trigger())
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
|
||||
seconds = conf.get(CONF_SECONDS, [x for x in range(0, 61)])
|
||||
add(trigger.add_seconds(ArrayInitializer(*seconds, multiline=False)))
|
||||
|
||||
minutes = conf.get(CONF_MINUTES, [x for x in range(0, 60)])
|
||||
add(trigger.add_minutes(ArrayInitializer(*minutes, multiline=False)))
|
||||
|
||||
hours = conf.get(CONF_HOURS, [x for x in range(0, 24)])
|
||||
add(trigger.add_hours(ArrayInitializer(*hours, multiline=False)))
|
||||
|
||||
days_of_month = conf.get(CONF_DAYS_OF_MONTH, [x for x in range(1, 32)])
|
||||
add(trigger.add_days_of_month(ArrayInitializer(*days_of_month, multiline=False)))
|
||||
|
||||
months = conf.get(CONF_MONTHS, [x for x in range(1, 13)])
|
||||
add(trigger.add_months(ArrayInitializer(*months, multiline=False)))
|
||||
|
||||
days_of_week = conf.get(CONF_DAYS_OF_WEEK, [x for x in range(1, 8)])
|
||||
add(trigger.add_days_of_week(ArrayInitializer(*days_of_week, multiline=False)))
|
||||
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
|
||||
def setup_time(time_var, config):
|
||||
CORE.add_job(setup_time_core_, time_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TIME'
|
||||
25
esphomeyaml/components/time/homeassistant.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from esphomeyaml.components import time as time_
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App
|
||||
|
||||
|
||||
DEPENDENCIES = ['api']
|
||||
|
||||
HomeAssistantTime = time_.time_ns.class_('HomeAssistantTime', time_.RealTimeClockComponent)
|
||||
|
||||
PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(HomeAssistantTime),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_homeassistant_time_component()
|
||||
ha_time = Pvariable(config[CONF_ID], rhs)
|
||||
time_.setup_time(ha_time, config)
|
||||
setup_component(ha_time, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HOMEASSISTANT_TIME'
|
||||
30
esphomeyaml/components/time/sntp.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import time as time_
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_SERVERS
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App
|
||||
|
||||
SNTPComponent = time_.time_ns.class_('SNTPComponent', time_.RealTimeClockComponent)
|
||||
|
||||
PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(SNTPComponent),
|
||||
vol.Optional(CONF_SERVERS): vol.All(cv.ensure_list(cv.domain), vol.Length(min=1, max=3)),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_sntp_component()
|
||||
sntp = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_SERVERS in config:
|
||||
servers = config[CONF_SERVERS]
|
||||
servers += [''] * (3 - len(servers))
|
||||
add(sntp.set_servers(*servers))
|
||||
|
||||
time_.setup_time(sntp, config)
|
||||
setup_component(sntp, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SNTP_COMPONENT'
|
||||
31
esphomeyaml/components/uart.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN
|
||||
from esphomeyaml.cpp_generator import Pvariable
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import App, Component, esphomelib_ns
|
||||
|
||||
UARTComponent = esphomelib_ns.class_('UARTComponent', Component)
|
||||
UARTDevice = esphomelib_ns.class_('UARTDevice')
|
||||
MULTI_CONF = True
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(UARTComponent),
|
||||
vol.Optional(CONF_TX_PIN): pins.output_pin,
|
||||
vol.Optional(CONF_RX_PIN): pins.input_pin,
|
||||
vol.Required(CONF_BAUD_RATE): cv.positive_int,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
tx = config.get(CONF_TX_PIN, -1)
|
||||
rx = config.get(CONF_RX_PIN, -1)
|
||||
rhs = App.init_uart(tx, rx, config[CONF_BAUD_RATE])
|
||||
var = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
setup_component(var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_UART'
|
||||
@@ -1,22 +1,20 @@
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_PORT, CONF_JS_URL, CONF_CSS_URL, CONF_ID, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
|
||||
from esphomeyaml.const import CONF_CSS_URL, CONF_ID, CONF_JS_URL, CONF_PORT
|
||||
from esphomeyaml.core import CORE
|
||||
from esphomeyaml.cpp_generator import Pvariable, add
|
||||
from esphomeyaml.cpp_helpers import setup_component
|
||||
from esphomeyaml.cpp_types import esphomelib_ns, StoringController, Component, App
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
WebServer = esphomelib_ns.WebServer
|
||||
WebServer = esphomelib_ns.class_('WebServer', Component, StoringController)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(WebServer),
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
vol.Optional(CONF_CSS_URL): cv.string,
|
||||
vol.Optional(CONF_JS_URL): cv.string,
|
||||
})
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -27,11 +25,15 @@ def to_code(config):
|
||||
if CONF_JS_URL in config:
|
||||
add(web_server.set_js_url(config[CONF_JS_URL]))
|
||||
|
||||
setup_component(web_server, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_WEB_SERVER'
|
||||
|
||||
|
||||
def lib_deps(config):
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
return 'FS'
|
||||
return ''
|
||||
deps = []
|
||||
if CORE.is_esp32:
|
||||
deps.append('FS')
|
||||
deps.append('ESP Async WebServer@1.1.1')
|
||||
return deps
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \
|
||||
CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, \
|
||||
CONF_STATIC_IP, CONF_SUBNET, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, esphomelib_ns, global_ns
|
||||
CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_POWER_SAVE_MODE, \
|
||||
CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, CONF_NETWORKS, CONF_BSSID, \
|
||||
CONF_FAST_CONNECT
|
||||
from esphomeyaml.core import CORE, HexInt
|
||||
from esphomeyaml.cpp_generator import Pvariable, StructInitializer, add, variable, ArrayInitializer
|
||||
from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns
|
||||
|
||||
|
||||
IPAddress = global_ns.class_('IPAddress')
|
||||
ManualIP = esphomelib_ns.struct('ManualIP')
|
||||
WiFiComponent = esphomelib_ns.class_('WiFiComponent', Component)
|
||||
WiFiAP = esphomelib_ns.struct('WiFiAP')
|
||||
|
||||
WiFiPowerSaveMode = esphomelib_ns.enum('WiFiPowerSaveMode')
|
||||
WIFI_POWER_SAVE_MODES = {
|
||||
'NONE': WiFiPowerSaveMode.WIFI_POWER_SAVE_NONE,
|
||||
'LIGHT': WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT,
|
||||
'HIGH': WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH,
|
||||
}
|
||||
|
||||
|
||||
def validate_password(value):
|
||||
@@ -14,8 +29,17 @@ def validate_password(value):
|
||||
return value
|
||||
if len(value) < 8:
|
||||
raise vol.Invalid(u"WPA password must be at least 8 characters long")
|
||||
if len(value) > 63:
|
||||
raise vol.Invalid(u"WPA password must be at most 63 characters long")
|
||||
if len(value) > 64:
|
||||
raise vol.Invalid(u"WPA password must be at most 64 characters long")
|
||||
return value
|
||||
|
||||
|
||||
def validate_channel(value):
|
||||
value = cv.positive_int(value)
|
||||
if value < 1:
|
||||
raise vol.Invalid("Minimum WiFi channel is 1")
|
||||
if value > 14:
|
||||
raise vol.Invalid("Maximum WiFi channel is 14")
|
||||
return value
|
||||
|
||||
|
||||
@@ -30,34 +54,76 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({
|
||||
vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4,
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
IPAddress = global_ns.IPAddress
|
||||
ManualIP = esphomelib_ns.ManualIP
|
||||
WiFiComponent = esphomelib_ns.WiFiComponent
|
||||
WIFI_NETWORK_BASE = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(WiFiAP),
|
||||
vol.Optional(CONF_SSID): cv.ssid,
|
||||
vol.Optional(CONF_PASSWORD): validate_password,
|
||||
vol.Optional(CONF_CHANNEL): validate_channel,
|
||||
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend({
|
||||
|
||||
})
|
||||
|
||||
WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend({
|
||||
vol.Optional(CONF_BSSID): cv.mac_address,
|
||||
})
|
||||
|
||||
|
||||
def validate(config):
|
||||
if CONF_PASSWORD in config and CONF_SSID not in config:
|
||||
raise vol.Invalid("Cannot have WiFi password without SSID!")
|
||||
|
||||
if CONF_SSID in config:
|
||||
network = {CONF_SSID: config.pop(CONF_SSID)}
|
||||
if CONF_PASSWORD in config:
|
||||
network[CONF_PASSWORD] = config.pop(CONF_PASSWORD)
|
||||
if CONF_NETWORKS in config:
|
||||
raise vol.Invalid("You cannot use the 'ssid:' option together with 'networks:'. Please "
|
||||
"copy your network into the 'networks:' key")
|
||||
config[CONF_NETWORKS] = cv.ensure_list(WIFI_NETWORK_STA)(network)
|
||||
|
||||
if (CONF_NETWORKS not in config) and (CONF_AP not in config):
|
||||
raise vol.Invalid("Please specify at least an SSID or an Access Point "
|
||||
"to create.")
|
||||
|
||||
if config.get(CONF_FAST_CONNECT, False):
|
||||
networks = config.get(CONF_NETWORKS, [])
|
||||
if not networks:
|
||||
raise vol.Invalid("At least one network required for fast_connect!")
|
||||
if len(networks) != 1:
|
||||
raise vol.Invalid("Fast connect can only be used with one network!")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(WiFiComponent),
|
||||
vol.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA),
|
||||
|
||||
vol.Optional(CONF_SSID): cv.ssid,
|
||||
vol.Optional(CONF_PASSWORD): validate_password,
|
||||
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
|
||||
vol.Optional(CONF_AP): vol.Schema({
|
||||
vol.Required(CONF_SSID): cv.ssid,
|
||||
vol.Optional(CONF_PASSWORD): validate_password,
|
||||
vol.Optional(CONF_CHANNEL): vol.All(cv.positive_int, vol.Range(min=1, max=14)),
|
||||
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
|
||||
}),
|
||||
|
||||
vol.Optional(CONF_AP): WIFI_NETWORK_AP,
|
||||
vol.Optional(CONF_HOSTNAME): cv.hostname,
|
||||
vol.Optional(CONF_DOMAIN, default='.local'): cv.domainname,
|
||||
})
|
||||
vol.Optional(CONF_DOMAIN, default='.local'): cv.domain_name,
|
||||
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_POWER_SAVE_MODE): cv.one_of(*WIFI_POWER_SAVE_MODES, upper=True),
|
||||
vol.Optional(CONF_FAST_CONNECT): cv.boolean,
|
||||
}), validate)
|
||||
|
||||
|
||||
def safe_ip(ip):
|
||||
if ip is None:
|
||||
return None
|
||||
return IPAddress(0, 0, 0, 0)
|
||||
return IPAddress(*ip.args)
|
||||
|
||||
|
||||
def manual_ip(config):
|
||||
if config is None:
|
||||
return None
|
||||
return StructInitializer(
|
||||
ManualIP,
|
||||
('static_ip', safe_ip(config[CONF_STATIC_IP])),
|
||||
@@ -68,33 +134,56 @@ def manual_ip(config):
|
||||
)
|
||||
|
||||
|
||||
def wifi_network(config, static_ip):
|
||||
ap = variable(config[CONF_ID], WiFiAP())
|
||||
if CONF_SSID in config:
|
||||
add(ap.set_ssid(config[CONF_SSID]))
|
||||
if CONF_PASSWORD in config:
|
||||
add(ap.set_password(config[CONF_PASSWORD]))
|
||||
if CONF_BSSID in config:
|
||||
bssid = [HexInt(i) for i in config[CONF_BSSID].parts]
|
||||
add(ap.set_bssid(ArrayInitializer(*bssid, multiline=False)))
|
||||
if CONF_CHANNEL in config:
|
||||
add(ap.set_channel(config[CONF_CHANNEL]))
|
||||
if static_ip is not None:
|
||||
add(ap.set_manual_ip(manual_ip(static_ip)))
|
||||
|
||||
return ap
|
||||
|
||||
|
||||
def get_upload_host(config):
|
||||
if CONF_MANUAL_IP in config:
|
||||
return str(config[CONF_MANUAL_IP][CONF_STATIC_IP])
|
||||
hostname = config.get(CONF_HOSTNAME) or CORE.name
|
||||
return hostname + config[CONF_DOMAIN]
|
||||
|
||||
|
||||
def to_code(config):
|
||||
sta = CONF_SSID in config
|
||||
ap = CONF_AP in config
|
||||
if sta:
|
||||
rhs = App.init_wifi(config[CONF_SSID], config.get(CONF_PASSWORD))
|
||||
else:
|
||||
rhs = App.init_wifi()
|
||||
rhs = App.init_wifi()
|
||||
wifi = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if sta and CONF_MANUAL_IP in config:
|
||||
add(wifi.set_sta_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
|
||||
for network in config.get(CONF_NETWORKS, []):
|
||||
add(wifi.add_sta(wifi_network(network, config.get(CONF_MANUAL_IP))))
|
||||
|
||||
if ap:
|
||||
conf = config[CONF_AP]
|
||||
password = config.get(CONF_PASSWORD)
|
||||
if password is None and CONF_CHANNEL in conf:
|
||||
password = u""
|
||||
add(wifi.set_ap(conf[CONF_SSID], password, conf.get(CONF_CHANNEL)))
|
||||
|
||||
if CONF_MANUAL_IP in conf:
|
||||
add(wifi.set_ap_manual_ip(manual_ip(conf[CONF_MANUAL_IP])))
|
||||
if CONF_AP in config:
|
||||
add(wifi.set_ap(wifi_network(config[CONF_AP], config.get(CONF_MANUAL_IP))))
|
||||
|
||||
if CONF_HOSTNAME in config:
|
||||
add(wifi.set_hostname(config[CONF_HOSTNAME]))
|
||||
|
||||
if CONF_REBOOT_TIMEOUT in config:
|
||||
add(wifi.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||
|
||||
if CONF_POWER_SAVE_MODE in config:
|
||||
add(wifi.set_power_save_mode(WIFI_POWER_SAVE_MODES[config[CONF_POWER_SAVE_MODE]]))
|
||||
|
||||
if CONF_FAST_CONNECT in config:
|
||||
add(wifi.set_fast_connect(config[CONF_FAST_CONNECT]))
|
||||
|
||||
|
||||
def lib_deps(config):
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
if CORE.is_esp8266:
|
||||
return 'ESP8266WiFi'
|
||||
return None
|
||||
if CORE.is_esp32:
|
||||
return None
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
{
|
||||
"name": "esphomeyaml",
|
||||
"version": "1.6.2",
|
||||
"version": "1.10.1",
|
||||
"slug": "esphomeyaml",
|
||||
"description": "esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices.",
|
||||
"description": "esphomeyaml Hass.io add-on for intelligently managing all your ESP8266/ESP32 devices.",
|
||||
"url": "https://esphomelib.com/esphomeyaml/index.html",
|
||||
"startup": "application",
|
||||
"webui": "http://[HOST]:[PORT:6052]",
|
||||
"startup": "application",
|
||||
"arch": [
|
||||
"amd64",
|
||||
"armhf",
|
||||
"i386"
|
||||
],
|
||||
"hassio_api": true,
|
||||
"auth_api": true,
|
||||
"hassio_role": "default",
|
||||
"homeassistant_api": false,
|
||||
"host_network": true,
|
||||
"boot": "auto",
|
||||
"ports": {
|
||||
"6052/tcp": 6052,
|
||||
"6053/tcp": 6053
|
||||
},
|
||||
"auto_uart": true,
|
||||
"map": [
|
||||
"ssl",
|
||||
"config:rw"
|
||||
],
|
||||
"options": {},
|
||||
"environment": {
|
||||
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
|
||||
"options": {
|
||||
"ssl": false,
|
||||
"certfile": "fullchain.pem",
|
||||
"keyfile": "privkey.pem",
|
||||
"port": 6052
|
||||
},
|
||||
"schema": {
|
||||
"ssl": "bool",
|
||||
"certfile": "str",
|
||||
"keyfile": "str",
|
||||
"port": "int",
|
||||
"leave_front_door_open": "bool?",
|
||||
"esphomeyaml_version": "str?"
|
||||
},
|
||||
"schema": {},
|
||||
"image": "ottowinter/esphomeyaml-hassio-{arch}"
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,28 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core, yaml_util
|
||||
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
|
||||
CONF_LIBRARY_URI, \
|
||||
CONF_NAME, CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, ESP_PLATFORMS, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, add, color
|
||||
from esphomeyaml import core, core_config, yaml_util
|
||||
from esphomeyaml.components import substitutions
|
||||
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, ESP_PLATFORMS
|
||||
from esphomeyaml.core import CORE, EsphomeyamlError
|
||||
from esphomeyaml.helpers import color, indent
|
||||
from esphomeyaml.py_compat import text_type
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
# pylint: disable=unused-import, wrong-import-order
|
||||
from typing import List, Optional, Tuple, Union # noqa
|
||||
from esphomeyaml.core import ConfigType # noqa
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.6.2'
|
||||
|
||||
BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout']
|
||||
|
||||
CORE_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.valid_name,
|
||||
vol.Required(CONF_PLATFORM): cv.string,
|
||||
vol.Required(CONF_BOARD): cv.string,
|
||||
vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string,
|
||||
vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean,
|
||||
vol.Optional(CONF_USE_BUILD_FLAGS, default=True): cv.boolean,
|
||||
vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, cv.one_of(*BUILD_FLASH_MODES)),
|
||||
})
|
||||
|
||||
REQUIRED_COMPONENTS = [
|
||||
CONF_ESPHOMEYAML, CONF_WIFI
|
||||
]
|
||||
|
||||
_COMPONENT_CACHE = {}
|
||||
_ALL_COMPONENTS = []
|
||||
|
||||
|
||||
def core_to_code(config):
|
||||
add(App.set_name(config[CONF_NAME]))
|
||||
|
||||
|
||||
def get_component(domain):
|
||||
@@ -51,7 +32,7 @@ def get_component(domain):
|
||||
path = 'esphomeyaml.components.{}'.format(domain)
|
||||
try:
|
||||
module = importlib.import_module(path)
|
||||
except ImportError as err:
|
||||
except (ImportError, ValueError) as err:
|
||||
_LOGGER.debug(err)
|
||||
else:
|
||||
_COMPONENT_CACHE[domain] = module
|
||||
@@ -70,11 +51,16 @@ def is_platform_component(component):
|
||||
|
||||
|
||||
def iter_components(config):
|
||||
for domain, conf in config.iteritems():
|
||||
for domain, conf in config.items():
|
||||
if domain == CONF_ESPHOMEYAML:
|
||||
yield CONF_ESPHOMEYAML, core_config, conf
|
||||
continue
|
||||
component = get_component(domain)
|
||||
yield domain, component, conf
|
||||
if getattr(component, 'MULTI_CONF', False):
|
||||
for conf_ in conf:
|
||||
yield domain, component, conf_
|
||||
else:
|
||||
yield domain, component, conf
|
||||
if is_platform_component(component):
|
||||
for p_config in conf:
|
||||
p_name = u"{}.{}".format(domain, p_config[CONF_PLATFORM])
|
||||
@@ -82,208 +68,384 @@ def iter_components(config):
|
||||
yield p_name, platform, p_config
|
||||
|
||||
|
||||
ConfigPath = List[Union[str, int]]
|
||||
|
||||
|
||||
def _path_begins_with_(path, other): # type: (ConfigPath, ConfigPath) -> bool
|
||||
if len(path) < len(other):
|
||||
return False
|
||||
return path[:len(other)] == other
|
||||
|
||||
|
||||
def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool
|
||||
ret = _path_begins_with_(path, other)
|
||||
return ret
|
||||
|
||||
|
||||
class Config(OrderedDict):
|
||||
def __init__(self):
|
||||
super(Config, self).__init__()
|
||||
self.errors = []
|
||||
self.errors = [] # type: List[Tuple[basestring, ConfigPath]]
|
||||
self.domains = [] # type: List[Tuple[ConfigPath, basestring]]
|
||||
|
||||
def add_error(self, message, domain=None, config=None):
|
||||
if not isinstance(message, unicode):
|
||||
message = unicode(message)
|
||||
self.errors.append((message, domain, config))
|
||||
def add_error(self, message, path):
|
||||
# type: (basestring, ConfigPath) -> None
|
||||
if not isinstance(message, text_type):
|
||||
message = text_type(message)
|
||||
self.errors.append((message, path))
|
||||
|
||||
def add_domain(self, path, name):
|
||||
# type: (ConfigPath, basestring) -> None
|
||||
self.domains.append((path, name))
|
||||
|
||||
def remove_domain(self, path, name):
|
||||
self.domains.remove((path, name))
|
||||
|
||||
def lookup_domain(self, path):
|
||||
# type: (ConfigPath) -> Optional[basestring]
|
||||
best_len = 0
|
||||
best_domain = None
|
||||
for d_path, domain in self.domains:
|
||||
if len(d_path) < best_len:
|
||||
continue
|
||||
if _path_begins_with(path, d_path):
|
||||
best_len = len(d_path)
|
||||
best_domain = domain
|
||||
return best_domain
|
||||
|
||||
def is_in_error_path(self, path):
|
||||
for _, p in self.errors:
|
||||
if _path_begins_with(p, path):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_error_for_path(self, path):
|
||||
for msg, p in self.errors:
|
||||
if self.nested_item_path(p) == path:
|
||||
return msg
|
||||
return None
|
||||
|
||||
def nested_item(self, path):
|
||||
data = self
|
||||
for item_index in path:
|
||||
try:
|
||||
data = data[item_index]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return {}
|
||||
return data
|
||||
|
||||
def nested_item_path(self, path):
|
||||
data = self
|
||||
part = []
|
||||
for item_index in path:
|
||||
try:
|
||||
data = data[item_index]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return part
|
||||
part.append(item_index)
|
||||
return part
|
||||
|
||||
|
||||
def iter_ids(config, prefix=None, parent=None):
|
||||
prefix = prefix or []
|
||||
parent = parent or {}
|
||||
def iter_ids(config, path=None):
|
||||
path = path or []
|
||||
if isinstance(config, core.ID):
|
||||
yield config, prefix, parent
|
||||
yield config, path
|
||||
elif isinstance(config, core.Lambda):
|
||||
for id in config.requires_ids:
|
||||
yield id, prefix, parent
|
||||
yield id, path
|
||||
elif isinstance(config, list):
|
||||
for i, item in enumerate(config):
|
||||
for result in iter_ids(item, prefix + [str(i)], config):
|
||||
for result in iter_ids(item, path + [i]):
|
||||
yield result
|
||||
elif isinstance(config, dict):
|
||||
for key, value in config.iteritems():
|
||||
for result in iter_ids(value, prefix + [str(key)], config):
|
||||
for key, value in config.items():
|
||||
for result in iter_ids(value, path + [key]):
|
||||
yield result
|
||||
|
||||
|
||||
def do_id_pass(result):
|
||||
declare_ids = []
|
||||
searching_ids = []
|
||||
for id, prefix, config in iter_ids(result):
|
||||
def do_id_pass(result): # type: (Config) -> None
|
||||
from esphomeyaml.cpp_generator import MockObjClass
|
||||
|
||||
declare_ids = [] # type: List[Tuple[core.ID, ConfigPath]]
|
||||
searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]]
|
||||
for id, path in iter_ids(result):
|
||||
if id.is_declaration:
|
||||
if id.id is not None and any(v[0].id == id.id for v in declare_ids):
|
||||
result.add_error("ID {} redefined!".format(id.id), '.'.join(prefix), config)
|
||||
result.add_error(u"ID {} redefined!".format(id.id), path)
|
||||
continue
|
||||
declare_ids.append((id, prefix, config))
|
||||
declare_ids.append((id, path))
|
||||
else:
|
||||
searching_ids.append((id, prefix, config))
|
||||
searching_ids.append((id, path))
|
||||
# Resolve default ids after manual IDs
|
||||
for id, _, _ in declare_ids:
|
||||
for id, _ in declare_ids:
|
||||
id.resolve([v[0].id for v in declare_ids])
|
||||
|
||||
# Check searched IDs
|
||||
for id, prefix, config in searching_ids:
|
||||
if id.id is not None and not any(v[0].id == id.id for v in declare_ids):
|
||||
result.add_error("Couldn't find ID {}".format(id.id), '.'.join(prefix), config)
|
||||
for id, path in searching_ids:
|
||||
if id.id is not None:
|
||||
# manually declared
|
||||
match = next((v[0] for v in declare_ids if v[0].id == id.id), None)
|
||||
if match is None:
|
||||
# No declared ID with this name
|
||||
result.add_error("Couldn't find ID '{}'".format(id.id), path)
|
||||
continue
|
||||
if not isinstance(match.type, MockObjClass) or not isinstance(id.type, MockObjClass):
|
||||
continue
|
||||
if not match.type.inherits_from(id.type):
|
||||
result.add_error("ID '{}' of type {} doesn't inherit from {}. Please double check "
|
||||
"your ID is pointing to the correct value"
|
||||
"".format(id.id, match.type, id.type), path)
|
||||
|
||||
if id.id is None and id.type is not None:
|
||||
id.id = next((v[0].id for v in declare_ids if v[0].type == id.type), None)
|
||||
if id.id is None:
|
||||
result.add_error("Couldn't resolve ID for type {}".format(id.type),
|
||||
'.'.join(prefix), config)
|
||||
for v in declare_ids:
|
||||
if v[0] is None or not isinstance(v[0].type, MockObjClass):
|
||||
continue
|
||||
inherits = v[0].type.inherits_from(id.type)
|
||||
if inherits:
|
||||
id.id = v[0].id
|
||||
break
|
||||
else:
|
||||
result.add_error("Couldn't resolve ID for type '{}'".format(id.type), path)
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
global _ALL_COMPONENTS
|
||||
|
||||
for req in REQUIRED_COMPONENTS:
|
||||
if req not in config:
|
||||
raise ESPHomeYAMLError("Component {} is required for esphomeyaml.".format(req))
|
||||
|
||||
_ALL_COMPONENTS = list(config.keys())
|
||||
|
||||
result = Config()
|
||||
|
||||
def _comp_error(ex, domain, config):
|
||||
result.add_error(_format_config_error(ex, domain, config), domain, config)
|
||||
def _comp_error(ex, path):
|
||||
# type: (vol.Invalid, List[basestring]) -> None
|
||||
if isinstance(ex, vol.MultipleInvalid):
|
||||
errors = ex.errors
|
||||
else:
|
||||
errors = [ex]
|
||||
|
||||
try:
|
||||
result[CONF_ESPHOMEYAML] = CORE_SCHEMA(config[CONF_ESPHOMEYAML])
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, CONF_ESPHOMEYAML, config)
|
||||
for e in errors:
|
||||
path_ = path + e.path
|
||||
domain = result.lookup_domain(path_) or ''
|
||||
result.add_error(_format_vol_invalid(e, config, path, domain), path_)
|
||||
|
||||
for domain, conf in config.iteritems():
|
||||
if domain == CONF_ESPHOMEYAML:
|
||||
skip_paths = list() # type: List[ConfigPath]
|
||||
|
||||
# Step 1: Load everything
|
||||
result.add_domain([CONF_ESPHOMEYAML], CONF_ESPHOMEYAML)
|
||||
result[CONF_ESPHOMEYAML] = config[CONF_ESPHOMEYAML]
|
||||
|
||||
for domain, conf in config.items():
|
||||
domain = str(domain)
|
||||
if domain == CONF_ESPHOMEYAML or domain.startswith(u'.'):
|
||||
skip_paths.append([domain])
|
||||
continue
|
||||
result.add_domain([domain], domain)
|
||||
result[domain] = conf
|
||||
if conf is None:
|
||||
conf = {}
|
||||
result[domain] = conf = {}
|
||||
component = get_component(domain)
|
||||
if component is None:
|
||||
result.add_error(u"Component not found: {}".format(domain))
|
||||
result.add_error(u"Component not found: {}".format(domain), [domain])
|
||||
skip_paths.append([domain])
|
||||
continue
|
||||
|
||||
esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS)
|
||||
if core.ESP_PLATFORM not in esp_platforms:
|
||||
result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM))
|
||||
continue
|
||||
if not isinstance(conf, list) and getattr(component, 'MULTI_CONF', False):
|
||||
result[domain] = conf = [conf]
|
||||
|
||||
success = True
|
||||
dependencies = getattr(component, 'DEPENDENCIES', [])
|
||||
for dependency in dependencies:
|
||||
if dependency not in _ALL_COMPONENTS:
|
||||
result.add_error(u"Component {} requires component {}".format(domain, dependency))
|
||||
if dependency not in config:
|
||||
result.add_error(u"Component {} requires component {}".format(domain, dependency),
|
||||
[domain])
|
||||
success = False
|
||||
if not success:
|
||||
skip_paths.append([domain])
|
||||
continue
|
||||
|
||||
if hasattr(component, 'CONFIG_SCHEMA'):
|
||||
try:
|
||||
validated = component.CONFIG_SCHEMA(conf)
|
||||
result[domain] = validated
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, domain, conf)
|
||||
continue
|
||||
success = True
|
||||
conflicts_with = getattr(component, 'CONFLICTS_WITH', [])
|
||||
for conflict in conflicts_with:
|
||||
if conflict in config:
|
||||
result.add_error(u"Component {} cannot be used together with component {}"
|
||||
u"".format(domain, conflict), [domain])
|
||||
success = False
|
||||
if not success:
|
||||
skip_paths.append([domain])
|
||||
continue
|
||||
|
||||
esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS)
|
||||
if CORE.esp_platform not in esp_platforms:
|
||||
result.add_error(u"Component {} doesn't support {}.".format(domain, CORE.esp_platform),
|
||||
[domain])
|
||||
skip_paths.append([domain])
|
||||
continue
|
||||
|
||||
if not hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
continue
|
||||
|
||||
platforms = []
|
||||
for p_config in conf:
|
||||
result.remove_domain([domain], domain)
|
||||
|
||||
if not isinstance(conf, list) and conf:
|
||||
result[domain] = conf = [conf]
|
||||
|
||||
for i, p_config in enumerate(conf):
|
||||
if not isinstance(p_config, dict):
|
||||
result.add_error(u"Platform schemas must have 'platform:' key")
|
||||
result.add_error(u"Platform schemas must have 'platform:' key", [domain, i])
|
||||
skip_paths.append([domain, i])
|
||||
continue
|
||||
p_name = p_config.get(u'platform')
|
||||
p_name = p_config.get('platform')
|
||||
if p_name is None:
|
||||
result.add_error(u"No platform specified for {}".format(domain))
|
||||
result.add_error(u"No platform specified for {}".format(domain), [domain, i])
|
||||
skip_paths.append([domain, i])
|
||||
continue
|
||||
p_domain = u'{}.{}'.format(domain, p_name)
|
||||
result.add_domain([domain, i], p_domain)
|
||||
platform = get_platform(domain, p_name)
|
||||
if platform is None:
|
||||
result.add_error(u"Platform not found: {}.{}")
|
||||
result.add_error(u"Platform not found: '{}'".format(p_domain), [domain, i])
|
||||
skip_paths.append([domain, i])
|
||||
continue
|
||||
|
||||
success = True
|
||||
dependencies = getattr(platform, 'DEPENDENCIES', [])
|
||||
for dependency in dependencies:
|
||||
if dependency not in _ALL_COMPONENTS:
|
||||
result.add_error(u"Platform {}.{} requires component {}".format(domain, p_name,
|
||||
dependency))
|
||||
if dependency not in config:
|
||||
result.add_error(u"Platform {} requires component {}"
|
||||
u"".format(p_domain, dependency), [domain, i])
|
||||
success = False
|
||||
if not success:
|
||||
skip_paths.append([domain, i])
|
||||
continue
|
||||
|
||||
success = True
|
||||
conflicts_with = getattr(platform, 'CONFLICTS_WITH', [])
|
||||
for conflict in conflicts_with:
|
||||
if conflict in config:
|
||||
result.add_error(u"Platform {} cannot be used together with component {}"
|
||||
u"".format(p_domain, conflict), [domain, i])
|
||||
success = False
|
||||
if not success:
|
||||
skip_paths.append([domain, i])
|
||||
continue
|
||||
|
||||
esp_platforms = getattr(platform, 'ESP_PLATFORMS', ESP_PLATFORMS)
|
||||
if core.ESP_PLATFORM not in esp_platforms:
|
||||
result.add_error(
|
||||
u"Platform {}.{} doesn't support {}.".format(domain, p_name, core.ESP_PLATFORM))
|
||||
if CORE.esp_platform not in esp_platforms:
|
||||
result.add_error(u"Platform {} doesn't support {}."
|
||||
u"".format(p_domain, CORE.esp_platform), [domain, i])
|
||||
skip_paths.append([domain, i])
|
||||
continue
|
||||
|
||||
if hasattr(platform, u'PLATFORM_SCHEMA'):
|
||||
# Step 2: Validate configuration
|
||||
try:
|
||||
result[CONF_ESPHOMEYAML] = core_config.CONFIG_SCHEMA(result[CONF_ESPHOMEYAML])
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, [CONF_ESPHOMEYAML])
|
||||
|
||||
for domain, conf in result.items():
|
||||
domain = str(domain)
|
||||
if [domain] in skip_paths:
|
||||
continue
|
||||
component = get_component(domain)
|
||||
|
||||
if hasattr(component, 'CONFIG_SCHEMA'):
|
||||
multi_conf = getattr(component, 'MULTI_CONF', False)
|
||||
|
||||
if multi_conf:
|
||||
for i, conf_ in enumerate(conf):
|
||||
try:
|
||||
validated = component.CONFIG_SCHEMA(conf_)
|
||||
result[domain][i] = validated
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, [domain, i])
|
||||
else:
|
||||
try:
|
||||
validated = component.CONFIG_SCHEMA(conf)
|
||||
result[domain] = validated
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, [domain])
|
||||
continue
|
||||
|
||||
if not hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
continue
|
||||
|
||||
for i, p_config in enumerate(conf):
|
||||
if [domain, i] in skip_paths:
|
||||
continue
|
||||
p_name = p_config['platform']
|
||||
platform = get_platform(domain, p_name)
|
||||
|
||||
if hasattr(platform, 'PLATFORM_SCHEMA'):
|
||||
try:
|
||||
p_validated = platform.PLATFORM_SCHEMA(p_config)
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, u'{}.{}'.format(domain, p_name), p_config)
|
||||
_comp_error(ex, [domain, i])
|
||||
continue
|
||||
platforms.append(p_validated)
|
||||
result[domain] = platforms
|
||||
result[domain][i] = p_validated
|
||||
|
||||
do_id_pass(result)
|
||||
if not result.errors:
|
||||
# Only parse IDs if no validation error. Otherwise
|
||||
# user gets confusing messages
|
||||
do_id_pass(result)
|
||||
return result
|
||||
|
||||
|
||||
REQUIRED = ['esphomeyaml', 'wifi']
|
||||
def _nested_getitem(data, path):
|
||||
for item_index in path:
|
||||
try:
|
||||
data = data[item_index]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return None
|
||||
return data
|
||||
|
||||
|
||||
def _format_config_error(ex, domain, config):
|
||||
message = u"Invalid config for [{}]: ".format(domain)
|
||||
def humanize_error(config, validation_error):
|
||||
offending_item_summary = _nested_getitem(config, validation_error.path)
|
||||
if isinstance(offending_item_summary, dict):
|
||||
try:
|
||||
offending_item_summary = json.dumps(offending_item_summary)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
validation_error = text_type(validation_error)
|
||||
m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error)
|
||||
if m is not None:
|
||||
validation_error = m.group(1)
|
||||
validation_error = validation_error.strip()
|
||||
if not validation_error.endswith(u'.'):
|
||||
validation_error += u'.'
|
||||
if offending_item_summary is None:
|
||||
return validation_error
|
||||
return u"{} Got '{}'".format(validation_error, offending_item_summary)
|
||||
|
||||
|
||||
def _format_vol_invalid(ex, config, path, domain):
|
||||
# type: (vol.Invalid, ConfigType, ConfigPath, basestring) -> unicode
|
||||
message = u''
|
||||
if u'extra keys not allowed' in ex.error_message:
|
||||
message += u'[{}] is an invalid option for [{}]. Check: {}->{}.' \
|
||||
.format(ex.path[-1], domain, domain,
|
||||
u'->'.join(str(m) for m in ex.path))
|
||||
try:
|
||||
paren = ex.path[-2]
|
||||
except IndexError:
|
||||
paren = domain
|
||||
message += u'[{}] is an invalid option for [{}].'.format(ex.path[-1], paren)
|
||||
elif u'required key not provided' in ex.error_message:
|
||||
try:
|
||||
paren = ex.path[-2]
|
||||
except IndexError:
|
||||
paren = domain
|
||||
message += u"'{}' is a required option for [{}].".format(ex.path[-1], paren)
|
||||
else:
|
||||
message += u'{}.'.format(humanize_error(config, ex))
|
||||
|
||||
if isinstance(config, list):
|
||||
return message
|
||||
|
||||
domain_config = config.get(domain, config)
|
||||
message += u" (See {}, line {}). ".format(
|
||||
getattr(domain_config, '__config_file__', '?'),
|
||||
getattr(domain_config, '__line__', '?'))
|
||||
message += humanize_error(_nested_getitem(config, path), ex)
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def load_config(path):
|
||||
def load_config():
|
||||
try:
|
||||
config = yaml_util.load_yaml(path)
|
||||
config = yaml_util.load_yaml(CORE.config_path)
|
||||
except OSError:
|
||||
raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path))
|
||||
core.RAW_CONFIG = config
|
||||
|
||||
if CONF_ESPHOMEYAML not in config:
|
||||
raise ESPHomeYAMLError(u"No esphomeyaml section in config")
|
||||
core_conf = config[CONF_ESPHOMEYAML]
|
||||
if CONF_PLATFORM not in core_conf:
|
||||
raise ESPHomeYAMLError("esphomeyaml.platform not specified.")
|
||||
esp_platform = unicode(core_conf[CONF_PLATFORM])
|
||||
esp_platform = esp_platform.upper()
|
||||
if '8266' in esp_platform:
|
||||
esp_platform = ESP_PLATFORM_ESP8266
|
||||
if '32' in esp_platform:
|
||||
esp_platform = ESP_PLATFORM_ESP32
|
||||
core.ESP_PLATFORM = esp_platform
|
||||
if CONF_BOARD not in core_conf:
|
||||
raise ESPHomeYAMLError("esphomeyaml.board not specified.")
|
||||
core.BOARD = unicode(core_conf[CONF_BOARD])
|
||||
core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True))
|
||||
raise EsphomeyamlError(u"Invalid YAML at {}".format(CORE.config_path))
|
||||
CORE.raw_config = config
|
||||
config = substitutions.do_substitution_pass(config)
|
||||
core_config.preload_core_config(config)
|
||||
|
||||
try:
|
||||
result = validate_config(config)
|
||||
except ESPHomeYAMLError:
|
||||
except EsphomeyamlError:
|
||||
raise
|
||||
except Exception:
|
||||
_LOGGER.error(u"Unexpected exception while reading configuration:")
|
||||
@@ -292,59 +454,151 @@ def load_config(path):
|
||||
return result
|
||||
|
||||
|
||||
def line_info(obj, **kwargs):
|
||||
def line_info(obj, highlight=True):
|
||||
"""Display line config source."""
|
||||
if not highlight:
|
||||
return None
|
||||
if hasattr(obj, '__config_file__'):
|
||||
return color('cyan', "[source {}:{}]"
|
||||
.format(obj.__config_file__, obj.__line__ or '?'),
|
||||
**kwargs)
|
||||
return '?'
|
||||
.format(obj.__config_file__, obj.__line__ or '?'))
|
||||
return None
|
||||
|
||||
|
||||
def dump_dict(layer, indent_count=3, listi=False, **kwargs):
|
||||
def sort_dict_key(val):
|
||||
"""Return the dict key for sorting."""
|
||||
key = str.lower(val[0])
|
||||
return '0' if key == 'platform' else key
|
||||
|
||||
indent_str = indent_count * ' '
|
||||
if listi or isinstance(layer, list):
|
||||
indent_str = indent_str[:-1] + '-'
|
||||
if isinstance(layer, dict):
|
||||
for key, value in sorted(layer.items(), key=sort_dict_key):
|
||||
if isinstance(value, (dict, list)):
|
||||
print(indent_str, key + ':', line_info(value, **kwargs))
|
||||
dump_dict(value, indent_count + 2)
|
||||
else:
|
||||
print(indent_str, key + ':', value)
|
||||
indent_str = indent_count * ' '
|
||||
if isinstance(layer, (list, tuple)):
|
||||
for i in layer:
|
||||
if isinstance(i, dict):
|
||||
dump_dict(i, indent_count + 2, True)
|
||||
else:
|
||||
print(' ', indent_str, i)
|
||||
def _print_on_next_line(obj):
|
||||
if isinstance(obj, (list, tuple, dict)):
|
||||
return True
|
||||
if isinstance(obj, str):
|
||||
return len(obj) > 80
|
||||
if isinstance(obj, core.Lambda):
|
||||
return len(obj.value) > 80
|
||||
return False
|
||||
|
||||
|
||||
def read_config(path):
|
||||
def dump_dict(config, path, at_root=True):
|
||||
# type: (Config, ConfigPath, bool) -> Tuple[unicode, bool]
|
||||
conf = config.nested_item(path)
|
||||
ret = u''
|
||||
multiline = False
|
||||
|
||||
if at_root:
|
||||
error = config.get_error_for_path(path)
|
||||
if error is not None:
|
||||
ret += u'\n' + color('bold_red', error) + u'\n'
|
||||
|
||||
if isinstance(conf, (list, tuple)):
|
||||
multiline = True
|
||||
if not conf:
|
||||
ret += u'[]'
|
||||
multiline = False
|
||||
|
||||
for i in range(len(conf)):
|
||||
path_ = path + [i]
|
||||
error = config.get_error_for_path(path_)
|
||||
if error is not None:
|
||||
ret += u'\n' + color('bold_red', error) + u'\n'
|
||||
|
||||
sep = u'- '
|
||||
if config.is_in_error_path(path_):
|
||||
sep = color('red', sep)
|
||||
msg, _ = dump_dict(config, path_, at_root=False)
|
||||
msg = indent(msg)
|
||||
inf = line_info(config.nested_item(path_), highlight=config.is_in_error_path(path_))
|
||||
if inf is not None:
|
||||
msg = inf + u'\n' + msg
|
||||
elif msg:
|
||||
msg = msg[2:]
|
||||
ret += sep + msg + u'\n'
|
||||
elif isinstance(conf, dict):
|
||||
multiline = True
|
||||
if not conf:
|
||||
ret += u'{}'
|
||||
multiline = False
|
||||
|
||||
for k in conf.keys():
|
||||
path_ = path + [k]
|
||||
error = config.get_error_for_path(path_)
|
||||
if error is not None:
|
||||
ret += u'\n' + color('bold_red', error) + u'\n'
|
||||
|
||||
st = u'{}: '.format(k)
|
||||
if config.is_in_error_path(path_):
|
||||
st = color('red', st)
|
||||
msg, m = dump_dict(config, path_, at_root=False)
|
||||
|
||||
inf = line_info(config.nested_item(path_), highlight=config.is_in_error_path(path_))
|
||||
if m:
|
||||
msg = u'\n' + indent(msg)
|
||||
|
||||
if inf is not None:
|
||||
if m:
|
||||
msg = u' ' + inf + msg
|
||||
else:
|
||||
msg = msg + u' ' + inf
|
||||
ret += st + msg + u'\n'
|
||||
elif isinstance(conf, str):
|
||||
if not conf:
|
||||
conf += u"''"
|
||||
|
||||
if len(conf) > 80:
|
||||
conf = u'|-\n' + indent(conf)
|
||||
error = config.get_error_for_path(path)
|
||||
col = 'bold_red' if error else 'white'
|
||||
ret += color(col, text_type(conf))
|
||||
elif isinstance(conf, core.Lambda):
|
||||
conf = u'!lambda |-\n' + indent(text_type(conf.value))
|
||||
error = config.get_error_for_path(path)
|
||||
col = 'bold_red' if error else 'white'
|
||||
ret += color(col, conf)
|
||||
elif conf is None:
|
||||
pass
|
||||
else:
|
||||
error = config.get_error_for_path(path)
|
||||
col = 'bold_red' if error else 'white'
|
||||
ret += color(col, text_type(conf))
|
||||
multiline = u'\n' in ret
|
||||
|
||||
return ret, multiline
|
||||
|
||||
|
||||
def strip_default_ids(config):
|
||||
if isinstance(config, list):
|
||||
to_remove = []
|
||||
for i, x in enumerate(config):
|
||||
x = config[i] = strip_default_ids(x)
|
||||
if isinstance(x, core.ID) and not x.is_manual:
|
||||
to_remove.append(x)
|
||||
for x in to_remove:
|
||||
config.remove(x)
|
||||
elif isinstance(config, dict):
|
||||
to_remove = []
|
||||
for k, v in config.items():
|
||||
v = config[k] = strip_default_ids(v)
|
||||
if isinstance(v, core.ID) and not v.is_manual:
|
||||
to_remove.append(k)
|
||||
for k in to_remove:
|
||||
config.pop(k)
|
||||
return config
|
||||
|
||||
|
||||
def read_config(verbose):
|
||||
_LOGGER.info("Reading configuration...")
|
||||
try:
|
||||
res = load_config(path)
|
||||
except ESPHomeYAMLError as err:
|
||||
res = load_config()
|
||||
except EsphomeyamlError as err:
|
||||
_LOGGER.error(u"Error while reading config: %s", err)
|
||||
return None
|
||||
excepts = {}
|
||||
for message, domain, config in res.errors:
|
||||
domain = domain or u"General Error"
|
||||
excepts.setdefault(domain, []).append(message)
|
||||
if config is not None:
|
||||
excepts[domain].append(config)
|
||||
if res.errors:
|
||||
if not verbose:
|
||||
res = strip_default_ids(res)
|
||||
|
||||
if excepts:
|
||||
print(color('bold_white', u"Failed config"))
|
||||
for domain, config in excepts.iteritems():
|
||||
print(' ', color('bold_red', domain + ':'), color('red', '', reset='red'))
|
||||
dump_dict(config, reset='red')
|
||||
print(color('reset'))
|
||||
safe_print(color('bold_red', u"Failed config"))
|
||||
safe_print('')
|
||||
for path, domain in res.domains:
|
||||
if not res.is_in_error_path(path):
|
||||
continue
|
||||
|
||||
safe_print(color('bold_red', u'{}:'.format(domain)) + u' ' +
|
||||
(line_info(res.nested_item(path)) or u''))
|
||||
safe_print(indent(dump_dict(res, path)[0]))
|
||||
return None
|
||||
return dict(**res)
|
||||
return OrderedDict(res)
|
||||
|
||||
@@ -3,25 +3,29 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import uuid as uuid_
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \
|
||||
CONF_NAME, CONF_PAYLOAD_AVAILABLE, \
|
||||
CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
||||
CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, \
|
||||
CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, ESP_PLATFORM_ESP32, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
||||
TimePeriodMilliseconds, TimePeriodSeconds
|
||||
from esphomeyaml.py_compat import text_type, string_types, integer_types
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
|
||||
positive_float = vol.All(vol.Coerce(float), vol.Range(min=0))
|
||||
zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
|
||||
float_ = vol.Coerce(float)
|
||||
positive_float = vol.All(float_, vol.Range(min=0))
|
||||
zero_to_one_float = vol.All(float_, vol.Range(min=0, max=1))
|
||||
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
|
||||
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
|
||||
|
||||
@@ -48,7 +52,7 @@ RESERVED_IDS = [
|
||||
def alphanumeric(value):
|
||||
if value is None:
|
||||
raise vol.Invalid("string value is None")
|
||||
value = unicode(value)
|
||||
value = text_type(value)
|
||||
if not value.isalnum():
|
||||
raise vol.Invalid("string value is not alphanumeric")
|
||||
return value
|
||||
@@ -67,16 +71,16 @@ def string(value):
|
||||
if isinstance(value, (dict, list)):
|
||||
raise vol.Invalid("string value cannot be dictionary or list.")
|
||||
if value is not None:
|
||||
return unicode(value)
|
||||
return text_type(value)
|
||||
raise vol.Invalid("string value is None")
|
||||
|
||||
|
||||
def string_strict(value):
|
||||
"""Strictly only allow strings."""
|
||||
if isinstance(value, (str, unicode)):
|
||||
if isinstance(value, string_types):
|
||||
return value
|
||||
raise vol.Invalid("Must be string, did you forget putting quotes "
|
||||
"around the value?")
|
||||
raise vol.Invalid("Must be string, got {}. did you forget putting quotes "
|
||||
"around the value?".format(type(value)))
|
||||
|
||||
|
||||
def icon(value):
|
||||
@@ -99,10 +103,28 @@ def boolean(value):
|
||||
return bool(value)
|
||||
|
||||
|
||||
def ensure_list(value):
|
||||
def ensure_list(*validators):
|
||||
"""Wrap value in list if it is not one."""
|
||||
if value is None:
|
||||
return []
|
||||
user = vol.All(*validators)
|
||||
|
||||
def validator(value):
|
||||
if value is None or (isinstance(value, dict) and not value):
|
||||
return []
|
||||
if not isinstance(value, list):
|
||||
return [user(value)]
|
||||
ret = []
|
||||
for i, val in enumerate(value):
|
||||
try:
|
||||
ret.append(user(val))
|
||||
except vol.Invalid as err:
|
||||
err.prepend([i])
|
||||
raise err
|
||||
return ret
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def ensure_list_not_empty(value):
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return [value]
|
||||
@@ -117,7 +139,7 @@ def ensure_dict(value):
|
||||
|
||||
|
||||
def hex_int_(value):
|
||||
if isinstance(value, (int, long)):
|
||||
if isinstance(value, integer_types):
|
||||
return HexInt(value)
|
||||
value = string_strict(value).lower()
|
||||
if value.startswith('0x'):
|
||||
@@ -126,7 +148,7 @@ def hex_int_(value):
|
||||
|
||||
|
||||
def int_(value):
|
||||
if isinstance(value, (int, long)):
|
||||
if isinstance(value, integer_types):
|
||||
return value
|
||||
value = string_strict(value).lower()
|
||||
if value.startswith('0x'):
|
||||
@@ -147,8 +169,9 @@ def variable_id_str_(value):
|
||||
raise vol.Invalid("Dashes are not supported in IDs, please use underscores instead.")
|
||||
for char in value:
|
||||
if char != '_' and not char.isalnum():
|
||||
raise vol.Invalid(u"IDs must only consist of upper/lowercase characters and numbers."
|
||||
u"The character '{}' cannot be used".format(char))
|
||||
raise vol.Invalid(u"IDs must only consist of upper/lowercase characters, the underscore"
|
||||
u"character and numbers. The character '{}' cannot be used"
|
||||
u"".format(char))
|
||||
if value in RESERVED_IDS:
|
||||
raise vol.Invalid(u"ID {} is reserved internally and cannot be used".format(value))
|
||||
return value
|
||||
@@ -178,6 +201,8 @@ def templatable(other_validators):
|
||||
def validator(value):
|
||||
if isinstance(value, Lambda):
|
||||
return value
|
||||
if isinstance(other_validators, dict):
|
||||
return vol.Schema(other_validators)(value)
|
||||
return other_validators(value)
|
||||
|
||||
return validator
|
||||
@@ -188,7 +213,7 @@ def only_on(platforms):
|
||||
platforms = [platforms]
|
||||
|
||||
def validator_(obj):
|
||||
if core.ESP_PLATFORM not in platforms:
|
||||
if CORE.esp_platform not in platforms:
|
||||
raise vol.Invalid(u"This feature is only available on {}".format(platforms))
|
||||
return obj
|
||||
|
||||
@@ -231,28 +256,34 @@ def has_exactly_one_key(*keys):
|
||||
return validate
|
||||
|
||||
|
||||
def has_at_most_one_key(*keys):
|
||||
def validate(obj):
|
||||
if not isinstance(obj, dict):
|
||||
raise vol.Invalid('expected dictionary')
|
||||
|
||||
number = sum(k in keys for k in obj)
|
||||
if number > 1:
|
||||
raise vol.Invalid("Cannot specify more than one of {}.".format(', '.join(keys)))
|
||||
return obj
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h"
|
||||
|
||||
time_period_dict = vol.All(
|
||||
dict, vol.Schema({
|
||||
'days': vol.Coerce(float),
|
||||
'hours': vol.Coerce(float),
|
||||
'minutes': vol.Coerce(float),
|
||||
'seconds': vol.Coerce(float),
|
||||
'milliseconds': vol.Coerce(float),
|
||||
'microseconds': vol.Coerce(float),
|
||||
'days': float_,
|
||||
'hours': float_,
|
||||
'minutes': float_,
|
||||
'seconds': float_,
|
||||
'milliseconds': float_,
|
||||
'microseconds': float_,
|
||||
}),
|
||||
has_at_least_one_key('days', 'hours', 'minutes',
|
||||
'seconds', 'milliseconds', 'microseconds'),
|
||||
lambda value: TimePeriod(**value))
|
||||
|
||||
TIME_PERIOD_EXPLICIT_MESSAGE = ("The old way of being able to write time values without a "
|
||||
"time unit (like \"1000\" for 1000 milliseconds) has been "
|
||||
"removed in 1.5.0 as it was ambiguous in some places. Please "
|
||||
"now explicitly specify the time unit (like \"1000ms\"). See "
|
||||
"https://esphomelib.com/esphomeyaml/configuration-types.html#time "
|
||||
"for more information.")
|
||||
|
||||
|
||||
def time_period_str_colon(value):
|
||||
"""Validate and transform time offset with format HH:MM[:SS]."""
|
||||
@@ -280,17 +311,11 @@ def time_period_str_colon(value):
|
||||
def time_period_str_unit(value):
|
||||
"""Validate and transform time period with time unit and integer value."""
|
||||
if isinstance(value, int):
|
||||
value = str(value)
|
||||
elif not isinstance(value, (str, unicode)):
|
||||
raise vol.Invalid("Don't know what '{0}' means as it has no time *unit*! Did you mean "
|
||||
"'{0}s'?".format(value))
|
||||
elif not isinstance(value, string_types):
|
||||
raise vol.Invalid("Expected string for time period with unit.")
|
||||
|
||||
try:
|
||||
float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise vol.Invalid(TIME_PERIOD_EXPLICIT_MESSAGE)
|
||||
|
||||
unit_to_kwarg = {
|
||||
'us': 'microseconds',
|
||||
'microseconds': 'microseconds',
|
||||
@@ -309,11 +334,11 @@ def time_period_str_unit(value):
|
||||
|
||||
match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*)$", value)
|
||||
|
||||
if match is None or match.group(2) not in unit_to_kwarg:
|
||||
if match is None:
|
||||
raise vol.Invalid(u"Expected time period with unit, "
|
||||
u"got {}".format(value))
|
||||
kwarg = unit_to_kwarg[one_of(*unit_to_kwarg)(match.group(2))]
|
||||
|
||||
kwarg = unit_to_kwarg[match.group(2)]
|
||||
return TimePeriod(**{kwarg: float(match.group(1))})
|
||||
|
||||
|
||||
@@ -335,14 +360,43 @@ def time_period_in_seconds_(value):
|
||||
return TimePeriodSeconds(**value.as_dict())
|
||||
|
||||
|
||||
def update_interval(value):
|
||||
if value == 'never':
|
||||
return 4294967295 # uint32_t max
|
||||
return positive_time_period_milliseconds(value)
|
||||
|
||||
|
||||
time_period = vol.Any(time_period_str_unit, time_period_str_colon, time_period_dict)
|
||||
positive_time_period = vol.All(time_period, vol.Range(min=TimePeriod()))
|
||||
positive_time_period_milliseconds = vol.All(positive_time_period, time_period_in_milliseconds_)
|
||||
positive_time_period_seconds = vol.All(positive_time_period, time_period_in_seconds_)
|
||||
time_period_microseconds = vol.All(time_period, time_period_in_microseconds_)
|
||||
positive_time_period_microseconds = vol.All(positive_time_period, time_period_in_microseconds_)
|
||||
positive_not_null_time_period = vol.All(time_period,
|
||||
vol.Range(min=TimePeriod(), min_included=False))
|
||||
|
||||
|
||||
def mac_address(value):
|
||||
value = string_strict(value)
|
||||
parts = value.split(':')
|
||||
if len(parts) != 6:
|
||||
raise vol.Invalid("MAC Address must consist of 6 : (colon) separated parts")
|
||||
parts_int = []
|
||||
if any(len(part) != 2 for part in parts):
|
||||
raise vol.Invalid("MAC Address must be format XX:XX:XX:XX:XX:XX")
|
||||
for part in parts:
|
||||
try:
|
||||
parts_int.append(int(part, 16))
|
||||
except ValueError:
|
||||
raise vol.Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
|
||||
|
||||
return core.MACAddress(*parts_int)
|
||||
|
||||
|
||||
def uuid(value):
|
||||
return vol.Coerce(uuid_.UUID)(value)
|
||||
|
||||
|
||||
METRIC_SUFFIXES = {
|
||||
'E': 1e18, 'P': 1e15, 'T': 1e12, 'G': 1e9, 'M': 1e6, 'k': 1e3, 'da': 10, 'd': 1e-1,
|
||||
'c': 1e-2, 'm': 0.001, u'µ': 1e-6, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12, 'f': 1e-15, 'a': 1e-18,
|
||||
@@ -350,20 +404,46 @@ METRIC_SUFFIXES = {
|
||||
}
|
||||
|
||||
|
||||
def frequency(value):
|
||||
def float_with_unit(quantity, regex_suffix):
|
||||
pattern = re.compile(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)" + regex_suffix + "$")
|
||||
|
||||
def validator(value):
|
||||
match = pattern.match(string(value))
|
||||
|
||||
if match is None:
|
||||
raise vol.Invalid(u"Expected {} with unit, got {}".format(quantity, value))
|
||||
|
||||
mantissa = float(match.group(1))
|
||||
if match.group(2) not in METRIC_SUFFIXES:
|
||||
raise vol.Invalid(u"Invalid {} suffix {}".format(quantity, match.group(2)))
|
||||
|
||||
multiplier = METRIC_SUFFIXES[match.group(2)]
|
||||
return mantissa * multiplier
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
frequency = float_with_unit("frequency", r"(Hz|HZ|hz)?")
|
||||
resistance = float_with_unit("resistance", r"(Ω|Ω|ohm|Ohm|OHM)?")
|
||||
current = float_with_unit("current", r"(a|A|amp|Amp|amps|Amps|ampere|Ampere)?")
|
||||
voltage = float_with_unit("voltage", r"(v|V|volt|Volts)?")
|
||||
|
||||
|
||||
def validate_bytes(value):
|
||||
value = string(value)
|
||||
match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)(?:Hz|HZ|hz)?$", value)
|
||||
match = re.match(r"^([0-9]+)\s*(\w*?)(?:byte|B|b)?s?$", value)
|
||||
|
||||
if match is None:
|
||||
raise vol.Invalid(u"Expected frequency with unit, "
|
||||
u"got {}".format(value))
|
||||
raise vol.Invalid(u"Expected number of bytes with unit, got {}".format(value))
|
||||
|
||||
mantissa = float(match.group(1))
|
||||
mantissa = int(match.group(1))
|
||||
if match.group(2) not in METRIC_SUFFIXES:
|
||||
raise vol.Invalid(u"Invalid frequency suffix {}".format(match.group(2)))
|
||||
|
||||
raise vol.Invalid(u"Invalid metric suffix {}".format(match.group(2)))
|
||||
multiplier = METRIC_SUFFIXES[match.group(2)]
|
||||
return mantissa * multiplier
|
||||
if multiplier < 1:
|
||||
raise vol.Invalid(u"Only suffixes with positive exponents are supported. "
|
||||
u"Got {}".format(match.group(2)))
|
||||
return int(mantissa * multiplier)
|
||||
|
||||
|
||||
def hostname(value):
|
||||
@@ -376,15 +456,22 @@ def hostname(value):
|
||||
return value
|
||||
|
||||
|
||||
def domainname(value):
|
||||
def domain(value):
|
||||
value = string(value)
|
||||
if re.match(vol.DOMAIN_REGEX, value) is None:
|
||||
raise vol.Invalid("Invalid domain: {}".format(value))
|
||||
return value
|
||||
|
||||
|
||||
def domain_name(value):
|
||||
value = string(value)
|
||||
if not value.startswith('.'):
|
||||
raise vol.Invalid("Domainname must start with .")
|
||||
raise vol.Invalid("Domain name must start with .")
|
||||
if value.startswith('..'):
|
||||
raise vol.Invalid("Domainname must start with single .")
|
||||
raise vol.Invalid("Domain name must start with single .")
|
||||
for c in value:
|
||||
if not (c.isalnum() or c in '._-'):
|
||||
raise vol.Invalid("Domainname can only have alphanumeric characters and _ or -")
|
||||
raise vol.Invalid("Domain name can only have alphanumeric characters and _ or -")
|
||||
return value
|
||||
|
||||
|
||||
@@ -395,16 +482,18 @@ def ssid(value):
|
||||
raise vol.Invalid("SSID must be a string. Did you wrap it in quotes?")
|
||||
if not value:
|
||||
raise vol.Invalid("SSID can't be empty.")
|
||||
if len(value) > 31:
|
||||
raise vol.Invalid("SSID can't be longer than 31 characters")
|
||||
if len(value) > 32:
|
||||
raise vol.Invalid("SSID can't be longer than 32 characters")
|
||||
return value
|
||||
|
||||
|
||||
def ipv4(value):
|
||||
if isinstance(value, list):
|
||||
parts = value
|
||||
elif isinstance(value, str):
|
||||
elif isinstance(value, string_types):
|
||||
parts = value.split('.')
|
||||
elif isinstance(value, IPAddress):
|
||||
return value
|
||||
else:
|
||||
raise vol.Invalid("IPv4 address must consist of either string or "
|
||||
"integer list")
|
||||
@@ -481,6 +570,14 @@ def mqtt_qos(value):
|
||||
return one_of(0, 1, 2)(value)
|
||||
|
||||
|
||||
def requires_component(comp):
|
||||
def validator(value):
|
||||
if comp not in CORE.raw_config:
|
||||
raise vol.Invalid("This option requires component {}".format(comp))
|
||||
return value
|
||||
return validator
|
||||
|
||||
|
||||
uint8_t = vol.All(int_, vol.Range(min=0, max=255))
|
||||
uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
|
||||
uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295))
|
||||
@@ -491,11 +588,23 @@ i2c_address = hex_uint8_t
|
||||
|
||||
|
||||
def percentage(value):
|
||||
if isinstance(value, (str, unicode)) and value.endswith('%'):
|
||||
has_percent_sign = isinstance(value, string_types) and value.endswith('%')
|
||||
if has_percent_sign:
|
||||
value = float(value[:-1].rstrip()) / 100.0
|
||||
if value > 1:
|
||||
msg = "Percentage must not be higher than 100%."
|
||||
if not has_percent_sign:
|
||||
msg += " Please put a percent sign after the number!"
|
||||
raise vol.Invalid(msg)
|
||||
return zero_to_one_float(value)
|
||||
|
||||
|
||||
def percentage_int(value):
|
||||
if isinstance(value, string_types) and value.endswith('%'):
|
||||
value = int(value[:-1].rstrip())
|
||||
return value
|
||||
|
||||
|
||||
def invalid(message):
|
||||
def validator(value):
|
||||
raise vol.Invalid(message)
|
||||
@@ -507,10 +616,24 @@ def valid(value):
|
||||
return value
|
||||
|
||||
|
||||
def one_of(*values):
|
||||
def one_of(*values, **kwargs):
|
||||
options = u', '.join(u"'{}'".format(x) for x in values)
|
||||
lower = kwargs.get('lower', False)
|
||||
upper = kwargs.get('upper', False)
|
||||
string_ = kwargs.get('string', False) or lower or upper
|
||||
to_int = kwargs.get('int', False)
|
||||
space = kwargs.get('space', ' ')
|
||||
|
||||
def validator(value):
|
||||
if string_:
|
||||
value = string(value)
|
||||
value = value.replace(' ', space)
|
||||
if to_int:
|
||||
value = int_(value)
|
||||
if lower:
|
||||
value = vol.Lower(value)
|
||||
if upper:
|
||||
value = vol.Upper(value)
|
||||
if value not in values:
|
||||
raise vol.Invalid(u"Unknown value '{}', must be one of {}".format(value, options))
|
||||
return value
|
||||
@@ -524,7 +647,58 @@ def lambda_(value):
|
||||
return Lambda(string_strict(value))
|
||||
|
||||
|
||||
REGISTERED_IDS = set()
|
||||
def dimensions(value):
|
||||
if isinstance(value, list):
|
||||
if len(value) != 2:
|
||||
raise vol.Invalid(u"Dimensions must have a length of two, not {}".format(len(value)))
|
||||
try:
|
||||
width, height = int(value[0]), int(value[1])
|
||||
except ValueError:
|
||||
raise vol.Invalid(u"Width and height dimensions must be integers")
|
||||
if width <= 0 or height <= 0:
|
||||
raise vol.Invalid(u"Width and height must at least be 1")
|
||||
return [width, height]
|
||||
value = string(value)
|
||||
match = re.match(r"\s*([0-9]+)\s*[xX]\s*([0-9]+)\s*", value)
|
||||
if not match:
|
||||
raise vol.Invalid(u"Invalid value '{}' for dimensions. Only WIDTHxHEIGHT is allowed.")
|
||||
return dimensions([match.group(1), match.group(2)])
|
||||
|
||||
|
||||
def directory(value):
|
||||
value = string(value)
|
||||
path = CORE.relative_path(value)
|
||||
if not os.path.exists(path):
|
||||
raise vol.Invalid(u"Could not find directory '{}'. Please make sure it exists.".format(
|
||||
path))
|
||||
if not os.path.isdir(path):
|
||||
raise vol.Invalid(u"Path '{}' is not a directory.".format(path))
|
||||
return value
|
||||
|
||||
|
||||
def file_(value):
|
||||
value = string(value)
|
||||
path = CORE.relative_path(value)
|
||||
if not os.path.exists(path):
|
||||
raise vol.Invalid(u"Could not find file '{}'. Please make sure it exists.".format(
|
||||
path))
|
||||
if not os.path.isfile(path):
|
||||
raise vol.Invalid(u"Path '{}' is not a file.".format(path))
|
||||
return value
|
||||
|
||||
|
||||
ENTITY_ID_CHARACTERS = 'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||
|
||||
|
||||
def entity_id(value):
|
||||
value = string_strict(value).lower()
|
||||
if value.count('.') != 1:
|
||||
raise vol.Invalid("Entity ID must have exactly one dot in it")
|
||||
for x in value.split('.'):
|
||||
for c in x:
|
||||
if c not in ENTITY_ID_CHARACTERS:
|
||||
raise vol.Invalid("Invalid character for entity ID: {}".format(c))
|
||||
return value
|
||||
|
||||
|
||||
class GenerateID(vol.Optional):
|
||||
@@ -532,6 +706,23 @@ class GenerateID(vol.Optional):
|
||||
super(GenerateID, self).__init__(key, default=lambda: None)
|
||||
|
||||
|
||||
def nameable(*schemas):
|
||||
def validator(config):
|
||||
config = vol.All(*schemas)(config)
|
||||
if CONF_NAME not in config and CONF_ID not in config:
|
||||
raise vol.Invalid("At least one of 'id:' or 'name:' is required!")
|
||||
if CONF_NAME not in config:
|
||||
id = config[CONF_ID]
|
||||
if not id.is_manual:
|
||||
raise vol.Invalid("At least one of 'id:' or 'name:' is required!")
|
||||
config[CONF_NAME] = id.id
|
||||
config[CONF_INTERNAL] = True
|
||||
return config
|
||||
return config
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): valid,
|
||||
})
|
||||
@@ -543,13 +734,19 @@ MQTT_COMPONENT_AVAILABILITY_SCHEMA = vol.Schema({
|
||||
})
|
||||
|
||||
MQTT_COMPONENT_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): string,
|
||||
vol.Optional(CONF_RETAIN): boolean,
|
||||
vol.Optional(CONF_DISCOVERY): boolean,
|
||||
vol.Optional(CONF_STATE_TOPIC): publish_topic,
|
||||
vol.Optional(CONF_AVAILABILITY): vol.Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA),
|
||||
vol.Optional(CONF_NAME): string,
|
||||
vol.Optional(CONF_RETAIN): vol.All(requires_component('mqtt'), boolean),
|
||||
vol.Optional(CONF_DISCOVERY): vol.All(requires_component('mqtt'), boolean),
|
||||
vol.Optional(CONF_STATE_TOPIC): vol.All(requires_component('mqtt'), publish_topic),
|
||||
vol.Optional(CONF_AVAILABILITY): vol.All(requires_component('mqtt'),
|
||||
vol.Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA)),
|
||||
vol.Optional(CONF_INTERNAL): boolean,
|
||||
})
|
||||
|
||||
MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({
|
||||
vol.Optional(CONF_COMMAND_TOPIC): subscribe_topic,
|
||||
vol.Optional(CONF_COMMAND_TOPIC): vol.All(requires_component('mqtt'), subscribe_topic),
|
||||
})
|
||||
|
||||
COMPONENT_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_SETUP_PRIORITY): float_
|
||||
})
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"""Constants used by esphomeyaml."""
|
||||
|
||||
MAJOR_VERSION = 1
|
||||
MINOR_VERSION = 6
|
||||
PATCH_VERSION = '2'
|
||||
MINOR_VERSION = 10
|
||||
PATCH_VERSION = '1'
|
||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||
ESPHOMELIB_VERSION = '1.10.1'
|
||||
|
||||
ESP_PLATFORM_ESP32 = 'ESP32'
|
||||
ESP_PLATFORM_ESP8266 = 'ESP8266'
|
||||
@@ -16,12 +17,18 @@ CONF_ESPHOMEYAML = 'esphomeyaml'
|
||||
CONF_NAME = 'name'
|
||||
CONF_PLATFORM = 'platform'
|
||||
CONF_BOARD = 'board'
|
||||
CONF_SIMPLIFY = 'simplify'
|
||||
CONF_USE_BUILD_FLAGS = 'use_build_flags'
|
||||
CONF_LIBRARY_URI = 'library_uri'
|
||||
CONF_ESPHOMELIB_VERSION = 'esphomelib_version'
|
||||
CONF_USE_CUSTOM_CODE = 'use_custom_code'
|
||||
CONF_ARDUINO_VERSION = 'arduino_version'
|
||||
CONF_LOCAL = 'local'
|
||||
CONF_REPOSITORY = 'repository'
|
||||
CONF_COMMIT = 'commit'
|
||||
CONF_TAG = 'tag'
|
||||
CONF_BRANCH = 'branch'
|
||||
CONF_LOGGER = 'logger'
|
||||
CONF_WIFI = 'wifi'
|
||||
CONF_SSID = 'ssid'
|
||||
CONF_BSSID = 'bssid'
|
||||
CONF_PASSWORD = 'password'
|
||||
CONF_MANUAL_IP = 'manual_ip'
|
||||
CONF_STATIC_IP = 'static_ip'
|
||||
@@ -85,6 +92,7 @@ CONF_ABOVE = 'above'
|
||||
CONF_BELOW = 'below'
|
||||
CONF_ON = 'on'
|
||||
CONF_IF = 'if'
|
||||
CONF_WHILE = 'while'
|
||||
CONF_THEN = 'then'
|
||||
CONF_BINARY = 'binary'
|
||||
CONF_WHITE = 'white'
|
||||
@@ -99,6 +107,7 @@ CONF_LOGS = 'logs'
|
||||
CONF_PORT = 'port'
|
||||
CONF_WILL_MESSAGE = 'will_message'
|
||||
CONF_BIRTH_MESSAGE = 'birth_message'
|
||||
CONF_SHUTDOWN_MESSAGE = 'shutdown_message'
|
||||
CONF_PAYLOAD = 'payload'
|
||||
CONF_QOS = 'qos'
|
||||
CONF_DISCOVERY_RETAIN = 'discovery_retain'
|
||||
@@ -148,13 +157,13 @@ CONF_TRIGGER_PIN = 'trigger_pin'
|
||||
CONF_ECHO_PIN = 'echo_pin'
|
||||
CONF_TIMEOUT_METER = 'timeout_meter'
|
||||
CONF_TIMEOUT_TIME = 'timeout_time'
|
||||
CONF_IR_TRANSMITTER_ID = 'ir_transmitter_id'
|
||||
CONF_CARRIER_DUTY_PERCENT = 'carrier_duty_percent'
|
||||
CONF_NEC = 'nec'
|
||||
CONF_COMMAND = 'command'
|
||||
CONF_DATA = 'data'
|
||||
CONF_NBITS = 'nbits'
|
||||
CONF_LG = 'lg'
|
||||
CONF_SAMSUNG = 'samsung'
|
||||
CONF_SONY = 'sony'
|
||||
CONF_PANASONIC = 'panasonic'
|
||||
CONF_REPEAT = 'repeat'
|
||||
@@ -195,6 +204,7 @@ CONF_RECEIVE_TIMEOUT = 'receive_timeout'
|
||||
CONF_SCAN_INTERVAL = 'scan_interval'
|
||||
CONF_MAC_ADDRESS = 'mac_address'
|
||||
CONF_SETUP_MODE = 'setup_mode'
|
||||
CONF_SETUP_PRIORITY = 'setup_priority'
|
||||
CONF_IIR_FILTER = 'iir_filter'
|
||||
CONF_MEASUREMENT_DURATION = 'measurement_duration'
|
||||
CONF_LOW_VOLTAGE_REFERENCE = 'low_voltage_reference'
|
||||
@@ -213,17 +223,20 @@ CONF_ACCURACY = 'accuracy'
|
||||
CONF_BOARD_FLASH_MODE = 'board_flash_mode'
|
||||
CONF_ON_PRESS = 'on_press'
|
||||
CONF_ON_RELEASE = 'on_release'
|
||||
CONF_ON_STATE = 'on_state'
|
||||
CONF_ON_CLICK = 'on_click'
|
||||
CONF_ON_DOUBLE_CLICK = 'on_double_click'
|
||||
CONF_ON_MULTI_CLICK = 'on_multi_click'
|
||||
CONF_MIN_LENGTH = 'min_length'
|
||||
CONF_MAX_LENGTH = 'max_length'
|
||||
CONF_ON_VALUE = 'on_value'
|
||||
CONF_ON_RAW_VALUE = 'on_raw_value'
|
||||
CONF_ON_VALUE_RANGE = 'on_value_range'
|
||||
CONF_ON_MESSAGE = 'on_message'
|
||||
CONF_PIN_CS = 'pin_cs'
|
||||
CONF_PIN_CLOCK = 'pin_clock'
|
||||
CONF_PIN_MISO = 'pin_miso'
|
||||
CONF_CS_PIN = 'cs_pin'
|
||||
CONF_CLK_PIN = 'clk_pin'
|
||||
CONF_MISO_PIN = 'miso_pin'
|
||||
CONF_MOSI_PIN = 'mosi_pin'
|
||||
CONF_TURN_ON_ACTION = 'turn_on_action'
|
||||
CONF_TURN_OFF_ACTION = 'turn_off_action'
|
||||
CONF_OPEN_ACTION = 'open_action'
|
||||
@@ -231,27 +244,163 @@ CONF_CLOSE_ACTION = 'close_action'
|
||||
CONF_STOP_ACTION = 'stop_action'
|
||||
CONF_DOMAIN = 'domain'
|
||||
CONF_OPTIMISTIC = 'optimistic'
|
||||
|
||||
ESP32_BOARDS = [
|
||||
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',
|
||||
'pocket_32', 'espectro32', 'esp32vn-iot-uno', 'esp320', 'esp-wrover-kit',
|
||||
'esp32dev', 'heltec_wifi_kit32', 'heltec_wifi_lora_32', 'hornbill32dev',
|
||||
'hornbill32minima', 'intorobot', 'm5stack-core-esp32', 'mhetesp32devkit',
|
||||
'mhetesp32minikit', 'nano32', 'microduino-core-esp32', 'nodemcu-32s',
|
||||
'quantum', 'esp32-evb', 'esp32-gateway', 'onehorse32dev', 'esp32thing',
|
||||
'espino32', 'lolin32', 'wemosbat', 'widora-air', 'nina_w10',
|
||||
]
|
||||
|
||||
ESP8266_BOARDS = [
|
||||
'gen4iod', 'huzzah', 'oak', 'espduino', 'espectro', 'espresso_lite_v1',
|
||||
'espresso_lite_v2', 'espino', 'esp01', 'esp01_1m', 'esp07', 'esp12e', 'esp8285',
|
||||
'esp_wroom_02', 'phoenix_v1', 'phoenix_v2', 'wifinfo', 'heltex_wifi_kit_8',
|
||||
'nodemcu', 'nodemcuv2', 'modwifi', 'wio_node', 'sparkfunBlynk', 'thing',
|
||||
'thingdev', 'esp210', 'espinotee', 'd1', 'd1_mini', 'd1_mini_lite', 'd1_mini_pro',
|
||||
]
|
||||
ESP_BOARDS_FOR_PLATFORM = {
|
||||
ESP_PLATFORM_ESP32: ESP32_BOARDS,
|
||||
ESP_PLATFORM_ESP8266: ESP8266_BOARDS
|
||||
}
|
||||
CONF_ON_BOOT = 'on_boot'
|
||||
CONF_ON_SHUTDOWN = 'on_shutdown'
|
||||
CONF_PRIORITY = 'priority'
|
||||
CONF_DUMP = 'dump'
|
||||
CONF_BUFFER_SIZE = 'buffer_size'
|
||||
CONF_TOLERANCE = 'tolerance'
|
||||
CONF_FILTER = 'filter'
|
||||
CONF_IDLE = 'idle'
|
||||
CONF_NETWORKS = 'networks'
|
||||
CONF_INTERNAL = 'internal'
|
||||
CONF_BUILD_PATH = 'build_path'
|
||||
CONF_PLATFORMIO_OPTIONS = 'platformio_options'
|
||||
CONF_REBOOT_TIMEOUT = 'reboot_timeout'
|
||||
CONF_INVERT = 'invert'
|
||||
CONF_DELAYED_ON = 'delayed_on'
|
||||
CONF_DELAYED_OFF = 'delayed_off'
|
||||
CONF_UUID = 'uuid'
|
||||
CONF_TYPE = 'type'
|
||||
CONF_SPI_ID = 'spi_id'
|
||||
CONF_UART_ID = 'uart_id'
|
||||
CONF_UID = 'uid'
|
||||
CONF_TX_PIN = 'tx_pin'
|
||||
CONF_RX_PIN = 'rx_pin'
|
||||
CONF_CO2 = 'co2'
|
||||
CONF_SHUNT_RESISTANCE = 'shunt_resistance'
|
||||
CONF_MAX_CURRENT = 'max_current'
|
||||
CONF_MAX_VOLTAGE = 'max_voltage'
|
||||
CONF_CURRENT = 'current'
|
||||
CONF_POWER = 'power'
|
||||
CONF_BUS_VOLTAGE = 'bus_voltage'
|
||||
CONF_SHUNT_VOLTAGE = 'shunt_voltage'
|
||||
CONF_CONDITION = 'condition'
|
||||
CONF_ELSE = 'else'
|
||||
CONF_EFFECTS = 'effects'
|
||||
CONF_RANDOM = 'random'
|
||||
CONF_EFFECT_ID = 'effect_id'
|
||||
CONF_COLORS = 'colors'
|
||||
CONF_STATE = 'state'
|
||||
CONF_DURATION = 'duration'
|
||||
CONF_WIDTH = 'width'
|
||||
CONF_ILLUMINANCE = 'illuminance'
|
||||
CONF_COLOR_TEMPERATURE = 'color_temperature'
|
||||
CONF_BATTERY_LEVEL = 'battery_level'
|
||||
CONF_MOISTURE = 'moisture'
|
||||
CONF_CONDUCTIVITY = 'conductivity'
|
||||
CONF_RC_SWITCH_RAW = 'rc_switch_raw'
|
||||
CONF_RC_SWITCH_TYPE_A = 'rc_switch_type_a'
|
||||
CONF_RC_SWITCH_TYPE_B = 'rc_switch_type_b'
|
||||
CONF_RC_SWITCH_TYPE_C = 'rc_switch_type_c'
|
||||
CONF_RC_SWITCH_TYPE_D = 'rc_switch_type_d'
|
||||
CONF_CODE = 'code'
|
||||
CONF_PROTOCOL = 'protocol'
|
||||
CONF_PULSE_LENGTH = 'pulse_length'
|
||||
CONF_SYNC = 'sync'
|
||||
CONF_ZERO = 'zero'
|
||||
CONF_ONE = 'one'
|
||||
CONF_GROUP = 'group'
|
||||
CONF_DEVICE = 'device'
|
||||
CONF_FAMILY = 'family'
|
||||
CONF_FILE = 'file'
|
||||
CONF_GLYPHS = 'glyphs'
|
||||
CONF_SIZE = 'size'
|
||||
CONF_RESIZE = 'resize'
|
||||
CONF_ROTATION = 'rotation'
|
||||
CONF_DC_PIN = 'dc_pin'
|
||||
CONF_RESET_PIN = 'reset_pin'
|
||||
CONF_BUSY_PIN = 'busy_pin'
|
||||
CONF_FULL_UPDATE_EVERY = 'full_update_every'
|
||||
CONF_DATA_PINS = 'data_pins'
|
||||
CONF_ENABLE_PIN = 'enable_pin'
|
||||
CONF_RS_PIN = 'rs_pin'
|
||||
CONF_RW_PIN = 'rw_pin'
|
||||
CONF_DIMENSIONS = 'dimensions'
|
||||
CONF_NUM_CHIPS = 'num_chips'
|
||||
CONF_INTENSITY = 'intensity'
|
||||
CONF_EXTERNAL_VCC = 'external_vcc'
|
||||
CONF_TIMEZONE = 'timezone'
|
||||
CONF_SERVERS = 'servers'
|
||||
CONF_HEATER = 'heater'
|
||||
CONF_VOLTAGE = 'voltage'
|
||||
CONF_CURRENT_RESISTOR = 'current_resistor'
|
||||
CONF_VOLTAGE_DIVIDER = 'voltage_divider'
|
||||
CONF_SEL_PIN = 'sel_pin'
|
||||
CONF_CF_PIN = 'cf_pin'
|
||||
CONF_CF1_PIN = 'cf1_pin'
|
||||
CONF_CHANGE_MODE_EVERY = 'change_mode_every'
|
||||
CONF_PAGE_ID = 'page_id'
|
||||
CONF_COMPONENT_ID = 'component_id'
|
||||
CONF_COLD_WHITE = 'cold_white'
|
||||
CONF_WARM_WHITE = 'warm_white'
|
||||
CONF_COLD_WHITE_COLOR_TEMPERATURE = 'cold_white_color_temperature'
|
||||
CONF_WARM_WHITE_COLOR_TEMPERATURE = 'warm_white_color_temperature'
|
||||
CONF_ON_LOOP = 'on_loop'
|
||||
CONF_ON_TIME = 'on_time'
|
||||
CONF_SECONDS = 'seconds'
|
||||
CONF_MINUTES = 'minutes'
|
||||
CONF_HOURS = 'hours'
|
||||
CONF_DAYS_OF_MONTH = 'days_of_month'
|
||||
CONF_MONTHS = 'months'
|
||||
CONF_DAYS_OF_WEEK = 'days_of_week'
|
||||
CONF_CRON = 'cron'
|
||||
CONF_POWER_SAVE_MODE = 'power_save_mode'
|
||||
CONF_POWER_ON_VALUE = 'power_on_value'
|
||||
CONF_PM_1_0 = 'pm_1_0'
|
||||
CONF_PM_2_5 = 'pm_2_5'
|
||||
CONF_PM_10_0 = 'pm_10_0'
|
||||
CONF_FORMALDEHYDE = 'formaldehyde'
|
||||
CONF_ON_TAG = 'on_tag'
|
||||
CONF_ARGS = 'args'
|
||||
CONF_FORMAT = 'format'
|
||||
CONF_COLOR_CORRECT = 'color_correct'
|
||||
CONF_ON_JSON_MESSAGE = 'on_json_message'
|
||||
CONF_ACCELERATION = 'acceleration'
|
||||
CONF_DECELERATION = 'deceleration'
|
||||
CONF_MAX_SPEED = 'max_speed'
|
||||
CONF_TARGET = 'target'
|
||||
CONF_POSITION = 'position'
|
||||
CONF_STEP_PIN = 'step_pin'
|
||||
CONF_DIR_PIN = 'dir_pin'
|
||||
CONF_SLEEP_PIN = 'sleep_pin'
|
||||
CONF_SEND_FIRST_AT = 'send_first_at'
|
||||
CONF_TIME_ID = 'time_id'
|
||||
CONF_RESTORE_STATE = 'restore_state'
|
||||
CONF_TIMING = 'timing'
|
||||
CONF_INVALID_COOLDOWN = 'invalid_cooldown'
|
||||
CONF_MY9231_ID = 'my9231_id'
|
||||
CONF_NUM_CHANNELS = 'num_channels'
|
||||
CONF_UPDATE_ON_BOOT = 'update_on_boot'
|
||||
CONF_INITIAL_VALUE = 'initial_value'
|
||||
CONF_RESTORE_VALUE = 'restore_value'
|
||||
CONF_PINS = 'pins'
|
||||
CONF_SENSORS = 'sensors'
|
||||
CONF_BINARY_SENSORS = 'binary_sensors'
|
||||
CONF_OUTPUTS = 'outputs'
|
||||
CONF_SWITCHES = 'switches'
|
||||
CONF_TEXT_SENSORS = 'text_sensors'
|
||||
CONF_INCLUDES = 'includes'
|
||||
CONF_LIBRARIES = 'libraries'
|
||||
CONF_PIN_A = 'pin_a'
|
||||
CONF_PIN_B = 'pin_b'
|
||||
CONF_PIN_C = 'pin_c'
|
||||
CONF_PIN_D = 'pin_d'
|
||||
CONF_SLEEP_WHEN_DONE = 'sleep_when_done'
|
||||
CONF_STEP_MODE = 'step_mode'
|
||||
CONF_COMPONENTS = 'components'
|
||||
CONF_DATA_TEMPLATE = 'data_template'
|
||||
CONF_VARIABLES = 'variables'
|
||||
CONF_SERVICE = 'service'
|
||||
CONF_ENTITY_ID = 'entity_id'
|
||||
CONF_RESTORE_MODE = 'restore_mode'
|
||||
CONF_INTERVAL = 'interval'
|
||||
CONF_DIRECTION = 'direction'
|
||||
CONF_VARIANT = 'variant'
|
||||
CONF_METHOD = 'method'
|
||||
CONF_FAST_CONNECT = 'fast_connect'
|
||||
|
||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
|
||||
ARDUINO_VERSION_ESP8266_DEV = 'https://github.com/platformio/platform-espressif8266.git#feature' \
|
||||
'/stage'
|
||||
|
||||