Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
e47bcb9abb | ||
|
|
30b94f06b3 | ||
|
|
19dfdb77eb | ||
|
|
b6e7ad3589 | ||
|
|
1267680379 | ||
|
|
b5f6b32fad | ||
|
|
7068808d4f | ||
|
|
c82a44d541 | ||
|
|
e650473682 | ||
|
|
2ee0d4242d | ||
|
|
b2aecf29dc | ||
|
|
081c2f4e07 | ||
|
|
0c77228421 | ||
|
|
8a509c6551 | ||
|
|
d249820bcd | ||
|
|
f39cf52eae | ||
|
|
63fb252b46 | ||
|
|
2f98adca49 | ||
|
|
084fc00517 | ||
|
|
abeac23abd | ||
|
|
7264e3a4bf | ||
|
|
16eeb3af31 | ||
|
|
b799d2df7f | ||
|
|
11d55fec4f | ||
|
|
923a5990c7 | ||
|
|
a0a615c335 | ||
|
|
e9ced0cc3f | ||
|
|
1a92381994 | ||
|
|
a07a7a87a2 | ||
|
|
6a823f5777 | ||
|
|
419c5afe27 | ||
|
|
17798dee1e | ||
|
|
ee2c53585f | ||
|
|
2f05acfa5a | ||
|
|
f4d393a59e | ||
|
|
967aa53bad | ||
|
|
4f3f460105 | ||
|
|
6e85a741ae | ||
|
|
2db45898e2 | ||
|
|
eb62599a98 | ||
|
|
9f0737e5b9 | ||
|
|
7a393c1a3a | ||
|
|
4fa7bc196a | ||
|
|
65d0dd47f3 | ||
|
|
d88634b196 | ||
|
|
976627eb38 | ||
|
|
5b995c0692 | ||
|
|
2d4b475951 | ||
|
|
93d962dd43 | ||
|
|
2e7d8540fb | ||
|
|
677fe8bacf | ||
|
|
94d7ac4ef0 | ||
|
|
ebb6d0d464 | ||
|
|
e8fe653140 | ||
|
|
374ea7044c |
@@ -106,3 +106,5 @@ venv.bak/
|
||||
config/
|
||||
examples/
|
||||
Dockerfile
|
||||
.git/
|
||||
tests/build/
|
||||
|
||||
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us 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!
|
||||
-->
|
||||
|
||||
**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
|
||||
|
||||
```
|
||||
|
||||
**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:**
|
||||
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
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.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!--
|
||||
A clear and concise description of what the problem is.
|
||||
-->
|
||||
Ex. I'm always frustrated when [...]
|
||||
|
||||
**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.
|
||||
20
.github/ISSUE_TEMPLATE/new-integration.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/new-integration.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: New integration
|
||||
about: Suggest a new integration for esphomelib
|
||||
|
||||
---
|
||||
|
||||
<!-- READ THIS FIRST:
|
||||
- This is for new integrations (such as new sensors/modules) only, for new features within the environment please use the "feature request" template.
|
||||
- Do not delete anything from this template and fill out the form as precisely as possible.
|
||||
-->
|
||||
|
||||
**What new integration would you wish to have?**
|
||||
<!-- A name/description of the new integration/board. -->
|
||||
|
||||
**If possible, provide a link to an existing library for the integration:**
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Additional context**
|
||||
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
## 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>
|
||||
|
||||
## Example entry for YAML configuration (if applicable):
|
||||
```yaml
|
||||
|
||||
```
|
||||
|
||||
## Checklist:
|
||||
- [ ] The code change is tested and works locally.
|
||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
- [ ] Check this box if you have read, understand, comply, and agree with the [Code of Conduct](https://github.com/OttoWinter/esphomeyaml/blob/master/CODE_OF_CONDUCT.md).
|
||||
|
||||
If user exposed functionality or configuration variables are added/changed:
|
||||
- [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -104,3 +104,4 @@ venv.bak/
|
||||
.mypy_cache/
|
||||
|
||||
config/
|
||||
tests/build/
|
||||
|
||||
233
.gitlab-ci.yml
Normal file
233
.gitlab-ci.yml
Normal file
@@ -0,0 +1,233 @@
|
||||
---
|
||||
# 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
|
||||
tags:
|
||||
- python2.7
|
||||
- esphomeyaml-lint
|
||||
|
||||
.test: &test
|
||||
stage: test
|
||||
before_script:
|
||||
- pip install -e .
|
||||
tags:
|
||||
- python2.7
|
||||
- esphomeyaml-test
|
||||
variables:
|
||||
TZ: UTC
|
||||
cache:
|
||||
paths:
|
||||
- tests/build
|
||||
|
||||
.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:
|
||||
- |
|
||||
hassio-builder.sh \
|
||||
-t . \
|
||||
-i ottowinter/esphomeyaml-hassio-${ADDON_ARCH} \
|
||||
-d "$CI_REGISTRY" \
|
||||
--${ADDON_ARCH}
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev"
|
||||
retry: 2
|
||||
|
||||
# Generic deploy template
|
||||
.deploy-release: &deploy-release
|
||||
<<: *docker-builder
|
||||
stage: deploy
|
||||
script:
|
||||
- version=${CI_COMMIT_TAG:1}
|
||||
- echo "Publishing release version ${version}"
|
||||
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
|
||||
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- |
|
||||
docker tag \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
|
||||
.deploy-beta: &deploy-beta
|
||||
<<: *docker-builder
|
||||
stage: deploy
|
||||
script:
|
||||
- version=${CI_COMMIT_TAG:1}
|
||||
- echo "Publishing beta version ${version}"
|
||||
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
|
||||
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- |
|
||||
docker tag \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
|
||||
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
|
||||
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
|
||||
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" .
|
||||
- |
|
||||
docker tag \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml:dev" \
|
||||
"${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}"
|
||||
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:dev"
|
||||
|
||||
build:armhf:
|
||||
<<: *build-hassio
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
|
||||
#build:aarch64:
|
||||
# <<: *build
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
|
||||
build:i386:
|
||||
<<: *build-hassio
|
||||
variables:
|
||||
ADDON_ARCH: i386
|
||||
|
||||
build:amd64:
|
||||
<<: *build-hassio
|
||||
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).+@/
|
||||
22
.travis.yml
22
.travis.yml
@@ -2,9 +2,19 @@ sudo: false
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- pip install flake8==3.5.0 pylint==1.8.4
|
||||
script:
|
||||
- flake8 esphomeyaml
|
||||
- pylint esphomeyaml
|
||||
jobs:
|
||||
include:
|
||||
- name: "Lint"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow
|
||||
script:
|
||||
- flake8 esphomeyaml
|
||||
- pylint esphomeyaml
|
||||
- name: "Test"
|
||||
install:
|
||||
- pip install -e .
|
||||
- pip install tzlocal pillow
|
||||
script:
|
||||
- esphomeyaml tests/test1.yaml compile
|
||||
- esphomeyaml tests/test2.yaml compile
|
||||
|
||||
46
CODE_OF_CONDUCT.md
Normal file
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
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.
|
||||
12
Dockerfile
12
Dockerfile
@@ -1,20 +1,26 @@
|
||||
FROM python:2.7
|
||||
MAINTAINER Otto Winter <contact@otto-winter.com>
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python-pil \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
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
|
||||
RUN pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
COPY docker/platformio.ini /usr/src/app/
|
||||
RUN platformio settings set enable_telemetry No && \
|
||||
platformio run -e espressif32 -e espressif8266; exit 0
|
||||
|
||||
COPY . .
|
||||
RUN pip install -e .
|
||||
RUN pip install --no-cache-dir -e . && \
|
||||
pip install --no-cache-dir tzlocal pillow
|
||||
|
||||
WORKDIR /config
|
||||
ENTRYPOINT ["esphomeyaml"]
|
||||
CMD ["/config", "dashboard"]
|
||||
|
||||
21
LICENSE
Normal file
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.
|
||||
4
MANIFEST.in
Normal file
4
MANIFEST.in
Normal file
@@ -0,0 +1,4 @@
|
||||
include README.md
|
||||
include esphomeyaml/dashboard/templates/index.html
|
||||
include esphomeyaml/dashboard/static/materialize-stepper.min.css
|
||||
include esphomeyaml/dashboard/static/materialize-stepper.min.js
|
||||
@@ -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.
|
||||
|
||||
21
docker/Dockerfile.aarch64
Normal file
21
docker/Dockerfile.aarch64
Normal file
@@ -0,0 +1,21 @@
|
||||
# Dockerfile for aarch64 version of HassIO add-on
|
||||
FROM arm64v8/ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python \
|
||||
python-pip \
|
||||
python-setuptools \
|
||||
python-pil \
|
||||
git \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
COPY docker/platformio.ini /pio/platformio.ini
|
||||
RUN platformio run -d /pio; rm -rf /pio
|
||||
|
||||
COPY . .
|
||||
RUN pip install --no-cache-dir --no-binary :all: -e . && \
|
||||
pip install --no-cache-dir --no-binary :all: tzlocal
|
||||
|
||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
|
||||
21
docker/Dockerfile.amd64
Normal file
21
docker/Dockerfile.amd64
Normal file
@@ -0,0 +1,21 @@
|
||||
# Dockerfile for amd64 version of HassIO add-on
|
||||
FROM ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python \
|
||||
python-pip \
|
||||
python-setuptools \
|
||||
python-pil \
|
||||
git \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
COPY docker/platformio.ini /pio/platformio.ini
|
||||
RUN platformio run -d /pio; rm -rf /pio
|
||||
|
||||
COPY . .
|
||||
RUN pip install --no-cache-dir --no-binary :all: -e . && \
|
||||
pip install --no-cache-dir --no-binary :all: tzlocal
|
||||
|
||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
|
||||
31
docker/Dockerfile.armhf
Normal file
31
docker/Dockerfile.armhf
Normal file
@@ -0,0 +1,31 @@
|
||||
# Dockerfile for armhf version of HassIO add-on
|
||||
FROM homeassistant/armhf-base:latest
|
||||
|
||||
RUN apk add --no-cache \
|
||||
python2 \
|
||||
python2-dev \
|
||||
py2-pip \
|
||||
git \
|
||||
gcc \
|
||||
openssh \
|
||||
libc6-compat \
|
||||
jpeg-dev \
|
||||
zlib-dev \
|
||||
freetype-dev \
|
||||
lcms2-dev \
|
||||
openjpeg-dev \
|
||||
tiff-dev \
|
||||
libc-dev \
|
||||
linux-headers \
|
||||
&& \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
COPY docker/platformio-esp8266.ini /pio/platformio.ini
|
||||
RUN platformio run -d /pio; rm -rf /pio
|
||||
|
||||
COPY . .
|
||||
RUN pip install --no-cache-dir --no-binary :all: -e . && \
|
||||
pip install --no-cache-dir pillow tzlocal
|
||||
|
||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
|
||||
32
docker/Dockerfile.builder
Normal file
32
docker/Dockerfile.builder
Normal file
@@ -0,0 +1,32 @@
|
||||
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/*
|
||||
|
||||
COPY docker/hassio-builder.sh /usr/bin/
|
||||
|
||||
WORKDIR /data
|
||||
21
docker/Dockerfile.i386
Normal file
21
docker/Dockerfile.i386
Normal file
@@ -0,0 +1,21 @@
|
||||
# Dockerfile for i386 version of HassIO add-on
|
||||
FROM i386/ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python \
|
||||
python-pip \
|
||||
python-setuptools \
|
||||
python-pil \
|
||||
git \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
COPY docker/platformio.ini /pio/platformio.ini
|
||||
RUN platformio run -d /pio; rm -rf /pio
|
||||
|
||||
COPY . .
|
||||
RUN pip install --no-cache-dir --no-binary :all: -e . && \
|
||||
pip install --no-cache-dir --no-binary :all: tzlocal
|
||||
|
||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
|
||||
6
docker/Dockerfile.lint
Normal file
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.5.0 pylint==1.9.3 tzlocal pillow
|
||||
19
docker/Dockerfile.test
Normal file
19
docker/Dockerfile.test
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python \
|
||||
python-pip \
|
||||
python-setuptools \
|
||||
python-pil \
|
||||
git \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||
pip install --no-cache-dir --no-binary :all: platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
COPY docker/platformio.ini /pio/platformio.ini
|
||||
RUN platformio run -d /pio; rm -rf /pio
|
||||
|
||||
COPY requirements.txt /requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir -r /requirements.txt && \
|
||||
pip install --no-cache-dir tzlocal pillow
|
||||
318
docker/hassio-builder.sh
Executable file
318
docker/hassio-builder.sh
Executable file
@@ -0,0 +1,318 @@
|
||||
#!/usr/bin/env bash
|
||||
# Based on Home Assistant's docker builder
|
||||
######################
|
||||
# Hass.io Build-env
|
||||
######################
|
||||
set -e
|
||||
|
||||
echo -- "$@"
|
||||
|
||||
#### Variable ####
|
||||
|
||||
DOCKER_TIMEOUT=20
|
||||
DOCKER_PID=-1
|
||||
DOCKER_HUB=""
|
||||
DOCKER_CACHE="true"
|
||||
DOCKER_LOCAL="false"
|
||||
TARGET=""
|
||||
IMAGE=""
|
||||
BUILD_LIST=()
|
||||
BUILD_TASKS=()
|
||||
|
||||
#### Misc functions ####
|
||||
|
||||
function print_help() {
|
||||
cat << EOF
|
||||
Hass.io build-env for ecosystem:
|
||||
docker run --rm homeassistant/{arch}-builder:latest [options]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Display this help and exit.
|
||||
|
||||
Repository / Data
|
||||
-t, --target <PATH_TO_BUILD>
|
||||
Set local folder or path inside repository for build.
|
||||
|
||||
Version/Image handling
|
||||
-i, --image <IMAGE_NAME>
|
||||
Overwrite image name of build / support {arch}
|
||||
|
||||
Architecture
|
||||
--armhf
|
||||
Build for arm.
|
||||
--amd64
|
||||
Build for intel/amd 64bit.
|
||||
--aarch64
|
||||
Build for arm 64bit.
|
||||
--i386
|
||||
Build for intel/amd 32bit.
|
||||
--all
|
||||
Build all architecture.
|
||||
|
||||
Build handling
|
||||
--no-cache
|
||||
Disable cache for the build (from latest).
|
||||
-d, --docker-hub <DOCKER_REPOSITORY>
|
||||
Set or overwrite the docker repository.
|
||||
|
||||
Use the host docker socket if mapped into container:
|
||||
/var/run/docker.sock
|
||||
|
||||
EOF
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
#### Docker functions ####
|
||||
|
||||
function start_docker() {
|
||||
local starttime
|
||||
local endtime
|
||||
|
||||
if [ -S "/var/run/docker.sock" ]; then
|
||||
echo "[INFO] Use host docker setup with '/var/run/docker.sock'"
|
||||
DOCKER_LOCAL="true"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Starting docker."
|
||||
dockerd 2> /dev/null &
|
||||
DOCKER_PID=$!
|
||||
|
||||
echo "[INFO] Waiting for docker to initialize..."
|
||||
starttime="$(date +%s)"
|
||||
endtime="$(date +%s)"
|
||||
until docker info >/dev/null 2>&1; do
|
||||
if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then
|
||||
sleep 1
|
||||
endtime=$(date +%s)
|
||||
else
|
||||
echo "[ERROR] Timeout while waiting for docker to come up"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "[INFO] Docker was initialized"
|
||||
}
|
||||
|
||||
|
||||
function stop_docker() {
|
||||
local starttime
|
||||
local endtime
|
||||
|
||||
if [ "$DOCKER_LOCAL" == "true" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Stopping in container docker..."
|
||||
if [ "$DOCKER_PID" -gt 0 ] && kill -0 "$DOCKER_PID" 2> /dev/null; then
|
||||
starttime="$(date +%s)"
|
||||
endtime="$(date +%s)"
|
||||
|
||||
# Now wait for it to die
|
||||
kill "$DOCKER_PID"
|
||||
while kill -0 "$DOCKER_PID" 2> /dev/null; do
|
||||
if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then
|
||||
sleep 1
|
||||
endtime=$(date +%s)
|
||||
else
|
||||
echo "[ERROR] Timeout while waiting for container docker to die"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "[WARN] Your host might have been left with unreleased resources"
|
||||
fi
|
||||
}
|
||||
|
||||
function run_build() {
|
||||
local build_dir=$1
|
||||
local repository=$2
|
||||
local image=$3
|
||||
local version=$4
|
||||
local build_arch=$5
|
||||
local docker_cli=("${!6}")
|
||||
|
||||
local push_images=()
|
||||
|
||||
# Overwrites
|
||||
if [ ! -z "$DOCKER_HUB" ]; then repository="$DOCKER_HUB"; fi
|
||||
if [ ! -z "$IMAGE" ]; then image="$IMAGE"; fi
|
||||
|
||||
# Init Cache
|
||||
if [ "$DOCKER_CACHE" == "true" ]; then
|
||||
echo "[INFO] Init cache for $repository/$image:$version"
|
||||
if docker pull "$repository/$image:latest" > /dev/null 2>&1; then
|
||||
docker_cli+=("--cache-from" "$repository/$image:latest")
|
||||
else
|
||||
docker_cli+=("--no-cache")
|
||||
echo "[WARN] No cache image found. Cache is disabled for build"
|
||||
fi
|
||||
else
|
||||
docker_cli+=("--no-cache")
|
||||
fi
|
||||
|
||||
# Build image
|
||||
echo "[INFO] Run build for $repository/$image:$version"
|
||||
docker build --pull -t "$repository/$image:$version" \
|
||||
--label "io.hass.version=$version" \
|
||||
--label "io.hass.arch=$build_arch" \
|
||||
-f "$TARGET/docker/Dockerfile.$build_arch" \
|
||||
"${docker_cli[@]}" \
|
||||
"$build_dir"
|
||||
|
||||
echo "[INFO] Finish build for $repository/$image:$version"
|
||||
docker tag "$repository/$image:$version" "$repository/$image:dev"
|
||||
}
|
||||
|
||||
|
||||
#### HassIO functions ####
|
||||
|
||||
function build_addon() {
|
||||
local build_arch=$1
|
||||
|
||||
local docker_cli=()
|
||||
local image=""
|
||||
local repository=""
|
||||
local raw_image=""
|
||||
local name=""
|
||||
local description=""
|
||||
local url=""
|
||||
local args=""
|
||||
|
||||
# Read addon config.json
|
||||
name="$(jq --raw-output '.name // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")"
|
||||
description="$(jq --raw-output '.description // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")"
|
||||
url="$(jq --raw-output '.url // empty' "$TARGET/esphomeyaml/config.json")"
|
||||
version="$(jq --raw-output '.version' "$TARGET/esphomeyaml/config.json")"
|
||||
raw_image="$(jq --raw-output '.image // empty' "$TARGET/esphomeyaml/config.json")"
|
||||
|
||||
# Read data from image
|
||||
if [ ! -z "$raw_image" ]; then
|
||||
repository="$(echo "$raw_image" | cut -f 1 -d '/')"
|
||||
image="$(echo "$raw_image" | cut -f 2 -d '/')"
|
||||
fi
|
||||
|
||||
# Set additional labels
|
||||
docker_cli+=("--label" "io.hass.name=$name")
|
||||
docker_cli+=("--label" "io.hass.description=$description")
|
||||
docker_cli+=("--label" "io.hass.type=addon")
|
||||
|
||||
if [ ! -z "$url" ]; then
|
||||
docker_cli+=("--label" "io.hass.url=$url")
|
||||
fi
|
||||
|
||||
# Start build
|
||||
run_build "$TARGET" "$repository" "$image" "$version" \
|
||||
"$build_arch" docker_cli[@]
|
||||
}
|
||||
|
||||
#### initialized cross-build ####
|
||||
|
||||
function init_crosscompile() {
|
||||
echo "[INFO] Setup crosscompiling feature"
|
||||
(
|
||||
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
|
||||
update-binfmts --enable qemu-arm
|
||||
update-binfmts --enable qemu-aarch64
|
||||
) > /dev/null 2>&1 || echo "[WARN] Can't enable crosscompiling feature"
|
||||
}
|
||||
|
||||
|
||||
function clean_crosscompile() {
|
||||
echo "[INFO] Clean crosscompiling feature"
|
||||
if [ -f /proc/sys/fs/binfmt_misc ]; then
|
||||
umount /proc/sys/fs/binfmt_misc || true
|
||||
fi
|
||||
|
||||
(
|
||||
update-binfmts --disable qemu-arm
|
||||
update-binfmts --disable qemu-aarch64
|
||||
) > /dev/null 2>&1 || echo "[WARN] No crosscompiling feature found for cleanup"
|
||||
}
|
||||
|
||||
#### Error handling ####
|
||||
|
||||
function error_handling() {
|
||||
stop_docker
|
||||
clean_crosscompile
|
||||
|
||||
exit 1
|
||||
}
|
||||
trap 'error_handling' SIGINT SIGTERM
|
||||
|
||||
#### Parse arguments ####
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key=$1
|
||||
case ${key} in
|
||||
-h|--help)
|
||||
print_help
|
||||
;;
|
||||
-t|--target)
|
||||
TARGET=$2
|
||||
shift
|
||||
;;
|
||||
-i|--image)
|
||||
IMAGE=$2
|
||||
shift
|
||||
;;
|
||||
--no-cache)
|
||||
DOCKER_CACHE="false"
|
||||
;;
|
||||
-d|--docker-hub)
|
||||
DOCKER_HUB=$2
|
||||
shift
|
||||
;;
|
||||
--armhf)
|
||||
BUILD_LIST+=("armhf")
|
||||
;;
|
||||
--amd64)
|
||||
BUILD_LIST+=("amd64")
|
||||
;;
|
||||
--i386)
|
||||
BUILD_LIST+=("i386")
|
||||
;;
|
||||
--aarch64)
|
||||
BUILD_LIST+=("aarch64")
|
||||
;;
|
||||
--all)
|
||||
BUILD_LIST=("armhf" "amd64" "i386" "aarch64")
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "[WARN] $0 : Argument '$1' unknown will be Ignoring"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Check if an architecture is available
|
||||
if [ "${#BUILD_LIST[@]}" -eq 0 ]; then
|
||||
echo "[ERROR] You need select an architecture for build!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
#### Main ####
|
||||
|
||||
mkdir -p /data
|
||||
|
||||
# Setup docker env
|
||||
init_crosscompile
|
||||
start_docker
|
||||
|
||||
# Select arch build
|
||||
for arch in "${BUILD_LIST[@]}"; do
|
||||
(build_addon "$arch") &
|
||||
BUILD_TASKS+=($!)
|
||||
done
|
||||
|
||||
# Wait until all build jobs are done
|
||||
wait "${BUILD_TASKS[@]}"
|
||||
|
||||
# Cleanup docker env
|
||||
clean_crosscompile
|
||||
stop_docker
|
||||
|
||||
exit 0
|
||||
7
docker/platformio-esp8266.ini
Normal file
7
docker/platformio-esp8266.ini
Normal file
@@ -0,0 +1,7 @@
|
||||
; This file allows the docker build file to install the required platformio
|
||||
; platforms
|
||||
|
||||
[env:espressif8266]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
@@ -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
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
|
||||
[env:espressif32]
|
||||
platform = espressif32
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
|
||||
33
esphomeyaml-beta/config.json
Normal file
33
esphomeyaml-beta/config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "esphomeyaml-beta",
|
||||
"version": "1.9.0b1",
|
||||
"slug": "esphomeyaml-beta",
|
||||
"description": "Beta version of esphomeyaml HassIO add-on.",
|
||||
"url": "https://esphomelib.com/esphomeyaml/index.html",
|
||||
"startup": "application",
|
||||
"webui": "http://[HOST]:[PORT:6052]",
|
||||
"boot": "auto",
|
||||
"ports": {
|
||||
"6052/tcp": 6052,
|
||||
"6053/tcp": 6053
|
||||
},
|
||||
"auto_uart": true,
|
||||
"map": [
|
||||
"config:rw"
|
||||
],
|
||||
"arch": [
|
||||
"amd64",
|
||||
"armhf",
|
||||
"i386"
|
||||
],
|
||||
"environment": {
|
||||
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
|
||||
},
|
||||
"options": {
|
||||
"password": ""
|
||||
},
|
||||
"schema": {
|
||||
"password": "str?"
|
||||
},
|
||||
"image": "ottowinter/esphomeyaml-hassio-{arch}"
|
||||
}
|
||||
59
esphomeyaml-edge/Dockerfile
Normal file
59
esphomeyaml-edge/Dockerfile
Normal file
@@ -0,0 +1,59 @@
|
||||
# Dockerfile for HassIO add-on
|
||||
ARG BUILD_FROM=ubuntu:bionic
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# Re-declare BUILD_FROM to fix weird docker issue
|
||||
ARG BUILD_FROM
|
||||
|
||||
# On amd64 and alike, using ubuntu as the base is better as building
|
||||
# for the ESP32 only works with glibc (and ubuntu). However, on armhf
|
||||
# the build toolchain frequently procudes segfaults under ubuntu.
|
||||
# -> Use ubuntu for most architectures, except alpine for armhf
|
||||
#
|
||||
# * python and related required because this is a python project
|
||||
# * git required for platformio library dependencies downloads
|
||||
# * libc6-compat and openssh required on alpine for weird reasons
|
||||
# * disable platformio telemetry on install
|
||||
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
python python-pip python-setuptools python-pil git && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/*; \
|
||||
else \
|
||||
apk add --no-cache \
|
||||
python2 \
|
||||
python2-dev \
|
||||
py2-pip \
|
||||
git \
|
||||
gcc \
|
||||
openssh \
|
||||
libc6-compat \
|
||||
jpeg-dev \
|
||||
zlib-dev \
|
||||
freetype-dev \
|
||||
lcms2-dev \
|
||||
openjpeg-dev \
|
||||
tiff-dev \
|
||||
libc-dev \
|
||||
linux-headers; \
|
||||
fi" && \
|
||||
pip install --no-cache-dir platformio && \
|
||||
platformio settings set enable_telemetry No
|
||||
|
||||
|
||||
# Create fake project to make platformio install all depdencies.
|
||||
# * Ignore build errors from platformio - empty project
|
||||
# * On alpine, only install ESP8266 toolchain
|
||||
COPY platformio.ini /pio/platformio.ini
|
||||
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
|
||||
platformio run -e espressif32 -e espressif8266 -d /pio; exit 0; \
|
||||
else \
|
||||
echo \"\$(head -8 /pio/platformio.ini)\" >/pio/platformio.ini; \
|
||||
platformio run -e espressif8266 -d /pio; exit 0; \
|
||||
fi"
|
||||
|
||||
# Install latest esphomeyaml from git
|
||||
RUN pip install --no-cache-dir \
|
||||
git+git://github.com/OttoWinter/esphomeyaml.git && \
|
||||
pip install --no-cache-dir pillow tzlocal
|
||||
|
||||
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
|
||||
10
esphomeyaml-edge/build.json
Normal file
10
esphomeyaml-edge/build.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"squash": false,
|
||||
"build_from": {
|
||||
"aarch64": "arm64v8/ubuntu:bionic",
|
||||
"amd64": "ubuntu:bionic",
|
||||
"armhf": "homeassistant/armhf-base:latest",
|
||||
"i386": "i386/ubuntu:bionic"
|
||||
},
|
||||
"args": {}
|
||||
}
|
||||
33
esphomeyaml-edge/config.json
Normal file
33
esphomeyaml-edge/config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "esphomeyaml-edge",
|
||||
"version": "dev",
|
||||
"slug": "esphomeyaml-edge",
|
||||
"description": "Development build of the esphomeyaml HassIO add-on.",
|
||||
"url": "https://esphomelib.com/esphomeyaml/index.html",
|
||||
"startup": "application",
|
||||
"webui": "http://[HOST]:[PORT:6052]",
|
||||
"boot": "auto",
|
||||
"ports": {
|
||||
"6052/tcp": 6052,
|
||||
"6053/tcp": 6053
|
||||
},
|
||||
"arch": [
|
||||
"aarch64",
|
||||
"amd64",
|
||||
"armhf",
|
||||
"i386"
|
||||
],
|
||||
"auto_uart": true,
|
||||
"map": [
|
||||
"config:rw"
|
||||
],
|
||||
"options": {
|
||||
"password": ""
|
||||
},
|
||||
"schema": {
|
||||
"password": "str?"
|
||||
},
|
||||
"environment": {
|
||||
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
|
||||
}
|
||||
}
|
||||
12
esphomeyaml-edge/platformio.ini
Normal file
12
esphomeyaml-edge/platformio.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
; This file allows the docker build file to install the required platformio
|
||||
; platforms
|
||||
|
||||
[env:espressif8266]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
|
||||
[env:espressif32]
|
||||
platform = espressif32
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
@@ -5,50 +5,46 @@ import logging
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const
|
||||
from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOGGER, \
|
||||
CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI
|
||||
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \
|
||||
color, get_variable, indent, quote, statement, Expression
|
||||
from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util
|
||||
from esphomeyaml.config import get_component, iter_components, read_config
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF_ESPHOMEYAML, \
|
||||
CONF_HOSTNAME, CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_USE_CUSTOM_CODE, \
|
||||
CONF_WIFI, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \
|
||||
_EXPRESSIONS, add, \
|
||||
add_job, color, flush_tasks, indent, quote, statement, relative_path
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
_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))
|
||||
|
||||
|
||||
def discover_serial_ports():
|
||||
def get_serial_ports():
|
||||
# from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
|
||||
try:
|
||||
from serial.tools.list_ports import comports
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
from serial.tools.list_ports import comports
|
||||
result = []
|
||||
descs = []
|
||||
for port, desc, info in comports():
|
||||
if not port:
|
||||
continue
|
||||
if "VID:PID" in info:
|
||||
result.append(port)
|
||||
descs.append(desc)
|
||||
result.append((port, desc))
|
||||
return result
|
||||
|
||||
|
||||
def choose_serial_port(config):
|
||||
result = get_serial_ports()
|
||||
|
||||
if not result:
|
||||
return None
|
||||
print(u"Found multiple serial port options, please choose one:")
|
||||
for i, (res, desc) in enumerate(zip(result, descs)):
|
||||
print(u" [{}] {} ({})".format(i, res, desc))
|
||||
print(u" [{}] Over The Air".format(len(result)))
|
||||
print()
|
||||
return 'OTA'
|
||||
safe_print(u"Found multiple serial port options, please choose one:")
|
||||
for i, (res, desc) in enumerate(result):
|
||||
safe_print(u" [{}] {} ({})".format(i, res, desc))
|
||||
safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
|
||||
safe_print()
|
||||
while True:
|
||||
opt = raw_input('(number): ')
|
||||
if opt in result:
|
||||
@@ -60,13 +56,13 @@ def discover_serial_ports():
|
||||
raise ValueError
|
||||
break
|
||||
except ValueError:
|
||||
print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||
if opt == len(result):
|
||||
return None
|
||||
return result[opt]
|
||||
return 'OTA'
|
||||
return result[opt][0]
|
||||
|
||||
|
||||
def run_platformio(*cmd):
|
||||
def run_platformio(*cmd, **kwargs):
|
||||
def mock_exit(return_code):
|
||||
raise SystemExit(return_code)
|
||||
|
||||
@@ -75,10 +71,13 @@ def run_platformio(*cmd):
|
||||
full_cmd = u' '.join(quote(x) for x in cmd)
|
||||
_LOGGER.info(u"Running: %s", full_cmd)
|
||||
try:
|
||||
import platformio.__main__
|
||||
func = kwargs.get('main')
|
||||
if func is None:
|
||||
import platformio.__main__
|
||||
func = platformio.__main__.main
|
||||
sys.argv = list(cmd)
|
||||
sys.exit = mock_exit
|
||||
return platformio.__main__.main()
|
||||
return func() or 0
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
except SystemExit as err:
|
||||
@@ -91,109 +90,159 @@ def run_platformio(*cmd):
|
||||
sys.exit = orig_exit
|
||||
|
||||
|
||||
def run_miniterm(config, port):
|
||||
from serial.tools import miniterm
|
||||
baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
|
||||
sys.argv = ['miniterm', '--raw', '--exit-char', '3']
|
||||
miniterm.main(
|
||||
default_port=port,
|
||||
default_baudrate=baud_rate)
|
||||
def run_miniterm(config, port, escape=False):
|
||||
import serial
|
||||
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)
|
||||
|
||||
with serial.Serial(port, baudrate=baud_rate) as ser:
|
||||
while True:
|
||||
try:
|
||||
raw = ser.readline()
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return
|
||||
line = raw.replace('\r', '').replace('\n', '')
|
||||
time = datetime.now().time().strftime('[%H:%M:%S]')
|
||||
message = time + line
|
||||
if escape:
|
||||
message = message.replace('\033', '\\033')
|
||||
safe_print(message)
|
||||
|
||||
|
||||
def write_cpp(config):
|
||||
_LOGGER.info("Generating C++ source...")
|
||||
|
||||
add_task(core_to_code, config[CONF_ESPHOMEYAML])
|
||||
add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml')
|
||||
for domain in PRE_INITIALIZE:
|
||||
if domain == CONF_ESPHOMEYAML:
|
||||
if domain == CONF_ESPHOMEYAML or domain not in config:
|
||||
continue
|
||||
if domain in config:
|
||||
add_task(get_component(domain).to_code, config[domain])
|
||||
|
||||
# Clear queue
|
||||
get_variable(None)
|
||||
add(RawStatement(''))
|
||||
add_job(get_component(domain).to_code, config[domain], domain=domain)
|
||||
|
||||
for domain, component, conf in iter_components(config):
|
||||
if domain in PRE_INITIALIZE:
|
||||
if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'):
|
||||
continue
|
||||
if not hasattr(component, 'to_code'):
|
||||
continue
|
||||
add_task(component.to_code, conf)
|
||||
add_job(component.to_code, conf, domain=domain)
|
||||
|
||||
# Clear queue
|
||||
get_variable(None)
|
||||
flush_tasks()
|
||||
add(RawStatement(''))
|
||||
add(RawStatement(''))
|
||||
|
||||
all_code = []
|
||||
for exp in _EXPRESSIONS:
|
||||
if core.SIMPLIFY:
|
||||
if 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)))
|
||||
|
||||
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)
|
||||
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
|
||||
writer.write_platformio_project(config, build_path)
|
||||
|
||||
code_s = indent('\n'.join(line.rstrip() for line in all_code))
|
||||
cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp')
|
||||
cpp_path = os.path.join(build_path, 'src', 'main.cpp')
|
||||
writer.write_cpp(code_s, cpp_path)
|
||||
return 0
|
||||
|
||||
|
||||
def compile_program(config):
|
||||
def compile_program(args, config):
|
||||
_LOGGER.info("Compiling app...")
|
||||
return run_platformio('platformio', 'run', '-d', get_base_path(config))
|
||||
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
|
||||
command = ['platformio', 'run', '-d', build_path]
|
||||
if args.verbose:
|
||||
command.append('-v')
|
||||
return run_platformio(*command)
|
||||
|
||||
|
||||
def get_upload_host(config):
|
||||
if CONF_MANUAL_IP in config[CONF_WIFI]:
|
||||
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
|
||||
elif CONF_HOSTNAME in config[CONF_WIFI]:
|
||||
host = config[CONF_WIFI][CONF_HOSTNAME] + config[CONF_WIFI][CONF_DOMAIN]
|
||||
else:
|
||||
host = config[CONF_ESPHOMEYAML][CONF_NAME] + config[CONF_WIFI][CONF_DOMAIN]
|
||||
return host
|
||||
|
||||
|
||||
def upload_using_esptool(config, port):
|
||||
import esptool
|
||||
|
||||
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
|
||||
path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
|
||||
# pylint: disable=protected-access
|
||||
return run_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
|
||||
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0',
|
||||
path, main=esptool._main)
|
||||
|
||||
|
||||
def upload_program(config, args, port):
|
||||
_LOGGER.info("Uploading binary...")
|
||||
if port is not None:
|
||||
return run_platformio('platformio', 'run', '-d', get_base_path(config),
|
||||
'-t', 'upload', '--upload-port', port)
|
||||
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
|
||||
|
||||
# if upload is to a serial port use platformio, otherwise assume ota
|
||||
serial_port = port.startswith('/') or port.startswith('COM')
|
||||
if port != 'OTA' and serial_port:
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy:
|
||||
return upload_using_esptool(config, port)
|
||||
command = ['platformio', 'run', '-d', build_path,
|
||||
'-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
|
||||
|
||||
if CONF_MANUAL_IP in config[CONF_WIFI]:
|
||||
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
|
||||
elif CONF_HOSTNAME in config[CONF_WIFI]:
|
||||
host = config[CONF_WIFI][CONF_HOSTNAME] + u'.local'
|
||||
# If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf
|
||||
# hostname. This is to support use cases where zeroconf (hostname.local) does not work.
|
||||
if port != 'OTA':
|
||||
host = port
|
||||
else:
|
||||
host = config[CONF_ESPHOMEYAML][CONF_NAME] + u'.local'
|
||||
host = get_upload_host(config)
|
||||
|
||||
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')
|
||||
bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
|
||||
if args.host_port is not None:
|
||||
host_port = args.host_port
|
||||
else:
|
||||
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)]
|
||||
return espota.main(espota_args)
|
||||
|
||||
verbose = args.verbose
|
||||
remote_port = ota.get_port(config)
|
||||
password = ota.get_auth(config)
|
||||
|
||||
res = espota2.run_ota(host, remote_port, password, bin_file)
|
||||
if res == 0:
|
||||
return res
|
||||
_LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...")
|
||||
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, bin_file)
|
||||
|
||||
|
||||
def show_logs(config, args, port):
|
||||
if port is not None and port != 'OTA':
|
||||
run_miniterm(config, port)
|
||||
def show_logs(config, args, port, escape=False):
|
||||
serial_port = port.startswith('/') or port.startswith('COM')
|
||||
if port != 'OTA' and serial_port:
|
||||
run_miniterm(config, port, escape=escape)
|
||||
return 0
|
||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
|
||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
|
||||
escape=escape)
|
||||
|
||||
|
||||
def clean_mqtt(config, args):
|
||||
return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id)
|
||||
|
||||
|
||||
def setup_log():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
def setup_log(debug=False):
|
||||
log_level = logging.DEBUG if debug else logging.INFO
|
||||
logging.basicConfig(level=log_level)
|
||||
fmt = "%(levelname)s [%(name)s] %(message)s"
|
||||
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
|
||||
datefmt = '%H:%M:%S'
|
||||
@@ -218,22 +267,154 @@ def setup_log():
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
setup_log()
|
||||
def command_wizard(args):
|
||||
return wizard.wizard(args.configuration)
|
||||
|
||||
|
||||
def strip_default_ids(config):
|
||||
value = config
|
||||
if isinstance(config, list):
|
||||
value = type(config)()
|
||||
for x in config:
|
||||
if isinstance(x, core.ID) and not x.is_manual:
|
||||
continue
|
||||
value.append(strip_default_ids(x))
|
||||
return value
|
||||
elif isinstance(config, dict):
|
||||
value = type(config)()
|
||||
for k, v in config.iteritems():
|
||||
if isinstance(v, core.ID) and not v.is_manual:
|
||||
continue
|
||||
value[k] = strip_default_ids(v)
|
||||
return value
|
||||
return value
|
||||
|
||||
|
||||
def command_config(args, config):
|
||||
if not args.verbose:
|
||||
config = strip_default_ids(config)
|
||||
safe_print(yaml_util.dump(config))
|
||||
return 0
|
||||
|
||||
|
||||
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
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
return 0
|
||||
|
||||
|
||||
def command_upload(args, config):
|
||||
port = args.upload_port or choose_serial_port(config)
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
return 0
|
||||
|
||||
|
||||
def command_logs(args, config):
|
||||
port = args.serial_port or choose_serial_port(config)
|
||||
return show_logs(config, args, port, escape=args.escape)
|
||||
|
||||
|
||||
def command_run(args, config):
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
exit_code = compile_program(args, config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
port = args.upload_port or choose_serial_port(config)
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
if args.no_logs:
|
||||
return 0
|
||||
return show_logs(config, args, port, escape=args.escape)
|
||||
|
||||
|
||||
def command_clean_mqtt(args, config):
|
||||
return clean_mqtt(config, args)
|
||||
|
||||
|
||||
def command_mqtt_fingerprint(args, config):
|
||||
return mqtt.get_fingerprint(config)
|
||||
|
||||
|
||||
def command_version(args):
|
||||
safe_print(u"Version: {}".format(const.__version__))
|
||||
return 0
|
||||
|
||||
|
||||
def command_clean(args, config):
|
||||
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
|
||||
try:
|
||||
writer.clean_build(build_path)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Error deleting build files: %s", err)
|
||||
return 1
|
||||
_LOGGER.info("Done!")
|
||||
return 0
|
||||
|
||||
|
||||
def command_dashboard(args):
|
||||
from esphomeyaml.dashboard import dashboard
|
||||
|
||||
return dashboard.start_web_server(args)
|
||||
|
||||
|
||||
PRE_CONFIG_ACTIONS = {
|
||||
'wizard': command_wizard,
|
||||
'version': command_version,
|
||||
'dashboard': command_dashboard
|
||||
}
|
||||
|
||||
POST_CONFIG_ACTIONS = {
|
||||
'config': command_config,
|
||||
'compile': command_compile,
|
||||
'upload': command_upload,
|
||||
'logs': command_logs,
|
||||
'run': command_run,
|
||||
'clean-mqtt': command_clean_mqtt,
|
||||
'mqtt-fingerprint': command_mqtt_fingerprint,
|
||||
'clean': command_clean,
|
||||
}
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
parser = argparse.ArgumentParser(prog='esphomeyaml')
|
||||
parser.add_argument('-v', '--verbose', help="Enable verbose esphomeyaml logs.",
|
||||
action='store_true')
|
||||
parser.add_argument('configuration', help='Your YAML configuration file.')
|
||||
|
||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
||||
subparsers.required = True
|
||||
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
||||
|
||||
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.')
|
||||
@@ -243,10 +424,12 @@ def main():
|
||||
parser_logs.add_argument('--client-id', help='Manually set the client id.')
|
||||
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
|
||||
"For example /dev/cu.SLAB_USBtoUART.")
|
||||
parser_logs.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
|
||||
action='store_true')
|
||||
|
||||
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
|
||||
'upload it, and start MQTT logs.')
|
||||
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.',
|
||||
@@ -255,6 +438,11 @@ def main():
|
||||
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
|
||||
parser_run.add_argument('--password', help='Manually set the MQTT password for logs.')
|
||||
parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
|
||||
parser_run.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
|
||||
action='store_true')
|
||||
parser_run.add_argument('--use-esptoolpy',
|
||||
help="Use esptool.py for the uploading (only for ESP8266)",
|
||||
action='store_true')
|
||||
|
||||
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
|
||||
"retain messages.")
|
||||
@@ -267,12 +455,32 @@ def main():
|
||||
"you through setting up esphomeyaml.")
|
||||
|
||||
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
|
||||
|
||||
subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
|
||||
|
||||
args = parser.parse_args()
|
||||
subparsers.add_parser('clean', help="Delete all temporary build files.")
|
||||
|
||||
if args.command == 'wizard':
|
||||
return wizard.wizard(args.configuration)
|
||||
dashboard = subparsers.add_parser('dashboard',
|
||||
help="Create a simple web server for a dashboard.")
|
||||
dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int,
|
||||
default=6052)
|
||||
dashboard.add_argument("--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')
|
||||
|
||||
return parser.parse_args(argv[1:])
|
||||
|
||||
|
||||
def run_esphomeyaml(argv):
|
||||
args = parse_args(argv)
|
||||
setup_log(args.verbose)
|
||||
if args.command in PRE_CONFIG_ACTIONS:
|
||||
try:
|
||||
return PRE_CONFIG_ACTIONS[args.command](args)
|
||||
except ESPHomeYAMLError as e:
|
||||
_LOGGER.error(e)
|
||||
return 1
|
||||
|
||||
core.CONFIG_PATH = args.configuration
|
||||
|
||||
@@ -280,54 +488,25 @@ def main():
|
||||
if config is None:
|
||||
return 1
|
||||
|
||||
if args.command == 'config':
|
||||
print(yaml_util.dump(config))
|
||||
return 0
|
||||
elif args.command == 'compile':
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
exit_code = compile_program(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
return 0
|
||||
elif args.command == 'upload':
|
||||
port = args.upload_port or discover_serial_ports()
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
return 0
|
||||
elif args.command == 'logs':
|
||||
port = args.serial_port or discover_serial_ports()
|
||||
return show_logs(config, args, port)
|
||||
elif args.command == 'clean-mqtt':
|
||||
return clean_mqtt(config, args)
|
||||
elif args.command == 'mqtt-fingerprint':
|
||||
return mqtt.get_fingerprint(config)
|
||||
elif args.command == 'run':
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
exit_code = compile_program(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
port = args.upload_port or discover_serial_ports()
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
if args.no_logs:
|
||||
return 0
|
||||
return show_logs(config, args, port)
|
||||
elif args.command == 'version':
|
||||
print(u"Version: {}".format(const.__version__))
|
||||
return 0
|
||||
print(u"Unknown command {}".format(args.command))
|
||||
if args.command in POST_CONFIG_ACTIONS:
|
||||
try:
|
||||
return POST_CONFIG_ACTIONS[args.command](args, config)
|
||||
except ESPHomeYAMLError as e:
|
||||
_LOGGER.error(e)
|
||||
return 1
|
||||
safe_print(u"Unknown command {}".format(args.command))
|
||||
return 1
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
return run_esphomeyaml(sys.argv)
|
||||
except ESPHomeYAMLError as e:
|
||||
_LOGGER.error(e)
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
276
esphomeyaml/automation.py
Normal file
276
esphomeyaml/automation.py
Normal file
@@ -0,0 +1,276 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import core
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \
|
||||
CONF_BELOW, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, \
|
||||
CONF_ELSE, CONF_ID, CONF_IF, CONF_LAMBDA, \
|
||||
CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, add_job, \
|
||||
esphomelib_ns, float_, process_lambda, templatable, uint32, get_variable
|
||||
from esphomeyaml.util import ServiceRegistry
|
||||
|
||||
|
||||
def maybe_simple_id(*validators):
|
||||
validator = vol.All(*validators)
|
||||
|
||||
def validate(value):
|
||||
if isinstance(value, dict):
|
||||
return validator(value)
|
||||
return validator({CONF_ID: value})
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
def validate_recursive_condition(value):
|
||||
return CONDITIONS_SCHEMA(value)
|
||||
|
||||
|
||||
def validate_recursive_action(value):
|
||||
value = cv.ensure_list(value)
|
||||
for i, item in enumerate(value):
|
||||
if not isinstance(item, dict):
|
||||
raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item))
|
||||
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))
|
||||
if key not in ACTION_REGISTRY:
|
||||
raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?"
|
||||
u"".format(key))
|
||||
item.setdefault(CONF_ACTION_ID, None)
|
||||
key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None)
|
||||
if key2 is not None:
|
||||
raise vol.Invalid(u"Cannot have two actions in one item. Key {} overrides {}!"
|
||||
u"".format(key, key2))
|
||||
validator = ACTION_REGISTRY[key][0]
|
||||
value[i] = {
|
||||
CONF_ACTION_ID: cv.declare_variable_id(None)(item[CONF_ACTION_ID]),
|
||||
key: validator(item[key])
|
||||
}
|
||||
return value
|
||||
|
||||
|
||||
ACTION_REGISTRY = ServiceRegistry()
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
DelayAction = esphomelib_ns.DelayAction
|
||||
LambdaAction = esphomelib_ns.LambdaAction
|
||||
IfAction = esphomelib_ns.IfAction
|
||||
UpdateComponentAction = esphomelib_ns.UpdateComponentAction
|
||||
Automation = esphomelib_ns.Automation
|
||||
|
||||
CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [cv.templatable({
|
||||
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_,
|
||||
})])
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
AndCondition = esphomelib_ns.AndCondition
|
||||
OrCondition = esphomelib_ns.OrCondition
|
||||
RangeCondition = esphomelib_ns.RangeCondition
|
||||
LambdaCondition = esphomelib_ns.LambdaCondition
|
||||
|
||||
|
||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||
schema = AUTOMATION_SCHEMA.extend(extra_schema or {})
|
||||
|
||||
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:
|
||||
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): validate_recursive_action,
|
||||
})
|
||||
|
||||
|
||||
def build_condition(config, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
if isinstance(config, core.Lambda):
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config, [(arg_type, 'x')]):
|
||||
yield
|
||||
yield LambdaCondition.new(template_arg, lambda_)
|
||||
elif CONF_AND in config:
|
||||
yield AndCondition.new(template_arg, build_conditions(config[CONF_AND], template_arg))
|
||||
elif CONF_OR in config:
|
||||
yield OrCondition.new(template_arg, build_conditions(config[CONF_OR], template_arg))
|
||||
elif CONF_LAMBDA in config:
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]):
|
||||
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)
|
||||
|
||||
|
||||
DELAY_ACTION_SCHEMA = cv.templatable(cv.positive_time_period_milliseconds)
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_DELAY, DELAY_ACTION_SCHEMA)
|
||||
def delay_action_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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 = TemplateArguments(arg_type)
|
||||
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
|
||||
|
||||
|
||||
LAMBDA_ACTION_SCHEMA = cv.lambda_
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_LAMBDA, LAMBDA_ACTION_SCHEMA)
|
||||
def lambda_action_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
for lambda_ in process_lambda(config, [(arg_type, 'x')]):
|
||||
yield None
|
||||
rhs = LambdaAction.new(template_arg, lambda_)
|
||||
type = LambdaAction.template(template_arg)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_COMPONENT_UPDATE = 'component.update'
|
||||
COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA)
|
||||
def component_update_action_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = UpdateComponentAction.new(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]
|
||||
for result in builder(config, action_id, arg_type):
|
||||
yield None
|
||||
yield result
|
||||
|
||||
|
||||
def build_actions(config, arg_type):
|
||||
actions = []
|
||||
for conf in config:
|
||||
action = None
|
||||
for action in build_action(conf, arg_type):
|
||||
yield None
|
||||
actions.append(action)
|
||||
yield ArrayInitializer(*actions, multiline=False)
|
||||
|
||||
|
||||
def build_automation_(trigger, arg_type, config):
|
||||
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 None
|
||||
add(obj.add_conditions(conditions))
|
||||
actions = None
|
||||
for actions in build_actions(config[CONF_THEN], arg_type):
|
||||
yield None
|
||||
add(obj.add_actions(actions))
|
||||
yield obj
|
||||
|
||||
|
||||
def build_automation(trigger, arg_type, config):
|
||||
add_job(build_automation_, trigger, arg_type, config)
|
||||
@@ -1,17 +1,18 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
ADS1115_COMPONENT_CLASS = 'sensor::ADS1115Component'
|
||||
ADS1115Component = sensor.sensor_ns.ADS1115Component
|
||||
|
||||
RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required."""
|
||||
|
||||
ADS1115_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('ads1115'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(ADS1115Component),
|
||||
vol.Required(CONF_ADDRESS): cv.i2c_address,
|
||||
|
||||
vol.Optional(CONF_RATE): cv.invalid(RATE_REMOVE_MESSAGE)
|
||||
@@ -23,7 +24,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
|
||||
Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED, CONF_MQTT_ID
|
||||
from esphomeyaml.helpers import add, setup_mqtt_component, App, Pvariable
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INTERNAL, 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, CONF_FILTERS, CONF_INVERT, CONF_DELAYED_ON, \
|
||||
CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT
|
||||
from esphomeyaml.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns, \
|
||||
setup_mqtt_component, bool_, process_lambda, ArrayInitializer
|
||||
|
||||
DEVICE_CLASSES = [
|
||||
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
|
||||
@@ -11,39 +16,142 @@ DEVICE_CLASSES = [
|
||||
'sound', 'vibration', 'window'
|
||||
]
|
||||
|
||||
DEVICE_CLASSES_MSG = "Unknown device class. Must be one of {}".format(', '.join(DEVICE_CLASSES))
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower,
|
||||
vol.Any(*DEVICE_CLASSES, msg=DEVICE_CLASSES_MSG)),
|
||||
})
|
||||
|
||||
MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
MQTT_BINARY_SENSOR_ID_SCHEMA = MQTT_BINARY_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID('mqtt_binary_sensor', CONF_MQTT_ID): cv.register_variable_id,
|
||||
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
|
||||
InvertFilter = binary_sensor_ns.InvertFilter
|
||||
LambdaFilter = binary_sensor_ns.LambdaFilter
|
||||
DelayedOnFilter = binary_sensor_ns.DelayedOnFilter
|
||||
DelayedOffFilter = binary_sensor_ns.DelayedOffFilter
|
||||
HeartbeatFilter = binary_sensor_ns.HeartbeatFilter
|
||||
MQTTBinarySensorComponent = binary_sensor_ns.MQTTBinarySensorComponent
|
||||
|
||||
FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT]
|
||||
|
||||
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
|
||||
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))])
|
||||
|
||||
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_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)),
|
||||
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_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_binary_sensor(obj, config):
|
||||
|
||||
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:
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')]):
|
||||
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(obj.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
add(binary_sensor_var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
if CONF_INVERTED in config:
|
||||
add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
add(binary_sensor_var.set_inverted(config[CONF_INVERTED]))
|
||||
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()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
for conf in config.get(CONF_ON_RELEASE, []):
|
||||
rhs = binary_sensor_var.make_release_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
for conf in config.get(CONF_ON_CLICK, []):
|
||||
rhs = binary_sensor_var.make_click_trigger(conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH])
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
|
||||
rhs = binary_sensor_var.make_double_click_trigger(conf[CONF_MIN_LENGTH],
|
||||
conf[CONF_MAX_LENGTH])
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
|
||||
def setup_mqtt_binary_sensor(obj, config):
|
||||
setup_mqtt_component(obj, config)
|
||||
def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config):
|
||||
binary_sensor_var = Pvariable(config[CONF_ID], binary_sensor_obj,
|
||||
has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj,
|
||||
has_side_effects=False)
|
||||
add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
def register_binary_sensor(var, config):
|
||||
setup_binary_sensor(var, config)
|
||||
rhs = App.register_binary_sensor(var)
|
||||
mqtt_sensor = Pvariable('binary_sensor::MQTTBinarySensorComponent', config[CONF_MQTT_ID], rhs)
|
||||
setup_mqtt_binary_sensor(mqtt_sensor, config)
|
||||
binary_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
|
||||
rhs = App.register_binary_sensor(binary_sensor_var)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
|
||||
add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_MAC_ADDRESS, CONF_NAME, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.core import HexInt, MACAddress
|
||||
from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
DEPENDENCIES = ['esp32_ble']
|
||||
|
||||
|
||||
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({
|
||||
cv.GenerateID('esp32_ble_device'): cv.register_variable_id,
|
||||
vol.Required(CONF_MAC_ADDRESS): validate_mac,
|
||||
}).extend(binary_sensor.MQTT_BINARY_SENSOR_ID_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = get_variable(None, type='ESP32BLETracker')
|
||||
addr = [HexInt(i) for i in config[CONF_MAC_ADDRESS].parts]
|
||||
rhs = hub.make_device(config[CONF_NAME], ArrayInitializer(*addr, multiline=False))
|
||||
device = Pvariable('ESP32BLEDevice', config[CONF_ID], rhs)
|
||||
binary_sensor.register_binary_sensor(device, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
|
||||
23
esphomeyaml/components/binary_sensor/esp32_ble_tracker.py
Normal file
23
esphomeyaml/components/binary_sensor/esp32_ble_tracker.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
|
||||
from esphomeyaml.helpers import get_variable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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)
|
||||
@@ -2,25 +2,28 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import Pvariable, RawExpression, get_variable
|
||||
from esphomeyaml.components.esp32_touch import ESP32TouchComponent
|
||||
from esphomeyaml.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import get_variable, global_ns
|
||||
from esphomeyaml.pins import validate_gpio_pin
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
DEPENDENCIES = ['esp32_touch']
|
||||
|
||||
CONF_ESP32_TOUCH_ID = 'esp32_touch_id'
|
||||
|
||||
TOUCH_PADS = {
|
||||
4: 'TOUCH_PAD_NUM0',
|
||||
0: 'TOUCH_PAD_NUM1',
|
||||
2: 'TOUCH_PAD_NUM2',
|
||||
15: 'TOUCH_PAD_NUM3',
|
||||
13: 'TOUCH_PAD_NUM4',
|
||||
12: 'TOUCH_PAD_NUM5',
|
||||
14: 'TOUCH_PAD_NUM6',
|
||||
27: 'TOUCH_PAD_NUM7',
|
||||
33: 'TOUCH_PAD_NUM8',
|
||||
32: 'TOUCH_PAD_NUM9',
|
||||
4: global_ns.TOUCH_PAD_NUM0,
|
||||
0: global_ns.TOUCH_PAD_NUM1,
|
||||
2: global_ns.TOUCH_PAD_NUM2,
|
||||
15: global_ns.TOUCH_PAD_NUM3,
|
||||
13: global_ns.TOUCH_PAD_NUM4,
|
||||
12: global_ns.TOUCH_PAD_NUM5,
|
||||
14: global_ns.TOUCH_PAD_NUM6,
|
||||
27: global_ns.TOUCH_PAD_NUM7,
|
||||
33: global_ns.TOUCH_PAD_NUM8,
|
||||
32: global_ns.TOUCH_PAD_NUM9,
|
||||
}
|
||||
|
||||
|
||||
@@ -31,19 +34,20 @@ def validate_touch_pad(value):
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('esp32_touch_pad'): cv.register_variable_id,
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_PIN): validate_touch_pad,
|
||||
vol.Required(CONF_THRESHOLD): cv.uint16_t,
|
||||
}).extend(binary_sensor.MQTT_BINARY_SENSOR_ID_SCHEMA.schema)
|
||||
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_variable_id(ESP32TouchComponent),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = get_variable(None, type='binary_sensor::ESP32TouchComponent')
|
||||
touch_pad = RawExpression(TOUCH_PADS[config[CONF_PIN]])
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ESP32_TOUCH_ID]):
|
||||
yield
|
||||
touch_pad = TOUCH_PADS[config[CONF_PIN]]
|
||||
rhs = hub.make_touch_pad(config[CONF_NAME], touch_pad, config[CONF_THRESHOLD])
|
||||
device = Pvariable('ESP32TouchBinarySensor', config[CONF_ID], rhs)
|
||||
binary_sensor.register_binary_sensor(device, config)
|
||||
binary_sensor.register_binary_sensor(rhs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'
|
||||
|
||||
@@ -3,23 +3,24 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, add, variable, gpio_input_pin_expression
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('gpio_binary_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): pins.GPIO_INPUT_PIN_SCHEMA
|
||||
}).extend(binary_sensor.MQTT_BINARY_SENSOR_SCHEMA.schema)
|
||||
MakeGPIOBinarySensor = Application.MakeGPIOBinarySensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOBinarySensor),
|
||||
vol.Required(CONF_PIN): pins.gpio_input_pin_schema
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_gpio_binary_sensor(config[CONF_NAME],
|
||||
gpio_input_pin_expression(config[CONF_PIN]))
|
||||
gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs)
|
||||
if CONF_INVERTED in config:
|
||||
add(gpio.Pgpio.set_inverted(config[CONF_INVERTED]))
|
||||
binary_sensor.setup_binary_sensor(gpio.Pgpio, config)
|
||||
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config)
|
||||
pin = None
|
||||
for pin in gpio_input_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin)
|
||||
gpio = variable(config[CONF_MAKE_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(gpio.Pgpio, gpio.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'
|
||||
|
||||
26
esphomeyaml/components/binary_sensor/nextion.py
Normal file
26
esphomeyaml/components/binary_sensor/nextion.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.display.nextion import Nextion
|
||||
from esphomeyaml.const import CONF_COMPONENT_ID, CONF_NAME, CONF_PAGE_ID
|
||||
from esphomeyaml.helpers import get_variable
|
||||
|
||||
DEPENDENCIES = ['display']
|
||||
|
||||
CONF_NEXTION_ID = 'nextion_id'
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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):
|
||||
hub = None
|
||||
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)
|
||||
42
esphomeyaml/components/binary_sensor/pn532.py
Normal file
42
esphomeyaml/components/binary_sensor/pn532.py
Normal file
@@ -0,0 +1,42 @@
|
||||
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.helpers import ArrayInitializer, get_variable
|
||||
|
||||
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
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_UID): validate_uid,
|
||||
cv.GenerateID(CONF_PN532_ID): cv.use_variable_id(PN532Component)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
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)
|
||||
23
esphomeyaml/components/binary_sensor/rdm6300.py
Normal file
23
esphomeyaml/components/binary_sensor/rdm6300.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor, rdm6300
|
||||
from esphomeyaml.const import CONF_NAME, CONF_UID
|
||||
from esphomeyaml.helpers import get_variable
|
||||
|
||||
DEPENDENCIES = ['rdm6300']
|
||||
|
||||
CONF_RDM6300_ID = 'rdm6300_id'
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_UID): cv.uint32_t,
|
||||
cv.GenerateID(CONF_RDM6300_ID): cv.use_variable_id(rdm6300.RDM6300Component)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
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)
|
||||
121
esphomeyaml/components/binary_sensor/remote_receiver.py
Normal file
121
esphomeyaml/components/binary_sensor/remote_receiver.py
Normal file
@@ -0,0 +1,121 @@
|
||||
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.helpers import ArrayInitializer, Pvariable, get_variable
|
||||
|
||||
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.RemoteReceiver
|
||||
LGReceiver = remote_ns.LGReceiver
|
||||
NECReceiver = remote_ns.NECReceiver
|
||||
PanasonicReceiver = remote_ns.PanasonicReceiver
|
||||
RawReceiver = remote_ns.RawReceiver
|
||||
SamsungReceiver = remote_ns.SamsungReceiver
|
||||
SonyReceiver = remote_ns.SonyReceiver
|
||||
RCSwitchRawReceiver = remote_ns.RCSwitchRawReceiver
|
||||
RCSwitchTypeAReceiver = remote_ns.RCSwitchTypeAReceiver
|
||||
RCSwitchTypeBReceiver = remote_ns.RCSwitchTypeBReceiver
|
||||
RCSwitchTypeCReceiver = remote_ns.RCSwitchTypeCReceiver
|
||||
RCSwitchTypeDReceiver = remote_ns.RCSwitchTypeDReceiver
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_LG): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)),
|
||||
}),
|
||||
vol.Optional(CONF_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): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)),
|
||||
}),
|
||||
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])
|
||||
elif key == CONF_NEC:
|
||||
return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
elif key == CONF_PANASONIC:
|
||||
return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
elif key == CONF_SAMSUNG:
|
||||
return SamsungReceiver.new(name, config[CONF_DATA])
|
||||
elif key == CONF_SONY:
|
||||
return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
|
||||
elif key == CONF_RAW:
|
||||
data = ArrayInitializer(*config, multiline=False)
|
||||
return RawReceiver.new(name, data)
|
||||
elif key == CONF_RC_SWITCH_RAW:
|
||||
return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
|
||||
elif key == CONF_RC_SWITCH_TYPE_A:
|
||||
return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
binary_code(config[CONF_GROUP]),
|
||||
binary_code(config[CONF_DEVICE]),
|
||||
config[CONF_STATE])
|
||||
elif key == CONF_RC_SWITCH_TYPE_B:
|
||||
return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
config[CONF_ADDRESS], config[CONF_CHANNEL],
|
||||
config[CONF_STATE])
|
||||
elif key == CONF_RC_SWITCH_TYPE_C:
|
||||
return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
ord(config[CONF_FAMILY][0]) - ord('a'),
|
||||
config[CONF_GROUP], config[CONF_DEVICE],
|
||||
config[CONF_STATE])
|
||||
elif key == CONF_RC_SWITCH_TYPE_D:
|
||||
return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
|
||||
ord(config[CONF_GROUP][0]) - ord('a'),
|
||||
config[CONF_DEVICE], config[CONF_STATE])
|
||||
else:
|
||||
raise NotImplementedError("Unknown receiver type {}".format(config))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
remote = None
|
||||
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'
|
||||
@@ -1,20 +1,21 @@
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, variable
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('status_binary_sensor'): cv.register_variable_id,
|
||||
}).extend(binary_sensor.MQTT_BINARY_SENSOR_SCHEMA.schema)
|
||||
MakeStatusBinarySensor = Application.MakeStatusBinarySensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeStatusBinarySensor),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_status_binary_sensor(config[CONF_NAME])
|
||||
status = variable('Application::MakeStatusBinarySensor', config[CONF_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(status.Pstatus, config)
|
||||
binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config)
|
||||
status = variable(config[CONF_MAKE_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(status.Pstatus, status.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'
|
||||
|
||||
28
esphomeyaml/components/binary_sensor/template.py
Normal file
28
esphomeyaml/components/binary_sensor/template.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, process_lambda, variable, optional, bool_, add
|
||||
|
||||
MakeTemplateBinarySensor = Application.MakeTemplateBinarySensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateBinarySensor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
}))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
template_ = None
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(bool_)):
|
||||
yield
|
||||
add(make.Ptemplate_.set_template(template_))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR'
|
||||
90
esphomeyaml/components/cover/__init__.py
Normal file
90
esphomeyaml/components/cover/__init__.py
Normal file
@@ -0,0 +1,90 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_INTERNAL
|
||||
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \
|
||||
TemplateArguments, get_variable
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
cover_ns = esphomelib_ns.namespace('cover')
|
||||
Cover = cover_ns.Cover
|
||||
MQTTCoverComponent = cover_ns.MQTTCoverComponent
|
||||
CoverState = cover_ns.CoverState
|
||||
COVER_OPEN = cover_ns.COVER_OPEN
|
||||
COVER_CLOSED = cover_ns.COVER_CLOSED
|
||||
OpenAction = cover_ns.OpenAction
|
||||
CloseAction = cover_ns.CloseAction
|
||||
StopAction = cover_ns.StopAction
|
||||
|
||||
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(Cover),
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTCoverComponent),
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def setup_cover(cover_obj, mqtt_obj, config):
|
||||
cover_var = Pvariable(config[CONF_ID], cover_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
setup_cover_core_(cover_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_COVER'
|
||||
|
||||
CONF_COVER_OPEN = 'cover.open'
|
||||
COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA)
|
||||
def cover_open_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA)
|
||||
def cover_close_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA)
|
||||
def cover_stop_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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)
|
||||
47
esphomeyaml/components/cover/template.py
Normal file
47
esphomeyaml/components/cover/template.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.components import cover
|
||||
from esphomeyaml.const import CONF_CLOSE_ACTION, CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, \
|
||||
CONF_OPEN_ACTION, CONF_STOP_ACTION, CONF_OPTIMISTIC
|
||||
from esphomeyaml.helpers import App, Application, NoArg, add, process_lambda, variable, optional
|
||||
|
||||
MakeTemplateCover = Application.MakeTemplateCover
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateCover),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True),
|
||||
}), 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)
|
||||
|
||||
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:
|
||||
automation.build_automation(make.Ptemplate_.get_open_trigger(), NoArg,
|
||||
config[CONF_OPEN_ACTION])
|
||||
if CONF_CLOSE_ACTION in config:
|
||||
automation.build_automation(make.Ptemplate_.get_close_trigger(), NoArg,
|
||||
config[CONF_CLOSE_ACTION])
|
||||
if CONF_STOP_ACTION in config:
|
||||
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]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_COVER'
|
||||
@@ -2,22 +2,23 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
|
||||
DALLAS_COMPONENT_CLASS = 'sensor::DallasComponent'
|
||||
DallasComponent = sensor.sensor_ns.DallasComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
||||
cv.GenerateID('dallas'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(DallasComponent),
|
||||
vol.Required(CONF_PIN): pins.input_output_pin,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL))
|
||||
Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import config_validation as cv, pins
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
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
|
||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression, esphomelib_ns, \
|
||||
TemplateArguments, get_variable
|
||||
|
||||
|
||||
def validate_pin_number(value):
|
||||
@@ -14,11 +16,25 @@ def validate_pin_number(value):
|
||||
return value
|
||||
|
||||
|
||||
DeepSleepComponent = esphomelib_ns.DeepSleepComponent
|
||||
EnterDeepSleepAction = esphomelib_ns.EnterDeepSleepAction
|
||||
PreventDeepSleepAction = esphomelib_ns.PreventDeepSleepAction
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('deep_sleep'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent),
|
||||
vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
|
||||
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_RUN_CYCLES): cv.positive_int,
|
||||
vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
@@ -26,12 +42,16 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_deep_sleep_component()
|
||||
deep_sleep = Pvariable('DeepSleepComponent', config[CONF_ID], rhs)
|
||||
deep_sleep = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION]))
|
||||
if CONF_WAKEUP_PIN in config:
|
||||
pin = gpio_input_pin_expression(config[CONF_WAKEUP_PIN])
|
||||
pin = None
|
||||
for pin in gpio_input_pin_expression(config[CONF_WAKEUP_PIN]):
|
||||
yield
|
||||
add(deep_sleep.set_wakeup_pin(pin))
|
||||
if CONF_WAKEUP_PIN_MODE in config:
|
||||
add(deep_sleep.set_wakeup_pin_mode(WAKEUP_PIN_MODES[config[CONF_WAKEUP_PIN_MODE]]))
|
||||
if CONF_RUN_CYCLES in config:
|
||||
add(deep_sleep.set_run_cycles(config[CONF_RUN_CYCLES]))
|
||||
if CONF_RUN_DURATION in config:
|
||||
@@ -39,3 +59,35 @@ def to_code(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 = TemplateArguments(arg_type)
|
||||
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 = TemplateArguments(arg_type)
|
||||
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)
|
||||
|
||||
56
esphomeyaml/components/display/__init__.py
Normal file
56
esphomeyaml/components/display/__init__.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# 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.helpers import add, add_job, esphomelib_ns
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
display_ns = esphomelib_ns.namespace('display')
|
||||
DisplayBuffer = display_ns.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):
|
||||
add_job(setup_display_core_, display_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DISPLAY'
|
||||
72
esphomeyaml/components/display/lcd_gpio.py
Normal file
72
esphomeyaml/components/display/lcd_gpio.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.const import CONF_DATA_PINS, CONF_DIMENSIONS, CONF_ENABLE_PIN, CONF_ID, \
|
||||
CONF_LAMBDA, CONF_RS_PIN, CONF_RW_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, process_lambda
|
||||
|
||||
GPIOLCDDisplay = display.display_ns.GPIOLCDDisplay
|
||||
LCDDisplay = display.display_ns.LCDDisplay
|
||||
LCDDisplayRef = LCDDisplay.operator('ref')
|
||||
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
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')]):
|
||||
yield
|
||||
add(lcd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(lcd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_LCD_DISPLAY'
|
||||
35
esphomeyaml/components/display/lcd_pcf8574.py
Normal file
35
esphomeyaml/components/display/lcd_pcf8574.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_DIMENSIONS, CONF_ID, CONF_LAMBDA
|
||||
from esphomeyaml.helpers import App, Pvariable, add, process_lambda
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
PCF8574LCDDisplay = display.display_ns.PCF8574LCDDisplay
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
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')]):
|
||||
yield
|
||||
add(lcd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(lcd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = ['-DUSE_LCD_DISPLAY', '-DUSE_LCD_DISPLAY_PCF8574']
|
||||
48
esphomeyaml/components/display/max7219.py
Normal file
48
esphomeyaml/components/display/max7219.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS, \
|
||||
CONF_SPI_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression, \
|
||||
process_lambda
|
||||
|
||||
DEPENDENCIES = ['spi']
|
||||
|
||||
MAX7219Component = display.display_ns.MAX7219Component
|
||||
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)),
|
||||
})
|
||||
|
||||
|
||||
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')]):
|
||||
yield
|
||||
add(max7219.set_writer(lambda_))
|
||||
|
||||
display.setup_display(max7219, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MAX7219'
|
||||
32
esphomeyaml/components/display/nextion.py
Normal file
32
esphomeyaml/components/display/nextion.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_UART_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, add, get_variable, process_lambda
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
Nextion = display.display_ns.Nextion
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
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')]):
|
||||
yield
|
||||
add(nextion.set_writer(lambda_))
|
||||
|
||||
display.setup_display(nextion, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_NEXTION'
|
||||
46
esphomeyaml/components/display/ssd1306_i2c.py
Normal file
46
esphomeyaml/components/display/ssd1306_i2c.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.display import ssd1306_spi
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, \
|
||||
CONF_MODEL, CONF_RESET_PIN, CONF_LAMBDA
|
||||
from esphomeyaml.helpers import App, Pvariable, add, \
|
||||
gpio_output_pin_expression, process_lambda
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
I2CSSD1306 = display.display_ns.I2CSSD1306
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
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')]):
|
||||
yield
|
||||
add(ssd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(ssd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SSD1306'
|
||||
68
esphomeyaml/components/display/ssd1306_spi.py
Normal file
68
esphomeyaml/components/display/ssd1306_spi.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, \
|
||||
CONF_ID, CONF_MODEL, \
|
||||
CONF_RESET_PIN, CONF_SPI_ID, CONF_LAMBDA
|
||||
from esphomeyaml.helpers import App, Pvariable, add, get_variable, \
|
||||
gpio_output_pin_expression, process_lambda
|
||||
|
||||
DEPENDENCIES = ['spi']
|
||||
|
||||
SPISSD1306 = display.display_ns.SPISSD1306
|
||||
|
||||
MODELS = {
|
||||
'SSD1306_128X32': display.display_ns.SSD1306_MODEL_128_32,
|
||||
'SSD1306_128X64': display.display_ns.SSD1306_MODEL_128_64,
|
||||
'SSD1306_96X16': display.display_ns.SSD1306_MODEL_96_16,
|
||||
'SSD1306_64X48': display.display_ns.SSD1306_MODEL_64_48,
|
||||
'SH1106_128X32': display.display_ns.SH1106_MODEL_128_32,
|
||||
'SH1106_128X64': display.display_ns.SH1106_MODEL_128_64,
|
||||
'SH1106_96X16': display.display_ns.SH1106_MODEL_96_16,
|
||||
'SH1106_64X48': display.display_ns.SH1106_MODEL_64_48,
|
||||
}
|
||||
|
||||
SSD1306_MODEL = vol.All(vol.Upper, vol.Replace(' ', '_'), cv.one_of(*MODELS))
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
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')]):
|
||||
yield
|
||||
add(ssd.set_writer(lambda_))
|
||||
|
||||
display.setup_display(ssd, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SSD1306'
|
||||
84
esphomeyaml/components/display/waveshare_epaper.py
Normal file
84
esphomeyaml/components/display/waveshare_epaper.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
from esphomeyaml.const import CONF_BUSY_PIN, CONF_CS_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \
|
||||
CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_input_pin_expression, \
|
||||
gpio_output_pin_expression, process_lambda
|
||||
|
||||
DEPENDENCIES = ['spi']
|
||||
|
||||
WaveshareEPaperTypeA = display.display_ns.WaveshareEPaperTypeA
|
||||
WaveshareEPaper = display.display_ns.WaveshareEPaper
|
||||
|
||||
MODELS = {
|
||||
'1.54in': ('a', display.display_ns.WAVESHARE_EPAPER_1_54_IN),
|
||||
'2.13in': ('a', display.display_ns.WAVESHARE_EPAPER_2_13_IN),
|
||||
'2.90in': ('a', display.display_ns.WAVESHARE_EPAPER_2_9_IN),
|
||||
'2.70in': ('b', display.display_ns.WAVESHARE_EPAPER_2_7_IN),
|
||||
'4.20in': ('b', display.display_ns.WAVESHARE_EPAPER_4_2_IN),
|
||||
'7.50in': ('b', display.display_ns.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(None),
|
||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Required(CONF_MODEL): vol.All(vol.Lower, cv.one_of(*MODELS)),
|
||||
vol.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,
|
||||
}), 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')]):
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_WAVESHARE_EPAPER'
|
||||
@@ -1,22 +1,5 @@
|
||||
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
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('esp32_ble'): cv.register_variable_id,
|
||||
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_esp32_ble_tracker()
|
||||
ble = Pvariable('ESP32BLETracker', 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'
|
||||
CONFIG_SCHEMA = cv.invalid("The 'esp32_ble' component has been renamed to the 'esp32_ble_tracker' "
|
||||
"component in order to avoid confusion with the new 'esp32_ble_beacon' "
|
||||
"component.")
|
||||
|
||||
35
esphomeyaml/components/esp32_ble_beacon.py
Normal file
35
esphomeyaml/components/esp32_ble_beacon.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_SCAN_INTERVAL, ESP_PLATFORM_ESP32, CONF_UUID, CONF_TYPE
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, RawExpression, ArrayInitializer
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
ESP32BLEBeacon = esphomelib_ns.ESP32BLEBeacon
|
||||
|
||||
CONF_MAJOR = 'major'
|
||||
CONF_MINOR = 'minor'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon),
|
||||
vol.Required(CONF_TYPE): vol.All(vol.Upper, cv.one_of('IBEACON')),
|
||||
vol.Required(CONF_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,
|
||||
})
|
||||
|
||||
|
||||
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]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_BEACON'
|
||||
31
esphomeyaml/components/esp32_ble_tracker.py
Normal file
31
esphomeyaml/components/esp32_ble_tracker.py
Normal file
@@ -0,0 +1,31 @@
|
||||
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.core import HexInt
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, ArrayInitializer
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
CONF_ESP32_BLE_ID = 'esp32_ble_id'
|
||||
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 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]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
|
||||
@@ -1,11 +1,12 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_SETUP_MODE, CONF_IIR_FILTER, \
|
||||
CONF_SLEEP_DURATION, CONF_MEASUREMENT_DURATION, CONF_LOW_VOLTAGE_REFERENCE, \
|
||||
CONF_HIGH_VOLTAGE_REFERENCE, CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.core import TimePeriod
|
||||
from esphomeyaml.helpers import App, Pvariable, add
|
||||
from esphomeyaml.helpers import App, Pvariable, add, global_ns
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
@@ -17,33 +18,33 @@ def validate_voltage(values):
|
||||
value = cv.string(value)
|
||||
if not value.endswith('V'):
|
||||
value += 'V'
|
||||
if value not in values:
|
||||
raise vol.Invalid('Must be one of {}'.format(values))
|
||||
return value
|
||||
return cv.one_of(*values)(value)
|
||||
return validator
|
||||
|
||||
|
||||
LOW_VOLTAGE_REFERENCE = {
|
||||
'0.5V': 'TOUCH_LVOLT_0V5',
|
||||
'0.6V': 'TOUCH_LVOLT_0V6',
|
||||
'0.7V': 'TOUCH_LVOLT_0V7',
|
||||
'0.8V': 'TOUCH_LVOLT_0V8',
|
||||
'0.5V': global_ns.TOUCH_LVOLT_0V5,
|
||||
'0.6V': global_ns.TOUCH_LVOLT_0V6,
|
||||
'0.7V': global_ns.TOUCH_LVOLT_0V7,
|
||||
'0.8V': global_ns.TOUCH_LVOLT_0V8,
|
||||
}
|
||||
HIGH_VOLTAGE_REFERENCE = {
|
||||
'2.4V': 'TOUCH_HVOLT_2V4',
|
||||
'2.5V': 'TOUCH_HVOLT_2V5',
|
||||
'2.6V': 'TOUCH_HVOLT_2V6',
|
||||
'2.7V': 'TOUCH_HVOLT_2V7',
|
||||
'2.4V': global_ns.TOUCH_HVOLT_2V4,
|
||||
'2.5V': global_ns.TOUCH_HVOLT_2V5,
|
||||
'2.6V': global_ns.TOUCH_HVOLT_2V6,
|
||||
'2.7V': global_ns.TOUCH_HVOLT_2V7,
|
||||
}
|
||||
VOLTAGE_ATTENUATION = {
|
||||
'1.5V': 'TOUCH_HVOLT_ATTEN_1V5',
|
||||
'1V': 'TOUCH_HVOLT_ATTEN_1V',
|
||||
'0.5V': 'TOUCH_HVOLT_ATTEN_0V5',
|
||||
'0V': 'TOUCH_HVOLT_ATTEN_0V',
|
||||
'1.5V': global_ns.TOUCH_HVOLT_ATTEN_1V5,
|
||||
'1V': global_ns.TOUCH_HVOLT_ATTEN_1V,
|
||||
'0.5V': global_ns.TOUCH_HVOLT_ATTEN_0V5,
|
||||
'0V': global_ns.TOUCH_HVOLT_ATTEN_0V,
|
||||
}
|
||||
|
||||
ESP32TouchComponent = binary_sensor.binary_sensor_ns.ESP32TouchComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('esp32_ble'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(ESP32TouchComponent),
|
||||
vol.Optional(CONF_SETUP_MODE): cv.boolean,
|
||||
vol.Optional(CONF_IIR_FILTER): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_SLEEP_DURATION):
|
||||
@@ -58,13 +59,13 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_esp32_touch_component()
|
||||
touch = Pvariable('binary_sensor::ESP32TouchComponent', config[CONF_ID], rhs)
|
||||
touch = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_SETUP_MODE in config:
|
||||
add(touch.set_setup_mode(config[CONF_SETUP_MODE]))
|
||||
if CONF_IIR_FILTER in config:
|
||||
add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
sleep_duration = int(config[CONF_SLEEP_DURATION].total_microseconds * 6.6667)
|
||||
sleep_duration = int(config[CONF_SLEEP_DURATION].total_microseconds * 0.15)
|
||||
add(touch.set_sleep_duration(sleep_duration))
|
||||
if CONF_MEASUREMENT_DURATION in config:
|
||||
measurement_duration = int(config[CONF_MEASUREMENT_DURATION].total_microseconds * 0.125)
|
||||
|
||||
@@ -1,26 +1,130 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, \
|
||||
CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
|
||||
from esphomeyaml.helpers import add, setup_mqtt_component
|
||||
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_OSCILLATION_COMMAND_TOPIC, \
|
||||
CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_INTERNAL, \
|
||||
CONF_SPEED, CONF_OSCILLATING
|
||||
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
|
||||
TemplateArguments, get_variable, templatable, 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
|
||||
|
||||
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(FanState),
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTFanComponent),
|
||||
vol.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.publish_topic,
|
||||
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.subscribe_topic,
|
||||
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
|
||||
})
|
||||
|
||||
FAN_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FAN_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_mqtt_fan(obj, config):
|
||||
FAN_SPEEDS = {
|
||||
'OFF': FAN_SPEED_OFF,
|
||||
'LOW': FAN_SPEED_LOW,
|
||||
'MEDIUM': FAN_SPEED_MEDIUM,
|
||||
'HIGH': FAN_SPEED_HIGH,
|
||||
}
|
||||
|
||||
|
||||
def validate_fan_speed(value):
|
||||
return vol.All(vol.Upper, cv.one_of(*FAN_SPEEDS))(value)
|
||||
|
||||
|
||||
def setup_fan_core_(fan_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(fan_var.set_internal(config[CONF_INTERNAL]))
|
||||
|
||||
if CONF_OSCILLATION_STATE_TOPIC in config:
|
||||
add(obj.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
|
||||
add(mqtt_var.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
|
||||
if CONF_OSCILLATION_COMMAND_TOPIC in config:
|
||||
add(obj.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
|
||||
add(mqtt_var.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
|
||||
if CONF_SPEED_STATE_TOPIC in config:
|
||||
add(obj.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
|
||||
add(mqtt_var.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
|
||||
if CONF_SPEED_COMMAND_TOPIC in config:
|
||||
add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
|
||||
setup_mqtt_component(obj, config)
|
||||
add(mqtt_var.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
|
||||
def setup_fan(fan_obj, mqtt_obj, config):
|
||||
fan_var = Pvariable(config[CONF_ID], fan_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
setup_fan_core_(fan_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_FAN'
|
||||
|
||||
|
||||
CONF_FAN_TOGGLE = 'fan.toggle'
|
||||
FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA)
|
||||
def fan_toggle_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_FAN_TURN_OFF, FAN_TURN_OFF_ACTION_SCHEMA)
|
||||
def fan_turn_off_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
|
||||
vol.Optional(CONF_SPEED): cv.templatable(validate_fan_speed),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_FAN_TURN_ON, FAN_TURN_ON_ACTION_SCHEMA)
|
||||
def fan_turn_on_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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
|
||||
|
||||
@@ -2,22 +2,28 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import fan
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, add, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('binary_fan'): cv.register_variable_id,
|
||||
vol.Required(CONF_OUTPUT): cv.variable_id,
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id,
|
||||
})
|
||||
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),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
|
||||
rhs = App.make_fan(config[CONF_NAME])
|
||||
fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
|
||||
fan_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
add(fan_struct.Poutput.set_binary(output))
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
oscillation_output = None
|
||||
for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
|
||||
yield
|
||||
add(fan_struct.Poutput.set_oscillation(oscillation_output))
|
||||
fan.setup_mqtt_fan(fan_struct.Pmqtt, config)
|
||||
|
||||
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)
|
||||
|
||||
@@ -2,32 +2,34 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import fan
|
||||
from esphomeyaml.const import CONF_HIGH, CONF_ID, CONF_LOW, \
|
||||
CONF_MEDIUM, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, \
|
||||
CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
|
||||
from esphomeyaml.const import CONF_HIGH, CONF_LOW, CONF_MAKE_ID, CONF_MEDIUM, CONF_NAME, \
|
||||
CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, \
|
||||
CONF_SPEED_STATE_TOPIC
|
||||
from esphomeyaml.helpers import App, add, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('speed_fan'): cv.register_variable_id,
|
||||
vol.Required(CONF_OUTPUT): cv.variable_id,
|
||||
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_SPEED_STATE_TOPIC): cv.publish_topic,
|
||||
vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id,
|
||||
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_SPEED): vol.Schema({
|
||||
vol.Required(CONF_LOW): cv.zero_to_one_float,
|
||||
vol.Required(CONF_MEDIUM): cv.zero_to_one_float,
|
||||
vol.Required(CONF_HIGH): cv.zero_to_one_float,
|
||||
vol.Required(CONF_LOW): cv.percentage,
|
||||
vol.Required(CONF_MEDIUM): cv.percentage,
|
||||
vol.Required(CONF_HIGH): cv.percentage,
|
||||
}),
|
||||
})
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_fan(config[CONF_NAME])
|
||||
fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
|
||||
fan_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
if CONF_SPEED in config:
|
||||
speeds = config[CONF_SPEED]
|
||||
add(fan_struct.Poutput.set_speed(output, 0.0,
|
||||
add(fan_struct.Poutput.set_speed(output,
|
||||
speeds[CONF_LOW],
|
||||
speeds[CONF_MEDIUM],
|
||||
speeds[CONF_HIGH]))
|
||||
@@ -35,6 +37,9 @@ def to_code(config):
|
||||
add(fan_struct.Poutput.set_speed(output))
|
||||
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
oscillation_output = None
|
||||
for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
|
||||
yield
|
||||
add(fan_struct.Poutput.set_oscillation(oscillation_output))
|
||||
fan.setup_mqtt_fan(fan_struct.Pmqtt, config)
|
||||
|
||||
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)
|
||||
|
||||
121
esphomeyaml/components/font.py
Normal file
121
esphomeyaml/components/font.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.components import display
|
||||
from esphomeyaml.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE
|
||||
from esphomeyaml.core import HexInt
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
|
||||
relative_path
|
||||
|
||||
DEPENDENCIES = ['display']
|
||||
|
||||
Font = display.display_ns.Font
|
||||
Glyph = display.display_ns.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
|
||||
elif len(x_) > len(y_):
|
||||
return 1
|
||||
else:
|
||||
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
|
||||
|
||||
value.sort(cmp=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. "
|
||||
"(pip2 install pillow)")
|
||||
|
||||
if PIL.__version__[0] < '4':
|
||||
raise vol.Invalid("Please update your pillow installation to at least 4.0.x. "
|
||||
"(pip2 install -U pillow)")
|
||||
|
||||
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, cv.ensure_list, [FONT_SCHEMA])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
from PIL import ImageFont
|
||||
|
||||
for conf in config:
|
||||
path = relative_path(conf[CONF_FILE])
|
||||
try:
|
||||
font = ImageFont.truetype(path, conf[CONF_SIZE])
|
||||
except Exception as e:
|
||||
raise core.ESPHomeYAMLError(u"Could not load truetype file {}: {}".format(path, e))
|
||||
|
||||
ascent, descent = font.getmetrics()
|
||||
|
||||
glyph_args = {}
|
||||
data = []
|
||||
for glyph in conf[CONF_GLYPHS]:
|
||||
mask = font.getmask(glyph, mode='1')
|
||||
_, (offset_x, offset_y) = font.font.getsize(glyph)
|
||||
width, height = mask.size
|
||||
width8 = ((width + 7) // 8) * 8
|
||||
glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if not mask.getpixel((x, y)):
|
||||
continue
|
||||
pos = x + y * width8
|
||||
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
||||
glyph_args[glyph] = (len(data), offset_x, offset_y, width, height)
|
||||
data += glyph_data
|
||||
|
||||
raw_data = MockObj(conf[CONF_RAW_DATA_ID])
|
||||
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
|
||||
raw_data, len(data),
|
||||
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
|
||||
|
||||
glyphs = []
|
||||
for glyph in conf[CONF_GLYPHS]:
|
||||
glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))
|
||||
|
||||
rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
@@ -4,25 +4,28 @@ 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
|
||||
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
|
||||
|
||||
I2CComponent = esphomelib_ns.I2CComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('i2c'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(I2CComponent),
|
||||
vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin,
|
||||
vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin,
|
||||
vol.Optional(CONF_FREQUENCY): cv.positive_int,
|
||||
vol.Optional(CONF_RECEIVE_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=0, min_included=False)),
|
||||
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.")
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_SCAN))
|
||||
i2c = Pvariable('I2CComponent', config[CONF_ID], rhs)
|
||||
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]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_I2C'
|
||||
|
||||
65
esphomeyaml/components/image.py
Normal file
65
esphomeyaml/components/image.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.components import display, font
|
||||
from esphomeyaml.const import CONF_FILE, CONF_ID, CONF_RESIZE
|
||||
from esphomeyaml.core import HexInt
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
|
||||
relative_path
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['display']
|
||||
|
||||
Image_ = display.display_ns.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, cv.ensure_list, [IMAGE_SCHEMA])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
from PIL import Image
|
||||
|
||||
for conf in config:
|
||||
path = relative_path(conf[CONF_FILE])
|
||||
try:
|
||||
image = Image.open(path)
|
||||
except Exception as e:
|
||||
raise core.ESPHomeYAMLError(u"Could not load image file {}: {}".format(path, e))
|
||||
|
||||
if CONF_RESIZE in conf:
|
||||
image.thumbnail(conf[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(conf[CONF_RAW_DATA_ID])
|
||||
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
|
||||
raw_data, len(data),
|
||||
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
|
||||
|
||||
rhs = App.make_image(raw_data, width, height)
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
@@ -1,25 +1,6 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
|
||||
|
||||
IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent'
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
||||
cv.GenerateID('ir_transmitter'): cv.register_variable_id,
|
||||
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 = gpio_output_pin_expression(conf[CONF_PIN])
|
||||
rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT))
|
||||
Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
|
||||
def CONFIG_SCHEMA(config):
|
||||
raise vol.Invalid("The ir_transmitter component has been renamed to "
|
||||
"remote_transmitter because of 433MHz signal support.")
|
||||
|
||||
@@ -1,17 +1,450 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT
|
||||
from esphomeyaml.helpers import add
|
||||
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.helpers import Application, ArrayInitializer, Pvariable, RawExpression, \
|
||||
StructInitializer, add, add_job, esphomelib_ns, process_lambda, setup_mqtt_component, \
|
||||
get_variable, TemplateArguments, templatable, uint32, float_, std_string
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
|
||||
})
|
||||
|
||||
light_ns = esphomelib_ns.namespace('light')
|
||||
LightState = light_ns.LightState
|
||||
LightColorValues = light_ns.LightColorValues
|
||||
MQTTJSONLightComponent = light_ns.MQTTJSONLightComponent
|
||||
ToggleAction = light_ns.ToggleAction
|
||||
TurnOffAction = light_ns.TurnOffAction
|
||||
TurnOnAction = light_ns.TurnOnAction
|
||||
MakeLight = Application.MakeLight
|
||||
RandomLightEffect = light_ns.RandomLightEffect
|
||||
LambdaLightEffect = light_ns.LambdaLightEffect
|
||||
StrobeLightEffect = light_ns.StrobeLightEffect
|
||||
StrobeLightEffectColor = light_ns.StrobeLightEffectColor
|
||||
FlickerLightEffect = light_ns.FlickerLightEffect
|
||||
FastLEDLambdaLightEffect = light_ns.FastLEDLambdaLightEffect
|
||||
FastLEDRainbowLightEffect = light_ns.FastLEDRainbowLightEffect
|
||||
FastLEDColorWipeEffect = light_ns.FastLEDColorWipeEffect
|
||||
FastLEDColorWipeEffectColor = light_ns.FastLEDColorWipeEffectColor
|
||||
FastLEDScanEffect = light_ns.FastLEDScanEffect
|
||||
FastLEDScanEffectColor = light_ns.FastLEDScanEffectColor
|
||||
FastLEDTwinkleEffect = light_ns.FastLEDTwinkleEffect
|
||||
FastLEDRandomTwinkleEffect = light_ns.FastLEDRandomTwinkleEffect
|
||||
FastLEDFireworksEffect = light_ns.FastLEDFireworksEffect
|
||||
FastLEDFlickerEffect = light_ns.FastLEDFlickerEffect
|
||||
FastLEDLightOutputComponent = light_ns.FastLEDLightOutputComponent
|
||||
|
||||
CONF_STROBE = 'strobe'
|
||||
CONF_FLICKER = 'flicker'
|
||||
CONF_FASTLED_LAMBDA = 'fastled_lambda'
|
||||
CONF_FASTLED_RAINBOW = 'fastled_rainbow'
|
||||
CONF_FASTLED_COLOR_WIPE = 'fastled_color_wipe'
|
||||
CONF_FASTLED_SCAN = 'fastled_scan'
|
||||
CONF_FASTLED_TWINKLE = 'fastled_twinkle'
|
||||
CONF_FASTLED_RANDOM_TWINKLE = 'fastled_random_twinkle'
|
||||
CONF_FASTLED_FIREWORKS = 'fastled_fireworks'
|
||||
CONF_FASTLED_FLICKER = 'fastled_flicker'
|
||||
|
||||
CONF_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]
|
||||
FASTLED_EFFECTS = RGB_EFFECTS + [CONF_FASTLED_LAMBDA, CONF_FASTLED_RAINBOW, CONF_FASTLED_COLOR_WIPE,
|
||||
CONF_FASTLED_SCAN, CONF_FASTLED_TWINKLE,
|
||||
CONF_FASTLED_RANDOM_TWINKLE, CONF_FASTLED_FIREWORKS,
|
||||
CONF_FASTLED_FLICKER]
|
||||
|
||||
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.All(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_FASTLED_LAMBDA): vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_RAINBOW): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRainbowLightEffect),
|
||||
vol.Optional(CONF_NAME, default="Rainbow"): cv.string,
|
||||
vol.Optional(CONF_SPEED): cv.uint32_t,
|
||||
vol.Optional(CONF_WIDTH): cv.uint32_t,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_COLOR_WIPE): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDColorWipeEffect),
|
||||
vol.Optional(CONF_NAME, default="Color Wipe"): cv.string,
|
||||
vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.Schema({
|
||||
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_RANDOM, default=False): cv.boolean,
|
||||
vol.Required(CONF_NUM_LEDS): vol.All(cv.uint32_t, vol.Range(min=1)),
|
||||
})]),
|
||||
vol.Optional(CONF_ADD_LED_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_REVERSE): cv.boolean,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_SCAN): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDScanEffect),
|
||||
vol.Optional(CONF_NAME, default="Scan"): cv.string,
|
||||
vol.Optional(CONF_MOVE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_TWINKLE): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDTwinkleEffect),
|
||||
vol.Optional(CONF_NAME, default="Twinkle"): cv.string,
|
||||
vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage,
|
||||
vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_RANDOM_TWINKLE): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRandomTwinkleEffect),
|
||||
vol.Optional(CONF_NAME, default="Random Twinkle"): cv.string,
|
||||
vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage,
|
||||
vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_FIREWORKS): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFireworksEffect),
|
||||
vol.Optional(CONF_NAME, default="Fireworks"): cv.string,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_SPARK_PROBABILITY): cv.percentage,
|
||||
vol.Optional(CONF_USE_RANDOM_COLOR): cv.boolean,
|
||||
vol.Optional(CONF_FADE_OUT_RATE): cv.uint8_t,
|
||||
}),
|
||||
vol.Optional(CONF_FASTLED_FLICKER): vol.Schema({
|
||||
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFlickerEffect),
|
||||
vol.Optional(CONF_NAME, default="FastLED Flicker"): cv.string,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_INTENSITY): cv.percentage,
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
def setup_light_component(obj, config):
|
||||
def validate_effects(allowed_effects):
|
||||
def validator(value):
|
||||
value = cv.ensure_list(value)
|
||||
names = set()
|
||||
ret = []
|
||||
for i, effect in enumerate(value):
|
||||
if not isinstance(effect, dict):
|
||||
raise vol.Invalid("Each effect must be a dictionary, not {}".format(type(value)))
|
||||
if len(effect) > 1:
|
||||
raise vol.Invalid("Each entry in the 'effects:' option must be a single effect.")
|
||||
if not effect:
|
||||
raise vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i))
|
||||
key = next(iter(effect.keys()))
|
||||
if key not in allowed_effects:
|
||||
raise vol.Invalid("The effect '{}' does not exist or is not allowed for this "
|
||||
"light type".format(key))
|
||||
effect[key] = effect[key] or {}
|
||||
conf = EFFECTS_SCHEMA(effect)
|
||||
name = conf[key][CONF_NAME]
|
||||
if name in names:
|
||||
raise vol.Invalid(u"Found the effect name '{}' twice. All effects must have "
|
||||
u"unique names".format(name))
|
||||
names.add(name)
|
||||
ret.append(conf)
|
||||
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], []):
|
||||
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_FASTLED_LAMBDA:
|
||||
lambda_ = None
|
||||
args = [(RawExpression('FastLEDLightOutputComponent &'), 'it')]
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], args):
|
||||
yield None
|
||||
yield FastLEDLambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL])
|
||||
elif key == CONF_FASTLED_RAINBOW:
|
||||
rhs = FastLEDRainbowLightEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
if CONF_SPEED in config:
|
||||
add(effect.set_speed(config[CONF_SPEED]))
|
||||
if CONF_WIDTH in config:
|
||||
add(effect.set_width(config[CONF_WIDTH]))
|
||||
yield effect
|
||||
elif key == CONF_FASTLED_COLOR_WIPE:
|
||||
rhs = FastLEDColorWipeEffect.new(config[CONF_NAME])
|
||||
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(
|
||||
FastLEDColorWipeEffectColor,
|
||||
('r', color[CONF_RED]),
|
||||
('g', color[CONF_GREEN]),
|
||||
('b', color[CONF_BLUE]),
|
||||
('random', color[CONF_RANDOM]),
|
||||
('num_leds', color[CONF_NUM_LEDS]),
|
||||
))
|
||||
if colors:
|
||||
add(effect.set_colors(ArrayInitializer(*colors)))
|
||||
yield effect
|
||||
elif key == CONF_FASTLED_SCAN:
|
||||
rhs = FastLEDScanEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
if CONF_MOVE_INTERVAL in config:
|
||||
add(effect.set_move_interval(config[CONF_MOVE_INTERVAL]))
|
||||
yield effect
|
||||
elif key == CONF_FASTLED_TWINKLE:
|
||||
rhs = FastLEDTwinkleEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
if CONF_TWINKLE_PROBABILITY in config:
|
||||
add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
|
||||
if CONF_PROGRESS_INTERVAL in config:
|
||||
add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL]))
|
||||
yield effect
|
||||
elif key == CONF_FASTLED_RANDOM_TWINKLE:
|
||||
rhs = FastLEDRandomTwinkleEffect.new(config[CONF_NAME])
|
||||
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
|
||||
if CONF_TWINKLE_PROBABILITY in config:
|
||||
add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
|
||||
if CONF_PROGRESS_INTERVAL in config:
|
||||
add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL]))
|
||||
yield effect
|
||||
elif key == CONF_FASTLED_FIREWORKS:
|
||||
rhs = FastLEDFireworksEffect.new(config[CONF_NAME])
|
||||
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_FASTLED_FLICKER:
|
||||
rhs = FastLEDFlickerEffect.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(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
|
||||
add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
|
||||
if CONF_GAMMA_CORRECT in config:
|
||||
add(obj.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
|
||||
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)
|
||||
|
||||
|
||||
def setup_light(light_obj, mqtt_obj, config):
|
||||
light_var = Pvariable(config[CONF_ID], light_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
add_job(setup_light_core_, light_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_LIGHT'
|
||||
|
||||
|
||||
CONF_LIGHT_TOGGLE = 'light.toggle'
|
||||
LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
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 = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
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 = TemplateArguments(arg_type)
|
||||
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(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_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 = TemplateArguments(arg_type)
|
||||
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
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, get_variable, variable, setup_mqtt_component
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT, CONF_EFFECTS
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('binary_light'): cv.register_variable_id,
|
||||
vol.Required(CONF_OUTPUT): cv.variable_id,
|
||||
})
|
||||
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.Optional(CONF_EFFECTS): light.validate_effects(light.BINARY_EFFECTS),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_binary_light(config[CONF_NAME], output)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
|
||||
34
esphomeyaml/components/light/cwww.py
Normal file
34
esphomeyaml/components/light/cwww.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
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.helpers import App, get_variable, variable
|
||||
|
||||
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(None),
|
||||
vol.Required(CONF_WARM_WHITE): cv.use_variable_id(None),
|
||||
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),
|
||||
}), 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)
|
||||
@@ -3,11 +3,12 @@ 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_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \
|
||||
CONF_RGB_ORDER
|
||||
from esphomeyaml.helpers import App, RawExpression, TemplateArguments, add, get_variable, \
|
||||
setup_mqtt_component, variable
|
||||
CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \
|
||||
CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT
|
||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
|
||||
get_variable, variable
|
||||
|
||||
TYPES = [
|
||||
'NEOPIXEL',
|
||||
@@ -52,25 +53,29 @@ def validate(value):
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('fast_led_clockless_light'): cv.register_variable_id,
|
||||
MakeFastLEDLight = Application.MakeFastLEDLight
|
||||
|
||||
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, vol.Any(*TYPES)),
|
||||
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_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, vol.Any(*RGB_ORDERS)),
|
||||
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)),
|
||||
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_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.variable_id,
|
||||
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
|
||||
}), validate)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_fast_led_light(config[CONF_NAME])
|
||||
make = variable('Application::MakeFastLEDLight', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
fast_led = make.Pfast_led
|
||||
|
||||
rgb_order = None
|
||||
@@ -84,11 +89,16 @@ def to_code(config):
|
||||
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = get_variable(config[CONF_POWER_SUPPLY])
|
||||
power_supply = None
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
add(fast_led.set_power_supply(power_supply))
|
||||
|
||||
setup_mqtt_component(make.Pmqtt, config)
|
||||
light.setup_light_component(make.Pstate, 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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
|
||||
|
||||
@@ -3,11 +3,12 @@ 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_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, \
|
||||
CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER
|
||||
from esphomeyaml.helpers import App, RawExpression, TemplateArguments, add, get_variable, \
|
||||
setup_mqtt_component, variable
|
||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \
|
||||
CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT
|
||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
|
||||
get_variable, variable
|
||||
|
||||
CHIPSETS = [
|
||||
'LPD8806',
|
||||
@@ -29,26 +30,30 @@ RGB_ORDERS = [
|
||||
'BGR',
|
||||
]
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('fast_led_spi_light'): cv.register_variable_id,
|
||||
MakeFastLEDLight = Application.MakeFastLEDLight
|
||||
|
||||
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, vol.Any(*CHIPSETS)),
|
||||
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_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, vol.Any(*RGB_ORDERS)),
|
||||
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)),
|
||||
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_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.variable_id,
|
||||
})
|
||||
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_fast_led_light(config[CONF_NAME])
|
||||
make = variable('Application::MakeFastLEDLight', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
fast_led = make.Pfast_led
|
||||
|
||||
rgb_order = None
|
||||
@@ -64,11 +69,16 @@ def to_code(config):
|
||||
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = get_variable(config[CONF_POWER_SUPPLY])
|
||||
power_supply = None
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
add(fast_led.set_power_supply(power_supply))
|
||||
|
||||
setup_mqtt_component(make.Pmqtt, config)
|
||||
light.setup_light_component(make.Pstate, 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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
|
||||
|
||||
@@ -2,21 +2,23 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
|
||||
CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_OUTPUT, CONF_EFFECTS
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('monochromatic_light'): cv.register_variable_id,
|
||||
vol.Required(CONF_OUTPUT): cv.variable_id,
|
||||
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.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),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_monochromatic_light(config[CONF_NAME], output)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
|
||||
@@ -3,24 +3,30 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED
|
||||
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
|
||||
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_EFFECTS
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('rgb_light'): cv.register_variable_id,
|
||||
vol.Required(CONF_RED): cv.variable_id,
|
||||
vol.Required(CONF_GREEN): cv.variable_id,
|
||||
vol.Required(CONF_BLUE): cv.variable_id,
|
||||
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.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),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
red = get_variable(config[CONF_RED])
|
||||
green = get_variable(config[CONF_GREEN])
|
||||
blue = get_variable(config[CONF_BLUE])
|
||||
red = None
|
||||
for red in get_variable(config[CONF_RED]):
|
||||
yield
|
||||
green = None
|
||||
for green in get_variable(config[CONF_GREEN]):
|
||||
yield
|
||||
blue = None
|
||||
for blue in get_variable(config[CONF_BLUE]):
|
||||
yield
|
||||
rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
|
||||
@@ -3,26 +3,34 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE
|
||||
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
|
||||
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE, CONF_EFFECTS
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('rgbw_light'): cv.register_variable_id,
|
||||
vol.Required(CONF_RED): cv.variable_id,
|
||||
vol.Required(CONF_GREEN): cv.variable_id,
|
||||
vol.Required(CONF_BLUE): cv.variable_id,
|
||||
vol.Required(CONF_WHITE): cv.variable_id,
|
||||
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.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),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
red = get_variable(config[CONF_RED])
|
||||
green = get_variable(config[CONF_GREEN])
|
||||
blue = get_variable(config[CONF_BLUE])
|
||||
white = get_variable(config[CONF_WHITE])
|
||||
red = None
|
||||
for red in get_variable(config[CONF_RED]):
|
||||
yield
|
||||
green = None
|
||||
for green in get_variable(config[CONF_GREEN]):
|
||||
yield
|
||||
blue = None
|
||||
for blue in get_variable(config[CONF_BLUE]):
|
||||
yield
|
||||
white = None
|
||||
for white in get_variable(config[CONF_WHITE]):
|
||||
yield
|
||||
rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
light_struct = variable(config[CONF_MAKE_ID], rhs)
|
||||
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)
|
||||
|
||||
62
esphomeyaml/components/light/rgbww.py
Normal file
62
esphomeyaml/components/light/rgbww.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \
|
||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_RED, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
|
||||
|
||||
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(None),
|
||||
vol.Required(CONF_GREEN): cv.use_variable_id(None),
|
||||
vol.Required(CONF_BLUE): cv.use_variable_id(None),
|
||||
vol.Required(CONF_COLD_WHITE): cv.use_variable_id(None),
|
||||
vol.Required(CONF_WARM_WHITE): cv.use_variable_id(None),
|
||||
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),
|
||||
}), 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)
|
||||
@@ -1,51 +1,128 @@
|
||||
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_LOGGER, CONF_LOGS, \
|
||||
CONF_TX_BUFFER_SIZE
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, RawExpression, add
|
||||
from esphomeyaml.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_ID, CONF_LEVEL, \
|
||||
CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE
|
||||
from esphomeyaml.core import ESPHomeYAMLError, Lambda
|
||||
from esphomeyaml.helpers import App, Pvariable, TemplateArguments, add, esphomelib_ns, global_ns, \
|
||||
process_lambda, RawExpression, statement
|
||||
|
||||
LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
|
||||
LOG_LEVELS = {
|
||||
'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE,
|
||||
'ERROR': global_ns.ESPHOMELIB_LOG_LEVEL_ERROR,
|
||||
'WARN': global_ns.ESPHOMELIB_LOG_LEVEL_WARN,
|
||||
'INFO': global_ns.ESPHOMELIB_LOG_LEVEL_INFO,
|
||||
'DEBUG': global_ns.ESPHOMELIB_LOG_LEVEL_DEBUG,
|
||||
'VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERBOSE,
|
||||
'VERY_VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERY_VERBOSE,
|
||||
}
|
||||
|
||||
LOG_LEVEL_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, vol.Any(*LOG_LEVELS))
|
||||
is_log_level = vol.All(vol.Upper, cv.one_of(*LOG_LEVELS))
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_LOGGER): cv.register_variable_id,
|
||||
vol.Optional(CONF_BAUD_RATE): cv.positive_int,
|
||||
vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int,
|
||||
|
||||
def validate_local_no_higher_than_global(value):
|
||||
global_level = value.get(CONF_LEVEL, 'DEBUG')
|
||||
for tag, level in value.get(CONF_LOGS, {}).iteritems():
|
||||
if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
|
||||
raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the "
|
||||
u"global log level {}.".format(level, tag, global_level))
|
||||
return value
|
||||
|
||||
|
||||
LogComponent = esphomelib_ns.LogComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(LogComponent),
|
||||
vol.Optional(CONF_BAUD_RATE, 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,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
def esphomelib_log_level(level):
|
||||
return u'ESPHOMELIB_LOG_LEVEL_{}'.format(level)
|
||||
|
||||
|
||||
def exp_log_level(level):
|
||||
return RawExpression(esphomelib_log_level(level))
|
||||
}), validate_local_no_higher_than_global)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.init_log(config.get(CONF_BAUD_RATE))
|
||||
log = Pvariable(u'LogComponent', config[CONF_ID], rhs)
|
||||
log = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_TX_BUFFER_SIZE in config:
|
||||
add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
|
||||
if CONF_LEVEL in config:
|
||||
add(log.set_global_log_level(exp_log_level(config[CONF_LEVEL])))
|
||||
add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]]))
|
||||
for tag, level in config.get(CONF_LOGS, {}).iteritems():
|
||||
global_level = config.get(CONF_LEVEL, 'DEBUG')
|
||||
if LOG_LEVELS.index(level) > LOG_LEVELS.index(global_level):
|
||||
raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the "
|
||||
u"global log level {}.".format(level, tag, global_level))
|
||||
add(log.set_log_level(tag, exp_log_level(level)))
|
||||
add(log.set_log_level(tag, LOG_LEVELS[level]))
|
||||
|
||||
|
||||
def required_build_flags(config):
|
||||
if CONF_LEVEL in config:
|
||||
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL]))
|
||||
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]]))
|
||||
return None
|
||||
|
||||
|
||||
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 "%%"
|
||||
"""
|
||||
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): vol.All(cv.ensure_list, [cv.lambda_]),
|
||||
vol.Optional(CONF_LEVEL, default="DEBUG"): vol.All(vol.Upper, cv.one_of(*LOG_LEVEL_TO_ESP_LOG)),
|
||||
vol.Optional(CONF_TAG, default="main"): cv.string,
|
||||
}), validate_printf)
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA)
|
||||
def logger_log_action_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]]
|
||||
args = [RawExpression(unicode(x)) for x in config[CONF_ARGS]]
|
||||
|
||||
text = unicode(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
|
||||
|
||||
for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]):
|
||||
yield None
|
||||
rhs = LambdaAction.new(template_arg, lambda_)
|
||||
type = LambdaAction.template(template_arg)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
|
||||
@@ -2,23 +2,28 @@ import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.automation import ACTION_REGISTRY
|
||||
from esphomeyaml.components import logger
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
|
||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_SSL_FINGERPRINTS, CONF_ID, CONF_LOG_TOPIC, \
|
||||
CONF_MQTT, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, \
|
||||
CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_KEEPALIVE
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, StructInitializer, add, \
|
||||
exp_empty_optional, RawExpression
|
||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \
|
||||
CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \
|
||||
CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \
|
||||
CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
|
||||
StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \
|
||||
uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef
|
||||
|
||||
|
||||
def validate_message_just_topic(value):
|
||||
value = cv.publish_topic(value)
|
||||
return {CONF_TOPIC: value}
|
||||
return MQTT_MESSAGE_BASE({CONF_TOPIC: value})
|
||||
|
||||
|
||||
MQTT_MESSAGE_BASE = vol.Schema({
|
||||
vol.Required(CONF_TOPIC): cv.publish_topic,
|
||||
vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
|
||||
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
|
||||
vol.Optional(CONF_RETAIN, default=True): cv.boolean,
|
||||
})
|
||||
|
||||
@@ -28,12 +33,17 @@ MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({
|
||||
vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
|
||||
}))
|
||||
|
||||
mqtt_ns = esphomelib_ns.namespace('mqtt')
|
||||
MQTTMessage = mqtt_ns.MQTTMessage
|
||||
MQTTClientComponent = mqtt_ns.MQTTClientComponent
|
||||
MQTTPublishAction = mqtt_ns.MQTTPublishAction
|
||||
MQTTPublishJsonAction = mqtt_ns.MQTTPublishJsonAction
|
||||
MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger
|
||||
MQTTJsonMessageTrigger = mqtt_ns.MQTTJsonMessageTrigger
|
||||
|
||||
|
||||
def validate_broker(value):
|
||||
value = cv.string_strict(value)
|
||||
if value.endswith(u'.local'):
|
||||
raise vol.Invalid(u"MQTT server addresses ending with '.local' are currently unsupported."
|
||||
u" Please use the static IP instead.")
|
||||
if u':' in value:
|
||||
raise vol.Invalid(u"Please specify the port using the port: option")
|
||||
if not value:
|
||||
@@ -49,7 +59,7 @@ def validate_fingerprint(value):
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_MQTT): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent),
|
||||
vol.Required(CONF_BROKER): validate_broker,
|
||||
vol.Optional(CONF_PORT, default=1883): cv.port,
|
||||
vol.Optional(CONF_USERNAME, default=''): cv.string,
|
||||
@@ -60,19 +70,33 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
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]),
|
||||
vol.Optional(CONF_KEEPALIVE): cv.positive_time_period_seconds,
|
||||
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_ON_MESSAGE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
|
||||
}),
|
||||
vol.Optional(CONF_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,
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
def exp_mqtt_message(config):
|
||||
if config is None:
|
||||
return exp_empty_optional('mqtt::MQTTMessage')
|
||||
return optional(TemplateArguments(MQTTMessage))
|
||||
exp = StructInitializer(
|
||||
'mqtt::MQTTMessage',
|
||||
MQTTMessage,
|
||||
('topic', config[CONF_TOPIC]),
|
||||
('payload', config.get(CONF_PAYLOAD, "")),
|
||||
('qos', config[CONF_QOS]),
|
||||
@@ -84,42 +108,132 @@ def exp_mqtt_message(config):
|
||||
def to_code(config):
|
||||
rhs = App.init_mqtt(config[CONF_BROKER], config[CONF_PORT],
|
||||
config[CONF_USERNAME], config[CONF_PASSWORD])
|
||||
mqtt = Pvariable('mqtt::MQTTClientComponent', config[CONF_ID], rhs)
|
||||
mqtt = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if not config.get(CONF_DISCOVERY, True):
|
||||
add(mqtt.disable_discovery())
|
||||
if CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
|
||||
elif CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
|
||||
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
|
||||
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
|
||||
add(mqtt.set_discovery_info(discovery_prefix, discovery_retain))
|
||||
if CONF_TOPIC_PREFIX in config:
|
||||
add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
|
||||
|
||||
if CONF_BIRTH_MESSAGE in config:
|
||||
birth_message = config[CONF_BIRTH_MESSAGE]
|
||||
if birth_message is None:
|
||||
if not birth_message:
|
||||
add(mqtt.disable_birth_message())
|
||||
else:
|
||||
add(mqtt.set_birth_message(exp_mqtt_message(birth_message)))
|
||||
if CONF_WILL_MESSAGE in config:
|
||||
will_message = config[CONF_WILL_MESSAGE]
|
||||
if will_message is None:
|
||||
if not will_message:
|
||||
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 log_topic is None:
|
||||
if not log_topic:
|
||||
add(mqtt.disable_log_message())
|
||||
else:
|
||||
add(mqtt.set_log_topic(exp_mqtt_message(log_topic)))
|
||||
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])
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
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 = TemplateArguments(arg_type)
|
||||
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 = TemplateArguments(arg_type)
|
||||
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')]):
|
||||
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:
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, add
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
OTAComponent = esphomelib_ns.OTAComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_OTA): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(OTAComponent),
|
||||
vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
|
||||
# TODO Num attempts + wait time
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
@@ -23,10 +24,11 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.init_ota()
|
||||
ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
|
||||
ota = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_PASSWORD in config:
|
||||
hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
||||
add(ota.set_auth_password_hash(hash_))
|
||||
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]:
|
||||
add(ota.start_safe_mode())
|
||||
|
||||
@@ -46,6 +48,7 @@ def get_auth(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OTA'
|
||||
REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA'
|
||||
|
||||
|
||||
def lib_deps(config):
|
||||
|
||||
@@ -1,28 +1,103 @@
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_POWER_SUPPLY, CONF_INVERTED, CONF_MAX_POWER
|
||||
from esphomeyaml.helpers import get_variable, add
|
||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY, CONF_ID, CONF_LEVEL
|
||||
from esphomeyaml.helpers import add, esphomelib_ns, get_variable, TemplateArguments, Pvariable, \
|
||||
templatable, bool_
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_POWER_SUPPLY): cv.variable_id,
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
}).extend(cv.REQUIRED_ID_SCHEMA.schema)
|
||||
|
||||
FLOAT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_MAX_POWER): cv.zero_to_one_float,
|
||||
})
|
||||
|
||||
BINARY_OUTPUT_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
})
|
||||
|
||||
def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
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')
|
||||
TurnOffAction = output_ns.TurnOffAction
|
||||
TurnOnAction = output_ns.TurnOnAction
|
||||
SetLevelAction = output_ns.SetLevelAction
|
||||
|
||||
|
||||
def setup_output_platform_(obj, config, skip_power_supply=False):
|
||||
if CONF_INVERTED in config:
|
||||
add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
if not skip_power_supply and CONF_POWER_SUPPLY in config:
|
||||
power_supply = get_variable(config[CONF_POWER_SUPPLY])
|
||||
power_supply = None
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
add(obj.set_power_supply(power_supply))
|
||||
if CONF_MAX_POWER in config:
|
||||
add(obj.set_max_power(config[CONF_MAX_POWER]))
|
||||
|
||||
|
||||
def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
for _ in setup_output_platform_(obj, config, skip_power_supply):
|
||||
yield
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT'
|
||||
|
||||
|
||||
CONF_OUTPUT_TURN_ON = 'output.turn_on'
|
||||
OUTPUT_TURN_OFF_ACTION = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_OFF_ACTION)
|
||||
def output_turn_on_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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_TURN_OFF = 'output.turn_off'
|
||||
OUTPUT_TURN_ON_ACTION = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None)
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_ON_ACTION)
|
||||
def output_turn_off_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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_SET_LEVEL = 'output.set_level'
|
||||
OUTPUT_SET_LEVEL_ACTION = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Required(CONF_LEVEL): 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 = TemplateArguments(arg_type)
|
||||
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, bool_):
|
||||
yield None
|
||||
add(action.set_level(template_))
|
||||
yield action
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266, CONF_NUMBER
|
||||
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
|
||||
|
||||
@@ -10,20 +11,25 @@ ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
|
||||
|
||||
|
||||
def valid_pwm_pin(value):
|
||||
if value[CONF_NUMBER] >= 16:
|
||||
if value[CONF_NUMBER] > 16:
|
||||
raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.")
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_PIN): vol.All(pins.GPIO_INTERNAL_OUTPUT_PIN_SCHEMA, valid_pwm_pin),
|
||||
ESP8266PWMOutput = output.output_ns.ESP8266PWMOutput
|
||||
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = gpio_output_pin_expression(config[CONF_PIN])
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_esp8266_pwm_output(pin)
|
||||
gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(gpio, config)
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
|
||||
|
||||
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
|
||||
GPIOBinaryOutputComponent = output.output_ns.GPIOBinaryOutputComponent
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = gpio_output_pin_expression(config[CONF_PIN])
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_gpio_output(pin)
|
||||
gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(gpio, config)
|
||||
|
||||
|
||||
|
||||
@@ -15,12 +15,15 @@ def validate_frequency_bit_depth(obj):
|
||||
bit_depth = obj.get(CONF_BIT_DEPTH, 12)
|
||||
max_freq = APB_CLOCK_FREQ / (2**bit_depth)
|
||||
if frequency > max_freq:
|
||||
raise vol.Invalid('Maximum frequency for bit depth {} is {}'.format(bit_depth, max_freq))
|
||||
raise vol.Invalid('Maximum frequency for bit depth {} is {}Hz'.format(bit_depth, max_freq))
|
||||
return obj
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(output.FLOAT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_PIN): vol.All(pins.output_pin, vol.Range(min=0, max=33)),
|
||||
LEDCOutputComponent = output.output_ns.LEDCOutputComponent
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(output.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))
|
||||
@@ -32,7 +35,7 @@ def to_code(config):
|
||||
if frequency is None and CONF_BIT_DEPTH in config:
|
||||
frequency = 1000
|
||||
rhs = App.make_ledc_output(config[CONF_PIN], frequency, config.get(CONF_BIT_DEPTH))
|
||||
ledc = Pvariable('output::LEDCOutputComponent', config[CONF_ID], rhs)
|
||||
ledc = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_CHANNEL in config:
|
||||
add(ledc.set_channel(config[CONF_CHANNEL]))
|
||||
output.setup_output_platform(ledc, config)
|
||||
|
||||
@@ -2,26 +2,32 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.components.pca9685 import PCA9685_COMPONENT_TYPE
|
||||
from esphomeyaml.components.pca9685 import PCA9685OutputComponent
|
||||
from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_PCA9685_ID, CONF_POWER_SUPPLY
|
||||
from esphomeyaml.helpers import Pvariable, get_variable
|
||||
|
||||
DEPENDENCIES = ['pca9685']
|
||||
|
||||
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
|
||||
Channel = PCA9685OutputComponent.Channel
|
||||
|
||||
PLATFORM_SCHEMA = output.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)),
|
||||
vol.Optional(CONF_PCA9685_ID): cv.variable_id,
|
||||
cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685OutputComponent),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
power_supply = None
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = get_variable(config[CONF_POWER_SUPPLY])
|
||||
pca9685 = get_variable(config.get(CONF_PCA9685_ID), PCA9685_COMPONENT_TYPE)
|
||||
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
|
||||
yield
|
||||
pca9685 = None
|
||||
for pca9685 in get_variable(config[CONF_PCA9685_ID]):
|
||||
yield
|
||||
rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
|
||||
out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs)
|
||||
out = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(out, config, skip_power_supply=True)
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID, CONF_PHASE_BALANCER
|
||||
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, RawExpression, add
|
||||
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, add
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
PHASE_BALANCERS = ['None', 'Linear', 'Weaved']
|
||||
|
||||
PCA9685_COMPONENT_TYPE = 'output::PCA9685OutputComponent'
|
||||
PCA9685OutputComponent = output.output_ns.namespace('PCA9685OutputComponent')
|
||||
|
||||
PHASE_BALANCER_MESSAGE = ("The phase_balancer option has been removed in version 1.5.0. "
|
||||
"esphomelib will now automatically choose a suitable phase balancer.")
|
||||
|
||||
PCA9685_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('pca9685'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(PCA9685OutputComponent),
|
||||
vol.Required(CONF_FREQUENCY): vol.All(cv.frequency,
|
||||
vol.Range(min=23.84, max=1525.88)),
|
||||
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
@@ -28,13 +27,9 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA])
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_pca9685_component(conf.get(CONF_FREQUENCY))
|
||||
pca9685 = Pvariable(PCA9685_COMPONENT_TYPE, conf[CONF_ID], rhs)
|
||||
pca9685 = Pvariable(conf[CONF_ID], rhs)
|
||||
if CONF_ADDRESS in conf:
|
||||
add(pca9685.set_address(HexIntLiteral(conf[CONF_ADDRESS])))
|
||||
if CONF_PHASE_BALANCER in conf:
|
||||
phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format(
|
||||
conf[CONF_PHASE_BALANCER]))
|
||||
add(pca9685.set_phase_balancer(phase_balancer))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
|
||||
|
||||
@@ -2,12 +2,15 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_PCF8575
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
io_ns = esphomelib_ns.namespace('io')
|
||||
PCF8574Component = io_ns.PCF8574Component
|
||||
|
||||
PCF8574_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.register_variable_id,
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(PCF8574Component),
|
||||
vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address,
|
||||
vol.Optional(CONF_PCF8575, default=False): cv.boolean,
|
||||
})
|
||||
@@ -18,7 +21,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA])
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575])
|
||||
Pvariable('io::PCF8574Component', conf[CONF_ID], rhs)
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCF8574'
|
||||
|
||||
43
esphomeyaml/components/pn532.py
Normal file
43
esphomeyaml/components/pn532.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins, automation
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.spi import SPIComponent
|
||||
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_SPI_ID, CONF_UPDATE_INTERVAL, \
|
||||
CONF_ON_TAG, CONF_TRIGGER_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression, std_string
|
||||
|
||||
DEPENDENCIES = ['spi']
|
||||
|
||||
PN532Component = binary_sensor.binary_sensor_ns.PN532Component
|
||||
PN532Trigger = binary_sensor.binary_sensor_ns.PN532Trigger
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [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),
|
||||
}),
|
||||
})])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
spi = None
|
||||
for spi in get_variable(conf[CONF_SPI_ID]):
|
||||
yield
|
||||
cs = None
|
||||
for cs in gpio_output_pin_expression(conf[CONF_CS_PIN]):
|
||||
yield
|
||||
rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL))
|
||||
pn532 = Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
for conf_ in conf.get(CONF_ON_TAG, []):
|
||||
trigger = Pvariable(conf_[CONF_TRIGGER_ID], pn532.make_trigger())
|
||||
automation.build_automation(trigger, std_string, conf_)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PN532'
|
||||
@@ -3,10 +3,13 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression
|
||||
|
||||
POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({
|
||||
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
|
||||
PowerSupplyComponent = esphomelib_ns.PowerSupplyComponent
|
||||
|
||||
POWER_SUPPLY_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(PowerSupplyComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
@@ -16,9 +19,11 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA])
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
pin = gpio_output_pin_expression(conf[CONF_PIN])
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(conf[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_power_supply(pin)
|
||||
psu = Pvariable('PowerSupplyComponent', conf[CONF_ID], rhs)
|
||||
psu = Pvariable(conf[CONF_ID], rhs)
|
||||
if CONF_ENABLE_TIME in conf:
|
||||
add(psu.set_enable_time(conf[CONF_ENABLE_TIME]))
|
||||
if CONF_KEEP_ON_TIME in conf:
|
||||
|
||||
28
esphomeyaml/components/rdm6300.py
Normal file
28
esphomeyaml/components/rdm6300.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
from esphomeyaml.const import CONF_ID, CONF_UART_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, get_variable
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
RDM6300Component = binary_sensor.binary_sensor_ns.RDM6300Component
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list_not_empty, [vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(RDM6300Component),
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
})])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
uart = None
|
||||
for uart in get_variable(conf[CONF_UART_ID]):
|
||||
yield
|
||||
rhs = App.make_rdm6300_component(uart)
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_RDM6300'
|
||||
64
esphomeyaml/components/remote_receiver.py
Normal file
64
esphomeyaml/components/remote_receiver.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, \
|
||||
CONF_PIN, CONF_TOLERANCE
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_input_pin_expression
|
||||
|
||||
remote_ns = esphomelib_ns.namespace('remote')
|
||||
|
||||
RemoteReceiverComponent = remote_ns.RemoteReceiverComponent
|
||||
|
||||
DUMPERS = {
|
||||
'lg': remote_ns.LGDumper,
|
||||
'nec': remote_ns.NECDumper,
|
||||
'panasonic': remote_ns.PanasonicDumper,
|
||||
'raw': remote_ns.RawDumper,
|
||||
'samsung': remote_ns.SamsungDumper,
|
||||
'sony': remote_ns.SonyDumper,
|
||||
'rc_switch': remote_ns.RCSwitchDumper,
|
||||
}
|
||||
|
||||
|
||||
def validate_dumpers_all(value):
|
||||
if not isinstance(value, (str, unicode)):
|
||||
raise vol.Invalid("Not valid dumpers")
|
||||
if value.upper() == "ALL":
|
||||
return list(sorted(list(DUMPERS)))
|
||||
raise vol.Invalid("Not valid dumpers")
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [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,
|
||||
vol.All(cv.ensure_list, [vol.All(vol.Lower, cv.one_of(*DUMPERS))])),
|
||||
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,
|
||||
})])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
pin = None
|
||||
for pin in gpio_input_pin_expression(conf[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_remote_receiver_component(pin)
|
||||
receiver = Pvariable(conf[CONF_ID], rhs)
|
||||
for dumper in conf[CONF_DUMP]:
|
||||
add(receiver.add_dumper(DUMPERS[dumper].new()))
|
||||
if CONF_TOLERANCE in conf:
|
||||
add(receiver.set_tolerance(conf[CONF_TOLERANCE]))
|
||||
if CONF_BUFFER_SIZE in conf:
|
||||
add(receiver.set_buffer_size(conf[CONF_BUFFER_SIZE]))
|
||||
if CONF_FILTER in conf:
|
||||
add(receiver.set_filter_us(conf[CONF_FILTER]))
|
||||
if CONF_IDLE in conf:
|
||||
add(receiver.set_idle_us(conf[CONF_IDLE]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER'
|
||||
116
esphomeyaml/components/remote_transmitter.py
Normal file
116
esphomeyaml/components/remote_transmitter.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
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.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression
|
||||
|
||||
remote_ns = esphomelib_ns.namespace('remote')
|
||||
|
||||
RemoteTransmitterComponent = remote_ns.RemoteTransmitterComponent
|
||||
RCSwitchProtocol = remote_ns.RCSwitchProtocol
|
||||
rc_switch_protocols = remote_ns.rc_switch_protocols
|
||||
|
||||
|
||||
def validate_rc_switch_code(value):
|
||||
if not isinstance(value, (str, unicode)):
|
||||
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): vol.All(
|
||||
cv.string, vol.Lower,
|
||||
cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p')),
|
||||
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): vol.All(cv.string, vol.Lower, cv.one_of('a', 'b', 'c', 'd')),
|
||||
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.All(cv.ensure_list, [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)),
|
||||
})])
|
||||
|
||||
|
||||
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 conf in config:
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(conf[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_remote_transmitter_component(pin)
|
||||
transmitter = Pvariable(conf[CONF_ID], rhs)
|
||||
if CONF_CARRIER_DUTY_PERCENT in conf:
|
||||
add(transmitter.set_carrier_duty_percent(conf[CONF_CARRIER_DUTY_PERCENT]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER'
|
||||
36
esphomeyaml/components/script.py
Normal file
36
esphomeyaml/components/script.py
Normal file
@@ -0,0 +1,36 @@
|
||||
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.helpers import NoArg, Pvariable, TemplateArguments, esphomelib_ns, get_variable
|
||||
|
||||
Script = esphomelib_ns.Script
|
||||
ScriptExecuteAction = esphomelib_ns.ScriptExecuteAction
|
||||
|
||||
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 = TemplateArguments(arg_type)
|
||||
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)
|
||||
@@ -1,13 +1,15 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_DEBOUNCE, CONF_DELTA, \
|
||||
CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, \
|
||||
CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, \
|
||||
CONF_NAME, CONF_OFFSET, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \
|
||||
CONF_THROTTLE, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
|
||||
setup_mqtt_component
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \
|
||||
CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, \
|
||||
CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_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_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, add_job, esphomelib_ns, \
|
||||
float_, process_lambda, setup_mqtt_component, templatable
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
@@ -18,123 +20,182 @@ def validate_recursive_filter(value):
|
||||
return FILTERS_SCHEMA(value)
|
||||
|
||||
|
||||
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.Any(
|
||||
vol.Schema({vol.Required(CONF_OFFSET): vol.Coerce(float)}),
|
||||
vol.Schema({vol.Required(CONF_MULTIPLY): vol.Coerce(float)}),
|
||||
vol.Schema({vol.Required(CONF_FILTER_OUT): vol.Coerce(float)}),
|
||||
vol.Schema({vol.Required(CONF_FILTER_NAN): None}),
|
||||
vol.Schema({
|
||||
vol.Required(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
|
||||
vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
|
||||
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
|
||||
})
|
||||
}),
|
||||
vol.Schema({
|
||||
vol.Required(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
|
||||
vol.Required(CONF_ALPHA): cv.positive_float,
|
||||
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
|
||||
})
|
||||
}),
|
||||
vol.Schema({vol.Required(CONF_LAMBDA): cv.string_strict}),
|
||||
vol.Schema({vol.Required(CONF_THROTTLE): cv.positive_time_period_milliseconds}),
|
||||
vol.Schema({vol.Required(CONF_DELTA): vol.Coerce(float)}),
|
||||
vol.Schema({vol.Required(CONF_UNIQUE): None}),
|
||||
vol.Schema({vol.Required(CONF_HEARTBEAT): cv.positive_time_period_milliseconds}),
|
||||
vol.Schema({vol.Required(CONF_DEBOUNCE): cv.positive_time_period_milliseconds}),
|
||||
vol.Schema({vol.Required(CONF_OR): validate_recursive_filter}),
|
||||
)])
|
||||
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]
|
||||
|
||||
MQTT_SENSOR_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
|
||||
vol.Optional(CONF_OFFSET): vol.Coerce(float),
|
||||
vol.Optional(CONF_MULTIPLY): vol.Coerce(float),
|
||||
vol.Optional(CONF_FILTER_OUT): vol.Coerce(float),
|
||||
vol.Optional(CONF_FILTER_NAN): None,
|
||||
vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
|
||||
vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
|
||||
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
|
||||
}),
|
||||
vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
|
||||
vol.Required(CONF_ALPHA): cv.positive_float,
|
||||
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
|
||||
}),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_THROTTLE): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_DELTA): vol.Coerce(float),
|
||||
vol.Optional(CONF_UNIQUE): None,
|
||||
vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_DEBOUNCE): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_OR): validate_recursive_filter,
|
||||
}, cv.has_exactly_one_key(*FILTER_KEYS))])
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
sensor_ns = esphomelib_ns.namespace('sensor')
|
||||
Sensor = sensor_ns.Sensor
|
||||
MQTTSensorComponent = sensor_ns.MQTTSensorComponent
|
||||
OffsetFilter = sensor_ns.OffsetFilter
|
||||
MultiplyFilter = sensor_ns.MultiplyFilter
|
||||
FilterOutValueFilter = sensor_ns.FilterOutValueFilter
|
||||
FilterOutNANFilter = sensor_ns.FilterOutNANFilter
|
||||
SlidingWindowMovingAverageFilter = sensor_ns.SlidingWindowMovingAverageFilter
|
||||
ExponentialMovingAverageFilter = sensor_ns.ExponentialMovingAverageFilter
|
||||
LambdaFilter = sensor_ns.LambdaFilter
|
||||
ThrottleFilter = sensor_ns.ThrottleFilter
|
||||
DeltaFilter = sensor_ns.DeltaFilter
|
||||
OrFilter = sensor_ns.OrFilter
|
||||
HeartbeatFilter = sensor_ns.HeartbeatFilter
|
||||
DebounceFilter = sensor_ns.DebounceFilter
|
||||
UniqueFilter = sensor_ns.UniqueFilter
|
||||
|
||||
SensorStateTrigger = sensor_ns.SensorStateTrigger
|
||||
SensorRawStateTrigger = sensor_ns.SensorRawStateTrigger
|
||||
ValueRangeTrigger = sensor_ns.ValueRangeTrigger
|
||||
|
||||
SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSensorComponent),
|
||||
cv.GenerateID(): cv.declare_variable_id(Sensor),
|
||||
vol.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_FILTERS): FILTERS_SCHEMA,
|
||||
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): vol.Coerce(float),
|
||||
vol.Optional(CONF_BELOW): vol.Coerce(float),
|
||||
}, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)),
|
||||
})
|
||||
|
||||
MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID('mqtt_sensor', CONF_MQTT_ID): cv.register_variable_id,
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
OffsetFilter = MockObj('new sensor::OffsetFilter')
|
||||
MultiplyFilter = MockObj('new sensor::MultiplyFilter')
|
||||
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
|
||||
FilterOutNANFilter = MockObj('new sensor::FilterOutNANFilter')
|
||||
SlidingWindowMovingAverageFilter = MockObj('new sensor::SlidingWindowMovingAverageFilter')
|
||||
ExponentialMovingAverageFilter = MockObj('new sensor::ExponentialMovingAverageFilter')
|
||||
LambdaFilter = MockObj('new sensor::LambdaFilter')
|
||||
ThrottleFilter = MockObj('new sensor::ThrottleFilter')
|
||||
DeltaFilter = MockObj('new sensor::DeltaFilter')
|
||||
OrFilter = MockObj('new sensor::OrFilter')
|
||||
HeartbeatFilter = MockObj('new sensor::HeartbeatFilter')
|
||||
DebounceFilter = MockObj('new sensor::DebounceFilter')
|
||||
UniqueFilter = MockObj('new sensor::UniqueFilter')
|
||||
SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_filter(config):
|
||||
if CONF_OFFSET in config:
|
||||
return OffsetFilter(config[CONF_OFFSET])
|
||||
if CONF_MULTIPLY in config:
|
||||
return MultiplyFilter(config[CONF_MULTIPLY])
|
||||
if CONF_FILTER_OUT in config:
|
||||
return FilterOutValueFilter(config[CONF_FILTER_OUT])
|
||||
if CONF_FILTER_NAN in config:
|
||||
return FilterOutNANFilter()
|
||||
if CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
|
||||
yield OffsetFilter.new(config[CONF_OFFSET])
|
||||
elif CONF_MULTIPLY in config:
|
||||
yield MultiplyFilter.new(config[CONF_MULTIPLY])
|
||||
elif CONF_FILTER_OUT in config:
|
||||
yield FilterOutValueFilter.new(config[CONF_FILTER_OUT])
|
||||
elif CONF_FILTER_NAN in config:
|
||||
yield FilterOutNANFilter.new()
|
||||
elif CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
|
||||
conf = config[CONF_SLIDING_WINDOW_MOVING_AVERAGE]
|
||||
return SlidingWindowMovingAverageFilter(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
|
||||
if CONF_EXPONENTIAL_MOVING_AVERAGE in config:
|
||||
yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
|
||||
elif CONF_EXPONENTIAL_MOVING_AVERAGE in config:
|
||||
conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE]
|
||||
return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
|
||||
if CONF_LAMBDA in config:
|
||||
s = u'[](float x) -> Optional<float> {{ return {}; }}'.format(config[CONF_LAMBDA])
|
||||
return LambdaFilter(RawExpression(s))
|
||||
if CONF_THROTTLE in config:
|
||||
return ThrottleFilter(config[CONF_THROTTLE])
|
||||
if CONF_DELTA in config:
|
||||
return DeltaFilter(config[CONF_DELTA])
|
||||
if CONF_OR in config:
|
||||
return OrFilter(setup_filters(config[CONF_OR]))
|
||||
if CONF_HEARTBEAT in config:
|
||||
return App.register_component(HeartbeatFilter(config[CONF_HEARTBEAT]))
|
||||
if CONF_DEBOUNCE in config:
|
||||
return App.register_component(DebounceFilter(config[CONF_DEBOUNCE]))
|
||||
if CONF_UNIQUE in config:
|
||||
return UniqueFilter()
|
||||
raise ValueError(u"Filter unsupported: {}".format(config))
|
||||
yield ExponentialMovingAverageFilter.new(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
|
||||
elif CONF_LAMBDA in config:
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')]):
|
||||
yield None
|
||||
yield LambdaFilter.new(lambda_)
|
||||
elif CONF_THROTTLE in config:
|
||||
yield ThrottleFilter.new(config[CONF_THROTTLE])
|
||||
elif CONF_DELTA in config:
|
||||
yield DeltaFilter.new(config[CONF_DELTA])
|
||||
elif CONF_OR in config:
|
||||
filters = None
|
||||
for filters in setup_filters(config[CONF_OR]):
|
||||
yield None
|
||||
yield OrFilter.new(filters)
|
||||
elif CONF_HEARTBEAT in config:
|
||||
yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT]))
|
||||
elif CONF_DEBOUNCE in config:
|
||||
yield App.register_component(DebounceFilter.new(config[CONF_DEBOUNCE]))
|
||||
elif CONF_UNIQUE in config:
|
||||
yield UniqueFilter.new()
|
||||
|
||||
|
||||
def setup_filters(config):
|
||||
return ArrayInitializer(*[setup_filter(x) for x in config])
|
||||
filters = []
|
||||
for conf in config:
|
||||
filter = None
|
||||
for filter in setup_filter(conf):
|
||||
yield None
|
||||
filters.append(filter)
|
||||
yield ArrayInitializer(*filters)
|
||||
|
||||
|
||||
def setup_mqtt_sensor_component(obj, 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:
|
||||
add(sensor_var.set_icon(config[CONF_ICON]))
|
||||
if CONF_ACCURACY_DECIMALS in config:
|
||||
add(sensor_var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
|
||||
if CONF_FILTERS in config:
|
||||
filters = None
|
||||
for filters in setup_filters(config[CONF_FILTERS]):
|
||||
yield
|
||||
add(sensor_var.set_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
rhs = sensor_var.make_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_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)
|
||||
if CONF_ABOVE in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_ABOVE], float_, float_):
|
||||
yield
|
||||
add(trigger.set_min(template_))
|
||||
if CONF_BELOW in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_BELOW], float_, float_):
|
||||
yield
|
||||
add(trigger.set_max(template_))
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
|
||||
if CONF_EXPIRE_AFTER in config:
|
||||
if config[CONF_EXPIRE_AFTER] is None:
|
||||
add(obj.disable_expire_after())
|
||||
add(mqtt_var.disable_expire_after())
|
||||
else:
|
||||
add(obj.set_expire_after(config[CONF_EXPIRE_AFTER]))
|
||||
setup_mqtt_component(obj, config)
|
||||
add(mqtt_var.set_expire_after(config[CONF_EXPIRE_AFTER]))
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
|
||||
def setup_sensor(obj, config):
|
||||
if CONF_UNIT_OF_MEASUREMENT in config:
|
||||
add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||
if CONF_ICON in config:
|
||||
add(obj.set_icon(config[CONF_ICON]))
|
||||
if CONF_ACCURACY_DECIMALS in config:
|
||||
add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
|
||||
if CONF_FILTERS in config:
|
||||
add(obj.set_filters(setup_filters(config[CONF_FILTERS])))
|
||||
def setup_sensor(sensor_obj, mqtt_obj, config):
|
||||
sensor_var = Pvariable(config[CONF_ID], sensor_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
def register_sensor(var, config):
|
||||
setup_sensor(var, config)
|
||||
rhs = App.register_sensor(var)
|
||||
mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_MQTT_ID], rhs)
|
||||
setup_mqtt_sensor_component(mqtt_sensor, config)
|
||||
sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
|
||||
rhs = App.register_sensor(sensor_var)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
|
||||
add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SENSOR'
|
||||
|
||||
@@ -3,37 +3,52 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ATTENUATION, CONF_ID, CONF_NAME, CONF_PIN, \
|
||||
from esphomeyaml.const import CONF_ATTENUATION, CONF_MAKE_ID, CONF_NAME, CONF_PIN, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable
|
||||
from esphomeyaml.helpers import App, Application, add, global_ns, variable
|
||||
|
||||
ATTENUATION_MODES = {
|
||||
'0db': 'ADC_0db',
|
||||
'2.5db': 'ADC_2_5db',
|
||||
'6db': 'ADC_6db',
|
||||
'11db': 'ADC_11db',
|
||||
'0db': global_ns.ADC_0db,
|
||||
'2.5db': global_ns.ADC_2_5db,
|
||||
'6db': global_ns.ADC_6db,
|
||||
'11db': global_ns.ADC_11db,
|
||||
}
|
||||
|
||||
ATTENUATION_MODE_SCHEMA = vol.Any(*list(ATTENUATION_MODES.keys()))
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('adc'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): pins.analog_pin,
|
||||
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, ATTENUATION_MODE_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
def validate_adc_pin(value):
|
||||
vcc = str(value).upper()
|
||||
if vcc == 'VCC':
|
||||
return cv.only_on_esp8266(vcc)
|
||||
return pins.analog_pin(value)
|
||||
|
||||
|
||||
MakeADCSensor = Application.MakeADCSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_adc_sensor(config[CONF_NAME], config[CONF_PIN],
|
||||
pin = config[CONF_PIN]
|
||||
if pin == 'VCC':
|
||||
pin = 0
|
||||
rhs = App.make_adc_sensor(config[CONF_NAME], pin,
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable('Application::MakeADCSensor', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
adc = make.Padc
|
||||
if CONF_ATTENUATION in config:
|
||||
attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]]
|
||||
add(adc.set_attenuation(RawExpression(attenuation)))
|
||||
sensor.setup_sensor(adc, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
add(adc.set_attenuation(ATTENUATION_MODES[config[CONF_ATTENUATION]]))
|
||||
sensor.setup_sensor(make.Padc, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADC_SENSOR'
|
||||
|
||||
|
||||
def required_build_flags(config):
|
||||
if config[CONF_PIN] == 'VCC':
|
||||
return '-DUSE_ADC_SENSOR_VCC'
|
||||
return None
|
||||
|
||||
@@ -2,30 +2,31 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \
|
||||
CONF_NAME, CONF_ID
|
||||
from esphomeyaml.helpers import RawExpression, get_variable, Pvariable
|
||||
from esphomeyaml.components.ads1115 import ADS1115Component
|
||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import get_variable
|
||||
|
||||
DEPENDENCIES = ['ads1115']
|
||||
|
||||
MUX = {
|
||||
'A0_A1': 'sensor::ADS1115_MULTIPLEXER_P0_N1',
|
||||
'A0_A3': 'sensor::ADS1115_MULTIPLEXER_P0_N3',
|
||||
'A1_A3': 'sensor::ADS1115_MULTIPLEXER_P1_N3',
|
||||
'A2_A3': 'sensor::ADS1115_MULTIPLEXER_P2_N3',
|
||||
'A0_GND': 'sensor::ADS1115_MULTIPLEXER_P0_NG',
|
||||
'A1_GND': 'sensor::ADS1115_MULTIPLEXER_P1_NG',
|
||||
'A2_GND': 'sensor::ADS1115_MULTIPLEXER_P2_NG',
|
||||
'A3_GND': 'sensor::ADS1115_MULTIPLEXER_P3_NG',
|
||||
'A0_A1': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N1,
|
||||
'A0_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N3,
|
||||
'A1_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_N3,
|
||||
'A2_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_N3,
|
||||
'A0_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_NG,
|
||||
'A1_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_NG,
|
||||
'A2_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_NG,
|
||||
'A3_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P3_NG,
|
||||
}
|
||||
|
||||
GAIN = {
|
||||
'6.144': 'sensor::ADS1115_GAIN_6P144',
|
||||
'4.096': 'sensor::ADS1115_GAIN_6P096',
|
||||
'2.048': 'sensor::ADS1115_GAIN_2P048',
|
||||
'1.024': 'sensor::ADS1115_GAIN_1P024',
|
||||
'0.512': 'sensor::ADS1115_GAIN_0P512',
|
||||
'0.256': 'sensor::ADS1115_GAIN_0P256',
|
||||
'6.144': sensor.sensor_ns.ADS1115_GAIN_6P144,
|
||||
'4.096': sensor.sensor_ns.ADS1115_GAIN_6P096,
|
||||
'2.048': sensor.sensor_ns.ADS1115_GAIN_2P048,
|
||||
'1.024': sensor.sensor_ns.ADS1115_GAIN_1P024,
|
||||
'0.512': sensor.sensor_ns.ADS1115_GAIN_0P512,
|
||||
'0.256': sensor.sensor_ns.ADS1115_GAIN_0P256,
|
||||
}
|
||||
|
||||
|
||||
@@ -35,28 +36,32 @@ def validate_gain(value):
|
||||
elif not isinstance(value, (str, unicode)):
|
||||
raise vol.Invalid('invalid gain "{}"'.format(value))
|
||||
|
||||
if value not in GAIN:
|
||||
raise vol.Invalid("Invalid gain, options are {}".format(', '.join(GAIN.keys())))
|
||||
return value
|
||||
return cv.one_of(*GAIN)(value)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('ads1115_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))),
|
||||
def validate_mux(value):
|
||||
value = cv.string(value).upper()
|
||||
value = value.replace(' ', '_')
|
||||
return cv.one_of(*MUX)(value)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_MULTIPLEXER): validate_mux,
|
||||
vol.Required(CONF_GAIN): validate_gain,
|
||||
vol.Optional(CONF_ADS1115_ID): cv.variable_id,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema)
|
||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_variable_id(ADS1115Component),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = get_variable(config.get(CONF_ADS1115_ID), u'sensor::ADS1115Component')
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ADS1115_ID]):
|
||||
yield
|
||||
|
||||
mux = RawExpression(MUX[config[CONF_MULTIPLEXER]])
|
||||
gain = RawExpression(GAIN[config[CONF_GAIN]])
|
||||
mux = MUX[config[CONF_MULTIPLEXER]]
|
||||
gain = GAIN[config[CONF_GAIN]]
|
||||
rhs = hub.get_sensor(config[CONF_NAME], mux, gain, config.get(CONF_UPDATE_INTERVAL))
|
||||
sensor_ = Pvariable('sensor::ADS1115Sensor', config[CONF_ID], rhs)
|
||||
sensor.register_sensor(sensor_, config)
|
||||
sensor.register_sensor(rhs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
|
||||
|
||||
@@ -2,36 +2,36 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_RESOLUTION, \
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
BH1750_RESOLUTIONS = {
|
||||
4.0: 'sensor::BH1750_RESOLUTION_4P0_LX',
|
||||
1.0: 'sensor::BH1750_RESOLUTION_1P0_LX',
|
||||
0.5: 'sensor::BH1750_RESOLUTION_0P5_LX',
|
||||
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,
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('bh1750_sensor'): cv.register_variable_id,
|
||||
MakeBH1750Sensor = Application.MakeBH1750Sensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBH1750Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x23): cv.i2c_address,
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, vol.Any(*BH1750_RESOLUTIONS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, cv.one_of(*BH1750_RESOLUTIONS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_bh1750_sensor(config[CONF_NAME], config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make_bh1750 = variable('Application::MakeBH1750Sensor', config[CONF_ID], rhs)
|
||||
make_bh1750 = variable(config[CONF_MAKE_ID], rhs)
|
||||
bh1750 = make_bh1750.Pbh1750
|
||||
if CONF_RESOLUTION in config:
|
||||
constant = BH1750_RESOLUTIONS[config[CONF_RESOLUTION]]
|
||||
add(bh1750.set_resolution(RawExpression(constant)))
|
||||
sensor.setup_sensor(bh1750, config)
|
||||
sensor.setup_mqtt_sensor_component(make_bh1750.Pmqtt, config)
|
||||
add(bh1750.set_resolution(BH1750_RESOLUTIONS[config[CONF_RESOLUTION]]))
|
||||
sensor.setup_sensor(bh1750, make_bh1750.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BH1750'
|
||||
|
||||
23
esphomeyaml/components/sensor/ble_rssi.py
Normal file
23
esphomeyaml/components/sensor/ble_rssi.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
|
||||
from esphomeyaml.helpers import get_variable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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_rssi_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
sensor.register_sensor(rhs, config)
|
||||
@@ -2,42 +2,43 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_NAME, \
|
||||
CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_IIR_FILTER, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': 'sensor::BME280_OVERSAMPLING_NONE',
|
||||
'1X': 'sensor::BME280_OVERSAMPLING_1X',
|
||||
'2X': 'sensor::BME280_OVERSAMPLING_2X',
|
||||
'4X': 'sensor::BME280_OVERSAMPLING_4X',
|
||||
'8X': 'sensor::BME280_OVERSAMPLING_8X',
|
||||
'16X': 'sensor::BME280_OVERSAMPLING_16X',
|
||||
'NONE': sensor.sensor_ns.BME280_OVERSAMPLING_NONE,
|
||||
'1X': sensor.sensor_ns.BME280_OVERSAMPLING_1X,
|
||||
'2X': sensor.sensor_ns.BME280_OVERSAMPLING_2X,
|
||||
'4X': sensor.sensor_ns.BME280_OVERSAMPLING_4X,
|
||||
'8X': sensor.sensor_ns.BME280_OVERSAMPLING_8X,
|
||||
'16X': sensor.sensor_ns.BME280_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': 'sensor::BME280_IIR_FILTER_OFF',
|
||||
'2X': 'sensor::BME280_IIR_FILTER_2X',
|
||||
'4X': 'sensor::BME280_IIR_FILTER_4X',
|
||||
'8X': 'sensor::BME280_IIR_FILTER_8X',
|
||||
'16X': 'sensor::BME280_IIR_FILTER_16X',
|
||||
'OFF': sensor.sensor_ns.BME280_IIR_FILTER_OFF,
|
||||
'2X': sensor.sensor_ns.BME280_IIR_FILTER_2X,
|
||||
'4X': sensor.sensor_ns.BME280_IIR_FILTER_4X,
|
||||
'8X': sensor.sensor_ns.BME280_IIR_FILTER_8X,
|
||||
'16X': sensor.sensor_ns.BME280_IIR_FILTER_16X,
|
||||
}
|
||||
|
||||
BME280_OVERSAMPLING_SENSOR_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, vol.Any(*OVERSAMPLING_OPTIONS)),
|
||||
BME280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
|
||||
})
|
||||
|
||||
MakeBME280Sensor = Application.MakeBME280Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('bme280'): cv.register_variable_id,
|
||||
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, vol.Any(*IIR_FILTER_OPTIONS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(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.update_interval,
|
||||
})
|
||||
|
||||
|
||||
@@ -47,29 +48,27 @@ def to_code(config):
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable('Application::MakeBME280Sensor', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
bme280 = make.Pbme280
|
||||
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
|
||||
add(bme280.set_temperature_oversampling(RawExpression(constant)))
|
||||
add(bme280.set_temperature_oversampling(constant))
|
||||
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
|
||||
add(bme280.set_pressure_oversampling(RawExpression(constant)))
|
||||
add(bme280.set_pressure_oversampling(constant))
|
||||
if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
|
||||
add(bme280.set_humidity_oversampling(RawExpression(constant)))
|
||||
add(bme280.set_humidity_oversampling(constant))
|
||||
if CONF_IIR_FILTER in config:
|
||||
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
|
||||
add(bme280.set_iir_filter(RawExpression(constant)))
|
||||
add(bme280.set_iir_filter(constant))
|
||||
|
||||
sensor.setup_sensor(bme280.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
|
||||
sensor.setup_sensor(bme280.Pget_pressure_sensor(), config[CONF_PRESSURE])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_pressure, config[CONF_PRESSURE])
|
||||
|
||||
sensor.setup_sensor(bme280.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
sensor.setup_sensor(bme280.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bme280.Pget_pressure_sensor(), make.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
sensor.setup_sensor(bme280.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BME280'
|
||||
|
||||
@@ -1,48 +1,55 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_NAME, \
|
||||
CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, CONF_GAS_RESISTANCE
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable
|
||||
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, CONF_HEATER, CONF_DURATION
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': 'sensor::BME680_OVERSAMPLING_NONE',
|
||||
'1X': 'sensor::BME680_OVERSAMPLING_1X',
|
||||
'2X': 'sensor::BME680_OVERSAMPLING_2X',
|
||||
'4X': 'sensor::BME680_OVERSAMPLING_4X',
|
||||
'8X': 'sensor::BME680_OVERSAMPLING_8X',
|
||||
'16X': 'sensor::BME680_OVERSAMPLING_16X',
|
||||
'NONE': sensor.sensor_ns.BME680_OVERSAMPLING_NONE,
|
||||
'1X': sensor.sensor_ns.BME680_OVERSAMPLING_1X,
|
||||
'2X': sensor.sensor_ns.BME680_OVERSAMPLING_2X,
|
||||
'4X': sensor.sensor_ns.BME680_OVERSAMPLING_4X,
|
||||
'8X': sensor.sensor_ns.BME680_OVERSAMPLING_8X,
|
||||
'16X': sensor.sensor_ns.BME680_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': 'sensor::BME680_IIR_FILTER_OFF',
|
||||
'1X': 'sensor::BME680_IIR_FILTER_1X',
|
||||
'3X': 'sensor::BME680_IIR_FILTER_3X',
|
||||
'7X': 'sensor::BME680_IIR_FILTER_7X',
|
||||
'15X': 'sensor::BME680_IIR_FILTER_15X',
|
||||
'31X': 'sensor::BME680_IIR_FILTER_31X',
|
||||
'63X': 'sensor::BME680_IIR_FILTER_63X',
|
||||
'127X': 'sensor::BME680_IIR_FILTER_127X',
|
||||
'OFF': sensor.sensor_ns.BME680_IIR_FILTER_OFF,
|
||||
'1X': sensor.sensor_ns.BME680_IIR_FILTER_1X,
|
||||
'3X': sensor.sensor_ns.BME680_IIR_FILTER_3X,
|
||||
'7X': sensor.sensor_ns.BME680_IIR_FILTER_7X,
|
||||
'15X': sensor.sensor_ns.BME680_IIR_FILTER_15X,
|
||||
'31X': sensor.sensor_ns.BME680_IIR_FILTER_31X,
|
||||
'63X': sensor.sensor_ns.BME680_IIR_FILTER_63X,
|
||||
'127X': sensor.sensor_ns.BME680_IIR_FILTER_127X,
|
||||
}
|
||||
|
||||
BME680_OVERSAMPLING_SENSOR_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, vol.Any(*OVERSAMPLING_OPTIONS)),
|
||||
BME680_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
|
||||
})
|
||||
|
||||
MakeBME680Sensor = Application.MakeBME680Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('bme680'): cv.register_variable_id,
|
||||
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): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, vol.Any(*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),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA),
|
||||
vol.Required(CONF_GAS_RESISTANCE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
@@ -53,32 +60,35 @@ def to_code(config):
|
||||
config[CONF_GAS_RESISTANCE][CONF_NAME],
|
||||
config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable('Application::MakeBME680Sensor', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
bme680 = make.Pbme680
|
||||
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
|
||||
add(bme680.set_temperature_oversampling(RawExpression(constant)))
|
||||
add(bme680.set_temperature_oversampling(constant))
|
||||
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
|
||||
add(bme680.set_pressure_oversampling(RawExpression(constant)))
|
||||
add(bme680.set_pressure_oversampling(constant))
|
||||
if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
|
||||
constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
|
||||
add(bme680.set_humidity_oversampling(RawExpression(constant)))
|
||||
add(bme680.set_humidity_oversampling(constant))
|
||||
if CONF_IIR_FILTER in config:
|
||||
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
|
||||
add(bme680.set_iir_filter(RawExpression(constant)))
|
||||
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(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
|
||||
sensor.setup_sensor(bme680.Pget_pressure_sensor(), config[CONF_PRESSURE])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_pressure, config[CONF_PRESSURE])
|
||||
|
||||
sensor.setup_sensor(bme680.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
|
||||
sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), config[CONF_GAS_RESISTANCE])
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt_gas_resistance, config[CONF_GAS_RESISTANCE])
|
||||
sensor.setup_sensor(bme680.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bme680.Pget_pressure_sensor(), make.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
sensor.setup_sensor(bme680.Pget_humidity_sensor(), make.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), make.Pmqtt_gas_resistance,
|
||||
config[CONF_GAS_RESISTANCE])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BME680'
|
||||
|
||||
@@ -2,19 +2,20 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, \
|
||||
CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, HexIntLiteral, add, variable
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, HexIntLiteral, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeBMP085Sensor = Application.MakeBMP085Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('bmp085_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_PRESSURE): MQTT_SENSOR_SCHEMA,
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP085Sensor),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
@@ -22,13 +23,14 @@ def to_code(config):
|
||||
rhs = App.make_bmp085_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_PRESSURE][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
bmp = variable('Application::MakeBMP085Sensor', config[CONF_ID], rhs)
|
||||
bmp = variable(config[CONF_MAKE_ID], rhs)
|
||||
if CONF_ADDRESS in config:
|
||||
add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS])))
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), config[CONF_PRESSURE])
|
||||
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE])
|
||||
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), bmp.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), bmp.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'
|
||||
|
||||
67
esphomeyaml/components/sensor/bmp280.py
Normal file
67
esphomeyaml/components/sensor/bmp280.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
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.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': sensor.sensor_ns.BMP280_OVERSAMPLING_NONE,
|
||||
'1X': sensor.sensor_ns.BMP280_OVERSAMPLING_1X,
|
||||
'2X': sensor.sensor_ns.BMP280_OVERSAMPLING_2X,
|
||||
'4X': sensor.sensor_ns.BMP280_OVERSAMPLING_4X,
|
||||
'8X': sensor.sensor_ns.BMP280_OVERSAMPLING_8X,
|
||||
'16X': sensor.sensor_ns.BMP280_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': sensor.sensor_ns.BMP280_IIR_FILTER_OFF,
|
||||
'2X': sensor.sensor_ns.BMP280_IIR_FILTER_2X,
|
||||
'4X': sensor.sensor_ns.BMP280_IIR_FILTER_4X,
|
||||
'8X': sensor.sensor_ns.BMP280_IIR_FILTER_8X,
|
||||
'16X': sensor.sensor_ns.BMP280_IIR_FILTER_16X,
|
||||
}
|
||||
|
||||
BMP280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
|
||||
})
|
||||
|
||||
MakeBMP280Sensor = Application.MakeBMP280Sensor
|
||||
|
||||
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),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
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])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BMP280'
|
||||
42
esphomeyaml/components/sensor/cse7766.py
Normal file
42
esphomeyaml/components/sensor/cse7766.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
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_VOLTAGE
|
||||
from esphomeyaml.helpers import App, Pvariable, get_variable
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
CSE7766Component = sensor.sensor_ns.CSE7766Component
|
||||
|
||||
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),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_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)
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_CSE7766'
|
||||
@@ -2,36 +2,30 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS
|
||||
from esphomeyaml.components.dallas import DallasComponent
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \
|
||||
CONF_RESOLUTION, \
|
||||
CONF_UPDATE_INTERVAL, CONF_ID
|
||||
from esphomeyaml.helpers import HexIntLiteral, get_variable, Pvariable
|
||||
CONF_RESOLUTION
|
||||
from esphomeyaml.helpers import HexIntLiteral, get_variable
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('dallas_sensor'): cv.register_variable_id,
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int,
|
||||
vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int,
|
||||
vol.Optional(CONF_DALLAS_ID): cv.variable_id,
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=8, max=12)),
|
||||
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
|
||||
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)),
|
||||
}), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = get_variable(config.get(CONF_DALLAS_ID), DALLAS_COMPONENT_CLASS)
|
||||
update_interval = config.get(CONF_UPDATE_INTERVAL)
|
||||
if CONF_RESOLUTION in config and update_interval is None:
|
||||
update_interval = 10000
|
||||
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_DALLAS_ID]):
|
||||
yield
|
||||
if CONF_ADDRESS in config:
|
||||
address = HexIntLiteral(config[CONF_ADDRESS])
|
||||
rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, update_interval,
|
||||
config.get(CONF_RESOLUTION))
|
||||
rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, config.get(CONF_RESOLUTION))
|
||||
else:
|
||||
rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX],
|
||||
update_interval, config.get(CONF_RESOLUTION))
|
||||
sensor_ = Pvariable('sensor::DallasTemperatureSensor', config[CONF_ID], rhs)
|
||||
sensor.register_sensor(sensor_, config)
|
||||
config.get(CONF_RESOLUTION))
|
||||
sensor.register_sensor(rhs, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
|
||||
|
||||
@@ -2,43 +2,47 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable, gpio_output_pin_expression
|
||||
from esphomeyaml.pins import GPIO_OUTPUT_PIN_SCHEMA
|
||||
from esphomeyaml.helpers import App, Application, add, gpio_output_pin_expression, variable
|
||||
from esphomeyaml.pins import gpio_output_pin_schema
|
||||
|
||||
DHT_MODELS = {
|
||||
'AUTO_DETECT': 'sensor::DHT_MODEL_AUTO_DETECT',
|
||||
'DHT11': 'sensor::DHT_MODEL_DHT11',
|
||||
'DHT22': 'sensor::DHT_MODEL_DHT22',
|
||||
'AM2302': 'sensor::DHT_MODEL_AM2302',
|
||||
'RHT03': 'sensor::DHT_MODEL_RHT03',
|
||||
'AUTO_DETECT': sensor.sensor_ns.DHT_MODEL_AUTO_DETECT,
|
||||
'DHT11': sensor.sensor_ns.DHT_MODEL_DHT11,
|
||||
'DHT22': sensor.sensor_ns.DHT_MODEL_DHT22,
|
||||
'AM2302': sensor.sensor_ns.DHT_MODEL_AM2302,
|
||||
'RHT03': sensor.sensor_ns.DHT_MODEL_RHT03,
|
||||
}
|
||||
|
||||
MakeDHTSensor = Application.MakeDHTSensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('dht_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): GPIO_OUTPUT_PIN_SCHEMA,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_MODEL): vol.All(vol.Upper, vol.Any(*DHT_MODELS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHTSensor),
|
||||
vol.Required(CONF_PIN): gpio_output_pin_schema,
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_MODEL): vol.All(vol.Upper, cv.one_of(*DHT_MODELS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = gpio_output_pin_expression(config[CONF_PIN])
|
||||
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('Application::MakeDHTSensor', config[CONF_ID], rhs)
|
||||
dht = variable(config[CONF_MAKE_ID], rhs)
|
||||
if CONF_MODEL in config:
|
||||
constant = DHT_MODELS[config[CONF_MODEL]]
|
||||
add(dht.Pdht.set_dht_model(RawExpression(constant)))
|
||||
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
add(dht.Pdht.set_dht_model(constant))
|
||||
|
||||
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(),
|
||||
dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(),
|
||||
dht.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DHT_SENSOR'
|
||||
|
||||
@@ -2,18 +2,19 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, variable
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeDHT12Sensor = Application.MakeDHT12Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('dht_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHT12Sensor),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
@@ -21,11 +22,12 @@ 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('Application::MakeDHT12Sensor', config[CONF_ID], rhs)
|
||||
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
dht = variable(config[CONF_MAKE_ID], rhs)
|
||||
|
||||
sensor.setup_sensor(dht.Pdht12.Pget_temperature_sensor(), dht.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht12.Pget_humidity_sensor(), dht.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DHT12_SENSOR'
|
||||
|
||||
28
esphomeyaml/components/sensor/duty_cycle.py
Normal file
28
esphomeyaml/components/sensor/duty_cycle.py
Normal file
@@ -0,0 +1,28 @@
|
||||
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, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, variable
|
||||
|
||||
MakeDutyCycleSensor = Application.MakeDutyCycleSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = None
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DUTY_CYCLE_SENSOR'
|
||||
24
esphomeyaml/components/sensor/esp32_hall.py
Normal file
24
esphomeyaml/components/sensor/esp32_hall.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
MakeESP32HallSensor = Application.MakeESP32HallSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeESP32HallSensor),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_esp32_hall_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Phall, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP32_HALL_SENSOR'
|
||||
@@ -2,18 +2,19 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, variable
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeHDC1080Sensor = Application.MakeHDC1080Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('dht_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHDC1080Sensor),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
@@ -21,11 +22,13 @@ def to_code(config):
|
||||
rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
hdc1080 = variable('Application::MakeHDC1080Sensor', config[CONF_ID], rhs)
|
||||
sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
hdc1080 = variable(config[CONF_MAKE_ID], rhs)
|
||||
|
||||
sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(),
|
||||
hdc1080.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), hdc1080.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'
|
||||
|
||||
57
esphomeyaml/components/sensor/hlw8012.py
Normal file
57
esphomeyaml/components/sensor/hlw8012.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
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.helpers import App, Pvariable, add, gpio_output_pin_expression
|
||||
|
||||
HLW8012Component = sensor.sensor_ns.HLW8012Component
|
||||
HLW8012VoltageSensor = sensor.sensor_ns.HLW8012VoltageSensor
|
||||
HLW8012CurrentSensor = sensor.sensor_ns.HLW8012CurrentSensor
|
||||
HLW8012PowerSensor = sensor.sensor_ns.HLW8012PowerSensor
|
||||
|
||||
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),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
|
||||
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,
|
||||
}), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, CONF_POWER))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
sel = None
|
||||
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_CHANGE_MODE_EVERY in config:
|
||||
add(hlw.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HLW8012'
|
||||
73
esphomeyaml/components/sensor/hmc5883l.py
Normal file
73
esphomeyaml/components/sensor/hmc5883l.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_RANGE
|
||||
from esphomeyaml.helpers import App, Pvariable, add
|
||||
|
||||
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.HMC5883LComponent
|
||||
HMC5883LFieldStrengthSensor = sensor.sensor_ns.HMC5883LFieldStrengthSensor
|
||||
HMC5883LHeadingSensor = sensor.sensor_ns.HMC5883LHeadingSensor
|
||||
|
||||
HMC5883L_RANGES = {
|
||||
88: sensor.sensor_ns.HMC5883L_RANGE_88_UT,
|
||||
130: sensor.sensor_ns.HMC5883L_RANGE_130_UT,
|
||||
190: sensor.sensor_ns.HMC5883L_RANGE_190_UT,
|
||||
250: sensor.sensor_ns.HMC5883L_RANGE_250_UT,
|
||||
400: sensor.sensor_ns.HMC5883L_RANGE_400_UT,
|
||||
470: sensor.sensor_ns.HMC5883L_RANGE_470_UT,
|
||||
560: sensor.sensor_ns.HMC5883L_RANGE_560_UT,
|
||||
810: sensor.sensor_ns.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(value))
|
||||
|
||||
|
||||
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),
|
||||
vol.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_HEADING): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
vol.Optional(CONF_RANGE): validate_range,
|
||||
}), cv.has_at_least_one_key(CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z,
|
||||
CONF_HEADING))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HMC5883L'
|
||||
@@ -2,18 +2,19 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, variable
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeHTU21DSensor = Application.MakeHTU21DSensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('htu21d'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHTU21DSensor),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
@@ -21,11 +22,11 @@ def to_code(config):
|
||||
rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
htu21d = variable('Application::MakeHTU21DSensor', config[CONF_ID], rhs)
|
||||
sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
htu21d = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), htu21d.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), htu21d.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'
|
||||
|
||||
46
esphomeyaml/components/sensor/hx711.py
Normal file
46
esphomeyaml/components/sensor/hx711.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_GAIN, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_CLK_PIN
|
||||
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable
|
||||
|
||||
MakeHX711Sensor = Application.MakeHX711Sensor
|
||||
|
||||
CONF_DOUT_PIN = 'dout_pin'
|
||||
|
||||
GAINS = {
|
||||
128: sensor.sensor_ns.HX711_GAIN_128,
|
||||
32: sensor.sensor_ns.HX711_GAIN_32,
|
||||
64: sensor.sensor_ns.HX711_GAIN_64,
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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): vol.All(cv.int_, cv.one_of(*GAINS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
dout_pin = None
|
||||
for dout_pin in gpio_input_pin_expression(config[CONF_DOUT_PIN]):
|
||||
yield
|
||||
sck_pin = None
|
||||
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)
|
||||
|
||||
if CONF_GAIN in config:
|
||||
add(make.Phx711.set_gain(GAINS[config[CONF_GAIN]]))
|
||||
|
||||
sensor.setup_sensor(make.Phx711, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HX711'
|
||||
53
esphomeyaml/components/sensor/ina219.py
Normal file
53
esphomeyaml/components/sensor/ina219.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, \
|
||||
CONF_MAX_VOLTAGE, CONF_NAME, CONF_POWER, CONF_UPDATE_INTERVAL, CONF_BUS_VOLTAGE, \
|
||||
CONF_SHUNT_VOLTAGE, CONF_SHUNT_RESISTANCE
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
INA219Component = sensor.sensor_ns.INA219Component
|
||||
INA219VoltageSensor = sensor.sensor_ns.INA219VoltageSensor
|
||||
INA219CurrentSensor = sensor.sensor_ns.INA219CurrentSensor
|
||||
INA219PowerSensor = sensor.sensor_ns.INA219PowerSensor
|
||||
|
||||
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),
|
||||
vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
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,
|
||||
}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT,
|
||||
CONF_POWER))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_INA219'
|
||||
64
esphomeyaml/components/sensor/ina3221.py
Normal file
64
esphomeyaml/components/sensor/ina3221.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
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.helpers import App, Pvariable, add
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
CONF_CHANNEL_1 = 'channel_1'
|
||||
CONF_CHANNEL_2 = 'channel_2'
|
||||
CONF_CHANNEL_3 = 'channel_3'
|
||||
|
||||
INA3221Component = sensor.sensor_ns.INA3221Component
|
||||
INA3221VoltageSensor = sensor.sensor_ns.INA3221VoltageSensor
|
||||
INA3221CurrentSensor = sensor.sensor_ns.INA3221CurrentSensor
|
||||
INA3221PowerSensor = sensor.sensor_ns.INA3221PowerSensor
|
||||
|
||||
INA3221_CHANNEL_SCHEMA = vol.All(vol.Schema({
|
||||
vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance,
|
||||
vol.Range(min=0.0, max=32.0)),
|
||||
}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT,
|
||||
CONF_POWER))
|
||||
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_INA3221'
|
||||
34
esphomeyaml/components/sensor/max6675.py
Normal file
34
esphomeyaml/components/sensor/max6675.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
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.helpers import App, Application, get_variable, gpio_output_pin_expression, variable
|
||||
|
||||
MakeMAX6675Sensor = Application.MakeMAX6675Sensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor),
|
||||
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,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
spi = None
|
||||
for spi in get_variable(config[CONF_SPI_ID]):
|
||||
yield
|
||||
cs = None
|
||||
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||
yield
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MAX6675_SENSOR'
|
||||
38
esphomeyaml/components/sensor/mhz19.py
Normal file
38
esphomeyaml/components/sensor/mhz19.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
from esphomeyaml.const import CONF_CO2, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, CONF_UART_ID, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, get_variable, variable
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
MakeMHZ19Sensor = Application.MakeMHZ19Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
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),
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
uart = None
|
||||
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
|
||||
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])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MHZ19'
|
||||
@@ -2,8 +2,7 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_ID_SCHEMA
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_MQTT_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
|
||||
@@ -16,58 +15,57 @@ CONF_GYRO_X = 'gyro_x'
|
||||
CONF_GYRO_Y = 'gyro_y'
|
||||
CONF_GYRO_Z = 'gyro_z'
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('mpu6050'): cv.register_variable_id,
|
||||
MPU6050Component = sensor.sensor_ns.MPU6050Component
|
||||
MPU6050AccelSensor = sensor.sensor_ns.MPU6050AccelSensor
|
||||
MPU6050GyroSensor = sensor.sensor_ns.MPU6050GyroSensor
|
||||
MPU6050TemperatureSensor = sensor.sensor_ns.MPU6050TemperatureSensor
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(MPU6050Component),
|
||||
vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address,
|
||||
vol.Optional(CONF_ACCEL_X): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_ACCEL_Y): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_ACCEL_Z): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_GYRO_X): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_GYRO_Y): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_GYRO_Z): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_TEMPERATURE): MQTT_SENSOR_ID_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
})
|
||||
vol.Optional(CONF_ACCEL_X): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ACCEL_Y): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ACCEL_Z): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_GYRO_X): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_GYRO_Y): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_GYRO_Z): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}), cv.has_at_least_one_key(CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
|
||||
CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_mpu6050_sensor(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
|
||||
mpu = Pvariable('sensor::MPU6050Component', config[CONF_ID], rhs)
|
||||
mpu = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_ACCEL_X in config:
|
||||
conf = config[CONF_ACCEL_X]
|
||||
rhs = mpu.Pmake_accel_x_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
if CONF_ACCEL_Y in config:
|
||||
conf = config[CONF_ACCEL_Y]
|
||||
rhs = mpu.Pmake_accel_y_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
if CONF_ACCEL_Z in config:
|
||||
conf = config[CONF_ACCEL_Z]
|
||||
rhs = mpu.Pmake_accel_z_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
if CONF_GYRO_X in config:
|
||||
conf = config[CONF_GYRO_X]
|
||||
rhs = mpu.Pmake_gyro_x_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
if CONF_GYRO_Y in config:
|
||||
conf = config[CONF_GYRO_Y]
|
||||
rhs = mpu.Pmake_gyro_y_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
if CONF_GYRO_Z in config:
|
||||
conf = config[CONF_GYRO_Z]
|
||||
rhs = mpu.Pmake_gyro_z_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME])
|
||||
sensor_ = Pvariable('sensor::MPU6050TemperatureSensor', conf[CONF_MQTT_ID], rhs)
|
||||
sensor.register_sensor(sensor_, conf)
|
||||
sensor.register_sensor(rhs, conf)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MPU6050'
|
||||
|
||||
29
esphomeyaml/components/sensor/mqtt_subscribe.py
Normal file
29
esphomeyaml/components/sensor/mqtt_subscribe.py
Normal file
@@ -0,0 +1,29 @@
|
||||
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.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
MakeMQTTSubscribeSensor = Application.MakeMQTTSubscribeSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMQTTSubscribeSensor),
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_QOS): cv.mqtt_qos,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_mqtt_subscribe_sensor(config[CONF_NAME], config[CONF_TOPIC])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
|
||||
if CONF_QOS in config:
|
||||
add(make.Psensor.set_qos(config[CONF_QOS]))
|
||||
|
||||
sensor.setup_sensor(make.Psensor, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_SENSOR'
|
||||
37
esphomeyaml/components/sensor/ms5611.py
Normal file
37
esphomeyaml/components/sensor/ms5611.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
MakeMS5611Sensor = Application.MakeMS5611Sensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
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),
|
||||
vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
})
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if CONF_ADDRESS in config:
|
||||
add(make.Pms5611.set_address(config[CONF_ADDRESS]))
|
||||
|
||||
sensor.setup_sensor(make.Pms5611.Pget_temperature_sensor(), make.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(make.Pms5611.Pget_pressure_sensor(), make.Pmqtt_pressure,
|
||||
config[CONF_PRESSURE])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MS5611'
|
||||
87
esphomeyaml/components/sensor/pmsx003.py
Normal file
87
esphomeyaml/components/sensor/pmsx003.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.components import sensor
|
||||
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.helpers import App, Pvariable, get_variable
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
PMSX003Component = sensor.sensor_ns.PMSX003Component
|
||||
|
||||
CONF_PMSX003 = 'PMSX003'
|
||||
CONF_PMS5003T = 'PMS5003T'
|
||||
CONF_PMS5003ST = 'PMS5003ST'
|
||||
|
||||
PMSX003_TYPES = {
|
||||
CONF_PMSX003: sensor.sensor_ns.PMSX003_TYPE_X003,
|
||||
CONF_PMS5003T: sensor.sensor_ns.PMSX003_TYPE_5003T,
|
||||
CONF_PMS5003ST: sensor.sensor_ns.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.iteritems():
|
||||
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
|
||||
|
||||
|
||||
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): vol.All(vol.Upper, cv.one_of(*PMSX003_TYPES)),
|
||||
|
||||
vol.Optional(CONF_PM_1_0): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_PM_2_5): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_PM_10_0): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_FORMALDEHYDE): cv.nameable(sensor.SENSOR_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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PMSX003'
|
||||
@@ -1,62 +1,67 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml import core, pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_ID, CONF_INTERNAL_FILTER, \
|
||||
CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
|
||||
from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_INTERNAL_FILTER, \
|
||||
CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
|
||||
ESP_PLATFORM_ESP32
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
GPIO_PULL_MODES = {
|
||||
'PULLUP': 'GPIO_PULLUP_ONLY',
|
||||
'PULLDOWN': 'GPIO_PULLDOWN_ONLY',
|
||||
'PULLUP_PULLDOWN': 'GPIO_PULLUP_PULLDOWN',
|
||||
'FLOATING': 'GPIO_FLOATING',
|
||||
}
|
||||
|
||||
GPIO_PULL_MODE_SCHEMA = vol.All(vol.Upper, vol.Any(*list(GPIO_PULL_MODES.keys())))
|
||||
from esphomeyaml.helpers import App, Application, add, variable, gpio_input_pin_expression
|
||||
|
||||
COUNT_MODES = {
|
||||
'DISABLE': 'PCNT_COUNT_DIS',
|
||||
'INCREMENT': 'PCNT_COUNT_INC',
|
||||
'DECREMENT': 'PCNT_COUNT_DEC',
|
||||
'DISABLE': sensor.sensor_ns.PULSE_COUNTER_DISABLE,
|
||||
'INCREMENT': sensor.sensor_ns.PULSE_COUNTER_INCREMENT,
|
||||
'DECREMENT': sensor.sensor_ns.PULSE_COUNTER_DECREMENT,
|
||||
}
|
||||
|
||||
COUNT_MODE_SCHEMA = vol.All(vol.Upper, vol.Any(*list(COUNT_MODES.keys())))
|
||||
COUNT_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*COUNT_MODES))
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('pulse_counter'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): pins.input_pin,
|
||||
vol.Optional(CONF_PULL_MODE): GPIO_PULL_MODE_SCHEMA,
|
||||
MakePulseCounterSensor = Application.MakePulseCounterSensor
|
||||
|
||||
|
||||
def validate_internal_filter(value):
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_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
|
||||
else:
|
||||
return cv.positive_time_period_microseconds(value)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakePulseCounterSensor),
|
||||
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.MQTT_SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_INTERNAL_FILTER): validate_internal_filter,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
|
||||
vol.Optional(CONF_PULL_MODE): cv.invalid("The pull_mode option has been removed in 1.7.0, "
|
||||
"please use the pin mode schema now.")
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_pulse_counter_sensor(config[CONF_NAME], config[CONF_PIN],
|
||||
pin = None
|
||||
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('Application::MakePulseCounterSensor', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
pcnt = make.Ppcnt
|
||||
if CONF_PULL_MODE in config:
|
||||
pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]]
|
||||
add(pcnt.set_pull_mode(RawExpression(pull_mode)))
|
||||
if CONF_COUNT_MODE in config:
|
||||
count_mode = config[CONF_COUNT_MODE]
|
||||
rising_edge = COUNT_MODES[count_mode[CONF_RISING_EDGE]]
|
||||
falling_edge = COUNT_MODES[count_mode[CONF_FALLING_EDGE]]
|
||||
add(pcnt.set_edge_mode(RawExpression(rising_edge), RawExpression(falling_edge)))
|
||||
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(pcnt, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
add(pcnt.set_filter_us(config[CONF_INTERNAL_FILTER]))
|
||||
sensor.setup_sensor(make.Ppcnt, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'
|
||||
|
||||
@@ -3,42 +3,49 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_RESOLUTION
|
||||
from esphomeyaml.helpers import App, RawExpression, add, gpio_input_pin_expression, variable
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION
|
||||
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable
|
||||
|
||||
RESOLUTIONS = {
|
||||
'1': 'sensor::ROTARY_ENCODER_1_PULSE_PER_CYCLE',
|
||||
'2': 'sensor::ROTARY_ENCODER_2_PULSES_PER_CYCLE',
|
||||
'4': 'sensor::ROTARY_ENCODER_4_PULSES_PER_CYCLE',
|
||||
'1': sensor.sensor_ns.ROTARY_ENCODER_1_PULSE_PER_CYCLE,
|
||||
'2': sensor.sensor_ns.ROTARY_ENCODER_2_PULSES_PER_CYCLE,
|
||||
'4': sensor.sensor_ns.ROTARY_ENCODER_4_PULSES_PER_CYCLE,
|
||||
}
|
||||
|
||||
CONF_PIN_A = 'pin_a'
|
||||
CONF_PIN_B = 'pin_b'
|
||||
CONF_PIN_RESET = 'pin_reset'
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('rotary_encoder'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN_A): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
|
||||
vol.Required(CONF_PIN_B): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
|
||||
vol.Optional(CONF_PIN_RESET): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(cv.string, vol.Any(*RESOLUTIONS)),
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
MakeRotaryEncoderSensor = Application.MakeRotaryEncoderSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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)),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin_a = gpio_input_pin_expression(config[CONF_PIN_A])
|
||||
pin_b = gpio_input_pin_expression(config[CONF_PIN_B])
|
||||
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('Application::MakeRotaryEncoderSensor', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
encoder = make.Protary_encoder
|
||||
if CONF_PIN_RESET in config:
|
||||
pin_i = gpio_input_pin_expression(config[CONF_PIN_RESET])
|
||||
pin_i = None
|
||||
for pin_i in gpio_input_pin_expression(config[CONF_PIN_RESET]):
|
||||
yield
|
||||
add(encoder.set_reset_pin(pin_i))
|
||||
if CONF_RESOLUTION in config:
|
||||
resolution = RESOLUTIONS[config[CONF_RESOLUTION]]
|
||||
add(encoder.set_resolution(RawExpression(resolution)))
|
||||
sensor.setup_sensor(encoder, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
add(encoder.set_resolution(resolution))
|
||||
sensor.setup_sensor(encoder, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR'
|
||||
|
||||
@@ -2,44 +2,37 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
|
||||
CONF_UPDATE_INTERVAL, CONF_ADDRESS, CONF_ACCURACY
|
||||
from esphomeyaml.helpers import App, variable, RawExpression, add
|
||||
from esphomeyaml.const import CONF_ACCURACY, CONF_ADDRESS, CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
SHT_ACCURACIES = {
|
||||
'LOW': 'sensor::SHT3XD_ACCURACY_LOW',
|
||||
'MEDIUM': 'sensor::SHT3XD_ACCURACY_MEDIUM',
|
||||
'HIGH': 'sensor::SHT3XD_ACCURACY_HIGH',
|
||||
}
|
||||
MakeSHT3XDSensor = Application.MakeSHT3XDSensor
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('sht3xd'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSHT3XDSensor),
|
||||
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ADDRESS, default=0x44): cv.i2c_address,
|
||||
vol.Optional(CONF_ACCURACY): vol.All(vol.Upper, vol.Any(*SHT_ACCURACIES)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
|
||||
vol.Optional(CONF_ACCURACY): cv.invalid("The accuracy option has been removed and now "
|
||||
"defaults to HIGH."),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_sht3xd_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
sht3xd = variable('Application::MakeSHT3XDSensor', config[CONF_ID], rhs)
|
||||
sht3xd = variable(config[CONF_MAKE_ID], rhs)
|
||||
|
||||
if CONF_ACCURACY in config:
|
||||
constant = RawExpression(SHT_ACCURACIES[config[CONF_ACCURACY]])
|
||||
add(sht3xd.Psht3xd.set_accuracy(constant))
|
||||
|
||||
sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(sht3xd.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
|
||||
sensor.setup_sensor(sht3xd.PPsht3xd.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(sht3xd.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), sht3xd.Pmqtt_temperature,
|
||||
config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(sht3xd.Psht3xd.Pget_humidity_sensor(), sht3xd.Pmqtt_humidity,
|
||||
config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SHT3XD'
|
||||
|
||||
80
esphomeyaml/components/sensor/tcs34725.py
Normal file
80
esphomeyaml/components/sensor/tcs34725.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# coding=utf-8
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
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.helpers import App, Pvariable, add
|
||||
|
||||
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.TCS34725Component
|
||||
|
||||
TCS34725_INTEGRATION_TIMES = {
|
||||
'2.4ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_2_4MS,
|
||||
'24ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_24MS,
|
||||
'50ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_50MS,
|
||||
'101ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_101MS,
|
||||
'154ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_154MS,
|
||||
'700ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_700MS,
|
||||
}
|
||||
|
||||
TCS34725_GAINS = {
|
||||
'1X': sensor.sensor_ns.TCS34725_GAIN_1X,
|
||||
'4X': sensor.sensor_ns.TCS34725_GAIN_4X,
|
||||
'16X': sensor.sensor_ns.TCS34725_GAIN_16X,
|
||||
'60X': sensor.sensor_ns.TCS34725_GAIN_60X,
|
||||
}
|
||||
|
||||
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(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_GREEN_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_BLUE_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_CLEAR_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_INTEGRATION_TIME): cv.one_of(*TCS34725_INTEGRATION_TIMES),
|
||||
vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*TCS34725_GAINS)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}), cv.has_at_least_one_key(CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL,
|
||||
CONF_CLEAR_CHANNEL, CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TCS34725'
|
||||
29
esphomeyaml/components/sensor/template.py
Normal file
29
esphomeyaml/components/sensor/template.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional, add
|
||||
|
||||
MakeTemplateSensor = Application.MakeTemplateSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSensor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_template_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config)
|
||||
|
||||
template_ = None
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(float_)):
|
||||
yield
|
||||
add(make.Ptemplate_.set_template(template_))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR'
|
||||
@@ -2,20 +2,20 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, CONF_NAME, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, RawExpression, add, variable
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_INTEGRATION_TIME, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
INTEGRATION_TIMES = {
|
||||
14: 'sensor::TSL2561_INTEGRATION_14MS',
|
||||
101: 'sensor::TSL2561_INTEGRATION_101MS',
|
||||
402: 'sensor::TSL2561_INTEGRATION_402MS',
|
||||
14: sensor.sensor_ns.TSL2561_INTEGRATION_14MS,
|
||||
101: sensor.sensor_ns.TSL2561_INTEGRATION_101MS,
|
||||
402: sensor.sensor_ns.TSL2561_INTEGRATION_402MS,
|
||||
}
|
||||
GAINS = {
|
||||
'1X': 'sensor::TSL2561_GAIN_1X',
|
||||
'16X': 'sensor::TSL2561_GAIN_16X',
|
||||
'1X': sensor.sensor_ns.TSL2561_GAIN_1X,
|
||||
'16X': sensor.sensor_ns.TSL2561_GAIN_16X,
|
||||
}
|
||||
|
||||
CONF_IS_CS_PACKAGE = 'is_cs_package'
|
||||
@@ -28,31 +28,30 @@ def validate_integration_time(value):
|
||||
return value
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('tsl2561_sensor'): cv.register_variable_id,
|
||||
MakeTSL2561Sensor = Application.MakeTSL2561Sensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTSL2561Sensor),
|
||||
vol.Optional(CONF_ADDRESS, default=0x39): cv.i2c_address,
|
||||
vol.Optional(CONF_INTEGRATION_TIME): validate_integration_time,
|
||||
vol.Optional(CONF_GAIN): vol.All(vol.Upper, vol.Any(*GAINS)),
|
||||
vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*GAINS)),
|
||||
vol.Optional(CONF_IS_CS_PACKAGE): cv.boolean,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_tsl2561_sensor(config[CONF_NAME], config[CONF_ADDRESS],
|
||||
config.get(CONF_UPDATE_INTERVAL))
|
||||
make_tsl = variable('Application::MakeTSL2561Sensor', config[CONF_ID], rhs)
|
||||
make_tsl = variable(config[CONF_MAKE_ID], rhs)
|
||||
tsl2561 = make_tsl.Ptsl2561
|
||||
if CONF_INTEGRATION_TIME in config:
|
||||
constant = INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]
|
||||
add(tsl2561.set_integration_time(RawExpression(constant)))
|
||||
add(tsl2561.set_integration_time(INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]))
|
||||
if CONF_GAIN in config:
|
||||
constant = GAINS[config[CONF_GAIN]]
|
||||
add(tsl2561.set_gain(RawExpression(constant)))
|
||||
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, config)
|
||||
sensor.setup_mqtt_sensor_component(make_tsl.Pmqtt, config)
|
||||
sensor.setup_sensor(tsl2561, make_tsl.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TSL2561'
|
||||
|
||||
@@ -3,34 +3,39 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ECHO_PIN, CONF_ID, CONF_NAME, \
|
||||
CONF_TIMEOUT_METER, CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import App, add, variable, gpio_output_pin_expression, \
|
||||
gpio_input_pin_expression
|
||||
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
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('ultrasonic'): cv.register_variable_id,
|
||||
vol.Required(CONF_TRIGGER_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
|
||||
vol.Required(CONF_ECHO_PIN): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
|
||||
MakeUltrasonicSensor = Application.MakeUltrasonicSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
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.MQTT_SENSOR_SCHEMA.schema)
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
trigger = gpio_output_pin_expression(config[CONF_TRIGGER_PIN])
|
||||
echo = gpio_input_pin_expression(config[CONF_ECHO_PIN])
|
||||
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('Application::MakeUltrasonicSensor', config[CONF_ID], rhs)
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
ultrasonic = make.Pultrasonic
|
||||
if CONF_TIMEOUT_TIME in config:
|
||||
add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME]))
|
||||
elif CONF_TIMEOUT_METER in config:
|
||||
add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER]))
|
||||
sensor.setup_sensor(ultrasonic, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
sensor.setup_sensor(ultrasonic, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'
|
||||
|
||||
22
esphomeyaml/components/sensor/uptime.py
Normal file
22
esphomeyaml/components/sensor/uptime.py
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
MakeUptimeSensor = Application.MakeUptimeSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUptimeSensor),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_uptime_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.setup_sensor(make.Puptime, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_UPTIME_SENSOR'
|
||||
22
esphomeyaml/components/sensor/wifi_signal.py
Normal file
22
esphomeyaml/components/sensor/wifi_signal.py
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
MakeWiFiSignalSensor = Application.MakeWiFiSignalSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeWiFiSignalSensor),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
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)
|
||||
sensor.setup_sensor(make.Pwifi, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_WIFI_SIGNAL_SENSOR'
|
||||
47
esphomeyaml/components/sensor/xiaomi_miflora.py
Normal file
47
esphomeyaml/components/sensor/xiaomi_miflora.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_CONDUCTIVITY, CONF_ILLUMINANCE, \
|
||||
CONF_MAC_ADDRESS, CONF_MAKE_ID, CONF_MOISTURE, CONF_NAME, CONF_TEMPERATURE
|
||||
from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
XiaomiDevice = esphomelib_ns.XiaomiDevice
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(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(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_MOISTURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_CONDUCTIVITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
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_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)
|
||||
39
esphomeyaml/components/sensor/xiaomi_mijia.py
Normal file
39
esphomeyaml/components/sensor/xiaomi_mijia.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
|
||||
make_address_array
|
||||
from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_MAKE_ID, \
|
||||
CONF_NAME, CONF_TEMPERATURE
|
||||
from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
XiaomiDevice = esphomelib_ns.XiaomiDevice
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(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(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = None
|
||||
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)
|
||||
38
esphomeyaml/components/spi.py
Normal file
38
esphomeyaml/components/spi.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \
|
||||
gpio_output_pin_expression, add
|
||||
|
||||
SPIComponent = esphomelib_ns.SPIComponent
|
||||
|
||||
SPI_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))
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
clk = None
|
||||
for clk in gpio_output_pin_expression(conf[CONF_CLK_PIN]):
|
||||
yield
|
||||
rhs = App.init_spi(clk)
|
||||
spi = Pvariable(conf[CONF_ID], rhs)
|
||||
if CONF_MISO_PIN in conf:
|
||||
for miso in gpio_input_pin_expression(conf[CONF_MISO_PIN]):
|
||||
yield
|
||||
add(spi.set_miso(miso))
|
||||
if CONF_MOSI_PIN in conf:
|
||||
for mosi in gpio_input_pin_expression(conf[CONF_MOSI_PIN]):
|
||||
yield
|
||||
add(spi.set_mosi(mosi))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SPI'
|
||||
23
esphomeyaml/components/status_led.py
Normal file
23
esphomeyaml/components/status_led.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import config_validation as cv, pins
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_output_pin_expression
|
||||
|
||||
StatusLEDComponent = esphomelib_ns.StatusLEDComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(StatusLEDComponent),
|
||||
vol.Optional(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_status_led(pin)
|
||||
Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_STATUS_LED'
|
||||
@@ -1,39 +1,102 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ICON, CONF_INVERTED, CONF_MQTT_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, add, setup_mqtt_component
|
||||
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
|
||||
TemplateArguments, get_variable
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
MQTT_SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
switch_ns = esphomelib_ns.namespace('switch_')
|
||||
Switch = switch_ns.Switch
|
||||
MQTTSwitchComponent = switch_ns.MQTTSwitchComponent
|
||||
ToggleAction = switch_ns.ToggleAction
|
||||
TurnOffAction = switch_ns.TurnOffAction
|
||||
TurnOnAction = switch_ns.TurnOnAction
|
||||
|
||||
SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(Switch),
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent),
|
||||
vol.Optional(CONF_ICON): cv.icon,
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
})
|
||||
|
||||
MQTT_SWITCH_ID_SCHEMA = MQTT_SWITCH_SCHEMA.extend({
|
||||
cv.GenerateID('mqtt_switch', CONF_MQTT_ID): cv.register_variable_id,
|
||||
})
|
||||
SWITCH_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SWITCH_SCHEMA.schema)
|
||||
|
||||
|
||||
def setup_mqtt_switch(obj, config):
|
||||
setup_mqtt_component(obj, config)
|
||||
|
||||
|
||||
def setup_switch(obj, config):
|
||||
def setup_switch_core_(switch_var, mqtt_var, config):
|
||||
if CONF_INTERNAL in config:
|
||||
add(switch_var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_ICON in config:
|
||||
add(obj.set_icon(config[CONF_ICON]))
|
||||
add(switch_var.set_icon(config[CONF_ICON]))
|
||||
if CONF_INVERTED in config:
|
||||
add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
add(switch_var.set_inverted(config[CONF_INVERTED]))
|
||||
|
||||
setup_mqtt_component(mqtt_var, config)
|
||||
|
||||
|
||||
def setup_switch(switch_obj, mqtt_obj, config):
|
||||
switch_var = Pvariable(config[CONF_ID], switch_obj, has_side_effects=False)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
|
||||
setup_switch_core_(switch_var, mqtt_var, config)
|
||||
|
||||
|
||||
def register_switch(var, config):
|
||||
setup_switch(var, config)
|
||||
rhs = App.register_switch(var)
|
||||
mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_MQTT_ID], rhs)
|
||||
setup_mqtt_switch(mqtt_switch, config)
|
||||
switch_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
|
||||
rhs = App.register_switch(switch_var)
|
||||
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
|
||||
setup_switch_core_(switch_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SWITCH'
|
||||
|
||||
|
||||
CONF_SWITCH_TOGGLE = 'switch.toggle'
|
||||
SWITCH_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_SWITCH_TOGGLE, SWITCH_TOGGLE_ACTION_SCHEMA)
|
||||
def switch_toggle_to_code(config, action_id, arg_type):
|
||||
template_arg = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
})
|
||||
|
||||
|
||||
@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 = TemplateArguments(arg_type)
|
||||
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(None),
|
||||
})
|
||||
|
||||
|
||||
@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 = TemplateArguments(arg_type)
|
||||
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)
|
||||
|
||||
@@ -3,20 +3,29 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, variable, gpio_output_pin_expression
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_POWER_ON_VALUE
|
||||
from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable, add
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('gpio_switch'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
|
||||
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
|
||||
MakeGPIOSwitch = Application.MakeGPIOSwitch
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOSwitch),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
vol.Optional(CONF_POWER_ON_VALUE): cv.boolean,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_gpio_switch(config[CONF_NAME], gpio_output_pin_expression(config[CONF_PIN]))
|
||||
gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs)
|
||||
switch.setup_switch(gpio.Pswitch_, config)
|
||||
switch.setup_mqtt_switch(gpio.Pmqtt, config)
|
||||
pin = None
|
||||
for pin in gpio_output_pin_expression(config[CONF_PIN]):
|
||||
yield
|
||||
rhs = App.make_gpio_switch(config[CONF_NAME], pin)
|
||||
gpio = variable(config[CONF_MAKE_ID], rhs)
|
||||
|
||||
if CONF_POWER_ON_VALUE in config:
|
||||
add(gpio.Pswitch_.set_power_on_value(config[CONF_POWER_ON_VALUE]))
|
||||
|
||||
switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'
|
||||
|
||||
@@ -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 IR_TRANSMITTER_COMPONENT_CLASS
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \
|
||||
CONF_ID, 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, CONF_INVERTED
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import ArrayInitializer, HexIntLiteral, MockObj, Pvariable, 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({
|
||||
cv.GenerateID('ir_transmitter_switch'): cv.register_variable_id,
|
||||
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),
|
||||
})),
|
||||
vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id,
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("IR Transmitters do not support inverted mode!"),
|
||||
}).extend(switch.MQTT_SWITCH_ID_SCHEMA.schema), cv.has_at_least_one_key(*IR_KEYS))
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
SendData = MockObj('switch_::ir::SendData', '::')
|
||||
|
||||
|
||||
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 = MockObj(unicode(base), u'.')
|
||||
base = base.repeat(times, wait_us)
|
||||
return base
|
||||
|
||||
|
||||
def to_code(config):
|
||||
ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS)
|
||||
send_data = exp_send_data(config)
|
||||
rhs = ir.create_transmitter(config[CONF_NAME], send_data)
|
||||
switch_ = Pvariable(IR_TRANSMITTER_COMPONENT_CLASS + '::DataTransmitter', config[CONF_ID],
|
||||
rhs)
|
||||
switch.register_switch(switch_, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
|
||||
@@ -2,21 +2,24 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, Application, get_variable, variable
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('output_switch'): cv.register_variable_id,
|
||||
vol.Required(CONF_OUTPUT): cv.variable_id,
|
||||
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
|
||||
MakeOutputSwitch = Application.MakeOutputSwitch
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeOutputSwitch),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
rhs = App.make_simple_switch(config[CONF_NAME], output)
|
||||
gpio = variable('Application::MakeSimpleSwitch', config[CONF_ID], rhs)
|
||||
switch.setup_switch(gpio.Pswitch_, config)
|
||||
switch.setup_mqtt_switch(gpio.Pmqtt, config)
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_output_switch(config[CONF_NAME], output)
|
||||
gpio = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SIMPLE_SWITCH'
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT_SWITCH'
|
||||
|
||||
148
esphomeyaml/components/switch/remote_transmitter.py
Normal file
148
esphomeyaml/components/switch/remote_transmitter.py
Normal file
@@ -0,0 +1,148 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
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
|
||||
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.helpers import App, 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.RemoteTransmitter
|
||||
LGTransmitter = remote_ns.LGTransmitter
|
||||
NECTransmitter = remote_ns.NECTransmitter
|
||||
PanasonicTransmitter = remote_ns.PanasonicTransmitter
|
||||
RawTransmitter = remote_ns.RawTransmitter
|
||||
SamsungTransmitter = remote_ns.SamsungTransmitter
|
||||
SonyTransmitter = remote_ns.SonyTransmitter
|
||||
RCSwitchRawTransmitter = remote_ns.RCSwitchRawTransmitter
|
||||
RCSwitchTypeATransmitter = remote_ns.RCSwitchTypeATransmitter
|
||||
RCSwitchTypeBTransmitter = remote_ns.RCSwitchTypeBTransmitter
|
||||
RCSwitchTypeCTransmitter = remote_ns.RCSwitchTypeCTransmitter
|
||||
RCSwitchTypeDTransmitter = remote_ns.RCSwitchTypeDTransmitter
|
||||
|
||||
validate_raw_data = [vol.Any(vol.Coerce(int), cv.time_period_microseconds)]
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_LG): vol.Schema({
|
||||
vol.Required(CONF_DATA): cv.hex_uint32_t,
|
||||
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)),
|
||||
}),
|
||||
vol.Optional(CONF_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): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)),
|
||||
}),
|
||||
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])
|
||||
elif key == CONF_NEC:
|
||||
return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
elif key == CONF_PANASONIC:
|
||||
return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
|
||||
elif key == CONF_SAMSUNG:
|
||||
return SamsungTransmitter.new(name, config[CONF_DATA])
|
||||
elif key == CONF_SONY:
|
||||
return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
|
||||
elif 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)
|
||||
elif 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]))
|
||||
elif 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])
|
||||
elif 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])
|
||||
elif 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])
|
||||
elif 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])
|
||||
else:
|
||||
raise NotImplementedError("Unknown transmitter type {}".format(config))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
remote = None
|
||||
for remote in get_variable(config[CONF_REMOTE_TRANSMITTER_ID]):
|
||||
yield
|
||||
rhs = App.register_component(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'
|
||||
@@ -2,20 +2,21 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_INVERTED
|
||||
from esphomeyaml.helpers import App, variable
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('restart_switch'): cv.register_variable_id,
|
||||
MakeRestartSwitch = Application.MakeRestartSwitch
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
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.MQTT_SWITCH_SCHEMA.schema)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_restart_switch(config[CONF_NAME])
|
||||
restart = variable('Application::MakeRestartSwitch', config[CONF_ID], rhs)
|
||||
switch.setup_switch(restart.Prestart, config)
|
||||
switch.setup_mqtt_switch(restart.Pmqtt, config)
|
||||
restart = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch.setup_switch(restart.Prestart, restart.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'
|
||||
|
||||
@@ -2,21 +2,21 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_INVERTED
|
||||
from esphomeyaml.helpers import App, variable
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('shutdown_switch'): cv.register_variable_id,
|
||||
MakeShutdownSwitch = Application.MakeShutdownSwitch
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeShutdownSwitch),
|
||||
vol.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"),
|
||||
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_shutdown_switch(config[CONF_NAME])
|
||||
shutdown = variable('Application::MakeShutdownSwitch', config[CONF_ID],
|
||||
rhs)
|
||||
switch.setup_switch(shutdown.Pshutdown, config)
|
||||
switch.setup_mqtt_switch(shutdown.Pmqtt, config)
|
||||
shutdown = variable(config[CONF_MAKE_ID], rhs)
|
||||
switch.setup_switch(shutdown.Pshutdown, shutdown.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH'
|
||||
|
||||
44
esphomeyaml/components/switch/template.py
Normal file
44
esphomeyaml/components/switch/template.py
Normal file
@@ -0,0 +1,44 @@
|
||||
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
|
||||
|
||||
MakeTemplateSwitch = Application.MakeTemplateSwitch
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSwitch),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True),
|
||||
vol.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True),
|
||||
}), 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)
|
||||
|
||||
switch.setup_switch(make.Ptemplate_, 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_))
|
||||
if CONF_TURN_OFF_ACTION in config:
|
||||
automation.build_automation(make.Ptemplate_.get_turn_off_trigger(), NoArg,
|
||||
config[CONF_TURN_OFF_ACTION])
|
||||
if CONF_TURN_ON_ACTION in config:
|
||||
automation.build_automation(make.Ptemplate_.get_turn_on_trigger(), NoArg,
|
||||
config[CONF_TURN_ON_ACTION])
|
||||
if CONF_OPTIMISTIC in config:
|
||||
add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH'
|
||||
45
esphomeyaml/components/switch/uart.py
Normal file
45
esphomeyaml/components/switch/uart.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.components.uart import UARTComponent
|
||||
from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, CONF_UART_ID
|
||||
from esphomeyaml.core import HexInt
|
||||
from esphomeyaml.helpers import App, Application, ArrayInitializer, get_variable, variable
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
MakeUARTSwitch = Application.MakeUARTSwitch
|
||||
|
||||
|
||||
def validate_data(value):
|
||||
if isinstance(value, unicode):
|
||||
return value.encode('utf-8')
|
||||
elif isinstance(value, str):
|
||||
return value
|
||||
elif 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(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):
|
||||
uart = None
|
||||
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'
|
||||
60
esphomeyaml/components/text_sensor/__init__.py
Normal file
60
esphomeyaml/components/text_sensor/__init__.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import automation
|
||||
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.helpers import App, Pvariable, add, add_job, esphomelib_ns, setup_mqtt_component, \
|
||||
std_string
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
text_sensor_ns = esphomelib_ns.namespace('text_sensor')
|
||||
TextSensor = text_sensor_ns.TextSensor
|
||||
MQTTTextSensor = text_sensor_ns.MQTTTextSensor
|
||||
|
||||
TextSensorStateTrigger = text_sensor_ns.TextSensorStateTrigger
|
||||
|
||||
TEXT_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTTextSensor),
|
||||
cv.GenerateID(): cv.declare_variable_id(TextSensor),
|
||||
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)
|
||||
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)
|
||||
add_job(setup_text_sensor_core_, text_sensor_var, mqtt_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEXT_SENSOR'
|
||||
27
esphomeyaml/components/text_sensor/mqtt_subscribe.py
Normal file
27
esphomeyaml/components/text_sensor/mqtt_subscribe.py
Normal file
@@ -0,0 +1,27 @@
|
||||
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.helpers import App, Application, add, variable
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
MakeMQTTSubscribeTextSensor = Application.MakeMQTTSubscribeTextSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMQTTSubscribeTextSensor),
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_QOS): cv.mqtt_qos,
|
||||
}))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_mqtt_subscribe_text_sensor(config[CONF_NAME], config[CONF_TOPIC])
|
||||
make = variable(config[CONF_MAKE_ID], rhs)
|
||||
if CONF_QOS in config:
|
||||
add(make.Psensor.set_qos(config[CONF_QOS]))
|
||||
text_sensor.setup_text_sensor(make.Psensor, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MQTT_SUBSCRIBE_TEXT_SENSOR'
|
||||
30
esphomeyaml/components/text_sensor/template.py
Normal file
30
esphomeyaml/components/text_sensor/template.py
Normal file
@@ -0,0 +1,30 @@
|
||||
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.helpers import App, Application, add, optional, process_lambda, std_string, \
|
||||
variable
|
||||
|
||||
MakeTemplateTextSensor = Application.MakeTemplateTextSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateTextSensor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
|
||||
}))
|
||||
|
||||
|
||||
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)
|
||||
text_sensor.setup_text_sensor(make.Ptemplate_, make.Pmqtt, config)
|
||||
|
||||
template_ = None
|
||||
for template_ in process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=optional.template(std_string)):
|
||||
yield
|
||||
add(make.Ptemplate_.set_template(template_))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TEMPLATE_TEXT_SENSOR'
|
||||
19
esphomeyaml/components/text_sensor/version.py
Normal file
19
esphomeyaml/components/text_sensor/version.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from esphomeyaml.components import text_sensor
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Application, variable
|
||||
|
||||
MakeVersionTextSensor = Application.MakeVersionTextSensor
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeVersionTextSensor),
|
||||
}))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_VERSION_TEXT_SENSOR'
|
||||
276
esphomeyaml/components/time/__init__.py
Normal file
276
esphomeyaml/components/time/__init__.py
Normal file
@@ -0,0 +1,276 @@
|
||||
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.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
|
||||
time_ns = esphomelib_ns.namespace('time')
|
||||
CronTrigger = time_ns.CronTrigger
|
||||
|
||||
|
||||
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'
|
||||
elif offset_minute == 0 and offset_second == 0:
|
||||
return '{}'.format(offset_hour)
|
||||
elif 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 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 (pip2 install tzlocal)")
|
||||
try:
|
||||
tz = tzlocal.get_localzone()
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
_LOGGER.warning("Could not auto-detect timezone. Using UTC...")
|
||||
return 'UTC'
|
||||
|
||||
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("Auto-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("Auto-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 _parse_cron_int(value, special_mapping, message):
|
||||
special_mapping = special_mapping or {}
|
||||
if isinstance(value, (str, unicode)) 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 == '*' or part == '?':
|
||||
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)
|
||||
|
||||
|
||||
TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_TIMEZONE, default=detect_tz): cv.string,
|
||||
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)
|
||||
for second in conf.get(CONF_SECONDS, [x for x in range(0, 61)]):
|
||||
add(trigger.add_second(second))
|
||||
for minute in conf.get(CONF_MINUTES, [x for x in range(0, 60)]):
|
||||
add(trigger.add_minute(minute))
|
||||
for hour in conf.get(CONF_HOURS, [x for x in range(0, 24)]):
|
||||
add(trigger.add_hour(hour))
|
||||
for day_of_month in conf.get(CONF_DAYS_OF_MONTH, [x for x in range(1, 32)]):
|
||||
add(trigger.add_day_of_month(day_of_month))
|
||||
for month in conf.get(CONF_MONTHS, [x for x in range(1, 13)]):
|
||||
add(trigger.add_month(month))
|
||||
for day_of_week in conf.get(CONF_DAYS_OF_WEEK, [x for x in range(1, 8)]):
|
||||
add(trigger.add_day_of_week(day_of_week))
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
|
||||
def setup_time(time_var, config):
|
||||
add_job(setup_time_core_, time_var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_TIME'
|
||||
24
esphomeyaml/components/time/sntp.py
Normal file
24
esphomeyaml/components/time/sntp.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import time as time_
|
||||
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SERVERS
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
|
||||
SNTPComponent = time_.time_ns.SNTPComponent
|
||||
|
||||
PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(SNTPComponent),
|
||||
vol.Optional(CONF_SERVERS): vol.All(cv.ensure_list, [cv.string], vol.Length(max=3)),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_sntp_component(*config.get(CONF_SERVERS, []))
|
||||
sntp = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
time_.setup_time(sntp, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SNTP_COMPONENT'
|
||||
28
esphomeyaml/components/uart.py
Normal file
28
esphomeyaml/components/uart.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN
|
||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns
|
||||
|
||||
UARTComponent = esphomelib_ns.UARTComponent
|
||||
|
||||
SPI_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,
|
||||
}), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
|
||||
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
tx = conf.get(CONF_TX_PIN, -1)
|
||||
rx = conf.get(CONF_RX_PIN, -1)
|
||||
rhs = App.init_uart(tx, rx, conf[CONF_BAUD_RATE])
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_UART'
|
||||
@@ -5,21 +5,23 @@ 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
|
||||
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
WebServer = esphomelib_ns.WebServer
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('web_server'): cv.register_variable_id,
|
||||
cv.GenerateID(): cv.declare_variable_id(WebServer),
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
vol.Optional(CONF_CSS_URL): vol.Url,
|
||||
vol.Optional(CONF_JS_URL): vol.Url,
|
||||
vol.Optional(CONF_CSS_URL): cv.string,
|
||||
vol.Optional(CONF_JS_URL): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.init_web_server(config.get(CONF_PORT))
|
||||
web_server = Pvariable('WebServer', config[CONF_ID], rhs)
|
||||
web_server = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_CSS_URL in config:
|
||||
add(web_server.set_css_url(config[CONF_CSS_URL]))
|
||||
if CONF_JS_URL in config:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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_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, MockObj, Pvariable, StructInitializer, add
|
||||
import esphomeyaml.config_validation as cv
|
||||
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_POWER_SAVE_MODE,\
|
||||
CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, esphomelib_ns, global_ns
|
||||
|
||||
|
||||
def validate_password(value):
|
||||
@@ -19,6 +19,15 @@ def validate_password(value):
|
||||
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
|
||||
|
||||
|
||||
AP_MANUAL_IP_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_STATIC_IP): cv.ipv4,
|
||||
vol.Required(CONF_GATEWAY): cv.ipv4,
|
||||
@@ -30,33 +39,67 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({
|
||||
vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4,
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('wifi'): cv.register_variable_id,
|
||||
WIFI_NETWORK_BASE = vol.Schema({
|
||||
vol.Required(CONF_SSID): cv.ssid,
|
||||
vol.Optional(CONF_PASSWORD): validate_password,
|
||||
vol.Optional(CONF_CHANNEL): validate_channel,
|
||||
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
|
||||
})
|
||||
|
||||
WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend({
|
||||
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
|
||||
})
|
||||
|
||||
WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend({
|
||||
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
|
||||
})
|
||||
|
||||
|
||||
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 not in config and CONF_AP not in config:
|
||||
raise vol.Invalid("Please specify at least an SSID or an Access Point "
|
||||
"to create.")
|
||||
return config
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
IPAddress = global_ns.IPAddress
|
||||
ManualIP = esphomelib_ns.ManualIP
|
||||
WiFiComponent = esphomelib_ns.WiFiComponent
|
||||
WiFiAp = esphomelib_ns.WiFiAp
|
||||
|
||||
WIFI_POWER_SAVE_MODES = {
|
||||
'NONE': esphomelib_ns.WIFI_POWER_SAVE_NONE,
|
||||
'LIGHT': esphomelib_ns.WIFI_POWER_SAVE_LIGHT,
|
||||
'HIGH': esphomelib_ns.WIFI_POWER_SAVE_HIGH,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(WiFiComponent),
|
||||
vol.Optional(CONF_SSID): cv.ssid,
|
||||
vol.Optional(CONF_PASSWORD): validate_password,
|
||||
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
|
||||
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,
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
IPAddress = MockObj('IPAddress')
|
||||
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): vol.All(vol.Upper, cv.one_of(*WIFI_POWER_SAVE_MODES)),
|
||||
}), 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',
|
||||
ManualIP,
|
||||
('static_ip', safe_ip(config[CONF_STATIC_IP])),
|
||||
('gateway', safe_ip(config[CONF_GATEWAY])),
|
||||
('subnet', safe_ip(config[CONF_SUBNET])),
|
||||
@@ -65,31 +108,35 @@ def manual_ip(config):
|
||||
)
|
||||
|
||||
|
||||
def wifi_network(config):
|
||||
return StructInitializer(
|
||||
WiFiAp,
|
||||
('ssid', config.get(CONF_SSID, "")),
|
||||
('password', config.get(CONF_PASSWORD, "")),
|
||||
('channel', config.get(CONF_CHANNEL, -1)),
|
||||
('manual_ip', manual_ip(config.get(CONF_MANUAL_IP))),
|
||||
)
|
||||
|
||||
|
||||
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()
|
||||
wifi = Pvariable('WiFiComponent', config[CONF_ID], rhs)
|
||||
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])))
|
||||
if CONF_SSID in config:
|
||||
add(wifi.set_sta(wifi_network(config)))
|
||||
|
||||
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])))
|
||||
|
||||
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]]))
|
||||
|
||||
|
||||
def lib_deps(config):
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
|
||||
33
esphomeyaml/config.json
Normal file
33
esphomeyaml/config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "esphomeyaml",
|
||||
"version": "1.9.0b1",
|
||||
"slug": "esphomeyaml",
|
||||
"description": "esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices.",
|
||||
"url": "https://esphomelib.com/esphomeyaml/index.html",
|
||||
"startup": "application",
|
||||
"webui": "http://[HOST]:[PORT:6052]",
|
||||
"boot": "auto",
|
||||
"ports": {
|
||||
"6052/tcp": 6052,
|
||||
"6053/tcp": 6053
|
||||
},
|
||||
"auto_uart": true,
|
||||
"map": [
|
||||
"config:rw"
|
||||
],
|
||||
"arch": [
|
||||
"amd64",
|
||||
"armhf",
|
||||
"i386"
|
||||
],
|
||||
"environment": {
|
||||
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
|
||||
},
|
||||
"options": {
|
||||
"password": ""
|
||||
},
|
||||
"schema": {
|
||||
"password": "str?"
|
||||
},
|
||||
"image": "ottowinter/esphomeyaml-hassio-{arch}"
|
||||
}
|
||||
@@ -7,43 +7,21 @@ from collections import OrderedDict
|
||||
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 import core, yaml_util, core_config
|
||||
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, CONF_WIFI, ESP_PLATFORMS
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, add, color
|
||||
from esphomeyaml.helpers import color
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.5.3'
|
||||
|
||||
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, vol.Any(*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):
|
||||
if domain in _COMPONENT_CACHE:
|
||||
return _COMPONENT_CACHE[domain]
|
||||
@@ -51,7 +29,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
|
||||
@@ -93,6 +71,50 @@ class Config(OrderedDict):
|
||||
self.errors.append((message, domain, config))
|
||||
|
||||
|
||||
def iter_ids(config, prefix=None, parent=None):
|
||||
prefix = prefix or []
|
||||
parent = parent or {}
|
||||
if isinstance(config, core.ID):
|
||||
yield config, prefix, parent
|
||||
elif isinstance(config, core.Lambda):
|
||||
for id in config.requires_ids:
|
||||
yield id, prefix, parent
|
||||
elif isinstance(config, list):
|
||||
for i, item in enumerate(config):
|
||||
for result in iter_ids(item, prefix + [str(i)], config):
|
||||
yield result
|
||||
elif isinstance(config, dict):
|
||||
for key, value in config.iteritems():
|
||||
for result in iter_ids(value, prefix + [str(key)], config):
|
||||
yield result
|
||||
|
||||
|
||||
def do_id_pass(result):
|
||||
declare_ids = []
|
||||
searching_ids = []
|
||||
for id, prefix, config in iter_ids(result):
|
||||
if id.is_declaration:
|
||||
if id.id is not None and any(v[0].id == id.id for v in declare_ids):
|
||||
result.add_error("ID {} redefined!".format(id.id), '.'.join(prefix), config)
|
||||
continue
|
||||
declare_ids.append((id, prefix, config))
|
||||
else:
|
||||
searching_ids.append((id, prefix, config))
|
||||
# Resolve default ids after manual IDs
|
||||
for id, _, _ in declare_ids:
|
||||
id.resolve([v[0].id for v in declare_ids])
|
||||
|
||||
# Check searched IDs
|
||||
for id, prefix, config in searching_ids:
|
||||
if id.id is not None and not any(v[0].id == id.id for v in declare_ids):
|
||||
result.add_error("Couldn't find ID {}".format(id.id), '.'.join(prefix), config)
|
||||
if id.id is None and id.type is not None:
|
||||
id.id = next((v[0].id for v in declare_ids if v[0].type == id.type), None)
|
||||
if id.id is None:
|
||||
result.add_error("Couldn't resolve ID for type {}".format(id.type),
|
||||
'.'.join(prefix), config)
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
global _ALL_COMPONENTS
|
||||
|
||||
@@ -107,31 +129,63 @@ def validate_config(config):
|
||||
def _comp_error(ex, domain, config):
|
||||
result.add_error(_format_config_error(ex, domain, config), domain, config)
|
||||
|
||||
try:
|
||||
result[CONF_ESPHOMEYAML] = CORE_SCHEMA(config[CONF_ESPHOMEYAML])
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, CONF_ESPHOMEYAML, config)
|
||||
|
||||
# Step 1: Load everything
|
||||
for domain, conf in config.iteritems():
|
||||
if domain == CONF_ESPHOMEYAML:
|
||||
domain = str(domain)
|
||||
if domain == CONF_ESPHOMEYAML or domain.startswith('.'):
|
||||
continue
|
||||
if conf is None:
|
||||
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, conf)
|
||||
continue
|
||||
|
||||
if not hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
continue
|
||||
|
||||
for p_config in conf:
|
||||
if not isinstance(p_config, dict):
|
||||
result.add_error(u"Platform schemas must have 'platform:' key", )
|
||||
continue
|
||||
p_name = p_config.get(u'platform')
|
||||
if p_name is None:
|
||||
result.add_error(u"No platform specified for {}".format(domain))
|
||||
continue
|
||||
p_domain = u'{}.{}'.format(domain, p_name)
|
||||
platform = get_platform(domain, p_name)
|
||||
if platform is None:
|
||||
result.add_error(u"Platform not found: '{}'".format(p_domain), p_domain, p_config)
|
||||
continue
|
||||
|
||||
# Step 2: Validate configuration
|
||||
try:
|
||||
result[CONF_ESPHOMEYAML] = core_config.CONFIG_SCHEMA(config[CONF_ESPHOMEYAML])
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, CONF_ESPHOMEYAML, config[CONF_ESPHOMEYAML])
|
||||
|
||||
for domain, conf in config.iteritems():
|
||||
if domain == CONF_ESPHOMEYAML or domain.startswith('.'):
|
||||
continue
|
||||
if conf is None:
|
||||
conf = {}
|
||||
domain = str(domain)
|
||||
component = get_component(domain)
|
||||
if component is None:
|
||||
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))
|
||||
result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM),
|
||||
domain, conf)
|
||||
continue
|
||||
|
||||
success = True
|
||||
dependencies = getattr(component, 'DEPENDENCIES', [])
|
||||
for dependency in dependencies:
|
||||
if dependency not in _ALL_COMPONENTS:
|
||||
result.add_error(u"Component {} requires {}".format(domain, dependency))
|
||||
result.add_error(u"Component {} requires component {}".format(domain, dependency),
|
||||
domain, conf)
|
||||
success = False
|
||||
if not success:
|
||||
continue
|
||||
@@ -150,35 +204,43 @@ def validate_config(config):
|
||||
platforms = []
|
||||
for p_config in conf:
|
||||
if not isinstance(p_config, dict):
|
||||
result.add_error(u"Platform schemas must have 'platform:' key")
|
||||
continue
|
||||
p_name = p_config.get(u'platform')
|
||||
if p_name is None:
|
||||
result.add_error(u"No platform specified for {}".format(domain))
|
||||
continue
|
||||
p_domain = u'{}.{}'.format(domain, p_name)
|
||||
platform = get_platform(domain, p_name)
|
||||
if platform is None:
|
||||
result.add_error(u"Platform not found: {}.{}")
|
||||
continue
|
||||
|
||||
success = True
|
||||
dependencies = getattr(platform, 'DEPENDENCIES', [])
|
||||
for dependency in dependencies:
|
||||
if dependency not in _ALL_COMPONENTS:
|
||||
result.add_error(u"Platform {}.{} requires {}".format(domain, p_name,
|
||||
dependency))
|
||||
result.add_error(
|
||||
u"Platform {} requires component {}".format(p_domain, dependency),
|
||||
p_domain, p_config)
|
||||
success = False
|
||||
if not success:
|
||||
continue
|
||||
|
||||
esp_platforms = getattr(platform, 'ESP_PLATFORMS', ESP_PLATFORMS)
|
||||
if core.ESP_PLATFORM not in esp_platforms:
|
||||
result.add_error(
|
||||
u"Platform {} doesn't support {}.".format(p_domain, core.ESP_PLATFORM),
|
||||
p_domain, p_config)
|
||||
continue
|
||||
|
||||
if hasattr(platform, u'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, p_domain, p_config)
|
||||
continue
|
||||
platforms.append(p_validated)
|
||||
result[domain] = platforms
|
||||
|
||||
do_id_pass(result)
|
||||
return result
|
||||
|
||||
|
||||
@@ -211,24 +273,14 @@ def load_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]
|
||||
esp_platform = unicode(core_conf.get(CONF_PLATFORM, u""))
|
||||
esp_platform = esp_platform.upper()
|
||||
if '8266' in esp_platform:
|
||||
esp_platform = ESP_PLATFORM_ESP8266
|
||||
if '32' in esp_platform:
|
||||
esp_platform = ESP_PLATFORM_ESP32
|
||||
core.ESP_PLATFORM = esp_platform
|
||||
core.BOARD = unicode(core_conf.get(CONF_BOARD, u""))
|
||||
core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True))
|
||||
core_config.preload_core_config(config)
|
||||
|
||||
try:
|
||||
result = validate_config(config)
|
||||
except ESPHomeYAMLError:
|
||||
raise
|
||||
except Exception:
|
||||
print(u"Unexpected exception while reading configuration:")
|
||||
_LOGGER.error(u"Unexpected exception while reading configuration:")
|
||||
raise
|
||||
|
||||
return result
|
||||
@@ -255,38 +307,39 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
|
||||
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))
|
||||
safe_print(u"{} {}: {}".format(indent_str, key, line_info(value, **kwargs)))
|
||||
dump_dict(value, indent_count + 2)
|
||||
else:
|
||||
print(indent_str, key + ':', value)
|
||||
safe_print(u"{} {}: {}".format(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)
|
||||
safe_print(u" {} {}".format(indent_str, i))
|
||||
|
||||
|
||||
def read_config(path):
|
||||
_LOGGER.debug("Reading configuration...")
|
||||
_LOGGER.info("Reading configuration...")
|
||||
try:
|
||||
res = load_config(path)
|
||||
except ESPHomeYAMLError as err:
|
||||
_LOGGER.error(u"Error while reading config: %s", err)
|
||||
return None
|
||||
excepts = {}
|
||||
for err in res.errors:
|
||||
domain = err[1] or u"General Error"
|
||||
excepts.setdefault(domain, []).append(err[0])
|
||||
if err[2] is not None:
|
||||
excepts[domain].append(err[2])
|
||||
for message, domain, config in res.errors:
|
||||
domain = domain or u"General Error"
|
||||
excepts.setdefault(domain, []).append(message)
|
||||
if config is not None:
|
||||
excepts[domain].append(config)
|
||||
|
||||
if excepts:
|
||||
print(color('bold_white', u"Failed config"))
|
||||
safe_print(color('bold_white', u"Failed config"))
|
||||
for domain, config in excepts.iteritems():
|
||||
print(' ', color('bold_red', domain + ':'), color('red', '', reset='red'))
|
||||
safe_print(u' {} {}'.format(color('bold_red', domain + u':'),
|
||||
color('red', '', reset='red')))
|
||||
dump_dict(config, reset='red')
|
||||
print(color('reset'))
|
||||
safe_print(color('reset'))
|
||||
return None
|
||||
return dict(**res)
|
||||
return OrderedDict(res)
|
||||
|
||||
@@ -3,18 +3,19 @@
|
||||
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 import core, helpers
|
||||
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, TimePeriod, TimePeriodMilliseconds, \
|
||||
TimePeriodMicroseconds, TimePeriodSeconds
|
||||
from esphomeyaml.helpers import ensure_unique_string
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_INTERNAL
|
||||
from esphomeyaml.core import HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
||||
TimePeriodMilliseconds, TimePeriodSeconds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -26,7 +27,7 @@ zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
|
||||
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
|
||||
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
|
||||
|
||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
|
||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||
|
||||
RESERVED_IDS = [
|
||||
# C++ keywords http://en.cppreference.com/w/cpp/keyword
|
||||
@@ -57,8 +58,10 @@ def alphanumeric(value):
|
||||
|
||||
def valid_name(value):
|
||||
value = string_strict(value)
|
||||
if not all(c in ALLOWED_NAME_CHARS for c in value):
|
||||
raise vol.Invalid(u"Valid characters for name are {}".format(ALLOWED_NAME_CHARS))
|
||||
for c in value:
|
||||
if c not in ALLOWED_NAME_CHARS:
|
||||
raise vol.Invalid(u"'{}' is an invalid character for names. Valid characters are: {}"
|
||||
u"".format(c, ALLOWED_NAME_CHARS))
|
||||
return value
|
||||
|
||||
|
||||
@@ -100,13 +103,19 @@ def boolean(value):
|
||||
|
||||
def ensure_list(value):
|
||||
"""Wrap value in list if it is not one."""
|
||||
if value is None:
|
||||
if value is None or (isinstance(value, dict) and not value):
|
||||
return []
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return [value]
|
||||
|
||||
|
||||
def ensure_list_not_empty(value):
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return [value]
|
||||
|
||||
|
||||
def ensure_dict(value):
|
||||
if value is None:
|
||||
return {}
|
||||
@@ -134,16 +143,56 @@ def int_(value):
|
||||
|
||||
|
||||
hex_int = vol.Coerce(hex_int_)
|
||||
match_cpp_var_ = vol.Match(r'^[a-zA-Z_][a-zA-Z0-9_]+$', msg=u"Must be a valid C++ variable name")
|
||||
|
||||
|
||||
def variable_id(value):
|
||||
value = match_cpp_var_(value)
|
||||
def variable_id_str_(value):
|
||||
value = string(value)
|
||||
if not value:
|
||||
raise vol.Invalid("ID must not be empty")
|
||||
if value[0].isdigit():
|
||||
raise vol.Invalid("First character in ID cannot be a digit.")
|
||||
if '-' in value:
|
||||
raise vol.Invalid("Dashes are not supported in IDs, please use underscores instead.")
|
||||
for char in value:
|
||||
if char != '_' and not char.isalnum():
|
||||
raise vol.Invalid(u"IDs must only consist of upper/lowercase characters and numbers."
|
||||
u"The character '{}' cannot be used".format(char))
|
||||
if value in RESERVED_IDS:
|
||||
raise vol.Invalid(u"ID {} is reserved internally and cannot be used".format(value))
|
||||
return value
|
||||
|
||||
|
||||
def use_variable_id(type):
|
||||
def validator(value):
|
||||
if value is None:
|
||||
return core.ID(None, is_declaration=False, type=type)
|
||||
|
||||
return core.ID(variable_id_str_(value), is_declaration=False, type=type)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def declare_variable_id(type):
|
||||
def validator(value):
|
||||
if value is None:
|
||||
return core.ID(None, is_declaration=True, type=type)
|
||||
|
||||
return core.ID(variable_id_str_(value), is_declaration=True, type=type)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def templatable(other_validators):
|
||||
def validator(value):
|
||||
if isinstance(value, Lambda):
|
||||
return value
|
||||
if isinstance(other_validators, dict):
|
||||
return vol.Schema(other_validators)(value)
|
||||
return other_validators(value)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def only_on(platforms):
|
||||
if not isinstance(platforms, list):
|
||||
platforms = [platforms]
|
||||
@@ -170,10 +219,37 @@ def has_at_least_one_key(*keys):
|
||||
if not isinstance(obj, dict):
|
||||
raise vol.Invalid('expected dictionary')
|
||||
|
||||
for k in obj.keys():
|
||||
if k in keys:
|
||||
return obj
|
||||
raise vol.Invalid('must contain one of {}.'.format(', '.join(keys)))
|
||||
if not any(k in keys for k in obj):
|
||||
raise vol.Invalid('Must contain at least one of {}.'.format(', '.join(keys)))
|
||||
return obj
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
def has_exactly_one_key(*keys):
|
||||
def validate(obj):
|
||||
if not isinstance(obj, dict):
|
||||
raise vol.Invalid('expected dictionary')
|
||||
|
||||
number = sum(k in keys for k in obj)
|
||||
if number > 1:
|
||||
raise vol.Invalid("Cannot specify more than one of {}.".format(', '.join(keys)))
|
||||
if number < 1:
|
||||
raise vol.Invalid('Must contain exactly one of {}.'.format(', '.join(keys)))
|
||||
return obj
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -194,14 +270,6 @@ time_period_dict = vol.All(
|
||||
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]."""
|
||||
if isinstance(value, int):
|
||||
@@ -232,13 +300,6 @@ def time_period_str_unit(value):
|
||||
elif not isinstance(value, (str, unicode)):
|
||||
raise vol.Invalid("Expected string for time period with unit.")
|
||||
|
||||
try:
|
||||
float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise vol.Invalid(TIME_PERIOD_EXPLICIT_MESSAGE)
|
||||
|
||||
unit_to_kwarg = {
|
||||
'us': 'microseconds',
|
||||
'microseconds': 'microseconds',
|
||||
@@ -283,15 +344,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,
|
||||
@@ -299,20 +388,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):
|
||||
@@ -325,6 +440,18 @@ def hostname(value):
|
||||
return value
|
||||
|
||||
|
||||
def domain_name(value):
|
||||
value = string(value)
|
||||
if not value.startswith('.'):
|
||||
raise vol.Invalid("Domain name must start with .")
|
||||
if value.startswith('..'):
|
||||
raise vol.Invalid("Domain name must start with single .")
|
||||
for c in value:
|
||||
if not (c.isalnum() or c in '._-'):
|
||||
raise vol.Invalid("Domain name can only have alphanumeric characters and _ or -")
|
||||
return value
|
||||
|
||||
|
||||
def ssid(value):
|
||||
if value is None:
|
||||
raise vol.Invalid("SSID can not be None")
|
||||
@@ -410,6 +537,14 @@ def mqtt_payload(value):
|
||||
return string(value)
|
||||
|
||||
|
||||
def mqtt_qos(value):
|
||||
try:
|
||||
value = int(value)
|
||||
except (TypeError, ValueError):
|
||||
raise vol.Invalid(u"MQTT Quality of Service must be integer, got {}".format(value))
|
||||
return one_of(0, 1, 2)(value)
|
||||
|
||||
|
||||
uint8_t = vol.All(int_, vol.Range(min=0, max=255))
|
||||
uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
|
||||
uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295))
|
||||
@@ -419,9 +554,22 @@ hex_uint32_t = vol.All(hex_int, vol.Range(min=0, max=4294967295))
|
||||
i2c_address = hex_uint8_t
|
||||
|
||||
|
||||
def percentage(value):
|
||||
if isinstance(value, (str, unicode)) and value.endswith('%'):
|
||||
value = float(value[:-1].rstrip()) / 100.0
|
||||
return zero_to_one_float(value)
|
||||
|
||||
|
||||
def percentage_int(value):
|
||||
if isinstance(value, (str, unicode)) and value.endswith('%'):
|
||||
value = int(value[:-1].rstrip())
|
||||
return value
|
||||
|
||||
|
||||
def invalid(message):
|
||||
def validator(value):
|
||||
raise vol.Invalid(message)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
@@ -429,29 +577,87 @@ def valid(value):
|
||||
return value
|
||||
|
||||
|
||||
def one_of(*values):
|
||||
options = u', '.join(u"'{}'".format(x) for x in values)
|
||||
|
||||
def validator(value):
|
||||
if value not in values:
|
||||
raise vol.Invalid(u"Unknown value '{}', must be one of {}".format(value, options))
|
||||
return value
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def lambda_(value):
|
||||
if isinstance(value, Lambda):
|
||||
return value
|
||||
return Lambda(string_strict(value))
|
||||
|
||||
|
||||
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 = helpers.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 = helpers.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
|
||||
|
||||
|
||||
REGISTERED_IDS = set()
|
||||
|
||||
|
||||
def register_variable_id(value):
|
||||
s = variable_id(value)
|
||||
if s in REGISTERED_IDS:
|
||||
raise vol.Invalid("This ID has already been used")
|
||||
REGISTERED_IDS.add(s)
|
||||
return s
|
||||
|
||||
|
||||
class GenerateID(vol.Optional):
|
||||
def __init__(self, basename, key=CONF_ID):
|
||||
self._basename = basename
|
||||
super(GenerateID, self).__init__(key, default=self.default_variable_id)
|
||||
|
||||
def default_variable_id(self):
|
||||
return ensure_unique_string(self._basename, REGISTERED_IDS)
|
||||
def __init__(self, key=CONF_ID):
|
||||
super(GenerateID, self).__init__(key, default=lambda: None)
|
||||
|
||||
|
||||
REQUIRED_ID_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): register_variable_id,
|
||||
})
|
||||
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,
|
||||
@@ -464,11 +670,12 @@ MQTT_COMPONENT_AVAILABILITY_SCHEMA = vol.Schema({
|
||||
})
|
||||
|
||||
MQTT_COMPONENT_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): string,
|
||||
vol.Optional(CONF_NAME): string,
|
||||
vol.Optional(CONF_RETAIN): boolean,
|
||||
vol.Optional(CONF_DISCOVERY): boolean,
|
||||
vol.Optional(CONF_STATE_TOPIC): publish_topic,
|
||||
vol.Optional(CONF_AVAILABILITY): MQTT_COMPONENT_AVAILABILITY_SCHEMA,
|
||||
vol.Optional(CONF_AVAILABILITY): vol.Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA),
|
||||
vol.Optional(CONF_INTERNAL): boolean,
|
||||
})
|
||||
|
||||
MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"""Constants used by esphomeyaml."""
|
||||
|
||||
MAJOR_VERSION = 1
|
||||
MINOR_VERSION = 5
|
||||
PATCH_VERSION = '3'
|
||||
MINOR_VERSION = 9
|
||||
PATCH_VERSION = '0b1'
|
||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||
ESPHOMELIB_VERSION = '1.9.0b1'
|
||||
|
||||
ESP_PLATFORM_ESP32 = 'ESP32'
|
||||
ESP_PLATFORM_ESP8266 = 'ESP8266'
|
||||
@@ -16,9 +17,14 @@ 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'
|
||||
@@ -34,6 +40,13 @@ CONF_USERNAME = 'username'
|
||||
CONF_POWER_SUPPLY = 'power_supply'
|
||||
CONF_ID = 'id'
|
||||
CONF_MQTT_ID = 'mqtt_id'
|
||||
CONF_SENSOR_ID = 'sensor_id'
|
||||
CONF_TRIGGER_ID = 'trigger_id'
|
||||
CONF_ACTION_ID = 'action_id'
|
||||
CONF_CONDITION_ID = 'condition_id'
|
||||
CONF_MAKE_ID = 'make_id'
|
||||
CONF_AUTOMATION_ID = 'automation_id'
|
||||
CONF_DELAY = 'delay'
|
||||
CONF_PIN = 'pin'
|
||||
CONF_NUMBER = 'number'
|
||||
CONF_INVERTED = 'inverted'
|
||||
@@ -70,6 +83,15 @@ CONF_TOPIC = 'topic'
|
||||
CONF_PAYLOAD_AVAILABLE = 'payload_available'
|
||||
CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available'
|
||||
CONF_DEFAULT_TRANSITION_LENGTH = 'default_transition_length'
|
||||
CONF_TRANSITION_LENGTH = 'transition_length'
|
||||
CONF_FLASH_LENGTH = 'flash_length'
|
||||
CONF_BRIGHTNESS = 'brightness'
|
||||
CONF_EFFECT = 'effect'
|
||||
CONF_ABOVE = 'above'
|
||||
CONF_BELOW = 'below'
|
||||
CONF_ON = 'on'
|
||||
CONF_IF = 'if'
|
||||
CONF_THEN = 'then'
|
||||
CONF_BINARY = 'binary'
|
||||
CONF_WHITE = 'white'
|
||||
CONF_RGBW = 'rgbw'
|
||||
@@ -83,6 +105,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'
|
||||
@@ -112,6 +135,8 @@ CONF_LAMBDA = 'lambda'
|
||||
CONF_THROTTLE = 'throttle'
|
||||
CONF_DELTA = 'delta'
|
||||
CONF_OR = 'or'
|
||||
CONF_AND = 'and'
|
||||
CONF_RANGE = 'range'
|
||||
CONF_UNIQUE = 'unique'
|
||||
CONF_HEARTBEAT = 'heartbeat'
|
||||
CONF_DEBOUNCE = 'debounce'
|
||||
@@ -130,13 +155,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'
|
||||
@@ -146,6 +171,7 @@ CONF_OSCILLATION_OUTPUT = 'oscillation_output'
|
||||
CONF_SPEED = 'speed'
|
||||
CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic'
|
||||
CONF_OSCILLATION_COMMAND_TOPIC = 'oscillation_command_topic'
|
||||
CONF_OSCILLATING = 'oscillating'
|
||||
CONF_SPEED_STATE_TOPIC = 'speed_state_topic'
|
||||
CONF_SPEED_COMMAND_TOPIC = 'speed_command_topic'
|
||||
CONF_LOW = 'low'
|
||||
@@ -192,25 +218,140 @@ CONF_CLOCK_PIN = 'clock_pin'
|
||||
CONF_RGB_ORDER = 'rgb_order'
|
||||
CONF_ACCURACY = 'accuracy'
|
||||
CONF_BOARD_FLASH_MODE = 'board_flash_mode'
|
||||
CONF_ON_PRESS = 'on_press'
|
||||
CONF_ON_RELEASE = 'on_release'
|
||||
CONF_ON_CLICK = 'on_click'
|
||||
CONF_ON_DOUBLE_CLICK = 'on_double_click'
|
||||
CONF_MIN_LENGTH = 'min_length'
|
||||
CONF_MAX_LENGTH = 'max_length'
|
||||
CONF_ON_VALUE = 'on_value'
|
||||
CONF_ON_RAW_VALUE = 'on_raw_value'
|
||||
CONF_ON_VALUE_RANGE = 'on_value_range'
|
||||
CONF_ON_MESSAGE = 'on_message'
|
||||
CONF_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'
|
||||
CONF_CLOSE_ACTION = 'close_action'
|
||||
CONF_STOP_ACTION = 'stop_action'
|
||||
CONF_DOMAIN = 'domain'
|
||||
CONF_OPTIMISTIC = 'optimistic'
|
||||
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_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'
|
||||
|
||||
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
|
||||
}
|
||||
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'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import math
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
@@ -9,6 +10,8 @@ class ESPHomeYAMLError(Exception):
|
||||
|
||||
class HexInt(long):
|
||||
def __str__(self):
|
||||
if 0 <= self <= 255:
|
||||
return "0x{:02X}".format(self)
|
||||
return "0x{:X}".format(self)
|
||||
|
||||
|
||||
@@ -31,6 +34,12 @@ class MACAddress(object):
|
||||
def __str__(self):
|
||||
return ':'.join('{:02X}'.format(part) for part in self.parts)
|
||||
|
||||
def as_hex(self):
|
||||
import esphomeyaml.helpers
|
||||
|
||||
num = ''.join('{:02X}'.format(part) for part in self.parts)
|
||||
return esphomeyaml.helpers.RawExpression('0x{}ULL'.format(num))
|
||||
|
||||
|
||||
def is_approximately_integer(value):
|
||||
if isinstance(value, (int, long)):
|
||||
@@ -104,6 +113,21 @@ class TimePeriod(object):
|
||||
out['days'] = self.days
|
||||
return out
|
||||
|
||||
def __str__(self):
|
||||
if self.microseconds is not None:
|
||||
return '{} us'.format(self.total_microseconds)
|
||||
if self.milliseconds is not None:
|
||||
return '{} ms'.format(self.total_milliseconds)
|
||||
if self.seconds is not None:
|
||||
return '{} s'.format(self.total_seconds)
|
||||
if self.minutes is not None:
|
||||
return '{} min'.format(self.total_minutes)
|
||||
if self.hours is not None:
|
||||
return '{} h'.format(self.total_hours)
|
||||
if self.days is not None:
|
||||
return '{} d'.format(self.total_days)
|
||||
return '0'
|
||||
|
||||
@property
|
||||
def total_microseconds(self):
|
||||
return self.total_milliseconds * 1000 + (self.microseconds or 0)
|
||||
@@ -171,8 +195,66 @@ class TimePeriodSeconds(TimePeriod):
|
||||
pass
|
||||
|
||||
|
||||
class Lambda(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)', value)
|
||||
self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)]
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return u'Lambda<{}>'.format(self.value)
|
||||
|
||||
|
||||
def ensure_unique_string(preferred_string, current_strings):
|
||||
test_string = preferred_string
|
||||
current_strings_set = set(current_strings)
|
||||
|
||||
tries = 1
|
||||
|
||||
while test_string in current_strings_set:
|
||||
tries += 1
|
||||
test_string = u"{}_{}".format(preferred_string, tries)
|
||||
|
||||
return test_string
|
||||
|
||||
|
||||
class ID(object):
|
||||
def __init__(self, id, is_declaration=False, type=None):
|
||||
self.id = id
|
||||
self.is_manual = id is not None
|
||||
self.is_declaration = is_declaration
|
||||
self.type = type
|
||||
|
||||
def resolve(self, registered_ids):
|
||||
if self.id is None:
|
||||
base = str(self.type).replace('::', '_').lower()
|
||||
name = ''.join(c for c in base if c.isalnum() or c == '_')
|
||||
self.id = ensure_unique_string(name, registered_ids)
|
||||
return self.id
|
||||
|
||||
def __str__(self):
|
||||
if self.id is None:
|
||||
return ''
|
||||
return self.id
|
||||
|
||||
def __repr__(self):
|
||||
return u'ID<{} declaration={}, type={}, manual={}>'.format(
|
||||
self.id, self.is_declaration, self.type, self.is_manual)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, ID):
|
||||
raise ValueError("other must be ID")
|
||||
return self.id == other.id
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.id)
|
||||
|
||||
|
||||
CONFIG_PATH = None
|
||||
SIMPLIFY = True
|
||||
ESP_PLATFORM = ''
|
||||
BOARD = ''
|
||||
RAW_CONFIG = None
|
||||
NAME = ''
|
||||
|
||||
258
esphomeyaml/core_config.py
Normal file
258
esphomeyaml/core_config.py
Normal file
@@ -0,0 +1,258 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import automation, core, pins
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266_DEV, \
|
||||
CONF_ARDUINO_VERSION, CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_BRANCH, CONF_BUILD_PATH, \
|
||||
CONF_COMMIT, CONF_ESPHOMELIB_VERSION, CONF_ESPHOMEYAML, CONF_LOCAL, CONF_NAME, CONF_ON_BOOT, \
|
||||
CONF_ON_LOOP, CONF_ON_SHUTDOWN, CONF_PLATFORM, CONF_PRIORITY, CONF_REPOSITORY, CONF_TAG, \
|
||||
CONF_TRIGGER_ID, CONF_USE_CUSTOM_CODE, ESPHOMELIB_VERSION, ESP_PLATFORM_ESP32, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, NoArg, Pvariable, RawExpression, add, const_char_p, \
|
||||
esphomelib_ns, relative_path
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
LIBRARY_URI_REPO = u'https://github.com/OttoWinter/esphomelib.git'
|
||||
|
||||
BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout']
|
||||
StartupTrigger = esphomelib_ns.StartupTrigger
|
||||
ShutdownTrigger = esphomelib_ns.ShutdownTrigger
|
||||
LoopTrigger = esphomelib_ns.LoopTrigger
|
||||
|
||||
VERSION_REGEX = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+(?:-beta)?(?:-alpha)?$')
|
||||
|
||||
|
||||
def validate_board(value):
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
board_pins = pins.ESP8266_BOARD_PINS
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
board_pins = pins.ESP32_BOARD_PINS
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
if value not in board_pins:
|
||||
raise vol.Invalid(u"Could not find board '{}'. Valid boards are {}".format(
|
||||
value, u', '.join(pins.ESP8266_BOARD_PINS.keys())))
|
||||
return value
|
||||
|
||||
|
||||
def validate_simple_esphomelib_version(value):
|
||||
value = cv.string_strict(value)
|
||||
if value.upper() == 'LATEST':
|
||||
return {
|
||||
CONF_REPOSITORY: LIBRARY_URI_REPO,
|
||||
CONF_TAG: 'v' + ESPHOMELIB_VERSION,
|
||||
}
|
||||
elif value.upper() == 'DEV':
|
||||
return {
|
||||
CONF_REPOSITORY: LIBRARY_URI_REPO,
|
||||
CONF_BRANCH: 'master'
|
||||
}
|
||||
elif VERSION_REGEX.match(value) is not None:
|
||||
return {
|
||||
CONF_REPOSITORY: LIBRARY_URI_REPO,
|
||||
CONF_TAG: 'v' + value,
|
||||
}
|
||||
raise vol.Invalid("Only simple esphomelib versions!")
|
||||
|
||||
|
||||
def validate_local_esphomelib_version(value):
|
||||
value = cv.directory(value)
|
||||
path = relative_path(value)
|
||||
library_json = os.path.join(path, 'library.json')
|
||||
if not os.path.exists(library_json):
|
||||
raise vol.Invalid(u"Could not find '{}' file. '{}' does not seem to point to an "
|
||||
u"esphomelib copy.".format(library_json, value))
|
||||
return value
|
||||
|
||||
|
||||
def validate_commit(value):
|
||||
value = cv.string(value)
|
||||
if re.match(r"^[0-9a-f]{7,}$", value) is None:
|
||||
raise vol.Invalid("Commit option only accepts commit hashes in hex format.")
|
||||
return value
|
||||
|
||||
|
||||
ESPHOMELIB_VERSION_SCHEMA = vol.Any(
|
||||
validate_simple_esphomelib_version,
|
||||
vol.Schema({
|
||||
vol.Required(CONF_LOCAL): validate_local_esphomelib_version,
|
||||
}),
|
||||
vol.All(
|
||||
vol.Schema({
|
||||
vol.Optional(CONF_REPOSITORY, default=LIBRARY_URI_REPO): cv.string,
|
||||
vol.Optional(CONF_COMMIT): validate_commit,
|
||||
vol.Optional(CONF_BRANCH): cv.string,
|
||||
vol.Optional(CONF_TAG): cv.string,
|
||||
}),
|
||||
cv.has_at_most_one_key(CONF_COMMIT, CONF_BRANCH, CONF_TAG)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def validate_platform(value):
|
||||
value = cv.string(value)
|
||||
if value.upper() in ('ESP8266', 'ESPRESSIF8266'):
|
||||
return ESP_PLATFORM_ESP8266
|
||||
if value.upper() in ('ESP32', 'ESPRESSIF32'):
|
||||
return ESP_PLATFORM_ESP32
|
||||
raise vol.Invalid(u"Invalid platform '{}'. Only options are ESP8266 and ESP32. Please note "
|
||||
u"the old way to use the latest arduino framework version has been split up "
|
||||
u"into the arduino_version configuration option.".format(value))
|
||||
|
||||
|
||||
PLATFORMIO_ESP8266_LUT = {
|
||||
'2.4.2': 'espressif8266@1.8.0',
|
||||
'2.4.1': 'espressif8266@1.7.3',
|
||||
'2.4.0': 'espressif8266@1.6.0',
|
||||
'2.3.0': 'espressif8266@1.5.0',
|
||||
'RECOMMENDED': 'espressif8266@>=1.8.0',
|
||||
'LATEST': 'espressif8266',
|
||||
'DEV': ARDUINO_VERSION_ESP8266_DEV,
|
||||
}
|
||||
|
||||
PLATFORMIO_ESP32_LUT = {
|
||||
'1.0.0': 'espressif32@1.4.0',
|
||||
'RECOMMENDED': 'espressif32@>=1.4.0',
|
||||
'LATEST': 'espressif32',
|
||||
'DEV': ARDUINO_VERSION_ESP32_DEV,
|
||||
}
|
||||
|
||||
|
||||
def validate_arduino_version(value):
|
||||
value = cv.string_strict(value)
|
||||
value_ = value.upper()
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP8266_LUT:
|
||||
raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported "
|
||||
"at this time. You can override this by manually using "
|
||||
"espressif8266@<platformio version>")
|
||||
if value_ in PLATFORMIO_ESP8266_LUT:
|
||||
return PLATFORMIO_ESP8266_LUT[value_]
|
||||
return value
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT:
|
||||
raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported "
|
||||
"at this time. You can override this by manually using "
|
||||
"espressif32@<platformio version>")
|
||||
if value_ in PLATFORMIO_ESP32_LUT:
|
||||
return PLATFORMIO_ESP32_LUT[value_]
|
||||
return value
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def default_build_path():
|
||||
return core.NAME
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.valid_name,
|
||||
vol.Required(CONF_PLATFORM): vol.All(vol.Upper, cv.one_of('ESP8266', 'ESPRESSIF8266',
|
||||
'ESP32', 'ESPRESSIF32')),
|
||||
vol.Required(CONF_BOARD): validate_board,
|
||||
vol.Optional(CONF_ESPHOMELIB_VERSION, default='latest'): ESPHOMELIB_VERSION_SCHEMA,
|
||||
vol.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version,
|
||||
vol.Optional(CONF_USE_CUSTOM_CODE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string,
|
||||
|
||||
vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, cv.one_of(*BUILD_FLASH_MODES)),
|
||||
vol.Optional(CONF_ON_BOOT): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StartupTrigger),
|
||||
vol.Optional(CONF_PRIORITY): vol.Coerce(float),
|
||||
}),
|
||||
vol.Optional(CONF_ON_SHUTDOWN): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ShutdownTrigger),
|
||||
}),
|
||||
vol.Optional(CONF_ON_LOOP): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(LoopTrigger),
|
||||
}),
|
||||
|
||||
vol.Optional('library_uri'): cv.invalid("The library_uri option has been removed in 1.8.0 and "
|
||||
"was moved into the esphomelib_version option."),
|
||||
vol.Optional('use_build_flags'): cv.invalid("The use_build_flags option has been replaced by "
|
||||
"use_custom_code option in 1.8.0."),
|
||||
})
|
||||
|
||||
|
||||
def preload_core_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.")
|
||||
if CONF_BOARD not in core_conf:
|
||||
raise ESPHomeYAMLError("esphomeyaml.board not specified.")
|
||||
if CONF_NAME not in core_conf:
|
||||
raise ESPHomeYAMLError("esphomeyaml.name not specified.")
|
||||
|
||||
try:
|
||||
core.ESP_PLATFORM = validate_platform(core_conf[CONF_PLATFORM])
|
||||
core.BOARD = validate_board(core_conf[CONF_BOARD])
|
||||
core.NAME = cv.valid_name(core_conf[CONF_NAME])
|
||||
except vol.Invalid as e:
|
||||
raise ESPHomeYAMLError(unicode(e))
|
||||
|
||||
|
||||
def run_command(*args):
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
rc = p.returncode
|
||||
return rc, stdout, stderr
|
||||
|
||||
|
||||
def update_esphomelib_repo(config):
|
||||
esphomelib_version = config[CONF_ESPHOMELIB_VERSION]
|
||||
if CONF_REPOSITORY not in esphomelib_version:
|
||||
return
|
||||
|
||||
build_path = relative_path(config[CONF_BUILD_PATH])
|
||||
esphomelib_path = os.path.join(build_path, '.piolibdeps', 'esphomelib')
|
||||
is_default_branch = all(x not in esphomelib_version
|
||||
for x in (CONF_BRANCH, CONF_TAG, CONF_COMMIT))
|
||||
if not (CONF_BRANCH in esphomelib_version or is_default_branch):
|
||||
# Git commit hash or tag cannot be updated
|
||||
return
|
||||
|
||||
rc, _, _ = run_command('git', '-C', esphomelib_path, '--help')
|
||||
if rc != 0:
|
||||
# git not installed or repo not downloaded yet
|
||||
return
|
||||
rc, _, _ = run_command('git', '-C', esphomelib_path, 'diff-index', '--quiet', 'HEAD', '--')
|
||||
if rc != 0:
|
||||
# local changes, cannot update
|
||||
_LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.")
|
||||
return
|
||||
rc, _, _ = run_command('git', '-C', esphomelib_path, 'pull')
|
||||
if rc != 0:
|
||||
_LOGGER.warn("Couldn't auto-update local git copy of esphomelib.")
|
||||
return
|
||||
|
||||
|
||||
def to_code(config):
|
||||
add(App.set_name(config[CONF_NAME]))
|
||||
|
||||
for conf in config.get(CONF_ON_BOOT, []):
|
||||
rhs = App.register_component(StartupTrigger.new(conf.get(CONF_PRIORITY)))
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
for conf in config.get(CONF_ON_SHUTDOWN, []):
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], ShutdownTrigger.new())
|
||||
automation.build_automation(trigger, const_char_p, conf)
|
||||
|
||||
for conf in config.get(CONF_ON_LOOP, []):
|
||||
rhs = App.register_component(LoopTrigger.new())
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, NoArg, conf)
|
||||
|
||||
update_esphomelib_repo(config)
|
||||
|
||||
add(App.set_compilation_datetime(RawExpression('__DATE__ ", " __TIME__')))
|
||||
0
esphomeyaml/dashboard/__init__.py
Normal file
0
esphomeyaml/dashboard/__init__.py
Normal file
279
esphomeyaml/dashboard/dashboard.py
Normal file
279
esphomeyaml/dashboard/dashboard.py
Normal file
@@ -0,0 +1,279 @@
|
||||
# pylint: disable=wrong-import-position
|
||||
from __future__ import print_function
|
||||
|
||||
import codecs
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import subprocess
|
||||
|
||||
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_BUILD_PATH
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml import const, core, __main__
|
||||
from esphomeyaml.__main__ import get_serial_ports
|
||||
from esphomeyaml.helpers import quote, relative_path
|
||||
|
||||
try:
|
||||
import tornado
|
||||
import tornado.gen
|
||||
import tornado.ioloop
|
||||
import tornado.iostream
|
||||
import tornado.process
|
||||
import tornado.web
|
||||
import tornado.websocket
|
||||
import tornado.concurrent
|
||||
except ImportError as err:
|
||||
tornado = None
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CONFIG_DIR = ''
|
||||
PASSWORD = ''
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
class BaseHandler(tornado.web.RequestHandler):
|
||||
def is_authenticated(self):
|
||||
return not PASSWORD or self.get_secure_cookie('authenticated') == 'yes'
|
||||
|
||||
|
||||
# pylint: disable=abstract-method, arguments-differ
|
||||
class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler):
|
||||
def __init__(self, application, request, **kwargs):
|
||||
super(EsphomeyamlCommandWebSocket, self).__init__(application, request, **kwargs)
|
||||
self.proc = None
|
||||
self.closed = False
|
||||
|
||||
def on_message(self, message):
|
||||
if PASSWORD and self.get_secure_cookie('authenticated') != 'yes':
|
||||
return
|
||||
if self.proc is not None:
|
||||
return
|
||||
command = self.build_command(message)
|
||||
_LOGGER.debug(u"WebSocket opened for command %s", [quote(x) for x in command])
|
||||
self.proc = tornado.process.Subprocess(command,
|
||||
stdout=tornado.process.Subprocess.STREAM,
|
||||
stderr=subprocess.STDOUT)
|
||||
self.proc.set_exit_callback(self.proc_on_exit)
|
||||
tornado.ioloop.IOLoop.current().spawn_callback(self.redirect_stream)
|
||||
|
||||
@tornado.gen.coroutine
|
||||
def redirect_stream(self):
|
||||
while True:
|
||||
try:
|
||||
data = yield self.proc.stdout.read_until_regex('[\n\r]')
|
||||
except tornado.iostream.StreamClosedError:
|
||||
break
|
||||
if data.endswith('\r') and random.randrange(100) < 90:
|
||||
continue
|
||||
try:
|
||||
data = data.replace('\033', '\\033')
|
||||
except UnicodeDecodeError:
|
||||
data = data.encode('ascii', 'backslashreplace')
|
||||
self.write_message({'event': 'line', 'data': data})
|
||||
|
||||
def proc_on_exit(self, returncode):
|
||||
if not self.closed:
|
||||
_LOGGER.debug("Process exited with return code %s", returncode)
|
||||
self.write_message({'event': 'exit', 'code': returncode})
|
||||
|
||||
def on_close(self):
|
||||
self.closed = True
|
||||
if self.proc is not None and self.proc.returncode is None:
|
||||
_LOGGER.debug("Terminating process")
|
||||
self.proc.proc.terminate()
|
||||
|
||||
def build_command(self, message):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = CONFIG_DIR + '/' + js['configuration']
|
||||
return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape']
|
||||
|
||||
|
||||
class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "run", '--upload-port', js["port"],
|
||||
'--escape', '--use-esptoolpy']
|
||||
|
||||
|
||||
class EsphomeyamlCompileHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "compile"]
|
||||
|
||||
|
||||
class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "config"]
|
||||
|
||||
|
||||
class EsphomeyamlCleanMqttHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "clean-mqtt"]
|
||||
|
||||
|
||||
class EsphomeyamlCleanHandler(EsphomeyamlCommandWebSocket):
|
||||
def build_command(self, message):
|
||||
js = json.loads(message)
|
||||
config_file = os.path.join(CONFIG_DIR, js['configuration'])
|
||||
return ["esphomeyaml", config_file, "clean"]
|
||||
|
||||
|
||||
class SerialPortRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
return
|
||||
ports = get_serial_ports()
|
||||
data = []
|
||||
for port, desc in ports:
|
||||
if port == '/dev/ttyAMA0':
|
||||
desc = 'UART pins on GPIO header'
|
||||
split_desc = desc.split(' - ')
|
||||
if len(split_desc) == 2 and split_desc[0] == split_desc[1]:
|
||||
# Some serial ports repeat their values
|
||||
desc = split_desc[0]
|
||||
data.append({'port': port, 'desc': desc})
|
||||
data.append({'port': 'OTA', 'desc': 'Over-The-Air'})
|
||||
self.write(json.dumps(sorted(data, reverse=True)))
|
||||
|
||||
|
||||
class WizardRequestHandler(BaseHandler):
|
||||
def post(self):
|
||||
from esphomeyaml import wizard
|
||||
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
return
|
||||
kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()}
|
||||
config = wizard.wizard_file(**kwargs)
|
||||
destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
|
||||
with codecs.open(destination, 'w') as f_handle:
|
||||
f_handle.write(config)
|
||||
|
||||
self.redirect('/?begin=True')
|
||||
|
||||
|
||||
class DownloadBinaryRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
return
|
||||
|
||||
configuration = self.get_argument('configuration')
|
||||
config_file = os.path.join(CONFIG_DIR, configuration)
|
||||
core.CONFIG_PATH = config_file
|
||||
config = __main__.read_config(core.CONFIG_PATH)
|
||||
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
|
||||
path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
|
||||
self.set_header('Content-Type', 'application/octet-stream')
|
||||
self.set_header("Content-Disposition", 'attachment; filename="{}.bin"'.format(core.NAME))
|
||||
with open(path, 'rb') as f:
|
||||
while 1:
|
||||
data = f.read(16384) # or some other nice-sized chunk
|
||||
if not data:
|
||||
break
|
||||
self.write(data)
|
||||
self.finish()
|
||||
|
||||
|
||||
class MainRequestHandler(BaseHandler):
|
||||
def get(self):
|
||||
if not self.is_authenticated():
|
||||
self.redirect('/login')
|
||||
return
|
||||
|
||||
begin = bool(self.get_argument('begin', False))
|
||||
files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml') and
|
||||
not f.startswith('.')])
|
||||
full_path_files = [os.path.join(CONFIG_DIR, f) for f in files]
|
||||
self.render("templates/index.html", files=files, full_path_files=full_path_files,
|
||||
version=const.__version__, begin=begin)
|
||||
|
||||
|
||||
class LoginHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write('<html><body><form action="/login" method="post">'
|
||||
'Password: <input type="password" name="password">'
|
||||
'<input type="submit" value="Sign in">'
|
||||
'</form></body></html>')
|
||||
|
||||
def post(self):
|
||||
password = str(self.get_argument("password", ''))
|
||||
password = hmac.new(password).digest()
|
||||
if hmac.compare_digest(PASSWORD, password):
|
||||
self.set_secure_cookie("authenticated", "yes")
|
||||
self.redirect("/")
|
||||
|
||||
|
||||
def make_app(debug=False):
|
||||
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
||||
return tornado.web.Application([
|
||||
(r"/", MainRequestHandler),
|
||||
(r"/login", LoginHandler),
|
||||
(r"/logs", EsphomeyamlLogsHandler),
|
||||
(r"/run", EsphomeyamlRunHandler),
|
||||
(r"/compile", EsphomeyamlCompileHandler),
|
||||
(r"/validate", EsphomeyamlValidateHandler),
|
||||
(r"/clean-mqtt", EsphomeyamlCleanMqttHandler),
|
||||
(r"/clean", EsphomeyamlCleanHandler),
|
||||
(r"/download.bin", DownloadBinaryRequestHandler),
|
||||
(r"/serial-ports", SerialPortRequestHandler),
|
||||
(r"/wizard.html", WizardRequestHandler),
|
||||
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}),
|
||||
], debug=debug, cookie_secret=PASSWORD)
|
||||
|
||||
|
||||
def start_web_server(args):
|
||||
global CONFIG_DIR
|
||||
global PASSWORD
|
||||
|
||||
if tornado is None:
|
||||
raise ESPHomeYAMLError("Attempted to load dashboard, but tornado is not installed! "
|
||||
"Please run \"pip2 install tornado esptool\" in your terminal.")
|
||||
|
||||
CONFIG_DIR = args.configuration
|
||||
if not os.path.exists(CONFIG_DIR):
|
||||
os.makedirs(CONFIG_DIR)
|
||||
|
||||
# HassIO options storage
|
||||
PASSWORD = args.password
|
||||
if os.path.isfile('/data/options.json'):
|
||||
with open('/data/options.json') as f:
|
||||
js = json.load(f)
|
||||
PASSWORD = js.get('password') or PASSWORD
|
||||
|
||||
if PASSWORD:
|
||||
PASSWORD = hmac.new(str(PASSWORD)).digest()
|
||||
# Use the digest of the password as our cookie secret. This makes sure the cookie
|
||||
# isn't too short. It, of course, enables local hash brute forcing (because the cookie
|
||||
# secret can be brute forced without making requests). But the hashing algorithm used
|
||||
# by tornado is apparently strong enough to make brute forcing even a short string pretty
|
||||
# hard.
|
||||
|
||||
_LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...",
|
||||
args.port, CONFIG_DIR)
|
||||
app = make_app(args.verbose)
|
||||
app.listen(args.port)
|
||||
|
||||
if args.open_ui:
|
||||
import webbrowser
|
||||
|
||||
webbrowser.open('localhost:{}'.format(args.port))
|
||||
|
||||
try:
|
||||
tornado.ioloop.IOLoop.current().start()
|
||||
except KeyboardInterrupt:
|
||||
_LOGGER.info("Shutting down...")
|
||||
5
esphomeyaml/dashboard/static/materialize-stepper.min.css
vendored
Executable file
5
esphomeyaml/dashboard/static/materialize-stepper.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
5
esphomeyaml/dashboard/static/materialize-stepper.min.js
vendored
Executable file
5
esphomeyaml/dashboard/static/materialize-stepper.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
962
esphomeyaml/dashboard/templates/index.html
Normal file
962
esphomeyaml/dashboard/templates/index.html
Normal file
@@ -0,0 +1,962 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>esphomeyaml Dashboard</title>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
|
||||
|
||||
<link rel="stylesheet" href="/static/materialize-stepper.min.css">
|
||||
|
||||
<!-- jQuery :( -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.8.5/jquery-ui.min.js" integrity="sha256-fOse6WapxTrUSJOJICXXYwHRJOPa6C1OUQXi7C9Ddy8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/jquery.validate.min.js"></script>
|
||||
|
||||
|
||||
<script src="/static/materialize-stepper.min.js"></script>
|
||||
|
||||
<style>
|
||||
nav .brand-logo {
|
||||
margin-left: 48px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
main .container {
|
||||
margin-top: -12vh;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ribbon {
|
||||
width: 100%;
|
||||
height: 17vh;
|
||||
background-color: #3F51B5;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ribbon-fab:not(.tap-target-origin) {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
top: calc(17vh + 34px);
|
||||
}
|
||||
|
||||
i.very-large {
|
||||
font-size: 8rem;
|
||||
padding-top: 2px;
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.card .card-content {
|
||||
padding-left: 18px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.log {
|
||||
background-color: #1c1c1c;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 12px;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
line-height: 1.45;
|
||||
border-radius: 3px;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
color: #DDD;
|
||||
}
|
||||
|
||||
.inlinecode {
|
||||
box-sizing: border-box;
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27,31,35,0.05);
|
||||
border-radius: 3px;
|
||||
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
|
||||
}
|
||||
|
||||
.log.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log .v {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.log .d {
|
||||
color: #00DDDD;
|
||||
}
|
||||
|
||||
.log .c {
|
||||
color: magenta;
|
||||
}
|
||||
|
||||
.log .i {
|
||||
color: limegreen;
|
||||
}
|
||||
|
||||
.log .w {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.log .e {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log .e {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.log .ww {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal {
|
||||
width: 95%;
|
||||
max-height: 90%;
|
||||
height: 85% !important;
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
ul.browser-default {
|
||||
padding-left: 30px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
ul.browser-default li {
|
||||
list-style-type: initial;
|
||||
}
|
||||
|
||||
ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .step.done::before, ul.stepper.horizontal .step.active .step-title::before, ul.stepper.horizontal .step.done .step-title::before {
|
||||
background-color: #3f51b5 !important;
|
||||
}
|
||||
|
||||
.select-port-container {
|
||||
margin-top: 8px;
|
||||
margin-right: 24px;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
<div class="nav-wrapper indigo">
|
||||
<a href="#" class="brand-logo left">esphomeyaml Dashboard</a>
|
||||
<div class="select-port-container right" id="select-port-target">
|
||||
<select></select>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="tap-target pink lighten-1 select-port" data-target="select-port-target">
|
||||
<div class="tap-target-content">
|
||||
<h5>Select Upload Port</h5>
|
||||
<p>
|
||||
Here you can select where esphomeyaml will attempt to show logs and upload firmwares to.
|
||||
By default, this is "OTA", or Over-The-Air. Note that you might have to restart the HassIO add-on
|
||||
for new serial ports to be detected.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ribbon"></div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
{% for i, (file, full_path) in enumerate(zip(files, full_path_files)) %}
|
||||
<div class="row">
|
||||
<div class="col s8 offset-s2 m10 offset-m1 l12">
|
||||
<div class="card horizontal">
|
||||
<div class="card-image center-align">
|
||||
<i class="material-icons very-large icon-grey">memory</i>
|
||||
</div>
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<span class="card-title">
|
||||
{{ escape(file) }}
|
||||
<i class="material-icons right dropdown-trigger" data-target="dropdown-{{ i }}">more_vert</i>
|
||||
</span>
|
||||
<p>
|
||||
Full path: <code class="inlinecode">{{ escape(full_path) }}</code>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<a href="#" class="action-upload" data-node="{{ file }}">Upload</a>
|
||||
<a href="#" class="action-compile" data-node="{{ file }}">Compile</a>
|
||||
<a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a>
|
||||
<a href="#" class="action-validate" data-node="{{ file }}">Validate</a>
|
||||
</div>
|
||||
<ul id="dropdown-{{ i }}" class="dropdown-content">
|
||||
<li><a href="#" class="action-clean-mqtt" data-node="{{ file }}">Clean MQTT</a></li>
|
||||
<li><a href="#" class="action-clean" data-node="{{ file }}">Clean Build</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
</div>
|
||||
|
||||
<div id="modal-logs" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Show Logs <code class="inlinecode filename"></code></h4>
|
||||
<div class="log-container">
|
||||
<pre class="log"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-upload" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Compile And Upload <code class="inlinecode filename"></code></h4>
|
||||
<div class="log-container">
|
||||
<pre class="log"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-compile" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Compile <code class="inlinecode filename"></code></h4>
|
||||
<div class="log-container">
|
||||
<pre class="log"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat disabled download-binary">Download Binary</a>
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-validate" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Validate <code class="inlinecode filename"></code></h4>
|
||||
<div class="log-container">
|
||||
<pre class="log"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-wizard" class="modal">
|
||||
<div class="modal-content">
|
||||
<form action="/wizard.html" method="POST">
|
||||
<ul class="stepper linear">
|
||||
<li class="step active">
|
||||
<div class="step-title waves-effect">Introduction And Name</div>
|
||||
<div class="step-content">
|
||||
<div class="row">
|
||||
<p>
|
||||
Hi there! I'm the esphomeyaml setup wizard and will guide you through setting up
|
||||
your first ESP8266 or ESP32-powered device using esphomeyaml.
|
||||
</p>
|
||||
<a href="https://www.espressif.com/en/products/hardware/esp8266ex/overview" target="_blank">ESP8266s</a> and
|
||||
their successors (the <a href="https://www.espressif.com/en/products/hardware/esp32/overview" target="_blank">ESP32s</a>)
|
||||
are great low-cost microcontrollers that can communicate with the outside world using WiFi.
|
||||
They're found in many devices such as the popular Sonoff/iTead, but also exist as development boards
|
||||
such as the <a href="https://esphomelib.com/esphomeyaml/devices/nodemcu_esp8266.html" target="_blank">NodeMCU</a>.
|
||||
<p>
|
||||
</p>
|
||||
<a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml</a>,
|
||||
the tool you're using here, creates custom firmwares for these devices using YAML configuration
|
||||
files (similar to the ones you might be used to with Home Assistant).
|
||||
<p>
|
||||
</p>
|
||||
This wizard will create a basic YAML configuration file for your "node" (the microcontroller).
|
||||
Later, you will be able to customize this file and add some of
|
||||
<a href="https://github.com/OttoWinter/esphomelib" target="_blank">esphomelib's</a>
|
||||
many integrations.
|
||||
<p>
|
||||
<p>
|
||||
First, I need to know what this node should be called. Choose this name wisely, changing this
|
||||
later makes Over-The-Air Update attempts difficult.
|
||||
Names must be <strong>lowercase</strong> and <strong>must not contain spaces</strong> (allowed characters: <code class="inlinecode">a-z</code>,
|
||||
<code class="inlinecode">0-9</code> and <code class="inlinecode">_</code>)
|
||||
</p>
|
||||
<div class="input-field col s12">
|
||||
<input id="node_name" class="validate" type="text" name="name" required>
|
||||
<label for="node_name">Name of node</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-actions">
|
||||
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step">
|
||||
<div class="step-title waves-effect">Device Type</div>
|
||||
<div class="step-content">
|
||||
<div class="row">
|
||||
<p>
|
||||
Great! Now I need to know what type of microcontroller you're using so that I can compile firmware for them.
|
||||
Please choose either ESP32 or ESP8266 (use ESP8266 for Sonoff devices).
|
||||
</p>
|
||||
<div class="input-field col s12">
|
||||
<select id="esp_type" name="platform" required>
|
||||
<option value="ESP8266">ESP8266</option>
|
||||
<option value="ESP32">ESP32</option>
|
||||
</select>
|
||||
<label>Microcontroller Type</label>
|
||||
</div>
|
||||
<p>
|
||||
I'm also going to need to know which type of board you're using. Please go to
|
||||
<a href="http://docs.platformio.org/en/latest/platforms/espressif32.html#boards" target="_blank">ESP32 boards</a> or
|
||||
<a href="http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards" target="_blank">ESP8266 boards</a>,
|
||||
find your board and enter it here. For example, enter <code class="inlinecode">nodemcuv2</code>
|
||||
for ESP8266 NodeMCU boards. Note: Use <code class="inlinecode">esp01_1m</code> for Sonoff devices.
|
||||
</p>
|
||||
<div class="input-field col s12">
|
||||
<input id="board_type" class="validate" type="text" name="board" required>
|
||||
<label for="board_type">Board Type</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-actions">
|
||||
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step">
|
||||
<div class="step-title waves-effect">WiFi And Over-The-Air Updates</div>
|
||||
<div class="step-content">
|
||||
<div class="row">
|
||||
<p>
|
||||
Thanks! Now I need to know what WiFi Access Point I should instruct the node to connect to.
|
||||
Please enter an SSID (name of the WiFi network) and password (leave empty for no password).
|
||||
</p>
|
||||
<div class="input-field col s12">
|
||||
<input id="wifi_ssid" class="validate" type="text" name="ssid" required>
|
||||
<label for="wifi_ssid">WiFi SSID</label>
|
||||
</div>
|
||||
<div class="input-field col s12">
|
||||
<input id="wifi_password" name="psk" type="password">
|
||||
<label for="wifi_password">WiFi Password</label>
|
||||
</div>
|
||||
<p>
|
||||
Esphomelib automatically sets up an Over-The-Air update server on the node
|
||||
so that you only need to flash a firmware via USB once.
|
||||
Optionally, you can set a password for this upload process here:
|
||||
</p>
|
||||
<div class="input-field col s12">
|
||||
<input id="ota_password" class="validate" name="ota_password" type="password">
|
||||
<label for="ota_password">OTA Password</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-actions">
|
||||
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step">
|
||||
<div class="step-title waves-effect">MQTT</div>
|
||||
<div class="step-content">
|
||||
<div class="row">
|
||||
<p>
|
||||
esphomelib connects to your Home Assistant instance via
|
||||
<a href="https://www.home-assistant.io/docs/mqtt/">MQTT</a>. If you haven't already, please set up
|
||||
MQTT on your Home Assistant server, for example with the awesome
|
||||
<a href="https://www.home-assistant.io/addons/mosquitto/">Mosquitto Hass.io Add-on</a>.
|
||||
</p>
|
||||
<p>
|
||||
When you're done with that, please enter your MQTT broker here. For example
|
||||
<code class="inlinecode">192.168.1.100</code> (Note
|
||||
<code class="inlinecode">hassio.local</code> doesn't always work, please use a static IP).
|
||||
Please also specify the MQTT username and password you wish esphomelib to use
|
||||
(leave them empty if you're not using any authentication).
|
||||
</p>
|
||||
<div class="input-field col s12">
|
||||
<input id="mqtt_broker" class="validate" type="text" name="broker" required>
|
||||
<label for="mqtt_broker">MQTT Broker</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<input id="mqtt_username" class="validate" type="text" name="mqtt_username">
|
||||
<label for="mqtt_username">MQTT Username</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<input id="mqtt_password" class="validate" name="mqtt_password" type="password">
|
||||
<label for="mqtt_password">MQTT Password</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-actions">
|
||||
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step">
|
||||
<div class="step-title waves-effect">Done!</div>
|
||||
<div class="step-content">
|
||||
<p>
|
||||
Hooray! 🎉🎉🎉 You've successfully created your first esphomeyaml configuration file.
|
||||
When you click Submit, I will save this configuration file under
|
||||
<code class="inlinecode"><HASS_CONFIG_FOLDER>/esphomeyaml/<NAME_OF_NODE>.yaml</code> and
|
||||
you will be able to edit this file with the
|
||||
<a href="https://www.home-assistant.io/addons/configurator/" target="_blank">HASS Configuratior add-on</a>.
|
||||
</p>
|
||||
<h5>Next steps</h5>
|
||||
<ul class="browser-default">
|
||||
<li>
|
||||
Flash the firmware. This can be done using the “UPLOAD” option in the dashboard. See
|
||||
<a href="https://esphomelib.com/esphomeyaml/index.html#devices" target="_blank">this</a>
|
||||
for guides on how to flash different types of devices. Note that you need to restart this add-on
|
||||
for newly plugged in serial devices to be detected.
|
||||
</li>
|
||||
<li>
|
||||
With the current configuration, your node will only connect to WiFi and MQTT. To make it actually <i>do</i>
|
||||
stuff, follow
|
||||
<a href="https://esphomelib.com/esphomeyaml/guides/getting_started_hassio.html#adding-some-basic-features">
|
||||
the rest of the getting started guide
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
See the <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml index</a>
|
||||
for a list of supported sensors/devices.
|
||||
</li>
|
||||
<li>
|
||||
Join the <a href="https://discord.gg/KhAMKrd" target="_blank">Discord server</a> and say hi! When I
|
||||
have time, I would be happy to help with issues and discuss new features.
|
||||
</li>
|
||||
<li>
|
||||
Star <a href="https://github.com/OttoWinter/esphomelib" target="_blank">esphomelib</a> and
|
||||
<a href="https://github.com/OttoWinter/esphomeyaml" target="_blank">esphomeyaml</a> on GitHub
|
||||
if you find this software awesome and report issues using the bug trackers there.
|
||||
</li>
|
||||
</ul>
|
||||
<div class="step-actions">
|
||||
<button class="waves-effect waves-dark btn indigo" type="submit">SUBMIT</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-close waves-effect waves-green btn-flat">Abort</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-clean-mqtt" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Clean MQTT discovery <code class="inlinecode filename"></code></h4>
|
||||
<div class="log-container">
|
||||
<pre class="log"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-clean" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Clean Build Files <code class="inlinecode filename"></code></h4>
|
||||
<div class="log-container">
|
||||
<pre class="log"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="btn-floating btn-large ribbon-fab waves-effect waves-light pink accent-2" id="setup-wizard-start">
|
||||
<i class="material-icons">add</i>
|
||||
</a>
|
||||
|
||||
<div class="tap-target pink lighten-1 setup-wizard" data-target="setup-wizard-start">
|
||||
<div class="tap-target-content">
|
||||
<h5>Set up your first Node</h5>
|
||||
<p>
|
||||
Huh... It seems like you you don't have any esphomeyaml configuration files yet...
|
||||
Fortunately, there's a setup wizard that will step you through setting up your first node 🎉
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="page-footer indigo darken-1">
|
||||
<div class="container">
|
||||
|
||||
</div>
|
||||
<div class="footer-copyright">
|
||||
<div class="container">
|
||||
© 2018 Copyright Otto Winter, Made with <a class="grey-text text-lighten-4" href="https://materializecss.com/" target="_blank">Materialize</a>
|
||||
<a class="grey-text text-lighten-4 right" href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml {{ version }} Documentation</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
M.AutoInit(document.body);
|
||||
});
|
||||
|
||||
const colorReplace = (input) => {
|
||||
input = input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
input = input.replace(/\\033\[(?:0;)?31m/g, '<span class="e">');
|
||||
input = input.replace(/\\033\[(?:1;)?31m/g, '<span class="e bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?32m/g, '<span class="i">');
|
||||
input = input.replace(/\\033\[(?:1;)?32m/g, '<span class="i bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?33m/g, '<span class="w">');
|
||||
input = input.replace(/\\033\[(?:1;)?33m/g, '<span class="w bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?35m/g, '<span class="c">');
|
||||
input = input.replace(/\\033\[(?:1;)?35m/g, '<span class="c bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?36m/g, '<span class="d">');
|
||||
input = input.replace(/\\033\[(?:1;)?36m/g, '<span class="d bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?37m/g, '<span class="v">');
|
||||
input = input.replace(/\\033\[(?:1;)?37m/g, '<span class="v bold">');
|
||||
input = input.replace(/\\033\[(?:0;)?38m/g, '<span class="vv">');
|
||||
input = input.replace(/\\033\[(?:1;)?38m/g, '<span class="vv bold">');
|
||||
input = input.replace(/\\033\[0m/g, '</span>');
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
let configuration = "";
|
||||
let wsProtocol = "ws:";
|
||||
if (window.location.protocol === "https:") {
|
||||
wsProtocol = 'wss:';
|
||||
}
|
||||
const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port;
|
||||
|
||||
const portSelect = document.querySelector('.nav-wrapper select');
|
||||
let ports = [];
|
||||
|
||||
const fetchSerialPorts = (begin=false) => {
|
||||
fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json())
|
||||
.then(response => {
|
||||
if (ports.length === response.length) {
|
||||
let allEqual = true;
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
if (ports[i].port !== response[i].port) {
|
||||
allEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allEqual)
|
||||
return;
|
||||
}
|
||||
const hasNewPort = response.length >= ports.length;
|
||||
|
||||
ports = response;
|
||||
|
||||
const inst = M.FormSelect.getInstance(portSelect);
|
||||
if (inst !== undefined) {
|
||||
inst.destroy();
|
||||
}
|
||||
|
||||
portSelect.innerHTML = "";
|
||||
const prevSelected = getUploadPort();
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
const val = response[i];
|
||||
if (val.port === prevSelected) {
|
||||
portSelect.innerHTML += `<option value="${val.port}" selected>${val.port} (${val.desc})</option>`;
|
||||
} else {
|
||||
portSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
|
||||
}
|
||||
}
|
||||
|
||||
M.FormSelect.init(portSelect, {});
|
||||
if (!begin && hasNewPort)
|
||||
M.toast({html: "Discovered new serial port."});
|
||||
});
|
||||
};
|
||||
|
||||
const getUploadPort = () => {
|
||||
const inst = M.FormSelect.getInstance(portSelect);
|
||||
if (inst === undefined) {
|
||||
return "OTA";
|
||||
}
|
||||
|
||||
inst._setSelectedStates();
|
||||
return inst.getSelectedValues()[0];
|
||||
};
|
||||
setInterval(fetchSerialPorts, 5000);
|
||||
fetchSerialPorts(true);
|
||||
|
||||
const logsModalElem = document.getElementById("modal-logs");
|
||||
|
||||
document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
|
||||
showLogs.addEventListener('click', (e) => {
|
||||
configuration = e.target.getAttribute('data-node');
|
||||
const modalInstance = M.Modal.getInstance(logsModalElem);
|
||||
const log = logsModalElem.querySelector(".log");
|
||||
log.innerHTML = "";
|
||||
const stopLogsButton = logsModalElem.querySelector(".stop-logs");
|
||||
let stopped = false;
|
||||
stopLogsButton.innerHTML = "Stop";
|
||||
modalInstance.open();
|
||||
|
||||
const filenameField = logsModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/logs");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
const msg = data.data;
|
||||
log.innerHTML += colorReplace(msg);
|
||||
} else if (data.event === "exit") {
|
||||
if (data.code === 0) {
|
||||
M.toast({html: "Program exited successfully."});
|
||||
} else {
|
||||
M.toast({html: `Program failed with code ${data.code}`});
|
||||
}
|
||||
|
||||
stopLogsButton.innerHTML = "Close";
|
||||
stopped = true;
|
||||
}
|
||||
});
|
||||
logSocket.addEventListener('open', () => {
|
||||
const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
|
||||
logSocket.send(msg);
|
||||
});
|
||||
logSocket.addEventListener('close', () => {
|
||||
if (!stopped) {
|
||||
M.toast({html: 'Terminated process.'});
|
||||
}
|
||||
});
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
logSocket.close();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const uploadModalElem = document.getElementById("modal-upload");
|
||||
|
||||
document.querySelectorAll(".action-upload").forEach((upload) => {
|
||||
upload.addEventListener('click', (e) => {
|
||||
configuration = e.target.getAttribute('data-node');
|
||||
const modalInstance = M.Modal.getInstance(uploadModalElem);
|
||||
const log = uploadModalElem.querySelector(".log");
|
||||
log.innerHTML = "";
|
||||
const stopLogsButton = uploadModalElem.querySelector(".stop-logs");
|
||||
let stopped = false;
|
||||
stopLogsButton.innerHTML = "Stop";
|
||||
modalInstance.open();
|
||||
|
||||
const filenameField = uploadModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/run");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
const msg = data.data;
|
||||
log.innerHTML += colorReplace(msg);
|
||||
} else if (data.event === "exit") {
|
||||
if (data.code === 0) {
|
||||
M.toast({html: "Program exited successfully."});
|
||||
} else {
|
||||
M.toast({html: `Program failed with code ${data.code}`});
|
||||
}
|
||||
|
||||
stopLogsButton.innerHTML = "Close";
|
||||
stopped = true;
|
||||
}
|
||||
});
|
||||
logSocket.addEventListener('open', () => {
|
||||
const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
|
||||
logSocket.send(msg);
|
||||
});
|
||||
logSocket.addEventListener('close', () => {
|
||||
if (!stopped) {
|
||||
M.toast({html: 'Terminated process.'});
|
||||
}
|
||||
});
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
logSocket.close();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const validateModalElem = document.getElementById("modal-validate");
|
||||
|
||||
document.querySelectorAll(".action-validate").forEach((upload) => {
|
||||
upload.addEventListener('click', (e) => {
|
||||
configuration = e.target.getAttribute('data-node');
|
||||
const modalInstance = M.Modal.getInstance(validateModalElem);
|
||||
const log = validateModalElem.querySelector(".log");
|
||||
log.innerHTML = "";
|
||||
const stopLogsButton = validateModalElem.querySelector(".stop-logs");
|
||||
let stopped = false;
|
||||
stopLogsButton.innerHTML = "Stop";
|
||||
modalInstance.open();
|
||||
|
||||
const filenameField = validateModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/validate");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
const msg = data.data;
|
||||
log.innerHTML += colorReplace(msg);
|
||||
} else if (data.event === "exit") {
|
||||
if (data.code === 0) {
|
||||
M.toast({
|
||||
html: `<code class="inlinecode">${configuration}</code> is valid 👍`,
|
||||
displayLength: 5000,
|
||||
});
|
||||
} else {
|
||||
M.toast({
|
||||
html: `<code class="inlinecode">${configuration}</code> is invalid 😕`,
|
||||
displayLength: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
stopLogsButton.innerHTML = "Close";
|
||||
stopped = true;
|
||||
}
|
||||
});
|
||||
logSocket.addEventListener('open', () => {
|
||||
const msg = JSON.stringify({configuration: configuration});
|
||||
logSocket.send(msg);
|
||||
});
|
||||
logSocket.addEventListener('close', () => {
|
||||
if (!stopped) {
|
||||
M.toast({html: 'Terminated process.'});
|
||||
}
|
||||
});
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
logSocket.close();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const compileModalElem = document.getElementById("modal-compile");
|
||||
const downloadButton = compileModalElem.querySelector('.download-binary');
|
||||
|
||||
document.querySelectorAll(".action-compile").forEach((upload) => {
|
||||
upload.addEventListener('click', (e) => {
|
||||
configuration = e.target.getAttribute('data-node');
|
||||
const modalInstance = M.Modal.getInstance(compileModalElem);
|
||||
const log = compileModalElem.querySelector(".log");
|
||||
log.innerHTML = "";
|
||||
const stopLogsButton = compileModalElem.querySelector(".stop-logs");
|
||||
let stopped = false;
|
||||
stopLogsButton.innerHTML = "Stop";
|
||||
downloadButton.classList.add('disabled');
|
||||
|
||||
modalInstance.open();
|
||||
|
||||
const filenameField = compileModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/compile");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
const msg = data.data;
|
||||
log.innerHTML += colorReplace(msg);
|
||||
} else if (data.event === "exit") {
|
||||
if (data.code === 0) {
|
||||
M.toast({html: "Program exited successfully."});
|
||||
downloadButton.classList.remove('disabled');
|
||||
} else {
|
||||
M.toast({html: `Program failed with code ${data.code}`});
|
||||
}
|
||||
|
||||
stopLogsButton.innerHTML = "Close";
|
||||
stopped = true;
|
||||
}
|
||||
});
|
||||
logSocket.addEventListener('open', () => {
|
||||
const msg = JSON.stringify({configuration: configuration});
|
||||
logSocket.send(msg);
|
||||
});
|
||||
logSocket.addEventListener('close', () => {
|
||||
if (!stopped) {
|
||||
M.toast({html: 'Terminated process.'});
|
||||
}
|
||||
});
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
logSocket.close();
|
||||
};
|
||||
});
|
||||
});
|
||||
downloadButton.addEventListener('click', () => {
|
||||
const link = document.createElement("a");
|
||||
link.download = name;
|
||||
link.href = '/download.bin?configuration=' + encodeURIComponent(configuration);
|
||||
link.click();
|
||||
});
|
||||
|
||||
const cleanMqttModalElem = document.getElementById("modal-clean-mqtt");
|
||||
|
||||
document.querySelectorAll(".action-clean-mqtt").forEach((btn) => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
configuration = e.target.getAttribute('data-node');
|
||||
const modalInstance = M.Modal.getInstance(cleanMqttModalElem);
|
||||
const log = cleanMqttModalElem.querySelector(".log");
|
||||
log.innerHTML = "";
|
||||
const stopLogsButton = cleanMqttModalElem.querySelector(".stop-logs");
|
||||
let stopped = false;
|
||||
stopLogsButton.innerHTML = "Stop";
|
||||
modalInstance.open();
|
||||
|
||||
const filenameField = cleanMqttModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/clean-mqtt");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
const msg = data.data;
|
||||
log.innerHTML += colorReplace(msg);
|
||||
} else if (data.event === "exit") {
|
||||
stopLogsButton.innerHTML = "Close";
|
||||
stopped = true;
|
||||
}
|
||||
});
|
||||
logSocket.addEventListener('open', () => {
|
||||
const msg = JSON.stringify({configuration: configuration});
|
||||
logSocket.send(msg);
|
||||
});
|
||||
logSocket.addEventListener('close', () => {
|
||||
if (!stopped) {
|
||||
M.toast({html: 'Terminated process.'});
|
||||
}
|
||||
});
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
logSocket.close();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const cleanModalElem = document.getElementById("modal-clean");
|
||||
|
||||
document.querySelectorAll(".action-clean").forEach((btn) => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
configuration = e.target.getAttribute('data-node');
|
||||
const modalInstance = M.Modal.getInstance(cleanModalElem);
|
||||
const log = cleanModalElem.querySelector(".log");
|
||||
log.innerHTML = "";
|
||||
const stopLogsButton = cleanModalElem.querySelector(".stop-logs");
|
||||
let stopped = false;
|
||||
stopLogsButton.innerHTML = "Stop";
|
||||
modalInstance.open();
|
||||
|
||||
const filenameField = cleanModalElem.querySelector('.filename');
|
||||
filenameField.innerHTML = configuration;
|
||||
|
||||
const logSocket = new WebSocket(wsUrl + "/clean");
|
||||
logSocket.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.event === "line") {
|
||||
const msg = data.data;
|
||||
log.innerHTML += colorReplace(msg);
|
||||
} else if (data.event === "exit") {
|
||||
if (data.code === 0) {
|
||||
M.toast({html: "Program exited successfully."});
|
||||
downloadButton.classList.remove('disabled');
|
||||
} else {
|
||||
M.toast({html: `Program failed with code ${data.code}`});
|
||||
}
|
||||
stopLogsButton.innerHTML = "Close";
|
||||
stopped = true;
|
||||
}
|
||||
});
|
||||
logSocket.addEventListener('open', () => {
|
||||
const msg = JSON.stringify({configuration: configuration});
|
||||
logSocket.send(msg);
|
||||
});
|
||||
logSocket.addEventListener('close', () => {
|
||||
if (!stopped) {
|
||||
M.toast({html: 'Terminated process.'});
|
||||
}
|
||||
});
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
logSocket.close();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const modalSetupElem = document.getElementById("modal-wizard");
|
||||
const setupWizardStart = document.getElementById('setup-wizard-start');
|
||||
const startWizard = () => {
|
||||
const modalInstance = M.Modal.getInstance(modalSetupElem);
|
||||
modalInstance.open();
|
||||
|
||||
modalInstance.options.onCloseStart = () => {
|
||||
|
||||
};
|
||||
|
||||
$('.stepper').activateStepper({
|
||||
linearStepsNavigation: false,
|
||||
autoFocusInput: true,
|
||||
autoFormCreation: true,
|
||||
showFeedbackLoader: true,
|
||||
parallel: false
|
||||
});
|
||||
};
|
||||
setupWizardStart.addEventListener('click', startWizard);
|
||||
</script>
|
||||
|
||||
{% if len(files) == 0 %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tapTargetElem = document.querySelector('.tap-target.setup-wizard');
|
||||
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
|
||||
tapTargetInstance.options.onOpen = () => {
|
||||
$('.tap-target-origin').on('click', () => {
|
||||
startWizard();
|
||||
});
|
||||
};
|
||||
tapTargetInstance.open();
|
||||
});
|
||||
</script>
|
||||
{% end %}
|
||||
|
||||
{% if begin %}
|
||||
<script>
|
||||
window.history.replaceState({}, document.title, "/");
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tapTargetElem = document.querySelector('.tap-target.select-port');
|
||||
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
|
||||
tapTargetInstance.open();
|
||||
|
||||
tapTargetInstance.contentEl.style["top"] = "300px";
|
||||
tapTargetInstance.contentEl.style["padding"] = "250px";
|
||||
tapTargetInstance.waveEl.style["top"] = "250px";
|
||||
tapTargetInstance.waveEl.style["left"] = "250px";
|
||||
tapTargetInstance.waveEl.style["width"] = "300px";
|
||||
tapTargetInstance.waveEl.style["height"] = "300px";
|
||||
});
|
||||
</script>
|
||||
{% end %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
230
esphomeyaml/espota2.py
Executable file
230
esphomeyaml/espota2.py
Executable file
@@ -0,0 +1,230 @@
|
||||
import hashlib
|
||||
import logging
|
||||
import random
|
||||
import socket
|
||||
import sys
|
||||
|
||||
RESPONSE_OK = 0
|
||||
RESPONSE_REQUEST_AUTH = 1
|
||||
|
||||
RESPONSE_HEADER_OK = 64
|
||||
RESPONSE_AUTH_OK = 65
|
||||
RESPONSE_UPDATE_PREPARE_OK = 66
|
||||
RESPONSE_BIN_MD5_OK = 67
|
||||
RESPONSE_RECEIVE_OK = 68
|
||||
RESPONSE_UPDATE_END_OK = 69
|
||||
|
||||
RESPONSE_ERROR_MAGIC = 128
|
||||
RESPONSE_ERROR_UPDATE_PREPARE = 129
|
||||
RESPONSE_ERROR_AUTH_INVALID = 130
|
||||
RESPONSE_ERROR_WRITING_FLASH = 131
|
||||
RESPONSE_ERROR_UPDATE_END = 132
|
||||
RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 133
|
||||
RESPONSE_ERROR_UNKNOWN = 255
|
||||
|
||||
OTA_VERSION_1_0 = 1
|
||||
|
||||
MAGIC_BYTES = [0x6C, 0x26, 0xF7, 0x5C, 0x45]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_progress(progress):
|
||||
bar_length = 60
|
||||
status = ""
|
||||
if progress >= 1:
|
||||
progress = 1
|
||||
status = "Done...\r\n"
|
||||
block = int(round(bar_length * progress))
|
||||
text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block),
|
||||
int(progress * 100), status)
|
||||
sys.stderr.write(text)
|
||||
sys.stderr.flush()
|
||||
|
||||
|
||||
class OTAError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def recv_decode(sock, amount, decode=True):
|
||||
data = sock.recv(amount)
|
||||
if not decode:
|
||||
return data
|
||||
return [ord(x) for x in data]
|
||||
|
||||
|
||||
def receive_exactly(sock, amount, msg, expect, decode=True):
|
||||
if decode:
|
||||
data = []
|
||||
else:
|
||||
data = ''
|
||||
|
||||
try:
|
||||
data += recv_decode(sock, 1, decode=decode)
|
||||
except socket.error as err:
|
||||
raise OTAError("Error receiving acknowledge {}: {}".format(msg, err))
|
||||
|
||||
try:
|
||||
check_error(data, expect)
|
||||
except OTAError:
|
||||
sock.close()
|
||||
raise
|
||||
|
||||
while len(data) < amount:
|
||||
try:
|
||||
data += recv_decode(sock, amount - len(data), decode=decode)
|
||||
except socket.error as err:
|
||||
raise OTAError("Error receiving {}: {}".format(msg, err))
|
||||
return data
|
||||
|
||||
|
||||
def check_error(data, expect):
|
||||
if not expect:
|
||||
return
|
||||
dat = data[0]
|
||||
if dat == RESPONSE_ERROR_MAGIC:
|
||||
raise OTAError("Error: Invalid magic byte")
|
||||
if dat == RESPONSE_ERROR_UPDATE_PREPARE:
|
||||
raise OTAError("Error: Couldn't prepare flash memory for update. Is the binary too big?")
|
||||
if dat == RESPONSE_ERROR_AUTH_INVALID:
|
||||
raise OTAError("Error: Authentication invalid. Is the password correct?")
|
||||
if dat == RESPONSE_ERROR_WRITING_FLASH:
|
||||
raise OTAError("Error: Wring OTA data to flash memory failed. See USB logs for more "
|
||||
"information.")
|
||||
if dat == RESPONSE_ERROR_UPDATE_END:
|
||||
raise OTAError("Error: Finishing update failed. See the MQTT/USB logs for more "
|
||||
"information.")
|
||||
if dat == RESPONSE_ERROR_INVALID_BOOTSTRAPPING:
|
||||
raise OTAError("Error: Please press the reset button on the ESP. A manual reset is "
|
||||
"required on the first OTA-Update after flashing via USB.")
|
||||
if dat == RESPONSE_ERROR_UNKNOWN:
|
||||
raise OTAError("Unknown error from ESP")
|
||||
if not isinstance(expect, (list, tuple)):
|
||||
expect = [expect]
|
||||
if dat not in expect:
|
||||
raise OTAError("Unexpected response from ESP: 0x{:02X}".format(data[0]))
|
||||
|
||||
|
||||
def send_check(sock, data, msg):
|
||||
try:
|
||||
if isinstance(data, (list, tuple)):
|
||||
data = ''.join([chr(x) for x in data])
|
||||
elif isinstance(data, int):
|
||||
data = chr(data)
|
||||
sock.send(data)
|
||||
except socket.error as err:
|
||||
raise OTAError("Error sending {}: {}".format(msg, err))
|
||||
|
||||
|
||||
def perform_ota(sock, password, file_handle, filename):
|
||||
file_md5 = hashlib.md5(file_handle.read()).hexdigest()
|
||||
file_size = file_handle.tell()
|
||||
_LOGGER.info('Uploading %s (%s bytes)', filename, file_size)
|
||||
file_handle.seek(0)
|
||||
_LOGGER.debug("MD5 of binary is %s", file_md5)
|
||||
|
||||
send_check(sock, MAGIC_BYTES, 'magic bytes')
|
||||
|
||||
_, version = receive_exactly(sock, 2, 'version', RESPONSE_OK)
|
||||
if version != OTA_VERSION_1_0:
|
||||
raise OTAError("Unsupported OTA version {}".format(version))
|
||||
|
||||
# Features
|
||||
send_check(sock, 0x00, 'features')
|
||||
receive_exactly(sock, 1, 'features', RESPONSE_HEADER_OK)
|
||||
|
||||
auth, = receive_exactly(sock, 1, 'auth', [RESPONSE_REQUEST_AUTH, RESPONSE_AUTH_OK])
|
||||
if auth == RESPONSE_REQUEST_AUTH:
|
||||
if not password:
|
||||
raise OTAError("ESP requests password, but no password given!")
|
||||
nonce = receive_exactly(sock, 32, 'authentication nonce', [], decode=False)
|
||||
_LOGGER.debug("Auth: Nonce is %s", nonce)
|
||||
cnonce = hashlib.md5(str(random.random()).encode()).hexdigest()
|
||||
_LOGGER.debug("Auth: CNonce is %s", cnonce)
|
||||
|
||||
send_check(sock, cnonce, 'auth cnonce')
|
||||
|
||||
result_md5 = hashlib.md5()
|
||||
result_md5.update(password.encode())
|
||||
result_md5.update(nonce.encode())
|
||||
result_md5.update(cnonce.encode())
|
||||
result = result_md5.hexdigest()
|
||||
_LOGGER.debug("Auth: Result is %s", result)
|
||||
|
||||
send_check(sock, result, 'auth result')
|
||||
receive_exactly(sock, 1, 'auth result', RESPONSE_AUTH_OK)
|
||||
else:
|
||||
if password:
|
||||
raise OTAError("Password specified, but ESP doesn't accept password!")
|
||||
|
||||
file_size_encoded = [
|
||||
(file_size >> 24) & 0xFF,
|
||||
(file_size >> 16) & 0xFF,
|
||||
(file_size >> 8) & 0xFF,
|
||||
(file_size >> 0) & 0xFF,
|
||||
]
|
||||
send_check(sock, file_size_encoded, 'binary size')
|
||||
receive_exactly(sock, 1, 'binary size', RESPONSE_UPDATE_PREPARE_OK)
|
||||
|
||||
send_check(sock, file_md5, 'file checksum')
|
||||
receive_exactly(sock, 1, 'file checksum', RESPONSE_BIN_MD5_OK)
|
||||
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4096)
|
||||
offset = 0
|
||||
update_progress(0.0)
|
||||
while True:
|
||||
chunk = file_handle.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
offset += len(chunk)
|
||||
|
||||
try:
|
||||
sock.sendall(chunk)
|
||||
except socket.error as err:
|
||||
sys.stderr.write('\n')
|
||||
raise OTAError("Error sending data: {}".format(err))
|
||||
|
||||
update_progress(offset / float(file_size))
|
||||
|
||||
sys.stderr.write('\n')
|
||||
_LOGGER.info("Waiting for result...")
|
||||
|
||||
receive_exactly(sock, 1, 'receive OK', RESPONSE_RECEIVE_OK)
|
||||
receive_exactly(sock, 1, 'Update end', RESPONSE_UPDATE_END_OK)
|
||||
|
||||
_LOGGER.info("OTA successful")
|
||||
|
||||
|
||||
def run_ota(remote_host, remote_port, password, filename):
|
||||
_LOGGER.info("Connecting to %s:%s...", remote_host, remote_port)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(5.0)
|
||||
try:
|
||||
sock.connect((remote_host, remote_port))
|
||||
except socket.error as err:
|
||||
sock.close()
|
||||
_LOGGER.error("Connecting to %s:%s failed: %s", remote_host, remote_port, err)
|
||||
return 1
|
||||
|
||||
file_handle = open(filename, 'rb')
|
||||
try:
|
||||
perform_ota(sock, password, file_handle, filename)
|
||||
except OTAError as err:
|
||||
_LOGGER.error(str(err))
|
||||
return 1
|
||||
finally:
|
||||
sock.close()
|
||||
file_handle.close()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def run_legacy_ota(verbose, host_port, remote_host, remote_port, password, filename):
|
||||
from esphomeyaml import espota
|
||||
|
||||
espota_args = ['espota.py', '--debug', '--progress', '-i', remote_host,
|
||||
'-p', str(remote_port), '-f', filename,
|
||||
'-a', password, '-P', str(host_port)]
|
||||
if verbose:
|
||||
espota_args.append('-d')
|
||||
return espota.main(espota_args)
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from collections import OrderedDict, deque
|
||||
|
||||
@@ -9,7 +11,7 @@ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOV
|
||||
CONF_INVERTED, \
|
||||
CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PCF8574, \
|
||||
CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC
|
||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, TimePeriodMicroseconds, \
|
||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, Lambda, TimePeriodMicroseconds, \
|
||||
TimePeriodMilliseconds, TimePeriodSeconds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -58,6 +60,9 @@ class Expression(object):
|
||||
continue
|
||||
require.require()
|
||||
|
||||
def has_side_effects(self):
|
||||
return self.required
|
||||
|
||||
|
||||
class RawExpression(Expression):
|
||||
def __init__(self, text):
|
||||
@@ -65,7 +70,7 @@ class RawExpression(Expression):
|
||||
self.text = text
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
return str(self.text)
|
||||
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
@@ -81,10 +86,11 @@ class AssignmentExpression(Expression):
|
||||
|
||||
def __str__(self):
|
||||
type_ = self.type
|
||||
if core.SIMPLIFY:
|
||||
type_ = u'auto'
|
||||
return u"{} {}{} = {}".format(type_, self.modifier, self.name, self.rhs)
|
||||
|
||||
def has_side_effects(self):
|
||||
return self.rhs.has_side_effects()
|
||||
|
||||
|
||||
class ExpressionList(Expression):
|
||||
def __init__(self, *args):
|
||||
@@ -137,6 +143,8 @@ class StructInitializer(Expression):
|
||||
def __init__(self, base, *args):
|
||||
super(StructInitializer, self).__init__()
|
||||
self.base = base
|
||||
if isinstance(base, Expression):
|
||||
self.requires.append(base)
|
||||
if not isinstance(args, OrderedDict):
|
||||
args = OrderedDict(args)
|
||||
self.args = OrderedDict()
|
||||
@@ -180,6 +188,58 @@ class ArrayInitializer(Expression):
|
||||
return cpp
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
class ParameterExpression(Expression):
|
||||
def __init__(self, type, id):
|
||||
super(ParameterExpression, self).__init__()
|
||||
self.type = type
|
||||
self.id = id
|
||||
|
||||
def __str__(self):
|
||||
return u"{} {}".format(self.type, self.id)
|
||||
|
||||
|
||||
class ParameterListExpression(Expression):
|
||||
def __init__(self, *parameters):
|
||||
super(ParameterListExpression, self).__init__()
|
||||
self.parameters = []
|
||||
for parameter in parameters:
|
||||
if not isinstance(parameter, ParameterExpression):
|
||||
parameter = ParameterExpression(*parameter)
|
||||
self.parameters.append(parameter)
|
||||
self.requires.append(parameter)
|
||||
|
||||
def __str__(self):
|
||||
return u", ".join(unicode(x) for x in self.parameters)
|
||||
|
||||
|
||||
class LambdaExpression(Expression):
|
||||
def __init__(self, parts, parameters, capture='=', return_type=None):
|
||||
super(LambdaExpression, self).__init__()
|
||||
self.parts = parts
|
||||
if not isinstance(parameters, ParameterListExpression):
|
||||
parameters = ParameterListExpression(*parameters)
|
||||
self.parameters = parameters
|
||||
self.requires.append(self.parameters)
|
||||
self.capture = capture
|
||||
self.return_type = return_type
|
||||
if return_type is not None:
|
||||
self.requires.append(return_type)
|
||||
for i in range(1, len(parts), 3):
|
||||
self.requires.append(parts[i])
|
||||
|
||||
def __str__(self):
|
||||
cpp = u'[{}]({})'.format(self.capture, self.parameters)
|
||||
if self.return_type is not None:
|
||||
cpp += u' -> {}'.format(self.return_type)
|
||||
cpp += u' {{\n{}\n}}'.format(self.content)
|
||||
return indent_all_but_first_and_last(cpp)
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
return u''.join(unicode(part) for part in self.parts)
|
||||
|
||||
|
||||
class Literal(Expression):
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
@@ -213,6 +273,12 @@ class IntLiteral(Literal):
|
||||
self.i = i
|
||||
|
||||
def __str__(self):
|
||||
if self.i > 4294967295:
|
||||
return u'{}ULL'.format(self.i)
|
||||
if self.i > 2147483647:
|
||||
return u'{}UL'.format(self.i)
|
||||
if self.i < -2147483648:
|
||||
return u'{}LL'.format(self.i)
|
||||
return unicode(self.i)
|
||||
|
||||
|
||||
@@ -235,9 +301,9 @@ class HexIntLiteral(Literal):
|
||||
|
||||
|
||||
class FloatLiteral(Literal):
|
||||
def __init__(self, float_):
|
||||
def __init__(self, value):
|
||||
super(FloatLiteral, self).__init__()
|
||||
self.float_ = float_
|
||||
self.float_ = value
|
||||
|
||||
def __str__(self):
|
||||
return u"{:f}f".format(self.float_)
|
||||
@@ -297,83 +363,141 @@ def statement(expression):
|
||||
return ExpressionStatement(expression)
|
||||
|
||||
|
||||
def register_variable(id, obj):
|
||||
_LOGGER.debug("Registered variable %s of type %s", id.id, id.type)
|
||||
_VARIABLES[id] = obj
|
||||
|
||||
|
||||
# pylint: disable=redefined-builtin, invalid-name
|
||||
def variable(type, id, rhs):
|
||||
def variable(id, rhs, type=None):
|
||||
rhs = safe_exp(rhs)
|
||||
obj = MockObj(id, u'.')
|
||||
assignment = AssignmentExpression(type, '', id, rhs, obj)
|
||||
id.type = type or id.type
|
||||
assignment = AssignmentExpression(id.type, '', id, rhs, obj)
|
||||
add(assignment)
|
||||
_VARIABLES[id] = obj, type
|
||||
register_variable(id, obj)
|
||||
obj.requires.append(assignment)
|
||||
return obj
|
||||
|
||||
|
||||
def Pvariable(type, id, rhs):
|
||||
def Pvariable(id, rhs, has_side_effects=True, type=None):
|
||||
rhs = safe_exp(rhs)
|
||||
obj = MockObj(id, u'->')
|
||||
assignment = AssignmentExpression(type, '*', id, rhs, obj)
|
||||
if not has_side_effects and hasattr(rhs, '_has_side_effects'):
|
||||
# pylint: disable=attribute-defined-outside-init, protected-access
|
||||
rhs._has_side_effects = False
|
||||
obj = MockObj(id, u'->', has_side_effects=has_side_effects)
|
||||
id.type = type or id.type
|
||||
assignment = AssignmentExpression(id.type, '*', id, rhs, obj)
|
||||
add(assignment)
|
||||
_VARIABLES[id] = obj, type
|
||||
register_variable(id, obj)
|
||||
obj.requires.append(assignment)
|
||||
return obj
|
||||
|
||||
|
||||
_QUEUE = deque()
|
||||
_TASKS = deque()
|
||||
_VARIABLES = {}
|
||||
_EXPRESSIONS = []
|
||||
|
||||
|
||||
def get_variable(id, type=None):
|
||||
result = None
|
||||
while _QUEUE:
|
||||
if id is not None:
|
||||
if id in _VARIABLES:
|
||||
result = _VARIABLES[id][0]
|
||||
break
|
||||
elif type is not None:
|
||||
result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
|
||||
if result is not None:
|
||||
break
|
||||
func, config = _QUEUE.popleft()
|
||||
func(config)
|
||||
if id is None and type is None:
|
||||
return None
|
||||
if result is None:
|
||||
if id is not None:
|
||||
if id in _VARIABLES:
|
||||
result = _VARIABLES[id][0]
|
||||
elif type is not None:
|
||||
result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
|
||||
|
||||
if result is None:
|
||||
raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type))
|
||||
return result
|
||||
def get_variable(id):
|
||||
while True:
|
||||
if id in _VARIABLES:
|
||||
yield _VARIABLES[id]
|
||||
return
|
||||
_LOGGER.debug("Waiting for variable %s", id)
|
||||
yield None
|
||||
|
||||
|
||||
def add_task(func, config):
|
||||
_QUEUE.append((func, config))
|
||||
def process_lambda(value, parameters, capture='=', return_type=None):
|
||||
if value is None:
|
||||
yield
|
||||
return
|
||||
parts = value.parts[:]
|
||||
for i, id in enumerate(value.requires_ids):
|
||||
var = None
|
||||
for var in get_variable(id):
|
||||
yield
|
||||
if parts[i * 3 + 2] == '.':
|
||||
parts[i * 3 + 1] = var._
|
||||
else:
|
||||
parts[i * 3 + 1] = var
|
||||
parts[i * 3 + 2] = ''
|
||||
yield LambdaExpression(parts, parameters, capture, return_type)
|
||||
|
||||
|
||||
def templatable(value, input_type, output_type):
|
||||
if isinstance(value, Lambda):
|
||||
lambda_ = None
|
||||
for lambda_ in process_lambda(value, [(input_type, 'x')], return_type=output_type):
|
||||
yield None
|
||||
yield lambda_
|
||||
else:
|
||||
yield value
|
||||
|
||||
|
||||
def add_job(func, *args, **kwargs):
|
||||
domain = kwargs.get('domain')
|
||||
if inspect.isgeneratorfunction(func):
|
||||
def func_():
|
||||
yield
|
||||
for _ in func(*args):
|
||||
yield
|
||||
else:
|
||||
def func_():
|
||||
yield
|
||||
func(*args)
|
||||
gen = func_()
|
||||
_TASKS.append((gen, domain))
|
||||
return gen
|
||||
|
||||
|
||||
def flush_tasks():
|
||||
i = 0
|
||||
while _TASKS:
|
||||
i += 1
|
||||
if i > 1000000:
|
||||
raise ESPHomeYAMLError("Circular dependency detected!")
|
||||
|
||||
task, domain = _TASKS.popleft()
|
||||
_LOGGER.debug("Executing task for domain=%s", domain)
|
||||
try:
|
||||
task.next()
|
||||
_TASKS.append((task, domain))
|
||||
except StopIteration:
|
||||
_LOGGER.debug(" -> %s finished", domain)
|
||||
|
||||
|
||||
def add(expression, require=True):
|
||||
if require and isinstance(expression, Expression):
|
||||
expression.require()
|
||||
_EXPRESSIONS.append(expression)
|
||||
_LOGGER.debug("Adding: %s", statement(expression))
|
||||
return expression
|
||||
|
||||
|
||||
class MockObj(Expression):
|
||||
def __init__(self, base, op=u'.'):
|
||||
def __init__(self, base, op=u'.', has_side_effects=True):
|
||||
self.base = base
|
||||
self.op = op
|
||||
self._has_side_effects = has_side_effects
|
||||
super(MockObj, self).__init__()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr == u'_':
|
||||
obj = MockObj(u'{}{}'.format(self.base, self.op))
|
||||
obj.requires.append(self)
|
||||
return obj
|
||||
if attr == u'new':
|
||||
obj = MockObj(u'new {}'.format(self.base), u'->')
|
||||
obj.requires.append(self)
|
||||
return obj
|
||||
next_op = u'.'
|
||||
if attr.startswith(u'P'):
|
||||
if attr.startswith(u'P') and self.op not in ['::', '']:
|
||||
attr = attr[1:]
|
||||
next_op = u'->'
|
||||
op = self.op
|
||||
obj = MockObj(u'{}{}{}'.format(self.base, op, attr), next_op)
|
||||
if attr.startswith(u'_'):
|
||||
attr = attr[1:]
|
||||
obj = MockObj(u'{}{}{}'.format(self.base, self.op, attr), next_op)
|
||||
obj.requires.append(self)
|
||||
return obj
|
||||
|
||||
@@ -394,12 +518,68 @@ class MockObj(Expression):
|
||||
continue
|
||||
require.require()
|
||||
|
||||
def template(self, args):
|
||||
if not isinstance(args, TemplateArguments):
|
||||
args = TemplateArguments(args)
|
||||
obj = MockObj(u'{}{}'.format(self.base, args))
|
||||
obj.requires.append(self)
|
||||
obj.requires.append(args)
|
||||
return obj
|
||||
|
||||
App = MockObj(u'App')
|
||||
def namespace(self, name):
|
||||
obj = MockObj(u'{}{}{}'.format(self.base, self.op, name), u'::')
|
||||
obj.requires.append(self)
|
||||
return obj
|
||||
|
||||
GPIOPin = MockObj(u'GPIOPin')
|
||||
GPIOOutputPin = MockObj(u'GPIOOutputPin')
|
||||
GPIOInputPin = MockObj(u'GPIOInputPin')
|
||||
def operator(self, name):
|
||||
if name == 'ref':
|
||||
obj = MockObj(u'{} &'.format(self.base), u'')
|
||||
obj.requires.append(self)
|
||||
return obj
|
||||
if name == "const":
|
||||
obj = MockObj(u'const {}'.format(self.base), u'')
|
||||
obj.requires.append(self)
|
||||
return obj
|
||||
raise NotImplementedError()
|
||||
|
||||
def has_side_effects(self):
|
||||
return self._has_side_effects
|
||||
|
||||
def __getitem__(self, item):
|
||||
next_op = u'.'
|
||||
if isinstance(item, str) and item.startswith(u'P'):
|
||||
item = item[1:]
|
||||
next_op = u'->'
|
||||
obj = MockObj(u'{}[{}]'.format(self.base, item), next_op)
|
||||
obj.requires.append(self)
|
||||
if isinstance(item, Expression):
|
||||
obj.requires.append(item)
|
||||
return obj
|
||||
|
||||
|
||||
global_ns = MockObj('', '')
|
||||
float_ = global_ns.namespace('float')
|
||||
bool_ = global_ns.namespace('bool')
|
||||
std_ns = global_ns.namespace('std')
|
||||
std_string = std_ns.string
|
||||
uint8 = global_ns.namespace('uint8_t')
|
||||
uint16 = global_ns.namespace('uint16_t')
|
||||
uint32 = global_ns.namespace('uint32_t')
|
||||
const_char_p = global_ns.namespace('const char *')
|
||||
NAN = global_ns.namespace('NAN')
|
||||
esphomelib_ns = global_ns # using namespace esphomelib;
|
||||
NoArg = esphomelib_ns.NoArg
|
||||
App = esphomelib_ns.App
|
||||
Application = esphomelib_ns.namespace('Application')
|
||||
optional = esphomelib_ns.optional
|
||||
arduino_json_ns = global_ns.namespace('ArduinoJson')
|
||||
JsonObject = arduino_json_ns.JsonObject
|
||||
JsonObjectRef = JsonObject.operator('ref')
|
||||
JsonObjectConstRef = JsonObjectRef.operator('const')
|
||||
|
||||
GPIOPin = esphomelib_ns.GPIOPin
|
||||
GPIOOutputPin = esphomelib_ns.GPIOOutputPin
|
||||
GPIOInputPin = esphomelib_ns.GPIOInputPin
|
||||
|
||||
|
||||
def get_gpio_pin_number(conf):
|
||||
@@ -410,32 +590,43 @@ def get_gpio_pin_number(conf):
|
||||
|
||||
def generic_gpio_pin_expression_(conf, mock_obj, default_mode):
|
||||
if conf is None:
|
||||
return None
|
||||
return
|
||||
number = conf[CONF_NUMBER]
|
||||
inverted = conf.get(CONF_INVERTED)
|
||||
if CONF_PCF8574 in conf:
|
||||
hub = get_variable(conf[CONF_PCF8574], 'io::PCF8574Component')
|
||||
hub = None
|
||||
for hub in get_variable(conf[CONF_PCF8574]):
|
||||
yield None
|
||||
if default_mode == u'INPUT':
|
||||
mode = conf.get(CONF_MODE, u'INPUT')
|
||||
return hub.make_input_pin(number,
|
||||
RawExpression('PCF8574_' + mode),
|
||||
inverted)
|
||||
yield hub.make_input_pin(number,
|
||||
RawExpression('PCF8574_' + mode),
|
||||
inverted)
|
||||
return
|
||||
elif default_mode == u'OUTPUT':
|
||||
return hub.make_output_pin(number, inverted)
|
||||
yield hub.make_output_pin(number, inverted)
|
||||
return
|
||||
else:
|
||||
raise ESPHomeYAMLError(u"Unknown default mode {}".format(default_mode))
|
||||
if len(conf) == 1:
|
||||
return IntLiteral(number)
|
||||
yield IntLiteral(number)
|
||||
return
|
||||
mode = RawExpression(conf.get(CONF_MODE, default_mode))
|
||||
return mock_obj(number, mode, inverted)
|
||||
yield mock_obj(number, mode, inverted)
|
||||
|
||||
|
||||
def gpio_output_pin_expression(conf):
|
||||
return generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT')
|
||||
exp = None
|
||||
for exp in generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT'):
|
||||
yield None
|
||||
yield exp
|
||||
|
||||
|
||||
def gpio_input_pin_expression(conf):
|
||||
return generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT')
|
||||
exp = None
|
||||
for exp in generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT'):
|
||||
yield None
|
||||
yield exp
|
||||
|
||||
|
||||
def setup_mqtt_component(obj, config):
|
||||
@@ -449,18 +640,11 @@ def setup_mqtt_component(obj, config):
|
||||
add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC]))
|
||||
if CONF_AVAILABILITY in config:
|
||||
availability = config[CONF_AVAILABILITY]
|
||||
add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
|
||||
availability[CONF_PAYLOAD_NOT_AVAILABLE]))
|
||||
|
||||
|
||||
def exp_empty_optional(type):
|
||||
return RawExpression(u'Optional<{}>()'.format(type))
|
||||
|
||||
|
||||
def exp_optional(type, value):
|
||||
if value is None:
|
||||
return exp_empty_optional(type)
|
||||
return value
|
||||
if not availability:
|
||||
add(obj.disable_availability())
|
||||
else:
|
||||
add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
|
||||
availability[CONF_PAYLOAD_NOT_AVAILABLE]))
|
||||
|
||||
|
||||
# shlex's quote for Python 2.7
|
||||
@@ -485,3 +669,7 @@ def color(the_color, message='', reset=None):
|
||||
if not message:
|
||||
return parse_colors(the_color)
|
||||
return parse_colors(the_color) + message + escape_codes[reset or 'reset']
|
||||
|
||||
|
||||
def relative_path(path):
|
||||
return os.path.join(os.path.dirname(core.CONFIG_PATH), os.path.expanduser(path))
|
||||
|
||||
@@ -2,6 +2,8 @@ from __future__ import print_function
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import ssl
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
@@ -10,8 +12,9 @@ from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \
|
||||
CONF_LOG_TOPIC, \
|
||||
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
|
||||
CONF_USERNAME
|
||||
CONF_USERNAME, CONF_TOPIC, CONF_SSL_FINGERPRINTS
|
||||
from esphomeyaml.helpers import color
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,6 +33,14 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
|
||||
config[CONF_MQTT][CONF_PASSWORD])
|
||||
elif username:
|
||||
client.username_pw_set(username, password)
|
||||
|
||||
if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS):
|
||||
if sys.version_info >= (2, 7, 13):
|
||||
tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member
|
||||
else:
|
||||
tls_version = ssl.PROTOCOL_SSLv23
|
||||
client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED,
|
||||
tls_version=tls_version, ciphers=None)
|
||||
client.connect(config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT])
|
||||
|
||||
try:
|
||||
@@ -39,13 +50,13 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
|
||||
return 0
|
||||
|
||||
|
||||
def show_logs(config, topic=None, username=None, password=None, client_id=None):
|
||||
def show_logs(config, topic=None, username=None, password=None, client_id=None, escape=False):
|
||||
if topic is not None:
|
||||
pass # already have topic
|
||||
elif CONF_MQTT in config:
|
||||
conf = config[CONF_MQTT]
|
||||
if CONF_LOG_TOPIC in conf:
|
||||
topic = config[CONF_MQTT][CONF_LOG_TOPIC]
|
||||
topic = config[CONF_MQTT][CONF_LOG_TOPIC][CONF_TOPIC]
|
||||
elif CONF_TOPIC_PREFIX in config[CONF_MQTT]:
|
||||
topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + u'/debug'
|
||||
else:
|
||||
@@ -57,7 +68,10 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None):
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
time = datetime.now().time().strftime(u'[%H:%M:%S]')
|
||||
print(time + msg.payload)
|
||||
message = time + msg.payload
|
||||
if escape:
|
||||
message = message.replace('\033', '\\033')
|
||||
safe_print(message)
|
||||
|
||||
return initialize(config, [topic], on_message, username, password, client_id)
|
||||
|
||||
@@ -67,12 +81,18 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
|
||||
discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant')
|
||||
name = config[CONF_ESPHOMEYAML][CONF_NAME]
|
||||
topic = u'{}/+/{}/#'.format(discovery_prefix, name)
|
||||
_LOGGER.info(u"Clearing messages from %s", topic)
|
||||
_LOGGER.info(u"Clearing messages from '%s'", topic)
|
||||
_LOGGER.info(u"Please close this window when no more messages appear and the "
|
||||
u"MQTT topic has been cleared of retained messages.")
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
if not msg.payload:
|
||||
if not msg.payload or not msg.retain:
|
||||
return
|
||||
try:
|
||||
print(u"Clearing topic {}".format(msg.topic))
|
||||
except UnicodeDecodeError:
|
||||
print(u"Skipping non-UTF-8 topic (prohibited by MQTT standard)")
|
||||
return
|
||||
print(u"Clearing topic {}".format(msg.topic))
|
||||
client.publish(msg.topic, None, retain=True)
|
||||
|
||||
return initialize(config, [topic], on_message, username, password, client_id)
|
||||
@@ -80,8 +100,6 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
|
||||
|
||||
# From marvinroger/async-mqtt-client -> scripts/get-fingerprint/get-fingerprint.py
|
||||
def get_fingerprint(config):
|
||||
import ssl
|
||||
|
||||
addr = config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT]
|
||||
_LOGGER.info("Getting fingerprint from %s:%s", addr[0], addr[1])
|
||||
try:
|
||||
@@ -93,6 +111,7 @@ def get_fingerprint(config):
|
||||
|
||||
sha1 = hashlib.sha1(cert_der).hexdigest()
|
||||
|
||||
print(u"SHA1 Fingerprint: " + color('cyan', sha1))
|
||||
print(u"Copy above string into mqtt.ssl_fingerprints section of {}".format(core.CONFIG_PATH))
|
||||
safe_print(u"SHA1 Fingerprint: " + color('cyan', sha1))
|
||||
safe_print(u"Copy the string above into mqtt.ssl_fingerprints section of {}"
|
||||
u"".format(core.CONFIG_PATH))
|
||||
return 0
|
||||
|
||||
@@ -4,67 +4,172 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.components import pcf8574
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ESP8266_PINS = {
|
||||
'A0': 17, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14,
|
||||
}
|
||||
ESP8266_NODEMCU_PINS = dict(ESP8266_PINS, **{
|
||||
'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'D9': 3,
|
||||
'D10': 1, 'LED': 16, 'SDA': 4, 'SCL': 5,
|
||||
})
|
||||
ESP8266_D1_PINS = dict(ESP8266_PINS, **{
|
||||
'D0': 3, 'D1': 1, 'D2': 16, 'D3': 5, 'D4': 4, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 0, 'D9': 2,
|
||||
'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2, 'SDA': 4, 'SCL': 5,
|
||||
})
|
||||
ESP8266_D1_MINI_PINS = dict(ESP8266_PINS, **{
|
||||
'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'RX': 3,
|
||||
'TX': 1, 'LED': 2, 'SDA': 4, 'SCL': 5,
|
||||
})
|
||||
ESP8266_THING_PINS = dict(ESP8266_PINS, **{
|
||||
'LED': 5, 'SDA': 2, 'SCL': 14,
|
||||
})
|
||||
ESP8266_ADAFRUIT_PINS = dict(ESP8266_PINS, **{
|
||||
'LED': 0, 'SDA': 4, 'SCL': 5,
|
||||
})
|
||||
ESP8266_ESPDUINO_PINS = dict(ESP8266_PINS, **{
|
||||
'LED': 16, 'SDA': 4, 'SCL': 5,
|
||||
})
|
||||
ESP8266_BOARD_TO_PINS = {
|
||||
'huzzah': ESP8266_ADAFRUIT_PINS,
|
||||
'espduino': ESP8266_ESPDUINO_PINS,
|
||||
'nodemcu': ESP8266_NODEMCU_PINS, 'nodemcuv2': ESP8266_NODEMCU_PINS,
|
||||
'thing': ESP8266_THING_PINS, 'thingdev': ESP8266_THING_PINS,
|
||||
'd1': ESP8266_D1_PINS,
|
||||
'd1_mini': ESP8266_D1_MINI_PINS, 'd1_mini_lite': ESP8266_D1_MINI_PINS,
|
||||
'd1_mini_pro': ESP8266_D1_MINI_PINS
|
||||
ESP8266_BASE_PINS = {
|
||||
'A0': 17, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14, 'SDA': 4, 'SCL': 5, 'RX': 3, 'TX': 1
|
||||
}
|
||||
|
||||
ESP32_PINS = {
|
||||
ESP8266_BOARD_PINS = {
|
||||
'd1': {'D0': 3, 'D1': 1, 'D2': 16, 'D3': 5, 'D4': 4, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 0,
|
||||
'D9': 2, 'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2},
|
||||
'd1_mini': {'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13,
|
||||
'D8': 15, 'LED': 2},
|
||||
'd1_mini_lite': 'd1_mini',
|
||||
'd1_mini_pro': 'd1_mini',
|
||||
'esp01': {},
|
||||
'esp01_1m': {},
|
||||
'esp07': {},
|
||||
'esp12e': {},
|
||||
'esp210': {},
|
||||
'esp8285': {},
|
||||
'esp_wroom_02': {},
|
||||
'espduino': {'LED': 16},
|
||||
'espectro': {'LED': 15, 'BUTTON': 2},
|
||||
'espino': {'LED': 2, 'LED_RED': 2, 'LED_GREEN': 4, 'LED_BLUE': 5, 'BUTTON': 0},
|
||||
'espinotee': {'LED': 16},
|
||||
'espresso_lite_v1': {'LED': 16},
|
||||
'espresso_lite_v2': {'LED': 2},
|
||||
'gen4iod': {},
|
||||
'heltec_wifi_kit_8': 'd1_mini',
|
||||
'huzzah': {'LED': 0},
|
||||
'modwifi': {},
|
||||
'nodemcu': {'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13,
|
||||
'D8': 15, 'D9': 3, 'D10': 1, 'LED': 16},
|
||||
'nodemcuv2': 'nodemcu',
|
||||
'oak': {'P0': 2, 'P1': 5, 'P2': 0, 'P3': 3, 'P4': 1, 'P5': 4, 'P6': 15, 'P7': 13, 'P8': 12,
|
||||
'P9': 14, 'P10': 16, 'P11': 17, 'LED': 5},
|
||||
'phoenix_v1': {'LED': 16},
|
||||
'phoenix_v2': {'LED': 2},
|
||||
'sparkfunBlynk': 'thing',
|
||||
'thing': {'LED': 5, 'SDA': 2, 'SCL': 14},
|
||||
'thingdev': 'thing',
|
||||
'wifi_slot': {'LED': 2},
|
||||
'wifiduino': {'D0': 3, 'D1': 1, 'D2': 2, 'D3': 0, 'D4': 4, 'D5': 5, 'D6': 16, 'D7': 14,
|
||||
'D8': 12, 'D9': 13, 'D10': 15, 'D11': 13, 'D12': 12, 'D13': 14},
|
||||
'wifinfo': {'LED': 12, 'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12,
|
||||
'D7': 13, 'D8': 15, 'D9': 3, 'D10': 1},
|
||||
'wio_link': {'LED': 2, 'GROVE': 15},
|
||||
'wio_node': 'nodemcu',
|
||||
'xinabox_cw01': {'SDA': 2, 'SCL': 14, 'LED': 5, 'LED_RED': 12, 'LED_GREEN': 13}
|
||||
}
|
||||
|
||||
ESP32_BASE_PINS = {
|
||||
'TX': 1, 'RX': 3, 'SDA': 21, 'SCL': 22, 'SS': 5, 'MOSI': 23, 'MISO': 19, 'SCK': 18, 'A0': 36,
|
||||
'A3': 39, 'A4': 32, 'A5': 33, 'A6': 34, 'A7': 35, 'A10': 4, 'A11': 0, 'A12': 2, 'A13': 15,
|
||||
'A14': 13, 'A15': 12, 'A16': 14, 'A17': 27, 'A18': 25, 'A19': 26, 'T0': 4, 'T1': 0, 'T2': 2,
|
||||
'T3': 15, 'T4': 12, 'T5': 12, 'T6': 14, 'T7': 27, 'T8': 33, 'T9': 32, 'DAC1': 25, 'DAC2': 26,
|
||||
'T3': 15, 'T4': 13, 'T5': 12, 'T6': 14, 'T7': 27, 'T8': 33, 'T9': 32, 'DAC1': 25, 'DAC2': 26,
|
||||
'SVP': 36, 'SVN': 39,
|
||||
}
|
||||
ESP32_NODEMCU_32S_PINS = dict(ESP32_PINS, **{
|
||||
'LED': 2,
|
||||
})
|
||||
ESP32_LOLIN32_PINS = dict(ESP32_PINS, **{
|
||||
'LED': 5
|
||||
})
|
||||
ESP32_BOARD_TO_PINS = {
|
||||
'nodemcu-32s': ESP32_NODEMCU_32S_PINS,
|
||||
'lolin32': ESP32_LOLIN32_PINS,
|
||||
|
||||
ESP32_BOARD_PINS = {
|
||||
'alksesp32': {'D0': 40, 'D1': 41, 'D2': 15, 'D3': 2, 'D4': 0, 'D5': 4, 'D6': 16, 'D7': 17,
|
||||
'D8': 5, 'D9': 18, 'D10': 19, 'D11': 21, 'D12': 22, 'D13': 23, 'A0': 32, 'A1': 33,
|
||||
'A2': 25, 'A3': 26, 'A4': 27, 'A5': 14, 'A6': 12, 'A7': 15, 'L_R': 22, 'L_G': 17,
|
||||
'L_Y': 23, 'L_B': 5, 'L_RGB_R': 4, 'L_RGB_G': 21, 'L_RGB_B': 16, 'SW1': 15,
|
||||
'SW2': 2, 'SW3': 0, 'POT1': 32, 'POT2': 33, 'PIEZO1': 19, 'PIEZO2': 18,
|
||||
'PHOTO': 25, 'DHT_PIN': 26, 'S1': 4, 'S2': 16, 'S3': 18, 'S4': 19, 'S5': 21,
|
||||
'SDA': 27, 'SCL': 14, 'SS': 19, 'MOSI': 21, 'MISO': 22, 'SCK': 23},
|
||||
'esp-wrover-kit': {},
|
||||
'esp32-evb': {'BUTTON': 34, 'SDA': 13, 'SCL': 16, 'SS': 17, 'MOSI': 2, 'MISO': 15, 'SCK': 14},
|
||||
'esp32-gateway': {'LED': 33, 'BUTTON': 34, 'SCL': 16, 'SDA': 17},
|
||||
'esp320': {'LED': 5, 'SDA': 2, 'SCL': 14, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14},
|
||||
'esp32dev': {},
|
||||
'esp32doit-devkit-v1': {'LED': 2},
|
||||
'esp32thing': {'LED': 5, 'BUTTON': 0, 'SS': 2},
|
||||
'esp32vn-iot-uno': {},
|
||||
'espea32': {'LED': 5, 'BUTTON': 0},
|
||||
'espectro32': {'LED': 15, 'SD_SS': 33},
|
||||
'espino32': {'LED': 16, 'BUTTON': 0},
|
||||
'featheresp32': {'LED': 13, 'TX': 17, 'RX': 16, 'SDA': 23, 'SS': 2, 'MOSI': 18, 'SCK': 5,
|
||||
'A0': 26, 'A1': 25, 'A2': 34, 'A4': 36, 'A5': 4, 'A6': 14, 'A7': 32, 'A8': 15,
|
||||
'A9': 33, 'A10': 27, 'A11': 12, 'A12': 13, 'A13': 35},
|
||||
'firebeetle32': {'LED': 2},
|
||||
'heltec_wifi_kit_32': {'LED': 25, 'BUTTON': 0, 'A1': 37, 'A2': 38},
|
||||
'heltec_wifi_lora_32': {'LED': 25, 'BUTTON': 0, 'SDA': 4, 'SCL': 15, 'SS': 18, 'MOSI': 27,
|
||||
'SCK': 5, 'A1': 37, 'A2': 38, 'T8': 32, 'T9': 33, 'DAC1': 26,
|
||||
'DAC2': 25, 'OLED_SCL': 15, 'OLED_SDA': 4, 'OLED_RST': 16,
|
||||
'LORA_SCK': 5, 'LORA_MOSI': 27, 'LORA_MISO': 19, 'LORA_CS': 18,
|
||||
'LORA_RST': 14, 'LORA_IRQ': 26},
|
||||
'hornbill32dev': {'LED': 13, 'BUTTON': 0},
|
||||
'hornbill32minima': {'SS': 2},
|
||||
'intorobot': {'LED': 4, 'LED_RED': 27, 'LED_GREEN': 21, 'LED_BLUE': 22,
|
||||
'BUTTON': 0, 'SDA': 23, 'SCL': 19, 'MOSI': 16, 'MISO': 17, 'A1': 39, 'A2': 35,
|
||||
'A3': 25, 'A4': 26, 'A5': 14, 'A6': 12, 'A7': 15, 'A8': 13, 'A9': 2, 'D0': 19,
|
||||
'D1': 23, 'D2': 18, 'D3': 17, 'D4': 16, 'D5': 5, 'D6': 4, 'T0': 19, 'T1': 23,
|
||||
'T2': 18, 'T3': 17, 'T4': 16, 'T5': 5, 'T6': 4},
|
||||
'lolin32': {'LED': 5},
|
||||
'lolin_d32': {'LED': 5, 'VBAT': 35},
|
||||
'lolin_d32_pro': {'LED': 5, 'VBAT': 35, 'TF_CS': 4, 'TS_CS': 12, 'TFT_CS': 14, 'TFT_LED': 32,
|
||||
'TFT_RST': 33, 'TFT_DC': 27},
|
||||
'm5stack-core-esp32': {'TXD2': 17, 'RXD2': 16, 'G23': 23, 'G19': 19, 'G18': 18, 'G3': 3,
|
||||
'G16': 16, 'G21': 21, 'G2': 2, 'G12': 12, 'G15': 15, 'G35': 35,
|
||||
'G36': 36, 'G25': 25, 'G26': 26, 'G1': 1, 'G17': 17, 'G22': 22, 'G5': 5,
|
||||
'G13': 13, 'G0': 0, 'G34': 34, 'ADC1': 35, 'ADC2': 36},
|
||||
'm5stack-fire': {'G23': 23, 'G19': 19, 'G18': 18, 'G3': 3, 'G16': 16, 'G21': 21, 'G2': 2,
|
||||
'G12': 12, 'G15': 15, 'G35': 35, 'G36': 36, 'G25': 25, 'G26': 26, 'G1': 1,
|
||||
'G17': 17, 'G22': 22, 'G5': 5, 'G13': 13, 'G0': 0, 'G34': 34, 'ADC1': 35,
|
||||
'ADC2': 36},
|
||||
'mhetesp32devkit': {'LED': 2},
|
||||
'mhetesp32minikit': {'LED': 2},
|
||||
'microduino-core-esp32': {'SDA': 22, 'SCL': 21, 'SDA1': 12, 'SCL1': 13, 'A0': 12, 'A1': 13,
|
||||
'A2': 15, 'A3': 4, 'A6': 38, 'A7': 37, 'A8': 32, 'A9': 33, 'A10': 25,
|
||||
'A11': 26, 'A12': 27, 'A13': 14, 'D0': 3, 'D1': 1, 'D2': 16, 'D3': 17,
|
||||
'D4': 32, 'D5': 33, 'D6': 25, 'D7': 26, 'D8': 27, 'D9': 14, 'D10': 5,
|
||||
'D11': 23, 'D12': 19, 'D13': 18, 'D14': 12, 'D15': 13, 'D16': 15,
|
||||
'D17': 4, 'D18': 22, 'D19': 21, 'D20': 38, 'D21': 37},
|
||||
'nano32': {'LED': 16, 'BUTTON': 0},
|
||||
'nina_w10': {'LED_GREEN': 33, 'LED_RED': 23, 'LED_BLUE': 21, 'SW1': 33, 'SW2': 27, 'SDA': 12,
|
||||
'SCL': 13, 'D0': 3, 'D1': 1, 'D2': 26, 'D3': 25, 'D4': 35, 'D5': 27, 'D6': 22,
|
||||
'D7': 0, 'D8': 15, 'D9': 14, 'D10': 5, 'D11': 19, 'D12': 23, 'D13': 18, 'D14': 13,
|
||||
'D15': 12, 'D16': 32, 'D17': 33, 'D18': 21, 'D19': 34, 'D20': 36, 'D21': 39},
|
||||
'node32s': {},
|
||||
'nodemcu-32s': {'LED': 2, 'BUTTON': 0},
|
||||
'odroid_esp32': {'LED': 2, 'SDA': 15, 'SCL': 4, 'SS': 22, 'ADC1': 35, 'ADC2': 36},
|
||||
'onehorse32dev': {'LED': 5, 'BUTTON': 0, 'A1': 37, 'A2': 38},
|
||||
'pico32': {},
|
||||
'pocket_32': {'LED': 16},
|
||||
'quantum': {},
|
||||
'ttgo-lora32-v1': {'LED': 2, 'BUTTON': 0, 'SS': 18, 'MOSI': 27, 'SCK': 5, 'A1': 37, 'A2': 38,
|
||||
'T8': 32, 'T9': 33, 'DAC1': 26, 'DAC2': 25, 'OLED_SDA': 4, 'OLED_SCL': 15,
|
||||
'OLED_RST': 16, 'LORA_SCK': 5, 'LORA_MISO': 19, 'LORA_MOSI': 27,
|
||||
'LORA_CS': 18, 'LORA_RST': 14, 'LORA_IRQ': 26},
|
||||
'wemosbat': 'pocket_32',
|
||||
'widora-air': {'LED': 25, 'BUTTON': 0, 'SDA': 23, 'SCL': 19, 'MOSI': 16, 'MISO': 17, 'A1': 39,
|
||||
'A2': 35, 'A3': 25, 'A4': 26, 'A5': 14, 'A6': 12, 'A7': 15, 'A8': 13, 'A9': 2,
|
||||
'D0': 19, 'D1': 23, 'D2': 18, 'D3': 17, 'D4': 16, 'D5': 5, 'D6': 4, 'T0': 19,
|
||||
'T1': 23, 'T2': 18, 'T3': 17, 'T4': 16, 'T5': 5, 'T6': 4},
|
||||
'xinabox_cw02': {'LED': 27},
|
||||
}
|
||||
|
||||
|
||||
def _lookup_pin(platform, board, value):
|
||||
if platform == ESP_PLATFORM_ESP8266:
|
||||
board_pins = ESP8266_BOARD_PINS.get(board, {})
|
||||
base_pins = ESP8266_BASE_PINS
|
||||
elif platform == ESP_PLATFORM_ESP32:
|
||||
board_pins = ESP32_BOARD_PINS.get(board, {})
|
||||
base_pins = ESP32_BASE_PINS
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
if isinstance(board_pins, str):
|
||||
return _lookup_pin(platform, board_pins, value)
|
||||
if value in board_pins:
|
||||
return board_pins[value]
|
||||
if value in base_pins:
|
||||
return base_pins[value]
|
||||
raise vol.Invalid(u"Can't find internal pin number for {}.".format(value))
|
||||
|
||||
|
||||
def _translate_pin(value):
|
||||
if isinstance(value, dict) or value is None:
|
||||
raise vol.Invalid(u"This option doesn't allow more complicated options like inverted.")
|
||||
raise vol.Invalid(u"This variable only supports pin numbers, not full pin schemas "
|
||||
u"(with inverted and mode).")
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
try:
|
||||
@@ -73,27 +178,7 @@ def _translate_pin(value):
|
||||
pass
|
||||
if value.startswith('GPIO'):
|
||||
return vol.Coerce(int)(value[len('GPIO'):].strip())
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
if value in ESP32_PINS:
|
||||
return ESP32_PINS[value]
|
||||
if core.BOARD not in ESP32_BOARD_TO_PINS:
|
||||
raise vol.Invalid(u"ESP32: Unknown board {} with unknown "
|
||||
u"pin {}.".format(core.BOARD, value))
|
||||
if value not in ESP32_BOARD_TO_PINS[core.BOARD]:
|
||||
raise vol.Invalid(u"ESP32: Board {} doesn't have "
|
||||
u"pin {}".format(core.BOARD, value))
|
||||
return ESP32_BOARD_TO_PINS[core.BOARD][value]
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
if value in ESP8266_PINS:
|
||||
return ESP8266_PINS[value]
|
||||
if core.BOARD not in ESP8266_BOARD_TO_PINS:
|
||||
raise vol.Invalid(u"ESP8266: Unknown board {} with unknown "
|
||||
u"pin {}.".format(core.BOARD, value))
|
||||
if value not in ESP8266_BOARD_TO_PINS[core.BOARD]:
|
||||
raise vol.Invalid(u"ESP8266: Board {} doesn't have "
|
||||
u"pin {}".format(core.BOARD, value))
|
||||
return ESP8266_BOARD_TO_PINS[core.BOARD][value]
|
||||
raise vol.Invalid(u"Invalid ESP platform.")
|
||||
return _lookup_pin(core.ESP_PLATFORM, core.BOARD, value)
|
||||
|
||||
|
||||
def validate_gpio_pin(value):
|
||||
@@ -171,9 +256,9 @@ PIN_MODES_ESP32 = [
|
||||
def pin_mode(value):
|
||||
value = vol.All(vol.Coerce(str), vol.Upper)(value)
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
return vol.Any(*PIN_MODES_ESP32)(value)
|
||||
return cv.one_of(*PIN_MODES_ESP32)(value)
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
return vol.Any(*PIN_MODES_ESP8266)(value)
|
||||
return cv.one_of(*PIN_MODES_ESP8266)(value)
|
||||
raise vol.Invalid(u"Invalid ESP platform.")
|
||||
|
||||
|
||||
@@ -201,20 +286,40 @@ def shorthand_input_pin(value):
|
||||
|
||||
|
||||
PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PCF8574): cv.variable_id,
|
||||
vol.Required(CONF_PCF8574): cv.use_variable_id(pcf8574.PCF8574Component),
|
||||
vol.Required(CONF_NUMBER): vol.Coerce(int),
|
||||
vol.Optional(CONF_MODE): vol.All(vol.Upper, "OUTPUT"),
|
||||
vol.Optional(CONF_MODE): vol.All(vol.Upper, cv.one_of("OUTPUT")),
|
||||
vol.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
})
|
||||
|
||||
PCF8574_INPUT_PIN_SCHEMA = PCF8574_OUTPUT_PIN_SCHEMA.extend({
|
||||
vol.Optional(CONF_MODE): vol.All(vol.Upper, vol.Any("INPUT", "INPUT_PULLUP")),
|
||||
vol.Optional(CONF_MODE): vol.All(vol.Upper, cv.one_of("INPUT", "INPUT_PULLUP")),
|
||||
})
|
||||
|
||||
GPIO_INTERNAL_OUTPUT_PIN_SCHEMA = vol.Any(shorthand_output_pin, GPIO_FULL_OUTPUT_PIN_SCHEMA)
|
||||
|
||||
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(PCF8574_OUTPUT_PIN_SCHEMA, GPIO_INTERNAL_OUTPUT_PIN_SCHEMA)
|
||||
def internal_gpio_output_pin_schema(value):
|
||||
if isinstance(value, dict):
|
||||
return GPIO_FULL_OUTPUT_PIN_SCHEMA(value)
|
||||
return shorthand_output_pin(value)
|
||||
|
||||
GPIO_INTERNAL_INPUT_PIN_SCHEMA = vol.Any(shorthand_input_pin, GPIO_FULL_INPUT_PIN_SCHEMA)
|
||||
|
||||
GPIO_INPUT_PIN_SCHEMA = vol.Any(PCF8574_INPUT_PIN_SCHEMA, GPIO_INTERNAL_INPUT_PIN_SCHEMA)
|
||||
def gpio_output_pin_schema(value):
|
||||
if isinstance(value, dict):
|
||||
if CONF_PCF8574 in value:
|
||||
return PCF8574_OUTPUT_PIN_SCHEMA(value)
|
||||
return GPIO_FULL_OUTPUT_PIN_SCHEMA(value)
|
||||
return shorthand_output_pin(value)
|
||||
|
||||
|
||||
def internal_gpio_input_pin_schema(value):
|
||||
if isinstance(value, dict):
|
||||
return GPIO_FULL_INPUT_PIN_SCHEMA(value)
|
||||
return shorthand_input_pin(value)
|
||||
|
||||
|
||||
def gpio_input_pin_schema(value):
|
||||
if isinstance(value, dict):
|
||||
if CONF_PCF8574 in value:
|
||||
return PCF8574_INPUT_PIN_SCHEMA(value)
|
||||
return GPIO_FULL_INPUT_PIN_SCHEMA(value)
|
||||
return shorthand_input_pin(value)
|
||||
|
||||
12
esphomeyaml/platformio.ini
Normal file
12
esphomeyaml/platformio.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
; This file allows the docker build file to install the required platformio
|
||||
; platforms
|
||||
|
||||
[env:espressif8266]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
|
||||
[env:espressif32]
|
||||
platform = espressif32
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
32
esphomeyaml/util.py
Normal file
32
esphomeyaml/util.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
class Registry(dict):
|
||||
def register(self, name):
|
||||
def decorator(fun):
|
||||
self[name] = fun
|
||||
return fun
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class ServiceRegistry(dict):
|
||||
def register(self, name, validator):
|
||||
def decorator(fun):
|
||||
self[name] = (validator, fun)
|
||||
return fun
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def safe_print(message=""):
|
||||
try:
|
||||
print(message)
|
||||
return
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
print(message.encode('ascii', 'backslashreplace'))
|
||||
except UnicodeEncodeError:
|
||||
print("Cannot print line because of invalid locale!")
|
||||
@@ -6,14 +6,14 @@ import unicodedata
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import mqtt
|
||||
from esphomeyaml.const import ESP_BOARDS_FOR_PLATFORM, ESP_PLATFORMS, ESP_PLATFORM_ESP32, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.helpers import color
|
||||
|
||||
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
CORE_BIG = """ _____ ____ _____ ______
|
||||
/ ____/ __ \| __ \| ____|
|
||||
| | | | | | |__) | |__
|
||||
@@ -51,7 +51,7 @@ OTA_BIG = """ ____ _______
|
||||
"""
|
||||
|
||||
# TODO handle escaping
|
||||
BASE_CONFIG = """esphomeyaml:
|
||||
BASE_CONFIG = u"""esphomeyaml:
|
||||
name: {name}
|
||||
platform: {platform}
|
||||
board: {board}
|
||||
@@ -70,6 +70,18 @@ logger:
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def wizard_file(**kwargs):
|
||||
config = BASE_CONFIG.format(**kwargs)
|
||||
|
||||
if kwargs['ota_password']:
|
||||
config += u"ota:\n password: '{}'\n".format(kwargs['ota_password'])
|
||||
else:
|
||||
config += u"ota:\n"
|
||||
|
||||
return config
|
||||
|
||||
|
||||
if os.getenv('ESPHOMEYAML_QUICKWIZARD', False):
|
||||
def sleep(time):
|
||||
pass
|
||||
@@ -77,18 +89,18 @@ else:
|
||||
from time import sleep
|
||||
|
||||
|
||||
def print_step(step, big):
|
||||
print()
|
||||
print()
|
||||
print("============= STEP {} =============".format(step))
|
||||
print(big)
|
||||
print("===================================")
|
||||
def safe_print_step(step, big):
|
||||
safe_print()
|
||||
safe_print()
|
||||
safe_print("============= STEP {} =============".format(step))
|
||||
safe_print(big)
|
||||
safe_print("===================================")
|
||||
sleep(0.25)
|
||||
|
||||
|
||||
def default_input(text, default):
|
||||
print()
|
||||
print("Press ENTER for default ({})".format(default))
|
||||
safe_print()
|
||||
safe_print(u"Press ENTER for default ({})".format(default))
|
||||
return raw_input(text.format(default)) or default
|
||||
|
||||
|
||||
@@ -100,30 +112,30 @@ def strip_accents(string):
|
||||
|
||||
def wizard(path):
|
||||
if not path.endswith('.yaml') and not path.endswith('.yml'):
|
||||
print("Please make your configuration file {} have the extension .yaml or .yml".format(
|
||||
color('cyan', path)))
|
||||
safe_print(u"Please make your configuration file {} have the extension .yaml or .yml"
|
||||
u"".format(color('cyan', path)))
|
||||
return 1
|
||||
if os.path.exists(path):
|
||||
print("Uh oh, it seems like {} already exists, please delete that file first "
|
||||
"or chose another configuration file.".format(color('cyan', path)))
|
||||
safe_print(u"Uh oh, it seems like {} already exists, please delete that file first "
|
||||
u"or chose another configuration file.".format(color('cyan', path)))
|
||||
return 1
|
||||
print("Hi there!")
|
||||
safe_print("Hi there!")
|
||||
sleep(1.5)
|
||||
print("I'm the wizard of esphomeyaml :)")
|
||||
safe_print("I'm the wizard of esphomeyaml :)")
|
||||
sleep(1.25)
|
||||
print("And I'm here to help you get started with esphomeyaml.")
|
||||
safe_print("And I'm here to help you get started with esphomeyaml.")
|
||||
sleep(2.0)
|
||||
print("In 5 steps I'm going to guide you through creating a basic "
|
||||
"configuration file for your custom ESP8266/ESP32 firmware. Yay!")
|
||||
safe_print("In 5 steps I'm going to guide you through creating a basic "
|
||||
"configuration file for your custom ESP8266/ESP32 firmware. Yay!")
|
||||
sleep(3.0)
|
||||
print()
|
||||
print_step(1, CORE_BIG)
|
||||
print("First up, please choose a " + color('green', 'name') + " for your node.")
|
||||
print("It should be a unique name that can be used to identify the device later.")
|
||||
safe_print()
|
||||
safe_print_step(1, CORE_BIG)
|
||||
safe_print("First up, please choose a " + color('green', 'name') + " for your node.")
|
||||
safe_print("It should be a unique name that can be used to identify the device later.")
|
||||
sleep(1)
|
||||
print("For example, I like calling the node in my living room {}.".format(
|
||||
safe_print("For example, I like calling the node in my living room {}.".format(
|
||||
color('bold_white', "livingroom")))
|
||||
print()
|
||||
safe_print()
|
||||
sleep(1)
|
||||
name = raw_input(color("bold_white", "(name): "))
|
||||
while True:
|
||||
@@ -131,34 +143,34 @@ def wizard(path):
|
||||
name = cv.valid_name(name)
|
||||
break
|
||||
except vol.Invalid:
|
||||
print(color("red", "Oh noes, \"{}\" isn't a valid name. Names can only include "
|
||||
"numbers, letters and underscores.".format(name)))
|
||||
safe_print(color("red", u"Oh noes, \"{}\" isn't a valid name. Names can only include "
|
||||
u"numbers, letters and underscores.".format(name)))
|
||||
name = strip_accents(name).replace(' ', '_')
|
||||
name = u''.join(c for c in name if c in cv.ALLOWED_NAME_CHARS)
|
||||
print("Shall I use \"{}\" as the name instead?".format(color('cyan', name)))
|
||||
safe_print(u"Shall I use \"{}\" as the name instead?".format(color('cyan', name)))
|
||||
sleep(0.5)
|
||||
name = default_input("(name [{}]): ", name)
|
||||
name = default_input(u"(name [{}]): ", name)
|
||||
|
||||
print("Great! Your node is now called \"{}\".".format(color('cyan', name)))
|
||||
safe_print(u"Great! Your node is now called \"{}\".".format(color('cyan', name)))
|
||||
sleep(1)
|
||||
print_step(2, ESP_BIG)
|
||||
print("Now I'd like to know what microcontroller you're using so that I can compile "
|
||||
"firmwares for it.")
|
||||
print("Are you using an " + color('green', 'ESP32') + " or " +
|
||||
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
|
||||
safe_print_step(2, ESP_BIG)
|
||||
safe_print("Now I'd like to know what microcontroller you're using so that I can compile "
|
||||
"firmwares for it.")
|
||||
safe_print("Are you using an " + color('green', 'ESP32') + " or " +
|
||||
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
|
||||
while True:
|
||||
sleep(0.5)
|
||||
print()
|
||||
print("Please enter either ESP32 or ESP8266.")
|
||||
safe_print()
|
||||
safe_print("Please enter either ESP32 or ESP8266.")
|
||||
platform = raw_input(color("bold_white", "(ESP32/ESP8266): "))
|
||||
try:
|
||||
platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform)
|
||||
break
|
||||
except vol.Invalid:
|
||||
print("Unfortunately, I can't find an espressif microcontroller called "
|
||||
"\"{}\". Please try again.".format(platform))
|
||||
print("Thanks! You've chosen {} as your platform.".format(color('cyan', platform)))
|
||||
print()
|
||||
safe_print(u"Unfortunately, I can't find an espressif microcontroller called "
|
||||
u"\"{}\". Please try again.".format(platform))
|
||||
safe_print(u"Thanks! You've chosen {} as your platform.".format(color('cyan', platform)))
|
||||
safe_print()
|
||||
sleep(1)
|
||||
|
||||
if platform == ESP_PLATFORM_ESP32:
|
||||
@@ -166,89 +178,92 @@ def wizard(path):
|
||||
else:
|
||||
board_link = 'http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards'
|
||||
|
||||
print("Next, I need to know what " + color('green', 'board') + " you're using.")
|
||||
safe_print("Next, I need to know what " + color('green', 'board') + " you're using.")
|
||||
sleep(0.5)
|
||||
print("Please go to {} and choose a board.".format(color('green', board_link)))
|
||||
safe_print("Please go to {} and choose a board.".format(color('green', board_link)))
|
||||
if platform == ESP_PLATFORM_ESP8266:
|
||||
print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
|
||||
print()
|
||||
safe_print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
|
||||
safe_print()
|
||||
# Don't sleep because user needs to copy link
|
||||
if platform == ESP_PLATFORM_ESP32:
|
||||
print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s')))
|
||||
safe_print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s')))
|
||||
boards = list(ESP32_BOARD_PINS.keys())
|
||||
else:
|
||||
print("For example \"{}\".".format(color("bold_white", 'nodemcuv2')))
|
||||
safe_print("For example \"{}\".".format(color("bold_white", 'nodemcuv2')))
|
||||
boards = list(ESP8266_BOARD_PINS.keys())
|
||||
while True:
|
||||
board = raw_input(color("bold_white", "(board): "))
|
||||
boards = ESP_BOARDS_FOR_PLATFORM[platform]
|
||||
try:
|
||||
board = vol.All(vol.Lower, vol.Any(*boards))(board)
|
||||
break
|
||||
except vol.Invalid:
|
||||
print(color('red', "Sorry, I don't think the board \"{}\" exists."))
|
||||
print()
|
||||
safe_print(color('red', "Sorry, I don't think the board \"{}\" exists."))
|
||||
safe_print()
|
||||
sleep(0.25)
|
||||
print("Possible options are {}".format(', '.join(boards)))
|
||||
print()
|
||||
safe_print("Possible options are {}".format(', '.join(boards)))
|
||||
safe_print()
|
||||
|
||||
print("Way to go! You've chosen {} as your board.".format(color('cyan', board)))
|
||||
print()
|
||||
safe_print(u"Way to go! You've chosen {} as your board.".format(color('cyan', board)))
|
||||
safe_print()
|
||||
sleep(1)
|
||||
|
||||
print_step(3, WIFI_BIG)
|
||||
print("In this step, I'm going to create the configuration for "
|
||||
"WiFi.")
|
||||
print()
|
||||
safe_print_step(3, WIFI_BIG)
|
||||
safe_print("In this step, I'm going to create the configuration for "
|
||||
"WiFi.")
|
||||
safe_print()
|
||||
sleep(1)
|
||||
print("First, what's the " + color('green', 'SSID') + " (the name) of the WiFi network {} "
|
||||
"I should connect to?".format(name))
|
||||
safe_print("First, what's the " + color('green', 'SSID') +
|
||||
u" (the name) of the WiFi network {} I should connect to?".format(name))
|
||||
sleep(1.5)
|
||||
print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
|
||||
safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
|
||||
while True:
|
||||
ssid = raw_input(color('bold_white', "(ssid): "))
|
||||
try:
|
||||
ssid = cv.ssid(ssid)
|
||||
break
|
||||
except vol.Invalid:
|
||||
print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
|
||||
"Please try again.".format(ssid)))
|
||||
print()
|
||||
safe_print(color('red', u"Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
|
||||
u"Please try again.".format(ssid)))
|
||||
safe_print()
|
||||
sleep(1)
|
||||
|
||||
print("Thank you very much! You've just chosen \"{}\" as your SSID.".format(
|
||||
color('cyan', ssid)))
|
||||
print()
|
||||
safe_print(u"Thank you very much! You've just chosen \"{}\" as your SSID."
|
||||
u"".format(color('cyan', ssid)))
|
||||
safe_print()
|
||||
sleep(0.75)
|
||||
|
||||
print("Now please state the " + color('green', 'password') +
|
||||
" of the WiFi network so that I can connect to it (Leave empty for no password)")
|
||||
print()
|
||||
print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
|
||||
safe_print("Now please state the " + color('green', 'password') +
|
||||
" of the WiFi network so that I can connect to it (Leave empty for no password)")
|
||||
safe_print()
|
||||
safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
|
||||
sleep(0.5)
|
||||
psk = raw_input(color('bold_white', '(PSK): '))
|
||||
print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
|
||||
safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
|
||||
sleep(1.5)
|
||||
|
||||
print_step(4, MQTT_BIG)
|
||||
print("Almost there! Now let's setup MQTT so that your node can connect to the outside world.")
|
||||
print()
|
||||
safe_print_step(4, MQTT_BIG)
|
||||
safe_print("Almost there! Now let's setup MQTT so that your node can connect to the "
|
||||
"outside world.")
|
||||
safe_print()
|
||||
sleep(1)
|
||||
print("Please enter the " + color('green', 'address') + " of your MQTT broker.")
|
||||
print()
|
||||
print("For example \"{}\".".format(color('bold_white', '192.168.178.84')))
|
||||
safe_print("Please enter the " + color('green', 'address') + " of your MQTT broker.")
|
||||
safe_print()
|
||||
safe_print("For example \"{}\".".format(color('bold_white', '192.168.178.84')))
|
||||
while True:
|
||||
broker = raw_input(color('bold_white', "(broker): "))
|
||||
try:
|
||||
broker = mqtt.validate_broker(broker)
|
||||
break
|
||||
except vol.Invalid as err:
|
||||
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
|
||||
broker, err)))
|
||||
print("Please try again.")
|
||||
print()
|
||||
safe_print(color('red', u"The broker address \"{}\" seems to be invalid: {} :("
|
||||
u"".format(broker, err)))
|
||||
safe_print("Please try again.")
|
||||
safe_print()
|
||||
sleep(1)
|
||||
|
||||
print("Thanks! Now enter the " + color('green', 'username') + " and " +
|
||||
color('green', 'password') + " for the MQTT broker. Leave empty for no authentication.")
|
||||
safe_print("Thanks! Now enter the " + color('green', 'username') + " and " +
|
||||
color('green', 'password') +
|
||||
" for the MQTT broker. Leave empty for no authentication.")
|
||||
mqtt_username = raw_input(color('bold_white', '(username): '))
|
||||
mqtt_password = ''
|
||||
if mqtt_username:
|
||||
@@ -257,46 +272,42 @@ def wizard(path):
|
||||
show = '*' * len(mqtt_password)
|
||||
if len(mqtt_password) >= 2:
|
||||
show = mqtt_password[:2] + '*' * len(mqtt_password)
|
||||
print("MQTT Username: \"{}\"; Password: \"{}\"".format(
|
||||
color('cyan', mqtt_username), color('cyan', show)))
|
||||
safe_print(u"MQTT Username: \"{}\"; Password: \"{}\""
|
||||
u"".format(color('cyan', mqtt_username), color('cyan', show)))
|
||||
else:
|
||||
print("No authentication for MQTT")
|
||||
safe_print("No authentication for MQTT")
|
||||
|
||||
print_step(5, OTA_BIG)
|
||||
print("Last step! esphomeyaml can automatically upload custom firmwares over WiFi "
|
||||
"(over the air).")
|
||||
print("This can be insecure if you do not trust the WiFi network. Do you want to set "
|
||||
"an " + color('green', 'OTA password') + " for remote updates?")
|
||||
print()
|
||||
safe_print_step(5, OTA_BIG)
|
||||
safe_print("Last step! esphomeyaml can automatically upload custom firmwares over WiFi "
|
||||
"(over the air).")
|
||||
safe_print("This can be insecure if you do not trust the WiFi network. Do you want to set "
|
||||
"an " + color('green', 'OTA password') + " for remote updates?")
|
||||
safe_print()
|
||||
sleep(0.25)
|
||||
print("Press ENTER for no password")
|
||||
safe_print("Press ENTER for no password")
|
||||
ota_password = raw_input(color('bold_white', '(password): '))
|
||||
|
||||
config = BASE_CONFIG.format(name=name, platform=platform, board=board,
|
||||
ssid=ssid, psk=psk, broker=broker,
|
||||
mqtt_username=mqtt_username, mqtt_password=mqtt_password)
|
||||
|
||||
if ota_password:
|
||||
config += "ota:\n password: '{}'\n".format(ota_password)
|
||||
else:
|
||||
config += "ota:\n"
|
||||
config = wizard_file(name=name, platform=platform, board=board,
|
||||
ssid=ssid, psk=psk, broker=broker,
|
||||
mqtt_username=mqtt_username, mqtt_password=mqtt_password,
|
||||
ota_password=ota_password)
|
||||
|
||||
with codecs.open(path, 'w') as f_handle:
|
||||
f_handle.write(config)
|
||||
|
||||
print()
|
||||
print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
||||
color('bold_cyan', path))
|
||||
print()
|
||||
print("Next steps:")
|
||||
print(" > If you haven't already, enable MQTT discovery in Home Assistant:")
|
||||
print()
|
||||
print(color('bold_white', "# In your configuration.yaml"))
|
||||
print(color('bold_white', "mqtt:"))
|
||||
print(color('bold_white', " broker: {}".format(broker)))
|
||||
print(color('bold_white', " # ..."))
|
||||
print(color('bold_white', " discovery: True"))
|
||||
print()
|
||||
print(" > Then follow the rest of the getting started guide:")
|
||||
print(" > https://esphomelib.com/esphomeyaml/getting-started.html")
|
||||
safe_print()
|
||||
safe_print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
||||
color('bold_cyan', path))
|
||||
safe_print()
|
||||
safe_print("Next steps:")
|
||||
safe_print(" > If you haven't already, enable MQTT discovery in Home Assistant:")
|
||||
safe_print()
|
||||
safe_print(color('bold_white', "# In your configuration.yaml"))
|
||||
safe_print(color('bold_white', "mqtt:"))
|
||||
safe_print(color('bold_white', u" broker: {}".format(broker)))
|
||||
safe_print(color('bold_white', " # ..."))
|
||||
safe_print(color('bold_white', " discovery: True"))
|
||||
safe_print()
|
||||
safe_print(" > Then follow the rest of the getting started guide:")
|
||||
safe_print(" > https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html")
|
||||
return 0
|
||||
|
||||
@@ -2,14 +2,21 @@ from __future__ import print_function
|
||||
|
||||
import codecs
|
||||
import errno
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.config import iter_components
|
||||
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
|
||||
CONF_LIBRARY_URI, \
|
||||
CONF_NAME, CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, CONF_ARDUINO_VERSION, CONF_BOARD, \
|
||||
CONF_BOARD_FLASH_MODE, CONF_ESPHOMELIB_VERSION, CONF_ESPHOMEYAML, CONF_LOCAL, CONF_NAME, \
|
||||
CONF_USE_CUSTOM_CODE, ESP_PLATFORM_ESP32, CONF_REPOSITORY, CONF_COMMIT, CONF_BRANCH, CONF_TAG
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.core_config import VERSION_REGEX
|
||||
from esphomeyaml.helpers import relative_path
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
|
||||
CPP_AUTO_GENERATE_END = u'// =========== AUTO GENERATED CODE END ============'
|
||||
@@ -57,11 +64,11 @@ lib_deps =
|
||||
build_flags =
|
||||
{build_flags}
|
||||
${{common.build_flags}}
|
||||
upload_speed = {upload_speed}
|
||||
"""
|
||||
|
||||
PLATFORM_TO_PLATFORMIO = {
|
||||
ESP_PLATFORM_ESP32: 'espressif32',
|
||||
ESP_PLATFORM_ESP8266: 'espressif8266'
|
||||
UPLOAD_SPEED_OVERRIDE = {
|
||||
'esp210': 57600,
|
||||
}
|
||||
|
||||
|
||||
@@ -81,21 +88,21 @@ def get_build_flags(config, key):
|
||||
return build_flags
|
||||
|
||||
|
||||
def get_ini_content(config):
|
||||
platform = config[CONF_ESPHOMEYAML][CONF_PLATFORM]
|
||||
if platform in PLATFORM_TO_PLATFORMIO:
|
||||
platform = PLATFORM_TO_PLATFORMIO[platform]
|
||||
def get_ini_content(config, path):
|
||||
version_specific_settings = determine_platformio_version_settings()
|
||||
options = {
|
||||
u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
|
||||
u'platform': platform,
|
||||
u'platform': config[CONF_ESPHOMEYAML][CONF_ARDUINO_VERSION],
|
||||
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
|
||||
u'build_flags': u'',
|
||||
u'upload_speed': UPLOAD_SPEED_OVERRIDE.get(core.BOARD, 115200),
|
||||
}
|
||||
build_flags = set()
|
||||
if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]:
|
||||
if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
|
||||
build_flags |= get_build_flags(config, 'build_flags')
|
||||
build_flags |= get_build_flags(config, 'BUILD_FLAGS')
|
||||
build_flags.add(u"-DESPHOMEYAML_USE")
|
||||
build_flags.add("-Wno-unused-variable")
|
||||
build_flags |= get_build_flags(config, 'required_build_flags')
|
||||
build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS')
|
||||
|
||||
@@ -105,13 +112,74 @@ def get_ini_content(config):
|
||||
options[u'build_flags'] = u'\n '.join(build_flags)
|
||||
|
||||
lib_deps = set()
|
||||
lib_deps.add(config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI])
|
||||
|
||||
lib_version = config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION]
|
||||
lib_path = os.path.join(path, 'lib')
|
||||
dst_path = os.path.join(lib_path, 'esphomelib')
|
||||
this_version = None
|
||||
if CONF_REPOSITORY in lib_version:
|
||||
tag = next((lib_version[x] for x in (CONF_COMMIT, CONF_BRANCH, CONF_TAG)
|
||||
if x in lib_version), None)
|
||||
this_version = lib_version[CONF_REPOSITORY]
|
||||
if tag is not None:
|
||||
this_version += '#' + tag
|
||||
lib_deps.add(this_version)
|
||||
if os.path.islink(dst_path):
|
||||
os.unlink(dst_path)
|
||||
elif CONF_LOCAL in lib_version:
|
||||
this_version = lib_version[CONF_LOCAL]
|
||||
src_path = relative_path(this_version)
|
||||
do_write = True
|
||||
if os.path.islink(dst_path):
|
||||
old_path = os.path.join(os.readlink(dst_path), lib_path)
|
||||
if old_path != lib_path:
|
||||
os.unlink(dst_path)
|
||||
else:
|
||||
do_write = False
|
||||
if do_write:
|
||||
mkdir_p(lib_path)
|
||||
os.symlink(src_path, dst_path)
|
||||
|
||||
# Manually add lib_deps because platformio seems to ignore them inside libs/
|
||||
library_json_path = os.path.join(src_path, 'library.json')
|
||||
with codecs.open(library_json_path, 'r', encoding='utf-8') as f_handle:
|
||||
library_json_text = f_handle.read()
|
||||
|
||||
library_json = json.loads(library_json_text)
|
||||
for dep in library_json.get('dependencies', []):
|
||||
if 'version' in dep and VERSION_REGEX.match(dep['version']) is not None:
|
||||
lib_deps.add(dep['name'] + '@' + dep['version'])
|
||||
else:
|
||||
lib_deps.add(dep['version'])
|
||||
else:
|
||||
this_version = lib_version
|
||||
lib_deps.add(lib_version)
|
||||
|
||||
version_file = os.path.join(path, '.esphomelib_version')
|
||||
version = None
|
||||
if os.path.isfile(version_file):
|
||||
with open(version_file, 'r') as ver_f:
|
||||
version = ver_f.read()
|
||||
|
||||
if version != this_version:
|
||||
_LOGGER.info("Esphomelib version change detected. Cleaning build files...")
|
||||
try:
|
||||
clean_build(path)
|
||||
except OSError as err:
|
||||
_LOGGER.warn("Error deleting build files (%s)! Ignoring...", err)
|
||||
|
||||
with open(version_file, 'w') as ver_f:
|
||||
ver_f.write(this_version)
|
||||
|
||||
lib_deps |= get_build_flags(config, 'LIB_DEPS')
|
||||
lib_deps |= get_build_flags(config, 'lib_deps')
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
lib_deps |= {
|
||||
'Preferences', # Preferences helper
|
||||
}
|
||||
# Manual fix for AsyncTCP
|
||||
if config[CONF_ESPHOMEYAML].get(CONF_ARDUINO_VERSION) == ARDUINO_VERSION_ESP32_DEV:
|
||||
lib_deps.add('https://github.com/me-no-dev/AsyncTCP.git#idf-update')
|
||||
# avoid changing build flags order
|
||||
lib_deps = sorted(x for x in lib_deps if x)
|
||||
if lib_deps:
|
||||
@@ -119,8 +187,9 @@ def get_ini_content(config):
|
||||
|
||||
content = INI_CONTENT_FORMAT.format(**options)
|
||||
if CONF_BOARD_FLASH_MODE in config[CONF_ESPHOMEYAML]:
|
||||
flash_mode_key = version_specific_settings['flash_mode_key']
|
||||
flash_mode = config[CONF_ESPHOMEYAML][CONF_BOARD_FLASH_MODE]
|
||||
content += "board_flash_mode = {}\n".format(flash_mode)
|
||||
content += "{} = {}\n".format(flash_mode_key, flash_mode)
|
||||
return content
|
||||
|
||||
|
||||
@@ -164,16 +233,34 @@ def write_platformio_ini(content, path):
|
||||
content_format = find_begin_end(text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END)
|
||||
else:
|
||||
prev_file = None
|
||||
mkdir_p(os.path.dirname(path))
|
||||
content_format = INI_BASE_FORMAT
|
||||
full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \
|
||||
content + INI_AUTO_GENERATE_END + content_format[1]
|
||||
content + INI_AUTO_GENERATE_END + content_format[1]
|
||||
if prev_file == full_file:
|
||||
return
|
||||
with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
|
||||
f_handle.write(full_file)
|
||||
|
||||
|
||||
def write_platformio_project(config, path):
|
||||
mkdir_p(path)
|
||||
platformio_ini = os.path.join(path, 'platformio.ini')
|
||||
content = get_ini_content(config, path)
|
||||
if 'esp32_ble_beacon' in config or 'esp32_ble_tracker' in config:
|
||||
content += 'board_build.partitions = partitions.csv\n'
|
||||
partitions_csv = os.path.join(path, 'partitions.csv')
|
||||
if not os.path.isfile(partitions_csv):
|
||||
mkdir_p(path)
|
||||
with open(partitions_csv, "w") as f:
|
||||
f.write("nvs, data, nvs, 0x009000, 0x005000,\n")
|
||||
f.write("otadata, data, ota, 0x00e000, 0x002000,\n")
|
||||
f.write("app0, app, ota_0, 0x010000, 0x190000,\n")
|
||||
f.write("app1, app, ota_1, 0x200000, 0x190000,\n")
|
||||
f.write("eeprom, data, 0x99, 0x390000, 0x001000,\n")
|
||||
f.write("spiffs, data, spiffs, 0x391000, 0x00F000\n")
|
||||
write_platformio_ini(content, platformio_ini)
|
||||
|
||||
|
||||
def write_cpp(code_s, path):
|
||||
if os.path.isfile(path):
|
||||
try:
|
||||
@@ -189,8 +276,29 @@ def write_cpp(code_s, path):
|
||||
code_format = CPP_BASE_FORMAT
|
||||
|
||||
full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \
|
||||
code_s + CPP_AUTO_GENERATE_END + code_format[1]
|
||||
code_s + CPP_AUTO_GENERATE_END + code_format[1]
|
||||
if prev_file == full_file:
|
||||
return
|
||||
with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
|
||||
f_handle.write(full_file)
|
||||
|
||||
|
||||
def determine_platformio_version_settings():
|
||||
import platformio
|
||||
|
||||
settings = {}
|
||||
|
||||
if platformio.VERSION < (3, 5, 3):
|
||||
settings['flash_mode_key'] = 'board_flash_mode'
|
||||
else:
|
||||
settings['flash_mode_key'] = 'board_build.flash_mode'
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def clean_build(build_path):
|
||||
for directory in ('.piolibdeps', '.pioenvs'):
|
||||
dir_path = os.path.join(build_path, directory)
|
||||
if os.path.isdir(dir_path):
|
||||
_LOGGER.info("Deleting %s", dir_path)
|
||||
shutil.rmtree(dir_path)
|
||||
|
||||
@@ -4,12 +4,14 @@ import codecs
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from collections import OrderedDict
|
||||
|
||||
import yaml
|
||||
import yaml.constructor
|
||||
|
||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, MACAddress, TimePeriod, \
|
||||
TimePeriodMicroseconds, TimePeriodMilliseconds, TimePeriodSeconds
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -48,8 +50,9 @@ def load_yaml(fname):
|
||||
with codecs.open(fname, encoding='utf-8') as conf_file:
|
||||
return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict()
|
||||
except yaml.YAMLError as exc:
|
||||
_LOGGER.error(exc)
|
||||
raise ESPHomeYAMLError(exc)
|
||||
except IOError as exc:
|
||||
raise ESPHomeYAMLError(u"Error accessing file {}: {}".format(fname, exc))
|
||||
except UnicodeDecodeError as exc:
|
||||
_LOGGER.error(u"Unable to read file %s: %s", fname, exc)
|
||||
raise ESPHomeYAMLError(exc)
|
||||
@@ -61,14 +64,83 @@ def dump(dict_):
|
||||
dict_, default_flow_style=False, allow_unicode=True)
|
||||
|
||||
|
||||
def custom_construct_pairs(loader, node):
|
||||
pairs = []
|
||||
for kv in node.value:
|
||||
if isinstance(kv, yaml.ScalarNode):
|
||||
obj = loader.construct_object(kv)
|
||||
if not isinstance(obj, dict):
|
||||
raise ESPHomeYAMLError(
|
||||
"Expected mapping for anchored include tag, got {}".format(type(obj)))
|
||||
for key, value in obj.iteritems():
|
||||
pairs.append((key, value))
|
||||
else:
|
||||
key_node, value_node = kv
|
||||
key = loader.construct_object(key_node)
|
||||
value = loader.construct_object(value_node)
|
||||
pairs.append((key, value))
|
||||
|
||||
return pairs
|
||||
|
||||
|
||||
def custom_flatten_mapping(loader, node):
|
||||
pre_merge = []
|
||||
post_merge = []
|
||||
index = 0
|
||||
while index < len(node.value):
|
||||
if isinstance(node.value[index], yaml.ScalarNode):
|
||||
index += 1
|
||||
continue
|
||||
|
||||
key_node, value_node = node.value[index]
|
||||
if key_node.tag == u'tag:yaml.org,2002:merge':
|
||||
del node.value[index]
|
||||
|
||||
if isinstance(value_node, yaml.MappingNode):
|
||||
custom_flatten_mapping(loader, value_node)
|
||||
node.value = node.value[:index] + value_node.value + node.value[index:]
|
||||
elif isinstance(value_node, yaml.SequenceNode):
|
||||
submerge = []
|
||||
for subnode in value_node.value:
|
||||
if not isinstance(subnode, yaml.MappingNode):
|
||||
raise yaml.constructor.ConstructorError(
|
||||
"while constructing a mapping", node.start_mark,
|
||||
"expected a mapping for merging, but found %{}".format(subnode.id),
|
||||
subnode.start_mark)
|
||||
custom_flatten_mapping(loader, subnode)
|
||||
submerge.append(subnode.value)
|
||||
# submerge.reverse()
|
||||
node.value = node.value[:index] + submerge + node.value[index:]
|
||||
elif isinstance(value_node, yaml.ScalarNode):
|
||||
node.value = node.value[:index] + [value_node] + node.value[index:]
|
||||
# post_merge.append(value_node)
|
||||
else:
|
||||
raise yaml.constructor.ConstructorError(
|
||||
"while constructing a mapping", node.start_mark,
|
||||
"expected a mapping or list of mappings for merging, "
|
||||
"but found {}".format(value_node.id), value_node.start_mark)
|
||||
elif key_node.tag == u'tag:yaml.org,2002:value':
|
||||
key_node.tag = u'tag:yaml.org,2002:str'
|
||||
index += 1
|
||||
else:
|
||||
index += 1
|
||||
if pre_merge:
|
||||
node.value = pre_merge + node.value
|
||||
if post_merge:
|
||||
node.value = node.value + post_merge
|
||||
|
||||
|
||||
def _ordered_dict(loader, node):
|
||||
"""Load YAML mappings into an ordered dictionary to preserve key order."""
|
||||
loader.flatten_mapping(node)
|
||||
nodes = loader.construct_pairs(node)
|
||||
custom_flatten_mapping(loader, node)
|
||||
nodes = custom_construct_pairs(loader, node)
|
||||
|
||||
seen = {}
|
||||
for (key, _), (child_node, _) in zip(nodes, node.value):
|
||||
line = child_node.start_mark.line
|
||||
for (key, _), nv in zip(nodes, node.value):
|
||||
if isinstance(nv, yaml.ScalarNode):
|
||||
line = nv.start_mark.line
|
||||
else:
|
||||
line = nv[0].start_mark.line
|
||||
|
||||
try:
|
||||
hash(key)
|
||||
@@ -195,6 +267,10 @@ def _secret_yaml(loader, node):
|
||||
return secrets[node.value]
|
||||
|
||||
|
||||
def _lambda(loader, node):
|
||||
return Lambda(unicode(node.value))
|
||||
|
||||
|
||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict)
|
||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
|
||||
yaml.SafeLoader.add_constructor('!env_var', _env_var_yaml)
|
||||
@@ -206,6 +282,7 @@ yaml.SafeLoader.add_constructor('!include_dir_merge_list',
|
||||
yaml.SafeLoader.add_constructor('!include_dir_named', _include_dir_named_yaml)
|
||||
yaml.SafeLoader.add_constructor('!include_dir_merge_named',
|
||||
_include_dir_merge_named_yaml)
|
||||
yaml.SafeLoader.add_constructor('!lambda', _lambda)
|
||||
|
||||
|
||||
# From: https://gist.github.com/miracle2k/3184458
|
||||
@@ -270,6 +347,19 @@ def represent_time_period(dumper, data):
|
||||
return represent_odict(dumper, 'tag:yaml.org,2002:map', dictionary)
|
||||
|
||||
|
||||
def represent_lambda(_, data):
|
||||
node = yaml.ScalarNode(tag='!lambda', value=data.value, style='>')
|
||||
return node
|
||||
|
||||
|
||||
def represent_id(_, data):
|
||||
return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=data.id)
|
||||
|
||||
|
||||
def represent_uuid(_, data):
|
||||
return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=str(data))
|
||||
|
||||
|
||||
yaml.SafeDumper.add_representer(
|
||||
OrderedDict,
|
||||
lambda dumper, value:
|
||||
@@ -286,7 +376,7 @@ yaml.SafeDumper.add_representer(unicode, unicode_representer)
|
||||
yaml.SafeDumper.add_representer(HexInt, hex_int_representer)
|
||||
yaml.SafeDumper.add_representer(IPAddress, stringify_representer)
|
||||
yaml.SafeDumper.add_representer(MACAddress, stringify_representer)
|
||||
yaml.SafeDumper.add_representer(TimePeriod, represent_time_period)
|
||||
yaml.SafeDumper.add_representer(TimePeriodMicroseconds, represent_time_period)
|
||||
yaml.SafeDumper.add_representer(TimePeriodMilliseconds, represent_time_period)
|
||||
yaml.SafeDumper.add_representer(TimePeriodSeconds, represent_time_period)
|
||||
yaml.SafeDumper.add_multi_representer(TimePeriod, represent_time_period)
|
||||
yaml.SafeDumper.add_multi_representer(Lambda, represent_lambda)
|
||||
yaml.SafeDumper.add_multi_representer(core.ID, represent_id)
|
||||
yaml.SafeDumper.add_multi_representer(uuid.UUID, represent_uuid)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Examples
|
||||
|
||||
This directory contains some of the ESP32/ESP8266 nodes I use at my home.
|
||||
@@ -1,127 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: cabinet
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.203
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: cabinet
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
power_supply:
|
||||
- id: 'atx'
|
||||
pin:
|
||||
number: 13
|
||||
inverted: true
|
||||
|
||||
i2c:
|
||||
sda: 14
|
||||
scl: 27
|
||||
frequency: 400000
|
||||
|
||||
pca9685:
|
||||
- id: 'pca9685'
|
||||
frequency: 500
|
||||
|
||||
output:
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'cabinet1_red'
|
||||
channel: 14
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'cabinet1_green'
|
||||
channel: 15
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'cabinet1_blue'
|
||||
channel: 13
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'cabinet2_red'
|
||||
channel: 11
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'cabinet2_green'
|
||||
channel: 12
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'cabinet2_blue'
|
||||
channel: 10
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'room_red'
|
||||
channel: 8
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'room_green'
|
||||
channel: 9
|
||||
power_supply: 'atx'
|
||||
- platform: pca9685
|
||||
pca9685_id: 'pca9685'
|
||||
id: 'room_blue'
|
||||
channel: 7
|
||||
power_supply: 'atx'
|
||||
|
||||
light:
|
||||
- platform: rgb
|
||||
name: 'Cabinet Light 1'
|
||||
red: 'cabinet1_red'
|
||||
green: 'cabinet1_green'
|
||||
blue: 'cabinet1_blue'
|
||||
- platform: rgb
|
||||
name: 'Cabinet Light 2'
|
||||
red: 'cabinet2_red'
|
||||
green: 'cabinet2_green'
|
||||
blue: 'cabinet2_blue'
|
||||
- platform: rgb
|
||||
name: 'Room Light'
|
||||
red: 'room_red'
|
||||
green: 'room_green'
|
||||
blue: 'room_blue'
|
||||
|
||||
sensor:
|
||||
- platform: dht
|
||||
pin: 23
|
||||
temperature:
|
||||
name: 'Cabinet Temperature'
|
||||
humidity:
|
||||
name: 'Cabinet Humidity'
|
||||
model: DHT22
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: 25
|
||||
name: 'Cabinet Motion'
|
||||
device_class: motion
|
||||
# Simple binary sensor that uses last will and birth messages to show
|
||||
# node state
|
||||
- platform: status
|
||||
name: "Cabinet Status"
|
||||
|
||||
switch:
|
||||
# Simple switch that restarts the ESP32
|
||||
- platform: restart
|
||||
name: "Cabinet Restart"
|
||||
@@ -1,57 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: dachboden
|
||||
platform: ESP8266
|
||||
board: nodemcuv2
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.212
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: dachboden
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
|
||||
dallas:
|
||||
id: 'dallas'
|
||||
pin: D1
|
||||
|
||||
sensor:
|
||||
- platform: dht
|
||||
pin: D3
|
||||
temperature:
|
||||
name: 'Dachboden Temperatur'
|
||||
humidity:
|
||||
name: 'Dachboden Luftfeuchtigkeit'
|
||||
model: DHT22
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x01031663650aff28
|
||||
name: "Dachboden Solar Süd Vorlauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x2b0416638fe6ff28
|
||||
name: "Dachboden Solar Süd Rücklauf"
|
||||
- platform: adc
|
||||
pin: A0
|
||||
name: "Dachboden Helligkeit"
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Dachboden Status"
|
||||
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Dachboden Neustart"
|
||||
@@ -1,117 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: heatpump
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.204
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: heatpump
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
dallas:
|
||||
id: 'dallas'
|
||||
pin: 15
|
||||
|
||||
sensor:
|
||||
- platform: dht
|
||||
pin: 0
|
||||
temperature:
|
||||
name: 'Outside Temperature'
|
||||
humidity:
|
||||
name: 'Outside Humidity'
|
||||
model: DHT22
|
||||
- platform: pulse_counter
|
||||
pin: 12
|
||||
unit_of_measurement: 'kW'
|
||||
name: 'Stromverbrauch Wintergarten'
|
||||
update_interval: 30s
|
||||
expire_after: 60s
|
||||
filters:
|
||||
- multiply: 0.06
|
||||
- platform: pulse_counter
|
||||
pin: 13
|
||||
unit_of_measurement: 'kW'
|
||||
name: 'Stromverbrauch Wärmepumpe'
|
||||
update_interval: 30s
|
||||
expire_after: 60s
|
||||
filters:
|
||||
- multiply: 0.06
|
||||
- platform: pulse_counter
|
||||
pin: 14
|
||||
unit_of_measurement: 'kW'
|
||||
name: 'Stromverbrauch Gesamt'
|
||||
update_interval: 30s
|
||||
expire_after: 60s
|
||||
filters:
|
||||
- multiply: 0.06
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0xfe0000031f1eaf28
|
||||
name: "Boiler Temperatur Oben"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0xba0000031f0e5228
|
||||
name: "Boiler Temperatur Unten"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0xa40000031f055028
|
||||
name: "Boiler Temperatur Mitte"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x790000031ee1dc28
|
||||
name: "Heizung Rücklauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0xdd0000031efb0428
|
||||
name: "Ölheizung Vorlauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x710000031f0e7e28
|
||||
name: "Boiler Solar Rücklauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x92041703081aff28
|
||||
name: "Boiler Solar Vorlauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x2c04173159f4ff28
|
||||
name: "Heizung Vorlauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0xd10417315babff28
|
||||
name: "Wärmepumpe Vorlauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x6c0517024a17ff28
|
||||
name: "Boiler Heizung Vorlauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x7d04173139eeff28
|
||||
name: "Wärmepumpe Rücklauf"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x3204166398a5ff28
|
||||
name: "Wärmepumpe Verdampfer"
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Heizung Status"
|
||||
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Heizung Neustart"
|
||||
@@ -1,60 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: kuche
|
||||
platform: ESP8266
|
||||
board: nodemcuv2
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.211
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: kuche
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
|
||||
dallas:
|
||||
id: 'dallas'
|
||||
pin: D1
|
||||
|
||||
sensor:
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x69041662d7f1ff28
|
||||
name: "Küche Raumtemperatur"
|
||||
- platform: dallas
|
||||
dallas_id: 'dallas'
|
||||
address: 0x800416636bebff28
|
||||
name: "Küche Heizkörpertemperatur"
|
||||
- platform: adc
|
||||
pin: A0
|
||||
name: "Küche Helligkeit"
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: D2
|
||||
id: 'ventilator'
|
||||
|
||||
fan:
|
||||
- platform: binary
|
||||
output: 'ventilator'
|
||||
name: 'Küche Heizkörper Ventilator'
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Küche Status"
|
||||
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Küche Neustart"
|
||||
@@ -1,58 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: lebensmittelkeller
|
||||
platform: ESP8266
|
||||
board: nodemcuv2
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.209
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: lebensmittelkeller
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
sensor:
|
||||
- platform: dht
|
||||
pin: D3
|
||||
temperature:
|
||||
name: 'Lebensmittelkeller Temperatur'
|
||||
humidity:
|
||||
name: 'Lebensmittelkeller Feuchtigkeit'
|
||||
model: DHT22
|
||||
- platform: adc
|
||||
pin: A0
|
||||
name: "Lebensmittelkeller Helligkeit"
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: D4
|
||||
id: 'ventilator'
|
||||
|
||||
fan:
|
||||
- platform: binary
|
||||
output: 'ventilator'
|
||||
name: 'Lebensmittelkeller Ventilator'
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
pin: D2
|
||||
name: 'Lebensmittelkeller Entfeuchter'
|
||||
icon: 'mdi:water-off'
|
||||
- platform: restart
|
||||
name: "Lebensmittelkeller Neustart"
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Lebensmittelkeller Status"
|
||||
@@ -1,344 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: livingroom
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.201
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: livingroom
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: 'fan_float'
|
||||
frequency: 50000
|
||||
pin: 22
|
||||
bit_depth: 8
|
||||
|
||||
dallas:
|
||||
pin: 23
|
||||
id: dallas
|
||||
|
||||
sensor:
|
||||
- platform: dallas
|
||||
dallas_id: dallas
|
||||
address: 0x1c0000031edd2a28
|
||||
name: "Wohnzimmer Raumtemperatur"
|
||||
filters:
|
||||
- sliding_window_moving_average:
|
||||
window_size: 15
|
||||
send_every: 15
|
||||
- filter_out: 85
|
||||
- platform: dallas
|
||||
dallas_id: dallas
|
||||
address: 0x7a0315a8371eff28
|
||||
name: "Wohnzimmer Heizkörpertemperatur"
|
||||
update_interval: 30s
|
||||
filters:
|
||||
- sliding_window_moving_average:
|
||||
window_size: 15
|
||||
send_every: 15
|
||||
- filter_out: 85
|
||||
|
||||
fan:
|
||||
- platform: speed
|
||||
output: 'fan_float'
|
||||
name: 'Wohnzimmer Heizkörper Ventilator'
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Wohnzimmer Status"
|
||||
|
||||
ir_transmitter:
|
||||
pin: 32
|
||||
id: 'ir'
|
||||
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Wohnzimmer Neustart"
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV On"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100BCBD
|
||||
repeat: 25
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Off"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100BCBD
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV SD Card"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x190D544
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Input TV"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1400C4D
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Input AV"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100A0A1
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Menu"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1004A4B
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Aspect Ratio"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1207B5A
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Viera Cast"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x190C958
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Direct TV REC"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1909100
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Info"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1009C9D
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Exit"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100CBCA
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Viera Link"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1908D1C
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Viera Tools"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100F7F6
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Guide"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x190E170
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Up"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1005253
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Left"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1007273
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV OK"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1009293
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Right"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100F2F3
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Down"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100D2D3
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Option"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x190E574
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Back"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1002B2A
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Red"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1000E0F
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Green"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1000E0F # TODO: FIXME
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Yellow"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1004E4F
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Blue"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100CECF
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Text"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x180C041
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Subtitle"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x180A021
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Index"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1801091
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Hold"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1809011
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 1"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1000809
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 2"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1008889
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 3"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1004849
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 4"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100C8C9
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 5"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1002829
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 6"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100A8A9
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 7"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1006869
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 8"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100E8E9
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 9"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1001819
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV 0"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1009899
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Mute"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1004C4D
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Last View"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100ECED
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Volume Up"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1000405
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Volume Down"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1008485
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Program Up"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1002C2D
|
||||
- platform: ir_transmitter
|
||||
ir_transmitter_id: 'ir'
|
||||
name: "Panasonic TV Program Down"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100ACAD
|
||||
@@ -1,48 +0,0 @@
|
||||
esphomeyaml:
|
||||
name: terrasse
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
|
||||
logger:
|
||||
level: verbose
|
||||
|
||||
wifi:
|
||||
ssid: '[SSID]'
|
||||
password: '[PASSWORD]'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.205
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
ota:
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.178.84
|
||||
username: terrasse
|
||||
password: '[PASSWORD]'
|
||||
# This is the default
|
||||
discovery: true
|
||||
|
||||
dallas:
|
||||
pin: 25
|
||||
id: dallas
|
||||
|
||||
sensor:
|
||||
- platform: pulse_counter
|
||||
pin: 34
|
||||
name: "Terrasse Wind"
|
||||
- platform: pulse_counter
|
||||
pin: 39
|
||||
name: "Terrasse Regen"
|
||||
- platform: dallas
|
||||
dallas_id: dallas
|
||||
index: 0
|
||||
name: "Terrasse Temperatur"
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Terrasse Status"
|
||||
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Terrasse Neustart"
|
||||
4
pylintrc
4
pylintrc
@@ -14,6 +14,10 @@ disable=
|
||||
too-many-arguments,
|
||||
too-many-return-statements,
|
||||
duplicate-code,
|
||||
invalid-name,
|
||||
cyclic-import,
|
||||
redefined-builtin,
|
||||
undefined-loop-variable,
|
||||
|
||||
|
||||
additional-builtins=
|
||||
|
||||
5
repository.json
Normal file
5
repository.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "esphomeyaml HassIO Add-On Repository",
|
||||
"url": "https://github.com/OttoWinter/esphomeyaml",
|
||||
"maintainer": "Otto Winter <contact@otto-winter.com>"
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
voluptuous==0.11.1
|
||||
platformio==3.5.2
|
||||
pyyaml==3.12
|
||||
paho-mqtt==1.3.1
|
||||
colorlog==3.1.2
|
||||
voluptuous>=0.11.1
|
||||
platformio>=3.5.3
|
||||
pyyaml>=3.12
|
||||
paho-mqtt>=1.3.1
|
||||
colorlog>=3.1.2
|
||||
tornado>=5.0.0
|
||||
esptool>=2.3.1
|
||||
|
||||
4
setup.py
4
setup.py
@@ -23,10 +23,12 @@ DOWNLOAD_URL = '{}/archive/{}.zip'.format(GITHUB_URL, const.__version__)
|
||||
|
||||
REQUIRES = [
|
||||
'voluptuous>=0.11.1',
|
||||
'platformio>=3.5.2',
|
||||
'platformio>=3.5.3',
|
||||
'pyyaml>=3.12',
|
||||
'paho-mqtt>=1.3.1',
|
||||
'colorlog>=3.1.2',
|
||||
'tornado>=5.0.0',
|
||||
'esptool>=2.3.1',
|
||||
]
|
||||
|
||||
CLASSIFIERS = [
|
||||
|
||||
11
tests/README.md
Normal file
11
tests/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Tests for esphomeyaml
|
||||
|
||||
This directory contains some tests for esphomeyaml.
|
||||
At the moment, all the tests only work by simply executing
|
||||
`esphomeyaml` over some YAML files that are made to test
|
||||
all of esphomeyaml's features.
|
||||
|
||||
Of course this is all just very high-level and things like
|
||||
unit tests would be much better. So if you have time and know
|
||||
how to set up a unit testing framework for python, please do
|
||||
give it a try.
|
||||
943
tests/test1.yaml
Normal file
943
tests/test1.yaml
Normal file
@@ -0,0 +1,943 @@
|
||||
esphomeyaml:
|
||||
name: test1
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
# Use latest upstream esphomelib git version.
|
||||
esphomelib_version: dev
|
||||
# Use this for testing while developing:
|
||||
# esphomelib_version:
|
||||
# local: ~/path/to/esphomelib
|
||||
use_custom_code: false
|
||||
on_boot:
|
||||
priority: 150.0
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "ON BOOT!");
|
||||
on_shutdown:
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "ON SHUTDOWN!");
|
||||
on_loop:
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGV("main", "ON LOOP!");
|
||||
build_path: build/test1
|
||||
|
||||
wifi:
|
||||
ssid: 'MySSID'
|
||||
password: 'password1'
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.230
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
dns1: 1.1.1.1
|
||||
dns2: 1.2.2.1
|
||||
hostname: myverylonghostname
|
||||
domain: .local
|
||||
reboot_timeout: 120s
|
||||
power_save_mode: none
|
||||
|
||||
mqtt:
|
||||
broker: '192.168.178.84'
|
||||
port: 1883
|
||||
username: 'debug'
|
||||
password: 'debug'
|
||||
client_id: someclient
|
||||
discovery: True
|
||||
discovery_retain: False
|
||||
discovery_prefix: discovery
|
||||
topic_prefix: helloworld
|
||||
log_topic:
|
||||
topic: helloworld/hi
|
||||
level: INFO
|
||||
birth_message:
|
||||
will_message:
|
||||
shutdown_message:
|
||||
topic: topic/to/send/to
|
||||
payload: hi
|
||||
qos: 2
|
||||
retain: True
|
||||
keepalive: 60s
|
||||
reboot_timeout: 60s
|
||||
on_message:
|
||||
- topic: my/custom/topic
|
||||
qos: 0
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Got message %s", x.c_str());
|
||||
- topic: livingroom/ota_mode
|
||||
then:
|
||||
- deep_sleep.prevent:
|
||||
id: deep_sleep_1
|
||||
- topic: livingroom/ota_mode
|
||||
then:
|
||||
- deep_sleep.enter:
|
||||
id: deep_sleep_1
|
||||
on_json_message:
|
||||
topic: the/topic
|
||||
then:
|
||||
- lambda: |-
|
||||
int data = x["my_data"];
|
||||
ESP_LOGD("main", "The data is: %d", data);
|
||||
- light.turn_on:
|
||||
id: living_room_lights
|
||||
transition_length: !lambda |-
|
||||
int length = 1000;
|
||||
if (x.containsKey("length"))
|
||||
length = x["length"];
|
||||
return length;
|
||||
effect: !lambda |-
|
||||
const char *effect = "None";
|
||||
if (x.containsKey("effect"))
|
||||
effect = x["effect"];
|
||||
return effect;
|
||||
|
||||
i2c:
|
||||
sda: 21
|
||||
scl: 22
|
||||
scan: True
|
||||
frequency: 100kHz
|
||||
|
||||
spi:
|
||||
clk_pin: GPIO21
|
||||
mosi_pin: GPIO22
|
||||
miso_pin: GPIO23
|
||||
|
||||
uart:
|
||||
tx_pin: GPIO22
|
||||
rx_pin: GPIO23
|
||||
baud_rate: 115200
|
||||
|
||||
ota:
|
||||
safe_mode: True
|
||||
password: 'superlongpasswordthatnoonewillknow'
|
||||
port: 3286
|
||||
|
||||
logger:
|
||||
baud_rate: 0
|
||||
level: VERBOSE
|
||||
logs:
|
||||
mqtt.component: DEBUG
|
||||
mqtt.client: ERROR
|
||||
|
||||
web_server:
|
||||
port: 8080
|
||||
css_url: https://esphomelib.com/_static/webserver-v1.min.css
|
||||
js_url: https://esphomelib.com/_static/webserver-v1.min.js
|
||||
|
||||
power_supply:
|
||||
id: 'atx_power_supply'
|
||||
enable_time: 20ms
|
||||
keep_on_time: 10s
|
||||
pin:
|
||||
number: 13
|
||||
inverted: true
|
||||
|
||||
deep_sleep:
|
||||
run_duration: 20s
|
||||
run_cycles: 500
|
||||
sleep_duration: 50s
|
||||
wakeup_pin: GPIO39
|
||||
wakeup_pin_mode: INVERT_WAKEUP
|
||||
id: deep_sleep_1
|
||||
|
||||
ads1115:
|
||||
address: 0x48
|
||||
|
||||
dallas:
|
||||
pin: GPIO23
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
pin: A0
|
||||
name: "Living Room Brightness"
|
||||
update_interval: '1:01'
|
||||
attenuation: 2.5db
|
||||
unit_of_measurement: "°C"
|
||||
icon: "mdi:water-percent"
|
||||
accuracy_decimals: 5
|
||||
expire_after: 120s
|
||||
filters:
|
||||
- offset: 2.0
|
||||
- multiply: 1.2
|
||||
- filter_out: 42.0
|
||||
- filter_nan:
|
||||
- sliding_window_moving_average:
|
||||
window_size: 15
|
||||
send_every: 15
|
||||
- exponential_moving_average:
|
||||
alpha: 0.1
|
||||
send_every: 15
|
||||
- throttle: 1s
|
||||
- heartbeat: 5s
|
||||
- debounce: 0.1s
|
||||
- delta: 5.0
|
||||
- unique:
|
||||
- or:
|
||||
- throttle: 1s
|
||||
- delta: 5.0
|
||||
- lambda: return x * (9.0/5.0) + 32.0;
|
||||
on_value:
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Got value %f", x);
|
||||
id(my_sensor).publish_state(42.0);
|
||||
ESP_LOGI("main", "Value of my sensor: %f", id(my_sensor).state);
|
||||
ESP_LOGI("main", "Raw Value of my sensor: %f", id(my_sensor).state);
|
||||
on_value_range:
|
||||
above: 5
|
||||
below: 10
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Got value range %f", x);
|
||||
on_raw_value:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Got raw value %f", x);
|
||||
- logger.log:
|
||||
level: DEBUG
|
||||
format: "Got raw value %f"
|
||||
args: ['x']
|
||||
- logger.log: "Got raw value NAN"
|
||||
- mqtt.publish:
|
||||
topic: some/topic
|
||||
payload: Hello
|
||||
qos: 2
|
||||
retain: True
|
||||
- platform: ads1115
|
||||
multiplexer: 'A0_A1'
|
||||
gain: 1.024
|
||||
id: my_sensor
|
||||
filters:
|
||||
state_topic: hi/me
|
||||
retain: false
|
||||
availability:
|
||||
- platform: bh1750
|
||||
name: "Living Room Brightness 3"
|
||||
internal: true
|
||||
address: 0x23
|
||||
resolution: 1.0
|
||||
update_interval: 30s
|
||||
retain: False
|
||||
availability:
|
||||
state_topic: livingroom/custom_state_topic
|
||||
- platform: bme280
|
||||
temperature:
|
||||
name: "Outside Temperature"
|
||||
oversampling: 16x
|
||||
pressure:
|
||||
name: "Outside Pressure"
|
||||
oversampling: none
|
||||
humidity:
|
||||
name: "Outside Humidity"
|
||||
oversampling: 8x
|
||||
address: 0x77
|
||||
iir_filter: 16x
|
||||
update_interval: 15s
|
||||
- platform: bme680
|
||||
temperature:
|
||||
name: "Outside Temperature"
|
||||
oversampling: 16x
|
||||
pressure:
|
||||
name: "Outside Pressure"
|
||||
humidity:
|
||||
name: "Outside Humidity"
|
||||
gas_resistance:
|
||||
name: "Outside Gas Sensor"
|
||||
address: 0x77
|
||||
heater:
|
||||
temperature: 320
|
||||
duration: 150ms
|
||||
update_interval: 15s
|
||||
- platform: bmp085
|
||||
temperature:
|
||||
name: "Outside Temperature"
|
||||
pressure:
|
||||
name: "Outside Pressure"
|
||||
filters:
|
||||
- lambda: >-
|
||||
return x / powf(1.0 - (x / 44330.0), 5.255);
|
||||
update_interval: 15s
|
||||
- platform: bmp280
|
||||
temperature:
|
||||
name: "Outside Temperature"
|
||||
oversampling: 16x
|
||||
pressure:
|
||||
name: "Outside Pressure"
|
||||
address: 0x77
|
||||
update_interval: 15s
|
||||
iir_filter: 16x
|
||||
- platform: dallas
|
||||
address: 0x1C0000031EDD2A28
|
||||
name: "Living Room Temperature"
|
||||
resolution: 9
|
||||
- platform: dallas
|
||||
index: 1
|
||||
name: "Living Room Temperature 2"
|
||||
- platform: dht
|
||||
pin: GPIO26
|
||||
temperature:
|
||||
name: "Living Room Temperature 3"
|
||||
humidity:
|
||||
name: "Living Room Humidity 3"
|
||||
model: AM2302
|
||||
update_interval: 15s
|
||||
- platform: dht12
|
||||
temperature:
|
||||
name: "Living Room Temperature 4"
|
||||
humidity:
|
||||
name: "Living Room Humidity 4"
|
||||
update_interval: 15s
|
||||
- platform: duty_cycle
|
||||
pin: GPIO25
|
||||
name: Duty Cycle Sensor
|
||||
- platform: esp32_hall
|
||||
name: "ESP32 Hall Sensor"
|
||||
update_interval: 15s
|
||||
- platform: hdc1080
|
||||
temperature:
|
||||
name: "Living Room Temperature 5"
|
||||
humidity:
|
||||
name: "Living Room Pressure 5"
|
||||
update_interval: 15s
|
||||
- platform: hlw8012
|
||||
sel_pin: 5
|
||||
cf_pin: 14
|
||||
cf1_pin: 13
|
||||
current:
|
||||
name: "HLW8012 Current"
|
||||
voltage:
|
||||
name: "HLW8012 Voltage"
|
||||
power:
|
||||
name: "HLW8012 Power"
|
||||
update_interval: 15s
|
||||
current_resistor: 0.001 ohm
|
||||
voltage_divider: 2351
|
||||
change_mode_every: 16
|
||||
- platform: hmc5883l
|
||||
address: 0x68
|
||||
field_strength_x:
|
||||
name: "HMC5883L Field Strength X"
|
||||
field_strength_y:
|
||||
name: "HMC5883L Field Strength Y"
|
||||
field_strength_z:
|
||||
name: "HMC5883L Field Strength Z"
|
||||
heading:
|
||||
name: "HMC5883L Heading"
|
||||
range: 130uT
|
||||
update_interval: 15s
|
||||
- platform: hx711
|
||||
name: "HX711 Value"
|
||||
dout_pin: GPIO23
|
||||
clk_pin: GPIO24
|
||||
gain: 128
|
||||
update_interval: 15s
|
||||
- platform: ina219
|
||||
address: 0x40
|
||||
shunt_resistance: 0.1 ohm
|
||||
current:
|
||||
name: "INA219 Current"
|
||||
power:
|
||||
name: "INA219 Power"
|
||||
bus_voltage:
|
||||
name: "INA219 Bus Voltage"
|
||||
shunt_voltage:
|
||||
name: "INA219 Shunt Voltage"
|
||||
max_voltage: 32.0V
|
||||
max_current: 3.2A
|
||||
update_interval: 15s
|
||||
- platform: ina3221
|
||||
address: 0x40
|
||||
channel_1:
|
||||
shunt_resistance: 0.1 ohm
|
||||
current:
|
||||
name: "INA3221 Channel 1 Current"
|
||||
power:
|
||||
name: "INA3221 Channel 1 Power"
|
||||
bus_voltage:
|
||||
name: "INA3221 Channel 1 Bus Voltage"
|
||||
shunt_voltage:
|
||||
name: "INA3221 Channel 1 Shunt Voltage"
|
||||
update_interval: 15s
|
||||
- platform: htu21d
|
||||
temperature:
|
||||
name: "Living Room Temperature 6"
|
||||
humidity:
|
||||
name: "Living Room Humidity 6"
|
||||
update_interval: 15s
|
||||
- platform: max6675
|
||||
name: "Living Room Temperature"
|
||||
cs_pin: GPIO23
|
||||
update_interval: 15s
|
||||
- platform: mhz19
|
||||
co2:
|
||||
name: "MH-Z19 CO2 Value"
|
||||
temperature:
|
||||
name: "MH-Z19 Temperature"
|
||||
update_interval: 15s
|
||||
- platform: mpu6050
|
||||
address: 0x68
|
||||
accel_x:
|
||||
name: "MPU6050 Accel X"
|
||||
accel_y:
|
||||
name: "MPU6050 Accel Y"
|
||||
accel_z:
|
||||
name: "MPU6050 Accel z"
|
||||
gyro_x:
|
||||
name: "MPU6050 Gyro X"
|
||||
gyro_y:
|
||||
name: "MPU6050 Gyro Y"
|
||||
gyro_z:
|
||||
name: "MPU6050 Gyro z"
|
||||
temperature:
|
||||
name: "MPU6050 Temperature"
|
||||
- platform: ms5611
|
||||
temperature:
|
||||
name: "Outside Temperature"
|
||||
pressure:
|
||||
name: "Outside Pressure"
|
||||
address: 0x77
|
||||
update_interval: 15s
|
||||
- platform: pulse_counter
|
||||
name: "Pulse Counter"
|
||||
pin: GPIO12
|
||||
count_mode:
|
||||
rising_edge: INCREMENT
|
||||
falling_edge: DECREMENT
|
||||
internal_filter: 13us
|
||||
update_interval: 15s
|
||||
- platform: rotary_encoder
|
||||
name: "Rotary Encoder"
|
||||
pin_a: GPIO23
|
||||
pin_b: GPIO24
|
||||
pin_reset: GPIO25
|
||||
filters:
|
||||
- or:
|
||||
- debounce: 0.1s
|
||||
- delta: 10
|
||||
- platform: sht3xd
|
||||
temperature:
|
||||
name: "Living Room Temperature 8"
|
||||
humidity:
|
||||
name: "Living Room Humidity 8"
|
||||
address: 0x44
|
||||
update_interval: 15s
|
||||
- platform: template
|
||||
name: "Template Sensor"
|
||||
lambda: >-
|
||||
if (id(ultrasonic_sensor1).state > 1) {
|
||||
return 42.0;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
update_interval: 15s
|
||||
- platform: tsl2561
|
||||
name: "TSL2561 Ambient Light"
|
||||
address: 0x39
|
||||
update_interval: 15s
|
||||
is_cs_package: true
|
||||
integration_time: 402ms
|
||||
gain: 16x
|
||||
- platform: ultrasonic
|
||||
trigger_pin: GPIO24
|
||||
echo_pin:
|
||||
number: GPIO23
|
||||
inverted: true
|
||||
name: "Ultrasonic Sensor"
|
||||
timeout_meter: 5.5
|
||||
id: ultrasonic_sensor1
|
||||
- platform: uptime
|
||||
name: Uptime Sensor
|
||||
- platform: wifi_signal
|
||||
name: "WiFi Signal Sensor"
|
||||
update_interval: 15s
|
||||
|
||||
|
||||
esp32_touch:
|
||||
setup_mode: False
|
||||
iir_filter: 10ms
|
||||
sleep_duration: 27ms
|
||||
measurement_duration: 8ms
|
||||
low_voltage_reference: 0.5V
|
||||
high_voltage_reference: 2.7V
|
||||
voltage_attenuation: 1.5V
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: GPIO9
|
||||
name: "Living Room Window"
|
||||
device_class: window
|
||||
filters:
|
||||
- invert:
|
||||
- delayed_on: 40ms
|
||||
- delayed_off: 40ms
|
||||
- heartbeat: 1s
|
||||
on_press:
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Pressed");
|
||||
on_release:
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Released");
|
||||
on_click:
|
||||
- min_length: 50ms
|
||||
max_length: 350ms
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Clicked");
|
||||
- then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Clicked");
|
||||
on_double_click:
|
||||
- min_length: 50ms
|
||||
max_length: 350ms
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Double Clicked");
|
||||
- then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Double Clicked");
|
||||
id: binary_sensor1
|
||||
- platform: status
|
||||
name: "Living Room Status"
|
||||
- platform: esp32_touch
|
||||
name: "ESP32 Touch Pad GPIO27"
|
||||
pin: GPIO27
|
||||
threshold: 1000
|
||||
- platform: nextion
|
||||
page_id: 0
|
||||
component_id: 2
|
||||
name: "Nextion Component 2 Touch"
|
||||
- platform: template
|
||||
name: "Garage Door Open"
|
||||
lambda: >-
|
||||
if (isnan(id(my_sensor).state)) {
|
||||
// isnan checks if the ultrasonic sensor echo
|
||||
// has timed out, resulting in a NaN (not a number) state
|
||||
// in that case, return {} to indicate that we don't know.
|
||||
return {};
|
||||
} else if (id(my_sensor).state > 30) {
|
||||
// Garage Door is open.
|
||||
return true;
|
||||
} else {
|
||||
// Garage Door is closed.
|
||||
return false;
|
||||
}
|
||||
- platform: pn532
|
||||
uid: 74-10-37-94
|
||||
name: "PN532 NFC Tag"
|
||||
- platform: rdm6300
|
||||
uid: 7616525
|
||||
name: "RDM6300 NFC Tag"
|
||||
- platform: gpio
|
||||
name: "PCF binary sensor"
|
||||
pin:
|
||||
pcf8574: pcf8574_hub
|
||||
number: 1
|
||||
mode: INPUT
|
||||
inverted: True
|
||||
|
||||
pca9685:
|
||||
frequency: 500
|
||||
address: 0x0
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: GPIO26
|
||||
id: gpio_26
|
||||
power_supply: atx_power_supply
|
||||
inverted: False
|
||||
- platform: ledc
|
||||
pin: 19
|
||||
id: gpio_19
|
||||
frequency: 1500Hz
|
||||
bit_depth: 8
|
||||
channel: 14
|
||||
max_power: 0.5
|
||||
- platform: pca9685
|
||||
id: pca_0
|
||||
channel: 0
|
||||
- platform: pca9685
|
||||
id: pca_1
|
||||
channel: 1
|
||||
- platform: pca9685
|
||||
id: pca_2
|
||||
channel: 2
|
||||
- platform: pca9685
|
||||
id: pca_3
|
||||
channel: 3
|
||||
- platform: pca9685
|
||||
id: pca_4
|
||||
channel: 4
|
||||
- platform: pca9685
|
||||
id: pca_5
|
||||
channel: 5
|
||||
- platform: pca9685
|
||||
id: pca_6
|
||||
channel: 6
|
||||
- platform: pca9685
|
||||
id: pca_7
|
||||
channel: 7
|
||||
- platform: gpio
|
||||
id: id2
|
||||
pin:
|
||||
pcf8574: pcf8574_hub
|
||||
number: 0
|
||||
mode: OUTPUT
|
||||
inverted: False
|
||||
|
||||
light:
|
||||
- platform: binary
|
||||
name: "Desk Lamp"
|
||||
output: gpio_26
|
||||
effects:
|
||||
- strobe:
|
||||
- strobe:
|
||||
name: "My Strobe"
|
||||
colors:
|
||||
- state: True
|
||||
duration: 250ms
|
||||
- state: False
|
||||
duration: 250ms
|
||||
- platform: monochromatic
|
||||
name: "Kitchen Lights"
|
||||
id: kitchen
|
||||
output: gpio_19
|
||||
gamma_correct: 2.8
|
||||
default_transition_length: 2s
|
||||
effects:
|
||||
- strobe:
|
||||
- flicker:
|
||||
- flicker:
|
||||
name: "My Flicker"
|
||||
alpha: 98%
|
||||
intensity: 1.5%
|
||||
- lambda:
|
||||
name: My Custom Effect
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
static int state = 0;
|
||||
state += 1;
|
||||
if (state == 4)
|
||||
state = 0;
|
||||
- platform: rgb
|
||||
name: "Living Room Lights"
|
||||
id: living_room_lights
|
||||
red: pca_0
|
||||
green: pca_1
|
||||
blue: pca_2
|
||||
- platform: rgbw
|
||||
name: "Living Room Lights 2"
|
||||
red: pca_3
|
||||
green: pca_4
|
||||
blue: pca_5
|
||||
white: pca_6
|
||||
- platform: rgbww
|
||||
name: "Living Room Lights 2"
|
||||
red: pca_3
|
||||
green: pca_4
|
||||
blue: pca_5
|
||||
cold_white: pca_6
|
||||
warm_white: pca_6
|
||||
cold_white_color_temperature: 153 mireds
|
||||
warm_white_color_temperature: 500 mireds
|
||||
- platform: cwww
|
||||
name: "Living Room Lights 2"
|
||||
cold_white: pca_6
|
||||
warm_white: pca_6
|
||||
cold_white_color_temperature: 153 mireds
|
||||
warm_white_color_temperature: 500 mireds
|
||||
- platform: fastled_clockless
|
||||
chipset: WS2811
|
||||
pin: GPIO23
|
||||
num_leds: 60
|
||||
rgb_order: BRG
|
||||
max_refresh_rate: 20ms
|
||||
power_supply: atx_power_supply
|
||||
color_correct: [75%, 100%, 50%]
|
||||
name: "FastLED WS2811 Light"
|
||||
effects:
|
||||
- fastled_color_wipe:
|
||||
- fastled_color_wipe:
|
||||
name: Color Wipe Effect With Custom Values
|
||||
colors:
|
||||
- red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
num_leds: 1
|
||||
- red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
num_leds: 1
|
||||
add_led_interval: 100ms
|
||||
reverse: False
|
||||
- fastled_scan:
|
||||
- fastled_scan:
|
||||
name: Scan Effect With Custom Values
|
||||
move_interval: 100ms
|
||||
- fastled_twinkle:
|
||||
- fastled_twinkle:
|
||||
name: Twinkle Effect With Custom Values
|
||||
twinkle_probability: 5%
|
||||
progress_interval: 4ms
|
||||
- fastled_random_twinkle:
|
||||
- fastled_random_twinkle:
|
||||
name: Random Twinkle Effect With Custom Values
|
||||
twinkle_probability: 5%
|
||||
progress_interval: 32ms
|
||||
- fastled_fireworks:
|
||||
- fastled_fireworks:
|
||||
name: Fireworks Effect With Custom Values
|
||||
update_interval: 32ms
|
||||
spark_probability: 10%
|
||||
use_random_color: false
|
||||
fade_out_rate: 120
|
||||
- fastled_flicker:
|
||||
- fastled_flicker:
|
||||
name: Flicker Effect With Custom Values
|
||||
update_interval: 16ms
|
||||
intensity: 5%
|
||||
- platform: fastled_spi
|
||||
chipset: WS2801
|
||||
data_pin: GPIO23
|
||||
clock_pin: GPIO22
|
||||
num_leds: 60
|
||||
rgb_order: BRG
|
||||
name: "FastLED SPI Light"
|
||||
|
||||
remote_transmitter:
|
||||
- pin: 32
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
pin: GPIO25
|
||||
name: "Living Room Dehumidifier"
|
||||
icon: "mdi:restart"
|
||||
inverted: True
|
||||
command_topic: custom_command_topic
|
||||
power_on_value: True
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
nec:
|
||||
address: 0x4242
|
||||
command: 0x8484
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
lg:
|
||||
data: 4294967295
|
||||
nbits: 28
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
samsung:
|
||||
data: 0xABCDEF
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
sony:
|
||||
data: 0xABCDEF
|
||||
nbits: 12
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x1000BCD
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
rc_switch_raw:
|
||||
code: '001010011001111101011011'
|
||||
protocol: 1
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
rc_switch_type_a:
|
||||
group: '11001'
|
||||
device: '01000'
|
||||
state: True
|
||||
protocol:
|
||||
pulse_length: 175
|
||||
sync: [1, 31]
|
||||
zero: [1, 3]
|
||||
one: [3, 1]
|
||||
inverted: False
|
||||
repeat: 25
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
rc_switch_type_b:
|
||||
address: 4
|
||||
channel: 2
|
||||
state: True
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
rc_switch_type_c:
|
||||
family: 'a'
|
||||
group: 1
|
||||
device: 2
|
||||
state: True
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
id: living_room_lights_on
|
||||
rc_switch_type_d:
|
||||
group: 'a'
|
||||
device: 2
|
||||
state: True
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
id: living_room_lights_off
|
||||
raw:
|
||||
carrier_frequency: 35kHz
|
||||
data:
|
||||
- 1000
|
||||
- -1000
|
||||
- platform: template
|
||||
name: Living Room Lights
|
||||
optimistic: True
|
||||
turn_on_action:
|
||||
- switch.turn_on: living_room_lights_on
|
||||
turn_off_action:
|
||||
- switch.turn_on: living_room_lights_off
|
||||
- platform: restart
|
||||
name: "Living Room Restart"
|
||||
- platform: shutdown
|
||||
name: "Living Room Shutdown"
|
||||
- platform: output
|
||||
name: "Generic Output"
|
||||
output: pca_6
|
||||
- platform: template
|
||||
name: "Template Switch"
|
||||
id: my_switch
|
||||
lambda: |-
|
||||
if (id(binary_sensor1).state) {
|
||||
return true;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
id(my_switch).publish_state(false);
|
||||
id(my_switch).publish_state(true);
|
||||
if (id(my_switch).state) {
|
||||
// Switch is ON, do something here
|
||||
id(my_switch).turn_off();
|
||||
id(my_switch).turn_on();
|
||||
} else {
|
||||
// Switch is OFF, do something else here
|
||||
}
|
||||
optimistic: true
|
||||
- platform: uart
|
||||
name: "UART String Output"
|
||||
data: 'DataToSend'
|
||||
- platform: uart
|
||||
name: "UART Bytes Output"
|
||||
data: [0xDE, 0xAD, 0xBE, 0xEF]
|
||||
|
||||
fan:
|
||||
- platform: binary
|
||||
output: gpio_26
|
||||
name: "Living Room Fan 1"
|
||||
- platform: speed
|
||||
output: pca_6
|
||||
name: "Living Room Fan 2"
|
||||
speed:
|
||||
low: 0.45
|
||||
medium: 0.75
|
||||
high: 1.0
|
||||
|
||||
display:
|
||||
- platform: lcd_gpio
|
||||
dimensions: 18x4
|
||||
data_pins:
|
||||
- GPIO19
|
||||
- GPIO20
|
||||
- GPIO21
|
||||
- GPIO22
|
||||
enable_pin: GPIO23
|
||||
rs_pin: GPIO24
|
||||
lambda: |-
|
||||
it.print("Hello World!");
|
||||
- platform: lcd_pcf8574
|
||||
dimensions: 18x4
|
||||
address: 0x3F
|
||||
lambda: |-
|
||||
it.print("Hello World!");
|
||||
- platform: max7219
|
||||
cs_pin: GPIO23
|
||||
num_chips: 1
|
||||
lambda: |-
|
||||
it.print("01234567");
|
||||
- platform: nextion
|
||||
lambda: |-
|
||||
it.set_component_value("gauge", 50);
|
||||
it.set_component_text("textview", "Hello World!");
|
||||
- platform: ssd1306_i2c
|
||||
model: "SSD1306 128x64"
|
||||
reset_pin: GPIO23
|
||||
address: 0x3C
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: ssd1306_spi
|
||||
model: "SSD1306 128x64"
|
||||
cs_pin: GPIO23
|
||||
dc_pin: GPIO23
|
||||
reset_pin: GPIO23
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: waveshare_epaper
|
||||
cs_pin: GPIO23
|
||||
dc_pin: GPIO23
|
||||
busy_pin: GPIO23
|
||||
reset_pin: GPIO23
|
||||
model: 2.90in
|
||||
full_update_every: 30
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
|
||||
remote_receiver:
|
||||
pin: GPIO32
|
||||
dump: all
|
||||
|
||||
status_led:
|
||||
pin: GPIO2
|
||||
|
||||
pn532:
|
||||
cs_pin: GPIO23
|
||||
update_interval: 1s
|
||||
on_tag:
|
||||
- lambda: |-
|
||||
ESP_LOGD("main", "Found tag %s", x.c_str());
|
||||
- mqtt.publish:
|
||||
topic: the/topic
|
||||
payload: !lambda 'return x;'
|
||||
|
||||
rdm6300:
|
||||
|
||||
time:
|
||||
- platform: sntp
|
||||
id: sntp_time
|
||||
servers:
|
||||
- 0.pool.ntp.org
|
||||
- 1.pool.ntp.org
|
||||
- 2.pool.ntp.org
|
||||
on_time:
|
||||
cron: '/30 0-30,30/5 * ? JAN-DEC MON,SAT-SUN,TUE-FRI'
|
||||
then:
|
||||
- lambda: 'ESP_LOGD("main", "time");'
|
||||
|
||||
cover:
|
||||
- platform: template
|
||||
name: "Template Cover"
|
||||
lambda: >-
|
||||
if (id(binary_sensor1).state) {
|
||||
return cover::COVER_OPEN;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
optimistic: true
|
||||
|
||||
debug:
|
||||
|
||||
pcf8574:
|
||||
- id: 'pcf8574_hub'
|
||||
address: 0x21
|
||||
pcf8575: False
|
||||
168
tests/test2.yaml
Normal file
168
tests/test2.yaml
Normal file
@@ -0,0 +1,168 @@
|
||||
esphomeyaml:
|
||||
name: test2
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
# Use latest upstream esphomelib git version.
|
||||
esphomelib_version: dev
|
||||
# Use this for testing while developing:
|
||||
# esphomelib_version:
|
||||
# local: ~/path/to/esphomelib
|
||||
use_custom_code: true
|
||||
build_path: build/test2
|
||||
|
||||
wifi:
|
||||
ssid: 'MySSID'
|
||||
password: 'password1'
|
||||
reboot_timeout: 120s
|
||||
|
||||
mqtt:
|
||||
broker: '192.168.178.84'
|
||||
port: 1883
|
||||
username: 'debug'
|
||||
password: 'debug'
|
||||
|
||||
i2c:
|
||||
sda: 21
|
||||
scl: 22
|
||||
scan: False
|
||||
|
||||
spi:
|
||||
clk_pin: GPIO21
|
||||
mosi_pin: GPIO22
|
||||
miso_pin: GPIO23
|
||||
|
||||
uart:
|
||||
tx_pin: GPIO22
|
||||
rx_pin: GPIO23
|
||||
baud_rate: 115200
|
||||
|
||||
ota:
|
||||
safe_mode: True
|
||||
port: 3286
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
web_server:
|
||||
|
||||
deep_sleep:
|
||||
run_duration: 20s
|
||||
sleep_duration: 50s
|
||||
|
||||
sensor:
|
||||
- platform: ble_rssi
|
||||
mac_address: AC:37:43:77:5F:4C
|
||||
name: "BLE Google Home Mini RSSI value"
|
||||
- platform: xiaomi_miflora
|
||||
mac_address: 94:2B:FF:5C:91:61
|
||||
temperature:
|
||||
name: "Xiaomi MiFlora Temperature"
|
||||
moisture:
|
||||
name: "Xiaomi MiFlora Moisture"
|
||||
illuminance:
|
||||
name: "Xiaomi MiFlora Illuminance"
|
||||
conductivity:
|
||||
name: "Xiaomi MiFlora Soil Conductivity"
|
||||
battery_level:
|
||||
name: "Xiaomi MiFlora Battery Level"
|
||||
- platform: xiaomi_mijia
|
||||
mac_address: 7A:80:8E:19:36:BA
|
||||
temperature:
|
||||
name: "Xiaomi MiJia Temperature"
|
||||
humidity:
|
||||
name: "Xiaomi MiJia Humidity"
|
||||
battery_level:
|
||||
name: "Xiaomi MiJia Battery Level"
|
||||
- platform: mqtt_subscribe
|
||||
name: "MQTT Subscribe Sensor 1"
|
||||
topic: "mqtt/topic"
|
||||
id: the_sensor
|
||||
qos: 2
|
||||
on_value:
|
||||
- mqtt.publish_json:
|
||||
topic: the/topic
|
||||
payload: |-
|
||||
root["key"] = id(the_sensor).value;
|
||||
root["greeting"] = "Hello World";
|
||||
- platform: pmsx003
|
||||
type: PMSX003
|
||||
pm_1_0:
|
||||
name: "PM 1.0 Concentration"
|
||||
pm_2_5:
|
||||
name: "PM 2.5 Concentration"
|
||||
pm_10_0:
|
||||
name: "PM 10.0 Concentration"
|
||||
- platform: pmsx003
|
||||
type: PMS5003T
|
||||
pm_2_5:
|
||||
name: "PM 2.5 Concentration"
|
||||
temperature:
|
||||
name: "PMS Temperature"
|
||||
humidity:
|
||||
name: "PMS Humidity"
|
||||
- platform: pmsx003
|
||||
type: PMS5003ST
|
||||
pm_2_5:
|
||||
name: "PM 2.5 Concentration"
|
||||
temperature:
|
||||
name: "PMS Temperature"
|
||||
humidity:
|
||||
name: "PMS Humidity"
|
||||
formaldehyde:
|
||||
name: "PMS Formaldehyde Concentration"
|
||||
- platform: cse7766
|
||||
voltage:
|
||||
name: "CSE7766 Voltage"
|
||||
current:
|
||||
name: "CSE7766 Current"
|
||||
power:
|
||||
name: "CSE776 Power"
|
||||
|
||||
esp32_touch:
|
||||
setup_mode: True
|
||||
|
||||
binary_sensor:
|
||||
- platform: esp32_ble_tracker
|
||||
mac_address: AC:37:43:77:5F:4C
|
||||
name: "ESP32 BLE Tracker Google Home Mini"
|
||||
- platform: esp32_touch
|
||||
name: "ESP32 Touch Pad GPIO27"
|
||||
pin: GPIO27
|
||||
threshold: 1000
|
||||
|
||||
remote_receiver:
|
||||
pin: GPIO32
|
||||
dump: []
|
||||
|
||||
esp32_ble_tracker:
|
||||
scan_interval: 300s
|
||||
|
||||
esp32_ble_beacon:
|
||||
type: iBeacon
|
||||
uuid: 'c29ce823-e67a-4e71-bff2-abaa32e77a98'
|
||||
|
||||
status_led:
|
||||
pin: GPIO2
|
||||
|
||||
text_sensor:
|
||||
- platform: version
|
||||
name: "Esphomelib Version"
|
||||
icon: mdi:icon
|
||||
id: version_sensor
|
||||
on_value:
|
||||
- lambda: |-
|
||||
ESP_LOGD("main", "The state is %s=%s", x.c_str(), id(version_sensor).state.c_str());
|
||||
- script.execute: my_script
|
||||
- platform: mqtt_subscribe
|
||||
name: "MQTT Subscribe Text"
|
||||
topic: "the/topic"
|
||||
qos: 2
|
||||
- platform: template
|
||||
name: "Template Text Sensor"
|
||||
lambda: |-
|
||||
return {"Hello World"};
|
||||
|
||||
script:
|
||||
- id: my_script
|
||||
then:
|
||||
- lambda: 'ESP_LOGD("main", "Hello World!");'
|
||||
Reference in New Issue
Block a user