mirror of
https://github.com/esphome/esphome.git
synced 2025-11-04 17:11:51 +00:00
Compare commits
50 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 |
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).
|
||||
118
.gitlab-ci.yml
118
.gitlab-ci.yml
@@ -76,12 +76,12 @@ test2:
|
||||
retry: 2
|
||||
|
||||
# Generic deploy template
|
||||
.deploy: &deploy
|
||||
.deploy-release: &deploy-release
|
||||
<<: *docker-builder
|
||||
stage: deploy
|
||||
script:
|
||||
- version=${CI_COMMIT_TAG:1}
|
||||
- echo "Publishing version ${version}"
|
||||
- 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}"
|
||||
- |
|
||||
@@ -99,6 +99,38 @@ test2:
|
||||
- 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:
|
||||
@@ -134,54 +166,68 @@ build:amd64:
|
||||
ADDON_ARCH: amd64
|
||||
|
||||
# Deploy jobs
|
||||
deploy:armhf:
|
||||
<<: *deploy
|
||||
deploy-release:armhf:
|
||||
<<: *deploy-release
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
|
||||
#deploy:aarch64:
|
||||
# <<: *deploy
|
||||
deploy-beta:armhf:
|
||||
<<: *deploy-beta
|
||||
variables:
|
||||
ADDON_ARCH: armhf
|
||||
|
||||
#deploy-release:aarch64:
|
||||
# <<: *deploy-release
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
# only:
|
||||
# - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/
|
||||
# except:
|
||||
# - /^(?!master).+@/
|
||||
|
||||
deploy:i386:
|
||||
<<: *deploy
|
||||
#deploy-beta:aarch64:
|
||||
# <<: *deploy-beta
|
||||
# variables:
|
||||
# ADDON_ARCH: aarch64
|
||||
|
||||
deploy-release:i386:
|
||||
<<: *deploy-release
|
||||
variables:
|
||||
ADDON_ARCH: i386
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
|
||||
deploy:amd64:
|
||||
<<: *deploy
|
||||
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+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
|
||||
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-beta:pypi:
|
||||
<<: *deploy-pypi
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/
|
||||
- /^v\d+\.\d+\.\d+b\d+$/
|
||||
except:
|
||||
- /^(?!master).+@/
|
||||
- /^(?!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.9.3 tzlocal pillow
|
||||
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.
|
||||
@@ -23,3 +23,4 @@ RUN pip install --no-cache-dir -e . && \
|
||||
|
||||
WORKDIR /config
|
||||
ENTRYPOINT ["esphomeyaml"]
|
||||
CMD ["/config", "dashboard"]
|
||||
|
||||
@@ -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.
|
||||
|
||||
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}"
|
||||
}
|
||||
@@ -13,23 +13,16 @@ from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF
|
||||
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
|
||||
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):
|
||||
build_path = config[CONF_ESPHOMEYAML].get(CONF_BUILD_PATH, get_name(config))
|
||||
return os.path.join(os.path.dirname(core.CONFIG_PATH), build_path)
|
||||
|
||||
|
||||
def get_serial_ports():
|
||||
# from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
|
||||
from serial.tools.list_ports import comports
|
||||
@@ -47,11 +40,11 @@ def choose_serial_port(config):
|
||||
|
||||
if not result:
|
||||
return 'OTA'
|
||||
print(u"Found multiple serial port options, please choose one:")
|
||||
safe_print(u"Found multiple serial port options, please choose one:")
|
||||
for i, (res, desc) in enumerate(result):
|
||||
print(u" [{}] {} ({})".format(i, res, desc))
|
||||
print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
|
||||
print()
|
||||
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:
|
||||
@@ -63,7 +56,7 @@ def choose_serial_port(config):
|
||||
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 'OTA'
|
||||
return result[opt][0]
|
||||
@@ -99,7 +92,12 @@ def run_platformio(*cmd, **kwargs):
|
||||
|
||||
def run_miniterm(config, port, escape=False):
|
||||
import serial
|
||||
baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
|
||||
if CONF_LOGGER not in config:
|
||||
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
|
||||
return
|
||||
baud_rate = config['logger'][CONF_BAUD_RATE]
|
||||
if baud_rate == 0:
|
||||
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||
|
||||
with serial.Serial(port, baudrate=baud_rate) as ser:
|
||||
@@ -114,10 +112,7 @@ def run_miniterm(config, port, escape=False):
|
||||
message = time + line
|
||||
if escape:
|
||||
message = message.replace('\033', '\\033')
|
||||
try:
|
||||
print(message)
|
||||
except UnicodeEncodeError:
|
||||
print(message.encode('ascii', 'backslashreplace'))
|
||||
safe_print(message)
|
||||
|
||||
|
||||
def write_cpp(config):
|
||||
@@ -148,17 +143,19 @@ def write_cpp(config):
|
||||
exp = exp.rhs
|
||||
all_code.append(unicode(statement(exp)))
|
||||
|
||||
writer.write_platformio_project(config, get_base_path(config))
|
||||
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(args, config):
|
||||
_LOGGER.info("Compiling app...")
|
||||
command = ['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)
|
||||
@@ -177,8 +174,8 @@ def get_upload_host(config):
|
||||
def upload_using_esptool(config, port):
|
||||
import esptool
|
||||
|
||||
name = get_name(config)
|
||||
path = os.path.join(get_base_path(config), '.pioenvs', name, 'firmware.bin')
|
||||
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',
|
||||
@@ -186,14 +183,14 @@ def upload_using_esptool(config, port):
|
||||
|
||||
|
||||
def upload_program(config, args, port):
|
||||
_LOGGER.info("Uploading binary...")
|
||||
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', get_base_path(config),
|
||||
command = ['platformio', 'run', '-d', build_path,
|
||||
'-t', 'upload', '--upload-port', port]
|
||||
if args.verbose:
|
||||
command.append('-v')
|
||||
@@ -211,19 +208,23 @@ def upload_program(config, args, port):
|
||||
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)]
|
||||
if args.verbose:
|
||||
espota_args.append('-d')
|
||||
return espota.main(espota_args)
|
||||
|
||||
verbose = args.verbose
|
||||
remote_port = ota.get_port(config)
|
||||
password = ota.get_auth(config)
|
||||
|
||||
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, escape=False):
|
||||
@@ -292,7 +293,7 @@ def strip_default_ids(config):
|
||||
def command_config(args, config):
|
||||
if not args.verbose:
|
||||
config = strip_default_ids(config)
|
||||
print(yaml_util.dump(config))
|
||||
safe_print(yaml_util.dump(config))
|
||||
return 0
|
||||
|
||||
|
||||
@@ -351,7 +352,18 @@ def command_mqtt_fingerprint(args, config):
|
||||
|
||||
|
||||
def command_version(args):
|
||||
print(u"Version: {}".format(const.__version__))
|
||||
safe_print(u"Version: {}".format(const.__version__))
|
||||
return 0
|
||||
|
||||
|
||||
def command_clean(args, config):
|
||||
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
|
||||
|
||||
|
||||
@@ -375,6 +387,7 @@ POST_CONFIG_ACTIONS = {
|
||||
'run': command_run,
|
||||
'clean-mqtt': command_clean_mqtt,
|
||||
'mqtt-fingerprint': command_mqtt_fingerprint,
|
||||
'clean': command_clean,
|
||||
}
|
||||
|
||||
|
||||
@@ -445,12 +458,16 @@ def parse_args(argv):
|
||||
|
||||
subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
|
||||
|
||||
subparsers.add_parser('clean', help="Delete all temporary build files.")
|
||||
|
||||
dashboard = subparsers.add_parser('dashboard',
|
||||
help="Create a simple webserver for a dashboard.")
|
||||
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:])
|
||||
|
||||
@@ -477,7 +494,7 @@ def run_esphomeyaml(argv):
|
||||
except ESPHomeYAMLError as e:
|
||||
_LOGGER.error(e)
|
||||
return 1
|
||||
print(u"Unknown command {}".format(args.command))
|
||||
safe_print(u"Unknown command {}".format(args.command))
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
@@ -1,37 +1,15 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.components import cover, deep_sleep, fan, output
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \
|
||||
CONF_BELOW, CONF_BLUE, CONF_BRIGHTNESS, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, \
|
||||
CONF_EFFECT, CONF_ELSE, CONF_FLASH_LENGTH, CONF_GREEN, CONF_ID, CONF_IF, CONF_LAMBDA, \
|
||||
CONF_LEVEL, CONF_OR, CONF_OSCILLATING, CONF_PAYLOAD, CONF_QOS, CONF_RANGE, CONF_RED, \
|
||||
CONF_RETAIN, CONF_SPEED, CONF_THEN, CONF_TOPIC, CONF_TRANSITION_LENGTH, CONF_TRIGGER_ID, \
|
||||
CONF_WHITE, CONF_COLOR_TEMPERATURE
|
||||
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, \
|
||||
bool_, esphomelib_ns, float_, get_variable, process_lambda, std_string, templatable, uint32, \
|
||||
uint8
|
||||
|
||||
CONF_MQTT_PUBLISH = 'mqtt.publish'
|
||||
CONF_LIGHT_TOGGLE = 'light.toggle'
|
||||
CONF_LIGHT_TURN_OFF = 'light.turn_off'
|
||||
CONF_LIGHT_TURN_ON = 'light.turn_on'
|
||||
CONF_SWITCH_TOGGLE = 'switch.toggle'
|
||||
CONF_SWITCH_TURN_OFF = 'switch.turn_off'
|
||||
CONF_SWITCH_TURN_ON = 'switch.turn_on'
|
||||
CONF_COVER_OPEN = 'cover.open'
|
||||
CONF_COVER_CLOSE = 'cover.close'
|
||||
CONF_COVER_STOP = 'cover.stop'
|
||||
CONF_FAN_TOGGLE = 'fan.toggle'
|
||||
CONF_FAN_TURN_OFF = 'fan.turn_off'
|
||||
CONF_FAN_TURN_ON = 'fan.turn_on'
|
||||
CONF_OUTPUT_TURN_ON = 'output.turn_on'
|
||||
CONF_OUTPUT_TURN_OFF = 'output.turn_off'
|
||||
CONF_OUTPUT_SET_LEVEL = 'output.set_level'
|
||||
CONF_DEEP_SLEEP_ENTER = 'deep_sleep.enter'
|
||||
CONF_DEEP_SLEEP_PREVENT = 'deep_sleep.prevent'
|
||||
esphomelib_ns, float_, process_lambda, templatable, uint32, get_variable
|
||||
from esphomeyaml.util import ServiceRegistry
|
||||
|
||||
|
||||
def maybe_simple_id(*validators):
|
||||
@@ -50,112 +28,36 @@ def validate_recursive_condition(value):
|
||||
|
||||
|
||||
def validate_recursive_action(value):
|
||||
return ACTIONS_SCHEMA(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_KEYS = [CONF_DELAY, CONF_MQTT_PUBLISH, CONF_LIGHT_TOGGLE, CONF_LIGHT_TURN_OFF,
|
||||
CONF_LIGHT_TURN_ON, CONF_SWITCH_TOGGLE, CONF_SWITCH_TURN_OFF, CONF_SWITCH_TURN_ON,
|
||||
CONF_LAMBDA, CONF_COVER_OPEN, CONF_COVER_CLOSE, CONF_COVER_STOP, CONF_FAN_TOGGLE,
|
||||
CONF_FAN_TURN_OFF, CONF_FAN_TURN_ON, CONF_OUTPUT_TURN_ON, CONF_OUTPUT_TURN_OFF,
|
||||
CONF_OUTPUT_SET_LEVEL, CONF_IF, CONF_DEEP_SLEEP_ENTER, CONF_DEEP_SLEEP_PREVENT]
|
||||
|
||||
ACTIONS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
|
||||
cv.GenerateID(CONF_ACTION_ID): cv.declare_variable_id(None),
|
||||
vol.Optional(CONF_DELAY): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
vol.Optional(CONF_MQTT_PUBLISH): vol.Schema({
|
||||
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
|
||||
vol.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload),
|
||||
vol.Optional(CONF_QOS): cv.templatable(cv.mqtt_qos),
|
||||
vol.Optional(CONF_RETAIN): cv.templatable(cv.boolean),
|
||||
}),
|
||||
vol.Optional(CONF_LIGHT_TOGGLE): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
}),
|
||||
vol.Optional(CONF_LIGHT_TURN_OFF): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
}),
|
||||
vol.Optional(CONF_LIGHT_TURN_ON): 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),
|
||||
}),
|
||||
vol.Optional(CONF_SWITCH_TOGGLE): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_SWITCH_TURN_OFF): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_SWITCH_TURN_ON): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_OPEN): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_CLOSE): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_STOP): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_OPEN): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_CLOSE): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_COVER_STOP): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_FAN_TOGGLE): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_FAN_TURN_OFF): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_FAN_TURN_ON): 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(fan.validate_fan_speed),
|
||||
}),
|
||||
vol.Optional(CONF_OUTPUT_TURN_OFF): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
}),
|
||||
vol.Optional(CONF_OUTPUT_TURN_ON): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None)
|
||||
}),
|
||||
vol.Optional(CONF_OUTPUT_SET_LEVEL): {
|
||||
vol.Required(CONF_ID): cv.use_variable_id(None),
|
||||
vol.Required(CONF_LEVEL): cv.percentage,
|
||||
},
|
||||
vol.Optional(CONF_DEEP_SLEEP_ENTER): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(deep_sleep.DeepSleepComponent),
|
||||
}),
|
||||
vol.Optional(CONF_DEEP_SLEEP_PREVENT): maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(deep_sleep.DeepSleepComponent),
|
||||
}),
|
||||
vol.Optional(CONF_IF): 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)),
|
||||
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
}, cv.has_exactly_one_key(*ACTION_KEYS))])
|
||||
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({
|
||||
@@ -176,17 +78,36 @@ RangeCondition = esphomelib_ns.RangeCondition
|
||||
LambdaCondition = esphomelib_ns.LambdaCondition
|
||||
|
||||
|
||||
def validate_automation(extra_schema=None):
|
||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||
schema = AUTOMATION_SCHEMA.extend(extra_schema or {})
|
||||
|
||||
def validator(value):
|
||||
def validator_(value):
|
||||
if isinstance(value, list):
|
||||
return schema({CONF_THEN: value})
|
||||
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})
|
||||
return schema(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
|
||||
|
||||
@@ -195,7 +116,7 @@ AUTOMATION_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(None),
|
||||
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(None),
|
||||
vol.Optional(CONF_IF): CONDITIONS_SCHEMA,
|
||||
vol.Required(CONF_THEN): ACTIONS_SCHEMA,
|
||||
vol.Required(CONF_THEN): validate_recursive_action,
|
||||
})
|
||||
|
||||
|
||||
@@ -245,227 +166,84 @@ def build_conditions(config, arg_type):
|
||||
yield ArrayInitializer(*conditions)
|
||||
|
||||
|
||||
def build_action(full_config, arg_type):
|
||||
from esphomeyaml.components import light, mqtt, switch
|
||||
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)
|
||||
# Keep pylint from freaking out
|
||||
var = None
|
||||
action_id = full_config[CONF_ACTION_ID]
|
||||
key, config = next((k, v) for k, v in full_config.items() if k in ACTION_KEYS)
|
||||
if key == CONF_DELAY:
|
||||
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
|
||||
elif key == CONF_LAMBDA:
|
||||
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)
|
||||
elif key == CONF_MQTT_PUBLISH:
|
||||
rhs = App.Pget_mqtt_client().Pmake_publish_action(template_arg)
|
||||
type = mqtt.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_))
|
||||
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
|
||||
|
||||
for template_ in templatable(config[CONF_PAYLOAD], arg_type, std_string):
|
||||
|
||||
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.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
|
||||
elif key == CONF_LIGHT_TOGGLE:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
add(action.add_then(actions))
|
||||
if CONF_ELSE in config:
|
||||
for actions in build_actions(config[CONF_ELSE], arg_type):
|
||||
yield None
|
||||
rhs = var.make_toggle_action(template_arg)
|
||||
type = light.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
|
||||
elif key == CONF_LIGHT_TURN_OFF:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = light.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
|
||||
elif key == CONF_LIGHT_TURN_ON:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = light.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
|
||||
elif key == CONF_SWITCH_TOGGLE:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_toggle_action(template_arg)
|
||||
type = switch.ToggleAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_SWITCH_TURN_OFF:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = switch.TurnOffAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_SWITCH_TURN_ON:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = switch.TurnOnAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_COVER_OPEN:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_open_action(template_arg)
|
||||
type = cover.OpenAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_COVER_CLOSE:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_close_action(template_arg)
|
||||
type = cover.CloseAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_COVER_STOP:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_stop_action(template_arg)
|
||||
type = cover.StopAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_FAN_TOGGLE:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_toggle_action(template_arg)
|
||||
type = fan.ToggleAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_FAN_TURN_OFF:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = fan.TurnOffAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_FAN_TURN_ON:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = fan.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, fan.FanSpeed):
|
||||
yield None
|
||||
add(action.set_speed(template_))
|
||||
yield action
|
||||
elif key == CONF_OUTPUT_TURN_OFF:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
type = output.TurnOffAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_OUTPUT_TURN_ON:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
type = output.TurnOnAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_OUTPUT_SET_LEVEL:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_set_level_action(template_arg)
|
||||
type = output.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
|
||||
elif key == CONF_IF:
|
||||
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
|
||||
elif key == CONF_DEEP_SLEEP_ENTER:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_enter_deep_sleep_action(template_arg)
|
||||
type = deep_sleep.EnterDeepSleepAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
elif key == CONF_DEEP_SLEEP_PREVENT:
|
||||
for var in get_variable(config[CONF_ID]):
|
||||
yield None
|
||||
rhs = var.make_prevent_deep_sleep_action(template_arg)
|
||||
type = deep_sleep.PreventDeepSleepAction.template(arg_type)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
else:
|
||||
raise ESPHomeYAMLError(u"Unsupported action {}".format(config))
|
||||
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):
|
||||
|
||||
@@ -33,7 +33,7 @@ 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]
|
||||
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,
|
||||
@@ -49,23 +49,22 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
|
||||
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)),
|
||||
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
|
||||
vol.Optional(CONF_ON_PRESS): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
vol.Optional(CONF_ON_PRESS): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_RELEASE): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
}),
|
||||
vol.Optional(CONF_ON_RELEASE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ReleaseTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_CLICK): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
}),
|
||||
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):
|
||||
vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(DoubleClickTrigger),
|
||||
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
|
||||
})]),
|
||||
}),
|
||||
vol.Optional(CONF_ON_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 "
|
||||
|
||||
@@ -9,14 +9,15 @@ from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \
|
||||
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_SONY, CONF_STATE
|
||||
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_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]
|
||||
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'
|
||||
@@ -26,6 +27,7 @@ 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
|
||||
@@ -42,6 +44,9 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
|
||||
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)),
|
||||
@@ -71,10 +76,12 @@ def receiver_base(full_config):
|
||||
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[CONF_RAW], multiline=False)
|
||||
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]),
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
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
|
||||
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \
|
||||
TemplateArguments, get_variable
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
@@ -37,3 +41,50 @@ def setup_cover(cover_obj, mqtt_obj, config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_COVER'
|
||||
|
||||
CONF_COVER_OPEN = 'cover.open'
|
||||
COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(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)
|
||||
|
||||
@@ -13,9 +13,9 @@ 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(),
|
||||
vol.Optional(CONF_CLOSE_ACTION): automation.validate_automation(),
|
||||
vol.Optional(CONF_STOP_ACTION): automation.validate_automation(),
|
||||
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))
|
||||
|
||||
|
||||
|
||||
@@ -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, esphomelib_ns
|
||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression, esphomelib_ns, \
|
||||
TemplateArguments, get_variable
|
||||
|
||||
|
||||
def validate_pin_number(value):
|
||||
@@ -57,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)
|
||||
|
||||
@@ -4,8 +4,10 @@ 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
|
||||
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression
|
||||
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']
|
||||
|
||||
@@ -32,6 +34,11 @@ def to_code(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)
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ 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
|
||||
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression
|
||||
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']
|
||||
|
||||
@@ -16,9 +18,11 @@ 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))
|
||||
@@ -52,6 +56,11 @@ def to_code(config):
|
||||
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)
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.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
|
||||
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component
|
||||
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({
|
||||
|
||||
@@ -66,3 +69,62 @@ def setup_fan(fan_obj, mqtt_obj, config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_FAN'
|
||||
|
||||
|
||||
CONF_FAN_TOGGLE = 'fan.toggle'
|
||||
FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(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
|
||||
|
||||
@@ -29,7 +29,7 @@ def to_code(config):
|
||||
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]))
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \
|
||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \
|
||||
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_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
|
||||
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({
|
||||
|
||||
@@ -338,3 +340,111 @@ def setup_light(light_obj, mqtt_obj, 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
|
||||
|
||||
@@ -6,7 +6,7 @@ from esphomeyaml.components import light
|
||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
|
||||
from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \
|
||||
CONF_RGB_ORDER, CONF_EFFECTS
|
||||
CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT
|
||||
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
|
||||
get_variable, variable
|
||||
|
||||
@@ -66,6 +66,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
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.use_variable_id(PowerSupplyComponent),
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
|
||||
@@ -93,6 +94,10 @@ def to_code(config):
|
||||
yield
|
||||
add(fast_led.set_power_supply(power_supply))
|
||||
|
||||
if CONF_COLOR_CORRECT in config:
|
||||
r, g, b = config[CONF_COLOR_CORRECT]
|
||||
add(fast_led.set_correction(r, g, b))
|
||||
|
||||
light.setup_light(make.Pstate, make.Pmqtt, config)
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from esphomeyaml.components import light
|
||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
|
||||
from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \
|
||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \
|
||||
CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER, CONF_EFFECTS
|
||||
CONF_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
|
||||
|
||||
@@ -44,6 +44,7 @@ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||
|
||||
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
|
||||
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)),
|
||||
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
|
||||
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
|
||||
@@ -73,7 +74,11 @@ def to_code(config):
|
||||
yield
|
||||
add(fast_led.set_power_supply(power_supply))
|
||||
|
||||
light.setup_light(make.Pstate, make.Pmqtt, config)
|
||||
if CONF_COLOR_CORRECT in config:
|
||||
r, g, b = config[CONF_COLOR_CORRECT]
|
||||
add(fast_led.set_correction(r, g, b))
|
||||
|
||||
light.setup_light(make.Pstate, make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import ACTION_REGISTRY, LambdaAction
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGS, \
|
||||
CONF_TX_BUFFER_SIZE
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, global_ns
|
||||
from esphomeyaml.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_ID, CONF_LEVEL, \
|
||||
CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE
|
||||
from esphomeyaml.core import ESPHomeYAMLError, Lambda
|
||||
from esphomeyaml.helpers import App, Pvariable, TemplateArguments, add, esphomelib_ns, global_ns, \
|
||||
process_lambda, RawExpression, statement
|
||||
|
||||
LOG_LEVELS = {
|
||||
'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE,
|
||||
@@ -16,6 +20,15 @@ LOG_LEVELS = {
|
||||
'VERY_VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERY_VERBOSE,
|
||||
}
|
||||
|
||||
LOG_LEVEL_TO_ESP_LOG = {
|
||||
'ERROR': global_ns.ESP_LOGE,
|
||||
'WARN': global_ns.ESP_LOGW,
|
||||
'INFO': global_ns.ESP_LOGI,
|
||||
'DEBUG': global_ns.ESP_LOGD,
|
||||
'VERBOSE': global_ns.ESP_LOGV,
|
||||
'VERY_VERBOSE': global_ns.ESP_LOGVV,
|
||||
}
|
||||
|
||||
LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
@@ -35,7 +48,7 @@ LogComponent = esphomelib_ns.LogComponent
|
||||
|
||||
CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(LogComponent),
|
||||
vol.Optional(CONF_BAUD_RATE): cv.positive_int,
|
||||
vol.Optional(CONF_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({
|
||||
@@ -59,3 +72,57 @@ def required_build_flags(config):
|
||||
if CONF_LEVEL in config:
|
||||
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,16 +2,18 @@ import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation
|
||||
from esphomeyaml.automation import ACTION_REGISTRY
|
||||
from esphomeyaml.components import logger
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
|
||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LOG_TOPIC, \
|
||||
CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, \
|
||||
CONF_SSL_FINGERPRINTS, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, \
|
||||
CONF_WILL_MESSAGE, CONF_REBOOT_TIMEOUT, CONF_SHUTDOWN_MESSAGE
|
||||
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
|
||||
StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \
|
||||
uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef
|
||||
|
||||
|
||||
def validate_message_just_topic(value):
|
||||
@@ -35,7 +37,9 @@ 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):
|
||||
@@ -68,16 +72,23 @@ CONFIG_SCHEMA = vol.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): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
vol.Optional(CONF_ON_MESSAGE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
|
||||
vol.Required(CONF_TOPIC): cv.publish_topic,
|
||||
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||
vol.Optional(CONF_QOS, 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,
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
@@ -98,14 +109,16 @@ def to_code(config):
|
||||
rhs = App.init_mqtt(config[CONF_BROKER], config[CONF_PORT],
|
||||
config[CONF_USERNAME], config[CONF_PASSWORD])
|
||||
mqtt = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if not config.get(CONF_DISCOVERY, True):
|
||||
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 not birth_message:
|
||||
@@ -124,20 +137,28 @@ def to_code(config):
|
||||
add(mqtt.disable_shutdown_message())
|
||||
else:
|
||||
add(mqtt.set_shutdown_message(exp_mqtt_message(shutdown_message)))
|
||||
|
||||
if CONF_CLIENT_ID in config:
|
||||
add(mqtt.set_client_id(config[CONF_CLIENT_ID]))
|
||||
|
||||
if CONF_LOG_TOPIC in config:
|
||||
log_topic = config[CONF_LOG_TOPIC]
|
||||
if not log_topic:
|
||||
add(mqtt.disable_log_message())
|
||||
else:
|
||||
add(mqtt.set_log_message_template(exp_mqtt_message(log_topic)))
|
||||
|
||||
if CONF_LEVEL in config:
|
||||
add(mqtt.set_log_level(logger.LOG_LEVELS[config[CONF_LEVEL]]))
|
||||
|
||||
if CONF_SSL_FINGERPRINTS in config:
|
||||
for fingerprint in config[CONF_SSL_FINGERPRINTS]:
|
||||
arr = [RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)]
|
||||
add(mqtt.add_ssl_fingerprint(ArrayInitializer(*arr, multiline=False)))
|
||||
|
||||
if CONF_KEEPALIVE in config:
|
||||
add(mqtt.set_keep_alive(config[CONF_KEEPALIVE]))
|
||||
|
||||
if CONF_REBOOT_TIMEOUT in config:
|
||||
add(mqtt.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||
|
||||
@@ -146,6 +167,73 @@ def to_code(config):
|
||||
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,10 +1,9 @@
|
||||
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
|
||||
@@ -27,8 +26,7 @@ def to_code(config):
|
||||
rhs = App.init_ota()
|
||||
ota = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_PASSWORD in config:
|
||||
hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
||||
add(ota.set_auth_password_hash(hash_))
|
||||
add(ota.set_auth_password(config[CONF_PASSWORD]))
|
||||
if CONF_PORT in config:
|
||||
add(ota.set_port(config[CONF_PORT]))
|
||||
if config[CONF_SAFE_MODE]:
|
||||
@@ -50,6 +48,7 @@ def get_auth(config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OTA'
|
||||
REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA'
|
||||
|
||||
|
||||
def lib_deps(config):
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components.power_supply import PowerSupplyComponent
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY
|
||||
from esphomeyaml.helpers import add, esphomelib_ns, get_variable
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY, CONF_ID, CONF_LEVEL
|
||||
from esphomeyaml.helpers import add, esphomelib_ns, get_variable, TemplateArguments, Pvariable, \
|
||||
templatable, bool_
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
@@ -46,3 +48,56 @@ def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
|
||||
|
||||
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,21 +1,26 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
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
|
||||
from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression
|
||||
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),
|
||||
}),
|
||||
})])
|
||||
|
||||
|
||||
@@ -28,7 +33,11 @@ def to_code(config):
|
||||
for cs in gpio_output_pin_expression(conf[CONF_CS_PIN]):
|
||||
yield
|
||||
rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL))
|
||||
Pvariable(conf[CONF_ID], rhs)
|
||||
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'
|
||||
|
||||
@@ -15,6 +15,7 @@ DUMPERS = {
|
||||
'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,
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -64,8 +64,8 @@ HeartbeatFilter = sensor_ns.HeartbeatFilter
|
||||
DebounceFilter = sensor_ns.DebounceFilter
|
||||
UniqueFilter = sensor_ns.UniqueFilter
|
||||
|
||||
SensorValueTrigger = sensor_ns.SensorValueTrigger
|
||||
RawSensorValueTrigger = sensor_ns.RawSensorValueTrigger
|
||||
SensorStateTrigger = sensor_ns.SensorStateTrigger
|
||||
SensorRawStateTrigger = sensor_ns.SensorRawStateTrigger
|
||||
ValueRangeTrigger = sensor_ns.ValueRangeTrigger
|
||||
|
||||
SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
@@ -76,18 +76,17 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int),
|
||||
vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period_milliseconds),
|
||||
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
|
||||
vol.Optional(CONF_ON_VALUE): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorValueTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_RAW_VALUE): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(RawSensorValueTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_VALUE_RANGE): vol.All(cv.ensure_list, [vol.All(
|
||||
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))]),
|
||||
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)),
|
||||
})
|
||||
|
||||
SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SENSOR_SCHEMA.schema)
|
||||
@@ -156,11 +155,11 @@ def setup_sensor_core_(sensor_var, mqtt_var, config):
|
||||
add(sensor_var.set_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
rhs = sensor_var.make_value_trigger()
|
||||
rhs = sensor_var.make_state_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
for conf in config.get(CONF_ON_RAW_VALUE, []):
|
||||
rhs = sensor_var.make_raw_value_trigger()
|
||||
rhs = sensor_var.make_raw_state_trigger()
|
||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
@@ -170,12 +169,12 @@ def setup_sensor_core_(sensor_var, mqtt_var, config):
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_ABOVE], float_, float_):
|
||||
yield
|
||||
trigger.set_min(template_)
|
||||
add(trigger.set_min(template_))
|
||||
if CONF_BELOW in conf:
|
||||
template_ = None
|
||||
for template_ in templatable(conf[CONF_BELOW], float_, float_):
|
||||
yield
|
||||
trigger.set_max(template_)
|
||||
add(trigger.set_max(template_))
|
||||
automation.build_automation(trigger, float_, conf)
|
||||
|
||||
if CONF_EXPIRE_AFTER in config:
|
||||
|
||||
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'
|
||||
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'
|
||||
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'
|
||||
@@ -10,10 +10,10 @@ from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
XiaomiMiFloraDevice = esphomelib_ns.XiaomiMiFloraDevice
|
||||
XiaomiDevice = esphomelib_ns.XiaomiDevice
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiMiFloraDevice),
|
||||
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),
|
||||
@@ -28,7 +28,7 @@ def to_code(config):
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
rhs = hub.make_miflora_sensor(make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
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]
|
||||
|
||||
@@ -10,14 +10,14 @@ from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
XiaomiMiJiaDevice = esphomelib_ns.XiaomiMiJiaDevice
|
||||
XiaomiDevice = esphomelib_ns.XiaomiDevice
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiMiJiaDevice),
|
||||
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.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
vol.Required(CONF_HUMIDITY): 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_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA),
|
||||
})
|
||||
|
||||
@@ -26,12 +26,14 @@ def to_code(config):
|
||||
hub = None
|
||||
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
|
||||
yield
|
||||
rhs = hub.make_mijia_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS]))
|
||||
dev = Pvariable(config[CONF_MAKE_ID], rhs)
|
||||
sensor.register_sensor(dev.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.register_sensor(dev.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
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)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
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_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component
|
||||
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
|
||||
TemplateArguments, get_variable
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
@@ -50,3 +52,51 @@ def register_switch(var, config):
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SWITCH'
|
||||
|
||||
|
||||
CONF_SWITCH_TOGGLE = 'switch.toggle'
|
||||
SWITCH_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(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,14 +3,15 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
|
||||
from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable
|
||||
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
|
||||
|
||||
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,
|
||||
}))
|
||||
|
||||
|
||||
@@ -20,6 +21,10 @@ def to_code(config):
|
||||
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)
|
||||
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ from esphomeyaml.components import switch
|
||||
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, Application, get_variable, variable
|
||||
|
||||
MakeSimpleSwitch = Application.MakeSimpleSwitch
|
||||
MakeOutputSwitch = Application.MakeOutputSwitch
|
||||
|
||||
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSimpleSwitch),
|
||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeOutputSwitch),
|
||||
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
|
||||
}))
|
||||
|
||||
@@ -17,9 +17,9 @@ def to_code(config):
|
||||
output = None
|
||||
for output in get_variable(config[CONF_OUTPUT]):
|
||||
yield
|
||||
rhs = App.make_simple_switch(config[CONF_NAME], output)
|
||||
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'
|
||||
|
||||
@@ -10,15 +10,15 @@ from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_CHANNEL
|
||||
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_SONY, CONF_STATE, CONF_TIMES, \
|
||||
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_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]
|
||||
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'
|
||||
@@ -28,6 +28,7 @@ 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
|
||||
@@ -46,6 +47,9 @@ PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
|
||||
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)),
|
||||
@@ -84,6 +88,8 @@ def transmitter_base(full_config):
|
||||
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:
|
||||
|
||||
@@ -14,8 +14,8 @@ 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(),
|
||||
vol.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(),
|
||||
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))
|
||||
|
||||
|
||||
|
||||
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'
|
||||
@@ -235,7 +235,7 @@ def validate_cron_keys(value):
|
||||
|
||||
TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_TIMEZONE, default=detect_tz): cv.string,
|
||||
vol.Optional(CONF_ON_TIME): vol.All(cv.ensure_list, [vol.All(automation.validate_automation({
|
||||
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,
|
||||
@@ -244,7 +244,7 @@ TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
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)]),
|
||||
}, validate_cron_keys),
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \
|
||||
CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_REBOOT_TIMEOUT, \
|
||||
CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, ESP_PLATFORM_ESP8266
|
||||
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
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@ 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,
|
||||
@@ -77,8 +83,9 @@ CONFIG_SCHEMA = vol.All(vol.Schema({
|
||||
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
|
||||
vol.Optional(CONF_AP): WIFI_NETWORK_AP,
|
||||
vol.Optional(CONF_HOSTNAME): cv.hostname,
|
||||
vol.Optional(CONF_DOMAIN, default='.local'): cv.domainname,
|
||||
vol.Optional(CONF_DOMAIN, default='.local'): cv.domain_name,
|
||||
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||
vol.Optional(CONF_POWER_SAVE_MODE): vol.All(vol.Upper, cv.one_of(*WIFI_POWER_SAVE_MODES)),
|
||||
}), validate)
|
||||
|
||||
|
||||
@@ -127,6 +134,9 @@ def to_code(config):
|
||||
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:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "esphomeyaml",
|
||||
"version": "1.8.1",
|
||||
"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",
|
||||
@@ -15,6 +15,11 @@
|
||||
"map": [
|
||||
"config:rw"
|
||||
],
|
||||
"arch": [
|
||||
"amd64",
|
||||
"armhf",
|
||||
"i386"
|
||||
],
|
||||
"environment": {
|
||||
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ 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 color
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -28,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
|
||||
@@ -128,11 +129,7 @@ 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_config.CONFIG_SCHEMA(config[CONF_ESPHOMEYAML])
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, CONF_ESPHOMEYAML, config[CONF_ESPHOMEYAML])
|
||||
|
||||
# Step 1: Load everything
|
||||
for domain, conf in config.iteritems():
|
||||
domain = str(domain)
|
||||
if domain == CONF_ESPHOMEYAML or domain.startswith('.'):
|
||||
@@ -144,6 +141,39 @@ def validate_config(config):
|
||||
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),
|
||||
@@ -174,16 +204,13 @@ 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: {}".format(p_domain), p_domain, p_config)
|
||||
continue
|
||||
|
||||
success = True
|
||||
@@ -280,17 +307,17 @@ 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):
|
||||
@@ -308,10 +335,11 @@ def read_config(path):
|
||||
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 OrderedDict(res)
|
||||
|
||||
@@ -440,15 +440,15 @@ def hostname(value):
|
||||
return value
|
||||
|
||||
|
||||
def domainname(value):
|
||||
def domain_name(value):
|
||||
value = string(value)
|
||||
if not value.startswith('.'):
|
||||
raise vol.Invalid("Domainname must start with .")
|
||||
raise vol.Invalid("Domain name must start with .")
|
||||
if value.startswith('..'):
|
||||
raise vol.Invalid("Domainname must start with single .")
|
||||
raise vol.Invalid("Domain name must start with single .")
|
||||
for c in value:
|
||||
if not (c.isalnum() or c in '._-'):
|
||||
raise vol.Invalid("Domainname can only have alphanumeric characters and _ or -")
|
||||
raise vol.Invalid("Domain name can only have alphanumeric characters and _ or -")
|
||||
return value
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""Constants used by esphomeyaml."""
|
||||
|
||||
MAJOR_VERSION = 1
|
||||
MINOR_VERSION = 8
|
||||
PATCH_VERSION = '1'
|
||||
MINOR_VERSION = 9
|
||||
PATCH_VERSION = '0b1'
|
||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||
ESPHOMELIB_VERSION = '1.8.0'
|
||||
ESPHOMELIB_VERSION = '1.9.0b1'
|
||||
|
||||
ESP_PLATFORM_ESP32 = 'ESP32'
|
||||
ESP_PLATFORM_ESP8266 = 'ESP8266'
|
||||
@@ -161,6 +161,7 @@ CONF_COMMAND = 'command'
|
||||
CONF_DATA = 'data'
|
||||
CONF_NBITS = 'nbits'
|
||||
CONF_LG = 'lg'
|
||||
CONF_SAMSUNG = 'samsung'
|
||||
CONF_SONY = 'sony'
|
||||
CONF_PANASONIC = 'panasonic'
|
||||
CONF_REPEAT = 'repeat'
|
||||
@@ -338,6 +339,17 @@ 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'
|
||||
|
||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
|
||||
|
||||
@@ -113,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)
|
||||
@@ -242,3 +257,4 @@ CONFIG_PATH = None
|
||||
ESP_PLATFORM = ''
|
||||
BOARD = ''
|
||||
RAW_CONFIG = None
|
||||
NAME = ''
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import automation, core, pins
|
||||
from esphomeyaml.const import 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, ARDUINO_VERSION_ESP8266_DEV, \
|
||||
ARDUINO_VERSION_ESP32_DEV
|
||||
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, add, const_char_p, esphomelib_ns, \
|
||||
relative_path
|
||||
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'
|
||||
|
||||
@@ -42,12 +46,21 @@ def validate_board(value):
|
||||
def validate_simple_esphomelib_version(value):
|
||||
value = cv.string_strict(value)
|
||||
if value.upper() == 'LATEST':
|
||||
return LIBRARY_URI_REPO + '#v{}'.format(ESPHOMELIB_VERSION)
|
||||
return {
|
||||
CONF_REPOSITORY: LIBRARY_URI_REPO,
|
||||
CONF_TAG: 'v' + ESPHOMELIB_VERSION,
|
||||
}
|
||||
elif value.upper() == 'DEV':
|
||||
return LIBRARY_URI_REPO
|
||||
return {
|
||||
CONF_REPOSITORY: LIBRARY_URI_REPO,
|
||||
CONF_BRANCH: 'master'
|
||||
}
|
||||
elif VERSION_REGEX.match(value) is not None:
|
||||
return LIBRARY_URI_REPO + '#v{}'.format(value)
|
||||
return value
|
||||
return {
|
||||
CONF_REPOSITORY: LIBRARY_URI_REPO,
|
||||
CONF_TAG: 'v' + value,
|
||||
}
|
||||
raise vol.Invalid("Only simple esphomelib versions!")
|
||||
|
||||
|
||||
def validate_local_esphomelib_version(value):
|
||||
@@ -60,12 +73,11 @@ def validate_local_esphomelib_version(value):
|
||||
return value
|
||||
|
||||
|
||||
def convert_esphomelib_version_schema(value):
|
||||
if CONF_COMMIT in value:
|
||||
return value[CONF_REPOSITORY] + '#' + value[CONF_COMMIT]
|
||||
if CONF_BRANCH in value:
|
||||
return value[CONF_REPOSITORY] + '#' + value[CONF_BRANCH]
|
||||
return value[CONF_REPOSITORY] + '#' + value[CONF_TAG]
|
||||
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(
|
||||
@@ -76,12 +88,11 @@ ESPHOMELIB_VERSION_SCHEMA = vol.Any(
|
||||
vol.All(
|
||||
vol.Schema({
|
||||
vol.Optional(CONF_REPOSITORY, default=LIBRARY_URI_REPO): cv.string,
|
||||
vol.Optional(CONF_COMMIT, 'tag'): cv.string,
|
||||
vol.Optional(CONF_BRANCH, 'tag'): cv.string,
|
||||
vol.Optional(CONF_TAG, 'tag'): 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),
|
||||
convert_esphomelib_version_schema
|
||||
cv.has_at_most_one_key(CONF_COMMIT, CONF_BRANCH, CONF_TAG)
|
||||
),
|
||||
)
|
||||
|
||||
@@ -108,8 +119,8 @@ PLATFORMIO_ESP8266_LUT = {
|
||||
}
|
||||
|
||||
PLATFORMIO_ESP32_LUT = {
|
||||
'1.0.0': 'espressif32@1.3.0',
|
||||
'RECOMMENDED': 'espressif32@>=1.3.0',
|
||||
'1.0.0': 'espressif32@1.4.0',
|
||||
'RECOMMENDED': 'espressif32@>=1.4.0',
|
||||
'LATEST': 'espressif32',
|
||||
'DEV': ARDUINO_VERSION_ESP32_DEV,
|
||||
}
|
||||
@@ -138,6 +149,10 @@ def validate_arduino_version(value):
|
||||
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',
|
||||
@@ -146,22 +161,24 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
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): cv.string,
|
||||
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): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
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): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
}),
|
||||
vol.Optional(CONF_ON_SHUTDOWN): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ShutdownTrigger),
|
||||
})]),
|
||||
vol.Optional(CONF_ON_LOOP): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||
}),
|
||||
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.")
|
||||
"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."),
|
||||
})
|
||||
|
||||
|
||||
@@ -173,14 +190,52 @@ def preload_core_config(config):
|
||||
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]))
|
||||
|
||||
@@ -197,3 +252,7 @@ def to_code(config):
|
||||
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__')))
|
||||
|
||||
@@ -9,10 +9,11 @@ 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, get_base_path, get_name
|
||||
from esphomeyaml.helpers import quote
|
||||
from esphomeyaml.__main__ import get_serial_ports
|
||||
from esphomeyaml.helpers import quote, relative_path
|
||||
|
||||
try:
|
||||
import tornado
|
||||
@@ -116,6 +117,20 @@ class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket):
|
||||
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():
|
||||
@@ -161,10 +176,10 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
||||
config_file = os.path.join(CONFIG_DIR, configuration)
|
||||
core.CONFIG_PATH = config_file
|
||||
config = __main__.read_config(core.CONFIG_PATH)
|
||||
name = get_name(config)
|
||||
path = os.path.join(get_base_path(config), '.pioenvs', name, 'firmware.bin')
|
||||
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(name))
|
||||
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
|
||||
@@ -212,6 +227,8 @@ def make_app(debug=False):
|
||||
(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),
|
||||
@@ -250,6 +267,12 @@ def start_web_server(args):
|
||||
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:
|
||||
|
||||
@@ -52,12 +52,6 @@
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.chip {
|
||||
height: 26px;
|
||||
font-size: 12px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.log {
|
||||
background-color: #1c1c1c;
|
||||
margin-top: 0;
|
||||
@@ -159,6 +153,10 @@
|
||||
margin-right: 24px;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
@@ -191,7 +189,7 @@
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
{% for file, full_path in zip(files, full_path_files) %}
|
||||
{% 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">
|
||||
@@ -200,7 +198,10 @@
|
||||
</div>
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<span class="card-title">{{ escape(file) }}</span>
|
||||
<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>
|
||||
@@ -211,6 +212,10 @@
|
||||
<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>
|
||||
@@ -462,6 +467,30 @@
|
||||
</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>
|
||||
@@ -539,6 +568,7 @@
|
||||
if (allEqual)
|
||||
return;
|
||||
}
|
||||
const hasNewPort = response.length >= ports.length;
|
||||
|
||||
ports = response;
|
||||
|
||||
@@ -559,7 +589,7 @@
|
||||
}
|
||||
|
||||
M.FormSelect.init(portSelect, {});
|
||||
if (!begin)
|
||||
if (!begin && hasNewPort)
|
||||
M.toast({html: "Discovered new serial port."});
|
||||
});
|
||||
};
|
||||
@@ -784,6 +814,96 @@
|
||||
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 = () => {
|
||||
|
||||
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)
|
||||
@@ -106,7 +106,7 @@ class ExpressionList(Expression):
|
||||
self.args.append(exp)
|
||||
|
||||
def __str__(self):
|
||||
text = u", ".join(str(x) for x in self.args)
|
||||
text = u", ".join(unicode(x) for x in self.args)
|
||||
return indent_all_but_first_and_last(text)
|
||||
|
||||
|
||||
@@ -232,12 +232,13 @@ class LambdaExpression(Expression):
|
||||
cpp = u'[{}]({})'.format(self.capture, self.parameters)
|
||||
if self.return_type is not None:
|
||||
cpp += u' -> {}'.format(self.return_type)
|
||||
cpp += u' {\n'
|
||||
for part in self.parts:
|
||||
cpp += unicode(part)
|
||||
cpp += u'\n}'
|
||||
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):
|
||||
@@ -422,7 +423,6 @@ def process_lambda(value, parameters, capture='=', return_type=None):
|
||||
parts[i * 3 + 1] = var
|
||||
parts[i * 3 + 2] = ''
|
||||
yield LambdaExpression(parts, parameters, capture, return_type)
|
||||
return
|
||||
|
||||
|
||||
def templatable(value, input_type, output_type):
|
||||
@@ -536,6 +536,10 @@ class MockObj(Expression):
|
||||
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):
|
||||
@@ -568,6 +572,10 @@ 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
|
||||
|
||||
@@ -14,6 +14,7 @@ from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYA
|
||||
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
|
||||
CONF_USERNAME, CONF_TOPIC, CONF_SSL_FINGERPRINTS
|
||||
from esphomeyaml.helpers import color
|
||||
from esphomeyaml.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -70,10 +71,7 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None,
|
||||
message = time + msg.payload
|
||||
if escape:
|
||||
message = message.replace('\033', '\\033')
|
||||
try:
|
||||
print(message)
|
||||
except UnicodeEncodeError:
|
||||
print(message.encode('ascii', 'backslashreplace'))
|
||||
safe_print(message)
|
||||
|
||||
return initialize(config, [topic], on_message, username, password, client_id)
|
||||
|
||||
@@ -83,7 +81,9 @@ 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 or not msg.retain:
|
||||
@@ -111,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
|
||||
|
||||
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,13 @@ import unicodedata
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import mqtt
|
||||
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 = """ _____ ____ _____ ______
|
||||
/ ____/ __ \| __ \| ____|
|
||||
@@ -52,7 +51,7 @@ OTA_BIG = """ ____ _______
|
||||
"""
|
||||
|
||||
# TODO handle escaping
|
||||
BASE_CONFIG = """esphomeyaml:
|
||||
BASE_CONFIG = u"""esphomeyaml:
|
||||
name: {name}
|
||||
platform: {platform}
|
||||
board: {board}
|
||||
@@ -76,9 +75,9 @@ def wizard_file(**kwargs):
|
||||
config = BASE_CONFIG.format(**kwargs)
|
||||
|
||||
if kwargs['ota_password']:
|
||||
config += "ota:\n password: '{}'\n".format(kwargs['ota_password'])
|
||||
config += u"ota:\n password: '{}'\n".format(kwargs['ota_password'])
|
||||
else:
|
||||
config += "ota:\n"
|
||||
config += u"ota:\n"
|
||||
|
||||
return config
|
||||
|
||||
@@ -90,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
|
||||
|
||||
|
||||
@@ -113,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:
|
||||
@@ -144,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:
|
||||
@@ -179,18 +178,18 @@ 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): "))
|
||||
@@ -198,71 +197,73 @@ def wizard(path):
|
||||
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:
|
||||
@@ -271,19 +272,19 @@ 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 = wizard_file(name=name, platform=platform, board=board,
|
||||
@@ -294,19 +295,19 @@ def wizard(path):
|
||||
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/guides/getting_started_command_line.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
|
||||
|
||||
@@ -3,17 +3,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 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_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 ============'
|
||||
INI_AUTO_GENERATE_BEGIN = u'; ========== AUTO GENERATED CODE BEGIN ==========='
|
||||
@@ -98,6 +102,7 @@ def get_ini_content(config, path):
|
||||
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')
|
||||
|
||||
@@ -111,12 +116,19 @@ def get_ini_content(config, path):
|
||||
lib_version = config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION]
|
||||
lib_path = os.path.join(path, 'lib')
|
||||
dst_path = os.path.join(lib_path, 'esphomelib')
|
||||
if isinstance(lib_version, (str, unicode)):
|
||||
lib_deps.add(lib_version)
|
||||
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)
|
||||
else:
|
||||
src_path = relative_path(lib_version[CONF_LOCAL])
|
||||
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)
|
||||
@@ -139,6 +151,25 @@ def get_ini_content(config, path):
|
||||
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')
|
||||
@@ -202,7 +233,6 @@ 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]
|
||||
@@ -213,6 +243,7 @@ def write_platformio_ini(content, path):
|
||||
|
||||
|
||||
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:
|
||||
@@ -263,3 +294,11 @@ def determine_platformio_version_settings():
|
||||
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)
|
||||
|
||||
@@ -35,6 +35,7 @@ wifi:
|
||||
hostname: myverylonghostname
|
||||
domain: .local
|
||||
reboot_timeout: 120s
|
||||
power_save_mode: none
|
||||
|
||||
mqtt:
|
||||
broker: '192.168.178.84'
|
||||
@@ -46,7 +47,9 @@ mqtt:
|
||||
discovery_retain: False
|
||||
discovery_prefix: discovery
|
||||
topic_prefix: helloworld
|
||||
log_topic: helloworld/hi
|
||||
log_topic:
|
||||
topic: helloworld/hi
|
||||
level: INFO
|
||||
birth_message:
|
||||
will_message:
|
||||
shutdown_message:
|
||||
@@ -70,6 +73,24 @@ mqtt:
|
||||
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
|
||||
@@ -160,9 +181,9 @@ sensor:
|
||||
then:
|
||||
- lambda: >-
|
||||
ESP_LOGD("main", "Got value %f", x);
|
||||
id(my_sensor).push_new_value(42.0);
|
||||
ESP_LOGI("main", "Value of my sensor: %f", id(my_sensor).value);
|
||||
ESP_LOGI("main", "Raw Value of my sensor: %f", id(my_sensor).value);
|
||||
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
|
||||
@@ -172,6 +193,11 @@ sensor:
|
||||
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
|
||||
@@ -398,7 +424,7 @@ sensor:
|
||||
- platform: template
|
||||
name: "Template Sensor"
|
||||
lambda: >-
|
||||
if (id(ultrasonic_sensor1).value > 1) {
|
||||
if (id(ultrasonic_sensor1).state > 1) {
|
||||
return 42.0;
|
||||
} else {
|
||||
return {};
|
||||
@@ -444,6 +470,7 @@ binary_sensor:
|
||||
- invert:
|
||||
- delayed_on: 40ms
|
||||
- delayed_off: 40ms
|
||||
- heartbeat: 1s
|
||||
on_press:
|
||||
then:
|
||||
- lambda: >-
|
||||
@@ -484,12 +511,12 @@ binary_sensor:
|
||||
- platform: template
|
||||
name: "Garage Door Open"
|
||||
lambda: >-
|
||||
if (isnan(id(my_sensor).value)) {
|
||||
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).value > 30) {
|
||||
} else if (id(my_sensor).state > 30) {
|
||||
// Garage Door is open.
|
||||
return true;
|
||||
} else {
|
||||
@@ -595,6 +622,7 @@ light:
|
||||
state = 0;
|
||||
- platform: rgb
|
||||
name: "Living Room Lights"
|
||||
id: living_room_lights
|
||||
red: pca_0
|
||||
green: pca_1
|
||||
blue: pca_2
|
||||
@@ -626,6 +654,7 @@ light:
|
||||
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:
|
||||
@@ -686,6 +715,7 @@ switch:
|
||||
icon: "mdi:restart"
|
||||
inverted: True
|
||||
command_topic: custom_command_topic
|
||||
power_on_value: True
|
||||
- platform: remote_transmitter
|
||||
name: "Panasonic TV Off"
|
||||
nec:
|
||||
@@ -698,6 +728,11 @@ switch:
|
||||
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:
|
||||
@@ -775,17 +810,17 @@ switch:
|
||||
name: "Template Switch"
|
||||
id: my_switch
|
||||
lambda: |-
|
||||
if (id(binary_sensor1).value) {
|
||||
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).value) {
|
||||
if (id(my_switch).state) {
|
||||
// Switch is ON, do something here
|
||||
id(my_switch).write_state(false);
|
||||
id(my_switch).write_state(true);
|
||||
id(my_switch).turn_off();
|
||||
id(my_switch).turn_on();
|
||||
} else {
|
||||
// Switch is OFF, do something else here
|
||||
}
|
||||
@@ -868,6 +903,12 @@ status_led:
|
||||
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:
|
||||
|
||||
@@ -887,7 +928,7 @@ cover:
|
||||
- platform: template
|
||||
name: "Template Cover"
|
||||
lambda: >-
|
||||
if (id(binary_sensor1).value) {
|
||||
if (id(binary_sensor1).state) {
|
||||
return cover::COVER_OPEN;
|
||||
} else {
|
||||
return {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
esphomeyaml:
|
||||
name: test1
|
||||
name: test2
|
||||
platform: ESP32
|
||||
board: nodemcu-32s
|
||||
# Use latest upstream esphomelib git version.
|
||||
@@ -73,7 +73,50 @@ sensor:
|
||||
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
|
||||
@@ -100,3 +143,26 @@ esp32_ble_beacon:
|
||||
|
||||
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