Compare commits

...

151 Commits

Author SHA1 Message Date
Otto Winter
70206df8b5 Fix gitlab 2018-10-20 18:26:44 +02:00
Otto Winter
15732ca465 Bump version to 1.9.0b1 2018-10-20 18:24:02 +02:00
Otto Winter
8e0f4f93d4 Fix HassIO add-on archs 2018-10-20 18:20:42 +02:00
Otto Winter
361baea17f Add beta builds 2018-10-20 18:20:21 +02:00
Otto Winter
8bbfbc4cc1 Add logger.log action (#198)
* Add logger.log Action

* Simple schema

* Validate printf

* Improve error message

* Undo unfix tests :)
2018-10-20 15:19:59 +02:00
Otto Winter
8c5d12df51 Fix some typos (#202) 2018-10-20 15:18:12 +02:00
Otto Winter
25c66ed8ca Improve API naming convention consistency (#197)
* Improve API naming convention consistency

* Fix
2018-10-20 15:16:58 +02:00
Otto Winter
7a55521807 Fix rebase error 2018-10-20 15:08:55 +02:00
Otto Winter
d04de7baeb Fix value range trigger 😑 (#201) 2018-10-20 14:40:46 +02:00
Otto Winter
4aeb756388 Fix triggers being interpreted as a sequence of automations (#199) 2018-10-20 13:15:09 +02:00
Otto Winter
92b6ed4180 Add FastLED color correction option (#200)
* Add FastLED color correction option

* Add test
2018-10-20 13:14:02 +02:00
Otto Winter
9efd9f4fe8 Add PMSX003 Particulate Matter Sensor (#192) 2018-10-20 12:58:52 +02:00
Otto Winter
34fc2147a4 Add update component action and scripts (#196)
* Add update component action

* Add script component
2018-10-20 12:58:02 +02:00
Otto Winter
629f2b128e Add MQTT publish JSON action and subscribe JSON trigger (#193)
* Add MQTT publish JSON action and subscribe JSON trigger

* Lint
2018-10-20 12:41:00 +02:00
Otto Winter
81bc400340 Add PN532 On Tag Trigger (#189)
* Add PN532 On Tag Trigger

* Lint

* Fix 😶

* Fix
2018-10-17 21:29:44 +02:00
Otto Winter
75628b96a1 Add Text Sensors (#166)
* Text Sensors

* Add version text sensor

* Fix

* Fixes

* Add tests

* Add template text sensor

* Lint

* Fix test
2018-10-17 21:24:02 +02:00
Otto Winter
b1f7ed4fdc Add CSE776 for Sonoff Pow R2 (#190) 2018-10-17 21:14:31 +02:00
Otto Winter
b8d7185d99 Unify Xiaomi implementations (#188) 2018-10-17 20:56:55 +02:00
Otto Winter
2d20a1c0fb Decentralize Automation Generator Code (#182)
* Decentralize Automation Generator Code

* Lint
2018-10-16 23:16:06 +02:00
Otto Winter
820067ae5a GPIO Switch Power On Value v2 (#183) 2018-10-16 22:58:26 +02:00
Otto Winter
db8313e0d5 Fix config dump time output (#184) 2018-10-16 20:45:24 +02:00
Otto Winter
27a77c685d Rework OTA to be more stable (#177)
* Rework OTA to be more stable

* Lint
2018-10-14 19:14:28 +02:00
Otto Winter
e34365dc7c Add clean build files command and auto-clean on version change (#181)
* Add clean build files command and auto-clean on version change

* Update __main__.py
2018-10-14 18:52:21 +02:00
Otto Winter
8d395e5338 MQTT different log level (#167)
* Add option to have different log level over MQTT

* Add Test

* Lint
2018-10-14 18:46:17 +02:00
Otto Winter
6f54afec00 Add MQTT Subscribe sensor (#175) 2018-10-14 18:45:13 +02:00
Otto Winter
6a24145be6 Fix Wifi power_save_mode option (#178)
* Fix WIFI power_save_mode option

* Add Test
2018-10-13 21:17:19 +02:00
escoand
4a2cdbf31c Add Samsung IR protocol (#176)
* add Samsung ir protocol

* fix pylint

* add test

* add transmitter
2018-10-13 19:21:06 +02:00
Otto Winter
1d75ed1ff4 Add use_build_flags removal notice (#173) 2018-10-12 12:12:07 +02:00
Otto Winter
76b1c6f47b Fix readme broken link (#174) 2018-10-12 12:05:48 +02:00
Otto Winter
06371c9e2d Create issue templates (#171)
* Create issue templates

* Update bug_report.md
2018-10-12 11:27:14 +02:00
Otto Winter
a9c130dd50 Add Code of Conduct (Contributor Covenant) (#168) 2018-10-12 11:26:26 +02:00
Otto Winter
1f82c1a483 Create CONTRIBUTING.md (#169) 2018-10-12 11:26:05 +02:00
Otto Winter
cb28429231 Create Pull Request Template (#172)
* Create PULL_REQUEST_TEMPLATE.md

* Update PULL_REQUEST_TEMPLATE.md

* Rename PULL_REQUEST_TEMPLATE.md to .github/PULL_REQUEST_TEMPLATE.md
2018-10-12 11:25:43 +02:00
Otto Winter
f2cd2ec178 Fix raw remote receiver (#158) 2018-10-12 09:49:10 +02:00
Otto Winter
37360bb797 Log esphomelib version and compilation time on boot (#159) 2018-10-12 09:48:55 +02:00
JonnyaiR
6c1dc0f2b3 Add a link to Home Assistant in README (#152)
As "Home Assistant" could be interpreted as a generic term for automating a home, i've added links to the Home-Assistant website :-)
2018-10-08 11:00:02 +02:00
Otto Winter
f7671f0c90 Bump version to 1.8.2 2018-10-07 17:01:15 +02:00
Otto Winter
639b97ccb2 WiFi: Add power save mode option (#150)
* WiFi: Add power save mode option

* Lint
2018-10-07 16:52:14 +02:00
Otto Winter
0374b3a0b3 Fix component loader value error (#149)
* Print better error message when loader fails with ValueError

* Improve

* Improve
2018-10-07 14:38:22 +02:00
Otto Winter
7ce753b76f Docker default to starting dashboard (#143) 2018-10-04 19:01:57 +02:00
Otto Winter
05a1089ed2 Bump platformio-espressif32 to 1.4.0 (#142) 2018-10-04 19:01:24 +02:00
Otto Winter
71947bb6ac Fix using unicode in lambdas (#141)
https://github.com/OttoWinter/esphomeyaml/issues/128#issuecomment-425777989
2018-10-04 19:01:13 +02:00
Otto Winter
ef54e33b70 Add clean MQTT button to dashboard (#139) 2018-10-04 19:01:02 +02:00
Otto Winter
12f20fc3cf Fix serial monitor opening when logger disabled (#138)
Fixes #137
2018-10-04 19:00:51 +02:00
Otto Winter
ffdcddc18e Add SSD1306 64x48 display (#136) 2018-09-29 14:50:14 +02:00
Otto Winter
85d70eb5a0 Auto-Update esphomelib dev version (#134)
* Auto-Update esphomelib dev version

* Lint
2018-09-29 14:50:04 +02:00
Otto Winter
ffb793177a Enable Travis Tests (#133) 2018-09-28 19:34:28 +02:00
Otto Winter
d3f2fab88a Fix SSD1306 lambda (#132)
As per #128
2018-09-28 18:17:22 +02:00
Otto Winter
0fa52d0ce6 Fix MQTT discovery enabled when discovery_retain in config (#131) 2018-09-27 16:34:11 +02:00
Otto Winter
cf264a2743 Fix binary sensor heartbeat not working (#130) 2018-09-27 16:34:02 +02:00
Otto Winter
433b605bef Bump version to 1.8.1 2018-09-26 19:00:41 +02:00
Otto Winter
6e60c6493a Use cache for HassIO add-on images 2018-09-26 18:59:39 +02:00
Otto Winter
4e63bc96d5 Fix main docker image missing platformio 2018-09-26 18:39:55 +02:00
Otto Winter
ce4b339d16 Split up BLE tests into new file 2018-09-26 18:39:41 +02:00
Otto Winter
ab6d293d0d Fix docker installs using old platformio version (#125)
* Fix min platformio version and update requirements

* Remove unnecessary requirements from travis
2018-09-27 01:14:51 +09:00
Otto Winter
5e5137960d Limit upload speed to 115200 (#122)
* Limit upload speed to 115200

* Lint
2018-09-26 01:27:47 +09:00
Otto Winter
8dba37846b Bump version to 1.8.0 2018-09-25 13:43:45 +02:00
Otto Winter
5cd82d7c25 Try fix docker build 2018-09-25 13:26:01 +02:00
Otto Winter
bc8354bad5 Lint 2018-09-25 10:36:12 +02:00
Otto Winter
2abbe1bca3 Updates 2018-09-25 10:30:45 +02:00
Otto Winter
74c70509c2 Fix docker build 2018-09-25 10:30:45 +02:00
Otto Winter
1576e1847e Lint 2018-09-25 10:30:45 +02:00
John Erik Halse
9ea9b4b102 Use tls for connecting to mqtt broker when fingerprints is set (#118) 2018-09-24 22:27:38 +09:00
Adriaan Peeters
490743c26e Add ‘only-generate’ parameter to generate command to only generate the C++ code (#84)
* Introduce parser_compile variable

* Add ‘only-generate’ parameter to the compile command which only generates the C++ code
2018-09-19 21:51:39 +09:00
Otto Winter
1c7bddd005 Last Touches 2018-08-25 22:18:22 +02:00
Otto Winter
5c39f73fda Further changes 2018-08-24 22:44:15 +02:00
Otto Winter
2fc78a1b33 Fixes for release-candidate 2018-08-22 22:04:56 +02:00
Otto Winter
03249780fd Displays 2018-08-18 21:40:59 +02:00
Otto Winter
5170a7cdf4 Smallish Update 2018-08-13 19:11:33 +02:00
Otto Winter
5680de79a9 Create LICENSE 2018-07-28 16:33:01 +02:00
Otto Winter
61bd2a3a44 Revert "Revert "Add password to esphomeyaml non-dev config""
This reverts commit 7802c63f5a.
2018-06-14 10:08:27 +02:00
Otto Winter
7802c63f5a Revert "Add password to esphomeyaml non-dev config"
This reverts commit 94bef2a5a4.
2018-06-14 09:39:18 +02:00
Otto Winter
94bef2a5a4 Add password to esphomeyaml non-dev config 2018-06-14 09:29:53 +02:00
Otto Winter
12c4b0788c Small fixes 2018-06-14 00:14:01 +02:00
Otto Winter
b13cc3a4a5 Fix lint 2018-06-13 23:01:50 +02:00
Otto Winter
c5d9bc5452 Bump version to 1.7.0 2018-06-13 22:56:49 +02:00
Otto Winter
6c6d21a7ab Allow simpler automation syntax 2018-06-13 21:27:58 +02:00
Otto Winter
9bb06782b2 Automate HassIO builds using Gitlab CI (#52)
* Add Gitlab CI

* Fix aarch64 edge build
2018-06-13 11:13:53 +02:00
Otto Winter
a827b51887 Add ESP32 BLE Beacon 2018-06-12 21:18:04 +02:00
Otto Winter
2c30d80490 Fix multi wifi validation 2018-06-11 16:13:46 +02:00
Otto Winter
e0acdc3ae0 Revert Multi Wifi 2018-06-11 15:22:03 +02:00
Otto Winter
36a3f96011 Fix RF receiver dumpers 2018-06-11 15:21:38 +02:00
Otto Winter
6ae8e3495f Add build_path option. Fixes#49 2018-06-11 10:12:44 +02:00
Otto Winter
6aa449115f Add internal modifier. Fixes ottowinter/esphomelib#77 2018-06-11 10:01:54 +02:00
Otto Winter
0be29d27d5 Fixes esphomelib#72 2018-06-08 20:33:14 +02:00
Otto Winter
68fa7489a2 Improve time config validation 2018-06-08 20:33:14 +02:00
Johan Bloemberg
d7699c93d6 Fix deprecation of board_flash_mode parameter (#41)
* Fix deprecation of board_flash_mode parameter

Fixes `Warning! `board_flash_mode` option is deprecated and will be removed in the next release! Please use `board_build.flash_mode` instead.`

* Don't break flashing for older installations.
2018-06-08 11:34:06 +02:00
Johan Bloemberg
a04438e924 Make sure logs after upload works when using explicit OTA. (#42) 2018-06-08 10:41:01 +02:00
Otto Winter
e063f2aaea Fix hmac with non-ASCII passwords 2018-06-07 21:52:41 +02:00
Otto Winter
7b630bfb8b Fix HassIO edge config 2018-06-07 21:22:11 +02:00
Otto Winter
ec3366cce0 Dashboard authentication 2018-06-07 20:47:06 +02:00
Johan Bloemberg
135117714b Support specifying hostname/ip as --upload-port (#36) 2018-06-07 20:36:00 +02:00
Brandon Davidson
91e6304505 Add WiFi Signal Strength sensor (#35) 2018-06-07 19:29:18 +02:00
Otto Winter
361a9da868 on_boot and on_shutdown triggers 2018-06-07 17:06:50 +02:00
Otto Winter
31d7656c07 Lint 2018-06-07 17:04:33 +02:00
Otto Winter
d1a7751dc9 SHT3X-D Remove Accuracy Option 2018-06-07 11:27:04 +02:00
Otto Winter
e47bcb9abb Bump version to 1.6.2 2018-06-06 21:01:20 +02:00
Otto Winter
30b94f06b3 Add verbose mode for espota 2018-06-06 11:12:42 +02:00
Otto Winter
19dfdb77eb Fix MQTT logs 2018-06-06 11:12:29 +02:00
Otto Winter
b6e7ad3589 Fix OTA port not being set 2018-06-06 11:12:14 +02:00
Otto Winter
1267680379 Improve error messages 2018-06-06 10:27:59 +02:00
Otto Winter
b5f6b32fad Fix dallas resolution 2018-06-06 10:27:50 +02:00
Otto Winter
7068808d4f Fix optional returns from templates 2018-06-06 09:48:59 +02:00
Otto Winter
c82a44d541 Fix ESP32 touch sleep duration conversion 2018-06-06 09:30:14 +02:00
Otto Winter
e650473682 Fix ESP32 touch ID issue 2018-06-06 09:26:56 +02:00
Otto Winter
2ee0d4242d Make Automation output readable again 2018-06-06 09:18:39 +02:00
Otto Winter
b2aecf29dc Fix ESP32 BLE 2018-06-06 09:18:21 +02:00
Otto Winter
081c2f4e07 Fix SHT3xD with manual update interval 2018-06-06 09:06:14 +02:00
Otto Winter
0c77228421 Improve wrong pin error message 2018-06-06 08:55:53 +02:00
Otto Winter
8a509c6551 Fix MQTT publish 2018-06-06 08:35:31 +02:00
Otto Winter
d249820bcd Fix MQTT availability option 2018-06-06 08:35:23 +02:00
Otto Winter
f39cf52eae Fix WiFi domain option 2018-06-06 08:13:05 +02:00
Otto Winter
63fb252b46 Fix web server CSS/JS URL 2018-06-06 08:12:51 +02:00
Otto Winter
2f98adca49 Fix OrFilter 2018-06-06 08:12:39 +02:00
Otto Winter
084fc00517 Fix MQTT messages 2018-06-06 08:12:30 +02:00
Otto Winter
abeac23abd Fix i2c frequency 2018-06-06 08:12:17 +02:00
Otto Winter
7264e3a4bf Fix ultrasonic typo Fixes #62 2018-06-06 07:09:03 +02:00
Otto Winter
16eeb3af31 Attempt to solve UnicodeEncodeError in HassIO when running on RPi 2018-06-05 23:19:46 +02:00
Otto Winter
b799d2df7f Adjust serial port fetch interval 2018-06-05 23:19:28 +02:00
Otto Winter
11d55fec4f Better OSError handling for YAML parser 2018-06-05 23:18:56 +02:00
Otto Winter
923a5990c7 Fix erroneous validation. Fixes OttoWinter/esphomelib#59 2018-06-05 22:32:34 +02:00
Otto Winter
a0a615c335 Deep Sleep wakeup pin mode 2018-06-05 22:31:39 +02:00
Otto Winter
e9ced0cc3f Fix lint 2018-06-04 20:47:29 +02:00
Otto Winter
1a92381994 Update Dockerfile 2018-06-04 20:47:23 +02:00
Otto Winter
a07a7a87a2 Fix filter out nan filter 2018-06-04 20:47:15 +02:00
Otto Winter
6a823f5777 Bump version to 1.6.1 2018-06-03 23:45:57 +02:00
Otto Winter
419c5afe27 Update Dockerfiles 2018-06-03 23:45:29 +02:00
Otto Winter
17798dee1e HassIO add-on pre-built images 2018-06-03 19:22:25 +02:00
Otto Winter
ee2c53585f Add Sonoff S20 example 2018-06-03 13:00:40 +02:00
Otto Winter
2f05acfa5a Validate configuration button 2018-06-03 12:16:43 +02:00
Otto Winter
f4d393a59e Fix dashboard upload port selection 2018-06-03 11:18:53 +02:00
Otto Winter
967aa53bad add_job 2018-06-03 07:11:11 +02:00
Otto Winter
4f3f460105 ESP32 Hall Sensor 2018-06-02 23:25:13 +02:00
Otto Winter
6e85a741ae New coroutine-based task execution 2018-06-02 22:22:20 +02:00
Otto Winter
2db45898e2 Sonoff 4CH example with automation 2018-06-02 15:41:11 +02:00
Otto Winter
eb62599a98 ADC VCC support Fixes #26 2018-06-02 13:43:49 +02:00
Otto Winter
9f0737e5b9 Fix pylint... Again... 2018-06-01 23:01:31 +02:00
Otto Winter
7a393c1a3a Fix template/switch cover example. Fixes #27 2018-06-01 22:58:23 +02:00
Otto Winter
4fa7bc196a Warn about tornado. Fixes #24 2018-06-01 22:49:14 +02:00
Brandon Davidson
65d0dd47f3 Fix #27 - invalid code gen for Template Cover (#29) 2018-06-01 22:48:07 +02:00
Otto Winter
d88634b196 Bump version to 1.6.0 2018-06-01 18:48:27 +02:00
Otto Winter
976627eb38 HassIO add-on 2018-06-01 18:45:23 +02:00
Otto Winter
5b995c0692 Updates 2018-06-01 18:06:18 +02:00
Otto Winter
2d4b475951 Fix cover automation 2018-05-27 15:00:56 +02:00
Otto Winter
93d962dd43 HassIO -> dashboard 2018-05-27 14:15:24 +02:00
Otto Winter
2e7d8540fb Update dev library URI 2018-05-21 20:55:39 +02:00
Otto Winter
677fe8bacf More Actions 2018-05-21 17:01:47 +02:00
Otto Winter
94d7ac4ef0 HassIO add-on (#18)
* HassIO Beginnings

* Updates

* Fix pylint errors

* Fix pylint error
2018-05-21 16:40:22 +02:00
Jimmy Hedman
ebb6d0d464 Add domain paramteter to wifi. (#16)
* Add domain paramteter to wifi.

- To be able to do OTA updates on networks that doesn't use .local as
  local domain parameter domain is added to wifi section. It's currently
  only used for OTA.

* Centralised default parameter for domain.

* Added input validation for domainname.
2018-05-21 15:47:30 +02:00
Jimmy Hedman
e8fe653140 Fix flake8 warnings. (#17) 2018-05-21 15:07:17 +02:00
Otto Winter
374ea7044c Automation API & Cleanup 2018-05-20 12:41:52 +02:00
185 changed files with 11464 additions and 2522 deletions

View File

@@ -106,3 +106,5 @@ venv.bak/
config/
examples/
Dockerfile
.git/
tests/build/

47
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View 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:**

View 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.

View 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
View File

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

1
.gitignore vendored
View File

@@ -104,3 +104,4 @@ venv.bak/
.mypy_cache/
config/
tests/build/

233
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,233 @@
---
# Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml
variables:
DOCKER_DRIVER: overlay2
stages:
- lint
- test
- build
- deploy
.lint: &lint
stage: lint
tags:
- python2.7
- esphomeyaml-lint
.test: &test
stage: test
before_script:
- pip install -e .
tags:
- python2.7
- esphomeyaml-test
variables:
TZ: UTC
cache:
paths:
- tests/build
.docker-builder: &docker-builder
before_script:
- docker info
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
services:
- docker:dind
tags:
- hassio-builder
flake8:
<<: *lint
script:
- flake8 esphomeyaml
pylint:
<<: *lint
script:
- pylint esphomeyaml
test1:
<<: *test
script:
- esphomeyaml tests/test1.yaml compile
test2:
<<: *test
script:
- esphomeyaml tests/test2.yaml compile
.build-hassio: &build-hassio
<<: *docker-builder
stage: build
script:
- |
hassio-builder.sh \
-t . \
-i ottowinter/esphomeyaml-hassio-${ADDON_ARCH} \
-d "$CI_REGISTRY" \
--${ADDON_ARCH}
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev"
retry: 2
# Generic deploy template
.deploy-release: &deploy-release
<<: *docker-builder
stage: deploy
script:
- version=${CI_COMMIT_TAG:1}
- echo "Publishing release version ${version}"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
only:
- /^v\d+\.\d+\.\d+$/
except:
- /^(?!master).+@/
.deploy-beta: &deploy-beta
<<: *docker-builder
stage: deploy
script:
- version=${CI_COMMIT_TAG:1}
- echo "Publishing beta version ${version}"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:beta"
only:
- /^v\d+\.\d+\.\d+b\d+$/
except:
- /^(?!rc).+@/
# Build jobs
build:normal:
<<: *docker-builder
stage: build
script:
- docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" .
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml:dev" \
"${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:${CI_COMMIT_SHA}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml:dev"
build:armhf:
<<: *build-hassio
variables:
ADDON_ARCH: armhf
#build:aarch64:
# <<: *build
# variables:
# ADDON_ARCH: aarch64
build:i386:
<<: *build-hassio
variables:
ADDON_ARCH: i386
build:amd64:
<<: *build-hassio
variables:
ADDON_ARCH: amd64
# Deploy jobs
deploy-release:armhf:
<<: *deploy-release
variables:
ADDON_ARCH: armhf
deploy-beta:armhf:
<<: *deploy-beta
variables:
ADDON_ARCH: armhf
#deploy-release:aarch64:
# <<: *deploy-release
# variables:
# ADDON_ARCH: aarch64
#deploy-beta:aarch64:
# <<: *deploy-beta
# variables:
# ADDON_ARCH: aarch64
deploy-release:i386:
<<: *deploy-release
variables:
ADDON_ARCH: i386
deploy-beta:i386:
<<: *deploy-beta
variables:
ADDON_ARCH: i386
deploy-release:amd64:
<<: *deploy-release
variables:
ADDON_ARCH: amd64
deploy-beta:amd64:
<<: *deploy-beta
variables:
ADDON_ARCH: amd64
.deploy-pypi: &deploy-pypi
stage: deploy
before_script:
- pip install -e .
- pip install twine
script:
- python setup.py sdist
- twine upload dist/*
tags:
- python2.7
- esphomeyaml-test
deploy-release:pypi:
<<: *deploy-pypi
only:
- /^v\d+\.\d+\.\d+$/
except:
- /^(?!master).+@/
deploy-beta:pypi:
<<: *deploy-pypi
only:
- /^v\d+\.\d+\.\d+b\d+$/
except:
- /^(?!rc).+@/

View File

@@ -2,9 +2,19 @@ sudo: false
language: python
python:
- "2.7"
install:
- pip install -r requirements.txt
- pip install flake8==3.5.0 pylint==1.8.4
script:
- flake8 esphomeyaml
- pylint esphomeyaml
jobs:
include:
- name: "Lint"
install:
- pip install -r requirements.txt
- pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow
script:
- flake8 esphomeyaml
- pylint esphomeyaml
- name: "Test"
install:
- pip install -e .
- pip install tzlocal pillow
script:
- esphomeyaml tests/test1.yaml compile
- esphomeyaml tests/test2.yaml compile

46
CODE_OF_CONDUCT.md Normal file
View 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
View 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.

View File

@@ -1,20 +1,26 @@
FROM python:2.7
MAINTAINER Otto Winter <contact@otto-winter.com>
RUN apt-get update && apt-get install -y \
python-pil \
&& rm -rf /var/lib/apt/lists/*
ENV ESPHOMEYAML_OTA_HOST_PORT=6123
EXPOSE 6123
VOLUME /config
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
COPY docker/platformio.ini /usr/src/app/
RUN platformio settings set enable_telemetry No && \
platformio run -e espressif32 -e espressif8266; exit 0
COPY . .
RUN pip install -e .
RUN pip install --no-cache-dir -e . && \
pip install --no-cache-dir tzlocal pillow
WORKDIR /config
ENTRYPOINT ["esphomeyaml"]
CMD ["/config", "dashboard"]

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Otto Winter
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
MANIFEST.in Normal file
View File

@@ -0,0 +1,4 @@
include README.md
include esphomeyaml/dashboard/templates/index.html
include esphomeyaml/dashboard/static/materialize-stepper.min.css
include esphomeyaml/dashboard/static/materialize-stepper.min.js

View File

@@ -1,10 +1,10 @@
# esphomeyaml for [esphomelib](https://github.com/OttoWinter/esphomelib)
### Getting Started Guide: https://esphomelib.com/esphomeyaml/getting-started.html
### Getting Started Guide: https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html
### Available Components: https://esphomelib.com/esphomeyaml/index.html
esphomeyaml is the solution for your ESP8266/ESP32 projects with Home Assistant. It allows you to create **custom firmwares** for your microcontrollers with no programming experience required. All you need to know is the YAML configuration format which is also used by Home Assistant.
esphomeyaml is the solution for your ESP8266/ESP32 projects with Home Assistant. It allows you to create **custom firmwares** for your microcontrollers with no programming experience required. All you need to know is the YAML configuration format which is also used by [Home Assistant](https://www.home-assistant.io).
esphomeyaml will:
@@ -26,7 +26,7 @@ esphomeyaml configuration.yaml run
files like you're used to with Home Assistant.
* **Flexible:** Use [esphomelib](https://github.com/OttoWinter/esphomelib)'s powerful core to create custom sensors/outputs.
* **Fast and efficient:** Written in C++ and keeps memory consumption to a minimum.
* **Made for Home Assistant:** Almost all Home Assistant features are supported out of the box. Including RGB lights and many more.
* **Made for [Home Assistant](https://www.home-assistant.io):** Almost all [Home Assistant](https://www.home-assistant.io) features are supported out of the box. Including RGB lights and many more.
* **Easy reproducible configuration:** No need to go through a long setup process for every single node. Just copy a configuration file and run a single command.
* **Smart Over The Air Updates:** esphomeyaml has OTA updates deeply integrated into the system. It even automatically enters a recovery mode if a boot loop is detected.
* **Powerful logging engine:** View colorful logs and debug issues remotely.

21
docker/Dockerfile.aarch64 Normal file
View File

@@ -0,0 +1,21 @@
# Dockerfile for aarch64 version of HassIO add-on
FROM arm64v8/ubuntu:bionic
RUN apt-get update && apt-get install -y --no-install-recommends \
python \
python-pip \
python-setuptools \
python-pil \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]

21
docker/Dockerfile.amd64 Normal file
View File

@@ -0,0 +1,21 @@
# Dockerfile for amd64 version of HassIO add-on
FROM ubuntu:bionic
RUN apt-get update && apt-get install -y --no-install-recommends \
python \
python-pip \
python-setuptools \
python-pil \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]

31
docker/Dockerfile.armhf Normal file
View File

@@ -0,0 +1,31 @@
# Dockerfile for armhf version of HassIO add-on
FROM homeassistant/armhf-base:latest
RUN apk add --no-cache \
python2 \
python2-dev \
py2-pip \
git \
gcc \
openssh \
libc6-compat \
jpeg-dev \
zlib-dev \
freetype-dev \
lcms2-dev \
openjpeg-dev \
tiff-dev \
libc-dev \
linux-headers \
&& \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
COPY docker/platformio-esp8266.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir pillow tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]

32
docker/Dockerfile.builder Normal file
View File

@@ -0,0 +1,32 @@
FROM multiarch/ubuntu-core:amd64-xenial
# setup locals
RUN apt-get update && apt-get install -y \
jq \
git \
python3-setuptools \
&& rm -rf /var/lib/apt/lists/* \
ENV LANG C.UTF-8
# Install docker
# https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/
RUN apt-get update && apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
software-properties-common \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
&& add-apt-repository "deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
&& apt-get update && apt-get install -y docker-ce \
&& rm -rf /var/lib/apt/lists/*
# setup arm binary support
RUN apt-get update && apt-get install -y \
qemu-user-static \
binfmt-support \
&& rm -rf /var/lib/apt/lists/*
COPY docker/hassio-builder.sh /usr/bin/
WORKDIR /data

21
docker/Dockerfile.i386 Normal file
View File

@@ -0,0 +1,21 @@
# Dockerfile for i386 version of HassIO add-on
FROM i386/ubuntu:bionic
RUN apt-get update && apt-get install -y --no-install-recommends \
python \
python-pip \
python-setuptools \
python-pil \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]

6
docker/Dockerfile.lint Normal file
View File

@@ -0,0 +1,6 @@
FROM python:2.7
COPY requirements.txt /requirements.txt
RUN pip install -r /requirements.txt && \
pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow

19
docker/Dockerfile.test Normal file
View File

@@ -0,0 +1,19 @@
FROM ubuntu:bionic
RUN apt-get update && apt-get install -y --no-install-recommends \
python \
python-pip \
python-setuptools \
python-pil \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No
COPY docker/platformio.ini /pio/platformio.ini
RUN platformio run -d /pio; rm -rf /pio
COPY requirements.txt /requirements.txt
RUN pip install --no-cache-dir -r /requirements.txt && \
pip install --no-cache-dir tzlocal pillow

318
docker/hassio-builder.sh Executable file
View File

@@ -0,0 +1,318 @@
#!/usr/bin/env bash
# Based on Home Assistant's docker builder
######################
# Hass.io Build-env
######################
set -e
echo -- "$@"
#### Variable ####
DOCKER_TIMEOUT=20
DOCKER_PID=-1
DOCKER_HUB=""
DOCKER_CACHE="true"
DOCKER_LOCAL="false"
TARGET=""
IMAGE=""
BUILD_LIST=()
BUILD_TASKS=()
#### Misc functions ####
function print_help() {
cat << EOF
Hass.io build-env for ecosystem:
docker run --rm homeassistant/{arch}-builder:latest [options]
Options:
-h, --help
Display this help and exit.
Repository / Data
-t, --target <PATH_TO_BUILD>
Set local folder or path inside repository for build.
Version/Image handling
-i, --image <IMAGE_NAME>
Overwrite image name of build / support {arch}
Architecture
--armhf
Build for arm.
--amd64
Build for intel/amd 64bit.
--aarch64
Build for arm 64bit.
--i386
Build for intel/amd 32bit.
--all
Build all architecture.
Build handling
--no-cache
Disable cache for the build (from latest).
-d, --docker-hub <DOCKER_REPOSITORY>
Set or overwrite the docker repository.
Use the host docker socket if mapped into container:
/var/run/docker.sock
EOF
exit 1
}
#### Docker functions ####
function start_docker() {
local starttime
local endtime
if [ -S "/var/run/docker.sock" ]; then
echo "[INFO] Use host docker setup with '/var/run/docker.sock'"
DOCKER_LOCAL="true"
return 0
fi
echo "[INFO] Starting docker."
dockerd 2> /dev/null &
DOCKER_PID=$!
echo "[INFO] Waiting for docker to initialize..."
starttime="$(date +%s)"
endtime="$(date +%s)"
until docker info >/dev/null 2>&1; do
if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then
sleep 1
endtime=$(date +%s)
else
echo "[ERROR] Timeout while waiting for docker to come up"
exit 1
fi
done
echo "[INFO] Docker was initialized"
}
function stop_docker() {
local starttime
local endtime
if [ "$DOCKER_LOCAL" == "true" ]; then
return 0
fi
echo "[INFO] Stopping in container docker..."
if [ "$DOCKER_PID" -gt 0 ] && kill -0 "$DOCKER_PID" 2> /dev/null; then
starttime="$(date +%s)"
endtime="$(date +%s)"
# Now wait for it to die
kill "$DOCKER_PID"
while kill -0 "$DOCKER_PID" 2> /dev/null; do
if [ $((endtime - starttime)) -le ${DOCKER_TIMEOUT} ]; then
sleep 1
endtime=$(date +%s)
else
echo "[ERROR] Timeout while waiting for container docker to die"
exit 1
fi
done
else
echo "[WARN] Your host might have been left with unreleased resources"
fi
}
function run_build() {
local build_dir=$1
local repository=$2
local image=$3
local version=$4
local build_arch=$5
local docker_cli=("${!6}")
local push_images=()
# Overwrites
if [ ! -z "$DOCKER_HUB" ]; then repository="$DOCKER_HUB"; fi
if [ ! -z "$IMAGE" ]; then image="$IMAGE"; fi
# Init Cache
if [ "$DOCKER_CACHE" == "true" ]; then
echo "[INFO] Init cache for $repository/$image:$version"
if docker pull "$repository/$image:latest" > /dev/null 2>&1; then
docker_cli+=("--cache-from" "$repository/$image:latest")
else
docker_cli+=("--no-cache")
echo "[WARN] No cache image found. Cache is disabled for build"
fi
else
docker_cli+=("--no-cache")
fi
# Build image
echo "[INFO] Run build for $repository/$image:$version"
docker build --pull -t "$repository/$image:$version" \
--label "io.hass.version=$version" \
--label "io.hass.arch=$build_arch" \
-f "$TARGET/docker/Dockerfile.$build_arch" \
"${docker_cli[@]}" \
"$build_dir"
echo "[INFO] Finish build for $repository/$image:$version"
docker tag "$repository/$image:$version" "$repository/$image:dev"
}
#### HassIO functions ####
function build_addon() {
local build_arch=$1
local docker_cli=()
local image=""
local repository=""
local raw_image=""
local name=""
local description=""
local url=""
local args=""
# Read addon config.json
name="$(jq --raw-output '.name // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")"
description="$(jq --raw-output '.description // empty' "$TARGET/esphomeyaml/config.json" | sed "s/'//g")"
url="$(jq --raw-output '.url // empty' "$TARGET/esphomeyaml/config.json")"
version="$(jq --raw-output '.version' "$TARGET/esphomeyaml/config.json")"
raw_image="$(jq --raw-output '.image // empty' "$TARGET/esphomeyaml/config.json")"
# Read data from image
if [ ! -z "$raw_image" ]; then
repository="$(echo "$raw_image" | cut -f 1 -d '/')"
image="$(echo "$raw_image" | cut -f 2 -d '/')"
fi
# Set additional labels
docker_cli+=("--label" "io.hass.name=$name")
docker_cli+=("--label" "io.hass.description=$description")
docker_cli+=("--label" "io.hass.type=addon")
if [ ! -z "$url" ]; then
docker_cli+=("--label" "io.hass.url=$url")
fi
# Start build
run_build "$TARGET" "$repository" "$image" "$version" \
"$build_arch" docker_cli[@]
}
#### initialized cross-build ####
function init_crosscompile() {
echo "[INFO] Setup crosscompiling feature"
(
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
update-binfmts --enable qemu-arm
update-binfmts --enable qemu-aarch64
) > /dev/null 2>&1 || echo "[WARN] Can't enable crosscompiling feature"
}
function clean_crosscompile() {
echo "[INFO] Clean crosscompiling feature"
if [ -f /proc/sys/fs/binfmt_misc ]; then
umount /proc/sys/fs/binfmt_misc || true
fi
(
update-binfmts --disable qemu-arm
update-binfmts --disable qemu-aarch64
) > /dev/null 2>&1 || echo "[WARN] No crosscompiling feature found for cleanup"
}
#### Error handling ####
function error_handling() {
stop_docker
clean_crosscompile
exit 1
}
trap 'error_handling' SIGINT SIGTERM
#### Parse arguments ####
while [[ $# -gt 0 ]]; do
key=$1
case ${key} in
-h|--help)
print_help
;;
-t|--target)
TARGET=$2
shift
;;
-i|--image)
IMAGE=$2
shift
;;
--no-cache)
DOCKER_CACHE="false"
;;
-d|--docker-hub)
DOCKER_HUB=$2
shift
;;
--armhf)
BUILD_LIST+=("armhf")
;;
--amd64)
BUILD_LIST+=("amd64")
;;
--i386)
BUILD_LIST+=("i386")
;;
--aarch64)
BUILD_LIST+=("aarch64")
;;
--all)
BUILD_LIST=("armhf" "amd64" "i386" "aarch64")
;;
*)
echo "[WARN] $0 : Argument '$1' unknown will be Ignoring"
;;
esac
shift
done
# Check if an architecture is available
if [ "${#BUILD_LIST[@]}" -eq 0 ]; then
echo "[ERROR] You need select an architecture for build!"
exit 1
fi
#### Main ####
mkdir -p /data
# Setup docker env
init_crosscompile
start_docker
# Select arch build
for arch in "${BUILD_LIST[@]}"; do
(build_addon "$arch") &
BUILD_TASKS+=($!)
done
# Wait until all build jobs are done
wait "${BUILD_TASKS[@]}"
# Cleanup docker env
clean_crosscompile
stop_docker
exit 0

View File

@@ -0,0 +1,7 @@
; This file allows the docker build file to install the required platformio
; platforms
[env:espressif8266]
platform = espressif8266
board = nodemcuv2
framework = arduino

View File

@@ -1,12 +1,12 @@
; This file allows the docker build file to install the required platformio
; platforms
[env:espressif32]
platform = espressif32
board = nodemcu-32s
framework = arduino
[env:espressif8266]
platform = espressif8266
board = nodemcuv2
framework = arduino
[env:espressif32]
platform = espressif32
board = nodemcu-32s
framework = arduino

View 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}"
}

View File

@@ -0,0 +1,59 @@
# Dockerfile for HassIO add-on
ARG BUILD_FROM=ubuntu:bionic
FROM ${BUILD_FROM}
# Re-declare BUILD_FROM to fix weird docker issue
ARG BUILD_FROM
# On amd64 and alike, using ubuntu as the base is better as building
# for the ESP32 only works with glibc (and ubuntu). However, on armhf
# the build toolchain frequently procudes segfaults under ubuntu.
# -> Use ubuntu for most architectures, except alpine for armhf
#
# * python and related required because this is a python project
# * git required for platformio library dependencies downloads
# * libc6-compat and openssh required on alpine for weird reasons
# * disable platformio telemetry on install
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
apt-get update && apt-get install -y --no-install-recommends \
python python-pip python-setuptools python-pil git && \
rm -rf /var/lib/apt/lists/* /tmp/*; \
else \
apk add --no-cache \
python2 \
python2-dev \
py2-pip \
git \
gcc \
openssh \
libc6-compat \
jpeg-dev \
zlib-dev \
freetype-dev \
lcms2-dev \
openjpeg-dev \
tiff-dev \
libc-dev \
linux-headers; \
fi" && \
pip install --no-cache-dir platformio && \
platformio settings set enable_telemetry No
# Create fake project to make platformio install all depdencies.
# * Ignore build errors from platformio - empty project
# * On alpine, only install ESP8266 toolchain
COPY platformio.ini /pio/platformio.ini
RUN /bin/bash -c "if [[ '$BUILD_FROM' = *\"ubuntu\"* ]]; then \
platformio run -e espressif32 -e espressif8266 -d /pio; exit 0; \
else \
echo \"\$(head -8 /pio/platformio.ini)\" >/pio/platformio.ini; \
platformio run -e espressif8266 -d /pio; exit 0; \
fi"
# Install latest esphomeyaml from git
RUN pip install --no-cache-dir \
git+git://github.com/OttoWinter/esphomeyaml.git && \
pip install --no-cache-dir pillow tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]

View File

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

View File

@@ -0,0 +1,33 @@
{
"name": "esphomeyaml-edge",
"version": "dev",
"slug": "esphomeyaml-edge",
"description": "Development build of the esphomeyaml HassIO add-on.",
"url": "https://esphomelib.com/esphomeyaml/index.html",
"startup": "application",
"webui": "http://[HOST]:[PORT:6052]",
"boot": "auto",
"ports": {
"6052/tcp": 6052,
"6053/tcp": 6053
},
"arch": [
"aarch64",
"amd64",
"armhf",
"i386"
],
"auto_uart": true,
"map": [
"config:rw"
],
"options": {
"password": ""
},
"schema": {
"password": "str?"
},
"environment": {
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
}
}

View File

@@ -0,0 +1,12 @@
; This file allows the docker build file to install the required platformio
; platforms
[env:espressif8266]
platform = espressif8266
board = nodemcuv2
framework = arduino
[env:espressif32]
platform = espressif32
board = nodemcu-32s
framework = arduino

View File

@@ -5,50 +5,46 @@ import logging
import os
import random
import sys
from datetime import datetime
from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const
from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOGGER, \
CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \
color, get_variable, indent, quote, statement, Expression
from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util
from esphomeyaml.config import get_component, iter_components, read_config
from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF_ESPHOMEYAML, \
CONF_HOSTNAME, CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_USE_CUSTOM_CODE, \
CONF_WIFI, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \
_EXPRESSIONS, add, \
add_job, color, flush_tasks, indent, quote, statement, relative_path
from esphomeyaml.util import safe_print
_LOGGER = logging.getLogger(__name__)
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c']
def get_name(config):
return config[CONF_ESPHOMEYAML][CONF_NAME]
def get_base_path(config):
return os.path.join(os.path.dirname(core.CONFIG_PATH), get_name(config))
def discover_serial_ports():
def get_serial_ports():
# from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
try:
from serial.tools.list_ports import comports
except ImportError:
return None
from serial.tools.list_ports import comports
result = []
descs = []
for port, desc, info in comports():
if not port:
continue
if "VID:PID" in info:
result.append(port)
descs.append(desc)
result.append((port, desc))
return result
def choose_serial_port(config):
result = get_serial_ports()
if not result:
return None
print(u"Found multiple serial port options, please choose one:")
for i, (res, desc) in enumerate(zip(result, descs)):
print(u" [{}] {} ({})".format(i, res, desc))
print(u" [{}] Over The Air".format(len(result)))
print()
return 'OTA'
safe_print(u"Found multiple serial port options, please choose one:")
for i, (res, desc) in enumerate(result):
safe_print(u" [{}] {} ({})".format(i, res, desc))
safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
safe_print()
while True:
opt = raw_input('(number): ')
if opt in result:
@@ -60,13 +56,13 @@ def discover_serial_ports():
raise ValueError
break
except ValueError:
print(color('red', u"Invalid option: '{}'".format(opt)))
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
if opt == len(result):
return None
return result[opt]
return 'OTA'
return result[opt][0]
def run_platformio(*cmd):
def run_platformio(*cmd, **kwargs):
def mock_exit(return_code):
raise SystemExit(return_code)
@@ -75,10 +71,13 @@ def run_platformio(*cmd):
full_cmd = u' '.join(quote(x) for x in cmd)
_LOGGER.info(u"Running: %s", full_cmd)
try:
import platformio.__main__
func = kwargs.get('main')
if func is None:
import platformio.__main__
func = platformio.__main__.main
sys.argv = list(cmd)
sys.exit = mock_exit
return platformio.__main__.main()
return func() or 0
except KeyboardInterrupt:
return 1
except SystemExit as err:
@@ -91,109 +90,159 @@ def run_platformio(*cmd):
sys.exit = orig_exit
def run_miniterm(config, port):
from serial.tools import miniterm
baud_rate = config.get(CONF_LOGGER, {}).get(CONF_BAUD_RATE, 115200)
sys.argv = ['miniterm', '--raw', '--exit-char', '3']
miniterm.main(
default_port=port,
default_baudrate=baud_rate)
def run_miniterm(config, port, escape=False):
import serial
if CONF_LOGGER not in config:
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
return
baud_rate = config['logger'][CONF_BAUD_RATE]
if baud_rate == 0:
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
with serial.Serial(port, baudrate=baud_rate) as ser:
while True:
try:
raw = ser.readline()
except serial.SerialException:
_LOGGER.error("Serial port closed!")
return
line = raw.replace('\r', '').replace('\n', '')
time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line
if escape:
message = message.replace('\033', '\\033')
safe_print(message)
def write_cpp(config):
_LOGGER.info("Generating C++ source...")
add_task(core_to_code, config[CONF_ESPHOMEYAML])
add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml')
for domain in PRE_INITIALIZE:
if domain == CONF_ESPHOMEYAML:
if domain == CONF_ESPHOMEYAML or domain not in config:
continue
if domain in config:
add_task(get_component(domain).to_code, config[domain])
# Clear queue
get_variable(None)
add(RawStatement(''))
add_job(get_component(domain).to_code, config[domain], domain=domain)
for domain, component, conf in iter_components(config):
if domain in PRE_INITIALIZE:
if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'):
continue
if not hasattr(component, 'to_code'):
continue
add_task(component.to_code, conf)
add_job(component.to_code, conf, domain=domain)
# Clear queue
get_variable(None)
flush_tasks()
add(RawStatement(''))
add(RawStatement(''))
all_code = []
for exp in _EXPRESSIONS:
if core.SIMPLIFY:
if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
if isinstance(exp, Expression) and not exp.required:
continue
if isinstance(exp, AssignmentExpression) and not exp.obj.required:
if not exp.has_side_effects():
continue
exp = exp.rhs
all_code.append(unicode(statement(exp)))
platformio_ini_s = writer.get_ini_content(config)
ini_path = os.path.join(get_base_path(config), 'platformio.ini')
writer.write_platformio_ini(platformio_ini_s, ini_path)
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
writer.write_platformio_project(config, build_path)
code_s = indent('\n'.join(line.rstrip() for line in all_code))
cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp')
cpp_path = os.path.join(build_path, 'src', 'main.cpp')
writer.write_cpp(code_s, cpp_path)
return 0
def compile_program(config):
def compile_program(args, config):
_LOGGER.info("Compiling app...")
return run_platformio('platformio', 'run', '-d', get_base_path(config))
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
command = ['platformio', 'run', '-d', build_path]
if args.verbose:
command.append('-v')
return run_platformio(*command)
def get_upload_host(config):
if CONF_MANUAL_IP in config[CONF_WIFI]:
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
elif CONF_HOSTNAME in config[CONF_WIFI]:
host = config[CONF_WIFI][CONF_HOSTNAME] + config[CONF_WIFI][CONF_DOMAIN]
else:
host = config[CONF_ESPHOMEYAML][CONF_NAME] + config[CONF_WIFI][CONF_DOMAIN]
return host
def upload_using_esptool(config, port):
import esptool
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
# pylint: disable=protected-access
return run_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0',
path, main=esptool._main)
def upload_program(config, args, port):
_LOGGER.info("Uploading binary...")
if port is not None:
return run_platformio('platformio', 'run', '-d', get_base_path(config),
'-t', 'upload', '--upload-port', port)
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
# if upload is to a serial port use platformio, otherwise assume ota
serial_port = port.startswith('/') or port.startswith('COM')
if port != 'OTA' and serial_port:
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy:
return upload_using_esptool(config, port)
command = ['platformio', 'run', '-d', build_path,
'-t', 'upload', '--upload-port', port]
if args.verbose:
command.append('-v')
return run_platformio(*command)
if 'ota' not in config:
_LOGGER.error("No serial port found and OTA not enabled. Can't upload!")
return -1
if CONF_MANUAL_IP in config[CONF_WIFI]:
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
elif CONF_HOSTNAME in config[CONF_WIFI]:
host = config[CONF_WIFI][CONF_HOSTNAME] + u'.local'
# If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf
# hostname. This is to support use cases where zeroconf (hostname.local) does not work.
if port != 'OTA':
host = port
else:
host = config[CONF_ESPHOMEYAML][CONF_NAME] + u'.local'
host = get_upload_host(config)
from esphomeyaml.components import ota
from esphomeyaml import espota
from esphomeyaml import espota2
bin_file = os.path.join(get_base_path(config), '.pioenvs', get_name(config), 'firmware.bin')
bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
if args.host_port is not None:
host_port = args.host_port
else:
host_port = int(os.getenv('ESPHOMEYAML_OTA_HOST_PORT', random.randint(10000, 60000)))
espota_args = ['espota.py', '--debug', '--progress', '-i', host,
'-p', str(ota.get_port(config)), '-f', bin_file,
'-a', ota.get_auth(config), '-P', str(host_port)]
return espota.main(espota_args)
verbose = args.verbose
remote_port = ota.get_port(config)
password = ota.get_auth(config)
res = espota2.run_ota(host, remote_port, password, bin_file)
if res == 0:
return res
_LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...")
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, bin_file)
def show_logs(config, args, port):
if port is not None and port != 'OTA':
run_miniterm(config, port)
def show_logs(config, args, port, escape=False):
serial_port = port.startswith('/') or port.startswith('COM')
if port != 'OTA' and serial_port:
run_miniterm(config, port, escape=escape)
return 0
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id,
escape=escape)
def clean_mqtt(config, args):
return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id)
def setup_log():
logging.basicConfig(level=logging.INFO)
def setup_log(debug=False):
log_level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(level=log_level)
fmt = "%(levelname)s [%(name)s] %(message)s"
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
datefmt = '%H:%M:%S'
@@ -218,22 +267,154 @@ def setup_log():
pass
def main():
setup_log()
def command_wizard(args):
return wizard.wizard(args.configuration)
def strip_default_ids(config):
value = config
if isinstance(config, list):
value = type(config)()
for x in config:
if isinstance(x, core.ID) and not x.is_manual:
continue
value.append(strip_default_ids(x))
return value
elif isinstance(config, dict):
value = type(config)()
for k, v in config.iteritems():
if isinstance(v, core.ID) and not v.is_manual:
continue
value[k] = strip_default_ids(v)
return value
return value
def command_config(args, config):
if not args.verbose:
config = strip_default_ids(config)
safe_print(yaml_util.dump(config))
return 0
def command_compile(args, config):
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
if args.only_generate:
_LOGGER.info(u"Successfully generated source code.")
return 0
exit_code = compile_program(args, config)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully compiled program.")
return 0
def command_upload(args, config):
port = args.upload_port or choose_serial_port(config)
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully uploaded program.")
return 0
def command_logs(args, config):
port = args.serial_port or choose_serial_port(config)
return show_logs(config, args, port, escape=args.escape)
def command_run(args, config):
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
exit_code = compile_program(args, config)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully compiled program.")
port = args.upload_port or choose_serial_port(config)
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully uploaded program.")
if args.no_logs:
return 0
return show_logs(config, args, port, escape=args.escape)
def command_clean_mqtt(args, config):
return clean_mqtt(config, args)
def command_mqtt_fingerprint(args, config):
return mqtt.get_fingerprint(config)
def command_version(args):
safe_print(u"Version: {}".format(const.__version__))
return 0
def command_clean(args, config):
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
try:
writer.clean_build(build_path)
except OSError as err:
_LOGGER.error("Error deleting build files: %s", err)
return 1
_LOGGER.info("Done!")
return 0
def command_dashboard(args):
from esphomeyaml.dashboard import dashboard
return dashboard.start_web_server(args)
PRE_CONFIG_ACTIONS = {
'wizard': command_wizard,
'version': command_version,
'dashboard': command_dashboard
}
POST_CONFIG_ACTIONS = {
'config': command_config,
'compile': command_compile,
'upload': command_upload,
'logs': command_logs,
'run': command_run,
'clean-mqtt': command_clean_mqtt,
'mqtt-fingerprint': command_mqtt_fingerprint,
'clean': command_clean,
}
def parse_args(argv):
parser = argparse.ArgumentParser(prog='esphomeyaml')
parser.add_argument('-v', '--verbose', help="Enable verbose esphomeyaml logs.",
action='store_true')
parser.add_argument('configuration', help='Your YAML configuration file.')
subparsers = parser.add_subparsers(help='Commands', dest='command')
subparsers.required = True
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
subparsers.add_parser('compile', help='Read the configuration and compile a program.')
parser_compile = subparsers.add_parser('compile',
help='Read the configuration and compile a program.')
parser_compile.add_argument('--only-generate',
help="Only generate source code, do not compile.",
action='store_true')
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
'and upload the latest binary.')
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
"For example /dev/cu.SLAB_USBtoUART.")
parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
parser_upload.add_argument('--use-esptoolpy',
help="Use esptool.py for the uploading (only for ESP8266)",
action='store_true')
parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
'and show all MQTT logs.')
@@ -243,10 +424,12 @@ def main():
parser_logs.add_argument('--client-id', help='Manually set the client id.')
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
"For example /dev/cu.SLAB_USBtoUART.")
parser_logs.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
action='store_true')
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
'upload it, and start MQTT logs.')
parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. "
parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
"For example /dev/cu.SLAB_USBtoUART.")
parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
@@ -255,6 +438,11 @@ def main():
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
parser_run.add_argument('--password', help='Manually set the MQTT password for logs.')
parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
parser_run.add_argument('--escape', help="Escape ANSI color codes for running in dashboard",
action='store_true')
parser_run.add_argument('--use-esptoolpy',
help="Use esptool.py for the uploading (only for ESP8266)",
action='store_true')
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
"retain messages.")
@@ -267,12 +455,32 @@ def main():
"you through setting up esphomeyaml.")
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
args = parser.parse_args()
subparsers.add_parser('clean', help="Delete all temporary build files.")
if args.command == 'wizard':
return wizard.wizard(args.configuration)
dashboard = subparsers.add_parser('dashboard',
help="Create a simple web server for a dashboard.")
dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int,
default=6052)
dashboard.add_argument("--password", help="The optional password to require for all requests.",
type=str, default='')
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
action='store_true')
return parser.parse_args(argv[1:])
def run_esphomeyaml(argv):
args = parse_args(argv)
setup_log(args.verbose)
if args.command in PRE_CONFIG_ACTIONS:
try:
return PRE_CONFIG_ACTIONS[args.command](args)
except ESPHomeYAMLError as e:
_LOGGER.error(e)
return 1
core.CONFIG_PATH = args.configuration
@@ -280,54 +488,25 @@ def main():
if config is None:
return 1
if args.command == 'config':
print(yaml_util.dump(config))
return 0
elif args.command == 'compile':
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
exit_code = compile_program(config)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully compiled program.")
return 0
elif args.command == 'upload':
port = args.upload_port or discover_serial_ports()
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully uploaded program.")
return 0
elif args.command == 'logs':
port = args.serial_port or discover_serial_ports()
return show_logs(config, args, port)
elif args.command == 'clean-mqtt':
return clean_mqtt(config, args)
elif args.command == 'mqtt-fingerprint':
return mqtt.get_fingerprint(config)
elif args.command == 'run':
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
exit_code = compile_program(config)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully compiled program.")
port = args.upload_port or discover_serial_ports()
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
_LOGGER.info(u"Successfully uploaded program.")
if args.no_logs:
return 0
return show_logs(config, args, port)
elif args.command == 'version':
print(u"Version: {}".format(const.__version__))
return 0
print(u"Unknown command {}".format(args.command))
if args.command in POST_CONFIG_ACTIONS:
try:
return POST_CONFIG_ACTIONS[args.command](args, config)
except ESPHomeYAMLError as e:
_LOGGER.error(e)
return 1
safe_print(u"Unknown command {}".format(args.command))
return 1
def main():
try:
return run_esphomeyaml(sys.argv)
except ESPHomeYAMLError as e:
_LOGGER.error(e)
return 1
except KeyboardInterrupt:
return 1
if __name__ == "__main__":
sys.exit(main())

276
esphomeyaml/automation.py Normal file
View File

@@ -0,0 +1,276 @@
import voluptuous as vol
from esphomeyaml import core
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, \
CONF_BELOW, CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, \
CONF_ELSE, CONF_ID, CONF_IF, CONF_LAMBDA, \
CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, TemplateArguments, add, add_job, \
esphomelib_ns, float_, process_lambda, templatable, uint32, get_variable
from esphomeyaml.util import ServiceRegistry
def maybe_simple_id(*validators):
validator = vol.All(*validators)
def validate(value):
if isinstance(value, dict):
return validator(value)
return validator({CONF_ID: value})
return validate
def validate_recursive_condition(value):
return CONDITIONS_SCHEMA(value)
def validate_recursive_action(value):
value = cv.ensure_list(value)
for i, item in enumerate(value):
if not isinstance(item, dict):
raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item))
key = next((x for x in item if x != CONF_ACTION_ID), None)
if key is None:
raise vol.Invalid(u"Key missing from action! Got {}".format(item))
if key not in ACTION_REGISTRY:
raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?"
u"".format(key))
item.setdefault(CONF_ACTION_ID, None)
key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None)
if key2 is not None:
raise vol.Invalid(u"Cannot have two actions in one item. Key {} overrides {}!"
u"".format(key, key2))
validator = ACTION_REGISTRY[key][0]
value[i] = {
CONF_ACTION_ID: cv.declare_variable_id(None)(item[CONF_ACTION_ID]),
key: validator(item[key])
}
return value
ACTION_REGISTRY = ServiceRegistry()
# pylint: disable=invalid-name
DelayAction = esphomelib_ns.DelayAction
LambdaAction = esphomelib_ns.LambdaAction
IfAction = esphomelib_ns.IfAction
UpdateComponentAction = esphomelib_ns.UpdateComponentAction
Automation = esphomelib_ns.Automation
CONDITIONS_SCHEMA = vol.All(cv.ensure_list, [cv.templatable({
cv.GenerateID(CONF_CONDITION_ID): cv.declare_variable_id(None),
vol.Optional(CONF_AND): validate_recursive_condition,
vol.Optional(CONF_OR): validate_recursive_condition,
vol.Optional(CONF_RANGE): vol.All(vol.Schema({
vol.Optional(CONF_ABOVE): vol.Coerce(float),
vol.Optional(CONF_BELOW): vol.Coerce(float),
}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)),
vol.Optional(CONF_LAMBDA): cv.lambda_,
})])
# pylint: disable=invalid-name
AndCondition = esphomelib_ns.AndCondition
OrCondition = esphomelib_ns.OrCondition
RangeCondition = esphomelib_ns.RangeCondition
LambdaCondition = esphomelib_ns.LambdaCondition
def validate_automation(extra_schema=None, extra_validators=None, single=False):
schema = AUTOMATION_SCHEMA.extend(extra_schema or {})
def validator_(value):
if isinstance(value, list):
try:
# First try as a sequence of actions
return [schema({CONF_THEN: value})]
except vol.Invalid as err:
# Next try as a sequence of automations
try:
return vol.Schema([schema])(value)
except vol.Invalid as err2:
raise vol.MultipleInvalid([err, err2])
elif isinstance(value, dict):
if CONF_THEN in value:
return [schema(value)]
return [schema({CONF_THEN: value})]
# This should only happen with invalid configs, but let's have a nice error message.
return [schema(value)]
def validator(value):
value = validator_(value)
if extra_validators is not None:
value = vol.Schema([extra_validators])(value)
if single:
if len(value) != 1:
raise vol.Invalid("Cannot have more than 1 automation for templates")
return value[0]
return value
return validator
AUTOMATION_SCHEMA = vol.Schema({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(None),
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(None),
vol.Optional(CONF_IF): CONDITIONS_SCHEMA,
vol.Required(CONF_THEN): validate_recursive_action,
})
def build_condition(config, arg_type):
template_arg = TemplateArguments(arg_type)
if isinstance(config, core.Lambda):
lambda_ = None
for lambda_ in process_lambda(config, [(arg_type, 'x')]):
yield
yield LambdaCondition.new(template_arg, lambda_)
elif CONF_AND in config:
yield AndCondition.new(template_arg, build_conditions(config[CONF_AND], template_arg))
elif CONF_OR in config:
yield OrCondition.new(template_arg, build_conditions(config[CONF_OR], template_arg))
elif CONF_LAMBDA in config:
lambda_ = None
for lambda_ in process_lambda(config[CONF_LAMBDA], [(arg_type, 'x')]):
yield
yield LambdaCondition.new(template_arg, lambda_)
elif CONF_RANGE in config:
conf = config[CONF_RANGE]
rhs = RangeCondition.new(template_arg)
type = RangeCondition.template(template_arg)
condition = Pvariable(config[CONF_CONDITION_ID], rhs, type=type)
if CONF_ABOVE in conf:
template_ = None
for template_ in templatable(conf[CONF_ABOVE], arg_type, float_):
yield
condition.set_min(template_)
if CONF_BELOW in conf:
template_ = None
for template_ in templatable(conf[CONF_BELOW], arg_type, float_):
yield
condition.set_max(template_)
yield condition
else:
raise ESPHomeYAMLError(u"Unsupported condition {}".format(config))
def build_conditions(config, arg_type):
conditions = []
for conf in config:
condition = None
for condition in build_condition(conf, arg_type):
yield None
conditions.append(condition)
yield ArrayInitializer(*conditions)
DELAY_ACTION_SCHEMA = cv.templatable(cv.positive_time_period_milliseconds)
@ACTION_REGISTRY.register(CONF_DELAY, DELAY_ACTION_SCHEMA)
def delay_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
rhs = App.register_component(DelayAction.new(template_arg))
type = DelayAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
for template_ in templatable(config, arg_type, uint32):
yield
add(action.set_delay(template_))
yield action
IF_ACTION_SCHEMA = vol.All({
vol.Required(CONF_CONDITION): validate_recursive_condition,
vol.Optional(CONF_THEN): validate_recursive_action,
vol.Optional(CONF_ELSE): validate_recursive_action,
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))
@ACTION_REGISTRY.register(CONF_IF, IF_ACTION_SCHEMA)
def if_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for conditions in build_conditions(config[CONF_CONDITION], arg_type):
yield None
rhs = IfAction.new(template_arg, conditions)
type = IfAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
if CONF_THEN in config:
for actions in build_actions(config[CONF_THEN], arg_type):
yield None
add(action.add_then(actions))
if CONF_ELSE in config:
for actions in build_actions(config[CONF_ELSE], arg_type):
yield None
add(action.add_else(actions))
yield action
LAMBDA_ACTION_SCHEMA = cv.lambda_
@ACTION_REGISTRY.register(CONF_LAMBDA, LAMBDA_ACTION_SCHEMA)
def lambda_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for lambda_ in process_lambda(config, [(arg_type, 'x')]):
yield None
rhs = LambdaAction.new(template_arg, lambda_)
type = LambdaAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)
CONF_COMPONENT_UPDATE = 'component.update'
COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA)
def component_update_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = UpdateComponentAction.new(var)
type = UpdateComponentAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)
def build_action(full_config, arg_type):
action_id = full_config[CONF_ACTION_ID]
key, config = next((k, v) for k, v in full_config.items() if k in ACTION_REGISTRY)
builder = ACTION_REGISTRY[key][1]
for result in builder(config, action_id, arg_type):
yield None
yield result
def build_actions(config, arg_type):
actions = []
for conf in config:
action = None
for action in build_action(conf, arg_type):
yield None
actions.append(action)
yield ArrayInitializer(*actions, multiline=False)
def build_automation_(trigger, arg_type, config):
rhs = App.make_automation(TemplateArguments(arg_type), trigger)
type = Automation.template(arg_type)
obj = Pvariable(config[CONF_AUTOMATION_ID], rhs, type=type)
if CONF_IF in config:
conditions = None
for conditions in build_conditions(config[CONF_IF], arg_type):
yield None
add(obj.add_conditions(conditions))
actions = None
for actions in build_actions(config[CONF_THEN], arg_type):
yield None
add(obj.add_actions(actions))
yield obj
def build_automation(trigger, arg_type, config):
add_job(build_automation_, trigger, arg_type, config)

View File

@@ -1,17 +1,18 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE
from esphomeyaml.helpers import App, Pvariable
DEPENDENCIES = ['i2c']
ADS1115_COMPONENT_CLASS = 'sensor::ADS1115Component'
ADS1115Component = sensor.sensor_ns.ADS1115Component
RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required."""
ADS1115_SCHEMA = vol.Schema({
cv.GenerateID('ads1115'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(ADS1115Component),
vol.Required(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_RATE): cv.invalid(RATE_REMOVE_MESSAGE)
@@ -23,7 +24,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
def to_code(config):
for conf in config:
rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
Pvariable(conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View File

@@ -1,8 +1,13 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED, CONF_MQTT_ID
from esphomeyaml.helpers import add, setup_mqtt_component, App, Pvariable
from esphomeyaml import automation
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INTERNAL, CONF_INVERTED, \
CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, CONF_ON_DOUBLE_CLICK, \
CONF_ON_PRESS, CONF_ON_RELEASE, CONF_TRIGGER_ID, CONF_FILTERS, CONF_INVERT, CONF_DELAYED_ON, \
CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT
from esphomeyaml.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns, \
setup_mqtt_component, bool_, process_lambda, ArrayInitializer
DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
@@ -11,39 +16,142 @@ DEVICE_CLASSES = [
'sound', 'vibration', 'window'
]
DEVICE_CLASSES_MSG = "Unknown device class. Must be one of {}".format(', '.join(DEVICE_CLASSES))
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
vol.Optional(CONF_INVERTED): cv.boolean,
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower,
vol.Any(*DEVICE_CLASSES, msg=DEVICE_CLASSES_MSG)),
})
MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
})
MQTT_BINARY_SENSOR_ID_SCHEMA = MQTT_BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID('mqtt_binary_sensor', CONF_MQTT_ID): cv.register_variable_id,
binary_sensor_ns = esphomelib_ns.namespace('binary_sensor')
PressTrigger = binary_sensor_ns.PressTrigger
ReleaseTrigger = binary_sensor_ns.ReleaseTrigger
ClickTrigger = binary_sensor_ns.ClickTrigger
DoubleClickTrigger = binary_sensor_ns.DoubleClickTrigger
BinarySensor = binary_sensor_ns.BinarySensor
InvertFilter = binary_sensor_ns.InvertFilter
LambdaFilter = binary_sensor_ns.LambdaFilter
DelayedOnFilter = binary_sensor_ns.DelayedOnFilter
DelayedOffFilter = binary_sensor_ns.DelayedOffFilter
HeartbeatFilter = binary_sensor_ns.HeartbeatFilter
MQTTBinarySensorComponent = binary_sensor_ns.MQTTBinarySensorComponent
FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT]
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
vol.Optional(CONF_INVERT): None,
vol.Optional(CONF_DELAYED_ON): cv.positive_time_period_milliseconds,
vol.Optional(CONF_DELAYED_OFF): cv.positive_time_period_milliseconds,
vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_LAMBDA): cv.lambda_,
}, cv.has_exactly_one_key(*FILTER_KEYS))])
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent),
cv.GenerateID(): cv.declare_variable_id(BinarySensor),
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)),
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
vol.Optional(CONF_ON_PRESS): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger),
}),
vol.Optional(CONF_ON_RELEASE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ReleaseTrigger),
}),
vol.Optional(CONF_ON_CLICK): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ClickTrigger),
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(DoubleClickTrigger),
vol.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds,
vol.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_INVERTED): cv.invalid(
"The inverted binary_sensor property has been replaced by the "
"new 'invert' binary sensor filter. Please see "
"https://esphomelib.com/esphomeyaml/components/binary_sensor/index.html."
),
})
BINARY_SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BINARY_SENSOR_SCHEMA.schema)
def setup_binary_sensor(obj, config):
def setup_filter(config):
if CONF_INVERT in config:
yield InvertFilter.new()
elif CONF_DELAYED_OFF in config:
yield App.register_component(DelayedOffFilter.new(config[CONF_DELAYED_OFF]))
elif CONF_DELAYED_ON in config:
yield App.register_component(DelayedOnFilter.new(config[CONF_DELAYED_ON]))
elif CONF_HEARTBEAT in config:
yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT]))
elif CONF_LAMBDA in config:
lambda_ = None
for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')]):
yield None
yield LambdaFilter.new(lambda_)
def setup_filters(config):
filters = []
for conf in config:
filter = None
for filter in setup_filter(conf):
yield None
filters.append(filter)
yield ArrayInitializer(*filters)
def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
if CONF_INTERNAL in config:
add(binary_sensor_var.set_internal(CONF_INTERNAL))
if CONF_DEVICE_CLASS in config:
add(obj.set_device_class(config[CONF_DEVICE_CLASS]))
add(binary_sensor_var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_INVERTED in config:
add(obj.set_inverted(config[CONF_INVERTED]))
add(binary_sensor_var.set_inverted(config[CONF_INVERTED]))
if CONF_FILTERS in config:
filters = None
for filters in setup_filters(config[CONF_FILTERS]):
yield
add(binary_sensor_var.add_filters(filters))
for conf in config.get(CONF_ON_PRESS, []):
rhs = binary_sensor_var.make_press_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, NoArg, conf)
for conf in config.get(CONF_ON_RELEASE, []):
rhs = binary_sensor_var.make_release_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, NoArg, conf)
for conf in config.get(CONF_ON_CLICK, []):
rhs = binary_sensor_var.make_click_trigger(conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH])
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, NoArg, conf)
for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
rhs = binary_sensor_var.make_double_click_trigger(conf[CONF_MIN_LENGTH],
conf[CONF_MAX_LENGTH])
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, NoArg, conf)
setup_mqtt_component(mqtt_var, config)
def setup_mqtt_binary_sensor(obj, config):
setup_mqtt_component(obj, config)
def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config):
binary_sensor_var = Pvariable(config[CONF_ID], binary_sensor_obj,
has_side_effects=False)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj,
has_side_effects=False)
add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
def register_binary_sensor(var, config):
setup_binary_sensor(var, config)
rhs = App.register_binary_sensor(var)
mqtt_sensor = Pvariable('binary_sensor::MQTTBinarySensorComponent', config[CONF_MQTT_ID], rhs)
setup_mqtt_binary_sensor(mqtt_sensor, config)
binary_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
rhs = App.register_binary_sensor(binary_sensor_var)
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'

View File

@@ -1,44 +0,0 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_MAC_ADDRESS, CONF_NAME, ESP_PLATFORM_ESP32
from esphomeyaml.core import HexInt, MACAddress
from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
DEPENDENCIES = ['esp32_ble']
def validate_mac(value):
value = cv.string_strict(value)
parts = value.split(':')
if len(parts) != 6:
raise vol.Invalid("MAC Address must consist of 6 : (colon) separated parts")
parts_int = []
if any(len(part) != 2 for part in parts):
raise vol.Invalid("MAC Address must be format XX:XX:XX:XX:XX:XX")
for part in parts:
try:
parts_int.append(int(part, 16))
except ValueError:
raise vol.Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
return MACAddress(*parts_int)
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('esp32_ble_device'): cv.register_variable_id,
vol.Required(CONF_MAC_ADDRESS): validate_mac,
}).extend(binary_sensor.MQTT_BINARY_SENSOR_ID_SCHEMA.schema)
def to_code(config):
hub = get_variable(None, type='ESP32BLETracker')
addr = [HexInt(i) for i in config[CONF_MAC_ADDRESS].parts]
rhs = hub.make_device(config[CONF_NAME], ArrayInitializer(*addr, multiline=False))
device = Pvariable('ESP32BLEDevice', config[CONF_ID], rhs)
binary_sensor.register_binary_sensor(device, config)
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'

View File

@@ -0,0 +1,23 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
make_address_array
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
from esphomeyaml.helpers import get_variable
DEPENDENCIES = ['esp32_ble_tracker']
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker)
}))
def to_code(config):
hub = None
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
yield
rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS]))
binary_sensor.register_binary_sensor(rhs, config)

View File

@@ -2,25 +2,28 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import Pvariable, RawExpression, get_variable
from esphomeyaml.components.esp32_touch import ESP32TouchComponent
from esphomeyaml.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import get_variable, global_ns
from esphomeyaml.pins import validate_gpio_pin
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
DEPENDENCIES = ['esp32_touch']
CONF_ESP32_TOUCH_ID = 'esp32_touch_id'
TOUCH_PADS = {
4: 'TOUCH_PAD_NUM0',
0: 'TOUCH_PAD_NUM1',
2: 'TOUCH_PAD_NUM2',
15: 'TOUCH_PAD_NUM3',
13: 'TOUCH_PAD_NUM4',
12: 'TOUCH_PAD_NUM5',
14: 'TOUCH_PAD_NUM6',
27: 'TOUCH_PAD_NUM7',
33: 'TOUCH_PAD_NUM8',
32: 'TOUCH_PAD_NUM9',
4: global_ns.TOUCH_PAD_NUM0,
0: global_ns.TOUCH_PAD_NUM1,
2: global_ns.TOUCH_PAD_NUM2,
15: global_ns.TOUCH_PAD_NUM3,
13: global_ns.TOUCH_PAD_NUM4,
12: global_ns.TOUCH_PAD_NUM5,
14: global_ns.TOUCH_PAD_NUM6,
27: global_ns.TOUCH_PAD_NUM7,
33: global_ns.TOUCH_PAD_NUM8,
32: global_ns.TOUCH_PAD_NUM9,
}
@@ -31,19 +34,20 @@ def validate_touch_pad(value):
return value
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('esp32_touch_pad'): cv.register_variable_id,
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PIN): validate_touch_pad,
vol.Required(CONF_THRESHOLD): cv.uint16_t,
}).extend(binary_sensor.MQTT_BINARY_SENSOR_ID_SCHEMA.schema)
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_variable_id(ESP32TouchComponent),
}))
def to_code(config):
hub = get_variable(None, type='binary_sensor::ESP32TouchComponent')
touch_pad = RawExpression(TOUCH_PADS[config[CONF_PIN]])
hub = None
for hub in get_variable(config[CONF_ESP32_TOUCH_ID]):
yield
touch_pad = TOUCH_PADS[config[CONF_PIN]]
rhs = hub.make_touch_pad(config[CONF_NAME], touch_pad, config[CONF_THRESHOLD])
device = Pvariable('ESP32TouchBinarySensor', config[CONF_ID], rhs)
binary_sensor.register_binary_sensor(device, config)
binary_sensor.register_binary_sensor(rhs, config)
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'

View File

@@ -3,23 +3,24 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN
from esphomeyaml.helpers import App, add, variable, gpio_input_pin_expression
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN
from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('gpio_binary_sensor'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.GPIO_INPUT_PIN_SCHEMA
}).extend(binary_sensor.MQTT_BINARY_SENSOR_SCHEMA.schema)
MakeGPIOBinarySensor = Application.MakeGPIOBinarySensor
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOBinarySensor),
vol.Required(CONF_PIN): pins.gpio_input_pin_schema
}))
def to_code(config):
rhs = App.make_gpio_binary_sensor(config[CONF_NAME],
gpio_input_pin_expression(config[CONF_PIN]))
gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs)
if CONF_INVERTED in config:
add(gpio.Pgpio.set_inverted(config[CONF_INVERTED]))
binary_sensor.setup_binary_sensor(gpio.Pgpio, config)
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config)
pin = None
for pin in gpio_input_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin)
gpio = variable(config[CONF_MAKE_ID], rhs)
binary_sensor.setup_binary_sensor(gpio.Pgpio, gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'

View File

@@ -0,0 +1,26 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.components.display.nextion import Nextion
from esphomeyaml.const import CONF_COMPONENT_ID, CONF_NAME, CONF_PAGE_ID
from esphomeyaml.helpers import get_variable
DEPENDENCIES = ['display']
CONF_NEXTION_ID = 'nextion_id'
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PAGE_ID): cv.uint8_t,
vol.Required(CONF_COMPONENT_ID): cv.uint8_t,
cv.GenerateID(CONF_NEXTION_ID): cv.use_variable_id(Nextion)
}))
def to_code(config):
hub = None
for hub in get_variable(config[CONF_NEXTION_ID]):
yield
rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID],
config[CONF_COMPONENT_ID])
binary_sensor.register_binary_sensor(rhs, config)

View File

@@ -0,0 +1,42 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.components.pn532 import PN532Component
from esphomeyaml.const import CONF_NAME, CONF_UID
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import ArrayInitializer, get_variable
DEPENDENCIES = ['pn532']
CONF_PN532_ID = 'pn532_id'
def validate_uid(value):
value = cv.string_strict(value)
for x in value.split('-'):
if len(x) != 2:
raise vol.Invalid("Each part (separated by '-') of the UID must be two characters "
"long.")
try:
x = int(x, 16)
except ValueError:
raise vol.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.")
if x < 0 or x > 255:
raise vol.Invalid("Valid values for UID parts (separated by '-') are 00 to FF")
return value
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_UID): validate_uid,
cv.GenerateID(CONF_PN532_ID): cv.use_variable_id(PN532Component)
}))
def to_code(config):
hub = None
for hub in get_variable(config[CONF_PN532_ID]):
yield
addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')]
rhs = hub.make_tag(config[CONF_NAME], ArrayInitializer(*addr, multiline=False))
binary_sensor.register_binary_sensor(rhs, config)

View File

@@ -0,0 +1,23 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor, rdm6300
from esphomeyaml.const import CONF_NAME, CONF_UID
from esphomeyaml.helpers import get_variable
DEPENDENCIES = ['rdm6300']
CONF_RDM6300_ID = 'rdm6300_id'
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_UID): cv.uint32_t,
cv.GenerateID(CONF_RDM6300_ID): cv.use_variable_id(rdm6300.RDM6300Component)
}))
def to_code(config):
hub = None
for hub in get_variable(config[CONF_RDM6300_ID]):
yield
rhs = hub.make_card(config[CONF_NAME], config[CONF_UID])
binary_sensor.register_binary_sensor(rhs, config)

View File

@@ -0,0 +1,121 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.components.remote_receiver import RemoteReceiverComponent, remote_ns
from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \
RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \
RC_SWITCH_TYPE_D_SCHEMA, binary_code, build_rc_switch_protocol
from esphomeyaml.const import CONF_ADDRESS, CONF_CHANNEL, CONF_CODE, CONF_COMMAND, CONF_DATA, \
CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \
CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, CONF_RC_SWITCH_TYPE_A, \
CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, CONF_SAMSUNG, CONF_SONY, \
CONF_STATE
from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable
DEPENDENCIES = ['remote_receiver']
REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_SAMSUNG, CONF_RAW,
CONF_RC_SWITCH_RAW, CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B,
CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D]
CONF_REMOTE_RECEIVER_ID = 'remote_receiver_id'
CONF_RECEIVER_ID = 'receiver_id'
RemoteReceiver = remote_ns.RemoteReceiver
LGReceiver = remote_ns.LGReceiver
NECReceiver = remote_ns.NECReceiver
PanasonicReceiver = remote_ns.PanasonicReceiver
RawReceiver = remote_ns.RawReceiver
SamsungReceiver = remote_ns.SamsungReceiver
SonyReceiver = remote_ns.SonyReceiver
RCSwitchRawReceiver = remote_ns.RCSwitchRawReceiver
RCSwitchTypeAReceiver = remote_ns.RCSwitchTypeAReceiver
RCSwitchTypeBReceiver = remote_ns.RCSwitchTypeBReceiver
RCSwitchTypeCReceiver = remote_ns.RCSwitchTypeCReceiver
RCSwitchTypeDReceiver = remote_ns.RCSwitchTypeDReceiver
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_LG): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)),
}),
vol.Optional(CONF_NEC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
}),
vol.Optional(CONF_SAMSUNG): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
}),
vol.Optional(CONF_SONY): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)),
}),
vol.Optional(CONF_PANASONIC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint32_t,
}),
vol.Optional(CONF_RAW): [vol.Any(vol.Coerce(int), cv.time_period_microseconds)],
vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_A): RC_SWITCH_TYPE_A_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA,
cv.GenerateID(CONF_REMOTE_RECEIVER_ID): cv.use_variable_id(RemoteReceiverComponent),
cv.GenerateID(CONF_RECEIVER_ID): cv.declare_variable_id(RemoteReceiver),
}), cv.has_exactly_one_key(*REMOTE_KEYS))
def receiver_base(full_config):
name = full_config[CONF_NAME]
key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS)
if key == CONF_LG:
return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_NEC:
return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_PANASONIC:
return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_SAMSUNG:
return SamsungReceiver.new(name, config[CONF_DATA])
elif key == CONF_SONY:
return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_RAW:
data = ArrayInitializer(*config, multiline=False)
return RawReceiver.new(name, data)
elif key == CONF_RC_SWITCH_RAW:
return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
elif key == CONF_RC_SWITCH_TYPE_A:
return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_GROUP]),
binary_code(config[CONF_DEVICE]),
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_B:
return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
config[CONF_ADDRESS], config[CONF_CHANNEL],
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_C:
return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_FAMILY][0]) - ord('a'),
config[CONF_GROUP], config[CONF_DEVICE],
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_D:
return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_GROUP][0]) - ord('a'),
config[CONF_DEVICE], config[CONF_STATE])
else:
raise NotImplementedError("Unknown receiver type {}".format(config))
def to_code(config):
remote = None
for remote in get_variable(config[CONF_REMOTE_RECEIVER_ID]):
yield
rhs = receiver_base(config)
receiver = Pvariable(config[CONF_RECEIVER_ID], rhs)
binary_sensor.register_binary_sensor(remote.add_decoder(receiver), config)
BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER'

View File

@@ -1,20 +1,21 @@
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, variable
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, variable
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('status_binary_sensor'): cv.register_variable_id,
}).extend(binary_sensor.MQTT_BINARY_SENSOR_SCHEMA.schema)
MakeStatusBinarySensor = Application.MakeStatusBinarySensor
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeStatusBinarySensor),
}))
def to_code(config):
rhs = App.make_status_binary_sensor(config[CONF_NAME])
status = variable('Application::MakeStatusBinarySensor', config[CONF_ID], rhs)
binary_sensor.setup_binary_sensor(status.Pstatus, config)
binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config)
status = variable(config[CONF_MAKE_ID], rhs)
binary_sensor.setup_binary_sensor(status.Pstatus, status.Pmqtt, config)
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'

View File

@@ -0,0 +1,28 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, process_lambda, variable, optional, bool_, add
MakeTemplateBinarySensor = Application.MakeTemplateBinarySensor
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateBinarySensor),
vol.Required(CONF_LAMBDA): cv.lambda_,
}))
def to_code(config):
rhs = App.make_template_binary_sensor(config[CONF_NAME])
make = variable(config[CONF_MAKE_ID], rhs)
binary_sensor.setup_binary_sensor(make.Ptemplate_, make.Pmqtt, config)
template_ = None
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(bool_)):
yield
add(make.Ptemplate_.set_template(template_))
BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR'

View File

@@ -0,0 +1,90 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_INTERNAL
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \
TemplateArguments, get_variable
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
cover_ns = esphomelib_ns.namespace('cover')
Cover = cover_ns.Cover
MQTTCoverComponent = cover_ns.MQTTCoverComponent
CoverState = cover_ns.CoverState
COVER_OPEN = cover_ns.COVER_OPEN
COVER_CLOSED = cover_ns.COVER_CLOSED
OpenAction = cover_ns.OpenAction
CloseAction = cover_ns.CloseAction
StopAction = cover_ns.StopAction
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(Cover),
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTCoverComponent),
})
COVER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(COVER_SCHEMA.schema)
def setup_cover_core_(cover_var, mqtt_var, config):
if CONF_INTERNAL in config:
add(cover_var.set_internal(config[CONF_INTERNAL]))
setup_mqtt_component(mqtt_var, config)
def setup_cover(cover_obj, mqtt_obj, config):
cover_var = Pvariable(config[CONF_ID], cover_obj, has_side_effects=False)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
setup_cover_core_(cover_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_COVER'
CONF_COVER_OPEN = 'cover.open'
COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA)
def cover_open_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_open_action(template_arg)
type = OpenAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_COVER_CLOSE = 'cover.close'
COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA)
def cover_close_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_close_action(template_arg)
type = CloseAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_COVER_STOP = 'cover.stop'
COVER_STOP_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA)
def cover_stop_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_stop_action(template_arg)
type = StopAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)

View File

@@ -0,0 +1,47 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import automation
from esphomeyaml.components import cover
from esphomeyaml.const import CONF_CLOSE_ACTION, CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, \
CONF_OPEN_ACTION, CONF_STOP_ACTION, CONF_OPTIMISTIC
from esphomeyaml.helpers import App, Application, NoArg, add, process_lambda, variable, optional
MakeTemplateCover = Application.MakeTemplateCover
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateCover),
vol.Optional(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
vol.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
vol.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True),
}), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
def to_code(config):
rhs = App.make_template_cover(config[CONF_NAME])
make = variable(config[CONF_MAKE_ID], rhs)
cover.setup_cover(make.Ptemplate_, make.Pmqtt, config)
if CONF_LAMBDA in config:
template_ = None
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(cover.CoverState)):
yield
add(make.Ptemplate_.set_state_lambda(template_))
if CONF_OPEN_ACTION in config:
automation.build_automation(make.Ptemplate_.get_open_trigger(), NoArg,
config[CONF_OPEN_ACTION])
if CONF_CLOSE_ACTION in config:
automation.build_automation(make.Ptemplate_.get_close_trigger(), NoArg,
config[CONF_CLOSE_ACTION])
if CONF_STOP_ACTION in config:
automation.build_automation(make.Ptemplate_.get_stop_trigger(), NoArg,
config[CONF_STOP_ACTION])
if CONF_OPTIMISTIC in config:
add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
BUILD_FLAGS = '-DUSE_TEMPLATE_COVER'

View File

@@ -2,22 +2,23 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ID, CONF_PIN, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable
DALLAS_COMPONENT_CLASS = 'sensor::DallasComponent'
DallasComponent = sensor.sensor_ns.DallasComponent
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID('dallas'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(DallasComponent),
vol.Required(CONF_PIN): pins.input_output_pin,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})])
def to_code(config):
for conf in config:
rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL))
Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs)
Pvariable(conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View File

@@ -1,9 +1,11 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv, pins
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \
CONF_SLEEP_DURATION, CONF_WAKEUP_PIN
from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression
from esphomeyaml.helpers import App, Pvariable, add, gpio_input_pin_expression, esphomelib_ns, \
TemplateArguments, get_variable
def validate_pin_number(value):
@@ -14,11 +16,25 @@ def validate_pin_number(value):
return value
DeepSleepComponent = esphomelib_ns.DeepSleepComponent
EnterDeepSleepAction = esphomelib_ns.EnterDeepSleepAction
PreventDeepSleepAction = esphomelib_ns.PreventDeepSleepAction
WAKEUP_PIN_MODES = {
'IGNORE': esphomelib_ns.WAKEUP_PIN_MODE_IGNORE,
'KEEP_AWAKE': esphomelib_ns.WAKEUP_PIN_MODE_KEEP_AWAKE,
'INVERT_WAKEUP': esphomelib_ns.WAKEUP_PIN_MODE_INVERT_WAKEUP,
}
CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode'
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('deep_sleep'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent),
vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema,
validate_pin_number),
vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32, vol.Upper,
cv.one_of(*WAKEUP_PIN_MODES)),
vol.Optional(CONF_RUN_CYCLES): cv.positive_int,
vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
})
@@ -26,12 +42,16 @@ CONFIG_SCHEMA = vol.Schema({
def to_code(config):
rhs = App.make_deep_sleep_component()
deep_sleep = Pvariable('DeepSleepComponent', config[CONF_ID], rhs)
deep_sleep = Pvariable(config[CONF_ID], rhs)
if CONF_SLEEP_DURATION in config:
add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION]))
if CONF_WAKEUP_PIN in config:
pin = gpio_input_pin_expression(config[CONF_WAKEUP_PIN])
pin = None
for pin in gpio_input_pin_expression(config[CONF_WAKEUP_PIN]):
yield
add(deep_sleep.set_wakeup_pin(pin))
if CONF_WAKEUP_PIN_MODE in config:
add(deep_sleep.set_wakeup_pin_mode(WAKEUP_PIN_MODES[config[CONF_WAKEUP_PIN_MODE]]))
if CONF_RUN_CYCLES in config:
add(deep_sleep.set_run_cycles(config[CONF_RUN_CYCLES]))
if CONF_RUN_DURATION in config:
@@ -39,3 +59,35 @@ def to_code(config):
BUILD_FLAGS = '-DUSE_DEEP_SLEEP'
CONF_DEEP_SLEEP_ENTER = 'deep_sleep.enter'
DEEP_SLEEP_ENTER_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(DeepSleepComponent),
})
@ACTION_REGISTRY.register(CONF_DEEP_SLEEP_ENTER, DEEP_SLEEP_ENTER_ACTION_SCHEMA)
def deep_sleep_enter_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_enter_deep_sleep_action(template_arg)
type = EnterDeepSleepAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_DEEP_SLEEP_PREVENT = 'deep_sleep.prevent'
DEEP_SLEEP_PREVENT_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(DeepSleepComponent),
})
@ACTION_REGISTRY.register(CONF_DEEP_SLEEP_PREVENT, DEEP_SLEEP_PREVENT_ACTION_SCHEMA)
def deep_sleep_prevent_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_prevent_deep_sleep_action(template_arg)
type = PreventDeepSleepAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)

View File

@@ -0,0 +1,56 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import add, add_job, esphomelib_ns
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
display_ns = esphomelib_ns.namespace('display')
DisplayBuffer = display_ns.DisplayBuffer
DisplayBufferRef = DisplayBuffer.operator('ref')
DISPLAY_ROTATIONS = {
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
90: display_ns.DISPLAY_ROTATION_90_DEGREES,
180: display_ns.DISPLAY_ROTATION_180_DEGREES,
270: display_ns.DISPLAY_ROTATION_270_DEGREES,
}
def validate_rotation(value):
value = cv.string(value)
if value.endswith(u"°"):
value = value[:-1]
try:
value = int(value)
except ValueError:
raise vol.Invalid(u"Expected integer for rotation")
return cv.one_of(*DISPLAY_ROTATIONS)(value)
BASIC_DISPLAY_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
vol.Optional(CONF_LAMBDA): cv.lambda_,
})
FULL_DISPLAY_PLATFORM_SCHEMA = BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_ROTATION): validate_rotation,
})
def setup_display_core_(display_var, config):
if CONF_UPDATE_INTERVAL in config:
add(display_var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
if CONF_ROTATION in config:
add(display_var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
def setup_display(display_var, config):
add_job(setup_display_core_, display_var, config)
BUILD_FLAGS = '-DUSE_DISPLAY'

View File

@@ -0,0 +1,72 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
from esphomeyaml.const import CONF_DATA_PINS, CONF_DIMENSIONS, CONF_ENABLE_PIN, CONF_ID, \
CONF_LAMBDA, CONF_RS_PIN, CONF_RW_PIN
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, process_lambda
GPIOLCDDisplay = display.display_ns.GPIOLCDDisplay
LCDDisplay = display.display_ns.LCDDisplay
LCDDisplayRef = LCDDisplay.operator('ref')
def validate_lcd_dimensions(value):
value = cv.dimensions(value)
if value[0] > 0x40:
raise vol.Invalid("LCD displays can't have more than 64 columns")
if value[1] > 4:
raise vol.Invalid("LCD displays can't have more than 4 rows")
return value
def validate_pin_length(value):
if len(value) != 4 and len(value) != 8:
raise vol.Invalid("LCD Displays can either operate in 4-pin or 8-pin mode,"
"not {}-pin mode".format(len(value)))
return value
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(GPIOLCDDisplay),
vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
vol.Required(CONF_DATA_PINS): vol.All([pins.gpio_output_pin_schema], validate_pin_length),
vol.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_RS_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema,
})
def to_code(config):
rhs = App.make_gpio_lcd_display(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
lcd = Pvariable(config[CONF_ID], rhs)
pins_ = []
for conf in config[CONF_DATA_PINS]:
for pin in gpio_output_pin_expression(conf):
yield
pins_.append(pin)
add(lcd.set_data_pins(*pins_))
for enable in gpio_output_pin_expression(config[CONF_ENABLE_PIN]):
yield
add(lcd.set_enable_pin(enable))
for rs in gpio_output_pin_expression(config[CONF_RS_PIN]):
yield
add(lcd.set_rs_pin(rs))
if CONF_RW_PIN in config:
for rw in gpio_output_pin_expression(config[CONF_RW_PIN]):
yield
add(lcd.set_rw_pin(rw))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]):
yield
add(lcd.set_writer(lambda_))
display.setup_display(lcd, config)
BUILD_FLAGS = '-DUSE_LCD_DISPLAY'

View File

@@ -0,0 +1,35 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import display
from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions
from esphomeyaml.const import CONF_ADDRESS, CONF_DIMENSIONS, CONF_ID, CONF_LAMBDA
from esphomeyaml.helpers import App, Pvariable, add, process_lambda
DEPENDENCIES = ['i2c']
PCF8574LCDDisplay = display.display_ns.PCF8574LCDDisplay
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(PCF8574LCDDisplay),
vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
vol.Optional(CONF_ADDRESS): cv.i2c_address,
})
def to_code(config):
rhs = App.make_pcf8574_lcd_display(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
lcd = Pvariable(config[CONF_ID], rhs)
if CONF_ADDRESS in config:
add(lcd.set_address(config[CONF_ADDRESS]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]):
yield
add(lcd.set_writer(lambda_))
display.setup_display(lcd, config)
BUILD_FLAGS = ['-DUSE_LCD_DISPLAY', '-DUSE_LCD_DISPLAY_PCF8574']

View File

@@ -0,0 +1,48 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS, \
CONF_SPI_ID
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression, \
process_lambda
DEPENDENCIES = ['spi']
MAX7219Component = display.display_ns.MAX7219Component
MAX7219ComponentRef = MAX7219Component.operator('ref')
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(MAX7219Component),
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_NUM_CHIPS): vol.All(cv.uint8_t, vol.Range(min=1)),
vol.Optional(CONF_INTENSITY): vol.All(cv.uint8_t, vol.Range(min=0, max=15)),
})
def to_code(config):
for spi in get_variable(config[CONF_SPI_ID]):
yield
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
yield
rhs = App.make_max7219(spi, cs)
max7219 = Pvariable(config[CONF_ID], rhs)
if CONF_NUM_CHIPS in config:
add(max7219.set_num_chips(config[CONF_NUM_CHIPS]))
if CONF_INTENSITY in config:
add(max7219.set_intensity(config[CONF_INTENSITY]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')]):
yield
add(max7219.set_writer(lambda_))
display.setup_display(max7219, config)
BUILD_FLAGS = '-DUSE_MAX7219'

View File

@@ -0,0 +1,32 @@
import esphomeyaml.config_validation as cv
from esphomeyaml.components import display
from esphomeyaml.components.uart import UARTComponent
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_UART_ID
from esphomeyaml.helpers import App, Pvariable, add, get_variable, process_lambda
DEPENDENCIES = ['uart']
Nextion = display.display_ns.Nextion
NextionRef = Nextion.operator('ref')
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(Nextion),
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
})
def to_code(config):
for uart in get_variable(config[CONF_UART_ID]):
yield
rhs = App.make_nextion(uart)
nextion = Pvariable(config[CONF_ID], rhs)
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')]):
yield
add(nextion.set_writer(lambda_))
display.setup_display(nextion, config)
BUILD_FLAGS = '-DUSE_NEXTION'

View File

@@ -0,0 +1,46 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
from esphomeyaml.components.display import ssd1306_spi
from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, \
CONF_MODEL, CONF_RESET_PIN, CONF_LAMBDA
from esphomeyaml.helpers import App, Pvariable, add, \
gpio_output_pin_expression, process_lambda
DEPENDENCIES = ['i2c']
I2CSSD1306 = display.display_ns.I2CSSD1306
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(I2CSSD1306),
vol.Required(CONF_MODEL): ssd1306_spi.SSD1306_MODEL,
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
vol.Optional(CONF_ADDRESS): cv.i2c_address,
})
def to_code(config):
ssd = Pvariable(config[CONF_ID], App.make_i2c_ssd1306())
add(ssd.set_model(ssd1306_spi.MODELS[config[CONF_MODEL]]))
if CONF_RESET_PIN in config:
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
yield
add(ssd.set_reset_pin(reset))
if CONF_EXTERNAL_VCC in config:
add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC]))
if CONF_ADDRESS in config:
add(ssd.set_address(config[CONF_ADDRESS]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA],
[(display.DisplayBufferRef, 'it')]):
yield
add(ssd.set_writer(lambda_))
display.setup_display(ssd, config)
BUILD_FLAGS = '-DUSE_SSD1306'

View File

@@ -0,0 +1,68 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, \
CONF_ID, CONF_MODEL, \
CONF_RESET_PIN, CONF_SPI_ID, CONF_LAMBDA
from esphomeyaml.helpers import App, Pvariable, add, get_variable, \
gpio_output_pin_expression, process_lambda
DEPENDENCIES = ['spi']
SPISSD1306 = display.display_ns.SPISSD1306
MODELS = {
'SSD1306_128X32': display.display_ns.SSD1306_MODEL_128_32,
'SSD1306_128X64': display.display_ns.SSD1306_MODEL_128_64,
'SSD1306_96X16': display.display_ns.SSD1306_MODEL_96_16,
'SSD1306_64X48': display.display_ns.SSD1306_MODEL_64_48,
'SH1106_128X32': display.display_ns.SH1106_MODEL_128_32,
'SH1106_128X64': display.display_ns.SH1106_MODEL_128_64,
'SH1106_96X16': display.display_ns.SH1106_MODEL_96_16,
'SH1106_64X48': display.display_ns.SH1106_MODEL_64_48,
}
SSD1306_MODEL = vol.All(vol.Upper, vol.Replace(' ', '_'), cv.one_of(*MODELS))
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(SPISSD1306),
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_MODEL): SSD1306_MODEL,
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
})
def to_code(config):
for spi in get_variable(config[CONF_SPI_ID]):
yield
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
yield
for dc in gpio_output_pin_expression(config[CONF_DC_PIN]):
yield
rhs = App.make_spi_ssd1306(spi, cs, dc)
ssd = Pvariable(config[CONF_ID], rhs)
add(ssd.set_model(MODELS[config[CONF_MODEL]]))
if CONF_RESET_PIN in config:
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
yield
add(ssd.set_reset_pin(reset))
if CONF_EXTERNAL_VCC in config:
add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC]))
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA],
[(display.DisplayBufferRef, 'it')]):
yield
add(ssd.set_writer(lambda_))
display.setup_display(ssd, config)
BUILD_FLAGS = '-DUSE_SSD1306'

View File

@@ -0,0 +1,84 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import display
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_BUSY_PIN, CONF_CS_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \
CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_input_pin_expression, \
gpio_output_pin_expression, process_lambda
DEPENDENCIES = ['spi']
WaveshareEPaperTypeA = display.display_ns.WaveshareEPaperTypeA
WaveshareEPaper = display.display_ns.WaveshareEPaper
MODELS = {
'1.54in': ('a', display.display_ns.WAVESHARE_EPAPER_1_54_IN),
'2.13in': ('a', display.display_ns.WAVESHARE_EPAPER_2_13_IN),
'2.90in': ('a', display.display_ns.WAVESHARE_EPAPER_2_9_IN),
'2.70in': ('b', display.display_ns.WAVESHARE_EPAPER_2_7_IN),
'4.20in': ('b', display.display_ns.WAVESHARE_EPAPER_4_2_IN),
'7.50in': ('b', display.display_ns.WAVESHARE_EPAPER_7_5_IN),
}
def validate_full_update_every_only_type_a(value):
if CONF_FULL_UPDATE_EVERY not in value:
return value
if MODELS[value[CONF_MODEL]][0] != 'a':
raise vol.Invalid("The 'full_update_every' option is only available for models "
"'1.54in', '2.13in' and '2.90in'.")
return value
PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(None),
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_MODEL): vol.All(vol.Lower, cv.one_of(*MODELS)),
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
vol.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t,
}), validate_full_update_every_only_type_a)
def to_code(config):
for spi in get_variable(config[CONF_SPI_ID]):
yield
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
yield
for dc in gpio_output_pin_expression(config[CONF_DC_PIN]):
yield
model_type, model = MODELS[config[CONF_MODEL]]
if model_type == 'a':
rhs = App.make_waveshare_epaper_type_a(spi, cs, dc, model)
epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaperTypeA)
elif model_type == 'b':
rhs = App.make_waveshare_epaper_type_b(spi, cs, dc, model)
epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaper)
else:
raise NotImplementedError()
if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')]):
yield
add(epaper.set_writer(lambda_))
if CONF_RESET_PIN in config:
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
yield
add(epaper.set_reset_pin(reset))
if CONF_BUSY_PIN in config:
for reset in gpio_input_pin_expression(config[CONF_BUSY_PIN]):
yield
add(epaper.set_busy_pin(reset))
if CONF_FULL_UPDATE_EVERY in config:
add(epaper.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
display.setup_display(epaper, config)
BUILD_FLAGS = '-DUSE_WAVESHARE_EPAPER'

View File

@@ -1,22 +1,5 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, Pvariable, add
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('esp32_ble'): cv.register_variable_id,
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_esp32_ble_tracker()
ble = Pvariable('ESP32BLETracker', config[CONF_ID], rhs)
if CONF_SCAN_INTERVAL in config:
add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'
CONFIG_SCHEMA = cv.invalid("The 'esp32_ble' component has been renamed to the 'esp32_ble_tracker' "
"component in order to avoid confusion with the new 'esp32_ble_beacon' "
"component.")

View File

@@ -0,0 +1,35 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32, CONF_UUID, CONF_TYPE
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, RawExpression, ArrayInitializer
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
ESP32BLEBeacon = esphomelib_ns.ESP32BLEBeacon
CONF_MAJOR = 'major'
CONF_MINOR = 'minor'
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon),
vol.Required(CONF_TYPE): vol.All(vol.Upper, cv.one_of('IBEACON')),
vol.Required(CONF_UUID): cv.uuid,
vol.Optional(CONF_MAJOR): cv.uint16_t,
vol.Optional(CONF_MINOR): cv.uint16_t,
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
})
def to_code(config):
uuid = config[CONF_UUID].hex
uuid_arr = [RawExpression('0x{}'.format(uuid[i:i+2])) for i in range(0, len(uuid), 2)]
rhs = App.make_esp32_ble_beacon(ArrayInitializer(*uuid_arr, multiline=False))
ble = Pvariable(config[CONF_ID], rhs)
if CONF_MAJOR in config:
add(ble.set_major(config[CONF_MAJOR]))
if CONF_MINOR in config:
add(ble.set_minor(config[CONF_MINOR]))
BUILD_FLAGS = '-DUSE_ESP32_BLE_BEACON'

View File

@@ -0,0 +1,31 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, ArrayInitializer
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONF_ESP32_BLE_ID = 'esp32_ble_id'
ESP32BLETracker = esphomelib_ns.ESP32BLETracker
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker),
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
})
def make_address_array(address):
addr = [HexInt(i) for i in address.parts]
return ArrayInitializer(*addr, multiline=False)
def to_code(config):
rhs = App.make_esp32_ble_tracker()
ble = Pvariable(config[CONF_ID], rhs)
if CONF_SCAN_INTERVAL in config:
add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'

View File

@@ -1,11 +1,12 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_SETUP_MODE, CONF_IIR_FILTER, \
CONF_SLEEP_DURATION, CONF_MEASUREMENT_DURATION, CONF_LOW_VOLTAGE_REFERENCE, \
CONF_HIGH_VOLTAGE_REFERENCE, CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
from esphomeyaml.core import TimePeriod
from esphomeyaml.helpers import App, Pvariable, add
from esphomeyaml.helpers import App, Pvariable, add, global_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
@@ -17,33 +18,33 @@ def validate_voltage(values):
value = cv.string(value)
if not value.endswith('V'):
value += 'V'
if value not in values:
raise vol.Invalid('Must be one of {}'.format(values))
return value
return cv.one_of(*values)(value)
return validator
LOW_VOLTAGE_REFERENCE = {
'0.5V': 'TOUCH_LVOLT_0V5',
'0.6V': 'TOUCH_LVOLT_0V6',
'0.7V': 'TOUCH_LVOLT_0V7',
'0.8V': 'TOUCH_LVOLT_0V8',
'0.5V': global_ns.TOUCH_LVOLT_0V5,
'0.6V': global_ns.TOUCH_LVOLT_0V6,
'0.7V': global_ns.TOUCH_LVOLT_0V7,
'0.8V': global_ns.TOUCH_LVOLT_0V8,
}
HIGH_VOLTAGE_REFERENCE = {
'2.4V': 'TOUCH_HVOLT_2V4',
'2.5V': 'TOUCH_HVOLT_2V5',
'2.6V': 'TOUCH_HVOLT_2V6',
'2.7V': 'TOUCH_HVOLT_2V7',
'2.4V': global_ns.TOUCH_HVOLT_2V4,
'2.5V': global_ns.TOUCH_HVOLT_2V5,
'2.6V': global_ns.TOUCH_HVOLT_2V6,
'2.7V': global_ns.TOUCH_HVOLT_2V7,
}
VOLTAGE_ATTENUATION = {
'1.5V': 'TOUCH_HVOLT_ATTEN_1V5',
'1V': 'TOUCH_HVOLT_ATTEN_1V',
'0.5V': 'TOUCH_HVOLT_ATTEN_0V5',
'0V': 'TOUCH_HVOLT_ATTEN_0V',
'1.5V': global_ns.TOUCH_HVOLT_ATTEN_1V5,
'1V': global_ns.TOUCH_HVOLT_ATTEN_1V,
'0.5V': global_ns.TOUCH_HVOLT_ATTEN_0V5,
'0V': global_ns.TOUCH_HVOLT_ATTEN_0V,
}
ESP32TouchComponent = binary_sensor.binary_sensor_ns.ESP32TouchComponent
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('esp32_ble'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(ESP32TouchComponent),
vol.Optional(CONF_SETUP_MODE): cv.boolean,
vol.Optional(CONF_IIR_FILTER): cv.positive_time_period_milliseconds,
vol.Optional(CONF_SLEEP_DURATION):
@@ -58,13 +59,13 @@ CONFIG_SCHEMA = vol.Schema({
def to_code(config):
rhs = App.make_esp32_touch_component()
touch = Pvariable('binary_sensor::ESP32TouchComponent', config[CONF_ID], rhs)
touch = Pvariable(config[CONF_ID], rhs)
if CONF_SETUP_MODE in config:
add(touch.set_setup_mode(config[CONF_SETUP_MODE]))
if CONF_IIR_FILTER in config:
add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
if CONF_SLEEP_DURATION in config:
sleep_duration = int(config[CONF_SLEEP_DURATION].total_microseconds * 6.6667)
sleep_duration = int(config[CONF_SLEEP_DURATION].total_microseconds * 0.15)
add(touch.set_sleep_duration(sleep_duration))
if CONF_MEASUREMENT_DURATION in config:
measurement_duration = int(config[CONF_MEASUREMENT_DURATION].total_microseconds * 0.125)

View File

@@ -1,26 +1,130 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, \
CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
from esphomeyaml.helpers import add, setup_mqtt_component
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_OSCILLATION_COMMAND_TOPIC, \
CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_INTERNAL, \
CONF_SPEED, CONF_OSCILLATING
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
TemplateArguments, get_variable, templatable, bool_
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
fan_ns = esphomelib_ns.namespace('fan')
FanState = fan_ns.FanState
MQTTFanComponent = fan_ns.MQTTFanComponent
MakeFan = Application.MakeFan
TurnOnAction = fan_ns.TurnOnAction
TurnOffAction = fan_ns.TurnOffAction
ToggleAction = fan_ns.ToggleAction
FanSpeed = fan_ns.FanSpeed
FAN_SPEED_OFF = fan_ns.FAN_SPEED_OFF
FAN_SPEED_LOW = fan_ns.FAN_SPEED_LOW
FAN_SPEED_MEDIUM = fan_ns.FAN_SPEED_MEDIUM
FAN_SPEED_HIGH = fan_ns.FAN_SPEED_HIGH
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(FanState),
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTFanComponent),
vol.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.publish_topic,
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.subscribe_topic,
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
})
FAN_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FAN_SCHEMA.schema)
def setup_mqtt_fan(obj, config):
FAN_SPEEDS = {
'OFF': FAN_SPEED_OFF,
'LOW': FAN_SPEED_LOW,
'MEDIUM': FAN_SPEED_MEDIUM,
'HIGH': FAN_SPEED_HIGH,
}
def validate_fan_speed(value):
return vol.All(vol.Upper, cv.one_of(*FAN_SPEEDS))(value)
def setup_fan_core_(fan_var, mqtt_var, config):
if CONF_INTERNAL in config:
add(fan_var.set_internal(config[CONF_INTERNAL]))
if CONF_OSCILLATION_STATE_TOPIC in config:
add(obj.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
add(mqtt_var.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
if CONF_OSCILLATION_COMMAND_TOPIC in config:
add(obj.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
add(mqtt_var.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
if CONF_SPEED_STATE_TOPIC in config:
add(obj.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
add(mqtt_var.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
if CONF_SPEED_COMMAND_TOPIC in config:
add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
setup_mqtt_component(obj, config)
add(mqtt_var.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
setup_mqtt_component(mqtt_var, config)
def setup_fan(fan_obj, mqtt_obj, config):
fan_var = Pvariable(config[CONF_ID], fan_obj, has_side_effects=False)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
setup_fan_core_(fan_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_FAN'
CONF_FAN_TOGGLE = 'fan.toggle'
FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA)
def fan_toggle_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_toggle_action(template_arg)
type = ToggleAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_FAN_TURN_OFF = 'fan.turn_off'
FAN_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_FAN_TURN_OFF, FAN_TURN_OFF_ACTION_SCHEMA)
def fan_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
type = TurnOffAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_FAN_TURN_ON = 'fan.turn_on'
FAN_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
vol.Optional(CONF_SPEED): cv.templatable(validate_fan_speed),
})
@ACTION_REGISTRY.register(CONF_FAN_TURN_ON, FAN_TURN_ON_ACTION_SCHEMA)
def fan_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)
type = TurnOnAction.template(arg_type)
action = Pvariable(action_id, rhs, type=type)
if CONF_OSCILLATING in config:
for template_ in templatable(config[CONF_OSCILLATING], arg_type, bool_):
yield None
add(action.set_oscillating(template_))
if CONF_SPEED in config:
for template_ in templatable(config[CONF_SPEED], arg_type, FanSpeed):
yield None
add(action.set_speed(template_))
yield action

View File

@@ -2,22 +2,28 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import fan
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
from esphomeyaml.helpers import App, add, get_variable, variable
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
cv.GenerateID('binary_fan'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id,
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id,
})
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
}))
def to_code(config):
output = get_variable(config[CONF_OUTPUT])
output = None
for output in get_variable(config[CONF_OUTPUT]):
yield
rhs = App.make_fan(config[CONF_NAME])
fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
fan_struct = variable(config[CONF_MAKE_ID], rhs)
add(fan_struct.Poutput.set_binary(output))
if CONF_OSCILLATION_OUTPUT in config:
oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
oscillation_output = None
for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
yield
add(fan_struct.Poutput.set_oscillation(oscillation_output))
fan.setup_mqtt_fan(fan_struct.Pmqtt, config)
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)

View File

@@ -2,32 +2,34 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import fan
from esphomeyaml.const import CONF_HIGH, CONF_ID, CONF_LOW, \
CONF_MEDIUM, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, \
CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
from esphomeyaml.const import CONF_HIGH, CONF_LOW, CONF_MAKE_ID, CONF_MEDIUM, CONF_NAME, \
CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, \
CONF_SPEED_STATE_TOPIC
from esphomeyaml.helpers import App, add, get_variable, variable
PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
cv.GenerateID('speed_fan'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id,
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
vol.Optional(CONF_SPEED_STATE_TOPIC): cv.publish_topic,
vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic,
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.variable_id,
vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(None),
vol.Optional(CONF_SPEED): vol.Schema({
vol.Required(CONF_LOW): cv.zero_to_one_float,
vol.Required(CONF_MEDIUM): cv.zero_to_one_float,
vol.Required(CONF_HIGH): cv.zero_to_one_float,
vol.Required(CONF_LOW): cv.percentage,
vol.Required(CONF_MEDIUM): cv.percentage,
vol.Required(CONF_HIGH): cv.percentage,
}),
})
}))
def to_code(config):
output = get_variable(config[CONF_OUTPUT])
output = None
for output in get_variable(config[CONF_OUTPUT]):
yield
rhs = App.make_fan(config[CONF_NAME])
fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
fan_struct = variable(config[CONF_MAKE_ID], rhs)
if CONF_SPEED in config:
speeds = config[CONF_SPEED]
add(fan_struct.Poutput.set_speed(output, 0.0,
add(fan_struct.Poutput.set_speed(output,
speeds[CONF_LOW],
speeds[CONF_MEDIUM],
speeds[CONF_HIGH]))
@@ -35,6 +37,9 @@ def to_code(config):
add(fan_struct.Poutput.set_speed(output))
if CONF_OSCILLATION_OUTPUT in config:
oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
oscillation_output = None
for oscillation_output in get_variable(config[CONF_OSCILLATION_OUTPUT]):
yield
add(fan_struct.Poutput.set_oscillation(oscillation_output))
fan.setup_mqtt_fan(fan_struct.Pmqtt, config)
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config)

View File

@@ -0,0 +1,121 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.components import display
from esphomeyaml.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
relative_path
DEPENDENCIES = ['display']
Font = display.display_ns.Font
Glyph = display.display_ns.Glyph
def validate_glyphs(value):
if isinstance(value, list):
value = vol.Schema([cv.string])(value)
value = vol.Schema([cv.string])(list(value))
def comparator(x, y):
x_ = x.encode('utf-8')
y_ = y.encode('utf-8')
for c in range(min(len(x_), len(y_))):
if x_[c] < y_[c]:
return -1
if x_[c] > y_[c]:
return 1
if len(x_) < len(y_):
return -1
elif len(x_) > len(y_):
return 1
else:
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
value.sort(cmp=comparator)
return value
def validate_pillow_installed(value):
try:
import PIL
except ImportError:
raise vol.Invalid("Please install the pillow python package to use this feature. "
"(pip2 install pillow)")
if PIL.__version__[0] < '4':
raise vol.Invalid("Please update your pillow installation to at least 4.0.x. "
"(pip2 install -U pillow)")
return value
def validate_truetype_file(value):
if value.endswith('.zip'): # for Google Fonts downloads
raise vol.Invalid(u"Please unzip the font archive '{}' first and then use the .ttf files "
u"inside.".format(value))
if not value.endswith('.ttf'):
raise vol.Invalid(u"Only truetype (.ttf) files are supported. Please make sure you're "
u"using the correct format or rename the extension to .ttf")
return cv.file_(value)
DEFAULT_GLYPHS = u' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
CONF_RAW_DATA_ID = 'raw_data_id'
FONT_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.declare_variable_id(Font),
vol.Required(CONF_FILE): validate_truetype_file,
vol.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
vol.Optional(CONF_SIZE, default=20): vol.All(cv.int_, vol.Range(min=1)),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
})
CONFIG_SCHEMA = vol.All(validate_pillow_installed, cv.ensure_list, [FONT_SCHEMA])
def to_code(config):
from PIL import ImageFont
for conf in config:
path = relative_path(conf[CONF_FILE])
try:
font = ImageFont.truetype(path, conf[CONF_SIZE])
except Exception as e:
raise core.ESPHomeYAMLError(u"Could not load truetype file {}: {}".format(path, e))
ascent, descent = font.getmetrics()
glyph_args = {}
data = []
for glyph in conf[CONF_GLYPHS]:
mask = font.getmask(glyph, mode='1')
_, (offset_x, offset_y) = font.font.getsize(glyph)
width, height = mask.size
width8 = ((width + 7) // 8) * 8
glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812
for y in range(height):
for x in range(width):
if not mask.getpixel((x, y)):
continue
pos = x + y * width8
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
glyph_args[glyph] = (len(data), offset_x, offset_y, width, height)
data += glyph_data
raw_data = MockObj(conf[CONF_RAW_DATA_ID])
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
raw_data, len(data),
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
glyphs = []
for glyph in conf[CONF_GLYPHS]:
glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))
rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
Pvariable(conf[CONF_ID], rhs)

View File

@@ -4,25 +4,28 @@ import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID, \
CONF_RECEIVE_TIMEOUT
from esphomeyaml.helpers import App, add, Pvariable
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
I2CComponent = esphomelib_ns.I2CComponent
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('i2c'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(I2CComponent),
vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin,
vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin,
vol.Optional(CONF_FREQUENCY): cv.positive_int,
vol.Optional(CONF_RECEIVE_TIMEOUT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=0, min_included=False)),
vol.Optional(CONF_SCAN): cv.boolean,
vol.Optional(CONF_RECEIVE_TIMEOUT): cv.invalid("The receive_timeout option has been removed "
"because timeouts are already handled by the "
"low-level i2c interface.")
})
def to_code(config):
rhs = App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_SCAN))
i2c = Pvariable('I2CComponent', config[CONF_ID], rhs)
i2c = Pvariable(config[CONF_ID], rhs)
if CONF_FREQUENCY in config:
add(i2c.set_frequency(config[CONF_FREQUENCY]))
if CONF_RECEIVE_TIMEOUT in config:
add(i2c.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT]))
BUILD_FLAGS = '-DUSE_I2C'

View File

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

View File

@@ -1,25 +1,6 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent'
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID('ir_transmitter'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int),
vol.Range(min=1, max=100)),
})])
def to_code(config):
for conf in config:
pin = gpio_output_pin_expression(conf[CONF_PIN])
rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT))
Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
def CONFIG_SCHEMA(config):
raise vol.Invalid("The ir_transmitter component has been renamed to "
"remote_transmitter because of 433MHz signal support.")

View File

@@ -1,17 +1,450 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT
from esphomeyaml.helpers import add
from esphomeyaml.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECTS, CONF_EFFECT_ID, \
CONF_GAMMA_CORRECT, CONF_GREEN, CONF_ID, CONF_INTERNAL, CONF_LAMBDA, CONF_MQTT_ID, CONF_NAME, \
CONF_NUM_LEDS, CONF_RANDOM, CONF_RED, CONF_SPEED, CONF_STATE, CONF_TRANSITION_LENGTH, \
CONF_UPDATE_INTERVAL, CONF_WHITE, CONF_WIDTH, CONF_FLASH_LENGTH, CONF_COLOR_TEMPERATURE, \
CONF_EFFECT
from esphomeyaml.helpers import Application, ArrayInitializer, Pvariable, RawExpression, \
StructInitializer, add, add_job, esphomelib_ns, process_lambda, setup_mqtt_component, \
get_variable, TemplateArguments, templatable, uint32, float_, std_string
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
})
light_ns = esphomelib_ns.namespace('light')
LightState = light_ns.LightState
LightColorValues = light_ns.LightColorValues
MQTTJSONLightComponent = light_ns.MQTTJSONLightComponent
ToggleAction = light_ns.ToggleAction
TurnOffAction = light_ns.TurnOffAction
TurnOnAction = light_ns.TurnOnAction
MakeLight = Application.MakeLight
RandomLightEffect = light_ns.RandomLightEffect
LambdaLightEffect = light_ns.LambdaLightEffect
StrobeLightEffect = light_ns.StrobeLightEffect
StrobeLightEffectColor = light_ns.StrobeLightEffectColor
FlickerLightEffect = light_ns.FlickerLightEffect
FastLEDLambdaLightEffect = light_ns.FastLEDLambdaLightEffect
FastLEDRainbowLightEffect = light_ns.FastLEDRainbowLightEffect
FastLEDColorWipeEffect = light_ns.FastLEDColorWipeEffect
FastLEDColorWipeEffectColor = light_ns.FastLEDColorWipeEffectColor
FastLEDScanEffect = light_ns.FastLEDScanEffect
FastLEDScanEffectColor = light_ns.FastLEDScanEffectColor
FastLEDTwinkleEffect = light_ns.FastLEDTwinkleEffect
FastLEDRandomTwinkleEffect = light_ns.FastLEDRandomTwinkleEffect
FastLEDFireworksEffect = light_ns.FastLEDFireworksEffect
FastLEDFlickerEffect = light_ns.FastLEDFlickerEffect
FastLEDLightOutputComponent = light_ns.FastLEDLightOutputComponent
CONF_STROBE = 'strobe'
CONF_FLICKER = 'flicker'
CONF_FASTLED_LAMBDA = 'fastled_lambda'
CONF_FASTLED_RAINBOW = 'fastled_rainbow'
CONF_FASTLED_COLOR_WIPE = 'fastled_color_wipe'
CONF_FASTLED_SCAN = 'fastled_scan'
CONF_FASTLED_TWINKLE = 'fastled_twinkle'
CONF_FASTLED_RANDOM_TWINKLE = 'fastled_random_twinkle'
CONF_FASTLED_FIREWORKS = 'fastled_fireworks'
CONF_FASTLED_FLICKER = 'fastled_flicker'
CONF_ADD_LED_INTERVAL = 'add_led_interval'
CONF_REVERSE = 'reverse'
CONF_MOVE_INTERVAL = 'move_interval'
CONF_TWINKLE_PROBABILITY = 'twinkle_probability'
CONF_PROGRESS_INTERVAL = 'progress_interval'
CONF_SPARK_PROBABILITY = 'spark_probability'
CONF_USE_RANDOM_COLOR = 'use_random_color'
CONF_FADE_OUT_RATE = 'fade_out_rate'
CONF_INTENSITY = 'intensity'
BINARY_EFFECTS = [CONF_LAMBDA, CONF_STROBE]
MONOCHROMATIC_EFFECTS = BINARY_EFFECTS + [CONF_FLICKER]
RGB_EFFECTS = MONOCHROMATIC_EFFECTS + [CONF_RANDOM]
FASTLED_EFFECTS = RGB_EFFECTS + [CONF_FASTLED_LAMBDA, CONF_FASTLED_RAINBOW, CONF_FASTLED_COLOR_WIPE,
CONF_FASTLED_SCAN, CONF_FASTLED_TWINKLE,
CONF_FASTLED_RANDOM_TWINKLE, CONF_FASTLED_FIREWORKS,
CONF_FASTLED_FLICKER]
EFFECTS_SCHEMA = vol.Schema({
vol.Optional(CONF_LAMBDA): vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_RANDOM): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(RandomLightEffect),
vol.Optional(CONF_NAME, default="Random"): cv.string,
vol.Optional(CONF_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_STROBE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(StrobeLightEffect),
vol.Optional(CONF_NAME, default="Strobe"): cv.string,
vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.All(vol.Schema({
vol.Optional(CONF_STATE, default=True): cv.boolean,
vol.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
vol.Optional(CONF_RED, default=1.0): cv.percentage,
vol.Optional(CONF_GREEN, default=1.0): cv.percentage,
vol.Optional(CONF_BLUE, default=1.0): cv.percentage,
vol.Optional(CONF_WHITE, default=1.0): cv.percentage,
vol.Required(CONF_DURATION): cv.positive_time_period_milliseconds,
}), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE,
CONF_WHITE))], vol.Length(min=2)),
}),
vol.Optional(CONF_FLICKER): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FlickerLightEffect),
vol.Optional(CONF_NAME, default="Flicker"): cv.string,
vol.Optional(CONF_ALPHA): cv.percentage,
vol.Optional(CONF_INTENSITY): cv.percentage,
}),
vol.Optional(CONF_FASTLED_LAMBDA): vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_RAINBOW): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRainbowLightEffect),
vol.Optional(CONF_NAME, default="Rainbow"): cv.string,
vol.Optional(CONF_SPEED): cv.uint32_t,
vol.Optional(CONF_WIDTH): cv.uint32_t,
}),
vol.Optional(CONF_FASTLED_COLOR_WIPE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDColorWipeEffect),
vol.Optional(CONF_NAME, default="Color Wipe"): cv.string,
vol.Optional(CONF_COLORS): vol.All(cv.ensure_list, [vol.Schema({
vol.Optional(CONF_RED, default=1.0): cv.percentage,
vol.Optional(CONF_GREEN, default=1.0): cv.percentage,
vol.Optional(CONF_BLUE, default=1.0): cv.percentage,
vol.Optional(CONF_RANDOM, default=False): cv.boolean,
vol.Required(CONF_NUM_LEDS): vol.All(cv.uint32_t, vol.Range(min=1)),
})]),
vol.Optional(CONF_ADD_LED_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_REVERSE): cv.boolean,
}),
vol.Optional(CONF_FASTLED_SCAN): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDScanEffect),
vol.Optional(CONF_NAME, default="Scan"): cv.string,
vol.Optional(CONF_MOVE_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_TWINKLE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDTwinkleEffect),
vol.Optional(CONF_NAME, default="Twinkle"): cv.string,
vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage,
vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_RANDOM_TWINKLE): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDRandomTwinkleEffect),
vol.Optional(CONF_NAME, default="Random Twinkle"): cv.string,
vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage,
vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds,
}),
vol.Optional(CONF_FASTLED_FIREWORKS): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFireworksEffect),
vol.Optional(CONF_NAME, default="Fireworks"): cv.string,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_SPARK_PROBABILITY): cv.percentage,
vol.Optional(CONF_USE_RANDOM_COLOR): cv.boolean,
vol.Optional(CONF_FADE_OUT_RATE): cv.uint8_t,
}),
vol.Optional(CONF_FASTLED_FLICKER): vol.Schema({
cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FastLEDFlickerEffect),
vol.Optional(CONF_NAME, default="FastLED Flicker"): cv.string,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_INTENSITY): cv.percentage,
}),
})
def setup_light_component(obj, config):
def validate_effects(allowed_effects):
def validator(value):
value = cv.ensure_list(value)
names = set()
ret = []
for i, effect in enumerate(value):
if not isinstance(effect, dict):
raise vol.Invalid("Each effect must be a dictionary, not {}".format(type(value)))
if len(effect) > 1:
raise vol.Invalid("Each entry in the 'effects:' option must be a single effect.")
if not effect:
raise vol.Invalid("Found no effect for the {}th entry in 'effects:'!".format(i))
key = next(iter(effect.keys()))
if key not in allowed_effects:
raise vol.Invalid("The effect '{}' does not exist or is not allowed for this "
"light type".format(key))
effect[key] = effect[key] or {}
conf = EFFECTS_SCHEMA(effect)
name = conf[key][CONF_NAME]
if name in names:
raise vol.Invalid(u"Found the effect name '{}' twice. All effects must have "
u"unique names".format(name))
names.add(name)
ret.append(conf)
return ret
return validator
LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(LightState),
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTJSONLightComponent),
})
LIGHT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(LIGHT_SCHEMA.schema)
def build_effect(full_config):
key, config = next(iter(full_config.items()))
if key == CONF_LAMBDA:
lambda_ = None
for lambda_ in process_lambda(config[CONF_LAMBDA], []):
yield None
yield LambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL])
elif key == CONF_RANDOM:
rhs = RandomLightEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_TRANSITION_LENGTH in config:
add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH]))
if CONF_UPDATE_INTERVAL in config:
add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
yield effect
elif key == CONF_STROBE:
rhs = StrobeLightEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
colors = []
for color in config.get(CONF_COLORS, []):
colors.append(StructInitializer(
StrobeLightEffectColor,
('color', LightColorValues(color[CONF_STATE], color[CONF_BRIGHTNESS],
color[CONF_RED], color[CONF_GREEN], color[CONF_BLUE],
color[CONF_WHITE])),
('duration', color[CONF_DURATION]),
))
if colors:
add(effect.set_colors(ArrayInitializer(*colors)))
yield effect
elif key == CONF_FLICKER:
rhs = FlickerLightEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_ALPHA in config:
add(effect.set_alpha(config[CONF_ALPHA]))
if CONF_INTENSITY in config:
add(effect.set_intensity(config[CONF_INTENSITY]))
yield effect
elif key == CONF_FASTLED_LAMBDA:
lambda_ = None
args = [(RawExpression('FastLEDLightOutputComponent &'), 'it')]
for lambda_ in process_lambda(config[CONF_LAMBDA], args):
yield None
yield FastLEDLambdaLightEffect.new(config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL])
elif key == CONF_FASTLED_RAINBOW:
rhs = FastLEDRainbowLightEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_SPEED in config:
add(effect.set_speed(config[CONF_SPEED]))
if CONF_WIDTH in config:
add(effect.set_width(config[CONF_WIDTH]))
yield effect
elif key == CONF_FASTLED_COLOR_WIPE:
rhs = FastLEDColorWipeEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_ADD_LED_INTERVAL in config:
add(effect.set_add_led_interval(config[CONF_ADD_LED_INTERVAL]))
if CONF_REVERSE in config:
add(effect.set_reverse(config[CONF_REVERSE]))
colors = []
for color in config.get(CONF_COLORS, []):
colors.append(StructInitializer(
FastLEDColorWipeEffectColor,
('r', color[CONF_RED]),
('g', color[CONF_GREEN]),
('b', color[CONF_BLUE]),
('random', color[CONF_RANDOM]),
('num_leds', color[CONF_NUM_LEDS]),
))
if colors:
add(effect.set_colors(ArrayInitializer(*colors)))
yield effect
elif key == CONF_FASTLED_SCAN:
rhs = FastLEDScanEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_MOVE_INTERVAL in config:
add(effect.set_move_interval(config[CONF_MOVE_INTERVAL]))
yield effect
elif key == CONF_FASTLED_TWINKLE:
rhs = FastLEDTwinkleEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_TWINKLE_PROBABILITY in config:
add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
if CONF_PROGRESS_INTERVAL in config:
add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL]))
yield effect
elif key == CONF_FASTLED_RANDOM_TWINKLE:
rhs = FastLEDRandomTwinkleEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_TWINKLE_PROBABILITY in config:
add(effect.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
if CONF_PROGRESS_INTERVAL in config:
add(effect.set_progress_interval(config[CONF_PROGRESS_INTERVAL]))
yield effect
elif key == CONF_FASTLED_FIREWORKS:
rhs = FastLEDFireworksEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_UPDATE_INTERVAL in config:
add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
if CONF_SPARK_PROBABILITY in config:
add(effect.set_spark_probability(config[CONF_SPARK_PROBABILITY]))
if CONF_USE_RANDOM_COLOR in config:
add(effect.set_spark_probability(config[CONF_USE_RANDOM_COLOR]))
if CONF_FADE_OUT_RATE in config:
add(effect.set_spark_probability(config[CONF_FADE_OUT_RATE]))
yield effect
elif key == CONF_FASTLED_FLICKER:
rhs = FastLEDFlickerEffect.new(config[CONF_NAME])
effect = Pvariable(config[CONF_EFFECT_ID], rhs)
if CONF_UPDATE_INTERVAL in config:
add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
if CONF_INTENSITY in config:
add(effect.set_intensity(config[CONF_INTENSITY]))
yield effect
else:
raise NotImplementedError("Effect {} not implemented".format(next(config.keys())))
def setup_light_core_(light_var, mqtt_var, config):
if CONF_INTERNAL in config:
add(light_var.set_internal(config[CONF_INTERNAL]))
if CONF_DEFAULT_TRANSITION_LENGTH in config:
add(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
if CONF_GAMMA_CORRECT in config:
add(obj.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
effects = []
for conf in config.get(CONF_EFFECTS, []):
for effect in build_effect(conf):
yield
effects.append(effect)
if effects:
add(light_var.add_effects(ArrayInitializer(*effects)))
setup_mqtt_component(mqtt_var, config)
def setup_light(light_obj, mqtt_obj, config):
light_var = Pvariable(config[CONF_ID], light_obj, has_side_effects=False)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
add_job(setup_light_core_, light_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_LIGHT'
CONF_LIGHT_TOGGLE = 'light.toggle'
LIGHT_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
})
@ACTION_REGISTRY.register(CONF_LIGHT_TOGGLE, LIGHT_TOGGLE_ACTION_SCHEMA)
def light_toggle_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_toggle_action(template_arg)
type = ToggleAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
if CONF_TRANSITION_LENGTH in config:
for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32):
yield None
add(action.set_transition_length(template_))
yield action
CONF_LIGHT_TURN_OFF = 'light.turn_off'
LIGHT_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
vol.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
})
@ACTION_REGISTRY.register(CONF_LIGHT_TURN_OFF, LIGHT_TURN_OFF_ACTION_SCHEMA)
def light_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
type = TurnOffAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
if CONF_TRANSITION_LENGTH in config:
for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32):
yield None
add(action.set_transition_length(template_))
yield action
CONF_LIGHT_TURN_ON = 'light.turn_on'
LIGHT_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
vol.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'):
cv.templatable(cv.positive_time_period_milliseconds),
vol.Exclusive(CONF_FLASH_LENGTH, 'transformer'):
cv.templatable(cv.positive_time_period_milliseconds),
vol.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
vol.Optional(CONF_RED): cv.templatable(cv.percentage),
vol.Optional(CONF_GREEN): cv.templatable(cv.percentage),
vol.Optional(CONF_BLUE): cv.templatable(cv.percentage),
vol.Optional(CONF_WHITE): cv.templatable(cv.percentage),
vol.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.positive_float),
vol.Optional(CONF_EFFECT): cv.templatable(cv.string),
})
@ACTION_REGISTRY.register(CONF_LIGHT_TURN_ON, LIGHT_TURN_ON_ACTION_SCHEMA)
def light_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)
type = TurnOnAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
if CONF_TRANSITION_LENGTH in config:
for template_ in templatable(config[CONF_TRANSITION_LENGTH], arg_type, uint32):
yield None
add(action.set_transition_length(template_))
if CONF_FLASH_LENGTH in config:
for template_ in templatable(config[CONF_FLASH_LENGTH], arg_type, uint32):
yield None
add(action.set_flash_length(template_))
if CONF_BRIGHTNESS in config:
for template_ in templatable(config[CONF_BRIGHTNESS], arg_type, float_):
yield None
add(action.set_brightness(template_))
if CONF_RED in config:
for template_ in templatable(config[CONF_RED], arg_type, float_):
yield None
add(action.set_red(template_))
if CONF_GREEN in config:
for template_ in templatable(config[CONF_GREEN], arg_type, float_):
yield None
add(action.set_green(template_))
if CONF_BLUE in config:
for template_ in templatable(config[CONF_BLUE], arg_type, float_):
yield None
add(action.set_blue(template_))
if CONF_WHITE in config:
for template_ in templatable(config[CONF_WHITE], arg_type, float_):
yield None
add(action.set_white(template_))
if CONF_COLOR_TEMPERATURE in config:
for template_ in templatable(config[CONF_COLOR_TEMPERATURE], arg_type, float_):
yield None
add(action.set_color_temperature(template_))
if CONF_EFFECT in config:
for template_ in templatable(config[CONF_EFFECT], arg_type, std_string):
yield None
add(action.set_effect(template_))
yield action

View File

@@ -1,20 +1,21 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, variable, setup_mqtt_component
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT, CONF_EFFECTS
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('binary_light'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id,
})
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.BINARY_EFFECTS),
}))
def to_code(config):
output = get_variable(config[CONF_OUTPUT])
output = None
for output in get_variable(config[CONF_OUTPUT]):
yield
rhs = App.make_binary_light(config[CONF_NAME], output)
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
setup_mqtt_component(light_struct.Pmqtt, config)
light.setup_light_component(light_struct.Pstate, config)
light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)

View File

@@ -0,0 +1,34 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light
from esphomeyaml.components.light.rgbww import validate_cold_white_colder, \
validate_color_temperature
from esphomeyaml.const import CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
CONF_NAME, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
vol.Required(CONF_COLD_WHITE): cv.use_variable_id(None),
vol.Required(CONF_WARM_WHITE): cv.use_variable_id(None),
vol.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
vol.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS),
}), validate_cold_white_colder)
def to_code(config):
for cold_white in get_variable(config[CONF_COLD_WHITE]):
yield
for warm_white in get_variable(config[CONF_WARM_WHITE]):
yield
rhs = App.make_cwww_light(config[CONF_NAME], config[CONF_COLD_WHITE_COLOR_TEMPERATURE],
config[CONF_WARM_WHITE_COLOR_TEMPERATURE],
cold_white, warm_white)
light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)

View File

@@ -3,11 +3,12 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import light
from esphomeyaml.components.power_supply import PowerSupplyComponent
from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \
CONF_RGB_ORDER
from esphomeyaml.helpers import App, RawExpression, TemplateArguments, add, get_variable, \
setup_mqtt_component, variable
CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \
CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
get_variable, variable
TYPES = [
'NEOPIXEL',
@@ -52,25 +53,29 @@ def validate(value):
return value
PLATFORM_SCHEMA = vol.All(light.PLATFORM_SCHEMA.extend({
cv.GenerateID('fast_led_clockless_light'): cv.register_variable_id,
MakeFastLEDLight = Application.MakeFastLEDLight
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, vol.Any(*TYPES)),
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight),
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*TYPES)),
vol.Required(CONF_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, vol.Any(*RGB_ORDERS)),
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)),
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.variable_id,
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
}), validate)
def to_code(config):
rhs = App.make_fast_led_light(config[CONF_NAME])
make = variable('Application::MakeFastLEDLight', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
fast_led = make.Pfast_led
rgb_order = None
@@ -84,11 +89,16 @@ def to_code(config):
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
if CONF_POWER_SUPPLY in config:
power_supply = get_variable(config[CONF_POWER_SUPPLY])
power_supply = None
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
yield
add(fast_led.set_power_supply(power_supply))
setup_mqtt_component(make.Pmqtt, config)
light.setup_light_component(make.Pstate, config)
if CONF_COLOR_CORRECT in config:
r, g, b = config[CONF_COLOR_CORRECT]
add(fast_led.set_correction(r, g, b))
light.setup_light(make.Pstate, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'

View File

@@ -3,11 +3,12 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import light
from esphomeyaml.components.power_supply import PowerSupplyComponent
from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, \
CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER
from esphomeyaml.helpers import App, RawExpression, TemplateArguments, add, get_variable, \
setup_mqtt_component, variable
CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \
CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \
get_variable, variable
CHIPSETS = [
'LPD8806',
@@ -29,26 +30,30 @@ RGB_ORDERS = [
'BGR',
]
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('fast_led_spi_light'): cv.register_variable_id,
MakeFastLEDLight = Application.MakeFastLEDLight
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, vol.Any(*CHIPSETS)),
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight),
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*CHIPSETS)),
vol.Required(CONF_DATA_PIN): pins.output_pin,
vol.Required(CONF_CLOCK_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, vol.Any(*RGB_ORDERS)),
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)),
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)),
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.variable_id,
})
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS),
}))
def to_code(config):
rhs = App.make_fast_led_light(config[CONF_NAME])
make = variable('Application::MakeFastLEDLight', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
fast_led = make.Pfast_led
rgb_order = None
@@ -64,11 +69,16 @@ def to_code(config):
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
if CONF_POWER_SUPPLY in config:
power_supply = get_variable(config[CONF_POWER_SUPPLY])
power_supply = None
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
yield
add(fast_led.set_power_supply(power_supply))
setup_mqtt_component(make.Pmqtt, config)
light.setup_light_component(make.Pstate, config)
if CONF_COLOR_CORRECT in config:
r, g, b = config[CONF_COLOR_CORRECT]
add(fast_led.set_correction(r, g, b))
light.setup_light(make.Pstate, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'

View File

@@ -2,21 +2,23 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
CONF_NAME, CONF_OUTPUT, CONF_EFFECTS
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('monochromatic_light'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id,
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
})
vol.Optional(CONF_EFFECTS): light.validate_effects(light.MONOCHROMATIC_EFFECTS),
}))
def to_code(config):
output = get_variable(config[CONF_OUTPUT])
output = None
for output in get_variable(config[CONF_OUTPUT]):
yield
rhs = App.make_monochromatic_light(config[CONF_NAME], output)
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
setup_mqtt_component(light_struct.Pmqtt, config)
light.setup_light_component(light_struct.Pstate, config)
light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)

View File

@@ -3,24 +3,30 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_EFFECTS
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('rgb_light'): cv.register_variable_id,
vol.Required(CONF_RED): cv.variable_id,
vol.Required(CONF_GREEN): cv.variable_id,
vol.Required(CONF_BLUE): cv.variable_id,
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
vol.Required(CONF_RED): cv.use_variable_id(None),
vol.Required(CONF_GREEN): cv.use_variable_id(None),
vol.Required(CONF_BLUE): cv.use_variable_id(None),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
})
vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS),
}))
def to_code(config):
red = get_variable(config[CONF_RED])
green = get_variable(config[CONF_GREEN])
blue = get_variable(config[CONF_BLUE])
red = None
for red in get_variable(config[CONF_RED]):
yield
green = None
for green in get_variable(config[CONF_GREEN]):
yield
blue = None
for blue in get_variable(config[CONF_BLUE]):
yield
rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue)
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
setup_mqtt_component(light_struct.Pmqtt, config)
light.setup_light_component(light_struct.Pstate, config)
light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)

View File

@@ -3,26 +3,34 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE, CONF_EFFECTS
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('rgbw_light'): cv.register_variable_id,
vol.Required(CONF_RED): cv.variable_id,
vol.Required(CONF_GREEN): cv.variable_id,
vol.Required(CONF_BLUE): cv.variable_id,
vol.Required(CONF_WHITE): cv.variable_id,
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
vol.Required(CONF_RED): cv.use_variable_id(None),
vol.Required(CONF_GREEN): cv.use_variable_id(None),
vol.Required(CONF_BLUE): cv.use_variable_id(None),
vol.Required(CONF_WHITE): cv.use_variable_id(None),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
})
vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS),
}))
def to_code(config):
red = get_variable(config[CONF_RED])
green = get_variable(config[CONF_GREEN])
blue = get_variable(config[CONF_BLUE])
white = get_variable(config[CONF_WHITE])
red = None
for red in get_variable(config[CONF_RED]):
yield
green = None
for green in get_variable(config[CONF_GREEN]):
yield
blue = None
for blue in get_variable(config[CONF_BLUE]):
yield
white = None
for white in get_variable(config[CONF_WHITE]):
yield
rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white)
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
setup_mqtt_component(light_struct.Pmqtt, config)
light.setup_light_component(light_struct.Pstate, config)
light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)

View File

@@ -0,0 +1,62 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import light
from esphomeyaml.const import CONF_BLUE, CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, \
CONF_NAME, CONF_RED, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
from esphomeyaml.helpers import App, get_variable, variable
def validate_color_temperature(value):
try:
val = cv.float_with_unit('Color Temperature', 'mireds')(value)
except vol.Invalid:
val = 1000000.0 / cv.float_with_unit('Color Temperature', 'K')(value)
if val < 0:
raise vol.Invalid("Color temperature cannot be negative")
return val
def validate_cold_white_colder(value):
cw = value[CONF_COLD_WHITE_COLOR_TEMPERATURE]
ww = value[CONF_WARM_WHITE_COLOR_TEMPERATURE]
if cw > ww:
raise vol.Invalid("Cold white color temperature cannot be higher than warm white")
if cw == ww:
raise vol.Invalid("Cold white color temperature cannot be the same as warm white")
return value
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
vol.Required(CONF_RED): cv.use_variable_id(None),
vol.Required(CONF_GREEN): cv.use_variable_id(None),
vol.Required(CONF_BLUE): cv.use_variable_id(None),
vol.Required(CONF_COLD_WHITE): cv.use_variable_id(None),
vol.Required(CONF_WARM_WHITE): cv.use_variable_id(None),
vol.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
vol.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): validate_color_temperature,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_EFFECTS): light.validate_effects(light.RGB_EFFECTS),
}), validate_cold_white_colder)
def to_code(config):
for red in get_variable(config[CONF_RED]):
yield
for green in get_variable(config[CONF_GREEN]):
yield
for blue in get_variable(config[CONF_BLUE]):
yield
for cold_white in get_variable(config[CONF_COLD_WHITE]):
yield
for warm_white in get_variable(config[CONF_WARM_WHITE]):
yield
rhs = App.make_rgbww_light(config[CONF_NAME], config[CONF_COLD_WHITE_COLOR_TEMPERATURE],
config[CONF_WARM_WHITE_COLOR_TEMPERATURE],
red, green, blue, cold_white, warm_white)
light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config)

View File

@@ -1,51 +1,128 @@
import re
import voluptuous as vol
from esphomeyaml.automation import ACTION_REGISTRY, LambdaAction
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGGER, CONF_LOGS, \
CONF_TX_BUFFER_SIZE
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, Pvariable, RawExpression, add
from esphomeyaml.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_ID, CONF_LEVEL, \
CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE
from esphomeyaml.core import ESPHomeYAMLError, Lambda
from esphomeyaml.helpers import App, Pvariable, TemplateArguments, add, esphomelib_ns, global_ns, \
process_lambda, RawExpression, statement
LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
LOG_LEVELS = {
'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE,
'ERROR': global_ns.ESPHOMELIB_LOG_LEVEL_ERROR,
'WARN': global_ns.ESPHOMELIB_LOG_LEVEL_WARN,
'INFO': global_ns.ESPHOMELIB_LOG_LEVEL_INFO,
'DEBUG': global_ns.ESPHOMELIB_LOG_LEVEL_DEBUG,
'VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERBOSE,
'VERY_VERBOSE': global_ns.ESPHOMELIB_LOG_LEVEL_VERY_VERBOSE,
}
LOG_LEVEL_TO_ESP_LOG = {
'ERROR': global_ns.ESP_LOGE,
'WARN': global_ns.ESP_LOGW,
'INFO': global_ns.ESP_LOGI,
'DEBUG': global_ns.ESP_LOGD,
'VERBOSE': global_ns.ESP_LOGV,
'VERY_VERBOSE': global_ns.ESP_LOGVV,
}
LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
# pylint: disable=invalid-name
is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS))
is_log_level = vol.All(vol.Upper, cv.one_of(*LOG_LEVELS))
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(CONF_LOGGER): cv.register_variable_id,
vol.Optional(CONF_BAUD_RATE): cv.positive_int,
vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int,
def validate_local_no_higher_than_global(value):
global_level = value.get(CONF_LEVEL, 'DEBUG')
for tag, level in value.get(CONF_LOGS, {}).iteritems():
if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the "
u"global log level {}.".format(level, tag, global_level))
return value
LogComponent = esphomelib_ns.LogComponent
CONFIG_SCHEMA = vol.All(vol.Schema({
cv.GenerateID(): cv.declare_variable_id(LogComponent),
vol.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
vol.Optional(CONF_TX_BUFFER_SIZE): cv.validate_bytes,
vol.Optional(CONF_LEVEL): is_log_level,
vol.Optional(CONF_LOGS): vol.Schema({
cv.string: is_log_level,
})
})
def esphomelib_log_level(level):
return u'ESPHOMELIB_LOG_LEVEL_{}'.format(level)
def exp_log_level(level):
return RawExpression(esphomelib_log_level(level))
}), validate_local_no_higher_than_global)
def to_code(config):
rhs = App.init_log(config.get(CONF_BAUD_RATE))
log = Pvariable(u'LogComponent', config[CONF_ID], rhs)
log = Pvariable(config[CONF_ID], rhs)
if CONF_TX_BUFFER_SIZE in config:
add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
if CONF_LEVEL in config:
add(log.set_global_log_level(exp_log_level(config[CONF_LEVEL])))
add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]]))
for tag, level in config.get(CONF_LOGS, {}).iteritems():
global_level = config.get(CONF_LEVEL, 'DEBUG')
if LOG_LEVELS.index(level) > LOG_LEVELS.index(global_level):
raise ESPHomeYAMLError(u"The local log level {} for {} must be less severe than the "
u"global log level {}.".format(level, tag, global_level))
add(log.set_log_level(tag, exp_log_level(level)))
add(log.set_log_level(tag, LOG_LEVELS[level]))
def required_build_flags(config):
if CONF_LEVEL in config:
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL]))
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]]))
return None
def maybe_simple_message(schema):
def validator(value):
if isinstance(value, dict):
return vol.Schema(schema)(value)
return vol.Schema(schema)({CONF_FORMAT: value})
return validator
def validate_printf(value):
# https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
# pylint: disable=anomalous-backslash-in-string
cfmt = u"""\
( # start of capture group 1
% # literal "%"
(?: # first option
(?:[-+0 #]{0,5}) # optional flags
(?:\d+|\*)? # width
(?:\.(?:\d+|\*))? # precision
(?:h|l|ll|w|I|I32|I64)? # size
[cCdiouxXeEfgGaAnpsSZ] # type
) | # OR
%%) # literal "%%"
"""
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X)
if len(matches) != len(value[CONF_ARGS]):
raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!"
u"".format(len(matches), u', '.join(matches), len(value[CONF_ARGS])))
return value
CONF_LOGGER_LOG = 'logger.log'
LOGGER_LOG_ACTION_SCHEMA = vol.All(maybe_simple_message({
vol.Required(CONF_FORMAT): cv.string,
vol.Optional(CONF_ARGS, default=list): vol.All(cv.ensure_list, [cv.lambda_]),
vol.Optional(CONF_LEVEL, default="DEBUG"): vol.All(vol.Upper, cv.one_of(*LOG_LEVEL_TO_ESP_LOG)),
vol.Optional(CONF_TAG, default="main"): cv.string,
}), validate_printf)
@ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA)
def logger_log_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]]
args = [RawExpression(unicode(x)) for x in config[CONF_ARGS]]
text = unicode(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]):
yield None
rhs = LambdaAction.new(template_arg, lambda_)
type = LambdaAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)

View File

@@ -2,23 +2,28 @@ import re
import voluptuous as vol
from esphomeyaml import automation
from esphomeyaml.automation import ACTION_REGISTRY
from esphomeyaml.components import logger
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_SSL_FINGERPRINTS, CONF_ID, CONF_LOG_TOPIC, \
CONF_MQTT, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, \
CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_KEEPALIVE
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, StructInitializer, add, \
exp_empty_optional, RawExpression
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \
CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \
CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \
CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \
uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef
def validate_message_just_topic(value):
value = cv.publish_topic(value)
return {CONF_TOPIC: value}
return MQTT_MESSAGE_BASE({CONF_TOPIC: value})
MQTT_MESSAGE_BASE = vol.Schema({
vol.Required(CONF_TOPIC): cv.publish_topic,
vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
vol.Optional(CONF_RETAIN, default=True): cv.boolean,
})
@@ -28,12 +33,17 @@ MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({
vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
}))
mqtt_ns = esphomelib_ns.namespace('mqtt')
MQTTMessage = mqtt_ns.MQTTMessage
MQTTClientComponent = mqtt_ns.MQTTClientComponent
MQTTPublishAction = mqtt_ns.MQTTPublishAction
MQTTPublishJsonAction = mqtt_ns.MQTTPublishJsonAction
MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger
MQTTJsonMessageTrigger = mqtt_ns.MQTTJsonMessageTrigger
def validate_broker(value):
value = cv.string_strict(value)
if value.endswith(u'.local'):
raise vol.Invalid(u"MQTT server addresses ending with '.local' are currently unsupported."
u" Please use the static IP instead.")
if u':' in value:
raise vol.Invalid(u"Please specify the port using the port: option")
if not value:
@@ -49,7 +59,7 @@ def validate_fingerprint(value):
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(CONF_MQTT): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent),
vol.Required(CONF_BROKER): validate_broker,
vol.Optional(CONF_PORT, default=1883): cv.port,
vol.Optional(CONF_USERNAME, default=''): cv.string,
@@ -60,19 +70,33 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_DISCOVERY_PREFIX): cv.publish_topic,
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_SHUTDOWN_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic,
vol.Optional(CONF_LOG_TOPIC): MQTT_MESSAGE_TEMPLATE_SCHEMA,
vol.Optional(CONF_LOG_TOPIC): vol.Any(None, MQTT_MESSAGE_BASE.extend({
vol.Optional(CONF_LEVEL): logger.is_log_level,
}), validate_message_just_topic),
vol.Optional(CONF_SSL_FINGERPRINTS): vol.All(cv.only_on_esp8266,
cv.ensure_list, [validate_fingerprint]),
vol.Optional(CONF_KEEPALIVE): cv.positive_time_period_seconds,
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_ON_MESSAGE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
vol.Required(CONF_TOPIC): cv.subscribe_topic,
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
}),
vol.Optional(CONF_ON_JSON_MESSAGE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTJsonMessageTrigger),
vol.Required(CONF_TOPIC): cv.subscribe_topic,
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
}),
})
def exp_mqtt_message(config):
if config is None:
return exp_empty_optional('mqtt::MQTTMessage')
return optional(TemplateArguments(MQTTMessage))
exp = StructInitializer(
'mqtt::MQTTMessage',
MQTTMessage,
('topic', config[CONF_TOPIC]),
('payload', config.get(CONF_PAYLOAD, "")),
('qos', config[CONF_QOS]),
@@ -84,42 +108,132 @@ def exp_mqtt_message(config):
def to_code(config):
rhs = App.init_mqtt(config[CONF_BROKER], config[CONF_PORT],
config[CONF_USERNAME], config[CONF_PASSWORD])
mqtt = Pvariable('mqtt::MQTTClientComponent', config[CONF_ID], rhs)
mqtt = Pvariable(config[CONF_ID], rhs)
if not config.get(CONF_DISCOVERY, True):
add(mqtt.disable_discovery())
if CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
elif CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
add(mqtt.set_discovery_info(discovery_prefix, discovery_retain))
if CONF_TOPIC_PREFIX in config:
add(mqtt.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
if CONF_BIRTH_MESSAGE in config:
birth_message = config[CONF_BIRTH_MESSAGE]
if birth_message is None:
if not birth_message:
add(mqtt.disable_birth_message())
else:
add(mqtt.set_birth_message(exp_mqtt_message(birth_message)))
if CONF_WILL_MESSAGE in config:
will_message = config[CONF_WILL_MESSAGE]
if will_message is None:
if not will_message:
add(mqtt.disable_last_will())
else:
add(mqtt.set_last_will(exp_mqtt_message(will_message)))
if CONF_SHUTDOWN_MESSAGE in config:
shutdown_message = config[CONF_SHUTDOWN_MESSAGE]
if not shutdown_message:
add(mqtt.disable_shutdown_message())
else:
add(mqtt.set_shutdown_message(exp_mqtt_message(shutdown_message)))
if CONF_CLIENT_ID in config:
add(mqtt.set_client_id(config[CONF_CLIENT_ID]))
if CONF_LOG_TOPIC in config:
log_topic = config[CONF_LOG_TOPIC]
if log_topic is None:
if not log_topic:
add(mqtt.disable_log_message())
else:
add(mqtt.set_log_topic(exp_mqtt_message(log_topic)))
add(mqtt.set_log_message_template(exp_mqtt_message(log_topic)))
if CONF_LEVEL in config:
add(mqtt.set_log_level(logger.LOG_LEVELS[config[CONF_LEVEL]]))
if CONF_SSL_FINGERPRINTS in config:
for fingerprint in config[CONF_SSL_FINGERPRINTS]:
arr = [RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)]
add(mqtt.add_ssl_fingerprint(ArrayInitializer(*arr, multiline=False)))
if CONF_KEEPALIVE in config:
add(mqtt.set_keep_alive(config[CONF_KEEPALIVE]))
if CONF_REBOOT_TIMEOUT in config:
add(mqtt.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
for conf in config.get(CONF_ON_MESSAGE, []):
rhs = mqtt.make_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS])
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, std_string, conf)
for conf in config.get(CONF_ON_JSON_MESSAGE, []):
rhs = mqtt.make_json_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS])
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, JsonObjectConstRef, conf)
CONF_MQTT_PUBLISH = 'mqtt.publish'
MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
vol.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload),
vol.Optional(CONF_QOS): cv.templatable(cv.mqtt_qos),
vol.Optional(CONF_RETAIN): cv.templatable(cv.boolean),
})
@ACTION_REGISTRY.register(CONF_MQTT_PUBLISH, MQTT_PUBLISH_ACTION_SCHEMA)
def mqtt_publish_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
rhs = App.Pget_mqtt_client().Pmake_publish_action(template_arg)
type = MQTTPublishAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
for template_ in templatable(config[CONF_TOPIC], arg_type, std_string):
yield None
add(action.set_topic(template_))
for template_ in templatable(config[CONF_PAYLOAD], arg_type, std_string):
yield None
add(action.set_payload(template_))
if CONF_QOS in config:
for template_ in templatable(config[CONF_QOS], arg_type, uint8):
yield
add(action.set_qos(template_))
if CONF_RETAIN in config:
for template_ in templatable(config[CONF_RETAIN], arg_type, bool_):
yield None
add(action.set_retain(template_))
yield action
CONF_MQTT_PUBLISH_JSON = 'mqtt.publish_json'
MQTT_PUBLISH_JSON_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
vol.Required(CONF_PAYLOAD): cv.lambda_,
vol.Optional(CONF_QOS): cv.mqtt_qos,
vol.Optional(CONF_RETAIN): cv.boolean,
})
@ACTION_REGISTRY.register(CONF_MQTT_PUBLISH_JSON, MQTT_PUBLISH_JSON_ACTION_SCHEMA)
def mqtt_publish_json_action_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
rhs = App.Pget_mqtt_client().Pmake_publish_json_action(template_arg)
type = MQTTPublishJsonAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
for template_ in templatable(config[CONF_TOPIC], arg_type, std_string):
yield None
add(action.set_topic(template_))
for lambda_ in process_lambda(config[CONF_PAYLOAD], [(arg_type, 'x'), (JsonObjectRef, 'root')]):
yield None
add(action.set_payload(lambda_))
if CONF_QOS in config:
add(action.set_qos(config[CONF_QOS]))
if CONF_RETAIN in config:
add(action.set_retain(config[CONF_RETAIN]))
yield action
def required_build_flags(config):
if CONF_SSL_FINGERPRINTS in config:

View File

@@ -1,19 +1,20 @@
import hashlib
import logging
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, Pvariable, add
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns
_LOGGER = logging.getLogger(__name__)
OTAComponent = esphomelib_ns.OTAComponent
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(CONF_OTA): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(OTAComponent),
vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
# TODO Num attempts + wait time
vol.Optional(CONF_PORT): cv.port,
@@ -23,10 +24,11 @@ CONFIG_SCHEMA = vol.Schema({
def to_code(config):
rhs = App.init_ota()
ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
ota = Pvariable(config[CONF_ID], rhs)
if CONF_PASSWORD in config:
hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
add(ota.set_auth_password_hash(hash_))
add(ota.set_auth_password(config[CONF_PASSWORD]))
if CONF_PORT in config:
add(ota.set_port(config[CONF_PORT]))
if config[CONF_SAFE_MODE]:
add(ota.start_safe_mode())
@@ -46,6 +48,7 @@ def get_auth(config):
BUILD_FLAGS = '-DUSE_OTA'
REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA'
def lib_deps(config):

View File

@@ -1,28 +1,103 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_POWER_SUPPLY, CONF_INVERTED, CONF_MAX_POWER
from esphomeyaml.helpers import get_variable, add
from esphomeyaml.components.power_supply import PowerSupplyComponent
from esphomeyaml.const import CONF_INVERTED, CONF_MAX_POWER, CONF_POWER_SUPPLY, CONF_ID, CONF_LEVEL
from esphomeyaml.helpers import add, esphomelib_ns, get_variable, TemplateArguments, Pvariable, \
templatable, bool_
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
vol.Optional(CONF_POWER_SUPPLY): cv.variable_id,
vol.Optional(CONF_INVERTED): cv.boolean,
}).extend(cv.REQUIRED_ID_SCHEMA.schema)
FLOAT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_MAX_POWER): cv.zero_to_one_float,
})
BINARY_OUTPUT_SCHEMA = vol.Schema({
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_INVERTED): cv.boolean,
})
def setup_output_platform(obj, config, skip_power_supply=False):
BINARY_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BINARY_OUTPUT_SCHEMA.schema)
FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({
vol.Optional(CONF_MAX_POWER): cv.percentage,
})
FLOAT_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FLOAT_OUTPUT_SCHEMA.schema)
output_ns = esphomelib_ns.namespace('output')
TurnOffAction = output_ns.TurnOffAction
TurnOnAction = output_ns.TurnOnAction
SetLevelAction = output_ns.SetLevelAction
def setup_output_platform_(obj, config, skip_power_supply=False):
if CONF_INVERTED in config:
add(obj.set_inverted(config[CONF_INVERTED]))
if not skip_power_supply and CONF_POWER_SUPPLY in config:
power_supply = get_variable(config[CONF_POWER_SUPPLY])
power_supply = None
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
yield
add(obj.set_power_supply(power_supply))
if CONF_MAX_POWER in config:
add(obj.set_max_power(config[CONF_MAX_POWER]))
def setup_output_platform(obj, config, skip_power_supply=False):
for _ in setup_output_platform_(obj, config, skip_power_supply):
yield
BUILD_FLAGS = '-DUSE_OUTPUT'
CONF_OUTPUT_TURN_ON = 'output.turn_on'
OUTPUT_TURN_OFF_ACTION = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_OFF_ACTION)
def output_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
type = TurnOffAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_OUTPUT_TURN_OFF = 'output.turn_off'
OUTPUT_TURN_ON_ACTION = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None)
})
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_ON_ACTION)
def output_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)
type = TurnOnAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_OUTPUT_SET_LEVEL = 'output.set_level'
OUTPUT_SET_LEVEL_ACTION = vol.Schema({
vol.Required(CONF_ID): cv.use_variable_id(None),
vol.Required(CONF_LEVEL): cv.percentage,
})
@ACTION_REGISTRY.register(CONF_OUTPUT_SET_LEVEL, OUTPUT_SET_LEVEL_ACTION)
def output_set_level_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_set_level_action(template_arg)
type = SetLevelAction.template(arg_type)
action = Pvariable(action_id, rhs, type=type)
for template_ in templatable(config[CONF_LEVEL], arg_type, bool_):
yield None
add(action.set_level(template_))
yield action

View File

@@ -1,8 +1,9 @@
import voluptuous as vol
from esphomeyaml import pins
import esphomeyaml.config_validation as cv
from esphomeyaml.components import output
from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266, CONF_NUMBER
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
@@ -10,20 +11,25 @@ ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
def valid_pwm_pin(value):
if value[CONF_NUMBER] >= 16:
if value[CONF_NUMBER] > 16:
raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.")
return value
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PIN): vol.All(pins.GPIO_INTERNAL_OUTPUT_PIN_SCHEMA, valid_pwm_pin),
ESP8266PWMOutput = output.output_ns.ESP8266PWMOutput
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_ID): cv.declare_variable_id(ESP8266PWMOutput),
vol.Required(CONF_PIN): vol.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin),
})
def to_code(config):
pin = gpio_output_pin_expression(config[CONF_PIN])
pin = None
for pin in gpio_output_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_esp8266_pwm_output(pin)
gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs)
gpio = Pvariable(config[CONF_ID], rhs)
output.setup_output_platform(gpio, config)

View File

@@ -1,19 +1,25 @@
import voluptuous as vol
from esphomeyaml import pins
import esphomeyaml.config_validation as cv
from esphomeyaml.components import output
from esphomeyaml.const import CONF_ID, CONF_PIN
from esphomeyaml.helpers import App, Pvariable, gpio_output_pin_expression
PLATFORM_SCHEMA = output.PLATFORM_SCHEMA.extend({
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
GPIOBinaryOutputComponent = output.output_ns.GPIOBinaryOutputComponent
PLATFORM_SCHEMA = output.BINARY_OUTPUT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_ID): cv.declare_variable_id(GPIOBinaryOutputComponent),
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
})
def to_code(config):
pin = gpio_output_pin_expression(config[CONF_PIN])
pin = None
for pin in gpio_output_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_gpio_output(pin)
gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs)
gpio = Pvariable(config[CONF_ID], rhs)
output.setup_output_platform(gpio, config)

View File

@@ -15,12 +15,15 @@ def validate_frequency_bit_depth(obj):
bit_depth = obj.get(CONF_BIT_DEPTH, 12)
max_freq = APB_CLOCK_FREQ / (2**bit_depth)
if frequency > max_freq:
raise vol.Invalid('Maximum frequency for bit depth {} is {}'.format(bit_depth, max_freq))
raise vol.Invalid('Maximum frequency for bit depth {} is {}Hz'.format(bit_depth, max_freq))
return obj
PLATFORM_SCHEMA = vol.All(output.FLOAT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PIN): vol.All(pins.output_pin, vol.Range(min=0, max=33)),
LEDCOutputComponent = output.output_ns.LEDCOutputComponent
PLATFORM_SCHEMA = vol.All(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_ID): cv.declare_variable_id(LEDCOutputComponent),
vol.Required(CONF_PIN): pins.output_pin,
vol.Optional(CONF_FREQUENCY): cv.frequency,
vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int), vol.Range(min=1, max=15)),
vol.Optional(CONF_CHANNEL): vol.All(vol.Coerce(int), vol.Range(min=0, max=15))
@@ -32,7 +35,7 @@ def to_code(config):
if frequency is None and CONF_BIT_DEPTH in config:
frequency = 1000
rhs = App.make_ledc_output(config[CONF_PIN], frequency, config.get(CONF_BIT_DEPTH))
ledc = Pvariable('output::LEDCOutputComponent', config[CONF_ID], rhs)
ledc = Pvariable(config[CONF_ID], rhs)
if CONF_CHANNEL in config:
add(ledc.set_channel(config[CONF_CHANNEL]))
output.setup_output_platform(ledc, config)

View File

@@ -2,26 +2,32 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import output
from esphomeyaml.components.pca9685 import PCA9685_COMPONENT_TYPE
from esphomeyaml.components.pca9685 import PCA9685OutputComponent
from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_PCA9685_ID, CONF_POWER_SUPPLY
from esphomeyaml.helpers import Pvariable, get_variable
DEPENDENCIES = ['pca9685']
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
Channel = PCA9685OutputComponent.Channel
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_ID): cv.declare_variable_id(Channel),
vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int),
vol.Range(min=0, max=15)),
vol.Optional(CONF_PCA9685_ID): cv.variable_id,
cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685OutputComponent),
})
def to_code(config):
power_supply = None
if CONF_POWER_SUPPLY in config:
power_supply = get_variable(config[CONF_POWER_SUPPLY])
pca9685 = get_variable(config.get(CONF_PCA9685_ID), PCA9685_COMPONENT_TYPE)
for power_supply in get_variable(config[CONF_POWER_SUPPLY]):
yield
pca9685 = None
for pca9685 in get_variable(config[CONF_PCA9685_ID]):
yield
rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs)
out = Pvariable(config[CONF_ID], rhs)
output.setup_output_platform(out, config, skip_power_supply=True)

View File

@@ -1,20 +1,19 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import output
from esphomeyaml.const import CONF_ADDRESS, CONF_FREQUENCY, CONF_ID, CONF_PHASE_BALANCER
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, RawExpression, add
from esphomeyaml.helpers import App, HexIntLiteral, Pvariable, add
DEPENDENCIES = ['i2c']
PHASE_BALANCERS = ['None', 'Linear', 'Weaved']
PCA9685_COMPONENT_TYPE = 'output::PCA9685OutputComponent'
PCA9685OutputComponent = output.output_ns.namespace('PCA9685OutputComponent')
PHASE_BALANCER_MESSAGE = ("The phase_balancer option has been removed in version 1.5.0. "
"esphomelib will now automatically choose a suitable phase balancer.")
PCA9685_SCHEMA = vol.Schema({
cv.GenerateID('pca9685'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(PCA9685OutputComponent),
vol.Required(CONF_FREQUENCY): vol.All(cv.frequency,
vol.Range(min=23.84, max=1525.88)),
vol.Optional(CONF_ADDRESS): cv.i2c_address,
@@ -28,13 +27,9 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA])
def to_code(config):
for conf in config:
rhs = App.make_pca9685_component(conf.get(CONF_FREQUENCY))
pca9685 = Pvariable(PCA9685_COMPONENT_TYPE, conf[CONF_ID], rhs)
pca9685 = Pvariable(conf[CONF_ID], rhs)
if CONF_ADDRESS in conf:
add(pca9685.set_address(HexIntLiteral(conf[CONF_ADDRESS])))
if CONF_PHASE_BALANCER in conf:
phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format(
conf[CONF_PHASE_BALANCER]))
add(pca9685.set_phase_balancer(phase_balancer))
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'

View File

@@ -2,12 +2,15 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_PCF8575
from esphomeyaml.helpers import App, Pvariable
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns
DEPENDENCIES = ['i2c']
io_ns = esphomelib_ns.namespace('io')
PCF8574Component = io_ns.PCF8574Component
PCF8574_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.register_variable_id,
vol.Required(CONF_ID): cv.declare_variable_id(PCF8574Component),
vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address,
vol.Optional(CONF_PCF8575, default=False): cv.boolean,
})
@@ -18,7 +21,7 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCF8574_SCHEMA])
def to_code(config):
for conf in config:
rhs = App.make_pcf8574_component(conf[CONF_ADDRESS], conf[CONF_PCF8575])
Pvariable('io::PCF8574Component', conf[CONF_ID], rhs)
Pvariable(conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_PCF8574'

View File

@@ -0,0 +1,43 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins, automation
from esphomeyaml.components import binary_sensor
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_SPI_ID, CONF_UPDATE_INTERVAL, \
CONF_ON_TAG, CONF_TRIGGER_ID
from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression, std_string
DEPENDENCIES = ['spi']
PN532Component = binary_sensor.binary_sensor_ns.PN532Component
PN532Trigger = binary_sensor.binary_sensor_ns.PN532Trigger
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID(): cv.declare_variable_id(PN532Component),
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
vol.Optional(CONF_ON_TAG): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PN532Trigger),
}),
})])
def to_code(config):
for conf in config:
spi = None
for spi in get_variable(conf[CONF_SPI_ID]):
yield
cs = None
for cs in gpio_output_pin_expression(conf[CONF_CS_PIN]):
yield
rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL))
pn532 = Pvariable(conf[CONF_ID], rhs)
for conf_ in conf.get(CONF_ON_TAG, []):
trigger = Pvariable(conf_[CONF_TRIGGER_ID], pn532.make_trigger())
automation.build_automation(trigger, std_string, conf_)
BUILD_FLAGS = '-DUSE_PN532'

View File

@@ -3,10 +3,13 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression
POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
PowerSupplyComponent = esphomelib_ns.PowerSupplyComponent
POWER_SUPPLY_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.declare_variable_id(PowerSupplyComponent),
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds,
vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period_milliseconds,
})
@@ -16,9 +19,11 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA])
def to_code(config):
for conf in config:
pin = gpio_output_pin_expression(conf[CONF_PIN])
pin = None
for pin in gpio_output_pin_expression(conf[CONF_PIN]):
yield
rhs = App.make_power_supply(pin)
psu = Pvariable('PowerSupplyComponent', conf[CONF_ID], rhs)
psu = Pvariable(conf[CONF_ID], rhs)
if CONF_ENABLE_TIME in conf:
add(psu.set_enable_time(conf[CONF_ENABLE_TIME]))
if CONF_KEEP_ON_TIME in conf:

View File

@@ -0,0 +1,28 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.components.uart import UARTComponent
from esphomeyaml.const import CONF_ID, CONF_UART_ID
from esphomeyaml.helpers import App, Pvariable, get_variable
DEPENDENCIES = ['uart']
RDM6300Component = binary_sensor.binary_sensor_ns.RDM6300Component
CONFIG_SCHEMA = vol.All(cv.ensure_list_not_empty, [vol.Schema({
cv.GenerateID(): cv.declare_variable_id(RDM6300Component),
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
})])
def to_code(config):
for conf in config:
uart = None
for uart in get_variable(conf[CONF_UART_ID]):
yield
rhs = App.make_rdm6300_component(uart)
Pvariable(conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_RDM6300'

View File

@@ -0,0 +1,64 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, \
CONF_PIN, CONF_TOLERANCE
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_input_pin_expression
remote_ns = esphomelib_ns.namespace('remote')
RemoteReceiverComponent = remote_ns.RemoteReceiverComponent
DUMPERS = {
'lg': remote_ns.LGDumper,
'nec': remote_ns.NECDumper,
'panasonic': remote_ns.PanasonicDumper,
'raw': remote_ns.RawDumper,
'samsung': remote_ns.SamsungDumper,
'sony': remote_ns.SonyDumper,
'rc_switch': remote_ns.RCSwitchDumper,
}
def validate_dumpers_all(value):
if not isinstance(value, (str, unicode)):
raise vol.Invalid("Not valid dumpers")
if value.upper() == "ALL":
return list(sorted(list(DUMPERS)))
raise vol.Invalid("Not valid dumpers")
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID(): cv.declare_variable_id(RemoteReceiverComponent),
vol.Required(CONF_PIN): pins.gpio_input_pin_schema,
vol.Optional(CONF_DUMP, default=[]):
vol.Any(validate_dumpers_all,
vol.All(cv.ensure_list, [vol.All(vol.Lower, cv.one_of(*DUMPERS))])),
vol.Optional(CONF_TOLERANCE): vol.All(cv.percentage_int, vol.Range(min=0)),
vol.Optional(CONF_BUFFER_SIZE): cv.validate_bytes,
vol.Optional(CONF_FILTER): cv.positive_time_period_microseconds,
vol.Optional(CONF_IDLE): cv.positive_time_period_microseconds,
})])
def to_code(config):
for conf in config:
pin = None
for pin in gpio_input_pin_expression(conf[CONF_PIN]):
yield
rhs = App.make_remote_receiver_component(pin)
receiver = Pvariable(conf[CONF_ID], rhs)
for dumper in conf[CONF_DUMP]:
add(receiver.add_dumper(DUMPERS[dumper].new()))
if CONF_TOLERANCE in conf:
add(receiver.set_tolerance(conf[CONF_TOLERANCE]))
if CONF_BUFFER_SIZE in conf:
add(receiver.set_buffer_size(conf[CONF_BUFFER_SIZE]))
if CONF_FILTER in conf:
add(receiver.set_filter_us(conf[CONF_FILTER]))
if CONF_IDLE in conf:
add(receiver.set_idle_us(conf[CONF_IDLE]))
BUILD_FLAGS = '-DUSE_REMOTE_RECEIVER'

View File

@@ -0,0 +1,116 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_DUTY_PERCENT, CONF_CHANNEL, CONF_CODE, \
CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_ID, CONF_INVERTED, CONF_ONE, CONF_PIN, \
CONF_PROTOCOL, CONF_PULSE_LENGTH, CONF_STATE, CONF_SYNC, CONF_ZERO
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, gpio_output_pin_expression
remote_ns = esphomelib_ns.namespace('remote')
RemoteTransmitterComponent = remote_ns.RemoteTransmitterComponent
RCSwitchProtocol = remote_ns.RCSwitchProtocol
rc_switch_protocols = remote_ns.rc_switch_protocols
def validate_rc_switch_code(value):
if not isinstance(value, (str, unicode)):
raise vol.Invalid("All RCSwitch codes must be in quotes ('')")
for c in value:
if c not in ('0', '1'):
raise vol.Invalid(u"Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed"
u"".format(c))
if len(value) > 32:
raise vol.Invalid("Maximum length for RCSwitch codes is 32, code '{}' has length {}"
"".format(value, len(value)))
if not value:
raise vol.Invalid("RCSwitch code must not be empty")
return value
RC_SWITCH_TIMING_SCHEMA = vol.All([cv.uint8_t], vol.Length(min=2, max=2))
RC_SWITCH_PROTOCOL_SCHEMA = vol.Any(
vol.All(vol.Coerce(int), vol.Range(min=1, max=7)),
vol.Schema({
vol.Required(CONF_PULSE_LENGTH): cv.uint32_t,
vol.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA,
vol.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA,
vol.Optional(CONF_ONE, default=[3, 1]): RC_SWITCH_TIMING_SCHEMA,
vol.Optional(CONF_INVERTED, default=False): cv.boolean,
})
)
RC_SWITCH_RAW_SCHEMA = vol.Schema({
vol.Required(CONF_CODE): validate_rc_switch_code,
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
})
RC_SWITCH_TYPE_A_SCHEMA = vol.Schema({
vol.Required(CONF_GROUP): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)),
vol.Required(CONF_DEVICE): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)),
vol.Required(CONF_STATE): cv.boolean,
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
})
RC_SWITCH_TYPE_B_SCHEMA = vol.Schema({
vol.Required(CONF_ADDRESS): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
vol.Required(CONF_CHANNEL): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
vol.Required(CONF_STATE): cv.boolean,
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
})
RC_SWITCH_TYPE_C_SCHEMA = vol.Schema({
vol.Required(CONF_FAMILY): vol.All(
cv.string, vol.Lower,
cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p')),
vol.Required(CONF_GROUP): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=4)),
vol.Required(CONF_STATE): cv.boolean,
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
})
RC_SWITCH_TYPE_D_SCHEMA = vol.Schema({
vol.Required(CONF_GROUP): vol.All(cv.string, vol.Lower, cv.one_of('a', 'b', 'c', 'd')),
vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=3)),
vol.Required(CONF_STATE): cv.boolean,
vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
})
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID(): cv.declare_variable_id(RemoteTransmitterComponent),
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(cv.percentage_int,
vol.Range(min=1, max=100)),
})])
def build_rc_switch_protocol(config):
if isinstance(config, int):
return rc_switch_protocols[config]
pl = config[CONF_PULSE_LENGTH]
return RCSwitchProtocol(config[CONF_SYNC][0] * pl, config[CONF_SYNC][1] * pl,
config[CONF_ZERO][0] * pl, config[CONF_ZERO][1] * pl,
config[CONF_ONE][0] * pl, config[CONF_ONE][1] * pl,
config[CONF_INVERTED])
def binary_code(value):
code = 0
for val in value:
code <<= 1
code |= val == '1'
return HexInt(code)
def to_code(config):
for conf in config:
pin = None
for pin in gpio_output_pin_expression(conf[CONF_PIN]):
yield
rhs = App.make_remote_transmitter_component(pin)
transmitter = Pvariable(conf[CONF_ID], rhs)
if CONF_CARRIER_DUTY_PERCENT in conf:
add(transmitter.set_carrier_duty_percent(conf[CONF_CARRIER_DUTY_PERCENT]))
BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER'

View 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)

View File

@@ -1,13 +1,15 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_DEBOUNCE, CONF_DELTA, \
CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, \
CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, \
CONF_NAME, CONF_OFFSET, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \
CONF_THROTTLE, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
setup_mqtt_component
from esphomeyaml import automation
from esphomeyaml.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \
CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, \
CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_HEARTBEAT, CONF_ICON, CONF_ID, CONF_INTERNAL, \
CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, CONF_OFFSET, CONF_ON_RAW_VALUE, CONF_ON_VALUE, \
CONF_ON_VALUE_RANGE, CONF_OR, CONF_SEND_EVERY, CONF_SLIDING_WINDOW_MOVING_AVERAGE, \
CONF_THROTTLE, CONF_TRIGGER_ID, CONF_UNIQUE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, add_job, esphomelib_ns, \
float_, process_lambda, setup_mqtt_component, templatable
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@@ -18,123 +20,182 @@ def validate_recursive_filter(value):
return FILTERS_SCHEMA(value)
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.Any(
vol.Schema({vol.Required(CONF_OFFSET): vol.Coerce(float)}),
vol.Schema({vol.Required(CONF_MULTIPLY): vol.Coerce(float)}),
vol.Schema({vol.Required(CONF_FILTER_OUT): vol.Coerce(float)}),
vol.Schema({vol.Required(CONF_FILTER_NAN): None}),
vol.Schema({
vol.Required(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
})
}),
vol.Schema({
vol.Required(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
vol.Required(CONF_ALPHA): cv.positive_float,
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
})
}),
vol.Schema({vol.Required(CONF_LAMBDA): cv.string_strict}),
vol.Schema({vol.Required(CONF_THROTTLE): cv.positive_time_period_milliseconds}),
vol.Schema({vol.Required(CONF_DELTA): vol.Coerce(float)}),
vol.Schema({vol.Required(CONF_UNIQUE): None}),
vol.Schema({vol.Required(CONF_HEARTBEAT): cv.positive_time_period_milliseconds}),
vol.Schema({vol.Required(CONF_DEBOUNCE): cv.positive_time_period_milliseconds}),
vol.Schema({vol.Required(CONF_OR): validate_recursive_filter}),
)])
FILTER_KEYS = [CONF_OFFSET, CONF_MULTIPLY, CONF_FILTER_OUT, CONF_FILTER_NAN,
CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_LAMBDA,
CONF_THROTTLE, CONF_DELTA, CONF_UNIQUE, CONF_HEARTBEAT, CONF_DEBOUNCE, CONF_OR]
MQTT_SENSOR_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({
vol.Optional(CONF_OFFSET): vol.Coerce(float),
vol.Optional(CONF_MULTIPLY): vol.Coerce(float),
vol.Optional(CONF_FILTER_OUT): vol.Coerce(float),
vol.Optional(CONF_FILTER_NAN): None,
vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.Schema({
vol.Required(CONF_WINDOW_SIZE): cv.positive_not_null_int,
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
}),
vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({
vol.Required(CONF_ALPHA): cv.positive_float,
vol.Required(CONF_SEND_EVERY): cv.positive_not_null_int,
}),
vol.Optional(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_THROTTLE): cv.positive_time_period_milliseconds,
vol.Optional(CONF_DELTA): vol.Coerce(float),
vol.Optional(CONF_UNIQUE): None,
vol.Optional(CONF_HEARTBEAT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_DEBOUNCE): cv.positive_time_period_milliseconds,
vol.Optional(CONF_OR): validate_recursive_filter,
}, cv.has_exactly_one_key(*FILTER_KEYS))])
# pylint: disable=invalid-name
sensor_ns = esphomelib_ns.namespace('sensor')
Sensor = sensor_ns.Sensor
MQTTSensorComponent = sensor_ns.MQTTSensorComponent
OffsetFilter = sensor_ns.OffsetFilter
MultiplyFilter = sensor_ns.MultiplyFilter
FilterOutValueFilter = sensor_ns.FilterOutValueFilter
FilterOutNANFilter = sensor_ns.FilterOutNANFilter
SlidingWindowMovingAverageFilter = sensor_ns.SlidingWindowMovingAverageFilter
ExponentialMovingAverageFilter = sensor_ns.ExponentialMovingAverageFilter
LambdaFilter = sensor_ns.LambdaFilter
ThrottleFilter = sensor_ns.ThrottleFilter
DeltaFilter = sensor_ns.DeltaFilter
OrFilter = sensor_ns.OrFilter
HeartbeatFilter = sensor_ns.HeartbeatFilter
DebounceFilter = sensor_ns.DebounceFilter
UniqueFilter = sensor_ns.UniqueFilter
SensorStateTrigger = sensor_ns.SensorStateTrigger
SensorRawStateTrigger = sensor_ns.SensorRawStateTrigger
ValueRangeTrigger = sensor_ns.ValueRangeTrigger
SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSensorComponent),
cv.GenerateID(): cv.declare_variable_id(Sensor),
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict,
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int),
vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period_milliseconds),
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
vol.Optional(CONF_ON_VALUE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorStateTrigger),
}),
vol.Optional(CONF_ON_RAW_VALUE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorRawStateTrigger),
}),
vol.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ValueRangeTrigger),
vol.Optional(CONF_ABOVE): vol.Coerce(float),
vol.Optional(CONF_BELOW): vol.Coerce(float),
}, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)),
})
MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
cv.GenerateID('mqtt_sensor', CONF_MQTT_ID): cv.register_variable_id,
})
# pylint: disable=invalid-name
OffsetFilter = MockObj('new sensor::OffsetFilter')
MultiplyFilter = MockObj('new sensor::MultiplyFilter')
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
FilterOutNANFilter = MockObj('new sensor::FilterOutNANFilter')
SlidingWindowMovingAverageFilter = MockObj('new sensor::SlidingWindowMovingAverageFilter')
ExponentialMovingAverageFilter = MockObj('new sensor::ExponentialMovingAverageFilter')
LambdaFilter = MockObj('new sensor::LambdaFilter')
ThrottleFilter = MockObj('new sensor::ThrottleFilter')
DeltaFilter = MockObj('new sensor::DeltaFilter')
OrFilter = MockObj('new sensor::OrFilter')
HeartbeatFilter = MockObj('new sensor::HeartbeatFilter')
DebounceFilter = MockObj('new sensor::DebounceFilter')
UniqueFilter = MockObj('new sensor::UniqueFilter')
SENSOR_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SENSOR_SCHEMA.schema)
def setup_filter(config):
if CONF_OFFSET in config:
return OffsetFilter(config[CONF_OFFSET])
if CONF_MULTIPLY in config:
return MultiplyFilter(config[CONF_MULTIPLY])
if CONF_FILTER_OUT in config:
return FilterOutValueFilter(config[CONF_FILTER_OUT])
if CONF_FILTER_NAN in config:
return FilterOutNANFilter()
if CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
yield OffsetFilter.new(config[CONF_OFFSET])
elif CONF_MULTIPLY in config:
yield MultiplyFilter.new(config[CONF_MULTIPLY])
elif CONF_FILTER_OUT in config:
yield FilterOutValueFilter.new(config[CONF_FILTER_OUT])
elif CONF_FILTER_NAN in config:
yield FilterOutNANFilter.new()
elif CONF_SLIDING_WINDOW_MOVING_AVERAGE in config:
conf = config[CONF_SLIDING_WINDOW_MOVING_AVERAGE]
return SlidingWindowMovingAverageFilter(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
if CONF_EXPONENTIAL_MOVING_AVERAGE in config:
yield SlidingWindowMovingAverageFilter.new(conf[CONF_WINDOW_SIZE], conf[CONF_SEND_EVERY])
elif CONF_EXPONENTIAL_MOVING_AVERAGE in config:
conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE]
return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
if CONF_LAMBDA in config:
s = u'[](float x) -> Optional<float> {{ return {}; }}'.format(config[CONF_LAMBDA])
return LambdaFilter(RawExpression(s))
if CONF_THROTTLE in config:
return ThrottleFilter(config[CONF_THROTTLE])
if CONF_DELTA in config:
return DeltaFilter(config[CONF_DELTA])
if CONF_OR in config:
return OrFilter(setup_filters(config[CONF_OR]))
if CONF_HEARTBEAT in config:
return App.register_component(HeartbeatFilter(config[CONF_HEARTBEAT]))
if CONF_DEBOUNCE in config:
return App.register_component(DebounceFilter(config[CONF_DEBOUNCE]))
if CONF_UNIQUE in config:
return UniqueFilter()
raise ValueError(u"Filter unsupported: {}".format(config))
yield ExponentialMovingAverageFilter.new(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
elif CONF_LAMBDA in config:
lambda_ = None
for lambda_ in process_lambda(config[CONF_LAMBDA], [(float_, 'x')]):
yield None
yield LambdaFilter.new(lambda_)
elif CONF_THROTTLE in config:
yield ThrottleFilter.new(config[CONF_THROTTLE])
elif CONF_DELTA in config:
yield DeltaFilter.new(config[CONF_DELTA])
elif CONF_OR in config:
filters = None
for filters in setup_filters(config[CONF_OR]):
yield None
yield OrFilter.new(filters)
elif CONF_HEARTBEAT in config:
yield App.register_component(HeartbeatFilter.new(config[CONF_HEARTBEAT]))
elif CONF_DEBOUNCE in config:
yield App.register_component(DebounceFilter.new(config[CONF_DEBOUNCE]))
elif CONF_UNIQUE in config:
yield UniqueFilter.new()
def setup_filters(config):
return ArrayInitializer(*[setup_filter(x) for x in config])
filters = []
for conf in config:
filter = None
for filter in setup_filter(conf):
yield None
filters.append(filter)
yield ArrayInitializer(*filters)
def setup_mqtt_sensor_component(obj, config):
def setup_sensor_core_(sensor_var, mqtt_var, config):
if CONF_INTERNAL in config:
add(sensor_var.set_internal(config[CONF_INTERNAL]))
if CONF_UNIT_OF_MEASUREMENT in config:
add(sensor_var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
if CONF_ICON in config:
add(sensor_var.set_icon(config[CONF_ICON]))
if CONF_ACCURACY_DECIMALS in config:
add(sensor_var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
if CONF_FILTERS in config:
filters = None
for filters in setup_filters(config[CONF_FILTERS]):
yield
add(sensor_var.set_filters(filters))
for conf in config.get(CONF_ON_VALUE, []):
rhs = sensor_var.make_state_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, float_, conf)
for conf in config.get(CONF_ON_RAW_VALUE, []):
rhs = sensor_var.make_raw_state_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, float_, conf)
for conf in config.get(CONF_ON_VALUE_RANGE, []):
rhs = sensor_var.make_value_range_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
if CONF_ABOVE in conf:
template_ = None
for template_ in templatable(conf[CONF_ABOVE], float_, float_):
yield
add(trigger.set_min(template_))
if CONF_BELOW in conf:
template_ = None
for template_ in templatable(conf[CONF_BELOW], float_, float_):
yield
add(trigger.set_max(template_))
automation.build_automation(trigger, float_, conf)
if CONF_EXPIRE_AFTER in config:
if config[CONF_EXPIRE_AFTER] is None:
add(obj.disable_expire_after())
add(mqtt_var.disable_expire_after())
else:
add(obj.set_expire_after(config[CONF_EXPIRE_AFTER]))
setup_mqtt_component(obj, config)
add(mqtt_var.set_expire_after(config[CONF_EXPIRE_AFTER]))
setup_mqtt_component(mqtt_var, config)
def setup_sensor(obj, config):
if CONF_UNIT_OF_MEASUREMENT in config:
add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
if CONF_ICON in config:
add(obj.set_icon(config[CONF_ICON]))
if CONF_ACCURACY_DECIMALS in config:
add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
if CONF_FILTERS in config:
add(obj.set_filters(setup_filters(config[CONF_FILTERS])))
def setup_sensor(sensor_obj, mqtt_obj, config):
sensor_var = Pvariable(config[CONF_ID], sensor_obj, has_side_effects=False)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
def register_sensor(var, config):
setup_sensor(var, config)
rhs = App.register_sensor(var)
mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_MQTT_ID], rhs)
setup_mqtt_sensor_component(mqtt_sensor, config)
sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
rhs = App.register_sensor(sensor_var)
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_SENSOR'

View File

@@ -3,37 +3,52 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ATTENUATION, CONF_ID, CONF_NAME, CONF_PIN, \
from esphomeyaml.const import CONF_ATTENUATION, CONF_MAKE_ID, CONF_NAME, CONF_PIN, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, RawExpression, add, variable
from esphomeyaml.helpers import App, Application, add, global_ns, variable
ATTENUATION_MODES = {
'0db': 'ADC_0db',
'2.5db': 'ADC_2_5db',
'6db': 'ADC_6db',
'11db': 'ADC_11db',
'0db': global_ns.ADC_0db,
'2.5db': global_ns.ADC_2_5db,
'6db': global_ns.ADC_6db,
'11db': global_ns.ADC_11db,
}
ATTENUATION_MODE_SCHEMA = vol.Any(*list(ATTENUATION_MODES.keys()))
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('adc'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.analog_pin,
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, ATTENUATION_MODE_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
def validate_adc_pin(value):
vcc = str(value).upper()
if vcc == 'VCC':
return cv.only_on_esp8266(vcc)
return pins.analog_pin(value)
MakeADCSensor = Application.MakeADCSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeADCSensor),
vol.Required(CONF_PIN): validate_adc_pin,
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_adc_sensor(config[CONF_NAME], config[CONF_PIN],
pin = config[CONF_PIN]
if pin == 'VCC':
pin = 0
rhs = App.make_adc_sensor(config[CONF_NAME], pin,
config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeADCSensor', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
adc = make.Padc
if CONF_ATTENUATION in config:
attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]]
add(adc.set_attenuation(RawExpression(attenuation)))
sensor.setup_sensor(adc, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
add(adc.set_attenuation(ATTENUATION_MODES[config[CONF_ATTENUATION]]))
sensor.setup_sensor(make.Padc, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_ADC_SENSOR'
def required_build_flags(config):
if config[CONF_PIN] == 'VCC':
return '-DUSE_ADC_SENSOR_VCC'
return None

View File

@@ -2,30 +2,31 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \
CONF_NAME, CONF_ID
from esphomeyaml.helpers import RawExpression, get_variable, Pvariable
from esphomeyaml.components.ads1115 import ADS1115Component
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import get_variable
DEPENDENCIES = ['ads1115']
MUX = {
'A0_A1': 'sensor::ADS1115_MULTIPLEXER_P0_N1',
'A0_A3': 'sensor::ADS1115_MULTIPLEXER_P0_N3',
'A1_A3': 'sensor::ADS1115_MULTIPLEXER_P1_N3',
'A2_A3': 'sensor::ADS1115_MULTIPLEXER_P2_N3',
'A0_GND': 'sensor::ADS1115_MULTIPLEXER_P0_NG',
'A1_GND': 'sensor::ADS1115_MULTIPLEXER_P1_NG',
'A2_GND': 'sensor::ADS1115_MULTIPLEXER_P2_NG',
'A3_GND': 'sensor::ADS1115_MULTIPLEXER_P3_NG',
'A0_A1': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N1,
'A0_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_N3,
'A1_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_N3,
'A2_A3': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_N3,
'A0_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P0_NG,
'A1_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P1_NG,
'A2_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P2_NG,
'A3_GND': sensor.sensor_ns.ADS1115_MULTIPLEXER_P3_NG,
}
GAIN = {
'6.144': 'sensor::ADS1115_GAIN_6P144',
'4.096': 'sensor::ADS1115_GAIN_6P096',
'2.048': 'sensor::ADS1115_GAIN_2P048',
'1.024': 'sensor::ADS1115_GAIN_1P024',
'0.512': 'sensor::ADS1115_GAIN_0P512',
'0.256': 'sensor::ADS1115_GAIN_0P256',
'6.144': sensor.sensor_ns.ADS1115_GAIN_6P144,
'4.096': sensor.sensor_ns.ADS1115_GAIN_6P096,
'2.048': sensor.sensor_ns.ADS1115_GAIN_2P048,
'1.024': sensor.sensor_ns.ADS1115_GAIN_1P024,
'0.512': sensor.sensor_ns.ADS1115_GAIN_0P512,
'0.256': sensor.sensor_ns.ADS1115_GAIN_0P256,
}
@@ -35,28 +36,32 @@ def validate_gain(value):
elif not isinstance(value, (str, unicode)):
raise vol.Invalid('invalid gain "{}"'.format(value))
if value not in GAIN:
raise vol.Invalid("Invalid gain, options are {}".format(', '.join(GAIN.keys())))
return value
return cv.one_of(*GAIN)(value)
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('ads1115_sensor'): cv.register_variable_id,
vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))),
def validate_mux(value):
value = cv.string(value).upper()
value = value.replace(' ', '_')
return cv.one_of(*MUX)(value)
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_MULTIPLEXER): validate_mux,
vol.Required(CONF_GAIN): validate_gain,
vol.Optional(CONF_ADS1115_ID): cv.variable_id,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema)
cv.GenerateID(CONF_ADS1115_ID): cv.use_variable_id(ADS1115Component),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
hub = get_variable(config.get(CONF_ADS1115_ID), u'sensor::ADS1115Component')
hub = None
for hub in get_variable(config[CONF_ADS1115_ID]):
yield
mux = RawExpression(MUX[config[CONF_MULTIPLEXER]])
gain = RawExpression(GAIN[config[CONF_GAIN]])
mux = MUX[config[CONF_MULTIPLEXER]]
gain = GAIN[config[CONF_GAIN]]
rhs = hub.get_sensor(config[CONF_NAME], mux, gain, config.get(CONF_UPDATE_INTERVAL))
sensor_ = Pvariable('sensor::ADS1115Sensor', config[CONF_ID], rhs)
sensor.register_sensor(sensor_, config)
sensor.register_sensor(rhs, config)
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View File

@@ -2,36 +2,36 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_RESOLUTION, \
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, RawExpression, add, variable
from esphomeyaml.helpers import App, Application, add, variable
DEPENDENCIES = ['i2c']
BH1750_RESOLUTIONS = {
4.0: 'sensor::BH1750_RESOLUTION_4P0_LX',
1.0: 'sensor::BH1750_RESOLUTION_1P0_LX',
0.5: 'sensor::BH1750_RESOLUTION_0P5_LX',
4.0: sensor.sensor_ns.BH1750_RESOLUTION_4P0_LX,
1.0: sensor.sensor_ns.BH1750_RESOLUTION_1P0_LX,
0.5: sensor.sensor_ns.BH1750_RESOLUTION_0P5_LX,
}
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('bh1750_sensor'): cv.register_variable_id,
MakeBH1750Sensor = Application.MakeBH1750Sensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBH1750Sensor),
vol.Optional(CONF_ADDRESS, default=0x23): cv.i2c_address,
vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, vol.Any(*BH1750_RESOLUTIONS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
vol.Optional(CONF_RESOLUTION): vol.All(cv.positive_float, cv.one_of(*BH1750_RESOLUTIONS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_bh1750_sensor(config[CONF_NAME], config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
make_bh1750 = variable('Application::MakeBH1750Sensor', config[CONF_ID], rhs)
make_bh1750 = variable(config[CONF_MAKE_ID], rhs)
bh1750 = make_bh1750.Pbh1750
if CONF_RESOLUTION in config:
constant = BH1750_RESOLUTIONS[config[CONF_RESOLUTION]]
add(bh1750.set_resolution(RawExpression(constant)))
sensor.setup_sensor(bh1750, config)
sensor.setup_mqtt_sensor_component(make_bh1750.Pmqtt, config)
add(bh1750.set_resolution(BH1750_RESOLUTIONS[config[CONF_RESOLUTION]]))
sensor.setup_sensor(bh1750, make_bh1750.Pmqtt, config)
BUILD_FLAGS = '-DUSE_BH1750'

View File

@@ -0,0 +1,23 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
make_address_array
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME
from esphomeyaml.helpers import get_variable
DEPENDENCIES = ['esp32_ble_tracker']
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker)
}))
def to_code(config):
hub = None
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
yield
rhs = hub.make_rssi_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS]))
sensor.register_sensor(rhs, config)

View File

@@ -2,42 +2,43 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_NAME, \
CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, RawExpression, add, variable
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_IIR_FILTER, CONF_MAKE_ID, \
CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, add, variable
DEPENDENCIES = ['i2c']
OVERSAMPLING_OPTIONS = {
'NONE': 'sensor::BME280_OVERSAMPLING_NONE',
'1X': 'sensor::BME280_OVERSAMPLING_1X',
'2X': 'sensor::BME280_OVERSAMPLING_2X',
'4X': 'sensor::BME280_OVERSAMPLING_4X',
'8X': 'sensor::BME280_OVERSAMPLING_8X',
'16X': 'sensor::BME280_OVERSAMPLING_16X',
'NONE': sensor.sensor_ns.BME280_OVERSAMPLING_NONE,
'1X': sensor.sensor_ns.BME280_OVERSAMPLING_1X,
'2X': sensor.sensor_ns.BME280_OVERSAMPLING_2X,
'4X': sensor.sensor_ns.BME280_OVERSAMPLING_4X,
'8X': sensor.sensor_ns.BME280_OVERSAMPLING_8X,
'16X': sensor.sensor_ns.BME280_OVERSAMPLING_16X,
}
IIR_FILTER_OPTIONS = {
'OFF': 'sensor::BME280_IIR_FILTER_OFF',
'2X': 'sensor::BME280_IIR_FILTER_2X',
'4X': 'sensor::BME280_IIR_FILTER_4X',
'8X': 'sensor::BME280_IIR_FILTER_8X',
'16X': 'sensor::BME280_IIR_FILTER_16X',
'OFF': sensor.sensor_ns.BME280_IIR_FILTER_OFF,
'2X': sensor.sensor_ns.BME280_IIR_FILTER_2X,
'4X': sensor.sensor_ns.BME280_IIR_FILTER_4X,
'8X': sensor.sensor_ns.BME280_IIR_FILTER_8X,
'16X': sensor.sensor_ns.BME280_IIR_FILTER_16X,
}
BME280_OVERSAMPLING_SENSOR_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, vol.Any(*OVERSAMPLING_OPTIONS)),
BME280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
})
MakeBME280Sensor = Application.MakeBME280Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('bme280'): cv.register_variable_id,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME280Sensor),
vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address,
vol.Required(CONF_TEMPERATURE): BME280_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_PRESSURE): BME280_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): BME280_OVERSAMPLING_SENSOR_SCHEMA,
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, vol.Any(*IIR_FILTER_OPTIONS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Required(CONF_TEMPERATURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA),
vol.Required(CONF_PRESSURE): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(BME280_OVERSAMPLING_SENSOR_SCHEMA),
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
@@ -47,29 +48,27 @@ def to_code(config):
config[CONF_HUMIDITY][CONF_NAME],
config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeBME280Sensor', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
bme280 = make.Pbme280
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
add(bme280.set_temperature_oversampling(RawExpression(constant)))
add(bme280.set_temperature_oversampling(constant))
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
add(bme280.set_pressure_oversampling(RawExpression(constant)))
add(bme280.set_pressure_oversampling(constant))
if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
add(bme280.set_humidity_oversampling(RawExpression(constant)))
add(bme280.set_humidity_oversampling(constant))
if CONF_IIR_FILTER in config:
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
add(bme280.set_iir_filter(RawExpression(constant)))
add(bme280.set_iir_filter(constant))
sensor.setup_sensor(bme280.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(bme280.Pget_pressure_sensor(), config[CONF_PRESSURE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_pressure, config[CONF_PRESSURE])
sensor.setup_sensor(bme280.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(make.Pmqtt_humidity, config[CONF_HUMIDITY])
sensor.setup_sensor(bme280.Pget_temperature_sensor(), make.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(bme280.Pget_pressure_sensor(), make.Pmqtt_pressure,
config[CONF_PRESSURE])
sensor.setup_sensor(bme280.Pget_humidity_sensor(), make.Pmqtt_humidity,
config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_BME280'

View File

@@ -1,48 +1,55 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_NAME, \
CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, CONF_GAS_RESISTANCE
from esphomeyaml.helpers import App, RawExpression, add, variable
from esphomeyaml.const import CONF_ADDRESS, CONF_GAS_RESISTANCE, CONF_HUMIDITY, CONF_IIR_FILTER, \
CONF_MAKE_ID, CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL, CONF_HEATER, CONF_DURATION
from esphomeyaml.helpers import App, Application, add, variable
DEPENDENCIES = ['i2c']
OVERSAMPLING_OPTIONS = {
'NONE': 'sensor::BME680_OVERSAMPLING_NONE',
'1X': 'sensor::BME680_OVERSAMPLING_1X',
'2X': 'sensor::BME680_OVERSAMPLING_2X',
'4X': 'sensor::BME680_OVERSAMPLING_4X',
'8X': 'sensor::BME680_OVERSAMPLING_8X',
'16X': 'sensor::BME680_OVERSAMPLING_16X',
'NONE': sensor.sensor_ns.BME680_OVERSAMPLING_NONE,
'1X': sensor.sensor_ns.BME680_OVERSAMPLING_1X,
'2X': sensor.sensor_ns.BME680_OVERSAMPLING_2X,
'4X': sensor.sensor_ns.BME680_OVERSAMPLING_4X,
'8X': sensor.sensor_ns.BME680_OVERSAMPLING_8X,
'16X': sensor.sensor_ns.BME680_OVERSAMPLING_16X,
}
IIR_FILTER_OPTIONS = {
'OFF': 'sensor::BME680_IIR_FILTER_OFF',
'1X': 'sensor::BME680_IIR_FILTER_1X',
'3X': 'sensor::BME680_IIR_FILTER_3X',
'7X': 'sensor::BME680_IIR_FILTER_7X',
'15X': 'sensor::BME680_IIR_FILTER_15X',
'31X': 'sensor::BME680_IIR_FILTER_31X',
'63X': 'sensor::BME680_IIR_FILTER_63X',
'127X': 'sensor::BME680_IIR_FILTER_127X',
'OFF': sensor.sensor_ns.BME680_IIR_FILTER_OFF,
'1X': sensor.sensor_ns.BME680_IIR_FILTER_1X,
'3X': sensor.sensor_ns.BME680_IIR_FILTER_3X,
'7X': sensor.sensor_ns.BME680_IIR_FILTER_7X,
'15X': sensor.sensor_ns.BME680_IIR_FILTER_15X,
'31X': sensor.sensor_ns.BME680_IIR_FILTER_31X,
'63X': sensor.sensor_ns.BME680_IIR_FILTER_63X,
'127X': sensor.sensor_ns.BME680_IIR_FILTER_127X,
}
BME680_OVERSAMPLING_SENSOR_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, vol.Any(*OVERSAMPLING_OPTIONS)),
BME680_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
})
MakeBME680Sensor = Application.MakeBME680Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('bme680'): cv.register_variable_id,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBME680Sensor),
vol.Optional(CONF_ADDRESS, default=0x76): cv.i2c_address,
vol.Required(CONF_TEMPERATURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_PRESSURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): BME680_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_GAS_RESISTANCE): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, vol.Any(*IIR_FILTER_OPTIONS)),
# TODO: Heater
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Required(CONF_TEMPERATURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA),
vol.Required(CONF_PRESSURE): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(BME680_OVERSAMPLING_SENSOR_SCHEMA),
vol.Required(CONF_GAS_RESISTANCE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
vol.Optional(CONF_HEATER): vol.Any(None, vol.All(vol.Schema({
vol.Optional(CONF_TEMPERATURE, default=320): vol.All(vol.Coerce(int), vol.Range(200, 400)),
vol.Optional(CONF_DURATION, default='150ms'): vol.All(
cv.positive_time_period_milliseconds, vol.Range(max=core.TimePeriod(milliseconds=4032)))
}, cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION)))),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
@@ -53,32 +60,35 @@ def to_code(config):
config[CONF_GAS_RESISTANCE][CONF_NAME],
config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeBME680Sensor', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
bme680 = make.Pbme680
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
add(bme680.set_temperature_oversampling(RawExpression(constant)))
add(bme680.set_temperature_oversampling(constant))
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
add(bme680.set_pressure_oversampling(RawExpression(constant)))
add(bme680.set_pressure_oversampling(constant))
if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
add(bme680.set_humidity_oversampling(RawExpression(constant)))
add(bme680.set_humidity_oversampling(constant))
if CONF_IIR_FILTER in config:
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
add(bme680.set_iir_filter(RawExpression(constant)))
add(bme680.set_iir_filter(constant))
if CONF_HEATER in config:
conf = config[CONF_HEATER]
if not conf:
add(bme680.set_heater(0, 0))
else:
add(bme680.set_heater(conf[CONF_TEMPERATURE], conf[CONF_DURATION]))
sensor.setup_sensor(bme680.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(bme680.Pget_pressure_sensor(), config[CONF_PRESSURE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_pressure, config[CONF_PRESSURE])
sensor.setup_sensor(bme680.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(make.Pmqtt_humidity, config[CONF_HUMIDITY])
sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), config[CONF_GAS_RESISTANCE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_gas_resistance, config[CONF_GAS_RESISTANCE])
sensor.setup_sensor(bme680.Pget_temperature_sensor(), make.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(bme680.Pget_pressure_sensor(), make.Pmqtt_pressure,
config[CONF_PRESSURE])
sensor.setup_sensor(bme680.Pget_humidity_sensor(), make.Pmqtt_humidity,
config[CONF_HUMIDITY])
sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), make.Pmqtt_gas_resistance,
config[CONF_GAS_RESISTANCE])
BUILD_FLAGS = '-DUSE_BME680'

View File

@@ -2,19 +2,20 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, \
CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, HexIntLiteral, add, variable
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, HexIntLiteral, add, variable
DEPENDENCIES = ['i2c']
MakeBMP085Sensor = Application.MakeBMP085Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('bmp085_sensor'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_PRESSURE): MQTT_SENSOR_SCHEMA,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP085Sensor),
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
@@ -22,13 +23,14 @@ def to_code(config):
rhs = App.make_bmp085_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_PRESSURE][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
bmp = variable('Application::MakeBMP085Sensor', config[CONF_ID], rhs)
bmp = variable(config[CONF_MAKE_ID], rhs)
if CONF_ADDRESS in config:
add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS])))
sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), config[CONF_PRESSURE])
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE])
sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), bmp.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), bmp.Pmqtt_pressure,
config[CONF_PRESSURE])
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'

View File

@@ -0,0 +1,67 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_IIR_FILTER, CONF_MAKE_ID, \
CONF_NAME, CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, add, variable
DEPENDENCIES = ['i2c']
OVERSAMPLING_OPTIONS = {
'NONE': sensor.sensor_ns.BMP280_OVERSAMPLING_NONE,
'1X': sensor.sensor_ns.BMP280_OVERSAMPLING_1X,
'2X': sensor.sensor_ns.BMP280_OVERSAMPLING_2X,
'4X': sensor.sensor_ns.BMP280_OVERSAMPLING_4X,
'8X': sensor.sensor_ns.BMP280_OVERSAMPLING_8X,
'16X': sensor.sensor_ns.BMP280_OVERSAMPLING_16X,
}
IIR_FILTER_OPTIONS = {
'OFF': sensor.sensor_ns.BMP280_IIR_FILTER_OFF,
'2X': sensor.sensor_ns.BMP280_IIR_FILTER_2X,
'4X': sensor.sensor_ns.BMP280_IIR_FILTER_4X,
'8X': sensor.sensor_ns.BMP280_IIR_FILTER_8X,
'16X': sensor.sensor_ns.BMP280_IIR_FILTER_16X,
}
BMP280_OVERSAMPLING_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, cv.one_of(*OVERSAMPLING_OPTIONS)),
})
MakeBMP280Sensor = Application.MakeBMP280Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeBMP280Sensor),
vol.Optional(CONF_ADDRESS, default=0x77): cv.i2c_address,
vol.Required(CONF_TEMPERATURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA),
vol.Required(CONF_PRESSURE): cv.nameable(BMP280_OVERSAMPLING_SENSOR_SCHEMA),
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, cv.one_of(*IIR_FILTER_OPTIONS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
def to_code(config):
rhs = App.make_bmp280_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_PRESSURE][CONF_NAME],
config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
bmp280 = make.Pbmp280
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
add(bmp280.set_temperature_oversampling(constant))
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
add(bmp280.set_pressure_oversampling(constant))
if CONF_IIR_FILTER in config:
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
add(bmp280.set_iir_filter(constant))
sensor.setup_sensor(bmp280.Pget_temperature_sensor(), make.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(bmp280.Pget_pressure_sensor(), make.Pmqtt_pressure,
config[CONF_PRESSURE])
BUILD_FLAGS = '-DUSE_BMP280'

View 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'

View File

@@ -2,36 +2,30 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS
from esphomeyaml.components.dallas import DallasComponent
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \
CONF_RESOLUTION, \
CONF_UPDATE_INTERVAL, CONF_ID
from esphomeyaml.helpers import HexIntLiteral, get_variable, Pvariable
CONF_RESOLUTION
from esphomeyaml.helpers import HexIntLiteral, get_variable
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dallas_sensor'): cv.register_variable_id,
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int,
vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int,
vol.Optional(CONF_DALLAS_ID): cv.variable_id,
vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=8, max=12)),
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
cv.GenerateID(CONF_DALLAS_ID): cv.use_variable_id(DallasComponent),
vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=9, max=12)),
}), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
def to_code(config):
hub = get_variable(config.get(CONF_DALLAS_ID), DALLAS_COMPONENT_CLASS)
update_interval = config.get(CONF_UPDATE_INTERVAL)
if CONF_RESOLUTION in config and update_interval is None:
update_interval = 10000
hub = None
for hub in get_variable(config[CONF_DALLAS_ID]):
yield
if CONF_ADDRESS in config:
address = HexIntLiteral(config[CONF_ADDRESS])
rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, update_interval,
config.get(CONF_RESOLUTION))
rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, config.get(CONF_RESOLUTION))
else:
rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX],
update_interval, config.get(CONF_RESOLUTION))
sensor_ = Pvariable('sensor::DallasTemperatureSensor', config[CONF_ID], rhs)
sensor.register_sensor(sensor_, config)
config.get(CONF_RESOLUTION))
sensor.register_sensor(rhs, config)
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View File

@@ -2,43 +2,47 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, RawExpression, add, variable, gpio_output_pin_expression
from esphomeyaml.pins import GPIO_OUTPUT_PIN_SCHEMA
from esphomeyaml.helpers import App, Application, add, gpio_output_pin_expression, variable
from esphomeyaml.pins import gpio_output_pin_schema
DHT_MODELS = {
'AUTO_DETECT': 'sensor::DHT_MODEL_AUTO_DETECT',
'DHT11': 'sensor::DHT_MODEL_DHT11',
'DHT22': 'sensor::DHT_MODEL_DHT22',
'AM2302': 'sensor::DHT_MODEL_AM2302',
'RHT03': 'sensor::DHT_MODEL_RHT03',
'AUTO_DETECT': sensor.sensor_ns.DHT_MODEL_AUTO_DETECT,
'DHT11': sensor.sensor_ns.DHT_MODEL_DHT11,
'DHT22': sensor.sensor_ns.DHT_MODEL_DHT22,
'AM2302': sensor.sensor_ns.DHT_MODEL_AM2302,
'RHT03': sensor.sensor_ns.DHT_MODEL_RHT03,
}
MakeDHTSensor = Application.MakeDHTSensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dht_sensor'): cv.register_variable_id,
vol.Required(CONF_PIN): GPIO_OUTPUT_PIN_SCHEMA,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_MODEL): vol.All(vol.Upper, vol.Any(*DHT_MODELS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHTSensor),
vol.Required(CONF_PIN): gpio_output_pin_schema,
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_MODEL): vol.All(vol.Upper, cv.one_of(*DHT_MODELS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
def to_code(config):
pin = gpio_output_pin_expression(config[CONF_PIN])
pin = None
for pin in gpio_output_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
pin, config.get(CONF_UPDATE_INTERVAL))
dht = variable('Application::MakeDHTSensor', config[CONF_ID], rhs)
dht = variable(config[CONF_MAKE_ID], rhs)
if CONF_MODEL in config:
constant = DHT_MODELS[config[CONF_MODEL]]
add(dht.Pdht.set_dht_model(RawExpression(constant)))
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
add(dht.Pdht.set_dht_model(constant))
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(),
dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(),
dht.Pmqtt_humidity, config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_DHT_SENSOR'

View File

@@ -2,18 +2,19 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, variable
from esphomeyaml.helpers import App, Application, variable
DEPENDENCIES = ['i2c']
MakeDHT12Sensor = Application.MakeDHT12Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dht_sensor'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDHT12Sensor),
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
@@ -21,11 +22,12 @@ def to_code(config):
rhs = App.make_dht12_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
dht = variable('Application::MakeDHT12Sensor', config[CONF_ID], rhs)
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
dht = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(dht.Pdht12.Pget_temperature_sensor(), dht.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(dht.Pdht12.Pget_humidity_sensor(), dht.Pmqtt_humidity,
config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_DHT12_SENSOR'

View File

@@ -0,0 +1,28 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, gpio_input_pin_expression, variable
MakeDutyCycleSensor = Application.MakeDutyCycleSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeDutyCycleSensor),
vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
pin = None
for pin in gpio_input_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_duty_cycle_sensor(config[CONF_NAME], pin,
config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(make.Pduty, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_DUTY_CYCLE_SENSOR'

View File

@@ -0,0 +1,24 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, Application, variable
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
MakeESP32HallSensor = Application.MakeESP32HallSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeESP32HallSensor),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_esp32_hall_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(make.Phall, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_ESP32_HALL_SENSOR'

View File

@@ -2,18 +2,19 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, variable
from esphomeyaml.helpers import App, Application, variable
DEPENDENCIES = ['i2c']
MakeHDC1080Sensor = Application.MakeHDC1080Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dht_sensor'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHDC1080Sensor),
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
@@ -21,11 +22,13 @@ def to_code(config):
rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
hdc1080 = variable('Application::MakeHDC1080Sensor', config[CONF_ID], rhs)
sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY])
hdc1080 = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(),
hdc1080.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), hdc1080.Pmqtt_humidity,
config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'

View File

@@ -0,0 +1,57 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_CF1_PIN, CONF_CF_PIN, CONF_CHANGE_MODE_EVERY, CONF_CURRENT, \
CONF_CURRENT_RESISTOR, CONF_ID, CONF_NAME, CONF_POWER, CONF_SEL_PIN, CONF_UPDATE_INTERVAL, \
CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression
HLW8012Component = sensor.sensor_ns.HLW8012Component
HLW8012VoltageSensor = sensor.sensor_ns.HLW8012VoltageSensor
HLW8012CurrentSensor = sensor.sensor_ns.HLW8012CurrentSensor
HLW8012PowerSensor = sensor.sensor_ns.HLW8012PowerSensor
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(HLW8012Component),
vol.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_CF_PIN): pins.input_pin,
vol.Required(CONF_CF1_PIN): pins.input_pin,
vol.Optional(CONF_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_CURRENT_RESISTOR): cv.resistance,
vol.Optional(CONF_VOLTAGE_DIVIDER): cv.positive_float,
vol.Optional(CONF_CHANGE_MODE_EVERY): vol.All(cv.uint32_t, vol.Range(min=1)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}), cv.has_at_least_one_key(CONF_VOLTAGE, CONF_CURRENT, CONF_POWER))
def to_code(config):
sel = None
for sel in gpio_output_pin_expression(config[CONF_SEL_PIN]):
yield
rhs = App.make_hlw8012(sel, config[CONF_CF_PIN], config[CONF_CF1_PIN],
config.get(CONF_UPDATE_INTERVAL))
hlw = Pvariable(config[CONF_ID], rhs)
if CONF_VOLTAGE in config:
conf = config[CONF_VOLTAGE]
sensor.register_sensor(hlw.make_voltage_sensor(conf[CONF_NAME]), conf)
if CONF_CURRENT in config:
conf = config[CONF_CURRENT]
sensor.register_sensor(hlw.make_current_sensor(conf[CONF_NAME]), conf)
if CONF_POWER in config:
conf = config[CONF_POWER]
sensor.register_sensor(hlw.make_power_sensor(conf[CONF_NAME]), conf)
if CONF_CURRENT_RESISTOR in config:
add(hlw.set_current_resistor(config[CONF_CURRENT_RESISTOR]))
if CONF_CHANGE_MODE_EVERY in config:
add(hlw.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY]))
BUILD_FLAGS = '-DUSE_HLW8012'

View File

@@ -0,0 +1,73 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_RANGE
from esphomeyaml.helpers import App, Pvariable, add
DEPENDENCIES = ['i2c']
CONF_FIELD_STRENGTH_X = 'field_strength_x'
CONF_FIELD_STRENGTH_Y = 'field_strength_y'
CONF_FIELD_STRENGTH_Z = 'field_strength_z'
CONF_HEADING = 'heading'
HMC5883LComponent = sensor.sensor_ns.HMC5883LComponent
HMC5883LFieldStrengthSensor = sensor.sensor_ns.HMC5883LFieldStrengthSensor
HMC5883LHeadingSensor = sensor.sensor_ns.HMC5883LHeadingSensor
HMC5883L_RANGES = {
88: sensor.sensor_ns.HMC5883L_RANGE_88_UT,
130: sensor.sensor_ns.HMC5883L_RANGE_130_UT,
190: sensor.sensor_ns.HMC5883L_RANGE_190_UT,
250: sensor.sensor_ns.HMC5883L_RANGE_250_UT,
400: sensor.sensor_ns.HMC5883L_RANGE_400_UT,
470: sensor.sensor_ns.HMC5883L_RANGE_470_UT,
560: sensor.sensor_ns.HMC5883L_RANGE_560_UT,
810: sensor.sensor_ns.HMC5883L_RANGE_810_UT,
}
def validate_range(value):
value = cv.string(value)
if value.endswith(u'µT') or value.endswith('uT'):
value = value[:-2]
return cv.one_of(*HMC5883L_RANGES)(int(value))
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(HMC5883LComponent),
vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_FIELD_STRENGTH_X): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_HEADING): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
vol.Optional(CONF_RANGE): validate_range,
}), cv.has_at_least_one_key(CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Z,
CONF_HEADING))
def to_code(config):
rhs = App.make_hmc5883l(config.get(CONF_UPDATE_INTERVAL))
hmc = Pvariable(config[CONF_ID], rhs)
if CONF_ADDRESS in config:
add(hmc.set_address(config[CONF_ADDRESS]))
if CONF_RANGE in config:
add(hmc.set_range(HMC5883L_RANGES[config[CONF_RANGE]]))
if CONF_FIELD_STRENGTH_X in config:
conf = config[CONF_FIELD_STRENGTH_X]
sensor.register_sensor(hmc.Pmake_x_sensor(conf[CONF_NAME]), conf)
if CONF_FIELD_STRENGTH_Y in config:
conf = config[CONF_FIELD_STRENGTH_Y]
sensor.register_sensor(hmc.Pmake_y_sensor(conf[CONF_NAME]), conf)
if CONF_FIELD_STRENGTH_Z in config:
conf = config[CONF_FIELD_STRENGTH_Z]
sensor.register_sensor(hmc.Pmake_z_sensor(conf[CONF_NAME]), conf)
if CONF_HEADING in config:
conf = config[CONF_HEADING]
sensor.register_sensor(hmc.Pmake_heading_sensor(conf[CONF_NAME]), conf)
BUILD_FLAGS = '-DUSE_HMC5883L'

View File

@@ -2,18 +2,19 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
from esphomeyaml.const import CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, variable
from esphomeyaml.helpers import App, Application, variable
DEPENDENCIES = ['i2c']
MakeHTU21DSensor = Application.MakeHTU21DSensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('htu21d'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHTU21DSensor),
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
@@ -21,11 +22,11 @@ def to_code(config):
rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
htu21d = variable('Application::MakeHTU21DSensor', config[CONF_ID], rhs)
sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY])
htu21d = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), htu21d.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), htu21d.Pmqtt_humidity,
config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'

View File

@@ -0,0 +1,46 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_GAIN, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_CLK_PIN
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable
MakeHX711Sensor = Application.MakeHX711Sensor
CONF_DOUT_PIN = 'dout_pin'
GAINS = {
128: sensor.sensor_ns.HX711_GAIN_128,
32: sensor.sensor_ns.HX711_GAIN_32,
64: sensor.sensor_ns.HX711_GAIN_64,
}
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeHX711Sensor),
vol.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema,
vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_GAIN): vol.All(cv.int_, cv.one_of(*GAINS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
dout_pin = None
for dout_pin in gpio_input_pin_expression(config[CONF_DOUT_PIN]):
yield
sck_pin = None
for sck_pin in gpio_input_pin_expression(config[CONF_CLK_PIN]):
yield
rhs = App.make_hx711_sensor(config[CONF_NAME], dout_pin, sck_pin,
config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
if CONF_GAIN in config:
add(make.Phx711.set_gain(GAINS[config[CONF_GAIN]]))
sensor.setup_sensor(make.Phx711, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_HX711'

View File

@@ -0,0 +1,53 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, \
CONF_MAX_VOLTAGE, CONF_NAME, CONF_POWER, CONF_UPDATE_INTERVAL, CONF_BUS_VOLTAGE, \
CONF_SHUNT_VOLTAGE, CONF_SHUNT_RESISTANCE
from esphomeyaml.helpers import App, Pvariable
DEPENDENCIES = ['i2c']
INA219Component = sensor.sensor_ns.INA219Component
INA219VoltageSensor = sensor.sensor_ns.INA219VoltageSensor
INA219CurrentSensor = sensor.sensor_ns.INA219CurrentSensor
INA219PowerSensor = sensor.sensor_ns.INA219PowerSensor
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(INA219Component),
vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address,
vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance,
vol.Range(min=0.0, max=32.0)),
vol.Optional(CONF_MAX_VOLTAGE, default=32.0): vol.All(cv.voltage, vol.Range(min=0.0, max=32.0)),
vol.Optional(CONF_MAX_CURRENT, default=3.2): vol.All(cv.current, vol.Range(min=0.0)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT,
CONF_POWER))
def to_code(config):
rhs = App.make_ina219(config[CONF_SHUNT_RESISTANCE],
config[CONF_MAX_CURRENT], config[CONF_MAX_VOLTAGE],
config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
ina = Pvariable(config[CONF_ID], rhs)
if CONF_BUS_VOLTAGE in config:
conf = config[CONF_BUS_VOLTAGE]
sensor.register_sensor(ina.Pmake_bus_voltage_sensor(conf[CONF_NAME]), conf)
if CONF_SHUNT_VOLTAGE in config:
conf = config[CONF_SHUNT_VOLTAGE]
sensor.register_sensor(ina.Pmake_shunt_voltage_sensor(conf[CONF_NAME]), conf)
if CONF_CURRENT in config:
conf = config[CONF_CURRENT]
sensor.register_sensor(ina.Pmake_current_sensor(conf[CONF_NAME]), conf)
if CONF_POWER in config:
conf = config[CONF_POWER]
sensor.register_sensor(ina.Pmake_power_sensor(conf[CONF_NAME]), conf)
BUILD_FLAGS = '-DUSE_INA219'

View File

@@ -0,0 +1,64 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_NAME, \
CONF_POWER, CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable, add
DEPENDENCIES = ['i2c']
CONF_CHANNEL_1 = 'channel_1'
CONF_CHANNEL_2 = 'channel_2'
CONF_CHANNEL_3 = 'channel_3'
INA3221Component = sensor.sensor_ns.INA3221Component
INA3221VoltageSensor = sensor.sensor_ns.INA3221VoltageSensor
INA3221CurrentSensor = sensor.sensor_ns.INA3221CurrentSensor
INA3221PowerSensor = sensor.sensor_ns.INA3221PowerSensor
INA3221_CHANNEL_SCHEMA = vol.All(vol.Schema({
vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_CURRENT): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_POWER): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_SHUNT_RESISTANCE, default=0.1): vol.All(cv.resistance,
vol.Range(min=0.0, max=32.0)),
}), cv.has_at_least_one_key(CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT,
CONF_POWER))
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(INA3221Component),
vol.Optional(CONF_ADDRESS, default=0x40): cv.i2c_address,
vol.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA,
vol.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA,
vol.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
def to_code(config):
rhs = App.make_ina3221(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
ina = Pvariable(config[CONF_ID], rhs)
for i, channel in enumerate([CONF_CHANNEL_1, CONF_CHANNEL_2, CONF_CHANNEL_3]):
if channel not in config:
continue
conf = config[channel]
if CONF_SHUNT_RESISTANCE in conf:
add(ina.set_shunt_resistance(i, conf[CONF_SHUNT_RESISTANCE]))
if CONF_BUS_VOLTAGE in conf:
c = conf[CONF_BUS_VOLTAGE]
sensor.register_sensor(ina.Pmake_bus_voltage_sensor(i, c[CONF_NAME]), c)
if CONF_SHUNT_VOLTAGE in conf:
c = conf[CONF_SHUNT_VOLTAGE]
sensor.register_sensor(ina.Pmake_shunt_voltage_sensor(i, c[CONF_NAME]), c)
if CONF_CURRENT in conf:
c = conf[CONF_CURRENT]
sensor.register_sensor(ina.Pmake_current_sensor(i, c[CONF_NAME]), c)
if CONF_POWER in conf:
c = conf[CONF_POWER]
sensor.register_sensor(ina.Pmake_power_sensor(i, c[CONF_NAME]), c)
BUILD_FLAGS = '-DUSE_INA3221'

View File

@@ -0,0 +1,34 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.components.spi import SPIComponent
from esphomeyaml.const import CONF_CS_PIN, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, get_variable, gpio_output_pin_expression, variable
MakeMAX6675Sensor = Application.MakeMAX6675Sensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor),
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
spi = None
for spi in get_variable(config[CONF_SPI_ID]):
yield
cs = None
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
yield
rhs = App.make_max6675_sensor(config[CONF_NAME], spi, cs,
config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(make.Pmax6675, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_MAX6675_SENSOR'

View File

@@ -0,0 +1,38 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.uart import UARTComponent
from esphomeyaml.const import CONF_CO2, CONF_MAKE_ID, CONF_NAME, CONF_TEMPERATURE, CONF_UART_ID, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, get_variable, variable
DEPENDENCIES = ['uart']
MakeMHZ19Sensor = Application.MakeMHZ19Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMHZ19Sensor),
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
vol.Required(CONF_CO2): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
def to_code(config):
uart = None
for uart in get_variable(config[CONF_UART_ID]):
yield
rhs = App.make_mhz19_sensor(uart, config[CONF_CO2][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
mhz19 = make.Pmhz19
sensor.setup_sensor(mhz19.Pget_co2_sensor(), make.Pmqtt, config[CONF_CO2])
if CONF_TEMPERATURE in config:
sensor.register_sensor(mhz19.Pmake_temperature_sensor(config[CONF_TEMPERATURE][CONF_NAME]),
config[CONF_TEMPERATURE])
BUILD_FLAGS = '-DUSE_MHZ19'

View File

@@ -2,8 +2,7 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_ID_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_MQTT_ID, CONF_NAME, CONF_TEMPERATURE, \
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable
@@ -16,58 +15,57 @@ CONF_GYRO_X = 'gyro_x'
CONF_GYRO_Y = 'gyro_y'
CONF_GYRO_Z = 'gyro_z'
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('mpu6050'): cv.register_variable_id,
MPU6050Component = sensor.sensor_ns.MPU6050Component
MPU6050AccelSensor = sensor.sensor_ns.MPU6050AccelSensor
MPU6050GyroSensor = sensor.sensor_ns.MPU6050GyroSensor
MPU6050TemperatureSensor = sensor.sensor_ns.MPU6050TemperatureSensor
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(MPU6050Component),
vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address,
vol.Optional(CONF_ACCEL_X): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_ACCEL_Y): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_ACCEL_Z): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_X): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_Y): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_Z): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_TEMPERATURE): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
})
vol.Optional(CONF_ACCEL_X): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_ACCEL_Y): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_ACCEL_Z): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_GYRO_X): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_GYRO_Y): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_GYRO_Z): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}), cv.has_at_least_one_key(CONF_ACCEL_X, CONF_ACCEL_Y, CONF_ACCEL_Z,
CONF_GYRO_X, CONF_GYRO_Y, CONF_GYRO_Z))
def to_code(config):
rhs = App.make_mpu6050_sensor(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
mpu = Pvariable('sensor::MPU6050Component', config[CONF_ID], rhs)
mpu = Pvariable(config[CONF_ID], rhs)
if CONF_ACCEL_X in config:
conf = config[CONF_ACCEL_X]
rhs = mpu.Pmake_accel_x_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
if CONF_ACCEL_Y in config:
conf = config[CONF_ACCEL_Y]
rhs = mpu.Pmake_accel_y_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
if CONF_ACCEL_Z in config:
conf = config[CONF_ACCEL_Z]
rhs = mpu.Pmake_accel_z_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
if CONF_GYRO_X in config:
conf = config[CONF_GYRO_X]
rhs = mpu.Pmake_gyro_x_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
if CONF_GYRO_Y in config:
conf = config[CONF_GYRO_Y]
rhs = mpu.Pmake_gyro_y_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
if CONF_GYRO_Z in config:
conf = config[CONF_GYRO_Z]
rhs = mpu.Pmake_gyro_z_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE]
rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050TemperatureSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
sensor.register_sensor(rhs, conf)
BUILD_FLAGS = '-DUSE_MPU6050'

View 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'

View File

@@ -0,0 +1,37 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_MAKE_ID, CONF_NAME, CONF_PRESSURE, \
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, add, variable
DEPENDENCIES = ['i2c']
MakeMS5611Sensor = Application.MakeMS5611Sensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMS5611Sensor),
vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_PRESSURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
})
def to_code(config):
rhs = App.make_ms5611_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_PRESSURE][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
if CONF_ADDRESS in config:
add(make.Pms5611.set_address(config[CONF_ADDRESS]))
sensor.setup_sensor(make.Pms5611.Pget_temperature_sensor(), make.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(make.Pms5611.Pget_pressure_sensor(), make.Pmqtt_pressure,
config[CONF_PRESSURE])
BUILD_FLAGS = '-DUSE_MS5611'

View 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'

View File

@@ -1,62 +1,67 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml import core, pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_ID, CONF_INTERNAL_FILTER, \
CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
from esphomeyaml.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_INTERNAL_FILTER, \
CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_PULL_MODE, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, \
ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, RawExpression, add, variable
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
GPIO_PULL_MODES = {
'PULLUP': 'GPIO_PULLUP_ONLY',
'PULLDOWN': 'GPIO_PULLDOWN_ONLY',
'PULLUP_PULLDOWN': 'GPIO_PULLUP_PULLDOWN',
'FLOATING': 'GPIO_FLOATING',
}
GPIO_PULL_MODE_SCHEMA = vol.All(vol.Upper, vol.Any(*list(GPIO_PULL_MODES.keys())))
from esphomeyaml.helpers import App, Application, add, variable, gpio_input_pin_expression
COUNT_MODES = {
'DISABLE': 'PCNT_COUNT_DIS',
'INCREMENT': 'PCNT_COUNT_INC',
'DECREMENT': 'PCNT_COUNT_DEC',
'DISABLE': sensor.sensor_ns.PULSE_COUNTER_DISABLE,
'INCREMENT': sensor.sensor_ns.PULSE_COUNTER_INCREMENT,
'DECREMENT': sensor.sensor_ns.PULSE_COUNTER_DECREMENT,
}
COUNT_MODE_SCHEMA = vol.All(vol.Upper, vol.Any(*list(COUNT_MODES.keys())))
COUNT_MODE_SCHEMA = vol.All(vol.Upper, cv.one_of(*COUNT_MODES))
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('pulse_counter'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.input_pin,
vol.Optional(CONF_PULL_MODE): GPIO_PULL_MODE_SCHEMA,
MakePulseCounterSensor = Application.MakePulseCounterSensor
def validate_internal_filter(value):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if isinstance(value, int):
raise vol.Invalid("Please specify the internal filter in microseconds now "
"(since 1.7.0). For example '17ms'")
value = cv.positive_time_period_microseconds(value)
if value.total_microseconds > 13:
raise vol.Invalid("Maximum internal filter value for ESP32 is 13us")
return value
else:
return cv.positive_time_period_microseconds(value)
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakePulseCounterSensor),
vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema,
vol.Optional(CONF_COUNT_MODE): vol.Schema({
vol.Required(CONF_RISING_EDGE): COUNT_MODE_SCHEMA,
vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA,
}),
vol.Optional(CONF_INTERNAL_FILTER): vol.All(vol.Coerce(int), vol.Range(min=0, max=1023)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
vol.Optional(CONF_INTERNAL_FILTER): validate_internal_filter,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
vol.Optional(CONF_PULL_MODE): cv.invalid("The pull_mode option has been removed in 1.7.0, "
"please use the pin mode schema now.")
}))
def to_code(config):
rhs = App.make_pulse_counter_sensor(config[CONF_NAME], config[CONF_PIN],
pin = None
for pin in gpio_input_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_pulse_counter_sensor(config[CONF_NAME], pin,
config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakePulseCounterSensor', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
pcnt = make.Ppcnt
if CONF_PULL_MODE in config:
pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]]
add(pcnt.set_pull_mode(RawExpression(pull_mode)))
if CONF_COUNT_MODE in config:
count_mode = config[CONF_COUNT_MODE]
rising_edge = COUNT_MODES[count_mode[CONF_RISING_EDGE]]
falling_edge = COUNT_MODES[count_mode[CONF_FALLING_EDGE]]
add(pcnt.set_edge_mode(RawExpression(rising_edge), RawExpression(falling_edge)))
rising_edge = COUNT_MODES[config[CONF_COUNT_MODE][CONF_RISING_EDGE]]
falling_edge = COUNT_MODES[config[CONF_COUNT_MODE][CONF_FALLING_EDGE]]
add(pcnt.set_edge_mode(rising_edge, falling_edge))
if CONF_INTERNAL_FILTER in config:
add(pcnt.set_filter(config[CONF_INTERNAL_FILTER]))
sensor.setup_sensor(pcnt, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
add(pcnt.set_filter_us(config[CONF_INTERNAL_FILTER]))
sensor.setup_sensor(make.Ppcnt, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'

View File

@@ -3,42 +3,49 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_RESOLUTION
from esphomeyaml.helpers import App, RawExpression, add, gpio_input_pin_expression, variable
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_RESOLUTION
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, variable
RESOLUTIONS = {
'1': 'sensor::ROTARY_ENCODER_1_PULSE_PER_CYCLE',
'2': 'sensor::ROTARY_ENCODER_2_PULSES_PER_CYCLE',
'4': 'sensor::ROTARY_ENCODER_4_PULSES_PER_CYCLE',
'1': sensor.sensor_ns.ROTARY_ENCODER_1_PULSE_PER_CYCLE,
'2': sensor.sensor_ns.ROTARY_ENCODER_2_PULSES_PER_CYCLE,
'4': sensor.sensor_ns.ROTARY_ENCODER_4_PULSES_PER_CYCLE,
}
CONF_PIN_A = 'pin_a'
CONF_PIN_B = 'pin_b'
CONF_PIN_RESET = 'pin_reset'
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('rotary_encoder'): cv.register_variable_id,
vol.Required(CONF_PIN_A): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
vol.Required(CONF_PIN_B): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
vol.Optional(CONF_PIN_RESET): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
vol.Optional(CONF_RESOLUTION): vol.All(cv.string, vol.Any(*RESOLUTIONS)),
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
MakeRotaryEncoderSensor = Application.MakeRotaryEncoderSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRotaryEncoderSensor),
vol.Required(CONF_PIN_A): pins.internal_gpio_input_pin_schema,
vol.Required(CONF_PIN_B): pins.internal_gpio_input_pin_schema,
vol.Optional(CONF_PIN_RESET): pins.internal_gpio_input_pin_schema,
vol.Optional(CONF_RESOLUTION): vol.All(cv.string, cv.one_of(*RESOLUTIONS)),
}))
def to_code(config):
pin_a = gpio_input_pin_expression(config[CONF_PIN_A])
pin_b = gpio_input_pin_expression(config[CONF_PIN_B])
pin_a = None
for pin_a in gpio_input_pin_expression(config[CONF_PIN_A]):
yield
pin_b = None
for pin_b in gpio_input_pin_expression(config[CONF_PIN_B]):
yield
rhs = App.make_rotary_encoder_sensor(config[CONF_NAME], pin_a, pin_b)
make = variable('Application::MakeRotaryEncoderSensor', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
encoder = make.Protary_encoder
if CONF_PIN_RESET in config:
pin_i = gpio_input_pin_expression(config[CONF_PIN_RESET])
pin_i = None
for pin_i in gpio_input_pin_expression(config[CONF_PIN_RESET]):
yield
add(encoder.set_reset_pin(pin_i))
if CONF_RESOLUTION in config:
resolution = RESOLUTIONS[config[CONF_RESOLUTION]]
add(encoder.set_resolution(RawExpression(resolution)))
sensor.setup_sensor(encoder, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
add(encoder.set_resolution(resolution))
sensor.setup_sensor(encoder, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_ROTARY_ENCODER_SENSOR'

View File

@@ -2,44 +2,37 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL, CONF_ADDRESS, CONF_ACCURACY
from esphomeyaml.helpers import App, variable, RawExpression, add
from esphomeyaml.const import CONF_ACCURACY, CONF_ADDRESS, CONF_HUMIDITY, CONF_MAKE_ID, CONF_NAME, \
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, variable
DEPENDENCIES = ['i2c']
SHT_ACCURACIES = {
'LOW': 'sensor::SHT3XD_ACCURACY_LOW',
'MEDIUM': 'sensor::SHT3XD_ACCURACY_MEDIUM',
'HIGH': 'sensor::SHT3XD_ACCURACY_HIGH',
}
MakeSHT3XDSensor = Application.MakeSHT3XDSensor
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('sht3xd'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeSHT3XDSensor),
vol.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_ADDRESS, default=0x44): cv.i2c_address,
vol.Optional(CONF_ACCURACY): vol.All(vol.Upper, vol.Any(*SHT_ACCURACIES)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
vol.Optional(CONF_ACCURACY): cv.invalid("The accuracy option has been removed and now "
"defaults to HIGH."),
})
def to_code(config):
rhs = App.make_sht3xd_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
sht3xd = variable('Application::MakeSHT3XDSensor', config[CONF_ID], rhs)
sht3xd = variable(config[CONF_MAKE_ID], rhs)
if CONF_ACCURACY in config:
constant = RawExpression(SHT_ACCURACIES[config[CONF_ACCURACY]])
add(sht3xd.Psht3xd.set_accuracy(constant))
sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(sht3xd.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(sht3xd.PPsht3xd.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(sht3xd.Pmqtt_humidity, config[CONF_HUMIDITY])
sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), sht3xd.Pmqtt_temperature,
config[CONF_TEMPERATURE])
sensor.setup_sensor(sht3xd.Psht3xd.Pget_humidity_sensor(), sht3xd.Pmqtt_humidity,
config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_SHT3XD'

View File

@@ -0,0 +1,80 @@
# coding=utf-8
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, \
CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, CONF_NAME, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable, add
DEPENDENCIES = ['i2c']
CONF_RED_CHANNEL = 'red_channel'
CONF_GREEN_CHANNEL = 'green_channel'
CONF_BLUE_CHANNEL = 'blue_channel'
CONF_CLEAR_CHANNEL = 'clear_channel'
TCS34725Component = sensor.sensor_ns.TCS34725Component
TCS34725_INTEGRATION_TIMES = {
'2.4ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_2_4MS,
'24ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_24MS,
'50ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_50MS,
'101ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_101MS,
'154ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_154MS,
'700ms': sensor.sensor_ns.TCS34725_INTEGRATION_TIME_700MS,
}
TCS34725_GAINS = {
'1X': sensor.sensor_ns.TCS34725_GAIN_1X,
'4X': sensor.sensor_ns.TCS34725_GAIN_4X,
'16X': sensor.sensor_ns.TCS34725_GAIN_16X,
'60X': sensor.sensor_ns.TCS34725_GAIN_60X,
}
PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TCS34725Component),
vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_RED_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_GREEN_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_BLUE_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_CLEAR_CHANNEL): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_INTEGRATION_TIME): cv.one_of(*TCS34725_INTEGRATION_TIMES),
vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*TCS34725_GAINS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}), cv.has_at_least_one_key(CONF_RED_CHANNEL, CONF_GREEN_CHANNEL, CONF_BLUE_CHANNEL,
CONF_CLEAR_CHANNEL, CONF_ILLUMINANCE, CONF_COLOR_TEMPERATURE))
def to_code(config):
rhs = App.make_tcs34725(config.get(CONF_UPDATE_INTERVAL))
tcs = Pvariable(config[CONF_ID], rhs)
if CONF_ADDRESS in config:
add(tcs.set_address(config[CONF_ADDRESS]))
if CONF_INTEGRATION_TIME in config:
add(tcs.set_integration_time(TCS34725_INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]))
if CONF_GAIN in config:
add(tcs.set_gain(TCS34725_GAINS[config[CONF_GAIN]]))
if CONF_RED_CHANNEL in config:
conf = config[CONF_RED_CHANNEL]
sensor.register_sensor(tcs.Pmake_red_sensor(conf[CONF_NAME]), conf)
if CONF_GREEN_CHANNEL in config:
conf = config[CONF_GREEN_CHANNEL]
sensor.register_sensor(tcs.Pmake_green_sensor(conf[CONF_NAME]), conf)
if CONF_BLUE_CHANNEL in config:
conf = config[CONF_BLUE_CHANNEL]
sensor.register_sensor(tcs.Pmake_blue_sensor(conf[CONF_NAME]), conf)
if CONF_CLEAR_CHANNEL in config:
conf = config[CONF_CLEAR_CHANNEL]
sensor.register_sensor(tcs.Pmake_clear_sensor(conf[CONF_NAME]), conf)
if CONF_ILLUMINANCE in config:
conf = config[CONF_ILLUMINANCE]
sensor.register_sensor(tcs.Pmake_illuminance_sensor(conf[CONF_NAME]), conf)
if CONF_COLOR_TEMPERATURE in config:
conf = config[CONF_COLOR_TEMPERATURE]
sensor.register_sensor(tcs.Pmake_color_temperature_sensor(conf[CONF_NAME]), conf)
BUILD_FLAGS = '-DUSE_TCS34725'

View File

@@ -0,0 +1,29 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, process_lambda, variable, Application, float_, optional, add
MakeTemplateSensor = Application.MakeTemplateSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSensor),
vol.Required(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_template_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(make.Ptemplate_, make.Pmqtt, config)
template_ = None
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(float_)):
yield
add(make.Ptemplate_.set_template(template_))
BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR'

View File

@@ -2,20 +2,20 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, CONF_NAME, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, RawExpression, add, variable
from esphomeyaml.const import CONF_ADDRESS, CONF_GAIN, CONF_INTEGRATION_TIME, CONF_MAKE_ID, \
CONF_NAME, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, add, variable
DEPENDENCIES = ['i2c']
INTEGRATION_TIMES = {
14: 'sensor::TSL2561_INTEGRATION_14MS',
101: 'sensor::TSL2561_INTEGRATION_101MS',
402: 'sensor::TSL2561_INTEGRATION_402MS',
14: sensor.sensor_ns.TSL2561_INTEGRATION_14MS,
101: sensor.sensor_ns.TSL2561_INTEGRATION_101MS,
402: sensor.sensor_ns.TSL2561_INTEGRATION_402MS,
}
GAINS = {
'1X': 'sensor::TSL2561_GAIN_1X',
'16X': 'sensor::TSL2561_GAIN_16X',
'1X': sensor.sensor_ns.TSL2561_GAIN_1X,
'16X': sensor.sensor_ns.TSL2561_GAIN_16X,
}
CONF_IS_CS_PACKAGE = 'is_cs_package'
@@ -28,31 +28,30 @@ def validate_integration_time(value):
return value
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('tsl2561_sensor'): cv.register_variable_id,
MakeTSL2561Sensor = Application.MakeTSL2561Sensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTSL2561Sensor),
vol.Optional(CONF_ADDRESS, default=0x39): cv.i2c_address,
vol.Optional(CONF_INTEGRATION_TIME): validate_integration_time,
vol.Optional(CONF_GAIN): vol.All(vol.Upper, vol.Any(*GAINS)),
vol.Optional(CONF_GAIN): vol.All(vol.Upper, cv.one_of(*GAINS)),
vol.Optional(CONF_IS_CS_PACKAGE): cv.boolean,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_tsl2561_sensor(config[CONF_NAME], config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
make_tsl = variable('Application::MakeTSL2561Sensor', config[CONF_ID], rhs)
make_tsl = variable(config[CONF_MAKE_ID], rhs)
tsl2561 = make_tsl.Ptsl2561
if CONF_INTEGRATION_TIME in config:
constant = INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]
add(tsl2561.set_integration_time(RawExpression(constant)))
add(tsl2561.set_integration_time(INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]]))
if CONF_GAIN in config:
constant = GAINS[config[CONF_GAIN]]
add(tsl2561.set_gain(RawExpression(constant)))
add(tsl2561.set_gain(GAINS[config[CONF_GAIN]]))
if CONF_IS_CS_PACKAGE in config:
add(tsl2561.set_is_cs_package(config[CONF_IS_CS_PACKAGE]))
sensor.setup_sensor(tsl2561, config)
sensor.setup_mqtt_sensor_component(make_tsl.Pmqtt, config)
sensor.setup_sensor(tsl2561, make_tsl.Pmqtt, config)
BUILD_FLAGS = '-DUSE_TSL2561'

View File

@@ -3,34 +3,39 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ECHO_PIN, CONF_ID, CONF_NAME, \
CONF_TIMEOUT_METER, CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, add, variable, gpio_output_pin_expression, \
gpio_input_pin_expression
from esphomeyaml.const import CONF_ECHO_PIN, CONF_MAKE_ID, CONF_NAME, CONF_TIMEOUT_METER, \
CONF_TIMEOUT_TIME, CONF_TRIGGER_PIN, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, add, gpio_input_pin_expression, \
gpio_output_pin_expression, variable
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('ultrasonic'): cv.register_variable_id,
vol.Required(CONF_TRIGGER_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
vol.Required(CONF_ECHO_PIN): pins.GPIO_INTERNAL_INPUT_PIN_SCHEMA,
MakeUltrasonicSensor = Application.MakeUltrasonicSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUltrasonicSensor),
vol.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema,
vol.Exclusive(CONF_TIMEOUT_METER, 'timeout'): cv.positive_float,
vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_time_period_microseconds,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
trigger = gpio_output_pin_expression(config[CONF_TRIGGER_PIN])
echo = gpio_input_pin_expression(config[CONF_ECHO_PIN])
trigger = None
for trigger in gpio_output_pin_expression(config[CONF_TRIGGER_PIN]):
yield
echo = None
for echo in gpio_input_pin_expression(config[CONF_ECHO_PIN]):
yield
rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo,
config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs)
make = variable(config[CONF_MAKE_ID], rhs)
ultrasonic = make.Pultrasonic
if CONF_TIMEOUT_TIME in config:
add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME]))
elif CONF_TIMEOUT_METER in config:
add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER]))
sensor.setup_sensor(ultrasonic, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
sensor.setup_sensor(ultrasonic, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'

View File

@@ -0,0 +1,22 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, variable
MakeUptimeSensor = Application.MakeUptimeSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUptimeSensor),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_uptime_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(make.Puptime, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_UPTIME_SENSOR'

View File

@@ -0,0 +1,22 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Application, variable
MakeWiFiSignalSensor = Application.MakeWiFiSignalSensor
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeWiFiSignalSensor),
vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval,
}))
def to_code(config):
rhs = App.make_wifi_signal_sensor(config[CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
make = variable(config[CONF_MAKE_ID], rhs)
sensor.setup_sensor(make.Pwifi, make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_WIFI_SIGNAL_SENSOR'

View File

@@ -0,0 +1,47 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
make_address_array
from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_CONDUCTIVITY, CONF_ILLUMINANCE, \
CONF_MAC_ADDRESS, CONF_MAKE_ID, CONF_MOISTURE, CONF_NAME, CONF_TEMPERATURE
from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable
DEPENDENCIES = ['esp32_ble_tracker']
XiaomiDevice = esphomelib_ns.XiaomiDevice
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiDevice),
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker),
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_MOISTURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_CONDUCTIVITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA),
})
def to_code(config):
hub = None
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
yield
rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS]))
dev = Pvariable(config[CONF_MAKE_ID], rhs)
if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE]
sensor.register_sensor(dev.Pmake_temperature_sensor(conf[CONF_NAME]), conf)
if CONF_MOISTURE in config:
conf = config[CONF_MOISTURE]
sensor.register_sensor(dev.Pmake_moisture_sensor(conf[CONF_NAME]), conf)
if CONF_ILLUMINANCE in config:
conf = config[CONF_ILLUMINANCE]
sensor.register_sensor(dev.Pmake_illuminance_sensor(conf[CONF_NAME]), conf)
if CONF_CONDUCTIVITY in config:
conf = config[CONF_CONDUCTIVITY]
sensor.register_sensor(dev.Pmake_conductivity_sensor(conf[CONF_NAME]), conf)
if CONF_BATTERY_LEVEL in config:
conf = config[CONF_BATTERY_LEVEL]
sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf)

View File

@@ -0,0 +1,39 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
make_address_array
from esphomeyaml.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_MAKE_ID, \
CONF_NAME, CONF_TEMPERATURE
from esphomeyaml.helpers import Pvariable, esphomelib_ns, get_variable
DEPENDENCIES = ['esp32_ble_tracker']
XiaomiDevice = esphomelib_ns.XiaomiDevice
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(XiaomiDevice),
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker),
vol.Required(CONF_MAC_ADDRESS): cv.mac_address,
vol.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA),
vol.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA),
})
def to_code(config):
hub = None
for hub in get_variable(config[CONF_ESP32_BLE_ID]):
yield
rhs = hub.make_xiaomi_device(make_address_array(config[CONF_MAC_ADDRESS]))
dev = Pvariable(config[CONF_MAKE_ID], rhs)
if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE]
sensor.register_sensor(dev.Pmake_temperature_sensor(conf[CONF_NAME]), conf)
if CONF_HUMIDITY in config:
conf = config[CONF_HUMIDITY]
sensor.register_sensor(dev.Pmake_humidity_sensor(conf[CONF_NAME]), conf)
if CONF_BATTERY_LEVEL in config:
conf = config[CONF_BATTERY_LEVEL]
sensor.register_sensor(dev.Pmake_battery_level_sensor(conf[CONF_NAME]), conf)

View File

@@ -0,0 +1,38 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \
gpio_output_pin_expression, add
SPIComponent = esphomelib_ns.SPIComponent
SPI_SCHEMA = vol.All(vol.Schema({
cv.GenerateID(): cv.declare_variable_id(SPIComponent),
vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema,
vol.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema,
}), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN))
CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
def to_code(config):
for conf in config:
clk = None
for clk in gpio_output_pin_expression(conf[CONF_CLK_PIN]):
yield
rhs = App.init_spi(clk)
spi = Pvariable(conf[CONF_ID], rhs)
if CONF_MISO_PIN in conf:
for miso in gpio_input_pin_expression(conf[CONF_MISO_PIN]):
yield
add(spi.set_miso(miso))
if CONF_MOSI_PIN in conf:
for mosi in gpio_input_pin_expression(conf[CONF_MOSI_PIN]):
yield
add(spi.set_mosi(mosi))
BUILD_FLAGS = '-DUSE_SPI'

View File

@@ -0,0 +1,23 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv, pins
from esphomeyaml.const import CONF_ID, CONF_PIN
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_output_pin_expression
StatusLEDComponent = esphomelib_ns.StatusLEDComponent
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(StatusLEDComponent),
vol.Optional(CONF_PIN): pins.gpio_output_pin_schema,
})
def to_code(config):
pin = None
for pin in gpio_output_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_status_led(pin)
Pvariable(config[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_STATUS_LED'

View File

@@ -1,39 +1,102 @@
import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ICON, CONF_INVERTED, CONF_MQTT_ID
from esphomeyaml.helpers import App, Pvariable, add, setup_mqtt_component
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, CONF_INTERNAL
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, setup_mqtt_component, \
TemplateArguments, get_variable
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
MQTT_SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
switch_ns = esphomelib_ns.namespace('switch_')
Switch = switch_ns.Switch
MQTTSwitchComponent = switch_ns.MQTTSwitchComponent
ToggleAction = switch_ns.ToggleAction
TurnOffAction = switch_ns.TurnOffAction
TurnOnAction = switch_ns.TurnOnAction
SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(Switch),
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTSwitchComponent),
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_INVERTED): cv.boolean,
})
MQTT_SWITCH_ID_SCHEMA = MQTT_SWITCH_SCHEMA.extend({
cv.GenerateID('mqtt_switch', CONF_MQTT_ID): cv.register_variable_id,
})
SWITCH_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(SWITCH_SCHEMA.schema)
def setup_mqtt_switch(obj, config):
setup_mqtt_component(obj, config)
def setup_switch(obj, config):
def setup_switch_core_(switch_var, mqtt_var, config):
if CONF_INTERNAL in config:
add(switch_var.set_internal(config[CONF_INTERNAL]))
if CONF_ICON in config:
add(obj.set_icon(config[CONF_ICON]))
add(switch_var.set_icon(config[CONF_ICON]))
if CONF_INVERTED in config:
add(obj.set_inverted(config[CONF_INVERTED]))
add(switch_var.set_inverted(config[CONF_INVERTED]))
setup_mqtt_component(mqtt_var, config)
def setup_switch(switch_obj, mqtt_obj, config):
switch_var = Pvariable(config[CONF_ID], switch_obj, has_side_effects=False)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
setup_switch_core_(switch_var, mqtt_var, config)
def register_switch(var, config):
setup_switch(var, config)
rhs = App.register_switch(var)
mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_MQTT_ID], rhs)
setup_mqtt_switch(mqtt_switch, config)
switch_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
rhs = App.register_switch(switch_var)
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
setup_switch_core_(switch_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_SWITCH'
CONF_SWITCH_TOGGLE = 'switch.toggle'
SWITCH_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_SWITCH_TOGGLE, SWITCH_TOGGLE_ACTION_SCHEMA)
def switch_toggle_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_toggle_action(template_arg)
type = ToggleAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_SWITCH_TURN_OFF = 'switch.turn_off'
SWITCH_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_SWITCH_TURN_OFF, SWITCH_TURN_OFF_ACTION_SCHEMA)
def switch_turn_off_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_off_action(template_arg)
type = TurnOffAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)
CONF_SWITCH_TURN_ON = 'switch.turn_on'
SWITCH_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(None),
})
@ACTION_REGISTRY.register(CONF_SWITCH_TURN_ON, SWITCH_TURN_ON_ACTION_SCHEMA)
def switch_turn_on_to_code(config, action_id, arg_type):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_turn_on_action(template_arg)
type = TurnOnAction.template(arg_type)
yield Pvariable(action_id, rhs, type=type)

View File

@@ -3,20 +3,29 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN
from esphomeyaml.helpers import App, variable, gpio_output_pin_expression
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN, CONF_POWER_ON_VALUE
from esphomeyaml.helpers import App, Application, gpio_output_pin_expression, variable, add
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('gpio_switch'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
MakeGPIOSwitch = Application.MakeGPIOSwitch
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOSwitch),
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_POWER_ON_VALUE): cv.boolean,
}))
def to_code(config):
rhs = App.make_gpio_switch(config[CONF_NAME], gpio_output_pin_expression(config[CONF_PIN]))
gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs)
switch.setup_switch(gpio.Pswitch_, config)
switch.setup_mqtt_switch(gpio.Pmqtt, config)
pin = None
for pin in gpio_output_pin_expression(config[CONF_PIN]):
yield
rhs = App.make_gpio_switch(config[CONF_NAME], pin)
gpio = variable(config[CONF_MAKE_ID], rhs)
if CONF_POWER_ON_VALUE in config:
add(gpio.Pswitch_.set_power_on_value(config[CONF_POWER_ON_VALUE]))
switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'

View File

@@ -1,104 +0,0 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.components.ir_transmitter import IR_TRANSMITTER_COMPONENT_CLASS
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \
CONF_ID, CONF_IR_TRANSMITTER_ID, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, \
CONF_RAW, CONF_REPEAT, CONF_SONY, CONF_TIMES, CONF_WAIT_TIME, CONF_INVERTED
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import ArrayInitializer, HexIntLiteral, MockObj, Pvariable, get_variable
DEPENDENCIES = ['ir_transmitter']
IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW]
WAIT_TIME_MESSAGE = "The wait_time_us option has been renamed to wait_time in order to decrease " \
"ambiguity. "
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('ir_transmitter_switch'): cv.register_variable_id,
vol.Exclusive(CONF_NEC, 'code'): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
}),
vol.Exclusive(CONF_LG, 'code'): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), vol.Range(min=0, max=32)),
}),
vol.Exclusive(CONF_SONY, 'code'): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), vol.Range(min=0, max=32)),
}),
vol.Exclusive(CONF_PANASONIC, 'code'): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint32_t,
}),
vol.Exclusive(CONF_RAW, 'code'): vol.Schema({
vol.Required(CONF_CARRIER_FREQUENCY): vol.All(cv.frequency, vol.Coerce(int)),
vol.Required(CONF_DATA): [vol.Coerce(int)],
}),
vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
vol.Required(CONF_TIMES): cv.positive_not_null_int,
vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds,
vol.Optional('wait_time_us'): cv.invalid(WAIT_TIME_MESSAGE),
})),
vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id,
vol.Optional(CONF_INVERTED): cv.invalid("IR Transmitters do not support inverted mode!"),
}).extend(switch.MQTT_SWITCH_ID_SCHEMA.schema), cv.has_at_least_one_key(*IR_KEYS))
# pylint: disable=invalid-name
SendData = MockObj('switch_::ir::SendData', '::')
def safe_hex(value):
if value is None:
return None
return HexIntLiteral(value)
def exp_send_data(config):
if CONF_NEC in config:
conf = config[CONF_NEC]
base = SendData.from_nec(safe_hex(conf[CONF_ADDRESS]),
safe_hex(conf[CONF_COMMAND]))
elif CONF_LG in config:
conf = config[CONF_LG]
base = SendData.from_lg(safe_hex(conf[CONF_DATA]), conf.get(CONF_NBITS))
elif CONF_SONY in config:
conf = config[CONF_SONY]
base = SendData.from_sony(safe_hex(conf[CONF_DATA]), conf.get(CONF_NBITS))
elif CONF_PANASONIC in config:
conf = config[CONF_PANASONIC]
base = SendData.from_panasonic(safe_hex(conf[CONF_ADDRESS]),
safe_hex(conf[CONF_COMMAND]))
elif CONF_RAW in config:
conf = config[CONF_RAW]
data = ArrayInitializer(*conf[CONF_DATA])
base = SendData.from_raw(data, conf[CONF_CARRIER_FREQUENCY])
else:
raise ESPHomeYAMLError(u"Unsupported IR mode {}".format(config))
if CONF_REPEAT in config:
if isinstance(config[CONF_REPEAT], int):
times = config[CONF_REPEAT]
wait_us = None
else:
times = config[CONF_REPEAT][CONF_TIMES]
wait_us = config[CONF_REPEAT][CONF_WAIT_TIME]
base = MockObj(unicode(base), u'.')
base = base.repeat(times, wait_us)
return base
def to_code(config):
ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS)
send_data = exp_send_data(config)
rhs = ir.create_transmitter(config[CONF_NAME], send_data)
switch_ = Pvariable(IR_TRANSMITTER_COMPONENT_CLASS + '::DataTransmitter', config[CONF_ID],
rhs)
switch.register_switch(switch_, config)
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'

View File

@@ -2,21 +2,24 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, variable
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, Application, get_variable, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('output_switch'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
MakeOutputSwitch = Application.MakeOutputSwitch
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeOutputSwitch),
vol.Required(CONF_OUTPUT): cv.use_variable_id(None),
}))
def to_code(config):
output = get_variable(config[CONF_OUTPUT])
rhs = App.make_simple_switch(config[CONF_NAME], output)
gpio = variable('Application::MakeSimpleSwitch', config[CONF_ID], rhs)
switch.setup_switch(gpio.Pswitch_, config)
switch.setup_mqtt_switch(gpio.Pmqtt, config)
output = None
for output in get_variable(config[CONF_OUTPUT]):
yield
rhs = App.make_output_switch(config[CONF_NAME], output)
gpio = variable(config[CONF_MAKE_ID], rhs)
switch.setup_switch(gpio.Pswitch_, gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_SIMPLE_SWITCH'
BUILD_FLAGS = '-DUSE_OUTPUT_SWITCH'

View File

@@ -0,0 +1,148 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \
RC_SWITCH_TYPE_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \
RC_SWITCH_TYPE_D_SCHEMA, RemoteTransmitterComponent, binary_code, build_rc_switch_protocol, \
remote_ns
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_CHANNEL, CONF_CODE, \
CONF_COMMAND, CONF_DATA, CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_INVERTED, CONF_LG, \
CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, \
CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, \
CONF_REPEAT, CONF_SAMSUNG, CONF_SONY, CONF_STATE, CONF_TIMES, \
CONF_WAIT_TIME
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, add, get_variable
DEPENDENCIES = ['remote_transmitter']
REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SAMSUNG, CONF_SONY, CONF_PANASONIC, CONF_RAW,
CONF_RC_SWITCH_RAW, CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B,
CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D]
CONF_REMOTE_TRANSMITTER_ID = 'remote_transmitter_id'
CONF_TRANSMITTER_ID = 'transmitter_id'
RemoteTransmitter = remote_ns.RemoteTransmitter
LGTransmitter = remote_ns.LGTransmitter
NECTransmitter = remote_ns.NECTransmitter
PanasonicTransmitter = remote_ns.PanasonicTransmitter
RawTransmitter = remote_ns.RawTransmitter
SamsungTransmitter = remote_ns.SamsungTransmitter
SonyTransmitter = remote_ns.SonyTransmitter
RCSwitchRawTransmitter = remote_ns.RCSwitchRawTransmitter
RCSwitchTypeATransmitter = remote_ns.RCSwitchTypeATransmitter
RCSwitchTypeBTransmitter = remote_ns.RCSwitchTypeBTransmitter
RCSwitchTypeCTransmitter = remote_ns.RCSwitchTypeCTransmitter
RCSwitchTypeDTransmitter = remote_ns.RCSwitchTypeDTransmitter
validate_raw_data = [vol.Any(vol.Coerce(int), cv.time_period_microseconds)]
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_LG): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)),
}),
vol.Optional(CONF_NEC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
}),
vol.Optional(CONF_SAMSUNG): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
}),
vol.Optional(CONF_SONY): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)),
}),
vol.Optional(CONF_PANASONIC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint32_t,
}),
vol.Optional(CONF_RAW): vol.Any(validate_raw_data, vol.Schema({
vol.Required(CONF_DATA): validate_raw_data,
vol.Optional(CONF_CARRIER_FREQUENCY): vol.All(cv.frequency, vol.Coerce(int)),
})),
vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_A): RC_SWITCH_TYPE_A_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA,
vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
vol.Required(CONF_TIMES): cv.positive_not_null_int,
vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds,
})),
cv.GenerateID(CONF_REMOTE_TRANSMITTER_ID): cv.use_variable_id(RemoteTransmitterComponent),
cv.GenerateID(CONF_TRANSMITTER_ID): cv.declare_variable_id(RemoteTransmitter),
vol.Optional(CONF_INVERTED): cv.invalid("Remote Transmitters do not support inverted mode!"),
}), cv.has_exactly_one_key(*REMOTE_KEYS))
def transmitter_base(full_config):
name = full_config[CONF_NAME]
key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS)
if key == CONF_LG:
return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_NEC:
return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_PANASONIC:
return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_SAMSUNG:
return SamsungTransmitter.new(name, config[CONF_DATA])
elif key == CONF_SONY:
return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_RAW:
if isinstance(config, dict):
data = config[CONF_DATA]
carrier_frequency = config.get(CONF_CARRIER_FREQUENCY)
else:
data = config
carrier_frequency = None
return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False),
carrier_frequency)
elif key == CONF_RC_SWITCH_RAW:
return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
elif key == CONF_RC_SWITCH_TYPE_A:
return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_GROUP]),
binary_code(config[CONF_DEVICE]),
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_B:
return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
config[CONF_ADDRESS], config[CONF_CHANNEL],
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_C:
return RCSwitchTypeCTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_FAMILY][0]) - ord('a'),
config[CONF_GROUP], config[CONF_DEVICE],
config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_D:
return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_GROUP][0]) - ord('a'),
config[CONF_DEVICE], config[CONF_STATE])
else:
raise NotImplementedError("Unknown transmitter type {}".format(config))
def to_code(config):
remote = None
for remote in get_variable(config[CONF_REMOTE_TRANSMITTER_ID]):
yield
rhs = App.register_component(transmitter_base(config))
transmitter = Pvariable(config[CONF_TRANSMITTER_ID], rhs)
if CONF_REPEAT in config:
if isinstance(config[CONF_REPEAT], int):
times = config[CONF_REPEAT]
wait_us = 1000
else:
times = config[CONF_REPEAT][CONF_TIMES]
wait_us = config[CONF_REPEAT][CONF_WAIT_TIME]
add(transmitter.set_repeat(times, wait_us))
switch.register_switch(remote.add_transmitter(transmitter), config)
BUILD_FLAGS = '-DUSE_REMOTE_TRANSMITTER'

View File

@@ -2,20 +2,21 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_INVERTED
from esphomeyaml.helpers import App, variable
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('restart_switch'): cv.register_variable_id,
MakeRestartSwitch = Application.MakeRestartSwitch
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeRestartSwitch),
vol.Optional(CONF_INVERTED): cv.invalid("Restart switches do not support inverted mode!"),
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
}))
def to_code(config):
rhs = App.make_restart_switch(config[CONF_NAME])
restart = variable('Application::MakeRestartSwitch', config[CONF_ID], rhs)
switch.setup_switch(restart.Prestart, config)
switch.setup_mqtt_switch(restart.Pmqtt, config)
restart = variable(config[CONF_MAKE_ID], rhs)
switch.setup_switch(restart.Prestart, restart.Pmqtt, config)
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'

View File

@@ -2,21 +2,21 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_INVERTED
from esphomeyaml.helpers import App, variable
from esphomeyaml.const import CONF_INVERTED, CONF_MAKE_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('shutdown_switch'): cv.register_variable_id,
MakeShutdownSwitch = Application.MakeShutdownSwitch
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeShutdownSwitch),
vol.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"),
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
}))
def to_code(config):
rhs = App.make_shutdown_switch(config[CONF_NAME])
shutdown = variable('Application::MakeShutdownSwitch', config[CONF_ID],
rhs)
switch.setup_switch(shutdown.Pshutdown, config)
switch.setup_mqtt_switch(shutdown.Pmqtt, config)
shutdown = variable(config[CONF_MAKE_ID], rhs)
switch.setup_switch(shutdown.Pshutdown, shutdown.Pmqtt, config)
BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH'

View File

@@ -0,0 +1,44 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import automation
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_LAMBDA, CONF_MAKE_ID, CONF_NAME, CONF_TURN_OFF_ACTION, \
CONF_TURN_ON_ACTION, CONF_OPTIMISTIC
from esphomeyaml.helpers import App, Application, process_lambda, variable, NoArg, add, bool_, \
optional
MakeTemplateSwitch = Application.MakeTemplateSwitch
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeTemplateSwitch),
vol.Optional(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True),
vol.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True),
}), cv.has_at_least_one_key(CONF_LAMBDA, CONF_OPTIMISTIC))
def to_code(config):
rhs = App.make_template_switch(config[CONF_NAME])
make = variable(config[CONF_MAKE_ID], rhs)
switch.setup_switch(make.Ptemplate_, make.Pmqtt, config)
if CONF_LAMBDA in config:
template_ = None
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(bool_)):
yield
add(make.Ptemplate_.set_state_lambda(template_))
if CONF_TURN_OFF_ACTION in config:
automation.build_automation(make.Ptemplate_.get_turn_off_trigger(), NoArg,
config[CONF_TURN_OFF_ACTION])
if CONF_TURN_ON_ACTION in config:
automation.build_automation(make.Ptemplate_.get_turn_on_trigger(), NoArg,
config[CONF_TURN_ON_ACTION])
if CONF_OPTIMISTIC in config:
add(make.Ptemplate_.set_optimistic(config[CONF_OPTIMISTIC]))
BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH'

View File

@@ -0,0 +1,45 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.components.uart import UARTComponent
from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, CONF_UART_ID
from esphomeyaml.core import HexInt
from esphomeyaml.helpers import App, Application, ArrayInitializer, get_variable, variable
DEPENDENCIES = ['uart']
MakeUARTSwitch = Application.MakeUARTSwitch
def validate_data(value):
if isinstance(value, unicode):
return value.encode('utf-8')
elif isinstance(value, str):
return value
elif isinstance(value, list):
return vol.Schema([cv.hex_uint8_t])(value)
raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes")
PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeUARTSwitch),
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
vol.Required(CONF_DATA): validate_data,
vol.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"),
}))
def to_code(config):
uart = None
for uart in get_variable(config[CONF_UART_ID]):
yield
data = config[CONF_DATA]
if isinstance(data, str):
data = [HexInt(ord(x)) for x in data]
rhs = App.make_uart_switch(uart, config[CONF_NAME], ArrayInitializer(*data, multiline=False))
restart = variable(config[CONF_MAKE_ID], rhs)
switch.setup_switch(restart.Puart, restart.Pmqtt, config)
BUILD_FLAGS = '-DUSE_UART_SWITCH'

View 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'

View 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'

View 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'

View 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'

View File

@@ -0,0 +1,276 @@
import datetime
import logging
import math
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import automation
from esphomeyaml.const import CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \
CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID
from esphomeyaml.helpers import App, NoArg, Pvariable, add, add_job, esphomelib_ns
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
time_ns = esphomelib_ns.namespace('time')
CronTrigger = time_ns.CronTrigger
def _tz_timedelta(td):
offset_hour = int(td.total_seconds() / (60 * 60))
offset_minute = int(abs(td.total_seconds() / 60)) % 60
offset_second = int(abs(td.total_seconds())) % 60
if offset_hour == 0 and offset_minute == 0 and offset_second == 0:
return '0'
elif offset_minute == 0 and offset_second == 0:
return '{}'.format(offset_hour)
elif offset_second == 0:
return '{}:{}'.format(offset_hour, offset_minute)
return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second)
# https://stackoverflow.com/a/16804556/8924614
def _week_of_month(dt):
first_day = dt.replace(day=1)
dom = dt.day
adjusted_dom = dom + first_day.weekday()
return int(math.ceil(adjusted_dom / 7.0))
def _tz_dst_str(dt):
td = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second)
return 'M{}.{}.{}/{}'.format(dt.month, _week_of_month(dt), dt.isoweekday() % 7,
_tz_timedelta(td))
def detect_tz():
try:
import tzlocal
import pytz
except ImportError:
raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically "
"detect the timezone please install tzlocal (pip2 install tzlocal)")
try:
tz = tzlocal.get_localzone()
except pytz.exceptions.UnknownTimeZoneError:
_LOGGER.warning("Could not auto-detect timezone. Using UTC...")
return 'UTC'
def _dst(dt, is_dst):
try:
return tz.dst(dt, is_dst=is_dst)
except TypeError: # stupid pytz...
return tz.dst(dt)
def _tzname(dt, is_dst):
try:
return tz.tzname(dt, is_dst=is_dst)
except TypeError: # stupid pytz...
return tz.tzname(dt)
def _utcoffset(dt, is_dst):
try:
return tz.utcoffset(dt, is_dst=is_dst)
except TypeError: # stupid pytz...
return tz.utcoffset(dt)
dst_begins = None
dst_tzname = None
dst_utcoffset = None
dst_ends = None
norm_tzname = None
norm_utcoffset = None
hour = datetime.timedelta(hours=1)
this_year = datetime.datetime.now().year
dt = datetime.datetime(year=this_year, month=1, day=1)
last_dst = None
while dt.year == this_year:
current_dst = _dst(dt, not last_dst)
is_dst = bool(current_dst)
if is_dst != last_dst:
if is_dst:
dst_begins = dt
dst_tzname = _tzname(dt, True)
dst_utcoffset = _utcoffset(dt, True)
else:
dst_ends = dt + hour
norm_tzname = _tzname(dt, False)
norm_utcoffset = _utcoffset(dt, False)
last_dst = is_dst
dt += hour
tzbase = '{}{}'.format(norm_tzname, _tz_timedelta(-1 * norm_utcoffset))
if dst_begins is None:
# No DST in this timezone
_LOGGER.info("Auto-detected timezone '%s' with UTC offset %s",
norm_tzname, _tz_timedelta(norm_utcoffset))
return tzbase
tzext = '{}{},{},{}'.format(dst_tzname, _tz_timedelta(-1 * dst_utcoffset),
_tz_dst_str(dst_begins), _tz_dst_str(dst_ends))
_LOGGER.info("Auto-detected timezone '%s' with UTC offset %s and daylight savings time from "
"%s to %s",
norm_tzname, _tz_timedelta(norm_utcoffset), dst_begins.strftime("%x %X"),
dst_ends.strftime("%x %X"))
return tzbase + tzext
def _parse_cron_int(value, special_mapping, message):
special_mapping = special_mapping or {}
if isinstance(value, (str, unicode)) and value in special_mapping:
return special_mapping[value]
try:
return int(value)
except ValueError:
raise vol.Invalid(message.format(value))
def _parse_cron_part(part, min_value, max_value, special_mapping):
if part == '*' or part == '?':
return set(x for x in range(min_value, max_value + 1))
if '/' in part:
data = part.split('/')
if len(data) > 2:
raise vol.Invalid(u"Can't have more than two '/' in one time expression, got {}"
.format(part))
offset, repeat = data
offset_n = 0
if offset:
offset_n = _parse_cron_int(offset, special_mapping,
u"Offset for '/' time expression must be an integer, got {}")
try:
repeat_n = int(repeat)
except ValueError:
raise vol.Invalid(u"Repeat for '/' time expression must be an integer, got {}"
.format(repeat))
return set(x for x in range(offset_n, max_value + 1, repeat_n))
if '-' in part:
data = part.split('-')
if len(data) > 2:
raise vol.Invalid(u"Can't have more than two '-' in range time expression '{}'"
.format(part))
begin, end = data
begin_n = _parse_cron_int(begin, special_mapping, u"Number for time range must be integer, "
u"got {}")
end_n = _parse_cron_int(end, special_mapping, u"Number for time range must be integer, "
u"got {}")
if end_n < begin_n:
return set(x for x in range(end_n, max_value + 1)) | \
set(x for x in range(min_value, begin_n + 1))
return set(x for x in range(begin_n, end_n + 1))
return {_parse_cron_int(part, special_mapping, u"Number for time expression must be an "
u"integer, got {}")}
def cron_expression_validator(name, min_value, max_value, special_mapping=None):
def validator(value):
if isinstance(value, list):
for v in value:
if not isinstance(v, int):
raise vol.Invalid(
"Expected integer for {} '{}', got {}".format(v, name, type(v)))
if v < min_value or v > max_value:
raise vol.Invalid(
"{} {} is out of range (min={} max={}).".format(name, v, min_value,
max_value))
return list(sorted(value))
value = cv.string(value)
values = set()
for part in value.split(','):
values |= _parse_cron_part(part, min_value, max_value, special_mapping)
return validator(list(values))
return validator
validate_cron_seconds = cron_expression_validator('seconds', 0, 60)
validate_cron_minutes = cron_expression_validator('minutes', 0, 59)
validate_cron_hours = cron_expression_validator('hours', 0, 23)
validate_cron_days_of_month = cron_expression_validator('days of month', 1, 31)
validate_cron_months = cron_expression_validator('months', 1, 12, {
'JAN': 1, 'FEB': 2, 'MAR': 3, 'APR': 4, 'MAY': 5, 'JUN': 6, 'JUL': 7, 'AUG': 8,
'SEP': 9, 'OCT': 10, 'NOV': 11, 'DEC': 12
})
validate_cron_days_of_week = cron_expression_validator('days of week', 1, 7, {
'SUN': 1, 'MON': 2, 'TUE': 3, 'WED': 4, 'THU': 5, 'FRI': 6, 'SAT': 7
})
CRON_KEYS = [CONF_SECONDS, CONF_MINUTES, CONF_HOURS, CONF_DAYS_OF_MONTH, CONF_MONTHS,
CONF_DAYS_OF_WEEK]
def validate_cron_raw(value):
value = cv.string(value)
value = value.split(' ')
if len(value) != 6:
raise vol.Invalid("Cron expression must consist of exactly 6 space-separated parts, "
"not {}".format(len(value)))
seconds, minutes, hours, days_of_month, months, days_of_week = value
return {
CONF_SECONDS: validate_cron_seconds(seconds),
CONF_MINUTES: validate_cron_minutes(minutes),
CONF_HOURS: validate_cron_hours(hours),
CONF_DAYS_OF_MONTH: validate_cron_days_of_month(days_of_month),
CONF_MONTHS: validate_cron_months(months),
CONF_DAYS_OF_WEEK: validate_cron_days_of_week(days_of_week),
}
def validate_cron_keys(value):
if CONF_CRON in value:
for key in value.keys():
if key in CRON_KEYS:
raise vol.Invalid("Cannot use option {} when cron: is specified.".format(key))
cron_ = value[CONF_CRON]
value = {x: value[x] for x in value if x != CONF_CRON}
value.update(cron_)
return value
return cv.has_at_least_one_key(*CRON_KEYS)(value)
TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_TIMEZONE, default=detect_tz): cv.string,
vol.Optional(CONF_ON_TIME): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(CronTrigger),
vol.Optional(CONF_SECONDS): validate_cron_seconds,
vol.Optional(CONF_MINUTES): validate_cron_minutes,
vol.Optional(CONF_HOURS): validate_cron_hours,
vol.Optional(CONF_DAYS_OF_MONTH): validate_cron_days_of_month,
vol.Optional(CONF_MONTHS): validate_cron_months,
vol.Optional(CONF_DAYS_OF_WEEK): validate_cron_days_of_week,
vol.Optional(CONF_CRON): validate_cron_raw,
}, validate_cron_keys),
})
def setup_time_core_(time_var, config):
add(time_var.set_timezone(config[CONF_TIMEZONE]))
for conf in config.get(CONF_ON_TIME, []):
rhs = App.register_component(time_var.Pmake_cron_trigger())
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
for second in conf.get(CONF_SECONDS, [x for x in range(0, 61)]):
add(trigger.add_second(second))
for minute in conf.get(CONF_MINUTES, [x for x in range(0, 60)]):
add(trigger.add_minute(minute))
for hour in conf.get(CONF_HOURS, [x for x in range(0, 24)]):
add(trigger.add_hour(hour))
for day_of_month in conf.get(CONF_DAYS_OF_MONTH, [x for x in range(1, 32)]):
add(trigger.add_day_of_month(day_of_month))
for month in conf.get(CONF_MONTHS, [x for x in range(1, 13)]):
add(trigger.add_month(month))
for day_of_week in conf.get(CONF_DAYS_OF_WEEK, [x for x in range(1, 8)]):
add(trigger.add_day_of_week(day_of_week))
automation.build_automation(trigger, NoArg, conf)
def setup_time(time_var, config):
add_job(setup_time_core_, time_var, config)
BUILD_FLAGS = '-DUSE_TIME'

View File

@@ -0,0 +1,24 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import time as time_
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SERVERS
from esphomeyaml.helpers import App, Pvariable
SNTPComponent = time_.time_ns.SNTPComponent
PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(SNTPComponent),
vol.Optional(CONF_SERVERS): vol.All(cv.ensure_list, [cv.string], vol.Length(max=3)),
vol.Optional(CONF_LAMBDA): cv.lambda_,
})
def to_code(config):
rhs = App.make_sntp_component(*config.get(CONF_SERVERS, []))
sntp = Pvariable(config[CONF_ID], rhs)
time_.setup_time(sntp, config)
BUILD_FLAGS = '-DUSE_SNTP_COMPONENT'

View File

@@ -0,0 +1,28 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns
UARTComponent = esphomelib_ns.UARTComponent
SPI_SCHEMA = vol.All(vol.Schema({
cv.GenerateID(): cv.declare_variable_id(UARTComponent),
vol.Optional(CONF_TX_PIN): pins.output_pin,
vol.Optional(CONF_RX_PIN): pins.input_pin,
vol.Required(CONF_BAUD_RATE): cv.positive_int,
}), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
def to_code(config):
for conf in config:
tx = conf.get(CONF_TX_PIN, -1)
rx = conf.get(CONF_RX_PIN, -1)
rhs = App.init_uart(tx, rx, conf[CONF_BAUD_RATE])
Pvariable(conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_UART'

View File

@@ -5,21 +5,23 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.const import CONF_PORT, CONF_JS_URL, CONF_CSS_URL, CONF_ID, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, add, Pvariable
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns
_LOGGER = logging.getLogger(__name__)
WebServer = esphomelib_ns.WebServer
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('web_server'): cv.register_variable_id,
cv.GenerateID(): cv.declare_variable_id(WebServer),
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_CSS_URL): vol.Url,
vol.Optional(CONF_JS_URL): vol.Url,
vol.Optional(CONF_CSS_URL): cv.string,
vol.Optional(CONF_JS_URL): cv.string,
})
def to_code(config):
rhs = App.init_web_server(config.get(CONF_PORT))
web_server = Pvariable('WebServer', config[CONF_ID], rhs)
web_server = Pvariable(config[CONF_ID], rhs)
if CONF_CSS_URL in config:
add(web_server.set_css_url(config[CONF_CSS_URL]))
if CONF_JS_URL in config:

View File

@@ -1,11 +1,11 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_GATEWAY, \
CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, \
ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import App, MockObj, Pvariable, StructInitializer, add
import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \
CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_POWER_SAVE_MODE,\
CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, esphomelib_ns, global_ns
def validate_password(value):
@@ -19,6 +19,15 @@ def validate_password(value):
return value
def validate_channel(value):
value = cv.positive_int(value)
if value < 1:
raise vol.Invalid("Minimum WiFi channel is 1")
if value > 14:
raise vol.Invalid("Maximum WiFi channel is 14")
return value
AP_MANUAL_IP_SCHEMA = vol.Schema({
vol.Required(CONF_STATIC_IP): cv.ipv4,
vol.Required(CONF_GATEWAY): cv.ipv4,
@@ -30,33 +39,67 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({
vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4,
})
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('wifi'): cv.register_variable_id,
WIFI_NETWORK_BASE = vol.Schema({
vol.Required(CONF_SSID): cv.ssid,
vol.Optional(CONF_PASSWORD): validate_password,
vol.Optional(CONF_CHANNEL): validate_channel,
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
})
WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend({
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
})
WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend({
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
})
def validate(config):
if CONF_PASSWORD in config and CONF_SSID not in config:
raise vol.Invalid("Cannot have WiFi password without SSID!")
if CONF_SSID not in config and CONF_AP not in config:
raise vol.Invalid("Please specify at least an SSID or an Access Point "
"to create.")
return config
# pylint: disable=invalid-name
IPAddress = global_ns.IPAddress
ManualIP = esphomelib_ns.ManualIP
WiFiComponent = esphomelib_ns.WiFiComponent
WiFiAp = esphomelib_ns.WiFiAp
WIFI_POWER_SAVE_MODES = {
'NONE': esphomelib_ns.WIFI_POWER_SAVE_NONE,
'LIGHT': esphomelib_ns.WIFI_POWER_SAVE_LIGHT,
'HIGH': esphomelib_ns.WIFI_POWER_SAVE_HIGH,
}
CONFIG_SCHEMA = vol.All(vol.Schema({
cv.GenerateID(): cv.declare_variable_id(WiFiComponent),
vol.Optional(CONF_SSID): cv.ssid,
vol.Optional(CONF_PASSWORD): validate_password,
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
vol.Optional(CONF_AP): vol.Schema({
vol.Required(CONF_SSID): cv.ssid,
vol.Optional(CONF_PASSWORD): validate_password,
vol.Optional(CONF_CHANNEL): vol.All(cv.positive_int, vol.Range(min=1, max=14)),
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
}),
vol.Optional(CONF_AP): WIFI_NETWORK_AP,
vol.Optional(CONF_HOSTNAME): cv.hostname,
})
# pylint: disable=invalid-name
IPAddress = MockObj('IPAddress')
vol.Optional(CONF_DOMAIN, default='.local'): cv.domain_name,
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SAVE_MODE): vol.All(vol.Upper, cv.one_of(*WIFI_POWER_SAVE_MODES)),
}), validate)
def safe_ip(ip):
if ip is None:
return None
return IPAddress(0, 0, 0, 0)
return IPAddress(*ip.args)
def manual_ip(config):
if config is None:
return None
return StructInitializer(
'ManualIP',
ManualIP,
('static_ip', safe_ip(config[CONF_STATIC_IP])),
('gateway', safe_ip(config[CONF_GATEWAY])),
('subnet', safe_ip(config[CONF_SUBNET])),
@@ -65,31 +108,35 @@ def manual_ip(config):
)
def wifi_network(config):
return StructInitializer(
WiFiAp,
('ssid', config.get(CONF_SSID, "")),
('password', config.get(CONF_PASSWORD, "")),
('channel', config.get(CONF_CHANNEL, -1)),
('manual_ip', manual_ip(config.get(CONF_MANUAL_IP))),
)
def to_code(config):
sta = CONF_SSID in config
ap = CONF_AP in config
if sta:
rhs = App.init_wifi(config[CONF_SSID], config.get(CONF_PASSWORD))
else:
rhs = App.init_wifi()
wifi = Pvariable('WiFiComponent', config[CONF_ID], rhs)
rhs = App.init_wifi()
wifi = Pvariable(config[CONF_ID], rhs)
if sta and CONF_MANUAL_IP in config:
add(wifi.set_sta_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
if CONF_SSID in config:
add(wifi.set_sta(wifi_network(config)))
if ap:
conf = config[CONF_AP]
password = config.get(CONF_PASSWORD)
if password is None and CONF_CHANNEL in conf:
password = u""
add(wifi.set_ap(conf[CONF_SSID], password, conf.get(CONF_CHANNEL)))
if CONF_MANUAL_IP in conf:
add(wifi.set_ap_manual_ip(manual_ip(conf[CONF_MANUAL_IP])))
if CONF_AP in config:
add(wifi.set_ap(wifi_network(config[CONF_AP])))
if CONF_HOSTNAME in config:
add(wifi.set_hostname(config[CONF_HOSTNAME]))
if CONF_REBOOT_TIMEOUT in config:
add(wifi.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
if CONF_POWER_SAVE_MODE in config:
add(wifi.set_power_save_mode(WIFI_POWER_SAVE_MODES[config[CONF_POWER_SAVE_MODE]]))
def lib_deps(config):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:

33
esphomeyaml/config.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "esphomeyaml",
"version": "1.9.0b1",
"slug": "esphomeyaml",
"description": "esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices.",
"url": "https://esphomelib.com/esphomeyaml/index.html",
"startup": "application",
"webui": "http://[HOST]:[PORT:6052]",
"boot": "auto",
"ports": {
"6052/tcp": 6052,
"6053/tcp": 6053
},
"auto_uart": true,
"map": [
"config:rw"
],
"arch": [
"amd64",
"armhf",
"i386"
],
"environment": {
"ESPHOMEYAML_OTA_HOST_PORT": "6053"
},
"options": {
"password": ""
},
"schema": {
"password": "str?"
},
"image": "ottowinter/esphomeyaml-hassio-{arch}"
}

View File

@@ -7,43 +7,21 @@ from collections import OrderedDict
import voluptuous as vol
from voluptuous.humanize import humanize_error
import esphomeyaml.config_validation as cv
from esphomeyaml import core, yaml_util
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
CONF_LIBRARY_URI, \
CONF_NAME, CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, ESP_PLATFORMS, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml import core, yaml_util, core_config
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, CONF_WIFI, ESP_PLATFORMS
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, add, color
from esphomeyaml.helpers import color
from esphomeyaml.util import safe_print
_LOGGER = logging.getLogger(__name__)
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.5.3'
BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout']
CORE_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.valid_name,
vol.Required(CONF_PLATFORM): cv.string,
vol.Required(CONF_BOARD): cv.string,
vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string,
vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean,
vol.Optional(CONF_USE_BUILD_FLAGS, default=True): cv.boolean,
vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, vol.Any(*BUILD_FLASH_MODES)),
})
REQUIRED_COMPONENTS = [
CONF_ESPHOMEYAML, CONF_WIFI
]
_COMPONENT_CACHE = {}
_ALL_COMPONENTS = []
def core_to_code(config):
add(App.set_name(config[CONF_NAME]))
def get_component(domain):
if domain in _COMPONENT_CACHE:
return _COMPONENT_CACHE[domain]
@@ -51,7 +29,7 @@ def get_component(domain):
path = 'esphomeyaml.components.{}'.format(domain)
try:
module = importlib.import_module(path)
except ImportError as err:
except (ImportError, ValueError) as err:
_LOGGER.debug(err)
else:
_COMPONENT_CACHE[domain] = module
@@ -93,6 +71,50 @@ class Config(OrderedDict):
self.errors.append((message, domain, config))
def iter_ids(config, prefix=None, parent=None):
prefix = prefix or []
parent = parent or {}
if isinstance(config, core.ID):
yield config, prefix, parent
elif isinstance(config, core.Lambda):
for id in config.requires_ids:
yield id, prefix, parent
elif isinstance(config, list):
for i, item in enumerate(config):
for result in iter_ids(item, prefix + [str(i)], config):
yield result
elif isinstance(config, dict):
for key, value in config.iteritems():
for result in iter_ids(value, prefix + [str(key)], config):
yield result
def do_id_pass(result):
declare_ids = []
searching_ids = []
for id, prefix, config in iter_ids(result):
if id.is_declaration:
if id.id is not None and any(v[0].id == id.id for v in declare_ids):
result.add_error("ID {} redefined!".format(id.id), '.'.join(prefix), config)
continue
declare_ids.append((id, prefix, config))
else:
searching_ids.append((id, prefix, config))
# Resolve default ids after manual IDs
for id, _, _ in declare_ids:
id.resolve([v[0].id for v in declare_ids])
# Check searched IDs
for id, prefix, config in searching_ids:
if id.id is not None and not any(v[0].id == id.id for v in declare_ids):
result.add_error("Couldn't find ID {}".format(id.id), '.'.join(prefix), config)
if id.id is None and id.type is not None:
id.id = next((v[0].id for v in declare_ids if v[0].type == id.type), None)
if id.id is None:
result.add_error("Couldn't resolve ID for type {}".format(id.type),
'.'.join(prefix), config)
def validate_config(config):
global _ALL_COMPONENTS
@@ -107,31 +129,63 @@ def validate_config(config):
def _comp_error(ex, domain, config):
result.add_error(_format_config_error(ex, domain, config), domain, config)
try:
result[CONF_ESPHOMEYAML] = CORE_SCHEMA(config[CONF_ESPHOMEYAML])
except vol.Invalid as ex:
_comp_error(ex, CONF_ESPHOMEYAML, config)
# Step 1: Load everything
for domain, conf in config.iteritems():
if domain == CONF_ESPHOMEYAML:
domain = str(domain)
if domain == CONF_ESPHOMEYAML or domain.startswith('.'):
continue
if conf is None:
conf = {}
component = get_component(domain)
if component is None:
result.add_error(u"Component not found: {}".format(domain))
result.add_error(u"Component not found: {}".format(domain), domain, conf)
continue
if not hasattr(component, 'PLATFORM_SCHEMA'):
continue
for p_config in conf:
if not isinstance(p_config, dict):
result.add_error(u"Platform schemas must have 'platform:' key", )
continue
p_name = p_config.get(u'platform')
if p_name is None:
result.add_error(u"No platform specified for {}".format(domain))
continue
p_domain = u'{}.{}'.format(domain, p_name)
platform = get_platform(domain, p_name)
if platform is None:
result.add_error(u"Platform not found: '{}'".format(p_domain), p_domain, p_config)
continue
# Step 2: Validate configuration
try:
result[CONF_ESPHOMEYAML] = core_config.CONFIG_SCHEMA(config[CONF_ESPHOMEYAML])
except vol.Invalid as ex:
_comp_error(ex, CONF_ESPHOMEYAML, config[CONF_ESPHOMEYAML])
for domain, conf in config.iteritems():
if domain == CONF_ESPHOMEYAML or domain.startswith('.'):
continue
if conf is None:
conf = {}
domain = str(domain)
component = get_component(domain)
if component is None:
continue
esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS)
if core.ESP_PLATFORM not in esp_platforms:
result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM))
result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM),
domain, conf)
continue
success = True
dependencies = getattr(component, 'DEPENDENCIES', [])
for dependency in dependencies:
if dependency not in _ALL_COMPONENTS:
result.add_error(u"Component {} requires {}".format(domain, dependency))
result.add_error(u"Component {} requires component {}".format(domain, dependency),
domain, conf)
success = False
if not success:
continue
@@ -150,35 +204,43 @@ def validate_config(config):
platforms = []
for p_config in conf:
if not isinstance(p_config, dict):
result.add_error(u"Platform schemas must have 'platform:' key")
continue
p_name = p_config.get(u'platform')
if p_name is None:
result.add_error(u"No platform specified for {}".format(domain))
continue
p_domain = u'{}.{}'.format(domain, p_name)
platform = get_platform(domain, p_name)
if platform is None:
result.add_error(u"Platform not found: {}.{}")
continue
success = True
dependencies = getattr(platform, 'DEPENDENCIES', [])
for dependency in dependencies:
if dependency not in _ALL_COMPONENTS:
result.add_error(u"Platform {}.{} requires {}".format(domain, p_name,
dependency))
result.add_error(
u"Platform {} requires component {}".format(p_domain, dependency),
p_domain, p_config)
success = False
if not success:
continue
esp_platforms = getattr(platform, 'ESP_PLATFORMS', ESP_PLATFORMS)
if core.ESP_PLATFORM not in esp_platforms:
result.add_error(
u"Platform {} doesn't support {}.".format(p_domain, core.ESP_PLATFORM),
p_domain, p_config)
continue
if hasattr(platform, u'PLATFORM_SCHEMA'):
try:
p_validated = platform.PLATFORM_SCHEMA(p_config)
except vol.Invalid as ex:
_comp_error(ex, u'{}.{}'.format(domain, p_name), p_config)
_comp_error(ex, p_domain, p_config)
continue
platforms.append(p_validated)
result[domain] = platforms
do_id_pass(result)
return result
@@ -211,24 +273,14 @@ def load_config(path):
except OSError:
raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path))
core.RAW_CONFIG = config
if CONF_ESPHOMEYAML not in config:
raise ESPHomeYAMLError(u"No esphomeyaml section in config")
core_conf = config[CONF_ESPHOMEYAML]
esp_platform = unicode(core_conf.get(CONF_PLATFORM, u""))
esp_platform = esp_platform.upper()
if '8266' in esp_platform:
esp_platform = ESP_PLATFORM_ESP8266
if '32' in esp_platform:
esp_platform = ESP_PLATFORM_ESP32
core.ESP_PLATFORM = esp_platform
core.BOARD = unicode(core_conf.get(CONF_BOARD, u""))
core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True))
core_config.preload_core_config(config)
try:
result = validate_config(config)
except ESPHomeYAMLError:
raise
except Exception:
print(u"Unexpected exception while reading configuration:")
_LOGGER.error(u"Unexpected exception while reading configuration:")
raise
return result
@@ -255,38 +307,39 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
if isinstance(layer, dict):
for key, value in sorted(layer.items(), key=sort_dict_key):
if isinstance(value, (dict, list)):
print(indent_str, key + ':', line_info(value, **kwargs))
safe_print(u"{} {}: {}".format(indent_str, key, line_info(value, **kwargs)))
dump_dict(value, indent_count + 2)
else:
print(indent_str, key + ':', value)
safe_print(u"{} {}: {}".format(indent_str, key, value))
indent_str = indent_count * ' '
if isinstance(layer, (list, tuple)):
for i in layer:
if isinstance(i, dict):
dump_dict(i, indent_count + 2, True)
else:
print(' ', indent_str, i)
safe_print(u" {} {}".format(indent_str, i))
def read_config(path):
_LOGGER.debug("Reading configuration...")
_LOGGER.info("Reading configuration...")
try:
res = load_config(path)
except ESPHomeYAMLError as err:
_LOGGER.error(u"Error while reading config: %s", err)
return None
excepts = {}
for err in res.errors:
domain = err[1] or u"General Error"
excepts.setdefault(domain, []).append(err[0])
if err[2] is not None:
excepts[domain].append(err[2])
for message, domain, config in res.errors:
domain = domain or u"General Error"
excepts.setdefault(domain, []).append(message)
if config is not None:
excepts[domain].append(config)
if excepts:
print(color('bold_white', u"Failed config"))
safe_print(color('bold_white', u"Failed config"))
for domain, config in excepts.iteritems():
print(' ', color('bold_red', domain + ':'), color('red', '', reset='red'))
safe_print(u' {} {}'.format(color('bold_red', domain + u':'),
color('red', '', reset='red')))
dump_dict(config, reset='red')
print(color('reset'))
safe_print(color('reset'))
return None
return dict(**res)
return OrderedDict(res)

View File

@@ -3,18 +3,19 @@
from __future__ import print_function
import logging
import os
import re
import uuid as uuid_
import voluptuous as vol
from esphomeyaml import core
from esphomeyaml import core, helpers
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \
CONF_NAME, CONF_PAYLOAD_AVAILABLE, \
CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.core import HexInt, IPAddress, TimePeriod, TimePeriodMilliseconds, \
TimePeriodMicroseconds, TimePeriodSeconds
from esphomeyaml.helpers import ensure_unique_string
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_INTERNAL
from esphomeyaml.core import HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
TimePeriodMilliseconds, TimePeriodSeconds
_LOGGER = logging.getLogger(__name__)
@@ -26,7 +27,7 @@ zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
RESERVED_IDS = [
# C++ keywords http://en.cppreference.com/w/cpp/keyword
@@ -57,8 +58,10 @@ def alphanumeric(value):
def valid_name(value):
value = string_strict(value)
if not all(c in ALLOWED_NAME_CHARS for c in value):
raise vol.Invalid(u"Valid characters for name are {}".format(ALLOWED_NAME_CHARS))
for c in value:
if c not in ALLOWED_NAME_CHARS:
raise vol.Invalid(u"'{}' is an invalid character for names. Valid characters are: {}"
u"".format(c, ALLOWED_NAME_CHARS))
return value
@@ -100,13 +103,19 @@ def boolean(value):
def ensure_list(value):
"""Wrap value in list if it is not one."""
if value is None:
if value is None or (isinstance(value, dict) and not value):
return []
if isinstance(value, list):
return value
return [value]
def ensure_list_not_empty(value):
if isinstance(value, list):
return value
return [value]
def ensure_dict(value):
if value is None:
return {}
@@ -134,16 +143,56 @@ def int_(value):
hex_int = vol.Coerce(hex_int_)
match_cpp_var_ = vol.Match(r'^[a-zA-Z_][a-zA-Z0-9_]+$', msg=u"Must be a valid C++ variable name")
def variable_id(value):
value = match_cpp_var_(value)
def variable_id_str_(value):
value = string(value)
if not value:
raise vol.Invalid("ID must not be empty")
if value[0].isdigit():
raise vol.Invalid("First character in ID cannot be a digit.")
if '-' in value:
raise vol.Invalid("Dashes are not supported in IDs, please use underscores instead.")
for char in value:
if char != '_' and not char.isalnum():
raise vol.Invalid(u"IDs must only consist of upper/lowercase characters and numbers."
u"The character '{}' cannot be used".format(char))
if value in RESERVED_IDS:
raise vol.Invalid(u"ID {} is reserved internally and cannot be used".format(value))
return value
def use_variable_id(type):
def validator(value):
if value is None:
return core.ID(None, is_declaration=False, type=type)
return core.ID(variable_id_str_(value), is_declaration=False, type=type)
return validator
def declare_variable_id(type):
def validator(value):
if value is None:
return core.ID(None, is_declaration=True, type=type)
return core.ID(variable_id_str_(value), is_declaration=True, type=type)
return validator
def templatable(other_validators):
def validator(value):
if isinstance(value, Lambda):
return value
if isinstance(other_validators, dict):
return vol.Schema(other_validators)(value)
return other_validators(value)
return validator
def only_on(platforms):
if not isinstance(platforms, list):
platforms = [platforms]
@@ -170,10 +219,37 @@ def has_at_least_one_key(*keys):
if not isinstance(obj, dict):
raise vol.Invalid('expected dictionary')
for k in obj.keys():
if k in keys:
return obj
raise vol.Invalid('must contain one of {}.'.format(', '.join(keys)))
if not any(k in keys for k in obj):
raise vol.Invalid('Must contain at least one of {}.'.format(', '.join(keys)))
return obj
return validate
def has_exactly_one_key(*keys):
def validate(obj):
if not isinstance(obj, dict):
raise vol.Invalid('expected dictionary')
number = sum(k in keys for k in obj)
if number > 1:
raise vol.Invalid("Cannot specify more than one of {}.".format(', '.join(keys)))
if number < 1:
raise vol.Invalid('Must contain exactly one of {}.'.format(', '.join(keys)))
return obj
return validate
def has_at_most_one_key(*keys):
def validate(obj):
if not isinstance(obj, dict):
raise vol.Invalid('expected dictionary')
number = sum(k in keys for k in obj)
if number > 1:
raise vol.Invalid("Cannot specify more than one of {}.".format(', '.join(keys)))
return obj
return validate
@@ -194,14 +270,6 @@ time_period_dict = vol.All(
lambda value: TimePeriod(**value))
TIME_PERIOD_EXPLICIT_MESSAGE = ("The old way of being able to write time values without a "
"time unit (like \"1000\" for 1000 milliseconds) has been "
"removed in 1.5.0 as it was ambiguous in some places. Please "
"now explicitly specify the time unit (like \"1000ms\"). See "
"https://esphomelib.com/esphomeyaml/configuration-types.html#time "
"for more information.")
def time_period_str_colon(value):
"""Validate and transform time offset with format HH:MM[:SS]."""
if isinstance(value, int):
@@ -232,13 +300,6 @@ def time_period_str_unit(value):
elif not isinstance(value, (str, unicode)):
raise vol.Invalid("Expected string for time period with unit.")
try:
float(value)
except ValueError:
pass
else:
raise vol.Invalid(TIME_PERIOD_EXPLICIT_MESSAGE)
unit_to_kwarg = {
'us': 'microseconds',
'microseconds': 'microseconds',
@@ -283,15 +344,43 @@ def time_period_in_seconds_(value):
return TimePeriodSeconds(**value.as_dict())
def update_interval(value):
if value == 'never':
return 4294967295 # uint32_t max
return positive_time_period_milliseconds(value)
time_period = vol.Any(time_period_str_unit, time_period_str_colon, time_period_dict)
positive_time_period = vol.All(time_period, vol.Range(min=TimePeriod()))
positive_time_period_milliseconds = vol.All(positive_time_period, time_period_in_milliseconds_)
positive_time_period_seconds = vol.All(positive_time_period, time_period_in_seconds_)
time_period_microseconds = vol.All(time_period, time_period_in_microseconds_)
positive_time_period_microseconds = vol.All(positive_time_period, time_period_in_microseconds_)
positive_not_null_time_period = vol.All(time_period,
vol.Range(min=TimePeriod(), min_included=False))
def mac_address(value):
value = string_strict(value)
parts = value.split(':')
if len(parts) != 6:
raise vol.Invalid("MAC Address must consist of 6 : (colon) separated parts")
parts_int = []
if any(len(part) != 2 for part in parts):
raise vol.Invalid("MAC Address must be format XX:XX:XX:XX:XX:XX")
for part in parts:
try:
parts_int.append(int(part, 16))
except ValueError:
raise vol.Invalid("MAC Address parts must be hexadecimal values from 00 to FF")
return core.MACAddress(*parts_int)
def uuid(value):
return vol.Coerce(uuid_.UUID)(value)
METRIC_SUFFIXES = {
'E': 1e18, 'P': 1e15, 'T': 1e12, 'G': 1e9, 'M': 1e6, 'k': 1e3, 'da': 10, 'd': 1e-1,
'c': 1e-2, 'm': 0.001, u'µ': 1e-6, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12, 'f': 1e-15, 'a': 1e-18,
@@ -299,20 +388,46 @@ METRIC_SUFFIXES = {
}
def frequency(value):
def float_with_unit(quantity, regex_suffix):
pattern = re.compile(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)" + regex_suffix + "$")
def validator(value):
match = pattern.match(string(value))
if match is None:
raise vol.Invalid(u"Expected {} with unit, got {}".format(quantity, value))
mantissa = float(match.group(1))
if match.group(2) not in METRIC_SUFFIXES:
raise vol.Invalid(u"Invalid {} suffix {}".format(quantity, match.group(2)))
multiplier = METRIC_SUFFIXES[match.group(2)]
return mantissa * multiplier
return validator
frequency = float_with_unit("frequency", r"(Hz|HZ|hz)?")
resistance = float_with_unit("resistance", r"(Ω|Ω|ohm|Ohm|OHM)?")
current = float_with_unit("current", r"(a|A|amp|Amp|amps|Amps|ampere|Ampere)?")
voltage = float_with_unit("voltage", r"(v|V|volt|Volts)?")
def validate_bytes(value):
value = string(value)
match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)(?:Hz|HZ|hz)?$", value)
match = re.match(r"^([0-9]+)\s*(\w*?)(?:byte|B|b)?s?$", value)
if match is None:
raise vol.Invalid(u"Expected frequency with unit, "
u"got {}".format(value))
raise vol.Invalid(u"Expected number of bytes with unit, got {}".format(value))
mantissa = float(match.group(1))
mantissa = int(match.group(1))
if match.group(2) not in METRIC_SUFFIXES:
raise vol.Invalid(u"Invalid frequency suffix {}".format(match.group(2)))
raise vol.Invalid(u"Invalid metric suffix {}".format(match.group(2)))
multiplier = METRIC_SUFFIXES[match.group(2)]
return mantissa * multiplier
if multiplier < 1:
raise vol.Invalid(u"Only suffixes with positive exponents are supported. "
u"Got {}".format(match.group(2)))
return int(mantissa * multiplier)
def hostname(value):
@@ -325,6 +440,18 @@ def hostname(value):
return value
def domain_name(value):
value = string(value)
if not value.startswith('.'):
raise vol.Invalid("Domain name must start with .")
if value.startswith('..'):
raise vol.Invalid("Domain name must start with single .")
for c in value:
if not (c.isalnum() or c in '._-'):
raise vol.Invalid("Domain name can only have alphanumeric characters and _ or -")
return value
def ssid(value):
if value is None:
raise vol.Invalid("SSID can not be None")
@@ -410,6 +537,14 @@ def mqtt_payload(value):
return string(value)
def mqtt_qos(value):
try:
value = int(value)
except (TypeError, ValueError):
raise vol.Invalid(u"MQTT Quality of Service must be integer, got {}".format(value))
return one_of(0, 1, 2)(value)
uint8_t = vol.All(int_, vol.Range(min=0, max=255))
uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295))
@@ -419,9 +554,22 @@ hex_uint32_t = vol.All(hex_int, vol.Range(min=0, max=4294967295))
i2c_address = hex_uint8_t
def percentage(value):
if isinstance(value, (str, unicode)) and value.endswith('%'):
value = float(value[:-1].rstrip()) / 100.0
return zero_to_one_float(value)
def percentage_int(value):
if isinstance(value, (str, unicode)) and value.endswith('%'):
value = int(value[:-1].rstrip())
return value
def invalid(message):
def validator(value):
raise vol.Invalid(message)
return validator
@@ -429,29 +577,87 @@ def valid(value):
return value
def one_of(*values):
options = u', '.join(u"'{}'".format(x) for x in values)
def validator(value):
if value not in values:
raise vol.Invalid(u"Unknown value '{}', must be one of {}".format(value, options))
return value
return validator
def lambda_(value):
if isinstance(value, Lambda):
return value
return Lambda(string_strict(value))
def dimensions(value):
if isinstance(value, list):
if len(value) != 2:
raise vol.Invalid(u"Dimensions must have a length of two, not {}".format(len(value)))
try:
width, height = int(value[0]), int(value[1])
except ValueError:
raise vol.Invalid(u"Width and height dimensions must be integers")
if width <= 0 or height <= 0:
raise vol.Invalid(u"Width and height must at least be 1")
return [width, height]
value = string(value)
match = re.match(r"\s*([0-9]+)\s*[xX]\s*([0-9]+)\s*", value)
if not match:
raise vol.Invalid(u"Invalid value '{}' for dimensions. Only WIDTHxHEIGHT is allowed.")
return dimensions([match.group(1), match.group(2)])
def directory(value):
value = string(value)
path = helpers.relative_path(value)
if not os.path.exists(path):
raise vol.Invalid(u"Could not find directory '{}'. Please make sure it exists.".format(
path))
if not os.path.isdir(path):
raise vol.Invalid(u"Path '{}' is not a directory.".format(path))
return value
def file_(value):
value = string(value)
path = helpers.relative_path(value)
if not os.path.exists(path):
raise vol.Invalid(u"Could not find file '{}'. Please make sure it exists.".format(
path))
if not os.path.isfile(path):
raise vol.Invalid(u"Path '{}' is not a file.".format(path))
return value
REGISTERED_IDS = set()
def register_variable_id(value):
s = variable_id(value)
if s in REGISTERED_IDS:
raise vol.Invalid("This ID has already been used")
REGISTERED_IDS.add(s)
return s
class GenerateID(vol.Optional):
def __init__(self, basename, key=CONF_ID):
self._basename = basename
super(GenerateID, self).__init__(key, default=self.default_variable_id)
def default_variable_id(self):
return ensure_unique_string(self._basename, REGISTERED_IDS)
def __init__(self, key=CONF_ID):
super(GenerateID, self).__init__(key, default=lambda: None)
REQUIRED_ID_SCHEMA = vol.Schema({
vol.Required(CONF_ID): register_variable_id,
})
def nameable(*schemas):
def validator(config):
config = vol.All(*schemas)(config)
if CONF_NAME not in config and CONF_ID not in config:
raise vol.Invalid("At least one of 'id:' or 'name:' is required!")
if CONF_NAME not in config:
id = config[CONF_ID]
if not id.is_manual:
raise vol.Invalid("At least one of 'id:' or 'name:' is required!")
config[CONF_NAME] = id.id
config[CONF_INTERNAL] = True
return config
return config
return validator
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): valid,
@@ -464,11 +670,12 @@ MQTT_COMPONENT_AVAILABILITY_SCHEMA = vol.Schema({
})
MQTT_COMPONENT_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): string,
vol.Optional(CONF_NAME): string,
vol.Optional(CONF_RETAIN): boolean,
vol.Optional(CONF_DISCOVERY): boolean,
vol.Optional(CONF_STATE_TOPIC): publish_topic,
vol.Optional(CONF_AVAILABILITY): MQTT_COMPONENT_AVAILABILITY_SCHEMA,
vol.Optional(CONF_AVAILABILITY): vol.Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA),
vol.Optional(CONF_INTERNAL): boolean,
})
MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({

View File

@@ -1,10 +1,11 @@
"""Constants used by esphomeyaml."""
MAJOR_VERSION = 1
MINOR_VERSION = 5
PATCH_VERSION = '3'
MINOR_VERSION = 9
PATCH_VERSION = '0b1'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
ESPHOMELIB_VERSION = '1.9.0b1'
ESP_PLATFORM_ESP32 = 'ESP32'
ESP_PLATFORM_ESP8266 = 'ESP8266'
@@ -16,9 +17,14 @@ CONF_ESPHOMEYAML = 'esphomeyaml'
CONF_NAME = 'name'
CONF_PLATFORM = 'platform'
CONF_BOARD = 'board'
CONF_SIMPLIFY = 'simplify'
CONF_USE_BUILD_FLAGS = 'use_build_flags'
CONF_LIBRARY_URI = 'library_uri'
CONF_ESPHOMELIB_VERSION = 'esphomelib_version'
CONF_USE_CUSTOM_CODE = 'use_custom_code'
CONF_ARDUINO_VERSION = 'arduino_version'
CONF_LOCAL = 'local'
CONF_REPOSITORY = 'repository'
CONF_COMMIT = 'commit'
CONF_TAG = 'tag'
CONF_BRANCH = 'branch'
CONF_LOGGER = 'logger'
CONF_WIFI = 'wifi'
CONF_SSID = 'ssid'
@@ -34,6 +40,13 @@ CONF_USERNAME = 'username'
CONF_POWER_SUPPLY = 'power_supply'
CONF_ID = 'id'
CONF_MQTT_ID = 'mqtt_id'
CONF_SENSOR_ID = 'sensor_id'
CONF_TRIGGER_ID = 'trigger_id'
CONF_ACTION_ID = 'action_id'
CONF_CONDITION_ID = 'condition_id'
CONF_MAKE_ID = 'make_id'
CONF_AUTOMATION_ID = 'automation_id'
CONF_DELAY = 'delay'
CONF_PIN = 'pin'
CONF_NUMBER = 'number'
CONF_INVERTED = 'inverted'
@@ -70,6 +83,15 @@ CONF_TOPIC = 'topic'
CONF_PAYLOAD_AVAILABLE = 'payload_available'
CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available'
CONF_DEFAULT_TRANSITION_LENGTH = 'default_transition_length'
CONF_TRANSITION_LENGTH = 'transition_length'
CONF_FLASH_LENGTH = 'flash_length'
CONF_BRIGHTNESS = 'brightness'
CONF_EFFECT = 'effect'
CONF_ABOVE = 'above'
CONF_BELOW = 'below'
CONF_ON = 'on'
CONF_IF = 'if'
CONF_THEN = 'then'
CONF_BINARY = 'binary'
CONF_WHITE = 'white'
CONF_RGBW = 'rgbw'
@@ -83,6 +105,7 @@ CONF_LOGS = 'logs'
CONF_PORT = 'port'
CONF_WILL_MESSAGE = 'will_message'
CONF_BIRTH_MESSAGE = 'birth_message'
CONF_SHUTDOWN_MESSAGE = 'shutdown_message'
CONF_PAYLOAD = 'payload'
CONF_QOS = 'qos'
CONF_DISCOVERY_RETAIN = 'discovery_retain'
@@ -112,6 +135,8 @@ CONF_LAMBDA = 'lambda'
CONF_THROTTLE = 'throttle'
CONF_DELTA = 'delta'
CONF_OR = 'or'
CONF_AND = 'and'
CONF_RANGE = 'range'
CONF_UNIQUE = 'unique'
CONF_HEARTBEAT = 'heartbeat'
CONF_DEBOUNCE = 'debounce'
@@ -130,13 +155,13 @@ CONF_TRIGGER_PIN = 'trigger_pin'
CONF_ECHO_PIN = 'echo_pin'
CONF_TIMEOUT_METER = 'timeout_meter'
CONF_TIMEOUT_TIME = 'timeout_time'
CONF_IR_TRANSMITTER_ID = 'ir_transmitter_id'
CONF_CARRIER_DUTY_PERCENT = 'carrier_duty_percent'
CONF_NEC = 'nec'
CONF_COMMAND = 'command'
CONF_DATA = 'data'
CONF_NBITS = 'nbits'
CONF_LG = 'lg'
CONF_SAMSUNG = 'samsung'
CONF_SONY = 'sony'
CONF_PANASONIC = 'panasonic'
CONF_REPEAT = 'repeat'
@@ -146,6 +171,7 @@ CONF_OSCILLATION_OUTPUT = 'oscillation_output'
CONF_SPEED = 'speed'
CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic'
CONF_OSCILLATION_COMMAND_TOPIC = 'oscillation_command_topic'
CONF_OSCILLATING = 'oscillating'
CONF_SPEED_STATE_TOPIC = 'speed_state_topic'
CONF_SPEED_COMMAND_TOPIC = 'speed_command_topic'
CONF_LOW = 'low'
@@ -192,25 +218,140 @@ CONF_CLOCK_PIN = 'clock_pin'
CONF_RGB_ORDER = 'rgb_order'
CONF_ACCURACY = 'accuracy'
CONF_BOARD_FLASH_MODE = 'board_flash_mode'
CONF_ON_PRESS = 'on_press'
CONF_ON_RELEASE = 'on_release'
CONF_ON_CLICK = 'on_click'
CONF_ON_DOUBLE_CLICK = 'on_double_click'
CONF_MIN_LENGTH = 'min_length'
CONF_MAX_LENGTH = 'max_length'
CONF_ON_VALUE = 'on_value'
CONF_ON_RAW_VALUE = 'on_raw_value'
CONF_ON_VALUE_RANGE = 'on_value_range'
CONF_ON_MESSAGE = 'on_message'
CONF_CS_PIN = 'cs_pin'
CONF_CLK_PIN = 'clk_pin'
CONF_MISO_PIN = 'miso_pin'
CONF_MOSI_PIN = 'mosi_pin'
CONF_TURN_ON_ACTION = 'turn_on_action'
CONF_TURN_OFF_ACTION = 'turn_off_action'
CONF_OPEN_ACTION = 'open_action'
CONF_CLOSE_ACTION = 'close_action'
CONF_STOP_ACTION = 'stop_action'
CONF_DOMAIN = 'domain'
CONF_OPTIMISTIC = 'optimistic'
CONF_ON_BOOT = 'on_boot'
CONF_ON_SHUTDOWN = 'on_shutdown'
CONF_PRIORITY = 'priority'
CONF_DUMP = 'dump'
CONF_BUFFER_SIZE = 'buffer_size'
CONF_TOLERANCE = 'tolerance'
CONF_FILTER = 'filter'
CONF_IDLE = 'idle'
CONF_NETWORKS = 'networks'
CONF_INTERNAL = 'internal'
CONF_BUILD_PATH = 'build_path'
CONF_REBOOT_TIMEOUT = 'reboot_timeout'
CONF_INVERT = 'invert'
CONF_DELAYED_ON = 'delayed_on'
CONF_DELAYED_OFF = 'delayed_off'
CONF_UUID = 'uuid'
CONF_TYPE = 'type'
CONF_SPI_ID = 'spi_id'
CONF_UART_ID = 'uart_id'
CONF_UID = 'uid'
CONF_TX_PIN = 'tx_pin'
CONF_RX_PIN = 'rx_pin'
CONF_CO2 = 'co2'
CONF_SHUNT_RESISTANCE = 'shunt_resistance'
CONF_MAX_CURRENT = 'max_current'
CONF_MAX_VOLTAGE = 'max_voltage'
CONF_CURRENT = 'current'
CONF_POWER = 'power'
CONF_BUS_VOLTAGE = 'bus_voltage'
CONF_SHUNT_VOLTAGE = 'shunt_voltage'
CONF_CONDITION = 'condition'
CONF_ELSE = 'else'
CONF_EFFECTS = 'effects'
CONF_RANDOM = 'random'
CONF_EFFECT_ID = 'effect_id'
CONF_COLORS = 'colors'
CONF_STATE = 'state'
CONF_DURATION = 'duration'
CONF_WIDTH = 'width'
CONF_ILLUMINANCE = 'illuminance'
CONF_COLOR_TEMPERATURE = 'color_temperature'
CONF_BATTERY_LEVEL = 'battery_level'
CONF_MOISTURE = 'moisture'
CONF_CONDUCTIVITY = 'conductivity'
CONF_RC_SWITCH_RAW = 'rc_switch_raw'
CONF_RC_SWITCH_TYPE_A = 'rc_switch_type_a'
CONF_RC_SWITCH_TYPE_B = 'rc_switch_type_b'
CONF_RC_SWITCH_TYPE_C = 'rc_switch_type_c'
CONF_RC_SWITCH_TYPE_D = 'rc_switch_type_d'
CONF_CODE = 'code'
CONF_PROTOCOL = 'protocol'
CONF_PULSE_LENGTH = 'pulse_length'
CONF_SYNC = 'sync'
CONF_ZERO = 'zero'
CONF_ONE = 'one'
CONF_GROUP = 'group'
CONF_DEVICE = 'device'
CONF_FAMILY = 'family'
CONF_FILE = 'file'
CONF_GLYPHS = 'glyphs'
CONF_SIZE = 'size'
CONF_RESIZE = 'resize'
CONF_ROTATION = 'rotation'
CONF_DC_PIN = 'dc_pin'
CONF_RESET_PIN = 'reset_pin'
CONF_BUSY_PIN = 'busy_pin'
CONF_FULL_UPDATE_EVERY = 'full_update_every'
CONF_DATA_PINS = 'data_pins'
CONF_ENABLE_PIN = 'enable_pin'
CONF_RS_PIN = 'rs_pin'
CONF_RW_PIN = 'rw_pin'
CONF_DIMENSIONS = 'dimensions'
CONF_NUM_CHIPS = 'num_chips'
CONF_INTENSITY = 'intensity'
CONF_EXTERNAL_VCC = 'external_vcc'
CONF_TIMEZONE = 'timezone'
CONF_SERVERS = 'servers'
CONF_HEATER = 'heater'
CONF_VOLTAGE = 'voltage'
CONF_CURRENT_RESISTOR = 'current_resistor'
CONF_VOLTAGE_DIVIDER = 'voltage_divider'
CONF_SEL_PIN = 'sel_pin'
CONF_CF_PIN = 'cf_pin'
CONF_CF1_PIN = 'cf1_pin'
CONF_CHANGE_MODE_EVERY = 'change_mode_every'
CONF_PAGE_ID = 'page_id'
CONF_COMPONENT_ID = 'component_id'
CONF_COLD_WHITE = 'cold_white'
CONF_WARM_WHITE = 'warm_white'
CONF_COLD_WHITE_COLOR_TEMPERATURE = 'cold_white_color_temperature'
CONF_WARM_WHITE_COLOR_TEMPERATURE = 'warm_white_color_temperature'
CONF_ON_LOOP = 'on_loop'
CONF_ON_TIME = 'on_time'
CONF_SECONDS = 'seconds'
CONF_MINUTES = 'minutes'
CONF_HOURS = 'hours'
CONF_DAYS_OF_MONTH = 'days_of_month'
CONF_MONTHS = 'months'
CONF_DAYS_OF_WEEK = 'days_of_week'
CONF_CRON = 'cron'
CONF_POWER_SAVE_MODE = 'power_save_mode'
CONF_POWER_ON_VALUE = 'power_on_value'
CONF_PM_1_0 = 'pm_1_0'
CONF_PM_2_5 = 'pm_2_5'
CONF_PM_10_0 = 'pm_10_0'
CONF_FORMALDEHYDE = 'formaldehyde'
CONF_ON_TAG = 'on_tag'
CONF_ARGS = 'args'
CONF_FORMAT = 'format'
CONF_COLOR_CORRECT = 'color_correct'
CONF_ON_JSON_MESSAGE = 'on_json_message'
ESP32_BOARDS = [
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',
'pocket_32', 'espectro32', 'esp32vn-iot-uno', 'esp320', 'esp-wrover-kit',
'esp32dev', 'heltec_wifi_kit32', 'heltec_wifi_lora_32', 'hornbill32dev',
'hornbill32minima', 'intorobot', 'm5stack-core-esp32', 'mhetesp32devkit',
'mhetesp32minikit', 'nano32', 'microduino-core-esp32', 'nodemcu-32s',
'quantum', 'esp32-evb', 'esp32-gateway', 'onehorse32dev', 'esp32thing',
'espino32', 'lolin32', 'wemosbat', 'widora-air', 'nina_w10',
]
ESP8266_BOARDS = [
'gen4iod', 'huzzah', 'oak', 'espduino', 'espectro', 'espresso_lite_v1',
'espresso_lite_v2', 'espino', 'esp01', 'esp01_1m', 'esp07', 'esp12e', 'esp8285',
'esp_wroom_02', 'phoenix_v1', 'phoenix_v2', 'wifinfo', 'heltex_wifi_kit_8',
'nodemcu', 'nodemcuv2', 'modwifi', 'wio_node', 'sparkfunBlynk', 'thing',
'thingdev', 'esp210', 'espinotee', 'd1', 'd1_mini', 'd1_mini_lite', 'd1_mini_pro',
]
ESP_BOARDS_FOR_PLATFORM = {
ESP_PLATFORM_ESP32: ESP32_BOARDS,
ESP_PLATFORM_ESP8266: ESP8266_BOARDS
}
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
ARDUINO_VERSION_ESP8266_DEV = 'https://github.com/platformio/platform-espressif8266.git#feature' \
'/stage'

View File

@@ -1,4 +1,5 @@
import math
import re
from collections import OrderedDict
@@ -9,6 +10,8 @@ class ESPHomeYAMLError(Exception):
class HexInt(long):
def __str__(self):
if 0 <= self <= 255:
return "0x{:02X}".format(self)
return "0x{:X}".format(self)
@@ -31,6 +34,12 @@ class MACAddress(object):
def __str__(self):
return ':'.join('{:02X}'.format(part) for part in self.parts)
def as_hex(self):
import esphomeyaml.helpers
num = ''.join('{:02X}'.format(part) for part in self.parts)
return esphomeyaml.helpers.RawExpression('0x{}ULL'.format(num))
def is_approximately_integer(value):
if isinstance(value, (int, long)):
@@ -104,6 +113,21 @@ class TimePeriod(object):
out['days'] = self.days
return out
def __str__(self):
if self.microseconds is not None:
return '{} us'.format(self.total_microseconds)
if self.milliseconds is not None:
return '{} ms'.format(self.total_milliseconds)
if self.seconds is not None:
return '{} s'.format(self.total_seconds)
if self.minutes is not None:
return '{} min'.format(self.total_minutes)
if self.hours is not None:
return '{} h'.format(self.total_hours)
if self.days is not None:
return '{} d'.format(self.total_days)
return '0'
@property
def total_microseconds(self):
return self.total_milliseconds * 1000 + (self.microseconds or 0)
@@ -171,8 +195,66 @@ class TimePeriodSeconds(TimePeriod):
pass
class Lambda(object):
def __init__(self, value):
self.value = value
self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)', value)
self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)]
def __str__(self):
return self.value
def __repr__(self):
return u'Lambda<{}>'.format(self.value)
def ensure_unique_string(preferred_string, current_strings):
test_string = preferred_string
current_strings_set = set(current_strings)
tries = 1
while test_string in current_strings_set:
tries += 1
test_string = u"{}_{}".format(preferred_string, tries)
return test_string
class ID(object):
def __init__(self, id, is_declaration=False, type=None):
self.id = id
self.is_manual = id is not None
self.is_declaration = is_declaration
self.type = type
def resolve(self, registered_ids):
if self.id is None:
base = str(self.type).replace('::', '_').lower()
name = ''.join(c for c in base if c.isalnum() or c == '_')
self.id = ensure_unique_string(name, registered_ids)
return self.id
def __str__(self):
if self.id is None:
return ''
return self.id
def __repr__(self):
return u'ID<{} declaration={}, type={}, manual={}>'.format(
self.id, self.is_declaration, self.type, self.is_manual)
def __eq__(self, other):
if not isinstance(other, ID):
raise ValueError("other must be ID")
return self.id == other.id
def __hash__(self):
return hash(self.id)
CONFIG_PATH = None
SIMPLIFY = True
ESP_PLATFORM = ''
BOARD = ''
RAW_CONFIG = None
NAME = ''

258
esphomeyaml/core_config.py Normal file
View File

@@ -0,0 +1,258 @@
import logging
import os
import re
import subprocess
import voluptuous as vol
from esphomeyaml import automation, core, pins
import esphomeyaml.config_validation as cv
from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266_DEV, \
CONF_ARDUINO_VERSION, CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_BRANCH, CONF_BUILD_PATH, \
CONF_COMMIT, CONF_ESPHOMELIB_VERSION, CONF_ESPHOMEYAML, CONF_LOCAL, CONF_NAME, CONF_ON_BOOT, \
CONF_ON_LOOP, CONF_ON_SHUTDOWN, CONF_PLATFORM, CONF_PRIORITY, CONF_REPOSITORY, CONF_TAG, \
CONF_TRIGGER_ID, CONF_USE_CUSTOM_CODE, ESPHOMELIB_VERSION, ESP_PLATFORM_ESP32, \
ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, NoArg, Pvariable, RawExpression, add, const_char_p, \
esphomelib_ns, relative_path
_LOGGER = logging.getLogger(__name__)
LIBRARY_URI_REPO = u'https://github.com/OttoWinter/esphomelib.git'
BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout']
StartupTrigger = esphomelib_ns.StartupTrigger
ShutdownTrigger = esphomelib_ns.ShutdownTrigger
LoopTrigger = esphomelib_ns.LoopTrigger
VERSION_REGEX = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+(?:-beta)?(?:-alpha)?$')
def validate_board(value):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
board_pins = pins.ESP8266_BOARD_PINS
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
board_pins = pins.ESP32_BOARD_PINS
else:
raise NotImplementedError
if value not in board_pins:
raise vol.Invalid(u"Could not find board '{}'. Valid boards are {}".format(
value, u', '.join(pins.ESP8266_BOARD_PINS.keys())))
return value
def validate_simple_esphomelib_version(value):
value = cv.string_strict(value)
if value.upper() == 'LATEST':
return {
CONF_REPOSITORY: LIBRARY_URI_REPO,
CONF_TAG: 'v' + ESPHOMELIB_VERSION,
}
elif value.upper() == 'DEV':
return {
CONF_REPOSITORY: LIBRARY_URI_REPO,
CONF_BRANCH: 'master'
}
elif VERSION_REGEX.match(value) is not None:
return {
CONF_REPOSITORY: LIBRARY_URI_REPO,
CONF_TAG: 'v' + value,
}
raise vol.Invalid("Only simple esphomelib versions!")
def validate_local_esphomelib_version(value):
value = cv.directory(value)
path = relative_path(value)
library_json = os.path.join(path, 'library.json')
if not os.path.exists(library_json):
raise vol.Invalid(u"Could not find '{}' file. '{}' does not seem to point to an "
u"esphomelib copy.".format(library_json, value))
return value
def validate_commit(value):
value = cv.string(value)
if re.match(r"^[0-9a-f]{7,}$", value) is None:
raise vol.Invalid("Commit option only accepts commit hashes in hex format.")
return value
ESPHOMELIB_VERSION_SCHEMA = vol.Any(
validate_simple_esphomelib_version,
vol.Schema({
vol.Required(CONF_LOCAL): validate_local_esphomelib_version,
}),
vol.All(
vol.Schema({
vol.Optional(CONF_REPOSITORY, default=LIBRARY_URI_REPO): cv.string,
vol.Optional(CONF_COMMIT): validate_commit,
vol.Optional(CONF_BRANCH): cv.string,
vol.Optional(CONF_TAG): cv.string,
}),
cv.has_at_most_one_key(CONF_COMMIT, CONF_BRANCH, CONF_TAG)
),
)
def validate_platform(value):
value = cv.string(value)
if value.upper() in ('ESP8266', 'ESPRESSIF8266'):
return ESP_PLATFORM_ESP8266
if value.upper() in ('ESP32', 'ESPRESSIF32'):
return ESP_PLATFORM_ESP32
raise vol.Invalid(u"Invalid platform '{}'. Only options are ESP8266 and ESP32. Please note "
u"the old way to use the latest arduino framework version has been split up "
u"into the arduino_version configuration option.".format(value))
PLATFORMIO_ESP8266_LUT = {
'2.4.2': 'espressif8266@1.8.0',
'2.4.1': 'espressif8266@1.7.3',
'2.4.0': 'espressif8266@1.6.0',
'2.3.0': 'espressif8266@1.5.0',
'RECOMMENDED': 'espressif8266@>=1.8.0',
'LATEST': 'espressif8266',
'DEV': ARDUINO_VERSION_ESP8266_DEV,
}
PLATFORMIO_ESP32_LUT = {
'1.0.0': 'espressif32@1.4.0',
'RECOMMENDED': 'espressif32@>=1.4.0',
'LATEST': 'espressif32',
'DEV': ARDUINO_VERSION_ESP32_DEV,
}
def validate_arduino_version(value):
value = cv.string_strict(value)
value_ = value.upper()
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP8266_LUT:
raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported "
"at this time. You can override this by manually using "
"espressif8266@<platformio version>")
if value_ in PLATFORMIO_ESP8266_LUT:
return PLATFORMIO_ESP8266_LUT[value_]
return value
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT:
raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported "
"at this time. You can override this by manually using "
"espressif32@<platformio version>")
if value_ in PLATFORMIO_ESP32_LUT:
return PLATFORMIO_ESP32_LUT[value_]
return value
else:
raise NotImplementedError
def default_build_path():
return core.NAME
CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.valid_name,
vol.Required(CONF_PLATFORM): vol.All(vol.Upper, cv.one_of('ESP8266', 'ESPRESSIF8266',
'ESP32', 'ESPRESSIF32')),
vol.Required(CONF_BOARD): validate_board,
vol.Optional(CONF_ESPHOMELIB_VERSION, default='latest'): ESPHOMELIB_VERSION_SCHEMA,
vol.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version,
vol.Optional(CONF_USE_CUSTOM_CODE, default=False): cv.boolean,
vol.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string,
vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, cv.one_of(*BUILD_FLASH_MODES)),
vol.Optional(CONF_ON_BOOT): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StartupTrigger),
vol.Optional(CONF_PRIORITY): vol.Coerce(float),
}),
vol.Optional(CONF_ON_SHUTDOWN): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ShutdownTrigger),
}),
vol.Optional(CONF_ON_LOOP): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(LoopTrigger),
}),
vol.Optional('library_uri'): cv.invalid("The library_uri option has been removed in 1.8.0 and "
"was moved into the esphomelib_version option."),
vol.Optional('use_build_flags'): cv.invalid("The use_build_flags option has been replaced by "
"use_custom_code option in 1.8.0."),
})
def preload_core_config(config):
if CONF_ESPHOMEYAML not in config:
raise ESPHomeYAMLError(u"No esphomeyaml section in config")
core_conf = config[CONF_ESPHOMEYAML]
if CONF_PLATFORM not in core_conf:
raise ESPHomeYAMLError("esphomeyaml.platform not specified.")
if CONF_BOARD not in core_conf:
raise ESPHomeYAMLError("esphomeyaml.board not specified.")
if CONF_NAME not in core_conf:
raise ESPHomeYAMLError("esphomeyaml.name not specified.")
try:
core.ESP_PLATFORM = validate_platform(core_conf[CONF_PLATFORM])
core.BOARD = validate_board(core_conf[CONF_BOARD])
core.NAME = cv.valid_name(core_conf[CONF_NAME])
except vol.Invalid as e:
raise ESPHomeYAMLError(unicode(e))
def run_command(*args):
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
rc = p.returncode
return rc, stdout, stderr
def update_esphomelib_repo(config):
esphomelib_version = config[CONF_ESPHOMELIB_VERSION]
if CONF_REPOSITORY not in esphomelib_version:
return
build_path = relative_path(config[CONF_BUILD_PATH])
esphomelib_path = os.path.join(build_path, '.piolibdeps', 'esphomelib')
is_default_branch = all(x not in esphomelib_version
for x in (CONF_BRANCH, CONF_TAG, CONF_COMMIT))
if not (CONF_BRANCH in esphomelib_version or is_default_branch):
# Git commit hash or tag cannot be updated
return
rc, _, _ = run_command('git', '-C', esphomelib_path, '--help')
if rc != 0:
# git not installed or repo not downloaded yet
return
rc, _, _ = run_command('git', '-C', esphomelib_path, 'diff-index', '--quiet', 'HEAD', '--')
if rc != 0:
# local changes, cannot update
_LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.")
return
rc, _, _ = run_command('git', '-C', esphomelib_path, 'pull')
if rc != 0:
_LOGGER.warn("Couldn't auto-update local git copy of esphomelib.")
return
def to_code(config):
add(App.set_name(config[CONF_NAME]))
for conf in config.get(CONF_ON_BOOT, []):
rhs = App.register_component(StartupTrigger.new(conf.get(CONF_PRIORITY)))
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, NoArg, conf)
for conf in config.get(CONF_ON_SHUTDOWN, []):
trigger = Pvariable(conf[CONF_TRIGGER_ID], ShutdownTrigger.new())
automation.build_automation(trigger, const_char_p, conf)
for conf in config.get(CONF_ON_LOOP, []):
rhs = App.register_component(LoopTrigger.new())
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, NoArg, conf)
update_esphomelib_repo(config)
add(App.set_compilation_datetime(RawExpression('__DATE__ ", " __TIME__')))

View File

View File

@@ -0,0 +1,279 @@
# pylint: disable=wrong-import-position
from __future__ import print_function
import codecs
import hmac
import json
import logging
import os
import random
import subprocess
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_BUILD_PATH
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml import const, core, __main__
from esphomeyaml.__main__ import get_serial_ports
from esphomeyaml.helpers import quote, relative_path
try:
import tornado
import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.process
import tornado.web
import tornado.websocket
import tornado.concurrent
except ImportError as err:
tornado = None
_LOGGER = logging.getLogger(__name__)
CONFIG_DIR = ''
PASSWORD = ''
# pylint: disable=abstract-method
class BaseHandler(tornado.web.RequestHandler):
def is_authenticated(self):
return not PASSWORD or self.get_secure_cookie('authenticated') == 'yes'
# pylint: disable=abstract-method, arguments-differ
class EsphomeyamlCommandWebSocket(tornado.websocket.WebSocketHandler):
def __init__(self, application, request, **kwargs):
super(EsphomeyamlCommandWebSocket, self).__init__(application, request, **kwargs)
self.proc = None
self.closed = False
def on_message(self, message):
if PASSWORD and self.get_secure_cookie('authenticated') != 'yes':
return
if self.proc is not None:
return
command = self.build_command(message)
_LOGGER.debug(u"WebSocket opened for command %s", [quote(x) for x in command])
self.proc = tornado.process.Subprocess(command,
stdout=tornado.process.Subprocess.STREAM,
stderr=subprocess.STDOUT)
self.proc.set_exit_callback(self.proc_on_exit)
tornado.ioloop.IOLoop.current().spawn_callback(self.redirect_stream)
@tornado.gen.coroutine
def redirect_stream(self):
while True:
try:
data = yield self.proc.stdout.read_until_regex('[\n\r]')
except tornado.iostream.StreamClosedError:
break
if data.endswith('\r') and random.randrange(100) < 90:
continue
try:
data = data.replace('\033', '\\033')
except UnicodeDecodeError:
data = data.encode('ascii', 'backslashreplace')
self.write_message({'event': 'line', 'data': data})
def proc_on_exit(self, returncode):
if not self.closed:
_LOGGER.debug("Process exited with return code %s", returncode)
self.write_message({'event': 'exit', 'code': returncode})
def on_close(self):
self.closed = True
if self.proc is not None and self.proc.returncode is None:
_LOGGER.debug("Terminating process")
self.proc.proc.terminate()
def build_command(self, message):
raise NotImplementedError
class EsphomeyamlLogsHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = CONFIG_DIR + '/' + js['configuration']
return ["esphomeyaml", config_file, "logs", '--serial-port', js["port"], '--escape']
class EsphomeyamlRunHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "run", '--upload-port', js["port"],
'--escape', '--use-esptoolpy']
class EsphomeyamlCompileHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "compile"]
class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "config"]
class EsphomeyamlCleanMqttHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "clean-mqtt"]
class EsphomeyamlCleanHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "clean"]
class SerialPortRequestHandler(BaseHandler):
def get(self):
if not self.is_authenticated():
self.redirect('/login')
return
ports = get_serial_ports()
data = []
for port, desc in ports:
if port == '/dev/ttyAMA0':
desc = 'UART pins on GPIO header'
split_desc = desc.split(' - ')
if len(split_desc) == 2 and split_desc[0] == split_desc[1]:
# Some serial ports repeat their values
desc = split_desc[0]
data.append({'port': port, 'desc': desc})
data.append({'port': 'OTA', 'desc': 'Over-The-Air'})
self.write(json.dumps(sorted(data, reverse=True)))
class WizardRequestHandler(BaseHandler):
def post(self):
from esphomeyaml import wizard
if not self.is_authenticated():
self.redirect('/login')
return
kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()}
config = wizard.wizard_file(**kwargs)
destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
with codecs.open(destination, 'w') as f_handle:
f_handle.write(config)
self.redirect('/?begin=True')
class DownloadBinaryRequestHandler(BaseHandler):
def get(self):
if not self.is_authenticated():
self.redirect('/login')
return
configuration = self.get_argument('configuration')
config_file = os.path.join(CONFIG_DIR, configuration)
core.CONFIG_PATH = config_file
config = __main__.read_config(core.CONFIG_PATH)
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
self.set_header('Content-Type', 'application/octet-stream')
self.set_header("Content-Disposition", 'attachment; filename="{}.bin"'.format(core.NAME))
with open(path, 'rb') as f:
while 1:
data = f.read(16384) # or some other nice-sized chunk
if not data:
break
self.write(data)
self.finish()
class MainRequestHandler(BaseHandler):
def get(self):
if not self.is_authenticated():
self.redirect('/login')
return
begin = bool(self.get_argument('begin', False))
files = sorted([f for f in os.listdir(CONFIG_DIR) if f.endswith('.yaml') and
not f.startswith('.')])
full_path_files = [os.path.join(CONFIG_DIR, f) for f in files]
self.render("templates/index.html", files=files, full_path_files=full_path_files,
version=const.__version__, begin=begin)
class LoginHandler(BaseHandler):
def get(self):
self.write('<html><body><form action="/login" method="post">'
'Password: <input type="password" name="password">'
'<input type="submit" value="Sign in">'
'</form></body></html>')
def post(self):
password = str(self.get_argument("password", ''))
password = hmac.new(password).digest()
if hmac.compare_digest(PASSWORD, password):
self.set_secure_cookie("authenticated", "yes")
self.redirect("/")
def make_app(debug=False):
static_path = os.path.join(os.path.dirname(__file__), 'static')
return tornado.web.Application([
(r"/", MainRequestHandler),
(r"/login", LoginHandler),
(r"/logs", EsphomeyamlLogsHandler),
(r"/run", EsphomeyamlRunHandler),
(r"/compile", EsphomeyamlCompileHandler),
(r"/validate", EsphomeyamlValidateHandler),
(r"/clean-mqtt", EsphomeyamlCleanMqttHandler),
(r"/clean", EsphomeyamlCleanHandler),
(r"/download.bin", DownloadBinaryRequestHandler),
(r"/serial-ports", SerialPortRequestHandler),
(r"/wizard.html", WizardRequestHandler),
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}),
], debug=debug, cookie_secret=PASSWORD)
def start_web_server(args):
global CONFIG_DIR
global PASSWORD
if tornado is None:
raise ESPHomeYAMLError("Attempted to load dashboard, but tornado is not installed! "
"Please run \"pip2 install tornado esptool\" in your terminal.")
CONFIG_DIR = args.configuration
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR)
# HassIO options storage
PASSWORD = args.password
if os.path.isfile('/data/options.json'):
with open('/data/options.json') as f:
js = json.load(f)
PASSWORD = js.get('password') or PASSWORD
if PASSWORD:
PASSWORD = hmac.new(str(PASSWORD)).digest()
# Use the digest of the password as our cookie secret. This makes sure the cookie
# isn't too short. It, of course, enables local hash brute forcing (because the cookie
# secret can be brute forced without making requests). But the hashing algorithm used
# by tornado is apparently strong enough to make brute forcing even a short string pretty
# hard.
_LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...",
args.port, CONFIG_DIR)
app = make_app(args.verbose)
app.listen(args.port)
if args.open_ui:
import webbrowser
webbrowser.open('localhost:{}'.format(args.port))
try:
tornado.ioloop.IOLoop.current().start()
except KeyboardInterrupt:
_LOGGER.info("Shutting down...")

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,962 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>esphomeyaml Dashboard</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
<link rel="stylesheet" href="/static/materialize-stepper.min.css">
<!-- jQuery :( -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.8.5/jquery-ui.min.js" integrity="sha256-fOse6WapxTrUSJOJICXXYwHRJOPa6C1OUQXi7C9Ddy8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/jquery.validate.min.js"></script>
<script src="/static/materialize-stepper.min.js"></script>
<style>
nav .brand-logo {
margin-left: 48px;
font-size: 20px;
}
main .container {
margin-top: -12vh;
flex-shrink: 0;
}
.ribbon {
width: 100%;
height: 17vh;
background-color: #3F51B5;
flex-shrink: 0;
}
.ribbon-fab:not(.tap-target-origin) {
position: absolute;
right: 24px;
top: calc(17vh + 34px);
}
i.very-large {
font-size: 8rem;
padding-top: 2px;
color: #424242;
}
.card .card-content {
padding-left: 18px;
padding-bottom: 10px;
}
.log {
background-color: #1c1c1c;
margin-top: 0;
margin-bottom: 0;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
padding: 16px;
overflow: auto;
line-height: 1.45;
border-radius: 3px;
white-space: pre-wrap;
overflow-wrap: break-word;
color: #DDD;
}
.inlinecode {
box-sizing: border-box;
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,0.05);
border-radius: 3px;
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
}
.log.bold {
font-weight: bold;
}
.log .v {
color: #888888;
}
.log .d {
color: #00DDDD;
}
.log .c {
color: magenta;
}
.log .i {
color: limegreen;
}
.log .w {
color: yellow;
}
.log .e {
color: red;
font-weight: bold;
}
.log .e {
color: red;
}
.log .ww {
color: white;
}
.modal {
width: 95%;
max-height: 90%;
height: 85% !important;
}
.page-footer {
padding-top: 0;
}
body {
display: flex;
min-height: 100vh;
flex-direction: column;
}
main {
flex: 1 0 auto;
}
ul.browser-default {
padding-left: 30px;
margin-top: 10px;
margin-bottom: 15px;
}
ul.browser-default li {
list-style-type: initial;
}
ul.stepper:not(.horizontal) .step.active::before, ul.stepper:not(.horizontal) .step.done::before, ul.stepper.horizontal .step.active .step-title::before, ul.stepper.horizontal .step.done .step-title::before {
background-color: #3f51b5 !important;
}
.select-port-container {
margin-top: 8px;
margin-right: 24px;
width: 350px;
}
.dropdown-trigger {
cursor: pointer;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<header>
<nav>
<div class="nav-wrapper indigo">
<a href="#" class="brand-logo left">esphomeyaml Dashboard</a>
<div class="select-port-container right" id="select-port-target">
<select></select>
</div>
</div>
</nav>
<div class="tap-target pink lighten-1 select-port" data-target="select-port-target">
<div class="tap-target-content">
<h5>Select Upload Port</h5>
<p>
Here you can select where esphomeyaml will attempt to show logs and upload firmwares to.
By default, this is "OTA", or Over-The-Air. Note that you might have to restart the HassIO add-on
for new serial ports to be detected.
</p>
</div>
</div>
<div class="ribbon"></div>
</header>
<main>
<div class="container">
{% for i, (file, full_path) in enumerate(zip(files, full_path_files)) %}
<div class="row">
<div class="col s8 offset-s2 m10 offset-m1 l12">
<div class="card horizontal">
<div class="card-image center-align">
<i class="material-icons very-large icon-grey">memory</i>
</div>
<div class="card-stacked">
<div class="card-content">
<span class="card-title">
{{ escape(file) }}
<i class="material-icons right dropdown-trigger" data-target="dropdown-{{ i }}">more_vert</i>
</span>
<p>
Full path: <code class="inlinecode">{{ escape(full_path) }}</code>
</p>
</div>
<div class="card-action">
<a href="#" class="action-upload" data-node="{{ file }}">Upload</a>
<a href="#" class="action-compile" data-node="{{ file }}">Compile</a>
<a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a>
<a href="#" class="action-validate" data-node="{{ file }}">Validate</a>
</div>
<ul id="dropdown-{{ i }}" class="dropdown-content">
<li><a href="#" class="action-clean-mqtt" data-node="{{ file }}">Clean MQTT</a></li>
<li><a href="#" class="action-clean" data-node="{{ file }}">Clean Build</a></li>
</ul>
</div>
</div>
</div>
</div>
{% end %}
</div>
<div id="modal-logs" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Show Logs <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Close</a>
</div>
</div>
<div id="modal-upload" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Compile And Upload <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<div id="modal-compile" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Compile <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat disabled download-binary">Download Binary</a>
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<div id="modal-validate" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Validate <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<div id="modal-wizard" class="modal">
<div class="modal-content">
<form action="/wizard.html" method="POST">
<ul class="stepper linear">
<li class="step active">
<div class="step-title waves-effect">Introduction And Name</div>
<div class="step-content">
<div class="row">
<p>
Hi there! I'm the esphomeyaml setup wizard and will guide you through setting up
your first ESP8266 or ESP32-powered device using esphomeyaml.
</p>
<a href="https://www.espressif.com/en/products/hardware/esp8266ex/overview" target="_blank">ESP8266s</a> and
their successors (the <a href="https://www.espressif.com/en/products/hardware/esp32/overview" target="_blank">ESP32s</a>)
are great low-cost microcontrollers that can communicate with the outside world using WiFi.
They're found in many devices such as the popular Sonoff/iTead, but also exist as development boards
such as the <a href="https://esphomelib.com/esphomeyaml/devices/nodemcu_esp8266.html" target="_blank">NodeMCU</a>.
<p>
</p>
<a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml</a>,
the tool you're using here, creates custom firmwares for these devices using YAML configuration
files (similar to the ones you might be used to with Home Assistant).
<p>
</p>
This wizard will create a basic YAML configuration file for your "node" (the microcontroller).
Later, you will be able to customize this file and add some of
<a href="https://github.com/OttoWinter/esphomelib" target="_blank">esphomelib's</a>
many integrations.
<p>
<p>
First, I need to know what this node should be called. Choose this name wisely, changing this
later makes Over-The-Air Update attempts difficult.
Names must be <strong>lowercase</strong> and <strong>must not contain spaces</strong> (allowed characters: <code class="inlinecode">a-z</code>,
<code class="inlinecode">0-9</code> and <code class="inlinecode">_</code>)
</p>
<div class="input-field col s12">
<input id="node_name" class="validate" type="text" name="name" required>
<label for="node_name">Name of node</label>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect">Device Type</div>
<div class="step-content">
<div class="row">
<p>
Great! Now I need to know what type of microcontroller you're using so that I can compile firmware for them.
Please choose either ESP32 or ESP8266 (use ESP8266 for Sonoff devices).
</p>
<div class="input-field col s12">
<select id="esp_type" name="platform" required>
<option value="ESP8266">ESP8266</option>
<option value="ESP32">ESP32</option>
</select>
<label>Microcontroller Type</label>
</div>
<p>
I'm also going to need to know which type of board you're using. Please go to
<a href="http://docs.platformio.org/en/latest/platforms/espressif32.html#boards" target="_blank">ESP32 boards</a> or
<a href="http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards" target="_blank">ESP8266 boards</a>,
find your board and enter it here. For example, enter <code class="inlinecode">nodemcuv2</code>
for ESP8266 NodeMCU boards. Note: Use <code class="inlinecode">esp01_1m</code> for Sonoff devices.
</p>
<div class="input-field col s12">
<input id="board_type" class="validate" type="text" name="board" required>
<label for="board_type">Board Type</label>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect">WiFi And Over-The-Air Updates</div>
<div class="step-content">
<div class="row">
<p>
Thanks! Now I need to know what WiFi Access Point I should instruct the node to connect to.
Please enter an SSID (name of the WiFi network) and password (leave empty for no password).
</p>
<div class="input-field col s12">
<input id="wifi_ssid" class="validate" type="text" name="ssid" required>
<label for="wifi_ssid">WiFi SSID</label>
</div>
<div class="input-field col s12">
<input id="wifi_password" name="psk" type="password">
<label for="wifi_password">WiFi Password</label>
</div>
<p>
Esphomelib automatically sets up an Over-The-Air update server on the node
so that you only need to flash a firmware via USB once.
Optionally, you can set a password for this upload process here:
</p>
<div class="input-field col s12">
<input id="ota_password" class="validate" name="ota_password" type="password">
<label for="ota_password">OTA Password</label>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect">MQTT</div>
<div class="step-content">
<div class="row">
<p>
esphomelib connects to your Home Assistant instance via
<a href="https://www.home-assistant.io/docs/mqtt/">MQTT</a>. If you haven't already, please set up
MQTT on your Home Assistant server, for example with the awesome
<a href="https://www.home-assistant.io/addons/mosquitto/">Mosquitto Hass.io Add-on</a>.
</p>
<p>
When you're done with that, please enter your MQTT broker here. For example
<code class="inlinecode">192.168.1.100</code> (Note
<code class="inlinecode">hassio.local</code> doesn't always work, please use a static IP).
Please also specify the MQTT username and password you wish esphomelib to use
(leave them empty if you're not using any authentication).
</p>
<div class="input-field col s12">
<input id="mqtt_broker" class="validate" type="text" name="broker" required>
<label for="mqtt_broker">MQTT Broker</label>
</div>
<div class="input-field col s6">
<input id="mqtt_username" class="validate" type="text" name="mqtt_username">
<label for="mqtt_username">MQTT Username</label>
</div>
<div class="input-field col s6">
<input id="mqtt_password" class="validate" name="mqtt_password" type="password">
<label for="mqtt_password">MQTT Password</label>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn indigo next-step">CONTINUE</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect">Done!</div>
<div class="step-content">
<p>
Hooray! 🎉🎉🎉 You've successfully created your first esphomeyaml configuration file.
When you click Submit, I will save this configuration file under
<code class="inlinecode">&lt;HASS_CONFIG_FOLDER&gt;/esphomeyaml/&lt;NAME_OF_NODE&gt;.yaml</code> and
you will be able to edit this file with the
<a href="https://www.home-assistant.io/addons/configurator/" target="_blank">HASS Configuratior add-on</a>.
</p>
<h5>Next steps</h5>
<ul class="browser-default">
<li>
Flash the firmware. This can be done using the “UPLOAD” option in the dashboard. See
<a href="https://esphomelib.com/esphomeyaml/index.html#devices" target="_blank">this</a>
for guides on how to flash different types of devices. Note that you need to restart this add-on
for newly plugged in serial devices to be detected.
</li>
<li>
With the current configuration, your node will only connect to WiFi and MQTT. To make it actually <i>do</i>
stuff, follow
<a href="https://esphomelib.com/esphomeyaml/guides/getting_started_hassio.html#adding-some-basic-features">
the rest of the getting started guide
</a>.
</li>
<li>
See the <a href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml index</a>
for a list of supported sensors/devices.
</li>
<li>
Join the <a href="https://discord.gg/KhAMKrd" target="_blank">Discord server</a> and say hi! When I
have time, I would be happy to help with issues and discuss new features.
</li>
<li>
Star <a href="https://github.com/OttoWinter/esphomelib" target="_blank">esphomelib</a> and
<a href="https://github.com/OttoWinter/esphomeyaml" target="_blank">esphomeyaml</a> on GitHub
if you find this software awesome and report issues using the bug trackers there.
</li>
</ul>
<div class="step-actions">
<button class="waves-effect waves-dark btn indigo" type="submit">SUBMIT</button>
</div>
</div>
</li>
</ul>
</form>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-green btn-flat">Abort</a>
</div>
</div>
<div id="modal-clean-mqtt" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Clean MQTT discovery <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<div id="modal-clean" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Clean Build Files <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<a class="btn-floating btn-large ribbon-fab waves-effect waves-light pink accent-2" id="setup-wizard-start">
<i class="material-icons">add</i>
</a>
<div class="tap-target pink lighten-1 setup-wizard" data-target="setup-wizard-start">
<div class="tap-target-content">
<h5>Set up your first Node</h5>
<p>
Huh... It seems like you you don't have any esphomeyaml configuration files yet...
Fortunately, there's a setup wizard that will step you through setting up your first node 🎉
</p>
</div>
</div>
</main>
<footer class="page-footer indigo darken-1">
<div class="container">
</div>
<div class="footer-copyright">
<div class="container">
© 2018 Copyright Otto Winter, Made with <a class="grey-text text-lighten-4" href="https://materializecss.com/" target="_blank">Materialize</a>
<a class="grey-text text-lighten-4 right" href="https://esphomelib.com/esphomeyaml/index.html" target="_blank">esphomeyaml {{ version }} Documentation</a>
</div>
</div>
</footer>
<script>
document.addEventListener('DOMContentLoaded', () => {
M.AutoInit(document.body);
});
const colorReplace = (input) => {
input = input.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
input = input.replace(/\\033\[(?:0;)?31m/g, '<span class="e">');
input = input.replace(/\\033\[(?:1;)?31m/g, '<span class="e bold">');
input = input.replace(/\\033\[(?:0;)?32m/g, '<span class="i">');
input = input.replace(/\\033\[(?:1;)?32m/g, '<span class="i bold">');
input = input.replace(/\\033\[(?:0;)?33m/g, '<span class="w">');
input = input.replace(/\\033\[(?:1;)?33m/g, '<span class="w bold">');
input = input.replace(/\\033\[(?:0;)?35m/g, '<span class="c">');
input = input.replace(/\\033\[(?:1;)?35m/g, '<span class="c bold">');
input = input.replace(/\\033\[(?:0;)?36m/g, '<span class="d">');
input = input.replace(/\\033\[(?:1;)?36m/g, '<span class="d bold">');
input = input.replace(/\\033\[(?:0;)?37m/g, '<span class="v">');
input = input.replace(/\\033\[(?:1;)?37m/g, '<span class="v bold">');
input = input.replace(/\\033\[(?:0;)?38m/g, '<span class="vv">');
input = input.replace(/\\033\[(?:1;)?38m/g, '<span class="vv bold">');
input = input.replace(/\\033\[0m/g, '</span>');
return input;
};
let configuration = "";
let wsProtocol = "ws:";
if (window.location.protocol === "https:") {
wsProtocol = 'wss:';
}
const wsUrl = wsProtocol + '//' + window.location.hostname + ':' + window.location.port;
const portSelect = document.querySelector('.nav-wrapper select');
let ports = [];
const fetchSerialPorts = (begin=false) => {
fetch('/serial-ports', {credentials: "same-origin"}).then(res => res.json())
.then(response => {
if (ports.length === response.length) {
let allEqual = true;
for (let i = 0; i < response.length; i++) {
if (ports[i].port !== response[i].port) {
allEqual = false;
break;
}
}
if (allEqual)
return;
}
const hasNewPort = response.length >= ports.length;
ports = response;
const inst = M.FormSelect.getInstance(portSelect);
if (inst !== undefined) {
inst.destroy();
}
portSelect.innerHTML = "";
const prevSelected = getUploadPort();
for (let i = 0; i < response.length; i++) {
const val = response[i];
if (val.port === prevSelected) {
portSelect.innerHTML += `<option value="${val.port}" selected>${val.port} (${val.desc})</option>`;
} else {
portSelect.innerHTML += `<option value="${val.port}">${val.port} (${val.desc})</option>`;
}
}
M.FormSelect.init(portSelect, {});
if (!begin && hasNewPort)
M.toast({html: "Discovered new serial port."});
});
};
const getUploadPort = () => {
const inst = M.FormSelect.getInstance(portSelect);
if (inst === undefined) {
return "OTA";
}
inst._setSelectedStates();
return inst.getSelectedValues()[0];
};
setInterval(fetchSerialPorts, 5000);
fetchSerialPorts(true);
const logsModalElem = document.getElementById("modal-logs");
document.querySelectorAll(".action-show-logs").forEach((showLogs) => {
showLogs.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(logsModalElem);
const log = logsModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = logsModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
modalInstance.open();
const filenameField = logsModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/logs");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
if (data.code === 0) {
M.toast({html: "Program exited successfully."});
} else {
M.toast({html: `Program failed with code ${data.code}`});
}
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
const uploadModalElem = document.getElementById("modal-upload");
document.querySelectorAll(".action-upload").forEach((upload) => {
upload.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(uploadModalElem);
const log = uploadModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = uploadModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
modalInstance.open();
const filenameField = uploadModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/run");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
if (data.code === 0) {
M.toast({html: "Program exited successfully."});
} else {
M.toast({html: `Program failed with code ${data.code}`});
}
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration, port: getUploadPort()});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
const validateModalElem = document.getElementById("modal-validate");
document.querySelectorAll(".action-validate").forEach((upload) => {
upload.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(validateModalElem);
const log = validateModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = validateModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
modalInstance.open();
const filenameField = validateModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/validate");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
if (data.code === 0) {
M.toast({
html: `<code class="inlinecode">${configuration}</code> is valid 👍`,
displayLength: 5000,
});
} else {
M.toast({
html: `<code class="inlinecode">${configuration}</code> is invalid 😕`,
displayLength: 5000,
});
}
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
const compileModalElem = document.getElementById("modal-compile");
const downloadButton = compileModalElem.querySelector('.download-binary');
document.querySelectorAll(".action-compile").forEach((upload) => {
upload.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(compileModalElem);
const log = compileModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = compileModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
downloadButton.classList.add('disabled');
modalInstance.open();
const filenameField = compileModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/compile");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
if (data.code === 0) {
M.toast({html: "Program exited successfully."});
downloadButton.classList.remove('disabled');
} else {
M.toast({html: `Program failed with code ${data.code}`});
}
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
downloadButton.addEventListener('click', () => {
const link = document.createElement("a");
link.download = name;
link.href = '/download.bin?configuration=' + encodeURIComponent(configuration);
link.click();
});
const cleanMqttModalElem = document.getElementById("modal-clean-mqtt");
document.querySelectorAll(".action-clean-mqtt").forEach((btn) => {
btn.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(cleanMqttModalElem);
const log = cleanMqttModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = cleanMqttModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
modalInstance.open();
const filenameField = cleanMqttModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/clean-mqtt");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
const cleanModalElem = document.getElementById("modal-clean");
document.querySelectorAll(".action-clean").forEach((btn) => {
btn.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(cleanModalElem);
const log = cleanModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = cleanModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
modalInstance.open();
const filenameField = cleanModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/clean");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
if (data.code === 0) {
M.toast({html: "Program exited successfully."});
downloadButton.classList.remove('disabled');
} else {
M.toast({html: `Program failed with code ${data.code}`});
}
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
const modalSetupElem = document.getElementById("modal-wizard");
const setupWizardStart = document.getElementById('setup-wizard-start');
const startWizard = () => {
const modalInstance = M.Modal.getInstance(modalSetupElem);
modalInstance.open();
modalInstance.options.onCloseStart = () => {
};
$('.stepper').activateStepper({
linearStepsNavigation: false,
autoFocusInput: true,
autoFormCreation: true,
showFeedbackLoader: true,
parallel: false
});
};
setupWizardStart.addEventListener('click', startWizard);
</script>
{% if len(files) == 0 %}
<script>
document.addEventListener('DOMContentLoaded', () => {
const tapTargetElem = document.querySelector('.tap-target.setup-wizard');
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
tapTargetInstance.options.onOpen = () => {
$('.tap-target-origin').on('click', () => {
startWizard();
});
};
tapTargetInstance.open();
});
</script>
{% end %}
{% if begin %}
<script>
window.history.replaceState({}, document.title, "/");
document.addEventListener('DOMContentLoaded', () => {
const tapTargetElem = document.querySelector('.tap-target.select-port');
const tapTargetInstance = M.TapTarget.getInstance(tapTargetElem);
tapTargetInstance.open();
tapTargetInstance.contentEl.style["top"] = "300px";
tapTargetInstance.contentEl.style["padding"] = "250px";
tapTargetInstance.waveEl.style["top"] = "250px";
tapTargetInstance.waveEl.style["left"] = "250px";
tapTargetInstance.waveEl.style["width"] = "300px";
tapTargetInstance.waveEl.style["height"] = "300px";
});
</script>
{% end %}
</body>
</html>

230
esphomeyaml/espota2.py Executable file
View 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)

View File

@@ -1,6 +1,8 @@
from __future__ import print_function
import inspect
import logging
import os
import re
from collections import OrderedDict, deque
@@ -9,7 +11,7 @@ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOV
CONF_INVERTED, \
CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PCF8574, \
CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC
from esphomeyaml.core import ESPHomeYAMLError, HexInt, TimePeriodMicroseconds, \
from esphomeyaml.core import ESPHomeYAMLError, HexInt, Lambda, TimePeriodMicroseconds, \
TimePeriodMilliseconds, TimePeriodSeconds
_LOGGER = logging.getLogger(__name__)
@@ -58,6 +60,9 @@ class Expression(object):
continue
require.require()
def has_side_effects(self):
return self.required
class RawExpression(Expression):
def __init__(self, text):
@@ -65,7 +70,7 @@ class RawExpression(Expression):
self.text = text
def __str__(self):
return self.text
return str(self.text)
# pylint: disable=redefined-builtin
@@ -81,10 +86,11 @@ class AssignmentExpression(Expression):
def __str__(self):
type_ = self.type
if core.SIMPLIFY:
type_ = u'auto'
return u"{} {}{} = {}".format(type_, self.modifier, self.name, self.rhs)
def has_side_effects(self):
return self.rhs.has_side_effects()
class ExpressionList(Expression):
def __init__(self, *args):
@@ -137,6 +143,8 @@ class StructInitializer(Expression):
def __init__(self, base, *args):
super(StructInitializer, self).__init__()
self.base = base
if isinstance(base, Expression):
self.requires.append(base)
if not isinstance(args, OrderedDict):
args = OrderedDict(args)
self.args = OrderedDict()
@@ -180,6 +188,58 @@ class ArrayInitializer(Expression):
return cpp
# pylint: disable=invalid-name
class ParameterExpression(Expression):
def __init__(self, type, id):
super(ParameterExpression, self).__init__()
self.type = type
self.id = id
def __str__(self):
return u"{} {}".format(self.type, self.id)
class ParameterListExpression(Expression):
def __init__(self, *parameters):
super(ParameterListExpression, self).__init__()
self.parameters = []
for parameter in parameters:
if not isinstance(parameter, ParameterExpression):
parameter = ParameterExpression(*parameter)
self.parameters.append(parameter)
self.requires.append(parameter)
def __str__(self):
return u", ".join(unicode(x) for x in self.parameters)
class LambdaExpression(Expression):
def __init__(self, parts, parameters, capture='=', return_type=None):
super(LambdaExpression, self).__init__()
self.parts = parts
if not isinstance(parameters, ParameterListExpression):
parameters = ParameterListExpression(*parameters)
self.parameters = parameters
self.requires.append(self.parameters)
self.capture = capture
self.return_type = return_type
if return_type is not None:
self.requires.append(return_type)
for i in range(1, len(parts), 3):
self.requires.append(parts[i])
def __str__(self):
cpp = u'[{}]({})'.format(self.capture, self.parameters)
if self.return_type is not None:
cpp += u' -> {}'.format(self.return_type)
cpp += u' {{\n{}\n}}'.format(self.content)
return indent_all_but_first_and_last(cpp)
@property
def content(self):
return u''.join(unicode(part) for part in self.parts)
class Literal(Expression):
def __str__(self):
raise NotImplementedError
@@ -213,6 +273,12 @@ class IntLiteral(Literal):
self.i = i
def __str__(self):
if self.i > 4294967295:
return u'{}ULL'.format(self.i)
if self.i > 2147483647:
return u'{}UL'.format(self.i)
if self.i < -2147483648:
return u'{}LL'.format(self.i)
return unicode(self.i)
@@ -235,9 +301,9 @@ class HexIntLiteral(Literal):
class FloatLiteral(Literal):
def __init__(self, float_):
def __init__(self, value):
super(FloatLiteral, self).__init__()
self.float_ = float_
self.float_ = value
def __str__(self):
return u"{:f}f".format(self.float_)
@@ -297,83 +363,141 @@ def statement(expression):
return ExpressionStatement(expression)
def register_variable(id, obj):
_LOGGER.debug("Registered variable %s of type %s", id.id, id.type)
_VARIABLES[id] = obj
# pylint: disable=redefined-builtin, invalid-name
def variable(type, id, rhs):
def variable(id, rhs, type=None):
rhs = safe_exp(rhs)
obj = MockObj(id, u'.')
assignment = AssignmentExpression(type, '', id, rhs, obj)
id.type = type or id.type
assignment = AssignmentExpression(id.type, '', id, rhs, obj)
add(assignment)
_VARIABLES[id] = obj, type
register_variable(id, obj)
obj.requires.append(assignment)
return obj
def Pvariable(type, id, rhs):
def Pvariable(id, rhs, has_side_effects=True, type=None):
rhs = safe_exp(rhs)
obj = MockObj(id, u'->')
assignment = AssignmentExpression(type, '*', id, rhs, obj)
if not has_side_effects and hasattr(rhs, '_has_side_effects'):
# pylint: disable=attribute-defined-outside-init, protected-access
rhs._has_side_effects = False
obj = MockObj(id, u'->', has_side_effects=has_side_effects)
id.type = type or id.type
assignment = AssignmentExpression(id.type, '*', id, rhs, obj)
add(assignment)
_VARIABLES[id] = obj, type
register_variable(id, obj)
obj.requires.append(assignment)
return obj
_QUEUE = deque()
_TASKS = deque()
_VARIABLES = {}
_EXPRESSIONS = []
def get_variable(id, type=None):
result = None
while _QUEUE:
if id is not None:
if id in _VARIABLES:
result = _VARIABLES[id][0]
break
elif type is not None:
result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
if result is not None:
break
func, config = _QUEUE.popleft()
func(config)
if id is None and type is None:
return None
if result is None:
if id is not None:
if id in _VARIABLES:
result = _VARIABLES[id][0]
elif type is not None:
result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
if result is None:
raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type))
return result
def get_variable(id):
while True:
if id in _VARIABLES:
yield _VARIABLES[id]
return
_LOGGER.debug("Waiting for variable %s", id)
yield None
def add_task(func, config):
_QUEUE.append((func, config))
def process_lambda(value, parameters, capture='=', return_type=None):
if value is None:
yield
return
parts = value.parts[:]
for i, id in enumerate(value.requires_ids):
var = None
for var in get_variable(id):
yield
if parts[i * 3 + 2] == '.':
parts[i * 3 + 1] = var._
else:
parts[i * 3 + 1] = var
parts[i * 3 + 2] = ''
yield LambdaExpression(parts, parameters, capture, return_type)
def templatable(value, input_type, output_type):
if isinstance(value, Lambda):
lambda_ = None
for lambda_ in process_lambda(value, [(input_type, 'x')], return_type=output_type):
yield None
yield lambda_
else:
yield value
def add_job(func, *args, **kwargs):
domain = kwargs.get('domain')
if inspect.isgeneratorfunction(func):
def func_():
yield
for _ in func(*args):
yield
else:
def func_():
yield
func(*args)
gen = func_()
_TASKS.append((gen, domain))
return gen
def flush_tasks():
i = 0
while _TASKS:
i += 1
if i > 1000000:
raise ESPHomeYAMLError("Circular dependency detected!")
task, domain = _TASKS.popleft()
_LOGGER.debug("Executing task for domain=%s", domain)
try:
task.next()
_TASKS.append((task, domain))
except StopIteration:
_LOGGER.debug(" -> %s finished", domain)
def add(expression, require=True):
if require and isinstance(expression, Expression):
expression.require()
_EXPRESSIONS.append(expression)
_LOGGER.debug("Adding: %s", statement(expression))
return expression
class MockObj(Expression):
def __init__(self, base, op=u'.'):
def __init__(self, base, op=u'.', has_side_effects=True):
self.base = base
self.op = op
self._has_side_effects = has_side_effects
super(MockObj, self).__init__()
def __getattr__(self, attr):
if attr == u'_':
obj = MockObj(u'{}{}'.format(self.base, self.op))
obj.requires.append(self)
return obj
if attr == u'new':
obj = MockObj(u'new {}'.format(self.base), u'->')
obj.requires.append(self)
return obj
next_op = u'.'
if attr.startswith(u'P'):
if attr.startswith(u'P') and self.op not in ['::', '']:
attr = attr[1:]
next_op = u'->'
op = self.op
obj = MockObj(u'{}{}{}'.format(self.base, op, attr), next_op)
if attr.startswith(u'_'):
attr = attr[1:]
obj = MockObj(u'{}{}{}'.format(self.base, self.op, attr), next_op)
obj.requires.append(self)
return obj
@@ -394,12 +518,68 @@ class MockObj(Expression):
continue
require.require()
def template(self, args):
if not isinstance(args, TemplateArguments):
args = TemplateArguments(args)
obj = MockObj(u'{}{}'.format(self.base, args))
obj.requires.append(self)
obj.requires.append(args)
return obj
App = MockObj(u'App')
def namespace(self, name):
obj = MockObj(u'{}{}{}'.format(self.base, self.op, name), u'::')
obj.requires.append(self)
return obj
GPIOPin = MockObj(u'GPIOPin')
GPIOOutputPin = MockObj(u'GPIOOutputPin')
GPIOInputPin = MockObj(u'GPIOInputPin')
def operator(self, name):
if name == 'ref':
obj = MockObj(u'{} &'.format(self.base), u'')
obj.requires.append(self)
return obj
if name == "const":
obj = MockObj(u'const {}'.format(self.base), u'')
obj.requires.append(self)
return obj
raise NotImplementedError()
def has_side_effects(self):
return self._has_side_effects
def __getitem__(self, item):
next_op = u'.'
if isinstance(item, str) and item.startswith(u'P'):
item = item[1:]
next_op = u'->'
obj = MockObj(u'{}[{}]'.format(self.base, item), next_op)
obj.requires.append(self)
if isinstance(item, Expression):
obj.requires.append(item)
return obj
global_ns = MockObj('', '')
float_ = global_ns.namespace('float')
bool_ = global_ns.namespace('bool')
std_ns = global_ns.namespace('std')
std_string = std_ns.string
uint8 = global_ns.namespace('uint8_t')
uint16 = global_ns.namespace('uint16_t')
uint32 = global_ns.namespace('uint32_t')
const_char_p = global_ns.namespace('const char *')
NAN = global_ns.namespace('NAN')
esphomelib_ns = global_ns # using namespace esphomelib;
NoArg = esphomelib_ns.NoArg
App = esphomelib_ns.App
Application = esphomelib_ns.namespace('Application')
optional = esphomelib_ns.optional
arduino_json_ns = global_ns.namespace('ArduinoJson')
JsonObject = arduino_json_ns.JsonObject
JsonObjectRef = JsonObject.operator('ref')
JsonObjectConstRef = JsonObjectRef.operator('const')
GPIOPin = esphomelib_ns.GPIOPin
GPIOOutputPin = esphomelib_ns.GPIOOutputPin
GPIOInputPin = esphomelib_ns.GPIOInputPin
def get_gpio_pin_number(conf):
@@ -410,32 +590,43 @@ def get_gpio_pin_number(conf):
def generic_gpio_pin_expression_(conf, mock_obj, default_mode):
if conf is None:
return None
return
number = conf[CONF_NUMBER]
inverted = conf.get(CONF_INVERTED)
if CONF_PCF8574 in conf:
hub = get_variable(conf[CONF_PCF8574], 'io::PCF8574Component')
hub = None
for hub in get_variable(conf[CONF_PCF8574]):
yield None
if default_mode == u'INPUT':
mode = conf.get(CONF_MODE, u'INPUT')
return hub.make_input_pin(number,
RawExpression('PCF8574_' + mode),
inverted)
yield hub.make_input_pin(number,
RawExpression('PCF8574_' + mode),
inverted)
return
elif default_mode == u'OUTPUT':
return hub.make_output_pin(number, inverted)
yield hub.make_output_pin(number, inverted)
return
else:
raise ESPHomeYAMLError(u"Unknown default mode {}".format(default_mode))
if len(conf) == 1:
return IntLiteral(number)
yield IntLiteral(number)
return
mode = RawExpression(conf.get(CONF_MODE, default_mode))
return mock_obj(number, mode, inverted)
yield mock_obj(number, mode, inverted)
def gpio_output_pin_expression(conf):
return generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT')
exp = None
for exp in generic_gpio_pin_expression_(conf, GPIOOutputPin, 'OUTPUT'):
yield None
yield exp
def gpio_input_pin_expression(conf):
return generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT')
exp = None
for exp in generic_gpio_pin_expression_(conf, GPIOInputPin, 'INPUT'):
yield None
yield exp
def setup_mqtt_component(obj, config):
@@ -449,18 +640,11 @@ def setup_mqtt_component(obj, config):
add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC]))
if CONF_AVAILABILITY in config:
availability = config[CONF_AVAILABILITY]
add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
availability[CONF_PAYLOAD_NOT_AVAILABLE]))
def exp_empty_optional(type):
return RawExpression(u'Optional<{}>()'.format(type))
def exp_optional(type, value):
if value is None:
return exp_empty_optional(type)
return value
if not availability:
add(obj.disable_availability())
else:
add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
availability[CONF_PAYLOAD_NOT_AVAILABLE]))
# shlex's quote for Python 2.7
@@ -485,3 +669,7 @@ def color(the_color, message='', reset=None):
if not message:
return parse_colors(the_color)
return parse_colors(the_color) + message + escape_codes[reset or 'reset']
def relative_path(path):
return os.path.join(os.path.dirname(core.CONFIG_PATH), os.path.expanduser(path))

View File

@@ -2,6 +2,8 @@ from __future__ import print_function
import hashlib
import logging
import ssl
import sys
from datetime import datetime
import paho.mqtt.client as mqtt
@@ -10,8 +12,9 @@ from esphomeyaml import core
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \
CONF_LOG_TOPIC, \
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
CONF_USERNAME
CONF_USERNAME, CONF_TOPIC, CONF_SSL_FINGERPRINTS
from esphomeyaml.helpers import color
from esphomeyaml.util import safe_print
_LOGGER = logging.getLogger(__name__)
@@ -30,6 +33,14 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
config[CONF_MQTT][CONF_PASSWORD])
elif username:
client.username_pw_set(username, password)
if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS):
if sys.version_info >= (2, 7, 13):
tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member
else:
tls_version = ssl.PROTOCOL_SSLv23
client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED,
tls_version=tls_version, ciphers=None)
client.connect(config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT])
try:
@@ -39,13 +50,13 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
return 0
def show_logs(config, topic=None, username=None, password=None, client_id=None):
def show_logs(config, topic=None, username=None, password=None, client_id=None, escape=False):
if topic is not None:
pass # already have topic
elif CONF_MQTT in config:
conf = config[CONF_MQTT]
if CONF_LOG_TOPIC in conf:
topic = config[CONF_MQTT][CONF_LOG_TOPIC]
topic = config[CONF_MQTT][CONF_LOG_TOPIC][CONF_TOPIC]
elif CONF_TOPIC_PREFIX in config[CONF_MQTT]:
topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + u'/debug'
else:
@@ -57,7 +68,10 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None):
def on_message(client, userdata, msg):
time = datetime.now().time().strftime(u'[%H:%M:%S]')
print(time + msg.payload)
message = time + msg.payload
if escape:
message = message.replace('\033', '\\033')
safe_print(message)
return initialize(config, [topic], on_message, username, password, client_id)
@@ -67,12 +81,18 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant')
name = config[CONF_ESPHOMEYAML][CONF_NAME]
topic = u'{}/+/{}/#'.format(discovery_prefix, name)
_LOGGER.info(u"Clearing messages from %s", topic)
_LOGGER.info(u"Clearing messages from '%s'", topic)
_LOGGER.info(u"Please close this window when no more messages appear and the "
u"MQTT topic has been cleared of retained messages.")
def on_message(client, userdata, msg):
if not msg.payload:
if not msg.payload or not msg.retain:
return
try:
print(u"Clearing topic {}".format(msg.topic))
except UnicodeDecodeError:
print(u"Skipping non-UTF-8 topic (prohibited by MQTT standard)")
return
print(u"Clearing topic {}".format(msg.topic))
client.publish(msg.topic, None, retain=True)
return initialize(config, [topic], on_message, username, password, client_id)
@@ -80,8 +100,6 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
# From marvinroger/async-mqtt-client -> scripts/get-fingerprint/get-fingerprint.py
def get_fingerprint(config):
import ssl
addr = config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT]
_LOGGER.info("Getting fingerprint from %s:%s", addr[0], addr[1])
try:
@@ -93,6 +111,7 @@ def get_fingerprint(config):
sha1 = hashlib.sha1(cert_der).hexdigest()
print(u"SHA1 Fingerprint: " + color('cyan', sha1))
print(u"Copy above string into mqtt.ssl_fingerprints section of {}".format(core.CONFIG_PATH))
safe_print(u"SHA1 Fingerprint: " + color('cyan', sha1))
safe_print(u"Copy the string above into mqtt.ssl_fingerprints section of {}"
u"".format(core.CONFIG_PATH))
return 0

View File

@@ -4,67 +4,172 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.components import pcf8574
from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
_LOGGER = logging.getLogger(__name__)
ESP8266_PINS = {
'A0': 17, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14,
}
ESP8266_NODEMCU_PINS = dict(ESP8266_PINS, **{
'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'D9': 3,
'D10': 1, 'LED': 16, 'SDA': 4, 'SCL': 5,
})
ESP8266_D1_PINS = dict(ESP8266_PINS, **{
'D0': 3, 'D1': 1, 'D2': 16, 'D3': 5, 'D4': 4, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 0, 'D9': 2,
'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2, 'SDA': 4, 'SCL': 5,
})
ESP8266_D1_MINI_PINS = dict(ESP8266_PINS, **{
'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'RX': 3,
'TX': 1, 'LED': 2, 'SDA': 4, 'SCL': 5,
})
ESP8266_THING_PINS = dict(ESP8266_PINS, **{
'LED': 5, 'SDA': 2, 'SCL': 14,
})
ESP8266_ADAFRUIT_PINS = dict(ESP8266_PINS, **{
'LED': 0, 'SDA': 4, 'SCL': 5,
})
ESP8266_ESPDUINO_PINS = dict(ESP8266_PINS, **{
'LED': 16, 'SDA': 4, 'SCL': 5,
})
ESP8266_BOARD_TO_PINS = {
'huzzah': ESP8266_ADAFRUIT_PINS,
'espduino': ESP8266_ESPDUINO_PINS,
'nodemcu': ESP8266_NODEMCU_PINS, 'nodemcuv2': ESP8266_NODEMCU_PINS,
'thing': ESP8266_THING_PINS, 'thingdev': ESP8266_THING_PINS,
'd1': ESP8266_D1_PINS,
'd1_mini': ESP8266_D1_MINI_PINS, 'd1_mini_lite': ESP8266_D1_MINI_PINS,
'd1_mini_pro': ESP8266_D1_MINI_PINS
ESP8266_BASE_PINS = {
'A0': 17, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14, 'SDA': 4, 'SCL': 5, 'RX': 3, 'TX': 1
}
ESP32_PINS = {
ESP8266_BOARD_PINS = {
'd1': {'D0': 3, 'D1': 1, 'D2': 16, 'D3': 5, 'D4': 4, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 0,
'D9': 2, 'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2},
'd1_mini': {'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13,
'D8': 15, 'LED': 2},
'd1_mini_lite': 'd1_mini',
'd1_mini_pro': 'd1_mini',
'esp01': {},
'esp01_1m': {},
'esp07': {},
'esp12e': {},
'esp210': {},
'esp8285': {},
'esp_wroom_02': {},
'espduino': {'LED': 16},
'espectro': {'LED': 15, 'BUTTON': 2},
'espino': {'LED': 2, 'LED_RED': 2, 'LED_GREEN': 4, 'LED_BLUE': 5, 'BUTTON': 0},
'espinotee': {'LED': 16},
'espresso_lite_v1': {'LED': 16},
'espresso_lite_v2': {'LED': 2},
'gen4iod': {},
'heltec_wifi_kit_8': 'd1_mini',
'huzzah': {'LED': 0},
'modwifi': {},
'nodemcu': {'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13,
'D8': 15, 'D9': 3, 'D10': 1, 'LED': 16},
'nodemcuv2': 'nodemcu',
'oak': {'P0': 2, 'P1': 5, 'P2': 0, 'P3': 3, 'P4': 1, 'P5': 4, 'P6': 15, 'P7': 13, 'P8': 12,
'P9': 14, 'P10': 16, 'P11': 17, 'LED': 5},
'phoenix_v1': {'LED': 16},
'phoenix_v2': {'LED': 2},
'sparkfunBlynk': 'thing',
'thing': {'LED': 5, 'SDA': 2, 'SCL': 14},
'thingdev': 'thing',
'wifi_slot': {'LED': 2},
'wifiduino': {'D0': 3, 'D1': 1, 'D2': 2, 'D3': 0, 'D4': 4, 'D5': 5, 'D6': 16, 'D7': 14,
'D8': 12, 'D9': 13, 'D10': 15, 'D11': 13, 'D12': 12, 'D13': 14},
'wifinfo': {'LED': 12, 'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12,
'D7': 13, 'D8': 15, 'D9': 3, 'D10': 1},
'wio_link': {'LED': 2, 'GROVE': 15},
'wio_node': 'nodemcu',
'xinabox_cw01': {'SDA': 2, 'SCL': 14, 'LED': 5, 'LED_RED': 12, 'LED_GREEN': 13}
}
ESP32_BASE_PINS = {
'TX': 1, 'RX': 3, 'SDA': 21, 'SCL': 22, 'SS': 5, 'MOSI': 23, 'MISO': 19, 'SCK': 18, 'A0': 36,
'A3': 39, 'A4': 32, 'A5': 33, 'A6': 34, 'A7': 35, 'A10': 4, 'A11': 0, 'A12': 2, 'A13': 15,
'A14': 13, 'A15': 12, 'A16': 14, 'A17': 27, 'A18': 25, 'A19': 26, 'T0': 4, 'T1': 0, 'T2': 2,
'T3': 15, 'T4': 12, 'T5': 12, 'T6': 14, 'T7': 27, 'T8': 33, 'T9': 32, 'DAC1': 25, 'DAC2': 26,
'T3': 15, 'T4': 13, 'T5': 12, 'T6': 14, 'T7': 27, 'T8': 33, 'T9': 32, 'DAC1': 25, 'DAC2': 26,
'SVP': 36, 'SVN': 39,
}
ESP32_NODEMCU_32S_PINS = dict(ESP32_PINS, **{
'LED': 2,
})
ESP32_LOLIN32_PINS = dict(ESP32_PINS, **{
'LED': 5
})
ESP32_BOARD_TO_PINS = {
'nodemcu-32s': ESP32_NODEMCU_32S_PINS,
'lolin32': ESP32_LOLIN32_PINS,
ESP32_BOARD_PINS = {
'alksesp32': {'D0': 40, 'D1': 41, 'D2': 15, 'D3': 2, 'D4': 0, 'D5': 4, 'D6': 16, 'D7': 17,
'D8': 5, 'D9': 18, 'D10': 19, 'D11': 21, 'D12': 22, 'D13': 23, 'A0': 32, 'A1': 33,
'A2': 25, 'A3': 26, 'A4': 27, 'A5': 14, 'A6': 12, 'A7': 15, 'L_R': 22, 'L_G': 17,
'L_Y': 23, 'L_B': 5, 'L_RGB_R': 4, 'L_RGB_G': 21, 'L_RGB_B': 16, 'SW1': 15,
'SW2': 2, 'SW3': 0, 'POT1': 32, 'POT2': 33, 'PIEZO1': 19, 'PIEZO2': 18,
'PHOTO': 25, 'DHT_PIN': 26, 'S1': 4, 'S2': 16, 'S3': 18, 'S4': 19, 'S5': 21,
'SDA': 27, 'SCL': 14, 'SS': 19, 'MOSI': 21, 'MISO': 22, 'SCK': 23},
'esp-wrover-kit': {},
'esp32-evb': {'BUTTON': 34, 'SDA': 13, 'SCL': 16, 'SS': 17, 'MOSI': 2, 'MISO': 15, 'SCK': 14},
'esp32-gateway': {'LED': 33, 'BUTTON': 34, 'SCL': 16, 'SDA': 17},
'esp320': {'LED': 5, 'SDA': 2, 'SCL': 14, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14},
'esp32dev': {},
'esp32doit-devkit-v1': {'LED': 2},
'esp32thing': {'LED': 5, 'BUTTON': 0, 'SS': 2},
'esp32vn-iot-uno': {},
'espea32': {'LED': 5, 'BUTTON': 0},
'espectro32': {'LED': 15, 'SD_SS': 33},
'espino32': {'LED': 16, 'BUTTON': 0},
'featheresp32': {'LED': 13, 'TX': 17, 'RX': 16, 'SDA': 23, 'SS': 2, 'MOSI': 18, 'SCK': 5,
'A0': 26, 'A1': 25, 'A2': 34, 'A4': 36, 'A5': 4, 'A6': 14, 'A7': 32, 'A8': 15,
'A9': 33, 'A10': 27, 'A11': 12, 'A12': 13, 'A13': 35},
'firebeetle32': {'LED': 2},
'heltec_wifi_kit_32': {'LED': 25, 'BUTTON': 0, 'A1': 37, 'A2': 38},
'heltec_wifi_lora_32': {'LED': 25, 'BUTTON': 0, 'SDA': 4, 'SCL': 15, 'SS': 18, 'MOSI': 27,
'SCK': 5, 'A1': 37, 'A2': 38, 'T8': 32, 'T9': 33, 'DAC1': 26,
'DAC2': 25, 'OLED_SCL': 15, 'OLED_SDA': 4, 'OLED_RST': 16,
'LORA_SCK': 5, 'LORA_MOSI': 27, 'LORA_MISO': 19, 'LORA_CS': 18,
'LORA_RST': 14, 'LORA_IRQ': 26},
'hornbill32dev': {'LED': 13, 'BUTTON': 0},
'hornbill32minima': {'SS': 2},
'intorobot': {'LED': 4, 'LED_RED': 27, 'LED_GREEN': 21, 'LED_BLUE': 22,
'BUTTON': 0, 'SDA': 23, 'SCL': 19, 'MOSI': 16, 'MISO': 17, 'A1': 39, 'A2': 35,
'A3': 25, 'A4': 26, 'A5': 14, 'A6': 12, 'A7': 15, 'A8': 13, 'A9': 2, 'D0': 19,
'D1': 23, 'D2': 18, 'D3': 17, 'D4': 16, 'D5': 5, 'D6': 4, 'T0': 19, 'T1': 23,
'T2': 18, 'T3': 17, 'T4': 16, 'T5': 5, 'T6': 4},
'lolin32': {'LED': 5},
'lolin_d32': {'LED': 5, 'VBAT': 35},
'lolin_d32_pro': {'LED': 5, 'VBAT': 35, 'TF_CS': 4, 'TS_CS': 12, 'TFT_CS': 14, 'TFT_LED': 32,
'TFT_RST': 33, 'TFT_DC': 27},
'm5stack-core-esp32': {'TXD2': 17, 'RXD2': 16, 'G23': 23, 'G19': 19, 'G18': 18, 'G3': 3,
'G16': 16, 'G21': 21, 'G2': 2, 'G12': 12, 'G15': 15, 'G35': 35,
'G36': 36, 'G25': 25, 'G26': 26, 'G1': 1, 'G17': 17, 'G22': 22, 'G5': 5,
'G13': 13, 'G0': 0, 'G34': 34, 'ADC1': 35, 'ADC2': 36},
'm5stack-fire': {'G23': 23, 'G19': 19, 'G18': 18, 'G3': 3, 'G16': 16, 'G21': 21, 'G2': 2,
'G12': 12, 'G15': 15, 'G35': 35, 'G36': 36, 'G25': 25, 'G26': 26, 'G1': 1,
'G17': 17, 'G22': 22, 'G5': 5, 'G13': 13, 'G0': 0, 'G34': 34, 'ADC1': 35,
'ADC2': 36},
'mhetesp32devkit': {'LED': 2},
'mhetesp32minikit': {'LED': 2},
'microduino-core-esp32': {'SDA': 22, 'SCL': 21, 'SDA1': 12, 'SCL1': 13, 'A0': 12, 'A1': 13,
'A2': 15, 'A3': 4, 'A6': 38, 'A7': 37, 'A8': 32, 'A9': 33, 'A10': 25,
'A11': 26, 'A12': 27, 'A13': 14, 'D0': 3, 'D1': 1, 'D2': 16, 'D3': 17,
'D4': 32, 'D5': 33, 'D6': 25, 'D7': 26, 'D8': 27, 'D9': 14, 'D10': 5,
'D11': 23, 'D12': 19, 'D13': 18, 'D14': 12, 'D15': 13, 'D16': 15,
'D17': 4, 'D18': 22, 'D19': 21, 'D20': 38, 'D21': 37},
'nano32': {'LED': 16, 'BUTTON': 0},
'nina_w10': {'LED_GREEN': 33, 'LED_RED': 23, 'LED_BLUE': 21, 'SW1': 33, 'SW2': 27, 'SDA': 12,
'SCL': 13, 'D0': 3, 'D1': 1, 'D2': 26, 'D3': 25, 'D4': 35, 'D5': 27, 'D6': 22,
'D7': 0, 'D8': 15, 'D9': 14, 'D10': 5, 'D11': 19, 'D12': 23, 'D13': 18, 'D14': 13,
'D15': 12, 'D16': 32, 'D17': 33, 'D18': 21, 'D19': 34, 'D20': 36, 'D21': 39},
'node32s': {},
'nodemcu-32s': {'LED': 2, 'BUTTON': 0},
'odroid_esp32': {'LED': 2, 'SDA': 15, 'SCL': 4, 'SS': 22, 'ADC1': 35, 'ADC2': 36},
'onehorse32dev': {'LED': 5, 'BUTTON': 0, 'A1': 37, 'A2': 38},
'pico32': {},
'pocket_32': {'LED': 16},
'quantum': {},
'ttgo-lora32-v1': {'LED': 2, 'BUTTON': 0, 'SS': 18, 'MOSI': 27, 'SCK': 5, 'A1': 37, 'A2': 38,
'T8': 32, 'T9': 33, 'DAC1': 26, 'DAC2': 25, 'OLED_SDA': 4, 'OLED_SCL': 15,
'OLED_RST': 16, 'LORA_SCK': 5, 'LORA_MISO': 19, 'LORA_MOSI': 27,
'LORA_CS': 18, 'LORA_RST': 14, 'LORA_IRQ': 26},
'wemosbat': 'pocket_32',
'widora-air': {'LED': 25, 'BUTTON': 0, 'SDA': 23, 'SCL': 19, 'MOSI': 16, 'MISO': 17, 'A1': 39,
'A2': 35, 'A3': 25, 'A4': 26, 'A5': 14, 'A6': 12, 'A7': 15, 'A8': 13, 'A9': 2,
'D0': 19, 'D1': 23, 'D2': 18, 'D3': 17, 'D4': 16, 'D5': 5, 'D6': 4, 'T0': 19,
'T1': 23, 'T2': 18, 'T3': 17, 'T4': 16, 'T5': 5, 'T6': 4},
'xinabox_cw02': {'LED': 27},
}
def _lookup_pin(platform, board, value):
if platform == ESP_PLATFORM_ESP8266:
board_pins = ESP8266_BOARD_PINS.get(board, {})
base_pins = ESP8266_BASE_PINS
elif platform == ESP_PLATFORM_ESP32:
board_pins = ESP32_BOARD_PINS.get(board, {})
base_pins = ESP32_BASE_PINS
else:
raise NotImplementedError
if isinstance(board_pins, str):
return _lookup_pin(platform, board_pins, value)
if value in board_pins:
return board_pins[value]
if value in base_pins:
return base_pins[value]
raise vol.Invalid(u"Can't find internal pin number for {}.".format(value))
def _translate_pin(value):
if isinstance(value, dict) or value is None:
raise vol.Invalid(u"This option doesn't allow more complicated options like inverted.")
raise vol.Invalid(u"This variable only supports pin numbers, not full pin schemas "
u"(with inverted and mode).")
if isinstance(value, int):
return value
try:
@@ -73,27 +178,7 @@ def _translate_pin(value):
pass
if value.startswith('GPIO'):
return vol.Coerce(int)(value[len('GPIO'):].strip())
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if value in ESP32_PINS:
return ESP32_PINS[value]
if core.BOARD not in ESP32_BOARD_TO_PINS:
raise vol.Invalid(u"ESP32: Unknown board {} with unknown "
u"pin {}.".format(core.BOARD, value))
if value not in ESP32_BOARD_TO_PINS[core.BOARD]:
raise vol.Invalid(u"ESP32: Board {} doesn't have "
u"pin {}".format(core.BOARD, value))
return ESP32_BOARD_TO_PINS[core.BOARD][value]
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if value in ESP8266_PINS:
return ESP8266_PINS[value]
if core.BOARD not in ESP8266_BOARD_TO_PINS:
raise vol.Invalid(u"ESP8266: Unknown board {} with unknown "
u"pin {}.".format(core.BOARD, value))
if value not in ESP8266_BOARD_TO_PINS[core.BOARD]:
raise vol.Invalid(u"ESP8266: Board {} doesn't have "
u"pin {}".format(core.BOARD, value))
return ESP8266_BOARD_TO_PINS[core.BOARD][value]
raise vol.Invalid(u"Invalid ESP platform.")
return _lookup_pin(core.ESP_PLATFORM, core.BOARD, value)
def validate_gpio_pin(value):
@@ -171,9 +256,9 @@ PIN_MODES_ESP32 = [
def pin_mode(value):
value = vol.All(vol.Coerce(str), vol.Upper)(value)
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return vol.Any(*PIN_MODES_ESP32)(value)
return cv.one_of(*PIN_MODES_ESP32)(value)
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
return vol.Any(*PIN_MODES_ESP8266)(value)
return cv.one_of(*PIN_MODES_ESP8266)(value)
raise vol.Invalid(u"Invalid ESP platform.")
@@ -201,20 +286,40 @@ def shorthand_input_pin(value):
PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({
vol.Required(CONF_PCF8574): cv.variable_id,
vol.Required(CONF_PCF8574): cv.use_variable_id(pcf8574.PCF8574Component),
vol.Required(CONF_NUMBER): vol.Coerce(int),
vol.Optional(CONF_MODE): vol.All(vol.Upper, "OUTPUT"),
vol.Optional(CONF_MODE): vol.All(vol.Upper, cv.one_of("OUTPUT")),
vol.Optional(CONF_INVERTED, default=False): cv.boolean,
})
PCF8574_INPUT_PIN_SCHEMA = PCF8574_OUTPUT_PIN_SCHEMA.extend({
vol.Optional(CONF_MODE): vol.All(vol.Upper, vol.Any("INPUT", "INPUT_PULLUP")),
vol.Optional(CONF_MODE): vol.All(vol.Upper, cv.one_of("INPUT", "INPUT_PULLUP")),
})
GPIO_INTERNAL_OUTPUT_PIN_SCHEMA = vol.Any(shorthand_output_pin, GPIO_FULL_OUTPUT_PIN_SCHEMA)
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(PCF8574_OUTPUT_PIN_SCHEMA, GPIO_INTERNAL_OUTPUT_PIN_SCHEMA)
def internal_gpio_output_pin_schema(value):
if isinstance(value, dict):
return GPIO_FULL_OUTPUT_PIN_SCHEMA(value)
return shorthand_output_pin(value)
GPIO_INTERNAL_INPUT_PIN_SCHEMA = vol.Any(shorthand_input_pin, GPIO_FULL_INPUT_PIN_SCHEMA)
GPIO_INPUT_PIN_SCHEMA = vol.Any(PCF8574_INPUT_PIN_SCHEMA, GPIO_INTERNAL_INPUT_PIN_SCHEMA)
def gpio_output_pin_schema(value):
if isinstance(value, dict):
if CONF_PCF8574 in value:
return PCF8574_OUTPUT_PIN_SCHEMA(value)
return GPIO_FULL_OUTPUT_PIN_SCHEMA(value)
return shorthand_output_pin(value)
def internal_gpio_input_pin_schema(value):
if isinstance(value, dict):
return GPIO_FULL_INPUT_PIN_SCHEMA(value)
return shorthand_input_pin(value)
def gpio_input_pin_schema(value):
if isinstance(value, dict):
if CONF_PCF8574 in value:
return PCF8574_INPUT_PIN_SCHEMA(value)
return GPIO_FULL_INPUT_PIN_SCHEMA(value)
return shorthand_input_pin(value)

View File

@@ -0,0 +1,12 @@
; This file allows the docker build file to install the required platformio
; platforms
[env:espressif8266]
platform = espressif8266
board = nodemcuv2
framework = arduino
[env:espressif32]
platform = espressif32
board = nodemcu-32s
framework = arduino

32
esphomeyaml/util.py Normal file
View 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!")

View File

@@ -6,14 +6,14 @@ import unicodedata
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import mqtt
from esphomeyaml.const import ESP_BOARDS_FOR_PLATFORM, ESP_PLATFORMS, ESP_PLATFORM_ESP32, \
ESP_PLATFORM_ESP8266
import esphomeyaml.config_validation as cv
from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import color
# pylint: disable=anomalous-backslash-in-string
from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
from esphomeyaml.util import safe_print
CORE_BIG = """ _____ ____ _____ ______
/ ____/ __ \| __ \| ____|
| | | | | | |__) | |__
@@ -51,7 +51,7 @@ OTA_BIG = """ ____ _______
"""
# TODO handle escaping
BASE_CONFIG = """esphomeyaml:
BASE_CONFIG = u"""esphomeyaml:
name: {name}
platform: {platform}
board: {board}
@@ -70,6 +70,18 @@ logger:
"""
def wizard_file(**kwargs):
config = BASE_CONFIG.format(**kwargs)
if kwargs['ota_password']:
config += u"ota:\n password: '{}'\n".format(kwargs['ota_password'])
else:
config += u"ota:\n"
return config
if os.getenv('ESPHOMEYAML_QUICKWIZARD', False):
def sleep(time):
pass
@@ -77,18 +89,18 @@ else:
from time import sleep
def print_step(step, big):
print()
print()
print("============= STEP {} =============".format(step))
print(big)
print("===================================")
def safe_print_step(step, big):
safe_print()
safe_print()
safe_print("============= STEP {} =============".format(step))
safe_print(big)
safe_print("===================================")
sleep(0.25)
def default_input(text, default):
print()
print("Press ENTER for default ({})".format(default))
safe_print()
safe_print(u"Press ENTER for default ({})".format(default))
return raw_input(text.format(default)) or default
@@ -100,30 +112,30 @@ def strip_accents(string):
def wizard(path):
if not path.endswith('.yaml') and not path.endswith('.yml'):
print("Please make your configuration file {} have the extension .yaml or .yml".format(
color('cyan', path)))
safe_print(u"Please make your configuration file {} have the extension .yaml or .yml"
u"".format(color('cyan', path)))
return 1
if os.path.exists(path):
print("Uh oh, it seems like {} already exists, please delete that file first "
"or chose another configuration file.".format(color('cyan', path)))
safe_print(u"Uh oh, it seems like {} already exists, please delete that file first "
u"or chose another configuration file.".format(color('cyan', path)))
return 1
print("Hi there!")
safe_print("Hi there!")
sleep(1.5)
print("I'm the wizard of esphomeyaml :)")
safe_print("I'm the wizard of esphomeyaml :)")
sleep(1.25)
print("And I'm here to help you get started with esphomeyaml.")
safe_print("And I'm here to help you get started with esphomeyaml.")
sleep(2.0)
print("In 5 steps I'm going to guide you through creating a basic "
"configuration file for your custom ESP8266/ESP32 firmware. Yay!")
safe_print("In 5 steps I'm going to guide you through creating a basic "
"configuration file for your custom ESP8266/ESP32 firmware. Yay!")
sleep(3.0)
print()
print_step(1, CORE_BIG)
print("First up, please choose a " + color('green', 'name') + " for your node.")
print("It should be a unique name that can be used to identify the device later.")
safe_print()
safe_print_step(1, CORE_BIG)
safe_print("First up, please choose a " + color('green', 'name') + " for your node.")
safe_print("It should be a unique name that can be used to identify the device later.")
sleep(1)
print("For example, I like calling the node in my living room {}.".format(
safe_print("For example, I like calling the node in my living room {}.".format(
color('bold_white', "livingroom")))
print()
safe_print()
sleep(1)
name = raw_input(color("bold_white", "(name): "))
while True:
@@ -131,34 +143,34 @@ def wizard(path):
name = cv.valid_name(name)
break
except vol.Invalid:
print(color("red", "Oh noes, \"{}\" isn't a valid name. Names can only include "
"numbers, letters and underscores.".format(name)))
safe_print(color("red", u"Oh noes, \"{}\" isn't a valid name. Names can only include "
u"numbers, letters and underscores.".format(name)))
name = strip_accents(name).replace(' ', '_')
name = u''.join(c for c in name if c in cv.ALLOWED_NAME_CHARS)
print("Shall I use \"{}\" as the name instead?".format(color('cyan', name)))
safe_print(u"Shall I use \"{}\" as the name instead?".format(color('cyan', name)))
sleep(0.5)
name = default_input("(name [{}]): ", name)
name = default_input(u"(name [{}]): ", name)
print("Great! Your node is now called \"{}\".".format(color('cyan', name)))
safe_print(u"Great! Your node is now called \"{}\".".format(color('cyan', name)))
sleep(1)
print_step(2, ESP_BIG)
print("Now I'd like to know what microcontroller you're using so that I can compile "
"firmwares for it.")
print("Are you using an " + color('green', 'ESP32') + " or " +
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
safe_print_step(2, ESP_BIG)
safe_print("Now I'd like to know what microcontroller you're using so that I can compile "
"firmwares for it.")
safe_print("Are you using an " + color('green', 'ESP32') + " or " +
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
while True:
sleep(0.5)
print()
print("Please enter either ESP32 or ESP8266.")
safe_print()
safe_print("Please enter either ESP32 or ESP8266.")
platform = raw_input(color("bold_white", "(ESP32/ESP8266): "))
try:
platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform)
break
except vol.Invalid:
print("Unfortunately, I can't find an espressif microcontroller called "
"\"{}\". Please try again.".format(platform))
print("Thanks! You've chosen {} as your platform.".format(color('cyan', platform)))
print()
safe_print(u"Unfortunately, I can't find an espressif microcontroller called "
u"\"{}\". Please try again.".format(platform))
safe_print(u"Thanks! You've chosen {} as your platform.".format(color('cyan', platform)))
safe_print()
sleep(1)
if platform == ESP_PLATFORM_ESP32:
@@ -166,89 +178,92 @@ def wizard(path):
else:
board_link = 'http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards'
print("Next, I need to know what " + color('green', 'board') + " you're using.")
safe_print("Next, I need to know what " + color('green', 'board') + " you're using.")
sleep(0.5)
print("Please go to {} and choose a board.".format(color('green', board_link)))
safe_print("Please go to {} and choose a board.".format(color('green', board_link)))
if platform == ESP_PLATFORM_ESP8266:
print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
print()
safe_print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
safe_print()
# Don't sleep because user needs to copy link
if platform == ESP_PLATFORM_ESP32:
print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s')))
safe_print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s')))
boards = list(ESP32_BOARD_PINS.keys())
else:
print("For example \"{}\".".format(color("bold_white", 'nodemcuv2')))
safe_print("For example \"{}\".".format(color("bold_white", 'nodemcuv2')))
boards = list(ESP8266_BOARD_PINS.keys())
while True:
board = raw_input(color("bold_white", "(board): "))
boards = ESP_BOARDS_FOR_PLATFORM[platform]
try:
board = vol.All(vol.Lower, vol.Any(*boards))(board)
break
except vol.Invalid:
print(color('red', "Sorry, I don't think the board \"{}\" exists."))
print()
safe_print(color('red', "Sorry, I don't think the board \"{}\" exists."))
safe_print()
sleep(0.25)
print("Possible options are {}".format(', '.join(boards)))
print()
safe_print("Possible options are {}".format(', '.join(boards)))
safe_print()
print("Way to go! You've chosen {} as your board.".format(color('cyan', board)))
print()
safe_print(u"Way to go! You've chosen {} as your board.".format(color('cyan', board)))
safe_print()
sleep(1)
print_step(3, WIFI_BIG)
print("In this step, I'm going to create the configuration for "
"WiFi.")
print()
safe_print_step(3, WIFI_BIG)
safe_print("In this step, I'm going to create the configuration for "
"WiFi.")
safe_print()
sleep(1)
print("First, what's the " + color('green', 'SSID') + " (the name) of the WiFi network {} "
"I should connect to?".format(name))
safe_print("First, what's the " + color('green', 'SSID') +
u" (the name) of the WiFi network {} I should connect to?".format(name))
sleep(1.5)
print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
while True:
ssid = raw_input(color('bold_white', "(ssid): "))
try:
ssid = cv.ssid(ssid)
break
except vol.Invalid:
print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
"Please try again.".format(ssid)))
print()
safe_print(color('red', u"Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
u"Please try again.".format(ssid)))
safe_print()
sleep(1)
print("Thank you very much! You've just chosen \"{}\" as your SSID.".format(
color('cyan', ssid)))
print()
safe_print(u"Thank you very much! You've just chosen \"{}\" as your SSID."
u"".format(color('cyan', ssid)))
safe_print()
sleep(0.75)
print("Now please state the " + color('green', 'password') +
" of the WiFi network so that I can connect to it (Leave empty for no password)")
print()
print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
safe_print("Now please state the " + color('green', 'password') +
" of the WiFi network so that I can connect to it (Leave empty for no password)")
safe_print()
safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
sleep(0.5)
psk = raw_input(color('bold_white', '(PSK): '))
print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
sleep(1.5)
print_step(4, MQTT_BIG)
print("Almost there! Now let's setup MQTT so that your node can connect to the outside world.")
print()
safe_print_step(4, MQTT_BIG)
safe_print("Almost there! Now let's setup MQTT so that your node can connect to the "
"outside world.")
safe_print()
sleep(1)
print("Please enter the " + color('green', 'address') + " of your MQTT broker.")
print()
print("For example \"{}\".".format(color('bold_white', '192.168.178.84')))
safe_print("Please enter the " + color('green', 'address') + " of your MQTT broker.")
safe_print()
safe_print("For example \"{}\".".format(color('bold_white', '192.168.178.84')))
while True:
broker = raw_input(color('bold_white', "(broker): "))
try:
broker = mqtt.validate_broker(broker)
break
except vol.Invalid as err:
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
broker, err)))
print("Please try again.")
print()
safe_print(color('red', u"The broker address \"{}\" seems to be invalid: {} :("
u"".format(broker, err)))
safe_print("Please try again.")
safe_print()
sleep(1)
print("Thanks! Now enter the " + color('green', 'username') + " and " +
color('green', 'password') + " for the MQTT broker. Leave empty for no authentication.")
safe_print("Thanks! Now enter the " + color('green', 'username') + " and " +
color('green', 'password') +
" for the MQTT broker. Leave empty for no authentication.")
mqtt_username = raw_input(color('bold_white', '(username): '))
mqtt_password = ''
if mqtt_username:
@@ -257,46 +272,42 @@ def wizard(path):
show = '*' * len(mqtt_password)
if len(mqtt_password) >= 2:
show = mqtt_password[:2] + '*' * len(mqtt_password)
print("MQTT Username: \"{}\"; Password: \"{}\"".format(
color('cyan', mqtt_username), color('cyan', show)))
safe_print(u"MQTT Username: \"{}\"; Password: \"{}\""
u"".format(color('cyan', mqtt_username), color('cyan', show)))
else:
print("No authentication for MQTT")
safe_print("No authentication for MQTT")
print_step(5, OTA_BIG)
print("Last step! esphomeyaml can automatically upload custom firmwares over WiFi "
"(over the air).")
print("This can be insecure if you do not trust the WiFi network. Do you want to set "
"an " + color('green', 'OTA password') + " for remote updates?")
print()
safe_print_step(5, OTA_BIG)
safe_print("Last step! esphomeyaml can automatically upload custom firmwares over WiFi "
"(over the air).")
safe_print("This can be insecure if you do not trust the WiFi network. Do you want to set "
"an " + color('green', 'OTA password') + " for remote updates?")
safe_print()
sleep(0.25)
print("Press ENTER for no password")
safe_print("Press ENTER for no password")
ota_password = raw_input(color('bold_white', '(password): '))
config = BASE_CONFIG.format(name=name, platform=platform, board=board,
ssid=ssid, psk=psk, broker=broker,
mqtt_username=mqtt_username, mqtt_password=mqtt_password)
if ota_password:
config += "ota:\n password: '{}'\n".format(ota_password)
else:
config += "ota:\n"
config = wizard_file(name=name, platform=platform, board=board,
ssid=ssid, psk=psk, broker=broker,
mqtt_username=mqtt_username, mqtt_password=mqtt_password,
ota_password=ota_password)
with codecs.open(path, 'w') as f_handle:
f_handle.write(config)
print()
print(color('cyan', "DONE! I've now written a new configuration file to ") +
color('bold_cyan', path))
print()
print("Next steps:")
print(" > If you haven't already, enable MQTT discovery in Home Assistant:")
print()
print(color('bold_white', "# In your configuration.yaml"))
print(color('bold_white', "mqtt:"))
print(color('bold_white', " broker: {}".format(broker)))
print(color('bold_white', " # ..."))
print(color('bold_white', " discovery: True"))
print()
print(" > Then follow the rest of the getting started guide:")
print(" > https://esphomelib.com/esphomeyaml/getting-started.html")
safe_print()
safe_print(color('cyan', "DONE! I've now written a new configuration file to ") +
color('bold_cyan', path))
safe_print()
safe_print("Next steps:")
safe_print(" > If you haven't already, enable MQTT discovery in Home Assistant:")
safe_print()
safe_print(color('bold_white', "# In your configuration.yaml"))
safe_print(color('bold_white', "mqtt:"))
safe_print(color('bold_white', u" broker: {}".format(broker)))
safe_print(color('bold_white', " # ..."))
safe_print(color('bold_white', " discovery: True"))
safe_print()
safe_print(" > Then follow the rest of the getting started guide:")
safe_print(" > https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html")
return 0

View File

@@ -2,14 +2,21 @@ from __future__ import print_function
import codecs
import errno
import json
import logging
import os
import shutil
from esphomeyaml import core
from esphomeyaml.config import iter_components
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
CONF_LIBRARY_URI, \
CONF_NAME, CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, CONF_ARDUINO_VERSION, CONF_BOARD, \
CONF_BOARD_FLASH_MODE, CONF_ESPHOMELIB_VERSION, CONF_ESPHOMEYAML, CONF_LOCAL, CONF_NAME, \
CONF_USE_CUSTOM_CODE, ESP_PLATFORM_ESP32, CONF_REPOSITORY, CONF_COMMIT, CONF_BRANCH, CONF_TAG
from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.core_config import VERSION_REGEX
from esphomeyaml.helpers import relative_path
_LOGGER = logging.getLogger(__name__)
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
CPP_AUTO_GENERATE_END = u'// =========== AUTO GENERATED CODE END ============'
@@ -57,11 +64,11 @@ lib_deps =
build_flags =
{build_flags}
${{common.build_flags}}
upload_speed = {upload_speed}
"""
PLATFORM_TO_PLATFORMIO = {
ESP_PLATFORM_ESP32: 'espressif32',
ESP_PLATFORM_ESP8266: 'espressif8266'
UPLOAD_SPEED_OVERRIDE = {
'esp210': 57600,
}
@@ -81,21 +88,21 @@ def get_build_flags(config, key):
return build_flags
def get_ini_content(config):
platform = config[CONF_ESPHOMEYAML][CONF_PLATFORM]
if platform in PLATFORM_TO_PLATFORMIO:
platform = PLATFORM_TO_PLATFORMIO[platform]
def get_ini_content(config, path):
version_specific_settings = determine_platformio_version_settings()
options = {
u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
u'platform': platform,
u'platform': config[CONF_ESPHOMEYAML][CONF_ARDUINO_VERSION],
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
u'build_flags': u'',
u'upload_speed': UPLOAD_SPEED_OVERRIDE.get(core.BOARD, 115200),
}
build_flags = set()
if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]:
if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
build_flags |= get_build_flags(config, 'build_flags')
build_flags |= get_build_flags(config, 'BUILD_FLAGS')
build_flags.add(u"-DESPHOMEYAML_USE")
build_flags.add("-Wno-unused-variable")
build_flags |= get_build_flags(config, 'required_build_flags')
build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS')
@@ -105,13 +112,74 @@ def get_ini_content(config):
options[u'build_flags'] = u'\n '.join(build_flags)
lib_deps = set()
lib_deps.add(config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI])
lib_version = config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION]
lib_path = os.path.join(path, 'lib')
dst_path = os.path.join(lib_path, 'esphomelib')
this_version = None
if CONF_REPOSITORY in lib_version:
tag = next((lib_version[x] for x in (CONF_COMMIT, CONF_BRANCH, CONF_TAG)
if x in lib_version), None)
this_version = lib_version[CONF_REPOSITORY]
if tag is not None:
this_version += '#' + tag
lib_deps.add(this_version)
if os.path.islink(dst_path):
os.unlink(dst_path)
elif CONF_LOCAL in lib_version:
this_version = lib_version[CONF_LOCAL]
src_path = relative_path(this_version)
do_write = True
if os.path.islink(dst_path):
old_path = os.path.join(os.readlink(dst_path), lib_path)
if old_path != lib_path:
os.unlink(dst_path)
else:
do_write = False
if do_write:
mkdir_p(lib_path)
os.symlink(src_path, dst_path)
# Manually add lib_deps because platformio seems to ignore them inside libs/
library_json_path = os.path.join(src_path, 'library.json')
with codecs.open(library_json_path, 'r', encoding='utf-8') as f_handle:
library_json_text = f_handle.read()
library_json = json.loads(library_json_text)
for dep in library_json.get('dependencies', []):
if 'version' in dep and VERSION_REGEX.match(dep['version']) is not None:
lib_deps.add(dep['name'] + '@' + dep['version'])
else:
lib_deps.add(dep['version'])
else:
this_version = lib_version
lib_deps.add(lib_version)
version_file = os.path.join(path, '.esphomelib_version')
version = None
if os.path.isfile(version_file):
with open(version_file, 'r') as ver_f:
version = ver_f.read()
if version != this_version:
_LOGGER.info("Esphomelib version change detected. Cleaning build files...")
try:
clean_build(path)
except OSError as err:
_LOGGER.warn("Error deleting build files (%s)! Ignoring...", err)
with open(version_file, 'w') as ver_f:
ver_f.write(this_version)
lib_deps |= get_build_flags(config, 'LIB_DEPS')
lib_deps |= get_build_flags(config, 'lib_deps')
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
lib_deps |= {
'Preferences', # Preferences helper
}
# Manual fix for AsyncTCP
if config[CONF_ESPHOMEYAML].get(CONF_ARDUINO_VERSION) == ARDUINO_VERSION_ESP32_DEV:
lib_deps.add('https://github.com/me-no-dev/AsyncTCP.git#idf-update')
# avoid changing build flags order
lib_deps = sorted(x for x in lib_deps if x)
if lib_deps:
@@ -119,8 +187,9 @@ def get_ini_content(config):
content = INI_CONTENT_FORMAT.format(**options)
if CONF_BOARD_FLASH_MODE in config[CONF_ESPHOMEYAML]:
flash_mode_key = version_specific_settings['flash_mode_key']
flash_mode = config[CONF_ESPHOMEYAML][CONF_BOARD_FLASH_MODE]
content += "board_flash_mode = {}\n".format(flash_mode)
content += "{} = {}\n".format(flash_mode_key, flash_mode)
return content
@@ -164,16 +233,34 @@ def write_platformio_ini(content, path):
content_format = find_begin_end(text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END)
else:
prev_file = None
mkdir_p(os.path.dirname(path))
content_format = INI_BASE_FORMAT
full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \
content + INI_AUTO_GENERATE_END + content_format[1]
content + INI_AUTO_GENERATE_END + content_format[1]
if prev_file == full_file:
return
with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
f_handle.write(full_file)
def write_platformio_project(config, path):
mkdir_p(path)
platformio_ini = os.path.join(path, 'platformio.ini')
content = get_ini_content(config, path)
if 'esp32_ble_beacon' in config or 'esp32_ble_tracker' in config:
content += 'board_build.partitions = partitions.csv\n'
partitions_csv = os.path.join(path, 'partitions.csv')
if not os.path.isfile(partitions_csv):
mkdir_p(path)
with open(partitions_csv, "w") as f:
f.write("nvs, data, nvs, 0x009000, 0x005000,\n")
f.write("otadata, data, ota, 0x00e000, 0x002000,\n")
f.write("app0, app, ota_0, 0x010000, 0x190000,\n")
f.write("app1, app, ota_1, 0x200000, 0x190000,\n")
f.write("eeprom, data, 0x99, 0x390000, 0x001000,\n")
f.write("spiffs, data, spiffs, 0x391000, 0x00F000\n")
write_platformio_ini(content, platformio_ini)
def write_cpp(code_s, path):
if os.path.isfile(path):
try:
@@ -189,8 +276,29 @@ def write_cpp(code_s, path):
code_format = CPP_BASE_FORMAT
full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \
code_s + CPP_AUTO_GENERATE_END + code_format[1]
code_s + CPP_AUTO_GENERATE_END + code_format[1]
if prev_file == full_file:
return
with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
f_handle.write(full_file)
def determine_platformio_version_settings():
import platformio
settings = {}
if platformio.VERSION < (3, 5, 3):
settings['flash_mode_key'] = 'board_flash_mode'
else:
settings['flash_mode_key'] = 'board_build.flash_mode'
return settings
def clean_build(build_path):
for directory in ('.piolibdeps', '.pioenvs'):
dir_path = os.path.join(build_path, directory)
if os.path.isdir(dir_path):
_LOGGER.info("Deleting %s", dir_path)
shutil.rmtree(dir_path)

View File

@@ -4,12 +4,14 @@ import codecs
import fnmatch
import logging
import os
import uuid
from collections import OrderedDict
import yaml
import yaml.constructor
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, MACAddress, TimePeriod, \
TimePeriodMicroseconds, TimePeriodMilliseconds, TimePeriodSeconds
from esphomeyaml import core
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod
_LOGGER = logging.getLogger(__name__)
@@ -48,8 +50,9 @@ def load_yaml(fname):
with codecs.open(fname, encoding='utf-8') as conf_file:
return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict()
except yaml.YAMLError as exc:
_LOGGER.error(exc)
raise ESPHomeYAMLError(exc)
except IOError as exc:
raise ESPHomeYAMLError(u"Error accessing file {}: {}".format(fname, exc))
except UnicodeDecodeError as exc:
_LOGGER.error(u"Unable to read file %s: %s", fname, exc)
raise ESPHomeYAMLError(exc)
@@ -61,14 +64,83 @@ def dump(dict_):
dict_, default_flow_style=False, allow_unicode=True)
def custom_construct_pairs(loader, node):
pairs = []
for kv in node.value:
if isinstance(kv, yaml.ScalarNode):
obj = loader.construct_object(kv)
if not isinstance(obj, dict):
raise ESPHomeYAMLError(
"Expected mapping for anchored include tag, got {}".format(type(obj)))
for key, value in obj.iteritems():
pairs.append((key, value))
else:
key_node, value_node = kv
key = loader.construct_object(key_node)
value = loader.construct_object(value_node)
pairs.append((key, value))
return pairs
def custom_flatten_mapping(loader, node):
pre_merge = []
post_merge = []
index = 0
while index < len(node.value):
if isinstance(node.value[index], yaml.ScalarNode):
index += 1
continue
key_node, value_node = node.value[index]
if key_node.tag == u'tag:yaml.org,2002:merge':
del node.value[index]
if isinstance(value_node, yaml.MappingNode):
custom_flatten_mapping(loader, value_node)
node.value = node.value[:index] + value_node.value + node.value[index:]
elif isinstance(value_node, yaml.SequenceNode):
submerge = []
for subnode in value_node.value:
if not isinstance(subnode, yaml.MappingNode):
raise yaml.constructor.ConstructorError(
"while constructing a mapping", node.start_mark,
"expected a mapping for merging, but found %{}".format(subnode.id),
subnode.start_mark)
custom_flatten_mapping(loader, subnode)
submerge.append(subnode.value)
# submerge.reverse()
node.value = node.value[:index] + submerge + node.value[index:]
elif isinstance(value_node, yaml.ScalarNode):
node.value = node.value[:index] + [value_node] + node.value[index:]
# post_merge.append(value_node)
else:
raise yaml.constructor.ConstructorError(
"while constructing a mapping", node.start_mark,
"expected a mapping or list of mappings for merging, "
"but found {}".format(value_node.id), value_node.start_mark)
elif key_node.tag == u'tag:yaml.org,2002:value':
key_node.tag = u'tag:yaml.org,2002:str'
index += 1
else:
index += 1
if pre_merge:
node.value = pre_merge + node.value
if post_merge:
node.value = node.value + post_merge
def _ordered_dict(loader, node):
"""Load YAML mappings into an ordered dictionary to preserve key order."""
loader.flatten_mapping(node)
nodes = loader.construct_pairs(node)
custom_flatten_mapping(loader, node)
nodes = custom_construct_pairs(loader, node)
seen = {}
for (key, _), (child_node, _) in zip(nodes, node.value):
line = child_node.start_mark.line
for (key, _), nv in zip(nodes, node.value):
if isinstance(nv, yaml.ScalarNode):
line = nv.start_mark.line
else:
line = nv[0].start_mark.line
try:
hash(key)
@@ -195,6 +267,10 @@ def _secret_yaml(loader, node):
return secrets[node.value]
def _lambda(loader, node):
return Lambda(unicode(node.value))
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict)
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
yaml.SafeLoader.add_constructor('!env_var', _env_var_yaml)
@@ -206,6 +282,7 @@ yaml.SafeLoader.add_constructor('!include_dir_merge_list',
yaml.SafeLoader.add_constructor('!include_dir_named', _include_dir_named_yaml)
yaml.SafeLoader.add_constructor('!include_dir_merge_named',
_include_dir_merge_named_yaml)
yaml.SafeLoader.add_constructor('!lambda', _lambda)
# From: https://gist.github.com/miracle2k/3184458
@@ -270,6 +347,19 @@ def represent_time_period(dumper, data):
return represent_odict(dumper, 'tag:yaml.org,2002:map', dictionary)
def represent_lambda(_, data):
node = yaml.ScalarNode(tag='!lambda', value=data.value, style='>')
return node
def represent_id(_, data):
return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=data.id)
def represent_uuid(_, data):
return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=str(data))
yaml.SafeDumper.add_representer(
OrderedDict,
lambda dumper, value:
@@ -286,7 +376,7 @@ yaml.SafeDumper.add_representer(unicode, unicode_representer)
yaml.SafeDumper.add_representer(HexInt, hex_int_representer)
yaml.SafeDumper.add_representer(IPAddress, stringify_representer)
yaml.SafeDumper.add_representer(MACAddress, stringify_representer)
yaml.SafeDumper.add_representer(TimePeriod, represent_time_period)
yaml.SafeDumper.add_representer(TimePeriodMicroseconds, represent_time_period)
yaml.SafeDumper.add_representer(TimePeriodMilliseconds, represent_time_period)
yaml.SafeDumper.add_representer(TimePeriodSeconds, represent_time_period)
yaml.SafeDumper.add_multi_representer(TimePeriod, represent_time_period)
yaml.SafeDumper.add_multi_representer(Lambda, represent_lambda)
yaml.SafeDumper.add_multi_representer(core.ID, represent_id)
yaml.SafeDumper.add_multi_representer(uuid.UUID, represent_uuid)

View File

@@ -1,3 +0,0 @@
# Examples
This directory contains some of the ESP32/ESP8266 nodes I use at my home.

View File

@@ -1,127 +0,0 @@
esphomeyaml:
name: cabinet
platform: ESP32
board: nodemcu-32s
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.203
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: cabinet
password: '[PASSWORD]'
# This is the default
discovery: true
power_supply:
- id: 'atx'
pin:
number: 13
inverted: true
i2c:
sda: 14
scl: 27
frequency: 400000
pca9685:
- id: 'pca9685'
frequency: 500
output:
- platform: pca9685
pca9685_id: 'pca9685'
id: 'cabinet1_red'
channel: 14
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'cabinet1_green'
channel: 15
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'cabinet1_blue'
channel: 13
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'cabinet2_red'
channel: 11
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'cabinet2_green'
channel: 12
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'cabinet2_blue'
channel: 10
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'room_red'
channel: 8
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'room_green'
channel: 9
power_supply: 'atx'
- platform: pca9685
pca9685_id: 'pca9685'
id: 'room_blue'
channel: 7
power_supply: 'atx'
light:
- platform: rgb
name: 'Cabinet Light 1'
red: 'cabinet1_red'
green: 'cabinet1_green'
blue: 'cabinet1_blue'
- platform: rgb
name: 'Cabinet Light 2'
red: 'cabinet2_red'
green: 'cabinet2_green'
blue: 'cabinet2_blue'
- platform: rgb
name: 'Room Light'
red: 'room_red'
green: 'room_green'
blue: 'room_blue'
sensor:
- platform: dht
pin: 23
temperature:
name: 'Cabinet Temperature'
humidity:
name: 'Cabinet Humidity'
model: DHT22
binary_sensor:
- platform: gpio
pin: 25
name: 'Cabinet Motion'
device_class: motion
# Simple binary sensor that uses last will and birth messages to show
# node state
- platform: status
name: "Cabinet Status"
switch:
# Simple switch that restarts the ESP32
- platform: restart
name: "Cabinet Restart"

View File

@@ -1,57 +0,0 @@
esphomeyaml:
name: dachboden
platform: ESP8266
board: nodemcuv2
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.212
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: dachboden
password: '[PASSWORD]'
# This is the default
discovery: true
dallas:
id: 'dallas'
pin: D1
sensor:
- platform: dht
pin: D3
temperature:
name: 'Dachboden Temperatur'
humidity:
name: 'Dachboden Luftfeuchtigkeit'
model: DHT22
- platform: dallas
dallas_id: 'dallas'
address: 0x01031663650aff28
name: "Dachboden Solar Süd Vorlauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x2b0416638fe6ff28
name: "Dachboden Solar Süd Rücklauf"
- platform: adc
pin: A0
name: "Dachboden Helligkeit"
binary_sensor:
- platform: status
name: "Dachboden Status"
switch:
- platform: restart
name: "Dachboden Neustart"

View File

@@ -1,117 +0,0 @@
esphomeyaml:
name: heatpump
platform: ESP32
board: nodemcu-32s
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.204
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: heatpump
password: '[PASSWORD]'
# This is the default
discovery: true
dallas:
id: 'dallas'
pin: 15
sensor:
- platform: dht
pin: 0
temperature:
name: 'Outside Temperature'
humidity:
name: 'Outside Humidity'
model: DHT22
- platform: pulse_counter
pin: 12
unit_of_measurement: 'kW'
name: 'Stromverbrauch Wintergarten'
update_interval: 30s
expire_after: 60s
filters:
- multiply: 0.06
- platform: pulse_counter
pin: 13
unit_of_measurement: 'kW'
name: 'Stromverbrauch Wärmepumpe'
update_interval: 30s
expire_after: 60s
filters:
- multiply: 0.06
- platform: pulse_counter
pin: 14
unit_of_measurement: 'kW'
name: 'Stromverbrauch Gesamt'
update_interval: 30s
expire_after: 60s
filters:
- multiply: 0.06
- platform: dallas
dallas_id: 'dallas'
address: 0xfe0000031f1eaf28
name: "Boiler Temperatur Oben"
- platform: dallas
dallas_id: 'dallas'
address: 0xba0000031f0e5228
name: "Boiler Temperatur Unten"
- platform: dallas
dallas_id: 'dallas'
address: 0xa40000031f055028
name: "Boiler Temperatur Mitte"
- platform: dallas
dallas_id: 'dallas'
address: 0x790000031ee1dc28
name: "Heizung Rücklauf"
- platform: dallas
dallas_id: 'dallas'
address: 0xdd0000031efb0428
name: "Ölheizung Vorlauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x710000031f0e7e28
name: "Boiler Solar Rücklauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x92041703081aff28
name: "Boiler Solar Vorlauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x2c04173159f4ff28
name: "Heizung Vorlauf"
- platform: dallas
dallas_id: 'dallas'
address: 0xd10417315babff28
name: "Wärmepumpe Vorlauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x6c0517024a17ff28
name: "Boiler Heizung Vorlauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x7d04173139eeff28
name: "Wärmepumpe Rücklauf"
- platform: dallas
dallas_id: 'dallas'
address: 0x3204166398a5ff28
name: "Wärmepumpe Verdampfer"
binary_sensor:
- platform: status
name: "Heizung Status"
switch:
- platform: restart
name: "Heizung Neustart"

View File

@@ -1,60 +0,0 @@
esphomeyaml:
name: kuche
platform: ESP8266
board: nodemcuv2
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.211
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: kuche
password: '[PASSWORD]'
# This is the default
discovery: true
dallas:
id: 'dallas'
pin: D1
sensor:
- platform: dallas
dallas_id: 'dallas'
address: 0x69041662d7f1ff28
name: "Küche Raumtemperatur"
- platform: dallas
dallas_id: 'dallas'
address: 0x800416636bebff28
name: "Küche Heizkörpertemperatur"
- platform: adc
pin: A0
name: "Küche Helligkeit"
output:
- platform: gpio
pin: D2
id: 'ventilator'
fan:
- platform: binary
output: 'ventilator'
name: 'Küche Heizkörper Ventilator'
binary_sensor:
- platform: status
name: "Küche Status"
switch:
- platform: restart
name: "Küche Neustart"

View File

@@ -1,58 +0,0 @@
esphomeyaml:
name: lebensmittelkeller
platform: ESP8266
board: nodemcuv2
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.209
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: lebensmittelkeller
password: '[PASSWORD]'
# This is the default
discovery: true
sensor:
- platform: dht
pin: D3
temperature:
name: 'Lebensmittelkeller Temperatur'
humidity:
name: 'Lebensmittelkeller Feuchtigkeit'
model: DHT22
- platform: adc
pin: A0
name: "Lebensmittelkeller Helligkeit"
output:
- platform: gpio
pin: D4
id: 'ventilator'
fan:
- platform: binary
output: 'ventilator'
name: 'Lebensmittelkeller Ventilator'
switch:
- platform: gpio
pin: D2
name: 'Lebensmittelkeller Entfeuchter'
icon: 'mdi:water-off'
- platform: restart
name: "Lebensmittelkeller Neustart"
binary_sensor:
- platform: status
name: "Lebensmittelkeller Status"

View File

@@ -1,344 +0,0 @@
esphomeyaml:
name: livingroom
platform: ESP32
board: nodemcu-32s
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.201
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: livingroom
password: '[PASSWORD]'
# This is the default
discovery: true
output:
- platform: ledc
id: 'fan_float'
frequency: 50000
pin: 22
bit_depth: 8
dallas:
pin: 23
id: dallas
sensor:
- platform: dallas
dallas_id: dallas
address: 0x1c0000031edd2a28
name: "Wohnzimmer Raumtemperatur"
filters:
- sliding_window_moving_average:
window_size: 15
send_every: 15
- filter_out: 85
- platform: dallas
dallas_id: dallas
address: 0x7a0315a8371eff28
name: "Wohnzimmer Heizkörpertemperatur"
update_interval: 30s
filters:
- sliding_window_moving_average:
window_size: 15
send_every: 15
- filter_out: 85
fan:
- platform: speed
output: 'fan_float'
name: 'Wohnzimmer Heizkörper Ventilator'
binary_sensor:
- platform: status
name: "Wohnzimmer Status"
ir_transmitter:
pin: 32
id: 'ir'
switch:
- platform: restart
name: "Wohnzimmer Neustart"
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV On"
panasonic:
address: 0x4004
command: 0x100BCBD
repeat: 25
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Off"
panasonic:
address: 0x4004
command: 0x100BCBD
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV SD Card"
panasonic:
address: 0x4004
command: 0x190D544
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Input TV"
panasonic:
address: 0x4004
command: 0x1400C4D
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Input AV"
panasonic:
address: 0x4004
command: 0x100A0A1
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Menu"
panasonic:
address: 0x4004
command: 0x1004A4B
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Aspect Ratio"
panasonic:
address: 0x4004
command: 0x1207B5A
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Viera Cast"
panasonic:
address: 0x4004
command: 0x190C958
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Direct TV REC"
panasonic:
address: 0x4004
command: 0x1909100
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Info"
panasonic:
address: 0x4004
command: 0x1009C9D
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Exit"
panasonic:
address: 0x4004
command: 0x100CBCA
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Viera Link"
panasonic:
address: 0x4004
command: 0x1908D1C
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Viera Tools"
panasonic:
address: 0x4004
command: 0x100F7F6
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Guide"
panasonic:
address: 0x4004
command: 0x190E170
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Up"
panasonic:
address: 0x4004
command: 0x1005253
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Left"
panasonic:
address: 0x4004
command: 0x1007273
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV OK"
panasonic:
address: 0x4004
command: 0x1009293
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Right"
panasonic:
address: 0x4004
command: 0x100F2F3
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Down"
panasonic:
address: 0x4004
command: 0x100D2D3
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Option"
panasonic:
address: 0x4004
command: 0x190E574
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Back"
panasonic:
address: 0x4004
command: 0x1002B2A
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Red"
panasonic:
address: 0x4004
command: 0x1000E0F
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Green"
panasonic:
address: 0x4004
command: 0x1000E0F # TODO: FIXME
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Yellow"
panasonic:
address: 0x4004
command: 0x1004E4F
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Blue"
panasonic:
address: 0x4004
command: 0x100CECF
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Text"
panasonic:
address: 0x4004
command: 0x180C041
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Subtitle"
panasonic:
address: 0x4004
command: 0x180A021
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Index"
panasonic:
address: 0x4004
command: 0x1801091
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Hold"
panasonic:
address: 0x4004
command: 0x1809011
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 1"
panasonic:
address: 0x4004
command: 0x1000809
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 2"
panasonic:
address: 0x4004
command: 0x1008889
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 3"
panasonic:
address: 0x4004
command: 0x1004849
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 4"
panasonic:
address: 0x4004
command: 0x100C8C9
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 5"
panasonic:
address: 0x4004
command: 0x1002829
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 6"
panasonic:
address: 0x4004
command: 0x100A8A9
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 7"
panasonic:
address: 0x4004
command: 0x1006869
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 8"
panasonic:
address: 0x4004
command: 0x100E8E9
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 9"
panasonic:
address: 0x4004
command: 0x1001819
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV 0"
panasonic:
address: 0x4004
command: 0x1009899
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Mute"
panasonic:
address: 0x4004
command: 0x1004C4D
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Last View"
panasonic:
address: 0x4004
command: 0x100ECED
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Volume Up"
panasonic:
address: 0x4004
command: 0x1000405
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Volume Down"
panasonic:
address: 0x4004
command: 0x1008485
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Program Up"
panasonic:
address: 0x4004
command: 0x1002C2D
- platform: ir_transmitter
ir_transmitter_id: 'ir'
name: "Panasonic TV Program Down"
panasonic:
address: 0x4004
command: 0x100ACAD

View File

@@ -1,48 +0,0 @@
esphomeyaml:
name: terrasse
platform: ESP32
board: nodemcu-32s
logger:
level: verbose
wifi:
ssid: '[SSID]'
password: '[PASSWORD]'
manual_ip:
static_ip: 192.168.178.205
gateway: 192.168.178.1
subnet: 255.255.255.0
ota:
mqtt:
broker: 192.168.178.84
username: terrasse
password: '[PASSWORD]'
# This is the default
discovery: true
dallas:
pin: 25
id: dallas
sensor:
- platform: pulse_counter
pin: 34
name: "Terrasse Wind"
- platform: pulse_counter
pin: 39
name: "Terrasse Regen"
- platform: dallas
dallas_id: dallas
index: 0
name: "Terrasse Temperatur"
binary_sensor:
- platform: status
name: "Terrasse Status"
switch:
- platform: restart
name: "Terrasse Neustart"

View File

@@ -14,6 +14,10 @@ disable=
too-many-arguments,
too-many-return-statements,
duplicate-code,
invalid-name,
cyclic-import,
redefined-builtin,
undefined-loop-variable,
additional-builtins=

5
repository.json Normal file
View File

@@ -0,0 +1,5 @@
{
"name": "esphomeyaml HassIO Add-On Repository",
"url": "https://github.com/OttoWinter/esphomeyaml",
"maintainer": "Otto Winter <contact@otto-winter.com>"
}

View File

@@ -1,5 +1,7 @@
voluptuous==0.11.1
platformio==3.5.2
pyyaml==3.12
paho-mqtt==1.3.1
colorlog==3.1.2
voluptuous>=0.11.1
platformio>=3.5.3
pyyaml>=3.12
paho-mqtt>=1.3.1
colorlog>=3.1.2
tornado>=5.0.0
esptool>=2.3.1

View File

@@ -23,10 +23,12 @@ DOWNLOAD_URL = '{}/archive/{}.zip'.format(GITHUB_URL, const.__version__)
REQUIRES = [
'voluptuous>=0.11.1',
'platformio>=3.5.2',
'platformio>=3.5.3',
'pyyaml>=3.12',
'paho-mqtt>=1.3.1',
'colorlog>=3.1.2',
'tornado>=5.0.0',
'esptool>=2.3.1',
]
CLASSIFIERS = [

11
tests/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Tests for esphomeyaml
This directory contains some tests for esphomeyaml.
At the moment, all the tests only work by simply executing
`esphomeyaml` over some YAML files that are made to test
all of esphomeyaml's features.
Of course this is all just very high-level and things like
unit tests would be much better. So if you have time and know
how to set up a unit testing framework for python, please do
give it a try.

943
tests/test1.yaml Normal file
View File

@@ -0,0 +1,943 @@
esphomeyaml:
name: test1
platform: ESP32
board: nodemcu-32s
# Use latest upstream esphomelib git version.
esphomelib_version: dev
# Use this for testing while developing:
# esphomelib_version:
# local: ~/path/to/esphomelib
use_custom_code: false
on_boot:
priority: 150.0
then:
- lambda: >-
ESP_LOGD("main", "ON BOOT!");
on_shutdown:
then:
- lambda: >-
ESP_LOGD("main", "ON SHUTDOWN!");
on_loop:
then:
- lambda: >-
ESP_LOGV("main", "ON LOOP!");
build_path: build/test1
wifi:
ssid: 'MySSID'
password: 'password1'
manual_ip:
static_ip: 192.168.178.230
gateway: 192.168.178.1
subnet: 255.255.255.0
dns1: 1.1.1.1
dns2: 1.2.2.1
hostname: myverylonghostname
domain: .local
reboot_timeout: 120s
power_save_mode: none
mqtt:
broker: '192.168.178.84'
port: 1883
username: 'debug'
password: 'debug'
client_id: someclient
discovery: True
discovery_retain: False
discovery_prefix: discovery
topic_prefix: helloworld
log_topic:
topic: helloworld/hi
level: INFO
birth_message:
will_message:
shutdown_message:
topic: topic/to/send/to
payload: hi
qos: 2
retain: True
keepalive: 60s
reboot_timeout: 60s
on_message:
- topic: my/custom/topic
qos: 0
then:
- lambda: >-
ESP_LOGD("main", "Got message %s", x.c_str());
- topic: livingroom/ota_mode
then:
- deep_sleep.prevent:
id: deep_sleep_1
- topic: livingroom/ota_mode
then:
- deep_sleep.enter:
id: deep_sleep_1
on_json_message:
topic: the/topic
then:
- lambda: |-
int data = x["my_data"];
ESP_LOGD("main", "The data is: %d", data);
- light.turn_on:
id: living_room_lights
transition_length: !lambda |-
int length = 1000;
if (x.containsKey("length"))
length = x["length"];
return length;
effect: !lambda |-
const char *effect = "None";
if (x.containsKey("effect"))
effect = x["effect"];
return effect;
i2c:
sda: 21
scl: 22
scan: True
frequency: 100kHz
spi:
clk_pin: GPIO21
mosi_pin: GPIO22
miso_pin: GPIO23
uart:
tx_pin: GPIO22
rx_pin: GPIO23
baud_rate: 115200
ota:
safe_mode: True
password: 'superlongpasswordthatnoonewillknow'
port: 3286
logger:
baud_rate: 0
level: VERBOSE
logs:
mqtt.component: DEBUG
mqtt.client: ERROR
web_server:
port: 8080
css_url: https://esphomelib.com/_static/webserver-v1.min.css
js_url: https://esphomelib.com/_static/webserver-v1.min.js
power_supply:
id: 'atx_power_supply'
enable_time: 20ms
keep_on_time: 10s
pin:
number: 13
inverted: true
deep_sleep:
run_duration: 20s
run_cycles: 500
sleep_duration: 50s
wakeup_pin: GPIO39
wakeup_pin_mode: INVERT_WAKEUP
id: deep_sleep_1
ads1115:
address: 0x48
dallas:
pin: GPIO23
sensor:
- platform: adc
pin: A0
name: "Living Room Brightness"
update_interval: '1:01'
attenuation: 2.5db
unit_of_measurement: "°C"
icon: "mdi:water-percent"
accuracy_decimals: 5
expire_after: 120s
filters:
- offset: 2.0
- multiply: 1.2
- filter_out: 42.0
- filter_nan:
- sliding_window_moving_average:
window_size: 15
send_every: 15
- exponential_moving_average:
alpha: 0.1
send_every: 15
- throttle: 1s
- heartbeat: 5s
- debounce: 0.1s
- delta: 5.0
- unique:
- or:
- throttle: 1s
- delta: 5.0
- lambda: return x * (9.0/5.0) + 32.0;
on_value:
then:
- lambda: >-
ESP_LOGD("main", "Got value %f", x);
id(my_sensor).publish_state(42.0);
ESP_LOGI("main", "Value of my sensor: %f", id(my_sensor).state);
ESP_LOGI("main", "Raw Value of my sensor: %f", id(my_sensor).state);
on_value_range:
above: 5
below: 10
then:
- lambda: >-
ESP_LOGD("main", "Got value range %f", x);
on_raw_value:
- lambda: >-
ESP_LOGD("main", "Got raw value %f", x);
- logger.log:
level: DEBUG
format: "Got raw value %f"
args: ['x']
- logger.log: "Got raw value NAN"
- mqtt.publish:
topic: some/topic
payload: Hello
qos: 2
retain: True
- platform: ads1115
multiplexer: 'A0_A1'
gain: 1.024
id: my_sensor
filters:
state_topic: hi/me
retain: false
availability:
- platform: bh1750
name: "Living Room Brightness 3"
internal: true
address: 0x23
resolution: 1.0
update_interval: 30s
retain: False
availability:
state_topic: livingroom/custom_state_topic
- platform: bme280
temperature:
name: "Outside Temperature"
oversampling: 16x
pressure:
name: "Outside Pressure"
oversampling: none
humidity:
name: "Outside Humidity"
oversampling: 8x
address: 0x77
iir_filter: 16x
update_interval: 15s
- platform: bme680
temperature:
name: "Outside Temperature"
oversampling: 16x
pressure:
name: "Outside Pressure"
humidity:
name: "Outside Humidity"
gas_resistance:
name: "Outside Gas Sensor"
address: 0x77
heater:
temperature: 320
duration: 150ms
update_interval: 15s
- platform: bmp085
temperature:
name: "Outside Temperature"
pressure:
name: "Outside Pressure"
filters:
- lambda: >-
return x / powf(1.0 - (x / 44330.0), 5.255);
update_interval: 15s
- platform: bmp280
temperature:
name: "Outside Temperature"
oversampling: 16x
pressure:
name: "Outside Pressure"
address: 0x77
update_interval: 15s
iir_filter: 16x
- platform: dallas
address: 0x1C0000031EDD2A28
name: "Living Room Temperature"
resolution: 9
- platform: dallas
index: 1
name: "Living Room Temperature 2"
- platform: dht
pin: GPIO26
temperature:
name: "Living Room Temperature 3"
humidity:
name: "Living Room Humidity 3"
model: AM2302
update_interval: 15s
- platform: dht12
temperature:
name: "Living Room Temperature 4"
humidity:
name: "Living Room Humidity 4"
update_interval: 15s
- platform: duty_cycle
pin: GPIO25
name: Duty Cycle Sensor
- platform: esp32_hall
name: "ESP32 Hall Sensor"
update_interval: 15s
- platform: hdc1080
temperature:
name: "Living Room Temperature 5"
humidity:
name: "Living Room Pressure 5"
update_interval: 15s
- platform: hlw8012
sel_pin: 5
cf_pin: 14
cf1_pin: 13
current:
name: "HLW8012 Current"
voltage:
name: "HLW8012 Voltage"
power:
name: "HLW8012 Power"
update_interval: 15s
current_resistor: 0.001 ohm
voltage_divider: 2351
change_mode_every: 16
- platform: hmc5883l
address: 0x68
field_strength_x:
name: "HMC5883L Field Strength X"
field_strength_y:
name: "HMC5883L Field Strength Y"
field_strength_z:
name: "HMC5883L Field Strength Z"
heading:
name: "HMC5883L Heading"
range: 130uT
update_interval: 15s
- platform: hx711
name: "HX711 Value"
dout_pin: GPIO23
clk_pin: GPIO24
gain: 128
update_interval: 15s
- platform: ina219
address: 0x40
shunt_resistance: 0.1 ohm
current:
name: "INA219 Current"
power:
name: "INA219 Power"
bus_voltage:
name: "INA219 Bus Voltage"
shunt_voltage:
name: "INA219 Shunt Voltage"
max_voltage: 32.0V
max_current: 3.2A
update_interval: 15s
- platform: ina3221
address: 0x40
channel_1:
shunt_resistance: 0.1 ohm
current:
name: "INA3221 Channel 1 Current"
power:
name: "INA3221 Channel 1 Power"
bus_voltage:
name: "INA3221 Channel 1 Bus Voltage"
shunt_voltage:
name: "INA3221 Channel 1 Shunt Voltage"
update_interval: 15s
- platform: htu21d
temperature:
name: "Living Room Temperature 6"
humidity:
name: "Living Room Humidity 6"
update_interval: 15s
- platform: max6675
name: "Living Room Temperature"
cs_pin: GPIO23
update_interval: 15s
- platform: mhz19
co2:
name: "MH-Z19 CO2 Value"
temperature:
name: "MH-Z19 Temperature"
update_interval: 15s
- platform: mpu6050
address: 0x68
accel_x:
name: "MPU6050 Accel X"
accel_y:
name: "MPU6050 Accel Y"
accel_z:
name: "MPU6050 Accel z"
gyro_x:
name: "MPU6050 Gyro X"
gyro_y:
name: "MPU6050 Gyro Y"
gyro_z:
name: "MPU6050 Gyro z"
temperature:
name: "MPU6050 Temperature"
- platform: ms5611
temperature:
name: "Outside Temperature"
pressure:
name: "Outside Pressure"
address: 0x77
update_interval: 15s
- platform: pulse_counter
name: "Pulse Counter"
pin: GPIO12
count_mode:
rising_edge: INCREMENT
falling_edge: DECREMENT
internal_filter: 13us
update_interval: 15s
- platform: rotary_encoder
name: "Rotary Encoder"
pin_a: GPIO23
pin_b: GPIO24
pin_reset: GPIO25
filters:
- or:
- debounce: 0.1s
- delta: 10
- platform: sht3xd
temperature:
name: "Living Room Temperature 8"
humidity:
name: "Living Room Humidity 8"
address: 0x44
update_interval: 15s
- platform: template
name: "Template Sensor"
lambda: >-
if (id(ultrasonic_sensor1).state > 1) {
return 42.0;
} else {
return {};
}
update_interval: 15s
- platform: tsl2561
name: "TSL2561 Ambient Light"
address: 0x39
update_interval: 15s
is_cs_package: true
integration_time: 402ms
gain: 16x
- platform: ultrasonic
trigger_pin: GPIO24
echo_pin:
number: GPIO23
inverted: true
name: "Ultrasonic Sensor"
timeout_meter: 5.5
id: ultrasonic_sensor1
- platform: uptime
name: Uptime Sensor
- platform: wifi_signal
name: "WiFi Signal Sensor"
update_interval: 15s
esp32_touch:
setup_mode: False
iir_filter: 10ms
sleep_duration: 27ms
measurement_duration: 8ms
low_voltage_reference: 0.5V
high_voltage_reference: 2.7V
voltage_attenuation: 1.5V
binary_sensor:
- platform: gpio
pin: GPIO9
name: "Living Room Window"
device_class: window
filters:
- invert:
- delayed_on: 40ms
- delayed_off: 40ms
- heartbeat: 1s
on_press:
then:
- lambda: >-
ESP_LOGD("main", "Pressed");
on_release:
then:
- lambda: >-
ESP_LOGD("main", "Released");
on_click:
- min_length: 50ms
max_length: 350ms
then:
- lambda: >-
ESP_LOGD("main", "Clicked");
- then:
- lambda: >-
ESP_LOGD("main", "Clicked");
on_double_click:
- min_length: 50ms
max_length: 350ms
then:
- lambda: >-
ESP_LOGD("main", "Double Clicked");
- then:
- lambda: >-
ESP_LOGD("main", "Double Clicked");
id: binary_sensor1
- platform: status
name: "Living Room Status"
- platform: esp32_touch
name: "ESP32 Touch Pad GPIO27"
pin: GPIO27
threshold: 1000
- platform: nextion
page_id: 0
component_id: 2
name: "Nextion Component 2 Touch"
- platform: template
name: "Garage Door Open"
lambda: >-
if (isnan(id(my_sensor).state)) {
// isnan checks if the ultrasonic sensor echo
// has timed out, resulting in a NaN (not a number) state
// in that case, return {} to indicate that we don't know.
return {};
} else if (id(my_sensor).state > 30) {
// Garage Door is open.
return true;
} else {
// Garage Door is closed.
return false;
}
- platform: pn532
uid: 74-10-37-94
name: "PN532 NFC Tag"
- platform: rdm6300
uid: 7616525
name: "RDM6300 NFC Tag"
- platform: gpio
name: "PCF binary sensor"
pin:
pcf8574: pcf8574_hub
number: 1
mode: INPUT
inverted: True
pca9685:
frequency: 500
address: 0x0
output:
- platform: gpio
pin: GPIO26
id: gpio_26
power_supply: atx_power_supply
inverted: False
- platform: ledc
pin: 19
id: gpio_19
frequency: 1500Hz
bit_depth: 8
channel: 14
max_power: 0.5
- platform: pca9685
id: pca_0
channel: 0
- platform: pca9685
id: pca_1
channel: 1
- platform: pca9685
id: pca_2
channel: 2
- platform: pca9685
id: pca_3
channel: 3
- platform: pca9685
id: pca_4
channel: 4
- platform: pca9685
id: pca_5
channel: 5
- platform: pca9685
id: pca_6
channel: 6
- platform: pca9685
id: pca_7
channel: 7
- platform: gpio
id: id2
pin:
pcf8574: pcf8574_hub
number: 0
mode: OUTPUT
inverted: False
light:
- platform: binary
name: "Desk Lamp"
output: gpio_26
effects:
- strobe:
- strobe:
name: "My Strobe"
colors:
- state: True
duration: 250ms
- state: False
duration: 250ms
- platform: monochromatic
name: "Kitchen Lights"
id: kitchen
output: gpio_19
gamma_correct: 2.8
default_transition_length: 2s
effects:
- strobe:
- flicker:
- flicker:
name: "My Flicker"
alpha: 98%
intensity: 1.5%
- lambda:
name: My Custom Effect
update_interval: 1s
lambda: |-
static int state = 0;
state += 1;
if (state == 4)
state = 0;
- platform: rgb
name: "Living Room Lights"
id: living_room_lights
red: pca_0
green: pca_1
blue: pca_2
- platform: rgbw
name: "Living Room Lights 2"
red: pca_3
green: pca_4
blue: pca_5
white: pca_6
- platform: rgbww
name: "Living Room Lights 2"
red: pca_3
green: pca_4
blue: pca_5
cold_white: pca_6
warm_white: pca_6
cold_white_color_temperature: 153 mireds
warm_white_color_temperature: 500 mireds
- platform: cwww
name: "Living Room Lights 2"
cold_white: pca_6
warm_white: pca_6
cold_white_color_temperature: 153 mireds
warm_white_color_temperature: 500 mireds
- platform: fastled_clockless
chipset: WS2811
pin: GPIO23
num_leds: 60
rgb_order: BRG
max_refresh_rate: 20ms
power_supply: atx_power_supply
color_correct: [75%, 100%, 50%]
name: "FastLED WS2811 Light"
effects:
- fastled_color_wipe:
- fastled_color_wipe:
name: Color Wipe Effect With Custom Values
colors:
- red: 100%
green: 100%
blue: 100%
num_leds: 1
- red: 0%
green: 0%
blue: 0%
num_leds: 1
add_led_interval: 100ms
reverse: False
- fastled_scan:
- fastled_scan:
name: Scan Effect With Custom Values
move_interval: 100ms
- fastled_twinkle:
- fastled_twinkle:
name: Twinkle Effect With Custom Values
twinkle_probability: 5%
progress_interval: 4ms
- fastled_random_twinkle:
- fastled_random_twinkle:
name: Random Twinkle Effect With Custom Values
twinkle_probability: 5%
progress_interval: 32ms
- fastled_fireworks:
- fastled_fireworks:
name: Fireworks Effect With Custom Values
update_interval: 32ms
spark_probability: 10%
use_random_color: false
fade_out_rate: 120
- fastled_flicker:
- fastled_flicker:
name: Flicker Effect With Custom Values
update_interval: 16ms
intensity: 5%
- platform: fastled_spi
chipset: WS2801
data_pin: GPIO23
clock_pin: GPIO22
num_leds: 60
rgb_order: BRG
name: "FastLED SPI Light"
remote_transmitter:
- pin: 32
switch:
- platform: gpio
pin: GPIO25
name: "Living Room Dehumidifier"
icon: "mdi:restart"
inverted: True
command_topic: custom_command_topic
power_on_value: True
- platform: remote_transmitter
name: "Panasonic TV Off"
nec:
address: 0x4242
command: 0x8484
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
lg:
data: 4294967295
nbits: 28
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
samsung:
data: 0xABCDEF
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
sony:
data: 0xABCDEF
nbits: 12
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
panasonic:
address: 0x4004
command: 0x1000BCD
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
rc_switch_raw:
code: '001010011001111101011011'
protocol: 1
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
rc_switch_type_a:
group: '11001'
device: '01000'
state: True
protocol:
pulse_length: 175
sync: [1, 31]
zero: [1, 3]
one: [3, 1]
inverted: False
repeat: 25
- platform: remote_transmitter
name: "Panasonic TV Off"
rc_switch_type_b:
address: 4
channel: 2
state: True
- platform: remote_transmitter
name: "Panasonic TV Off"
rc_switch_type_c:
family: 'a'
group: 1
device: 2
state: True
- platform: remote_transmitter
name: "Panasonic TV Off"
id: living_room_lights_on
rc_switch_type_d:
group: 'a'
device: 2
state: True
- platform: remote_transmitter
name: "Panasonic TV Off"
id: living_room_lights_off
raw:
carrier_frequency: 35kHz
data:
- 1000
- -1000
- platform: template
name: Living Room Lights
optimistic: True
turn_on_action:
- switch.turn_on: living_room_lights_on
turn_off_action:
- switch.turn_on: living_room_lights_off
- platform: restart
name: "Living Room Restart"
- platform: shutdown
name: "Living Room Shutdown"
- platform: output
name: "Generic Output"
output: pca_6
- platform: template
name: "Template Switch"
id: my_switch
lambda: |-
if (id(binary_sensor1).state) {
return true;
} else {
return {};
}
id(my_switch).publish_state(false);
id(my_switch).publish_state(true);
if (id(my_switch).state) {
// Switch is ON, do something here
id(my_switch).turn_off();
id(my_switch).turn_on();
} else {
// Switch is OFF, do something else here
}
optimistic: true
- platform: uart
name: "UART String Output"
data: 'DataToSend'
- platform: uart
name: "UART Bytes Output"
data: [0xDE, 0xAD, 0xBE, 0xEF]
fan:
- platform: binary
output: gpio_26
name: "Living Room Fan 1"
- platform: speed
output: pca_6
name: "Living Room Fan 2"
speed:
low: 0.45
medium: 0.75
high: 1.0
display:
- platform: lcd_gpio
dimensions: 18x4
data_pins:
- GPIO19
- GPIO20
- GPIO21
- GPIO22
enable_pin: GPIO23
rs_pin: GPIO24
lambda: |-
it.print("Hello World!");
- platform: lcd_pcf8574
dimensions: 18x4
address: 0x3F
lambda: |-
it.print("Hello World!");
- platform: max7219
cs_pin: GPIO23
num_chips: 1
lambda: |-
it.print("01234567");
- platform: nextion
lambda: |-
it.set_component_value("gauge", 50);
it.set_component_text("textview", "Hello World!");
- platform: ssd1306_i2c
model: "SSD1306 128x64"
reset_pin: GPIO23
address: 0x3C
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
- platform: ssd1306_spi
model: "SSD1306 128x64"
cs_pin: GPIO23
dc_pin: GPIO23
reset_pin: GPIO23
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
- platform: waveshare_epaper
cs_pin: GPIO23
dc_pin: GPIO23
busy_pin: GPIO23
reset_pin: GPIO23
model: 2.90in
full_update_every: 30
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
remote_receiver:
pin: GPIO32
dump: all
status_led:
pin: GPIO2
pn532:
cs_pin: GPIO23
update_interval: 1s
on_tag:
- lambda: |-
ESP_LOGD("main", "Found tag %s", x.c_str());
- mqtt.publish:
topic: the/topic
payload: !lambda 'return x;'
rdm6300:
time:
- platform: sntp
id: sntp_time
servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
- 2.pool.ntp.org
on_time:
cron: '/30 0-30,30/5 * ? JAN-DEC MON,SAT-SUN,TUE-FRI'
then:
- lambda: 'ESP_LOGD("main", "time");'
cover:
- platform: template
name: "Template Cover"
lambda: >-
if (id(binary_sensor1).state) {
return cover::COVER_OPEN;
} else {
return {};
}
optimistic: true
debug:
pcf8574:
- id: 'pcf8574_hub'
address: 0x21
pcf8575: False

168
tests/test2.yaml Normal file
View File

@@ -0,0 +1,168 @@
esphomeyaml:
name: test2
platform: ESP32
board: nodemcu-32s
# Use latest upstream esphomelib git version.
esphomelib_version: dev
# Use this for testing while developing:
# esphomelib_version:
# local: ~/path/to/esphomelib
use_custom_code: true
build_path: build/test2
wifi:
ssid: 'MySSID'
password: 'password1'
reboot_timeout: 120s
mqtt:
broker: '192.168.178.84'
port: 1883
username: 'debug'
password: 'debug'
i2c:
sda: 21
scl: 22
scan: False
spi:
clk_pin: GPIO21
mosi_pin: GPIO22
miso_pin: GPIO23
uart:
tx_pin: GPIO22
rx_pin: GPIO23
baud_rate: 115200
ota:
safe_mode: True
port: 3286
logger:
level: DEBUG
web_server:
deep_sleep:
run_duration: 20s
sleep_duration: 50s
sensor:
- platform: ble_rssi
mac_address: AC:37:43:77:5F:4C
name: "BLE Google Home Mini RSSI value"
- platform: xiaomi_miflora
mac_address: 94:2B:FF:5C:91:61
temperature:
name: "Xiaomi MiFlora Temperature"
moisture:
name: "Xiaomi MiFlora Moisture"
illuminance:
name: "Xiaomi MiFlora Illuminance"
conductivity:
name: "Xiaomi MiFlora Soil Conductivity"
battery_level:
name: "Xiaomi MiFlora Battery Level"
- platform: xiaomi_mijia
mac_address: 7A:80:8E:19:36:BA
temperature:
name: "Xiaomi MiJia Temperature"
humidity:
name: "Xiaomi MiJia Humidity"
battery_level:
name: "Xiaomi MiJia Battery Level"
- platform: mqtt_subscribe
name: "MQTT Subscribe Sensor 1"
topic: "mqtt/topic"
id: the_sensor
qos: 2
on_value:
- mqtt.publish_json:
topic: the/topic
payload: |-
root["key"] = id(the_sensor).value;
root["greeting"] = "Hello World";
- platform: pmsx003
type: PMSX003
pm_1_0:
name: "PM 1.0 Concentration"
pm_2_5:
name: "PM 2.5 Concentration"
pm_10_0:
name: "PM 10.0 Concentration"
- platform: pmsx003
type: PMS5003T
pm_2_5:
name: "PM 2.5 Concentration"
temperature:
name: "PMS Temperature"
humidity:
name: "PMS Humidity"
- platform: pmsx003
type: PMS5003ST
pm_2_5:
name: "PM 2.5 Concentration"
temperature:
name: "PMS Temperature"
humidity:
name: "PMS Humidity"
formaldehyde:
name: "PMS Formaldehyde Concentration"
- platform: cse7766
voltage:
name: "CSE7766 Voltage"
current:
name: "CSE7766 Current"
power:
name: "CSE776 Power"
esp32_touch:
setup_mode: True
binary_sensor:
- platform: esp32_ble_tracker
mac_address: AC:37:43:77:5F:4C
name: "ESP32 BLE Tracker Google Home Mini"
- platform: esp32_touch
name: "ESP32 Touch Pad GPIO27"
pin: GPIO27
threshold: 1000
remote_receiver:
pin: GPIO32
dump: []
esp32_ble_tracker:
scan_interval: 300s
esp32_ble_beacon:
type: iBeacon
uuid: 'c29ce823-e67a-4e71-bff2-abaa32e77a98'
status_led:
pin: GPIO2
text_sensor:
- platform: version
name: "Esphomelib Version"
icon: mdi:icon
id: version_sensor
on_value:
- lambda: |-
ESP_LOGD("main", "The state is %s=%s", x.c_str(), id(version_sensor).state.c_str());
- script.execute: my_script
- platform: mqtt_subscribe
name: "MQTT Subscribe Text"
topic: "the/topic"
qos: 2
- platform: template
name: "Template Text Sensor"
lambda: |-
return {"Hello World"};
script:
- id: my_script
then:
- lambda: 'ESP_LOGD("main", "Hello World!");'