1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-05 09:31:54 +00:00

Compare commits

...

154 Commits

Author SHA1 Message Date
Otto Winter
e2b1d5438d Bump version to v1.11.0b2 2019-02-20 12:50:28 +01:00
Otto Winter
fe66f93a01 Fix MQTT log topic level (#445) 2019-02-20 12:50:24 +01:00
Florian Gareis
581dffe2d4 Remove unnecessary wrapper (#444) 2019-02-20 12:50:24 +01:00
Florian Gareis
6f22006bf7 Remove duplicate scrollbar & move scrollbar (#443)
* Remove duplicate scrollbar

* Move scrolling from modal-content to log-container

* Replace css autoscroll with stable js autoscroll
2019-02-20 12:50:24 +01:00
Otto Winter
fe54700687 Fix custom components not registered (#441) 2019-02-20 12:50:24 +01:00
Otto Winter
efe81e0856 Bump version to v1.11.0b1 2019-02-17 20:23:20 +01:00
Otto Winter
41d637affe Add wait_until action (#440) 2019-02-17 19:27:33 +01:00
Florian Gareis
10cc11bab1 Include tapTarget html element only when needed (#439) 2019-02-17 18:42:44 +01:00
Otto Winter
34ca5d6d8a Update base image to 1.2.1 2019-02-17 15:54:15 +01:00
Florian Gareis
b27c778cb7 Fix dashboard style issues (#437)
* Update dashboard copyright year

* Fix overflow and select color issue

* Make upload and compile modals not dismissible on outside click

* Fix to small dropdown
2019-02-17 15:31:41 +01:00
Otto Winter
f311a1bb30 Add display page abstraction (#435) 2019-02-17 00:35:23 +01:00
Otto Winter
7a450ed41c Add hidden option to wifi networks (#436) 2019-02-16 16:49:27 +01:00
Otto Winter
944f0169cb Add light partition platform (#434) 2019-02-16 16:47:23 +01:00
Otto Winter
4da0e0c223 Add text_sensor.template.publish action (#433)
* Add text_sensor.template.publish action

* Fix

* Add test
2019-02-16 16:47:05 +01:00
Otto Winter
52c0b45f41 Add RC5 IR code support (#432) 2019-02-16 15:30:59 +01:00
Otto Winter
fcef3be5c7 Template lambda is optional 2019-02-16 13:12:49 +01:00
Otto Winter
ef7b936d60 Add ADC sensor 2019-02-15 13:22:31 +01:00
Otto Winter
e0eac6ba25 Make esphomeyaml rename a bit more graceful 2019-02-15 10:25:56 +01:00
Otto Winter
50af0da13f Add esphomeyaml legacy script 2019-02-15 10:19:18 +01:00
Otto Winter
a8d87a1fee Update to 1.1.0 docker base image 2019-02-14 16:07:28 +01:00
Otto Winter
a66ed437d6 Include common components for compiles (#431) 2019-02-14 14:46:10 +01:00
Otto Winter
b95f53a7c7 Update README.md 2019-02-14 13:49:22 +01:00
Otto Winter
d7eacb2a2f Fix docker hub build 2019-02-13 21:40:10 +01:00
Otto Winter
41ec337ba0 Try to fix docker hub build 2019-02-13 21:29:08 +01:00
Otto Winter
5e61014e98 Update Gitlab CI 2019-02-13 21:14:03 +01:00
Brad Davidson
0a4d49accb Fix missing whitespace in key migration message 2019-02-13 09:42:54 -08:00
Otto Winter
3d9301a0f7 Rename esphomeyaml to esphome (#426)
* Rename

* Update

* Add migration

* Fix

* Fix dashboard

* Change test

* Fixes

* Code cleanup

* Fix import order

* Update

* Automate docker builds

* Shellcheck
2019-02-13 16:54:02 +01:00
Brandon Davidson
1b8d242505 Enable use of alternate hardware UARTs for logging (#427)
* Enable use of alternate hardware UARTs for logging

Enable use of Serial1 on ESP8266 and Serial1/Serial2 on ESP32 for logging.
This is frequently done on ESP8266 to allow use of Serial for UART TX+RX,
while maintaining logging output on Serial1 which is TX-only via GPIO2.

* ESPHOMELIB_UART -> UART_SELECTION_UART; HW_UART -> HARDWARE_UART

* Add test3 to travis; remove test4

* Set DEBUG_ESP_PORT based on logger UART setting
2019-02-13 11:20:22 +01:00
Jesse Hills
463ad6f94b Add support for JVC remote transmitting and receiving (#423)
* Add support for JVC remote transmitting and receiving

* Add missing import

* Fix line length
2019-02-11 18:09:31 +01:00
Otto Winter
e4e315f723 Revert "Upgrade espressif32 package to 1.6.0 (#355)" (#422)
* Revert "Upgrade espressif32 package to 1.6.0 (#355)"

This reverts commit e347cb538d.

* Fix revert

* Fix
2019-02-11 16:50:10 +01:00
Otto Winter
7fecec128f Make dout the default flash mode (#420) 2019-02-11 16:49:12 +01:00
Otto Winter
a15b647d13 Rework UART component for fixes (#419) 2019-02-11 15:50:12 +01:00
Otto Winter
feab956ea9 Add use_address (#417) 2019-02-10 23:55:13 +01:00
Otto Winter
4b7a41922c Add template publish actions and switch triggers (#391)
* Add template publish actions and switch triggers

* Fix

* Add tests

* Add cover

* Lint

* Fix

* Fix
2019-02-10 23:35:07 +01:00
Otto Winter
61762bf299 Replace optimistic with Assumed State (#394)
* Replace optimistic with Assumed State

* Add tests

* Fix

* Lint
2019-02-10 23:28:56 +01:00
Otto Winter
6eb769e6d5 Auto-Redact personal information from logs (#421) 2019-02-10 20:30:05 +01:00
Otto Winter
4c8bd7a70c Fix build 2019-02-10 20:29:06 +01:00
Otto Winter
d9cf91210e Add local mDNS responder for .local (#386)
* Add local mDNS responder

* Fix

* Use mDNS in dashboard status

* Lint

* Lint

* Fix test

* Remove hostname

* Fix use enum

* Lint
2019-02-10 16:57:34 +01:00
Otto Winter
fcf5da66c0 Add Switch Interlocking (#411) 2019-02-10 16:47:39 +01:00
Otto Winter
b6edbf9d0b Better error messages for OTA (#418)
* Better error messages for OTA

* Update espota2.py
2019-02-10 16:43:44 +01:00
Otto Winter
596f995af8 Store Raw Remote Codes in PROGMEM (#392)
* Store Raw Remote Codes in PROGMEM

* Lint

* Lint

* Fix
2019-02-10 16:41:12 +01:00
Otto Winter
8ce176aaba Allow pins 9&10 for PWM (#410)
Ref https://github.com/esp8266/Arduino/pull/5055
2019-02-10 16:32:02 +01:00
Otto Winter
f185ba7a21 Add Homeassistant Binary Sensor (#409) 2019-02-10 16:31:17 +01:00
Otto Winter
7c28eca6de Print error when mqtt.publish used without MQTT enabled (#408)
Fixes https://github.com/esphome/issues/issues/40
2019-02-10 16:30:17 +01:00
Otto Winter
9fa95a9a21 Lint 2019-02-10 16:22:12 +01:00
Otto Winter
6ccb68aaf1 Fix custom components 2019-02-10 14:15:20 +01:00
Otto Winter
6a76a3642e Fix SSID must be ascii 2019-02-08 17:52:42 +01:00
Otto Winter
a50eb3579a Validate neopixelbus method (#398)
* Validate NeoPixelBus Method Pin

* Add path

* Prefix GPIO
2019-02-03 21:18:45 +01:00
Otto Winter
4a74027848 Miscellaneous Fixes 2019-02-03 20:46:18 +01:00
Otto Winter
98bdfc821e Disable travis test for Python 3
It hasn't found any bugs so far - and it takes a lot of time
2019-01-29 17:46:02 +01:00
Otto Winter
176c712eeb Disable MQTT if not used (#373)
* Disable MQTT if not used

* Lint
2019-01-29 17:29:21 +01:00
Otto Winter
7b26ecc0dc Remove Heartbeat Filter (#393)
* Remove Heartbeat Filter

* Fix tests
2019-01-29 16:45:50 +01:00
Otto Winter
92e909568c Fix Custom Components No Name (#395)
* Fix Custom Components No Name

Fixes https://github.com/esphome/ESPHome-Core/issues/445

* Fix
2019-01-29 16:39:29 +01:00
Marcin Jaworski
e3f7e0d14a Generate variable for each custom component id (#382)
* Generate variable for each custom component id

* Change variable to Pvariable for custom components
2019-01-29 16:21:00 +01:00
Marcin Jaworski
5fcda34c45 Includes should be relative to the src directory, not main.cpp file (#390) 2019-01-29 16:12:38 +01:00
Otto Winter
bd23584073 Better error message 2019-01-27 16:38:29 +01:00
Antoine GRÉA
6d6876ca39 Adding SI7021 sensor to config validation (#375) 2019-01-25 17:55:29 +01:00
Otto Winter
9ebf6439b2 Revert "Enable aarch64"
This reverts commit 7153c7a941.
2019-01-22 21:34:45 +01:00
Otto Winter
7153c7a941 Enable aarch64
The repo just mirrors armhf, thanks @Frenck !
2019-01-22 20:43:45 +01:00
Otto Winter
3437e23c8e Disable platformio LDF (#352)
* Disable platformio LDF

* Use required build flags
2019-01-19 22:14:46 +01:00
Otto Winter
eb39add6fc Fix nginx closing WebSocket connection after 60 seconds (#370)
* Test

* Add keepalive

* Revert "Add keepalive"

This reverts commit 8b92198122.

* Update dashboard.py

* Revert

* Lint
2019-01-19 22:10:10 +01:00
Otto Winter
e13bffbb2f Fix dashboard password with python 3 (#339)
* Fix dashboard password with python 3

* Lint
2019-01-19 22:09:46 +01:00
Otto Winter
e347cb538d Upgrade espressif32 package to 1.6.0 (#355)
* Upgrade espressif32 package to 1.6.0

* Remove AsyncTCP pinning

* Lint

* Fix AsyncTCP version

* Fix test2
2019-01-19 21:44:47 +01:00
Robert Schütz
d799c03f0c call platformio and esptool using subprocess if $ESPHOME_USE_SUBPROCESS is set (#359)
* call platformio and esptool using subprocess if $ESPHOME_USE_SUBPROCESS is set

* Update setup.py
2019-01-19 17:54:41 +01:00
Otto Winter
c86675f644 Fix custom output requiring type (#344)
* Fix custom output requiring type

Fixes #343

* Revert "Fix custom output requiring type"

This reverts commit 37f995d32a.

* Fix
2019-01-19 16:53:54 +01:00
Otto Winter
a00fe09c66 Remove 2019-01-19 16:52:31 +01:00
Otto Winter
e0fe0a2835 Remove deep sleep run_cycles (#353)
* Remove deep sleep run_cycles

* Fix test
2019-01-19 16:51:20 +01:00
Otto Winter
e1f48b5028 Rmove DNS1,DNS2 inclusive (#357) 2019-01-19 16:50:31 +01:00
Otto Winter
e4a2677de4 ESP8266 Better Exception Code Names (#358) 2019-01-19 16:50:17 +01:00
Otto Winter
ab40156c16 Fix ESP32 BLE tracker scan interval in seconds (#367)
Fixes #361
2019-01-19 16:48:54 +01:00
Otto Winter
804a1ed7b3 Upgrade HassIO Ubuntu Base to 2.2.1 (#368) 2019-01-19 16:48:35 +01:00
Otto Winter
4de98fd782 Fix Non-ASCII characters being escaped if in wrong locale (#369) 2019-01-19 16:48:16 +01:00
Otto Winter
d8fd5e9bb4 Use strict string mode for WiFi password (#351)
Fixes #350
2019-01-18 11:21:08 +01:00
Otto Winter
8424f9c34b Fix expire_after requires MQTT (#354)
Fixes https://github.com/OttoWinter/esphomelib/issues/372
2019-01-18 11:16:53 +01:00
Robert Schütz
70016d7641 Add pyserial to install_requires (#348) 2019-01-17 09:40:31 +01:00
Otto Winter
7ced977f2a Remove aarch64 from edge 2019-01-16 11:31:27 +01:00
Otto Winter
5ac9b8d545 Allow IPv4 addresses for SNTP servers (#340)
Fixes https://github.com/OttoWinter/esphomelib/issues/395
2019-01-15 20:13:20 +01:00
Otto Winter
a683108b5d Fix install pillow in docker image (#338)
Fixes https://github.com/OttoWinter/esphomeyaml/issues/266
2019-01-15 20:13:09 +01:00
Robert Schütz
54dd9e94ab typing is only required for python < 3.5 (#341)
Since 3.5, it is included in the standard library.
2019-01-15 16:50:50 +01:00
Otto Winter
4f5389998f Add homeassistant.service test 2019-01-14 11:09:32 +01:00
Otto Winter
8a61c21051 Use noreferrer for dashboard links
Was showing up in GA, and this feels creepy. I don't want to know your HA installation address
2019-01-14 11:09:23 +01:00
Otto Winter
9b2b7dabd1 Bump HassIO version to v1.10.1 2019-01-13 19:06:07 +01:00
Otto Winter
86130b2954 Pin platformio platforms (#335) 2019-01-13 19:03:07 +01:00
Otto Winter
c2787c1ce5 Introduce wifi fast connect mode (#333) 2019-01-13 18:33:41 +01:00
Otto Winter
e81338b4f5 Fix show logs with MQTT and dashboard (#332)
Fixes #327
2019-01-13 16:21:10 +01:00
Otto Winter
40ab2d5e5a Fix AsyncTCP compilation on ESP32 with Arduino breaking change (#334) 2019-01-13 16:20:28 +01:00
Otto Winter
7a703d10f4 Fix ESP32 not decoding stacktrace on broken PC (#330) 2019-01-13 09:56:29 +01:00
Otto Winter
4c5f908e3a Fix auto_uart 2019-01-10 10:56:07 +01:00
Otto Winter
27e1294630 Bump HassIO version to v1.10.0 2019-01-09 20:30:35 +01:00
Otto Winter
c2fb71c41f Fix Gitlab CI 2019-01-09 16:30:35 +01:00
Otto Winter
7685b32ac0 Bump beta version to v1.10.0b2 2019-01-09 16:16:15 +01:00
Otto Winter
5a1e806d14 Fix interval trigger (#313) 2019-01-09 15:18:54 +01:00
escoand
cf73d777db use full space on small devices (#310)
* use full space on small devices

I often use my smartphone for quick actions and always was annoyed of this wasted space.

* hide card-image on small devices
2019-01-09 14:58:23 +01:00
Otto Winter
47231977d7 OTA don't error when upgrading from no password to password mode (#309) 2019-01-08 12:51:26 +01:00
Otto Winter
d29b280d82 Fix component.update action (#308)
Fixes https://github.com/OttoWinter/esphomeyaml/issues/286
2019-01-08 12:15:03 +01:00
Otto Winter
77514c0a06 Update config.json 2019-01-07 15:54:24 +01:00
Otto Winter
341a27e56b Update beta config (#305)
* Update beta Hass.io add-on config

* Add README
2019-01-06 21:26:39 +01:00
Otto Winter
739b7bfc05 Bump beta version to v1.10.0b1 2019-01-06 19:39:12 +01:00
Otto Winter
01b49e8d59 Home Assistant platform tests 2019-01-06 19:03:34 +01:00
Otto Winter
52dbd35118 Add neopixelbus tests 2019-01-06 16:59:11 +01:00
Otto Winter
92439abeb4 Add tests, Fix stuff 2019-01-06 11:43:14 +01:00
Otto Winter
9bce35e335 Make tzlocal a requirement 2019-01-06 09:48:03 +01:00
Otto Winter
94cb7bf6bd Validate MQTT enabled for MQTT options 2019-01-06 09:47:47 +01:00
Otto Winter
cdf53cb3e8 Remove component that was never added 2019-01-06 09:47:28 +01:00
Otto Winter
30bd74cb6c Simplify validator 2019-01-06 09:47:14 +01:00
Otto Winter
e72ef9e061 Add ESP32 Ethernet Support (#301)
* Fix

* Build flags

* Fix
2019-01-05 20:47:33 +01:00
Otto Winter
3f4bba57f4 ULN2003 Support (#304) 2019-01-05 20:11:31 +01:00
sherbang
38c24fd100 Add support for MAX31855 sensor (#258)
* Add support for MAX31855 sensor

* Update for latest core changes

* Update max31855.py
2019-01-05 20:08:25 +01:00
Otto Winter
c6ed88579f Add neopixelbus component (#303) 2019-01-05 20:01:19 +01:00
Otto Winter
7f9462ebf3 Add APDS9960 Support (#300) 2019-01-05 19:52:01 +01:00
Otto Winter
4e44081fdf Add clean MQTT discovery option for native API (#302) 2019-01-05 19:51:01 +01:00
Otto Winter
3cd1c2d723 Fix Python 3 conversion errors
Fixes #299
2019-01-05 14:24:15 +01:00
Otto Winter
907be3025c Supply return_type for more lambdas 2019-01-03 20:39:54 +01:00
Otto Winter
815da05b29 Fix OTA Auth for Python 3 2019-01-03 19:54:36 +01:00
Otto Winter
a2083fc901 Add ACE searchbox 2019-01-03 19:54:21 +01:00
Otto Winter
06cb7497c8 Another fix for Python 3 2019-01-03 17:09:28 +01:00
Otto Winter
1e12cba176 Fixes for Python 3 Compatability (#297) 2019-01-03 16:05:33 +01:00
Vladimir Eremin
1d0c812e44 GPIO Switch Fix restore_mode validator (#296) 2019-01-02 18:24:47 +01:00
Otto Winter
4948ad95b0 Lint 2019-01-02 18:24:20 +01:00
Otto Winter
dd801636af Fix Interval Trigger 2019-01-02 16:49:26 +01:00
Otto Winter
a110911371 Commit forgotten local change
Fixes #295
2019-01-02 16:34:38 +01:00
Otto Winter
22fd4ec722 Make compatible with python 3 (#281)
* Make compatible with python 3

* Update travis

* Env

* Fix typo

* Fix install correct platformio

* Lint

* Fix build flags

* Lint

* Upgrade pylint

* Lint
2019-01-02 14:11:11 +01:00
Otto Winter
5db70bea3c Toggle Auto-Update Check With Environment Variable (#292) 2019-01-02 14:02:59 +01:00
Otto Winter
00ff99cc7d Addressable Lights (#294) 2019-01-02 13:04:27 +01:00
Otto Winter
a51eaa93b5 GPIO Switch Restore Mode (#287) 2019-01-02 12:30:55 +01:00
Otto Winter
eddaee3a80 Fix MQTT message trigger (#282)
Fixes https://github.com/OttoWinter/esphomelib/issues/335
2019-01-02 12:29:29 +01:00
Otto Winter
2b432ea829 Disable SPIFFS to save flash space (#288) 2019-01-02 12:29:15 +01:00
Otto Winter
09ab55b85d API Server Watchdog (#290) 2019-01-02 12:27:16 +01:00
Otto Winter
183e0f4d23 Fix ESP8266 verbose logging (#291) 2019-01-02 12:27:05 +01:00
Otto Winter
a8c17e5d05 Fix host network (#280)
* Fix Add-On host network mode

* Split up esphomeyaml tests

* Fix perms

* Fix

* Add esphomeyaml_version option

* Revert change to travis.yml
2019-01-02 12:21:26 +01:00
Otto Winter
2e65d6c02c Api fixes (#289)
* API Client Fixes

* Fix redirect stdout stderr
2019-01-02 12:15:13 +01:00
Otto Winter
07da8950c4 Lint 2019-01-02 12:02:28 +01:00
Otto Winter
c206846816 Better internal debugging 2018-12-30 12:46:08 +01:00
Otto Winter
6aac4191f8 Hash dashboard resources 2018-12-24 14:29:11 +01:00
Otto Winter
5a7b66e207 Fix some stuff 2018-12-24 14:15:54 +01:00
Otto Winter
da2821ab36 Add native ESPHome API (#265)
* Esphomeapi

* Updates

* Remove MQTT from wizard

* Add protobuf to requirements

* Fix

* API Client updates

* Dump config on API connect

* Old WiFi config migration

* Home Assistant state import

* Lint
2018-12-18 19:31:43 +01:00
Otto Winter
7556845079 Fix download binary button for firefox 2018-12-07 10:17:19 +01:00
Otto Winter
e646f02e27 Update Hass.io READMe 2018-12-05 22:42:40 +01:00
Otto Winter
39ead80df3 Fix Hass.io requirements script 2018-12-05 22:35:35 +01:00
Otto Winter
71e221f6ec Fix 2018-12-05 21:34:24 +01:00
Otto Winter
7c7032c59e [Huge] Util Refactor, Dashboard Improvements, Hass.io Auth API, Better Validation Errors, Conditions, Custom Platforms, Substitutions (#234)
* Implement custom sensor platform

* Update

* Ethernet

* Lint

* Fix

* Login page

* Rename cookie secret

* Update manifest

* Update cookie check logic

* Favicon

* Fix

* Favicon manifest

* Fix

* Fix

* Fix

* Use hostname

* Message

* Temporary commit for screenshot

* Automatic board selection

* Undo temporary commit

* Update esphomeyaml-edge

* In-dashboard editing and hosting files locally

* Update esphomeyaml-edge

* Better ANSI color escaping

* Message

* Lint

* Download Efficiency

* Fix gitlab

* Fix

* Rename extra_libraries to libraries

* Add example

* Update README.md

* Update README.md

* Update README.md

* HassIO -> Hass.io

* Updates

* Add update available notice

* Update

* Fix substitutions

* Better error message

* Re-do dashboard ANSI colors

* Only include FastLED if user says so

* Autoscroll logs

* Remove old checks

* Use safer RedirectText

* Improvements

* Fix

* Use enviornment variable

* Use http://hassio/host/info

* Fix conditions

* Update platformio versions

* Revert "Use enviornment variable"

This reverts commit 7f038eb5d2.

* Fix

* README update

* Temp

* Better invalid config messages

* Platformio debug

* Improve error messages

* Debug

* Remove debug

* Multi Conf

* Update

* Better paths

* Remove unused

* Fixes

* Lint

* lib_ignore

* Try fix platformio colors

* Fix dashboard scrolling

* Revert

* Lint

* Revert
2018-12-05 21:22:06 +01:00
Otto Winter
2b88c987da Fix GPIO input schema validator (#253) 2018-12-02 13:16:02 +01:00
Otto Winter
3d49ffa134 Time SNTP validate server format (#254) 2018-12-02 13:15:28 +01:00
Otto Winter
f2f869db89 Bump HassIO version to v1.9.3 2018-12-01 13:23:16 +01:00
Otto Winter
e37e986276 CSE7766 update interval (#250)
* CSE7766 update interval

* PollingComponent
2018-12-01 13:19:34 +01:00
Otto Winter
5d5529ff15 Bump HassIO version to v1.9.2 2018-11-25 18:00:20 +01:00
Otto Winter
db90a7a334 Add Logo and Icon to HassIO add-on (#241) 2018-11-23 13:51:39 +01:00
Otto Winter
bb9c1faffa Improve one_of validator (#240) 2018-11-23 13:51:22 +01:00
Otto Winter
b4c4dc8cfb Bump HassIO version to v1.9.1 2018-11-19 23:01:27 +01:00
Otto Winter
c3b2ab0085 Fix SNTP servers option (#237)
* Fix SNTP servers option

* Lint
2018-11-19 22:49:06 +01:00
Otto Winter
f087e313d4 Bump HassIO version to v1.9.0 2018-11-15 12:07:20 +01:00
Otto Winter
f88cf99b67 Cleanup git auto-update 2018-11-15 10:13:32 +01:00
Otto Winter
f394968bd0 Fix fastled lambda light effect
Fixes #232
2018-11-15 10:13:21 +01:00
Otto Winter
70def85ba1 Bump beta version to v1.9.0b6 2018-11-13 19:10:28 +01:00
289 changed files with 16129 additions and 6501 deletions

View File

@@ -1,47 +0,0 @@
---
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

@@ -1,22 +0,0 @@
---
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

@@ -1,20 +0,0 @@
---
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**

View File

@@ -3,18 +3,12 @@
**Related issue (if applicable):** fixes <link to issue> **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 [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs 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> **Pull request in [esphome-core](https://github.com/esphome/esphome-core) with C++ framework changes (if applicable):** esphome/esphome-core#<esphome-core PR number goes here>
## Example entry for YAML configuration (if applicable):
```yaml
```
## Checklist: ## Checklist:
- [ ] The code change is tested and works locally. - [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works (under `tests/` folder). - [ ] 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: If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs). - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).

1
.gitignore vendored
View File

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

View File

@@ -2,299 +2,126 @@
# Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml # Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml
variables: variables:
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375/
stages: stages:
- lint - lint
- test - test
- build
- deploy - deploy
.lint: &lint .lint: &lint
image: esphome/esphome-base-amd64
stage: lint stage: lint
before_script:
- pip install -e .
- pip install flake8==3.6.0 pylint==1.9.4 pillow
tags: tags:
- python2.7 - docker
- esphomeyaml-lint
.test: &test .test: &test
image: esphome/esphome-base-amd64
stage: test stage: test
before_script: before_script:
- pip install -e . - pip install -e .
tags: tags:
- python2.7 - docker
- esphomeyaml-test
variables: variables:
TZ: UTC TZ: UTC
cache:
paths:
- tests/build
.docker-builder: &docker-builder .docker-base: &docker-base
image: esphome/esphome-base-builder
before_script: before_script:
- docker info - docker info
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
script:
- docker run --rm --privileged hassioaddons/qemu-user-static:latest
- TAG="${CI_COMMIT_TAG#v}"
- TAG="${TAG:-${CI_COMMIT_SHA:0:7}}"
- echo "Tag ${TAG}"
- |
if [[ "${IS_HASSIO}" == "YES" ]]; then
BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:1.2.1
BUILD_TO=esphome/esphome-hassio-${BUILD_ARCH}
DOCKERFILE=docker/Dockerfile.hassio
else
BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:1.2.1
if [[ "${BUILD_ARCH}" == "amd64" ]]; then
BUILD_TO=esphome/esphome
else
BUILD_TO=esphome/esphome-${BUILD_ARCH}
fi
DOCKERFILE=docker/Dockerfile
fi
- |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=${TAG}" \
--tag "${BUILD_TO}:${TAG}" \
--file "${DOCKERFILE}" \
.
- |
if [[ "${RELEASE}" = "YES" ]]; then
echo "Pushing to ${BUILD_TO}:${TAG}"
docker push "${BUILD_TO}:${TAG}"
fi
- |
if [[ "${LATEST}" = "YES" ]]; then
echo "Pushing to :latest"
docker tag ${BUILD_TO}:${TAG} ${BUILD_TO}:latest
docker push ${BUILD_TO}:latest
fi
- |
if [[ "${BETA}" = "YES" ]]; then
echo "Pushing to :beta"
docker tag \
${BUILD_TO}:${TAG} \
${BUILD_TO}:beta
docker push ${BUILD_TO}:beta
fi
- |
if [[ "${DEV}" = "YES" ]]; then
echo "Pushing to :dev"
docker tag \
${BUILD_TO}:${TAG} \
${BUILD_TO}:dev
docker push ${BUILD_TO}:dev
fi
services: services:
- docker:dind - docker:dind
tags: tags:
- hassio-builder - docker
stage: deploy
flake8: flake8:
<<: *lint <<: *lint
script: script:
- flake8 esphomeyaml - flake8 esphome
pylint: pylint:
<<: *lint <<: *lint
script: script:
- pylint esphomeyaml - pylint esphome
test1: test1:
<<: *test <<: *test
script: script:
- esphomeyaml tests/test1.yaml compile - esphome tests/test1.yaml compile
test2: test2:
<<: *test <<: *test
script: script:
- esphomeyaml tests/test2.yaml compile - esphome tests/test2.yaml compile
.build-hassio: &build-hassio test3:
<<: *docker-builder <<: *test
stage: build
script: script:
- docker run --rm --privileged hassioaddons/qemu-user-static:latest - esphome tests/test3.yaml compile
- BUILD_FROM=homeassistant/${ADDON_ARCH}-base-ubuntu:latest
- ADDON_VERSION="${CI_COMMIT_TAG#v}"
- ADDON_VERSION="${ADDON_VERSION:-${CI_COMMIT_SHA:0:7}}"
- ESPHOMELIB_VERSION="${ESPHOMELIB_VERSION:-''}"
- echo "Build from ${BUILD_FROM}"
- echo "Add-on version ${ADDON_VERSION}"
- echo "Esphomelib version ${ESPHOMELIB_VERSION}"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev"
- echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "ADDON_ARCH=${ADDON_ARCH}" \
--build-arg "ADDON_VERSION=${ADDON_VERSION}" \
--build-arg "ESPHOMELIB_VERSION=${ESPHOMELIB_VERSION}" \
--tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev" \
--tag "${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
--file "docker/Dockerfile.hassio" \
.
- |
if [ "${DO_PUSH:-true}" = true ]; then
echo "Pushing to CI registry"
docker push ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}
docker push ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev
fi
# Generic deploy template
.deploy-release: &deploy-release
<<: *docker-builder
stage: deploy
script:
- version="${CI_COMMIT_TAG#v}"
- echo "Publishing release version ${version}"
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- |
docker tag \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
only:
- /^v\d+\.\d+\.\d+$/
except:
- /^(?!master).+@/
.deploy-beta: &deploy-beta
<<: *docker-builder
stage: deploy
script:
- version="${CI_COMMIT_TAG#v}"
- echo "Publishing beta version ${version}"
- docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- |
docker tag \
"${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
- echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- |
docker tag \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
"ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
- docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
only:
- /^v\d+\.\d+\.\d+b\d+$/
except:
- /^(?!rc).+@/
# Build jobs
build:normal:
<<: *docker-builder
stage: build
script:
- docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" .
.build-hassio-edge: &build-hassio-edge
<<: *build-hassio
except:
- /^v\d+\.\d+\.\d+$/
- /^v\d+\.\d+\.\d+b\d+$/
.build-hassio-release: &build-hassio-release
<<: *build-hassio
only:
- /^v\d+\.\d+\.\d+$/
- /^v\d+\.\d+\.\d+b\d+$/
build:hassio-armhf-edge:
<<: *build-hassio-edge
variables:
ADDON_ARCH: armhf
DO_PUSH: "false"
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
build:hassio-armhf:
<<: *build-hassio-release
variables:
ADDON_ARCH: armhf
build:hassio-aarch64-edge:
<<: *build-hassio-edge
variables:
ADDON_ARCH: aarch64
DO_PUSH: "false"
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
build:hassio-aarch64:
<<: *build-hassio-release
variables:
ADDON_ARCH: aarch64
build:hassio-i386-edge:
<<: *build-hassio-edge
variables:
ADDON_ARCH: i386
DO_PUSH: "false"
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
build:hassio-i386:
<<: *build-hassio-release
variables:
ADDON_ARCH: i386
build:hassio-amd64-edge:
<<: *build-hassio-edge
variables:
ADDON_ARCH: amd64
DO_PUSH: "false"
ESPHOMELIB_VERSION: "${CI_COMMIT_TAG}"
build:hassio-amd64:
<<: *build-hassio-release
variables:
ADDON_ARCH: amd64
# Deploy jobs
deploy-release:armhf:
<<: *deploy-release
variables:
ADDON_ARCH: armhf
deploy-beta:armhf:
<<: *deploy-beta
variables:
ADDON_ARCH: armhf
deploy-release:aarch64:
<<: *deploy-release
variables:
ADDON_ARCH: aarch64
deploy-beta:aarch64:
<<: *deploy-beta
variables:
ADDON_ARCH: aarch64
deploy-release:i386:
<<: *deploy-release
variables:
ADDON_ARCH: i386
deploy-beta:i386:
<<: *deploy-beta
variables:
ADDON_ARCH: i386
deploy-release:amd64:
<<: *deploy-release
variables:
ADDON_ARCH: amd64
deploy-beta:amd64:
<<: *deploy-beta
variables:
ADDON_ARCH: amd64
.deploy-pypi: &deploy-pypi .deploy-pypi: &deploy-pypi
stage: deploy stage: deploy
image: python:2.7
before_script: before_script:
- pip install -e . - pip install -e .
- pip install twine - pip install twine
@@ -302,8 +129,7 @@ deploy-beta:amd64:
- python setup.py sdist - python setup.py sdist
- twine upload dist/* - twine upload dist/*
tags: tags:
- python2.7 - docker
- esphomeyaml-test
deploy-release:pypi: deploy-release:pypi:
<<: *deploy-pypi <<: *deploy-pypi
@@ -318,3 +144,204 @@ deploy-beta:pypi:
- /^v\d+\.\d+\.\d+b\d+$/ - /^v\d+\.\d+\.\d+b\d+$/
except: except:
- /^(?!rc).+@/ - /^(?!rc).+@/
.latest: &latest
<<: *docker-base
only:
- /^v([0-9\.]+)$/
except:
- branches
.latest-vars: &latest-vars
RELEASE: YES
LATEST: YES
# Also push to beta tag
BETA: YES
.beta: &beta
<<: *docker-base
only:
- /^v([0-9\.]+b\d+)$/
except:
- branches
.beta-vars: &beta-vars
RELEASE: YES
BETA: YES
.dev: &dev
<<: *docker-base
only:
- dev
.dev-vars: &dev-vars
DEV: YES
aarch64-beta-docker:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: aarch64
IS_HASSIO: "NO"
RELEASE: "YES"
aarch64-beta-hassio:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: aarch64
IS_HASSIO: "YES"
RELEASE: "YES"
aarch64-dev-docker:
<<: *dev
variables:
BUILD_ARCH: aarch64
DEV: "YES"
IS_HASSIO: "NO"
aarch64-dev-hassio:
<<: *dev
variables:
BUILD_ARCH: aarch64
DEV: "YES"
IS_HASSIO: "YES"
aarch64-latest-docker:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: aarch64
IS_HASSIO: "NO"
LATEST: "YES"
RELEASE: "YES"
aarch64-latest-hassio:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: aarch64
IS_HASSIO: "YES"
LATEST: "YES"
RELEASE: "YES"
amd64-beta-docker:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: amd64
IS_HASSIO: "NO"
RELEASE: "YES"
amd64-beta-hassio:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: amd64
IS_HASSIO: "YES"
RELEASE: "YES"
amd64-dev-docker:
<<: *dev
variables:
BUILD_ARCH: amd64
DEV: "YES"
IS_HASSIO: "NO"
amd64-dev-hassio:
<<: *dev
variables:
BUILD_ARCH: amd64
DEV: "YES"
IS_HASSIO: "YES"
amd64-latest-docker:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: amd64
IS_HASSIO: "NO"
LATEST: "YES"
RELEASE: "YES"
amd64-latest-hassio:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: amd64
IS_HASSIO: "YES"
LATEST: "YES"
RELEASE: "YES"
armhf-beta-docker:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: armhf
IS_HASSIO: "NO"
RELEASE: "YES"
armhf-beta-hassio:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: armhf
IS_HASSIO: "YES"
RELEASE: "YES"
armhf-dev-docker:
<<: *dev
variables:
BUILD_ARCH: armhf
DEV: "YES"
IS_HASSIO: "NO"
armhf-dev-hassio:
<<: *dev
variables:
BUILD_ARCH: armhf
DEV: "YES"
IS_HASSIO: "YES"
armhf-latest-docker:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: armhf
IS_HASSIO: "NO"
LATEST: "YES"
RELEASE: "YES"
armhf-latest-hassio:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: armhf
IS_HASSIO: "YES"
LATEST: "YES"
RELEASE: "YES"
i386-beta-docker:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: i386
IS_HASSIO: "NO"
RELEASE: "YES"
i386-beta-hassio:
<<: *beta
variables:
BETA: "YES"
BUILD_ARCH: i386
IS_HASSIO: "YES"
RELEASE: "YES"
i386-dev-docker:
<<: *dev
variables:
BUILD_ARCH: i386
DEV: "YES"
IS_HASSIO: "NO"
i386-dev-hassio:
<<: *dev
variables:
BUILD_ARCH: i386
DEV: "YES"
IS_HASSIO: "YES"
i386-latest-docker:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: i386
IS_HASSIO: "NO"
LATEST: "YES"
RELEASE: "YES"
i386-latest-hassio:
<<: *latest
variables:
BETA: "YES"
BUILD_ARCH: i386
IS_HASSIO: "YES"
LATEST: "YES"
RELEASE: "YES"

View File

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

View File

@@ -1,16 +1,16 @@
# Contributing to esphomeyaml # Contributing to ESPHome
esphomeyaml is a part of esphomelib and is responsible for reading in YAML configuration files, This python project is responsible for reading in YAML configuration files,
converting them to C++ code. This code is then converted to a platformio project and compiled 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. with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project.
For a detailed guide, please see https://esphomelib.com/esphomeyaml/guides/contributing.html#contributing-to-esphomeyaml For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml
Things to note when contributing: Things to note when contributing:
- Please test your changes :) - Please test your changes :)
- If a new feature is added or an existing user-facing feature is changed, you should also - 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) update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
for more information. 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 - 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. which checks if your new feature compiles correctly.

View File

@@ -1,33 +0,0 @@
ARG BUILD_FROM=python:2.7
FROM ${BUILD_FROM}
MAINTAINER Otto Winter <contact@otto-winter.com>
RUN apt-get update && apt-get install -y \
python-pil \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No && \
platformio settings set check_libraries_interval 1000000 && \
platformio settings set check_platformio_interval 1000000 && \
platformio settings set check_platforms_interval 1000000
ENV ESPHOMEYAML_OTA_HOST_PORT=6123
EXPOSE 6123
VOLUME /config
WORKDIR /usr/src/app
COPY docker/platformio.ini /pio/platformio.ini
ARG ESPHOMELIB_VERSION=""
RUN platformio run -d /pio; rm -rf /pio && \
/bin/bash -c "if [ ! -z '$ESPHOMELIB_VERSION']; then \
platformio lib -g install '${ESPHOMELIB_VERSION}'; \
fi"
COPY . .
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
WORKDIR /config
ENTRYPOINT ["esphomeyaml"]
CMD ["/config", "dashboard"]

View File

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

View File

@@ -1,38 +1,9 @@
# esphomeyaml for [esphomelib](https://github.com/OttoWinter/esphomelib) # ESPHome [![Build Status](https://travis-ci.org/esphome/esphome.svg?branch=master)](https://travis-ci.org/esphome/esphome) [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/)
### Getting Started Guide: https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html [![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/)
### Available Components: https://esphomelib.com/esphomeyaml/index.html **Documentation:** https://esphome.io/
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). For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
esphomeyaml will: For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
* Read your configuration file and warn you about potential errors (like using the invalid pins.)
* Create a custom C++ sketch file for you using esphomeyaml's powerful C++ generation engine.
* Compile the sketch file for you using [platformio](http://platformio.org/).
* Upload the binary to your ESP via Over the Air updates.
* Automatically start remote logs via MQTT.
And all of that with a single command 🎉:
```bash
esphomeyaml configuration.yaml run
```
## Features
* **No programming experience required:** just edit YAML configuration
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](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.
* **Open Source**
* For me: Makes documenting esphomelib's features a lot easier.
## Special Thanks
Special Thanks to the Home Assistant project. Lots of the code base of esphomeyaml is based off of Home Assistant, for example the loading and config validation code.

10
docker/Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:1.2.1
FROM ${BUILD_FROM}
COPY . .
RUN \
pip2 install --no-cache-dir --no-binary :all: -e .
WORKDIR /config
ENTRYPOINT ["esphome"]
CMD ["/config", "dashboard"]

View File

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

View File

@@ -1,40 +1,20 @@
# Dockerfile for HassIO add-on ARG BUILD_FROM=esphome/esphome-hassio-base-amd64:1.2.1
ARG BUILD_FROM=homeassistant/amd64-base-ubuntu:latest
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
RUN apt-get update && apt-get install -y --no-install-recommends \ # Copy root filesystem
python \ COPY docker/rootfs/ /
python-pip \ COPY setup.py setup.cfg MANIFEST.in /opt/esphome/
python-setuptools \ COPY esphome /opt/esphome/esphome
python-pil \
git \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \
pip install --no-cache-dir --no-binary :all: platformio && \
platformio settings set enable_telemetry No && \
platformio settings set check_libraries_interval 1000000 && \
platformio settings set check_platformio_interval 1000000 && \
platformio settings set check_platforms_interval 1000000
COPY docker/platformio.ini /pio/platformio.ini RUN \
RUN platformio run -d /pio; rm -rf /pio pip2 install --no-cache-dir --no-binary :all: -e /opt/esphome
# Build arguments
COPY . . ARG BUILD_VERSION=dev
RUN pip install --no-cache-dir --no-binary :all: -e . && \
pip install --no-cache-dir --no-binary :all: tzlocal
CMD ["esphomeyaml", "/config/esphomeyaml", "dashboard"]
# Build arugments
ARG ADDON_ARCH
ARG ADDON_VERSION
# Labels # Labels
LABEL \ LABEL \
io.hass.name="esphomeyaml" \ io.hass.name="ESPHome" \
io.hass.description="esphomeyaml HassIO add-on for intelligently managing all your ESP8266/ESP32 devices." \ io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
io.hass.arch="${ADDON_ARCH}" \
io.hass.type="addon" \ io.hass.type="addon" \
io.hass.version="${ADDON_VERSION}" \ io.hass.version=${BUILD_VERSION}
io.hass.url="https://esphomelib.com/esphomeyaml/index.html" \
maintainer="Otto Winter <contact@otto-winter.com>"

View File

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

View File

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

26
docker/hooks/build Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# the Docker repository tag being built.
declare CACHE_TAG
echo "CACHE_TAG: ${CACHE_TAG}"
# the name and tag of the Docker repository being built. (This variable is a combination of DOCKER_REPO:CACHE_TAG.)
declare IMAGE_NAME
echo "IMAGE_NAME: ${IMAGE_NAME}"
# the architecture to build
declare BUILD_ARCH
echo "BUILD_ARCH: ${BUILD_ARCH}"
# whether this is a hassio build
declare IS_HASSIO
echo "IS_HASSIO: ${IS_HASSIO}"
echo "PWD: $PWD"
if [[ ${IS_HASSIO} = "YES" ]]; then
docker build \
--build-arg "BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:1.2.1" \
--build-arg "BUILD_VERSION=${CACHE_TAG}" \
-t "${IMAGE_NAME}" -f ../docker/Dockerfile.hassio ..
else
docker build \
--build-arg "BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:1.2.1" \
-t "${IMAGE_NAME}" -f ../docker/Dockerfile ..
fi

18
docker/hooks/pre_build Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# the architecture to build
declare BUILD_ARCH
echo "BUILD_ARCH: ${BUILD_ARCH}"
if [[ ${BUILD_ARCH} = "amd64" ]]; then
echo "No qemu required..."
exit 0
fi
if [[ ${BUILD_ARCH} = "i386" ]]; then
echo "No qemu required..."
exit 0
fi
echo "Installing qemu..."
docker run --rm --privileged multiarch/qemu-user-static:register --reset

View File

@@ -2,11 +2,11 @@
; platforms ; platforms
[env:espressif8266] [env:espressif8266]
platform = espressif8266 platform = espressif8266@1.8.0
board = nodemcuv2 board = nodemcuv2
framework = arduino framework = arduino
[env:espressif32] [env:espressif32]
platform = espressif32 platform = espressif32@1.5.0
board = nodemcu-32s board = nodemcu-32s
framework = arduino framework = arduino

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
#!/usr/bin/with-contenv bash
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# This files migrates the esphome config directory from the old path
# ==============================================================================
# shellcheck disable=SC1091
source /usr/lib/hassio-addons/base.sh
if [[ ! -d /config/esphome && -d /config/esphomeyaml ]]; then
echo "Moving config directory from /config/esphomeyaml to /config/esphome"
mv /config/esphomeyaml /config/esphome
mv /config/esphome/.esphomeyaml /config/esphome/.esphome
fi

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,25 +2,29 @@ from __future__ import print_function
import argparse import argparse
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime
import logging import logging
import os import os
import random import random
import sys import sys
from datetime import datetime
from esphomeyaml import const, core, core_config, mqtt, wizard, writer, yaml_util, platformio_api from esphome import const, core_config, mqtt, platformio_api, wizard, writer, yaml_util
from esphomeyaml.config import get_component, iter_components, read_config from esphome.api.client import run_logs
from esphomeyaml.const import CONF_BAUD_RATE, CONF_BUILD_PATH, CONF_DOMAIN, CONF_ESPHOMEYAML, \ from esphome.config import get_component, iter_components, read_config, strip_default_ids
CONF_HOSTNAME, CONF_LOGGER, CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_USE_CUSTOM_CODE, \ from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_ESPHOME, CONF_LOGGER, \
CONF_WIFI, ESP_PLATFORM_ESP8266 CONF_USE_CUSTOM_CODE
from esphomeyaml.core import ESPHomeYAMLError from esphome.core import CORE, EsphomeError
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \ from esphome.cpp_generator import Expression, RawStatement, add, statement
_EXPRESSIONS, add, add_job, color, flush_tasks, indent, statement, relative_path from esphome.helpers import color, indent
from esphomeyaml.util import safe_print, run_external_command from esphome.py_compat import IS_PY2, safe_input, text_type
from esphome.storage_json import StorageJSON, esphome_storage_path, \
start_update_check_thread, storage_path
from esphome.util import run_external_command, run_external_process, safe_print
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c'] PRE_INITIALIZE = ['esphome', 'logger', 'wifi', 'ethernet', 'ota', 'mqtt', 'web_server', 'api',
'i2c']
def get_serial_ports(): def get_serial_ports():
@@ -32,37 +36,64 @@ def get_serial_ports():
continue continue
if "VID:PID" in info: if "VID:PID" in info:
result.append((port, desc)) result.append((port, desc))
result.sort(key=lambda x: x[0])
return result return result
def choose_serial_port(config): def choose_prompt(options):
result = get_serial_ports() if not options:
raise ValueError
if len(options) == 1:
return options[0][1]
safe_print(u"Found multiple options, please choose one:")
for i, (desc, _) in enumerate(options):
safe_print(u" [{}] {}".format(i + 1, desc))
if not result:
return 'OTA'
safe_print(u"Found multiple serial port options, please choose one:")
for i, (res, desc) in enumerate(result):
safe_print(u" [{}] {} ({})".format(i, res, desc))
safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
safe_print()
while True: while True:
opt = raw_input('(number): ') opt = safe_input('(number): ')
if opt in result: if opt in options:
opt = result.index(opt) opt = options.index(opt)
break break
try: try:
opt = int(opt) opt = int(opt)
if opt < 0 or opt > len(result): if opt < 1 or opt > len(options):
raise ValueError raise ValueError
break break
except ValueError: except ValueError:
safe_print(color('red', u"Invalid option: '{}'".format(opt))) safe_print(color('red', u"Invalid option: '{}'".format(opt)))
if opt == len(result): return options[opt - 1][1]
return 'OTA'
return result[opt][0]
def run_miniterm(config, port, escape=False): def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
options = []
for res, desc in get_serial_ports():
options.append((u"{} ({})".format(res, desc), res))
if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config):
options.append((u"Over The Air ({})".format(CORE.address), CORE.address))
if default == 'OTA':
return CORE.address
if show_mqtt and 'mqtt' in CORE.config:
options.append((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
if default == 'OTA':
return 'MQTT'
if default is not None:
return default
if check_default is not None and check_default in [opt[1] for opt in options]:
return check_default
return choose_prompt(options)
def get_port_type(port):
if port.startswith('/') or port.startswith('COM'):
return 'SERIAL'
if port == 'MQTT':
return 'MQTT'
return 'NETWORK'
def run_miniterm(config, port):
import serial import serial
if CONF_LOGGER not in config: if CONF_LOGGER not in config:
_LOGGER.info("Logger is not enabled. Not starting UART logs.") _LOGGER.info("Logger is not enabled. Not starting UART logs.")
@@ -80,11 +111,13 @@ def run_miniterm(config, port, escape=False):
except serial.SerialException: except serial.SerialException:
_LOGGER.error("Serial port closed!") _LOGGER.error("Serial port closed!")
return return
line = raw.replace('\r', '').replace('\n', '') if IS_PY2:
line = raw.replace('\r', '').replace('\n', '')
else:
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8',
'backslashreplace')
time = datetime.now().time().strftime('[%H:%M:%S]') time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line message = time + line
if escape:
message = message.replace('\033', '\\033')
safe_print(message) safe_print(message)
backtrace_state = platformio_api.process_stacktrace( backtrace_state = platformio_api.process_stacktrace(
@@ -94,114 +127,104 @@ def run_miniterm(config, port, escape=False):
def write_cpp(config): def write_cpp(config):
_LOGGER.info("Generating C++ source...") _LOGGER.info("Generating C++ source...")
add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml') CORE.add_job(core_config.to_code, config[CONF_ESPHOME], domain='esphome')
for domain in PRE_INITIALIZE: for domain in PRE_INITIALIZE:
if domain == CONF_ESPHOMEYAML or domain not in config: if domain == CONF_ESPHOME or domain not in config:
continue continue
add_job(get_component(domain).to_code, config[domain], domain=domain) CORE.add_job(get_component(domain).to_code, config[domain], domain=domain)
for domain, component, conf in iter_components(config): for domain, component, conf in iter_components(config):
if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'): if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'):
continue continue
add_job(component.to_code, conf, domain=domain) CORE.add_job(component.to_code, conf, domain=domain)
flush_tasks() CORE.flush_tasks()
add(RawStatement('')) add(RawStatement(''))
add(RawStatement('')) add(RawStatement(''))
all_code = [] all_code = []
for exp in _EXPRESSIONS: for exp in CORE.expressions:
if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]: if not config[CONF_ESPHOME][CONF_USE_CUSTOM_CODE]:
if isinstance(exp, Expression) and not exp.required: if isinstance(exp, Expression) and not exp.required:
continue continue
if isinstance(exp, AssignmentExpression) and not exp.obj.required: all_code.append(text_type(statement(exp)))
if not exp.has_side_effects():
continue
exp = exp.rhs
all_code.append(unicode(statement(exp)))
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) writer.write_platformio_project()
writer.write_platformio_project(config, build_path)
code_s = indent('\n'.join(line.rstrip() for line in all_code)) code_s = indent('\n'.join(line.rstrip() for line in all_code))
cpp_path = os.path.join(build_path, 'src', 'main.cpp') writer.write_cpp(code_s)
writer.write_cpp(code_s, cpp_path)
return 0 return 0
def compile_program(args, config): def compile_program(args, config):
_LOGGER.info("Compiling app...") _LOGGER.info("Compiling app...")
return platformio_api.run_compile(config, args.verbose) update_check = not os.getenv('ESPHOME_NO_UPDATE_CHECK', '')
if update_check:
thread = start_update_check_thread(esphome_storage_path(CORE.config_dir))
def get_upload_host(config): rc = platformio_api.run_compile(config, args.verbose)
if CONF_MANUAL_IP in config[CONF_WIFI]: if update_check:
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP]) thread.join()
elif CONF_HOSTNAME in config[CONF_WIFI]: return rc
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): def upload_using_esptool(config, port):
import esptool path = os.path.join(CORE.build_path, '.pioenvs', CORE.name, 'firmware.bin')
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
# pylint: disable=protected-access
return run_external_command(esptool._main, *cmd) if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
import esptool
# pylint: disable=protected-access
return run_external_command(esptool._main, *cmd)
return run_external_process(*cmd)
def upload_program(config, args, port): def upload_program(config, args, host):
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
# if upload is to a serial port use platformio, otherwise assume ota # if upload is to a serial port use platformio, otherwise assume ota
serial_port = port.startswith('/') or port.startswith('COM') if get_port_type(host) == 'SERIAL':
if port != 'OTA' and serial_port: if CORE.is_esp8266:
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy: return upload_using_esptool(config, host)
return upload_using_esptool(config, port) return platformio_api.run_upload(config, args.verbose, host)
return platformio_api.run_upload(config, args.verbose, port)
if 'ota' not in config: from esphome.components import ota
_LOGGER.error("No serial port found and OTA not enabled. Can't upload!") from esphome import espota2
return -1
# If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf
# hostname. This is to support use cases where zeroconf (hostname.local) does not work.
if port != 'OTA':
host = port
else:
host = get_upload_host(config)
from esphomeyaml.components import ota
from esphomeyaml import espota2
bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin')
if args.host_port is not None: if args.host_port is not None:
host_port = args.host_port host_port = args.host_port
else: else:
host_port = int(os.getenv('ESPHOMEYAML_OTA_HOST_PORT', random.randint(10000, 60000))) host_port = int(os.getenv('ESPHOME_OTA_HOST_PORT', random.randint(10000, 60000)))
verbose = args.verbose verbose = args.verbose
remote_port = ota.get_port(config) remote_port = ota.get_port(config)
password = ota.get_auth(config) password = ota.get_auth(config)
res = espota2.run_ota(host, remote_port, password, bin_file) storage = StorageJSON.load(storage_path())
res = espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
if res == 0: if res == 0:
if storage is not None and storage.use_legacy_ota:
storage.use_legacy_ota = False
storage.save(storage_path())
return res return res
_LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...") if storage is not None and not storage.use_legacy_ota:
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, bin_file) return res
_LOGGER.warning("OTA v2 method failed. Trying with legacy OTA...")
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password,
CORE.firmware_bin)
def show_logs(config, args, port, escape=False): def show_logs(config, args, port):
serial_port = port.startswith('/') or port.startswith('COM') if 'logger' not in config:
if port != 'OTA' and serial_port: raise EsphomeError("Logger is not configured!")
run_miniterm(config, port, escape=escape) if get_port_type(port) == 'SERIAL':
run_miniterm(config, port)
return 0 return 0
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id, if get_port_type(port) == 'NETWORK' and 'api' in config:
escape=escape) return run_logs(config, port)
if get_port_type(port) == 'MQTT' and 'mqtt' in config:
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
raise ValueError
def clean_mqtt(config, args): def clean_mqtt(config, args):
@@ -239,26 +262,8 @@ def command_wizard(args):
return wizard.wizard(args.configuration) 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): def command_config(args, config):
_LOGGER.info("Configuration is valid!")
if not args.verbose: if not args.verbose:
config = strip_default_ids(config) config = strip_default_ids(config)
safe_print(yaml_util.dump(config)) safe_print(yaml_util.dump(config))
@@ -280,7 +285,8 @@ def command_compile(args, config):
def command_upload(args, config): def command_upload(args, config):
port = args.upload_port or choose_serial_port(config) port = choose_upload_log_host(default=args.upload_port, check_default=None,
show_ota=True, show_mqtt=False, show_api=False)
exit_code = upload_program(config, args, port) exit_code = upload_program(config, args, port)
if exit_code != 0: if exit_code != 0:
return exit_code return exit_code
@@ -289,8 +295,9 @@ def command_upload(args, config):
def command_logs(args, config): def command_logs(args, config):
port = args.serial_port or choose_serial_port(config) port = choose_upload_log_host(default=args.serial_port, check_default=None,
return show_logs(config, args, port, escape=args.escape) show_ota=False, show_mqtt=True, show_api=True)
return show_logs(config, args, port)
def command_run(args, config): def command_run(args, config):
@@ -301,14 +308,17 @@ def command_run(args, config):
if exit_code != 0: if exit_code != 0:
return exit_code return exit_code
_LOGGER.info(u"Successfully compiled program.") _LOGGER.info(u"Successfully compiled program.")
port = args.upload_port or choose_serial_port(config) port = choose_upload_log_host(default=args.upload_port, check_default=None,
show_ota=True, show_mqtt=False, show_api=True)
exit_code = upload_program(config, args, port) exit_code = upload_program(config, args, port)
if exit_code != 0: if exit_code != 0:
return exit_code return exit_code
_LOGGER.info(u"Successfully uploaded program.") _LOGGER.info(u"Successfully uploaded program.")
if args.no_logs: if args.no_logs:
return 0 return 0
return show_logs(config, args, port, escape=args.escape) port = choose_upload_log_host(default=args.upload_port, check_default=port,
show_ota=False, show_mqtt=True, show_api=True)
return show_logs(config, args, port)
def command_clean_mqtt(args, config): def command_clean_mqtt(args, config):
@@ -325,9 +335,8 @@ def command_version(args):
def command_clean(args, config): def command_clean(args, config):
build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH])
try: try:
writer.clean_build(build_path) writer.clean_build()
except OSError as err: except OSError as err:
_LOGGER.error("Error deleting build files: %s", err) _LOGGER.error("Error deleting build files: %s", err)
return 1 return 1
@@ -336,7 +345,7 @@ def command_clean(args, config):
def command_hass_config(args, config): def command_hass_config(args, config):
from esphomeyaml.components import mqtt as mqtt_component from esphome.components import mqtt as mqtt_component
_LOGGER.info("This is what you should put in your Home Assistant YAML configuration.") _LOGGER.info("This is what you should put in your Home Assistant YAML configuration.")
_LOGGER.info("Please note this is only necessary if you're not using MQTT discovery.") _LOGGER.info("Please note this is only necessary if you're not using MQTT discovery.")
@@ -358,7 +367,7 @@ def command_hass_config(args, config):
def command_dashboard(args): def command_dashboard(args):
from esphomeyaml.dashboard import dashboard from esphome.dashboard import dashboard
return dashboard.start_web_server(args) return dashboard.start_web_server(args)
@@ -383,9 +392,11 @@ POST_CONFIG_ACTIONS = {
def parse_args(argv): def parse_args(argv):
parser = argparse.ArgumentParser(prog='esphomeyaml') parser = argparse.ArgumentParser(prog='esphome')
parser.add_argument('-v', '--verbose', help="Enable verbose esphomeyaml logs.", parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
action='store_true') action='store_true')
parser.add_argument('--dashboard', help="Internal flag to set if the command is run from the "
"dashboard.", action='store_true')
parser.add_argument('configuration', help='Your YAML configuration file.') parser.add_argument('configuration', help='Your YAML configuration file.')
subparsers = parser.add_subparsers(help='Commands', dest='command') subparsers = parser.add_subparsers(help='Commands', dest='command')
@@ -403,9 +414,6 @@ def parse_args(argv):
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. " parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
"For example /dev/cu.SLAB_USBtoUART.") "For example /dev/cu.SLAB_USBtoUART.")
parser_upload.add_argument('--host-port', help="Specify the host port.", type=int) 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 ' parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
'and show all MQTT logs.') 'and show all MQTT logs.')
@@ -415,8 +423,6 @@ def parse_args(argv):
parser_logs.add_argument('--client-id', help='Manually set the client id.') 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" parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
"For example /dev/cu.SLAB_USBtoUART.") "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, ' parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
'upload it, and start MQTT logs.') 'upload it, and start MQTT logs.')
@@ -429,11 +435,6 @@ def parse_args(argv):
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.') 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('--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('--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 " parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
"retain messages.") "retain messages.")
@@ -443,49 +444,59 @@ def parse_args(argv):
parser_clean.add_argument('--client-id', help='Manually set the client id.') parser_clean.add_argument('--client-id', help='Manually set the client id.')
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide " subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
"you through setting up esphomeyaml.") "you through setting up esphome.")
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.") 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.") subparsers.add_parser('version', help="Print the esphome version and exit.")
subparsers.add_parser('clean', help="Delete all temporary build files.") subparsers.add_parser('clean', help="Delete all temporary build files.")
dashboard = subparsers.add_parser('dashboard', dashboard = subparsers.add_parser('dashboard',
help="Create a simple web server for a dashboard.") help="Create a simple web server for a dashboard.")
dashboard.add_argument("--port", help="The HTTP port to open connections on.", type=int, dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
default=6052) type=int, default=6052)
dashboard.add_argument("--password", help="The optional password to require for all requests.", dashboard.add_argument("--password", help="The optional password to require for all requests.",
type=str, default='') type=str, default='')
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
action='store_true') action='store_true')
dashboard.add_argument("--hassio",
help="Internal flag used to tell esphome is started as a Hass.io "
"add-on.",
action="store_true")
dashboard.add_argument("--socket",
help="Make the dashboard serve under a unix socket", type=str)
subparsers.add_parser('hass-config', help="Dump the configuration entries that should be added" subparsers.add_parser('hass-config',
"to Home Assistant when not using MQTT discovery.") help="Dump the configuration entries that should be added "
"to Home Assistant when not using MQTT discovery.")
return parser.parse_args(argv[1:]) return parser.parse_args(argv[1:])
def run_esphomeyaml(argv): def run_esphome(argv):
args = parse_args(argv) args = parse_args(argv)
CORE.dashboard = args.dashboard
setup_log(args.verbose) setup_log(args.verbose)
if args.command in PRE_CONFIG_ACTIONS: if args.command in PRE_CONFIG_ACTIONS:
try: try:
return PRE_CONFIG_ACTIONS[args.command](args) return PRE_CONFIG_ACTIONS[args.command](args)
except ESPHomeYAMLError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e)
return 1 return 1
core.CONFIG_PATH = args.configuration CORE.config_path = args.configuration
config = read_config(core.CONFIG_PATH) config = read_config(args.verbose)
if config is None: if config is None:
return 1 return 1
CORE.config = config
if args.command in POST_CONFIG_ACTIONS: if args.command in POST_CONFIG_ACTIONS:
try: try:
return POST_CONFIG_ACTIONS[args.command](args, config) return POST_CONFIG_ACTIONS[args.command](args, config)
except ESPHomeYAMLError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e)
return 1 return 1
safe_print(u"Unknown command {}".format(args.command)) safe_print(u"Unknown command {}".format(args.command))
@@ -494,8 +505,8 @@ def run_esphomeyaml(argv):
def main(): def main():
try: try:
return run_esphomeyaml(sys.argv) return run_esphome(sys.argv)
except ESPHomeYAMLError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e)
return 1 return 1
except KeyboardInterrupt: except KeyboardInterrupt:

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

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

2484
esphome/api/api_pb2.py Normal file

File diff suppressed because one or more lines are too long

495
esphome/api/client.py Normal file
View File

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

391
esphome/automation.py Normal file
View File

@@ -0,0 +1,391 @@
import copy
import voluptuous as vol
import esphome.config_validation as cv
from esphome.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, CONF_WHILE, CONF_WAIT_UNTIL
from esphome.core import CORE
from esphome.cpp_generator import Pvariable, TemplateArguments, add, get_variable, \
process_lambda, templatable
from esphome.cpp_types import Action, App, Component, PollingComponent, Trigger, bool_, \
esphome_ns, float_, uint32, void
from esphome.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):
is_list = isinstance(value, list)
value = cv.ensure_list()(value)[:]
for i, item in enumerate(value):
path = [i] if is_list else []
item = copy.deepcopy(item)
if not isinstance(item, dict):
raise vol.Invalid(u"Condition must consist of key-value mapping! Got {}".format(item),
path)
key = next((x for x in item if x != CONF_CONDITION_ID), None)
if key is None:
raise vol.Invalid(u"Key missing from action! Got {}".format(item), path)
if key not in CONDITION_REGISTRY:
raise vol.Invalid(u"Unable to find condition with the name '{}', is the "
u"component loaded?".format(key), path + [key])
item.setdefault(CONF_CONDITION_ID, None)
key2 = next((x for x in item if x not in (CONF_CONDITION_ID, key)), None)
if key2 is not None:
raise vol.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! "
u"Did you forget to indent the block inside the condition?"
u"".format(key, key2), path)
validator = CONDITION_REGISTRY[key][0]
try:
condition = validator(item[key])
except vol.Invalid as err:
err.prepend(path)
raise err
value[i] = {
CONF_CONDITION_ID: cv.declare_variable_id(Condition)(item[CONF_CONDITION_ID]),
key: condition,
}
return value
def validate_recursive_action(value):
is_list = isinstance(value, list)
if not is_list:
value = [value]
for i, item in enumerate(value):
path = [i] if is_list else []
item = copy.deepcopy(item)
if not isinstance(item, dict):
raise vol.Invalid(u"Action must consist of key-value mapping! Got {}".format(item),
path)
key = next((x for x in item if x != CONF_ACTION_ID), None)
if key is None:
raise vol.Invalid(u"Key missing from action! Got {}".format(item), path)
if key not in ACTION_REGISTRY:
raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?"
u"".format(key), path + [key])
item.setdefault(CONF_ACTION_ID, None)
key2 = next((x for x in item if x not in (CONF_ACTION_ID, key)), None)
if key2 is not None:
raise vol.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! "
u"Did you forget to indent the block inside the action?"
u"".format(key, key2), path)
validator = ACTION_REGISTRY[key][0]
try:
action = validator(item[key])
except vol.Invalid as err:
err.prepend(path)
raise err
value[i] = {
CONF_ACTION_ID: cv.declare_variable_id(Action)(item[CONF_ACTION_ID]),
key: action,
}
return value
ACTION_REGISTRY = ServiceRegistry()
CONDITION_REGISTRY = ServiceRegistry()
# pylint: disable=invalid-name
DelayAction = esphome_ns.class_('DelayAction', Action, Component)
LambdaAction = esphome_ns.class_('LambdaAction', Action)
IfAction = esphome_ns.class_('IfAction', Action)
WhileAction = esphome_ns.class_('WhileAction', Action)
WaitUntilAction = esphome_ns.class_('WaitUntilAction', Action, Component)
UpdateComponentAction = esphome_ns.class_('UpdateComponentAction', Action)
Automation = esphome_ns.class_('Automation')
Condition = esphome_ns.class_('Condition')
AndCondition = esphome_ns.class_('AndCondition', Condition)
OrCondition = esphome_ns.class_('OrCondition', Condition)
RangeCondition = esphome_ns.class_('RangeCondition', Condition)
LambdaCondition = esphome_ns.class_('LambdaCondition', Condition)
def validate_automation(extra_schema=None, extra_validators=None, single=False):
if extra_schema is None:
extra_schema = {}
if isinstance(extra_schema, vol.Schema):
extra_schema = extra_schema.schema
schema = AUTOMATION_SCHEMA.extend(extra_schema)
def validator_(value):
if isinstance(value, list):
try:
# First try as a sequence of actions
return [schema({CONF_THEN: value})]
except vol.Invalid as err:
if err.path and err.path[0] == CONF_THEN:
err.path.pop(0)
# Next try as a sequence of automations
try:
return vol.Schema([schema])(value)
except vol.Invalid as err2:
if 'Unable to find action' in str(err):
raise err2
raise vol.MultipleInvalid([err, err2])
elif isinstance(value, dict):
if CONF_THEN in value:
return [schema(value)]
return [schema({CONF_THEN: value})]
# This should only happen with invalid configs, but let's have a nice error message.
return [schema(value)]
def validator(value):
value = validator_(value)
if extra_validators is not None:
value = vol.Schema([extra_validators])(value)
if single:
if len(value) != 1:
raise vol.Invalid("Cannot have more than 1 automation for templates")
return value[0]
return value
return validator
AUTOMATION_SCHEMA = vol.Schema({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(Trigger),
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(Automation),
vol.Optional(CONF_IF): validate_recursive_condition,
vol.Required(CONF_THEN): validate_recursive_action,
})
AND_CONDITION_SCHEMA = validate_recursive_condition
@CONDITION_REGISTRY.register(CONF_AND, AND_CONDITION_SCHEMA)
def and_condition_to_code(config, condition_id, arg_type, template_arg):
for conditions in build_conditions(config, arg_type):
yield
rhs = AndCondition.new(template_arg, conditions)
type = AndCondition.template(template_arg)
yield Pvariable(condition_id, rhs, type=type)
OR_CONDITION_SCHEMA = validate_recursive_condition
@CONDITION_REGISTRY.register(CONF_OR, OR_CONDITION_SCHEMA)
def or_condition_to_code(config, condition_id, arg_type, template_arg):
for conditions in build_conditions(config, arg_type):
yield
rhs = OrCondition.new(template_arg, conditions)
type = OrCondition.template(template_arg)
yield Pvariable(condition_id, rhs, type=type)
RANGE_CONDITION_SCHEMA = vol.All(vol.Schema({
vol.Optional(CONF_ABOVE): cv.templatable(cv.float_),
vol.Optional(CONF_BELOW): cv.templatable(cv.float_),
}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW))
@CONDITION_REGISTRY.register(CONF_RANGE, RANGE_CONDITION_SCHEMA)
def range_condition_to_code(config, condition_id, arg_type, template_arg):
for conditions in build_conditions(config, arg_type):
yield
rhs = RangeCondition.new(template_arg, conditions)
type = RangeCondition.template(template_arg)
condition = Pvariable(condition_id, rhs, type=type)
if CONF_ABOVE in config:
for template_ in templatable(config[CONF_ABOVE], arg_type, float_):
yield
condition.set_min(template_)
if CONF_BELOW in config:
for template_ in templatable(config[CONF_BELOW], arg_type, float_):
yield
condition.set_max(template_)
yield condition
DELAY_ACTION_SCHEMA = cv.templatable(cv.positive_time_period_milliseconds)
@ACTION_REGISTRY.register(CONF_DELAY, DELAY_ACTION_SCHEMA)
def delay_action_to_code(config, action_id, arg_type, template_arg):
rhs = App.register_component(DelayAction.new(template_arg))
type = DelayAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
for template_ in templatable(config, arg_type, uint32):
yield
add(action.set_delay(template_))
yield action
IF_ACTION_SCHEMA = vol.All({
vol.Required(CONF_CONDITION): validate_recursive_condition,
vol.Optional(CONF_THEN): validate_recursive_action,
vol.Optional(CONF_ELSE): validate_recursive_action,
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))
@ACTION_REGISTRY.register(CONF_IF, IF_ACTION_SCHEMA)
def if_action_to_code(config, action_id, arg_type, template_arg):
for conditions in build_conditions(config[CONF_CONDITION], arg_type):
yield None
rhs = IfAction.new(template_arg, conditions)
type = IfAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
if CONF_THEN in config:
for actions in build_actions(config[CONF_THEN], arg_type):
yield None
add(action.add_then(actions))
if CONF_ELSE in config:
for actions in build_actions(config[CONF_ELSE], arg_type):
yield None
add(action.add_else(actions))
yield action
WHILE_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_CONDITION): validate_recursive_condition,
vol.Required(CONF_THEN): validate_recursive_action,
})
@ACTION_REGISTRY.register(CONF_WHILE, WHILE_ACTION_SCHEMA)
def while_action_to_code(config, action_id, arg_type, template_arg):
for conditions in build_conditions(config[CONF_CONDITION], arg_type):
yield None
rhs = WhileAction.new(template_arg, conditions)
type = WhileAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
for actions in build_actions(config[CONF_THEN], arg_type):
yield None
add(action.add_then(actions))
yield action
def validate_wait_until(value):
schema = vol.Schema({
vol.Required(CONF_CONDITION): validate_recursive_condition
})
if isinstance(value, dict) and CONF_CONDITION in value:
return schema(value)
return validate_wait_until({CONF_CONDITION: value})
WAIT_UNTIL_ACTION_SCHEMA = validate_wait_until
@ACTION_REGISTRY.register(CONF_WAIT_UNTIL, WAIT_UNTIL_ACTION_SCHEMA)
def wait_until_action_to_code(config, action_id, arg_type, template_arg):
for conditions in build_conditions(config[CONF_CONDITION], arg_type):
yield None
rhs = WaitUntilAction.new(template_arg, conditions)
type = WaitUntilAction.template(template_arg)
action = Pvariable(action_id, rhs, type=type)
add(App.register_component(action))
yield action
LAMBDA_ACTION_SCHEMA = cv.lambda_
@ACTION_REGISTRY.register(CONF_LAMBDA, LAMBDA_ACTION_SCHEMA)
def lambda_action_to_code(config, action_id, arg_type, template_arg):
for lambda_ in process_lambda(config, [(arg_type, 'x')], return_type=void):
yield None
rhs = LambdaAction.new(template_arg, lambda_)
type = LambdaAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)
LAMBDA_CONDITION_SCHEMA = cv.lambda_
@CONDITION_REGISTRY.register(CONF_LAMBDA, LAMBDA_CONDITION_SCHEMA)
def lambda_condition_to_code(config, condition_id, arg_type, template_arg):
for lambda_ in process_lambda(config, [(arg_type, 'x')], return_type=bool_):
yield
rhs = LambdaCondition.new(template_arg, lambda_)
type = LambdaCondition.template(template_arg)
yield Pvariable(condition_id, rhs, type=type)
CONF_COMPONENT_UPDATE = 'component.update'
COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(PollingComponent),
})
@ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA)
def component_update_action_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = UpdateComponentAction.new(template_arg, var)
type = UpdateComponentAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)
def build_action(full_config, arg_type):
action_id = full_config[CONF_ACTION_ID]
key, config = next((k, v) for k, v in full_config.items() if k in ACTION_REGISTRY)
builder = ACTION_REGISTRY[key][1]
template_arg = TemplateArguments(arg_type)
for result in builder(config, action_id, arg_type, template_arg):
yield None
yield result
def build_actions(config, arg_type):
actions = []
for conf in config:
for action in build_action(conf, arg_type):
yield None
actions.append(action)
yield actions
def build_condition(full_config, arg_type):
action_id = full_config[CONF_CONDITION_ID]
key, config = next((k, v) for k, v in full_config.items() if k in CONDITION_REGISTRY)
builder = CONDITION_REGISTRY[key][1]
template_arg = TemplateArguments(arg_type)
for result in builder(config, action_id, arg_type, template_arg):
yield None
yield result
def build_conditions(config, arg_type):
conditions = []
for conf in config:
for condition in build_condition(conf, arg_type):
yield None
conditions.append(condition)
yield conditions
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):
CORE.add_job(build_automation_, trigger, arg_type, config)

View File

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

View File

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

88
esphome/components/api.py Normal file
View File

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

View File

@@ -1,16 +1,19 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml import automation, core from esphome import automation, core
from esphomeyaml.components import mqtt from esphome.automation import CONDITION_REGISTRY, Condition, maybe_simple_id
import esphomeyaml.config_validation as cv from esphome.components import mqtt
from esphomeyaml.const import CONF_DELAYED_OFF, CONF_DELAYED_ON, CONF_DEVICE_CLASS, CONF_FILTERS, \ from esphome.components.mqtt import setup_mqtt_component
import esphome.config_validation as cv
from esphome.const import CONF_DELAYED_OFF, CONF_DELAYED_ON, CONF_DEVICE_CLASS, CONF_FILTERS, \
CONF_HEARTBEAT, CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERT, CONF_INVERTED, \ CONF_HEARTBEAT, CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERT, CONF_INVERTED, \
CONF_LAMBDA, CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, \ CONF_LAMBDA, CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_MQTT_ID, CONF_ON_CLICK, \
CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_STATE, \ CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \
CONF_TIMING, CONF_TRIGGER_ID CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID
from esphomeyaml.helpers import App, ArrayInitializer, NoArg, Pvariable, StructInitializer, add, \ from esphome.core import CORE
add_job, bool_, esphomelib_ns, process_lambda, setup_mqtt_component, Nameable, Trigger, \ from esphome.cpp_generator import Pvariable, StructInitializer, add, get_variable, process_lambda
Component from esphome.cpp_types import App, Component, Nameable, NoArg, Trigger, bool_, esphome_ns, optional
from esphome.py_compat import string_types
DEVICE_CLASSES = [ DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
@@ -23,8 +26,9 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
}) })
binary_sensor_ns = esphomelib_ns.namespace('binary_sensor') binary_sensor_ns = esphome_ns.namespace('binary_sensor')
BinarySensor = binary_sensor_ns.class_('BinarySensor', Nameable) BinarySensor = binary_sensor_ns.class_('BinarySensor', Nameable)
BinarySensorPtr = BinarySensor.operator('ptr')
MQTTBinarySensorComponent = binary_sensor_ns.class_('MQTTBinarySensorComponent', mqtt.MQTTComponent) MQTTBinarySensorComponent = binary_sensor_ns.class_('MQTTBinarySensorComponent', mqtt.MQTTComponent)
# Triggers # Triggers
@@ -34,6 +38,10 @@ ClickTrigger = binary_sensor_ns.class_('ClickTrigger', Trigger.template(NoArg))
DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', Trigger.template(NoArg)) DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', Trigger.template(NoArg))
MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', Trigger.template(NoArg), Component) MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', Trigger.template(NoArg), Component)
MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent') MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent')
StateTrigger = binary_sensor_ns.class_('StateTrigger', Trigger.template(bool_))
# Condition
BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition)
# Filters # Filters
Filter = binary_sensor_ns.class_('Filter') Filter = binary_sensor_ns.class_('Filter')
@@ -43,16 +51,16 @@ HeartbeatFilter = binary_sensor_ns.class_('HeartbeatFilter', Filter, Component)
InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter) InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter)
LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter) LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter)
FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT] FILTER_KEYS = [CONF_INVERT, CONF_DELAYED_ON, CONF_DELAYED_OFF, CONF_LAMBDA, CONF_HEARTBEAT]
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.All({ FILTERS_SCHEMA = cv.ensure_list({
vol.Optional(CONF_INVERT): None, vol.Optional(CONF_INVERT): None,
vol.Optional(CONF_DELAYED_ON): cv.positive_time_period_milliseconds, vol.Optional(CONF_DELAYED_ON): cv.positive_time_period_milliseconds,
vol.Optional(CONF_DELAYED_OFF): 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_, vol.Optional(CONF_LAMBDA): cv.lambda_,
}, cv.has_exactly_one_key(*FILTER_KEYS))])
vol.Optional(CONF_HEARTBEAT): cv.invalid("The heartbeat filter has been removed in 1.11.0"),
}, cv.has_exactly_one_key(*FILTER_KEYS))
MULTI_CLICK_TIMING_SCHEMA = vol.Schema({ MULTI_CLICK_TIMING_SCHEMA = vol.Schema({
vol.Optional(CONF_STATE): cv.boolean, vol.Optional(CONF_STATE): cv.boolean,
@@ -62,7 +70,7 @@ MULTI_CLICK_TIMING_SCHEMA = vol.Schema({
def parse_multi_click_timing_str(value): def parse_multi_click_timing_str(value):
if not isinstance(value, basestring): if not isinstance(value, string_types):
return value return value
parts = value.lower().split(' ') parts = value.lower().split(' ')
@@ -150,7 +158,7 @@ def validate_multi_click_timing(value):
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent), cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTBinarySensorComponent),
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, cv.one_of(*DEVICE_CLASSES)), vol.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA, vol.Optional(CONF_FILTERS): FILTERS_SCHEMA,
vol.Optional(CONF_ON_PRESS): automation.validate_automation({ vol.Optional(CONF_ON_PRESS): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger),
@@ -174,11 +182,14 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
validate_multi_click_timing), validate_multi_click_timing),
vol.Optional(CONF_INVALID_COOLDOWN): cv.positive_time_period_milliseconds, vol.Optional(CONF_INVALID_COOLDOWN): cv.positive_time_period_milliseconds,
}), }),
vol.Optional(CONF_ON_STATE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StateTrigger),
}),
vol.Optional(CONF_INVERTED): cv.invalid( vol.Optional(CONF_INVERTED): cv.invalid(
"The inverted binary_sensor property has been replaced by the " "The inverted binary_sensor property has been replaced by the "
"new 'invert' binary sensor filter. Please see " "new 'invert' binary sensor filter. Please see "
"https://esphomelib.com/esphomeyaml/components/binary_sensor/index.html." "https://esphome.io/components/binary_sensor/index.html."
), ),
}) })
@@ -192,11 +203,9 @@ def setup_filter(config):
yield App.register_component(DelayedOffFilter.new(config[CONF_DELAYED_OFF])) yield App.register_component(DelayedOffFilter.new(config[CONF_DELAYED_OFF]))
elif CONF_DELAYED_ON in config: elif CONF_DELAYED_ON in config:
yield App.register_component(DelayedOnFilter.new(config[CONF_DELAYED_ON])) 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: elif CONF_LAMBDA in config:
lambda_ = None for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')],
for lambda_ in process_lambda(config[CONF_LAMBDA], [(bool_, 'x')]): return_type=optional.template(bool_)):
yield None yield None
yield LambdaFilter.new(lambda_) yield LambdaFilter.new(lambda_)
@@ -204,14 +213,13 @@ def setup_filter(config):
def setup_filters(config): def setup_filters(config):
filters = [] filters = []
for conf in config: for conf in config:
filter = None
for filter in setup_filter(conf): for filter in setup_filter(conf):
yield None yield None
filters.append(filter) filters.append(filter)
yield ArrayInitializer(*filters) yield filters
def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config): def setup_binary_sensor_core_(binary_sensor_var, config):
if CONF_INTERNAL in config: if CONF_INTERNAL in config:
add(binary_sensor_var.set_internal(CONF_INTERNAL)) add(binary_sensor_var.set_internal(CONF_INTERNAL))
if CONF_DEVICE_CLASS in config: if CONF_DEVICE_CLASS in config:
@@ -219,7 +227,6 @@ def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
if CONF_INVERTED in config: if CONF_INVERTED in config:
add(binary_sensor_var.set_inverted(config[CONF_INVERTED])) add(binary_sensor_var.set_inverted(config[CONF_INVERTED]))
if CONF_FILTERS in config: if CONF_FILTERS in config:
filters = None
for filters in setup_filters(config[CONF_FILTERS]): for filters in setup_filters(config[CONF_FILTERS]):
yield yield
add(binary_sensor_var.add_filters(filters)) add(binary_sensor_var.add_filters(filters))
@@ -254,29 +261,30 @@ def setup_binary_sensor_core_(binary_sensor_var, mqtt_var, config):
('min_length', tim[CONF_MIN_LENGTH]), ('min_length', tim[CONF_MIN_LENGTH]),
('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)), ('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)),
)) ))
timings = ArrayInitializer(*timings, multiline=False)
rhs = App.register_component(binary_sensor_var.make_multi_click_trigger(timings)) rhs = App.register_component(binary_sensor_var.make_multi_click_trigger(timings))
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs) trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
if CONF_INVALID_COOLDOWN in conf: if CONF_INVALID_COOLDOWN in conf:
add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN]))
automation.build_automation(trigger, NoArg, conf) automation.build_automation(trigger, NoArg, conf)
setup_mqtt_component(mqtt_var, config) for conf in config.get(CONF_ON_STATE, []):
rhs = binary_sensor_var.make_state_trigger()
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
automation.build_automation(trigger, bool_, conf)
setup_mqtt_component(binary_sensor_var.Pget_mqtt(), config)
def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config): def setup_binary_sensor(binary_sensor_obj, config):
binary_sensor_var = Pvariable(config[CONF_ID], binary_sensor_obj, if not CORE.has_id(config[CONF_ID]):
has_side_effects=False) binary_sensor_obj = Pvariable(config[CONF_ID], binary_sensor_obj, has_side_effects=True)
mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, CORE.add_job(setup_binary_sensor_core_, binary_sensor_obj, config)
has_side_effects=False)
add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
def register_binary_sensor(var, config): def register_binary_sensor(var, config):
binary_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True) binary_sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
rhs = App.register_binary_sensor(binary_sensor_var) add(App.register_binary_sensor(binary_sensor_var))
mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True) CORE.add_job(setup_binary_sensor_core_, binary_sensor_var, config)
add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var, config)
def core_to_hass_config(data, config): def core_to_hass_config(data, config):
@@ -290,3 +298,32 @@ def core_to_hass_config(data, config):
BUILD_FLAGS = '-DUSE_BINARY_SENSOR' BUILD_FLAGS = '-DUSE_BINARY_SENSOR'
CONF_BINARY_SENSOR_IS_ON = 'binary_sensor.is_on'
BINARY_SENSOR_IS_ON_CONDITION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(BinarySensor),
})
@CONDITION_REGISTRY.register(CONF_BINARY_SENSOR_IS_ON, BINARY_SENSOR_IS_ON_CONDITION_SCHEMA)
def binary_sensor_is_on_to_code(config, condition_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_binary_sensor_is_on_condition(template_arg)
type = BinarySensorCondition.template(arg_type)
yield Pvariable(condition_id, rhs, type=type)
CONF_BINARY_SENSOR_IS_OFF = 'binary_sensor.is_off'
BINARY_SENSOR_IS_OFF_CONDITION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(BinarySensor),
})
@CONDITION_REGISTRY.register(CONF_BINARY_SENSOR_IS_OFF, BINARY_SENSOR_IS_OFF_CONDITION_SCHEMA)
def binary_sensor_is_off_to_code(config, condition_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_binary_sensor_is_off_condition(template_arg)
type = BinarySensorCondition.template(arg_type)
yield Pvariable(condition_id, rhs, type=type)

View File

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

View File

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

View File

@@ -1,14 +1,15 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml.components import binary_sensor from esphome.components import binary_sensor
from esphomeyaml.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \ from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP32BLETracker, \
make_address_array make_address_array
import esphomeyaml.config_validation as cv import esphome.config_validation as cv
from esphomeyaml.const import CONF_MAC_ADDRESS, CONF_NAME from esphome.const import CONF_MAC_ADDRESS, CONF_NAME
from esphomeyaml.helpers import esphomelib_ns, get_variable from esphome.cpp_generator import get_variable
from esphome.cpp_types import esphome_ns
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ['esp32_ble_tracker']
ESP32BLEPresenceDevice = esphomelib_ns.class_('ESP32BLEPresenceDevice', binary_sensor.BinarySensor) ESP32BLEPresenceDevice = esphome_ns.class_('ESP32BLEPresenceDevice', binary_sensor.BinarySensor)
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(ESP32BLEPresenceDevice), cv.GenerateID(): cv.declare_variable_id(ESP32BLEPresenceDevice),
@@ -18,7 +19,6 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
def to_code(config): def to_code(config):
hub = None
for hub in get_variable(config[CONF_ESP32_BLE_ID]): for hub in get_variable(config[CONF_ESP32_BLE_ID]):
yield yield
rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS])) rhs = hub.make_presence_sensor(config[CONF_NAME], make_address_array(config[CONF_MAC_ADDRESS]))

View File

@@ -1,11 +1,12 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import binary_sensor
from esphomeyaml.components import binary_sensor from esphome.components.esp32_touch import ESP32TouchComponent
from esphomeyaml.components.esp32_touch import ESP32TouchComponent import esphome.config_validation as cv
from esphomeyaml.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32 from esphome.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import get_variable, global_ns from esphome.cpp_generator import get_variable
from esphomeyaml.pins import validate_gpio_pin from esphome.cpp_types import global_ns
from esphome.pins import validate_gpio_pin
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]

View File

@@ -1,20 +1,19 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome import pins
from esphomeyaml import pins from esphome.components import binary_sensor
from esphomeyaml.components import binary_sensor import esphome.config_validation as cv
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_PIN from esphome.const import CONF_ID, CONF_NAME, CONF_PIN
from esphomeyaml.helpers import App, gpio_input_pin_expression, variable, Application, \ from esphome.cpp_generator import Pvariable
setup_component, Component from esphome.cpp_helpers import gpio_input_pin_expression, setup_component
from esphome.cpp_types import App, Component
MakeGPIOBinarySensor = Application.struct('MakeGPIOBinarySensor')
GPIOBinarySensorComponent = binary_sensor.binary_sensor_ns.class_('GPIOBinarySensorComponent', GPIOBinarySensorComponent = binary_sensor.binary_sensor_ns.class_('GPIOBinarySensorComponent',
binary_sensor.BinarySensor, binary_sensor.BinarySensor,
Component) Component)
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(GPIOBinarySensorComponent), cv.GenerateID(): cv.declare_variable_id(GPIOBinarySensorComponent),
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeGPIOBinarySensor),
vol.Required(CONF_PIN): pins.gpio_input_pin_schema vol.Required(CONF_PIN): pins.gpio_input_pin_schema
}).extend(cv.COMPONENT_SCHEMA.schema)) }).extend(cv.COMPONENT_SCHEMA.schema))
@@ -24,9 +23,9 @@ def to_code(config):
for pin in gpio_input_pin_expression(config[CONF_PIN]): for pin in gpio_input_pin_expression(config[CONF_PIN]):
yield yield
rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin) rhs = App.make_gpio_binary_sensor(config[CONF_NAME], pin)
gpio = variable(config[CONF_MAKE_ID], rhs) gpio = Pvariable(config[CONF_ID], rhs)
binary_sensor.setup_binary_sensor(gpio.Pgpio, gpio.Pmqtt, config) binary_sensor.setup_binary_sensor(gpio, config)
setup_component(gpio.Pgpio, config) setup_component(gpio, config)
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR' BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'

View File

@@ -0,0 +1,30 @@
import voluptuous as vol
from esphome.components import binary_sensor
import esphome.config_validation as cv
from esphome.const import CONF_ENTITY_ID, CONF_ID, CONF_NAME
from esphome.cpp_generator import Pvariable
from esphome.cpp_types import App
DEPENDENCIES = ['api']
HomeassistantBinarySensor = binary_sensor.binary_sensor_ns.class_('HomeassistantBinarySensor',
binary_sensor.BinarySensor)
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(HomeassistantBinarySensor),
vol.Required(CONF_ENTITY_ID): cv.entity_id,
}))
def to_code(config):
rhs = App.make_homeassistant_binary_sensor(config[CONF_NAME], config[CONF_ENTITY_ID])
subs = Pvariable(config[CONF_ID], rhs)
binary_sensor.setup_binary_sensor(subs, config)
BUILD_FLAGS = '-DUSE_HOMEASSISTANT_BINARY_SENSOR'
def to_hass_config(data, config):
return binary_sensor.core_to_hass_config(data, config)

View File

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

View File

@@ -1,11 +1,11 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import binary_sensor
from esphomeyaml.components import binary_sensor from esphome.components.pn532 import PN532Component
from esphomeyaml.components.pn532 import PN532Component import esphome.config_validation as cv
from esphomeyaml.const import CONF_NAME, CONF_UID from esphome.const import CONF_NAME, CONF_UID
from esphomeyaml.core import HexInt from esphome.core import HexInt
from esphomeyaml.helpers import ArrayInitializer, get_variable from esphome.cpp_generator import get_variable
DEPENDENCIES = ['pn532'] DEPENDENCIES = ['pn532']
@@ -41,7 +41,7 @@ def to_code(config):
for hub in get_variable(config[CONF_PN532_ID]): for hub in get_variable(config[CONF_PN532_ID]):
yield yield
addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')]
rhs = hub.make_tag(config[CONF_NAME], ArrayInitializer(*addr, multiline=False)) rhs = hub.make_tag(config[CONF_NAME], addr)
binary_sensor.register_binary_sensor(rhs, config) binary_sensor.register_binary_sensor(rhs, config)

View File

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

View File

@@ -1,45 +1,63 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import binary_sensor
from esphomeyaml.components import binary_sensor from esphome.components.remote_receiver import RemoteReceiverComponent, remote_ns
from esphomeyaml.components.remote_receiver import RemoteReceiverComponent, remote_ns from esphome.components.remote_transmitter import RC_SWITCH_RAW_SCHEMA, \
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_A_SCHEMA, RC_SWITCH_TYPE_B_SCHEMA, RC_SWITCH_TYPE_C_SCHEMA, \
RC_SWITCH_TYPE_D_SCHEMA, binary_code, build_rc_switch_protocol 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, \ import esphome.config_validation as cv
CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, \ from esphome.const import CONF_ADDRESS, CONF_CHANNEL, CONF_CODE, CONF_COMMAND, CONF_DATA, \
CONF_PANASONIC, CONF_PROTOCOL, CONF_RAW, CONF_RC_SWITCH_RAW, CONF_RC_SWITCH_TYPE_A, \ CONF_DEVICE, CONF_FAMILY, CONF_GROUP, CONF_ID, CONF_JVC, 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_RC_SWITCH_TYPE_B, CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, CONF_SAMSUNG, CONF_SONY, \
CONF_STATE CONF_STATE, CONF_RC5
from esphomeyaml.helpers import ArrayInitializer, Pvariable, get_variable from esphome.cpp_generator import Pvariable, get_variable, progmem_array
from esphome.cpp_types import int32
DEPENDENCIES = ['remote_receiver'] DEPENDENCIES = ['remote_receiver']
REMOTE_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_SAMSUNG, CONF_RAW, REMOTE_KEYS = [CONF_JVC, 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_RAW, CONF_RC_SWITCH_TYPE_A, CONF_RC_SWITCH_TYPE_B,
CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D] CONF_RC_SWITCH_TYPE_C, CONF_RC_SWITCH_TYPE_D, CONF_RC5]
CONF_REMOTE_RECEIVER_ID = 'remote_receiver_id' CONF_REMOTE_RECEIVER_ID = 'remote_receiver_id'
CONF_RECEIVER_ID = 'receiver_id' CONF_RECEIVER_ID = 'receiver_id'
RemoteReceiver = remote_ns.class_('RemoteReceiver', binary_sensor.BinarySensor) RemoteReceiver = remote_ns.class_('RemoteReceiver', binary_sensor.BinarySensor)
JVCReceiver = remote_ns.class_('JVCReceiver', RemoteReceiver)
LGReceiver = remote_ns.class_('LGReceiver', RemoteReceiver) LGReceiver = remote_ns.class_('LGReceiver', RemoteReceiver)
NECReceiver = remote_ns.class_('NECReceiver', RemoteReceiver) NECReceiver = remote_ns.class_('NECReceiver', RemoteReceiver)
PanasonicReceiver = remote_ns.class_('PanasonicReceiver', RemoteReceiver) PanasonicReceiver = remote_ns.class_('PanasonicReceiver', RemoteReceiver)
RawReceiver = remote_ns.class_('RawReceiver', RemoteReceiver) RawReceiver = remote_ns.class_('RawReceiver', RemoteReceiver)
SamsungReceiver = remote_ns.class_('SamsungReceiver', RemoteReceiver) SamsungReceiver = remote_ns.class_('SamsungReceiver', RemoteReceiver)
SonyReceiver = remote_ns.class_('SonyReceiver', RemoteReceiver) SonyReceiver = remote_ns.class_('SonyReceiver', RemoteReceiver)
RC5Receiver = remote_ns.class_('RC5Receiver', RemoteReceiver)
RCSwitchRawReceiver = remote_ns.class_('RCSwitchRawReceiver', RemoteReceiver) RCSwitchRawReceiver = remote_ns.class_('RCSwitchRawReceiver', RemoteReceiver)
RCSwitchTypeAReceiver = remote_ns.class_('RCSwitchTypeAReceiver', RCSwitchRawReceiver) RCSwitchTypeAReceiver = remote_ns.class_('RCSwitchTypeAReceiver', RCSwitchRawReceiver)
RCSwitchTypeBReceiver = remote_ns.class_('RCSwitchTypeBReceiver', RCSwitchRawReceiver) RCSwitchTypeBReceiver = remote_ns.class_('RCSwitchTypeBReceiver', RCSwitchRawReceiver)
RCSwitchTypeCReceiver = remote_ns.class_('RCSwitchTypeCReceiver', RCSwitchRawReceiver) RCSwitchTypeCReceiver = remote_ns.class_('RCSwitchTypeCReceiver', RCSwitchRawReceiver)
RCSwitchTypeDReceiver = remote_ns.class_('RCSwitchTypeDReceiver', RCSwitchRawReceiver) RCSwitchTypeDReceiver = remote_ns.class_('RCSwitchTypeDReceiver', RCSwitchRawReceiver)
def validate_raw(value):
if isinstance(value, dict):
return vol.Schema({
cv.GenerateID(): cv.declare_variable_id(int32),
vol.Required(CONF_DATA): [vol.Any(vol.Coerce(int), cv.time_period_microseconds)],
})(value)
return validate_raw({
CONF_DATA: value
})
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(RemoteReceiver), cv.GenerateID(): cv.declare_variable_id(RemoteReceiver),
vol.Optional(CONF_JVC): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t,
}),
vol.Optional(CONF_LG): vol.Schema({ vol.Optional(CONF_LG): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=28): vol.All(vol.Coerce(int), cv.one_of(28, 32)), vol.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True),
}), }),
vol.Optional(CONF_NEC): vol.Schema({ vol.Optional(CONF_NEC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
@@ -50,13 +68,17 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
}), }),
vol.Optional(CONF_SONY): vol.Schema({ vol.Optional(CONF_SONY): vol.Schema({
vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Required(CONF_DATA): cv.hex_uint32_t,
vol.Optional(CONF_NBITS, default=12): vol.All(vol.Coerce(int), cv.one_of(12, 15, 20)), vol.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True),
}), }),
vol.Optional(CONF_PANASONIC): vol.Schema({ vol.Optional(CONF_PANASONIC): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint32_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_RC5): vol.Schema({
vol.Required(CONF_ADDRESS): vol.All(cv.hex_int, vol.Range(min=0, max=0x1F)),
vol.Required(CONF_COMMAND): vol.All(cv.hex_int, vol.Range(min=0, max=0x3F)),
}),
vol.Optional(CONF_RAW): validate_raw,
vol.Optional(CONF_RC_SWITCH_RAW): RC_SWITCH_RAW_SCHEMA, 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_A): RC_SWITCH_TYPE_A_SCHEMA,
vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA, vol.Optional(CONF_RC_SWITCH_TYPE_B): RC_SWITCH_TYPE_B_SCHEMA,
@@ -71,42 +93,46 @@ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend
def receiver_base(full_config): def receiver_base(full_config):
name = full_config[CONF_NAME] name = full_config[CONF_NAME]
key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS) key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS)
if key == CONF_JVC:
return JVCReceiver.new(name, config[CONF_DATA])
if key == CONF_LG: if key == CONF_LG:
return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_NEC: if key == CONF_NEC:
return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_PANASONIC: if key == CONF_PANASONIC:
return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_SAMSUNG: if key == CONF_SAMSUNG:
return SamsungReceiver.new(name, config[CONF_DATA]) return SamsungReceiver.new(name, config[CONF_DATA])
elif key == CONF_SONY: if key == CONF_SONY:
return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_RAW: if key == CONF_RC5:
data = ArrayInitializer(*config, multiline=False) return RC5Receiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
return RawReceiver.new(name, data) if key == CONF_RAW:
elif key == CONF_RC_SWITCH_RAW: arr = progmem_array(config[CONF_ID], config[CONF_DATA])
return RawReceiver.new(name, arr, len(config[CONF_DATA]))
if key == CONF_RC_SWITCH_RAW:
return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_CODE]), len(config[CONF_CODE])) binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
elif key == CONF_RC_SWITCH_TYPE_A: if key == CONF_RC_SWITCH_TYPE_A:
return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_GROUP]), binary_code(config[CONF_GROUP]),
binary_code(config[CONF_DEVICE]), binary_code(config[CONF_DEVICE]),
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_B: if key == CONF_RC_SWITCH_TYPE_B:
return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_ADDRESS], config[CONF_CHANNEL],
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_C: if key == CONF_RC_SWITCH_TYPE_C:
return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_FAMILY][0]) - ord('a'), ord(config[CONF_FAMILY][0]) - ord('a'),
config[CONF_GROUP], config[CONF_DEVICE], config[CONF_GROUP], config[CONF_DEVICE],
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_D: if key == CONF_RC_SWITCH_TYPE_D:
return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_GROUP][0]) - ord('a'), ord(config[CONF_GROUP][0]) - ord('a'),
config[CONF_DEVICE], config[CONF_STATE]) config[CONF_DEVICE], config[CONF_STATE])
else:
raise NotImplementedError("Unknown receiver type {}".format(config)) raise NotImplementedError("Unknown receiver type {}".format(config))
def to_code(config): def to_code(config):

View File

@@ -1,26 +1,24 @@
import esphomeyaml.config_validation as cv from esphome.components import binary_sensor
from esphomeyaml.components import binary_sensor import esphome.config_validation as cv
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME from esphome.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, Application, variable, setup_component, Component from esphome.cpp_generator import Pvariable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component
DEPENDENCIES = ['mqtt']
MakeStatusBinarySensor = Application.struct('MakeStatusBinarySensor')
StatusBinarySensor = binary_sensor.binary_sensor_ns.class_('StatusBinarySensor', StatusBinarySensor = binary_sensor.binary_sensor_ns.class_('StatusBinarySensor',
binary_sensor.BinarySensor, binary_sensor.BinarySensor,
Component) Component)
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeStatusBinarySensor),
cv.GenerateID(): cv.declare_variable_id(StatusBinarySensor), cv.GenerateID(): cv.declare_variable_id(StatusBinarySensor),
}).extend(cv.COMPONENT_SCHEMA.schema)) }).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config): def to_code(config):
rhs = App.make_status_binary_sensor(config[CONF_NAME]) rhs = App.make_status_binary_sensor(config[CONF_NAME])
status = variable(config[CONF_MAKE_ID], rhs) status = Pvariable(config[CONF_ID], rhs)
binary_sensor.setup_binary_sensor(status.Pstatus, status.Pmqtt, config) binary_sensor.setup_binary_sensor(status, config)
setup_component(status.Pstatus, config) setup_component(status, config)
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR' BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'

View File

@@ -0,0 +1,60 @@
import voluptuous as vol
from esphome.automation import ACTION_REGISTRY
from esphome.components import binary_sensor
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE
from esphome.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import Action, App, Component, bool_, optional
TemplateBinarySensor = binary_sensor.binary_sensor_ns.class_('TemplateBinarySensor',
binary_sensor.BinarySensor,
Component)
BinarySensorPublishAction = binary_sensor.binary_sensor_ns.class_('BinarySensorPublishAction',
Action)
PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateBinarySensor),
vol.Optional(CONF_LAMBDA): cv.lambda_,
}).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config):
rhs = App.make_template_binary_sensor(config[CONF_NAME])
var = Pvariable(config[CONF_ID], rhs)
binary_sensor.setup_binary_sensor(var, config)
setup_component(var, config)
if CONF_LAMBDA in config:
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(bool_)):
yield
add(var.set_template(template_))
BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR'
CONF_BINARY_SENSOR_TEMPLATE_PUBLISH = 'binary_sensor.template.publish'
BINARY_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.use_variable_id(binary_sensor.BinarySensor),
vol.Required(CONF_STATE): cv.templatable(cv.boolean),
})
@ACTION_REGISTRY.register(CONF_BINARY_SENSOR_TEMPLATE_PUBLISH,
BINARY_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA)
def binary_sensor_template_publish_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_binary_sensor_publish_action(template_arg)
type = BinarySensorPublishAction.template(arg_type)
action = Pvariable(action_id, rhs, type=type)
for template_ in templatable(config[CONF_STATE], arg_type, bool_):
yield None
add(action.set_state(template_))
yield action
def to_hass_config(data, config):
return binary_sensor.core_to_hass_config(data, config)

View File

@@ -1,17 +1,19 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY from esphome.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.components import mqtt from esphome.components import mqtt
import esphomeyaml.config_validation as cv from esphome.components.mqtt import setup_mqtt_component
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_INTERNAL import esphome.config_validation as cv
from esphomeyaml.helpers import Pvariable, esphomelib_ns, setup_mqtt_component, add, \ from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID
TemplateArguments, get_variable, Action, Nameable from esphome.core import CORE
from esphome.cpp_generator import Pvariable, add, get_variable
from esphome.cpp_types import Action, Nameable, esphome_ns
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
}) })
cover_ns = esphomelib_ns.namespace('cover') cover_ns = esphome_ns.namespace('cover')
Cover = cover_ns.class_('Cover', Nameable) Cover = cover_ns.class_('Cover', Nameable)
MQTTCoverComponent = cover_ns.class_('MQTTCoverComponent', mqtt.MQTTComponent) MQTTCoverComponent = cover_ns.class_('MQTTCoverComponent', mqtt.MQTTComponent)
@@ -20,6 +22,12 @@ CoverState = cover_ns.class_('CoverState')
COVER_OPEN = cover_ns.COVER_OPEN COVER_OPEN = cover_ns.COVER_OPEN
COVER_CLOSED = cover_ns.COVER_CLOSED COVER_CLOSED = cover_ns.COVER_CLOSED
validate_cover_state = cv.one_of('OPEN', 'CLOSED', upper=True)
COVER_STATES = {
'OPEN': COVER_OPEN,
'CLOSED': COVER_CLOSED,
}
# Actions # Actions
OpenAction = cover_ns.class_('OpenAction', Action) OpenAction = cover_ns.class_('OpenAction', Action)
CloseAction = cover_ns.class_('CloseAction', Action) CloseAction = cover_ns.class_('CloseAction', Action)
@@ -33,16 +41,14 @@ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
COVER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(COVER_SCHEMA.schema) COVER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(COVER_SCHEMA.schema)
def setup_cover_core_(cover_var, mqtt_var, config): def setup_cover_core_(cover_var, config):
if CONF_INTERNAL in config: if CONF_INTERNAL in config:
add(cover_var.set_internal(config[CONF_INTERNAL])) add(cover_var.set_internal(config[CONF_INTERNAL]))
setup_mqtt_component(mqtt_var, config) setup_mqtt_component(cover_var.Pget_mqtt(), config)
def setup_cover(cover_obj, mqtt_obj, config): def setup_cover(cover_obj, config):
cover_var = Pvariable(config[CONF_ID], cover_obj, has_side_effects=False) CORE.add_job(setup_cover_core_, cover_obj, config)
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' BUILD_FLAGS = '-DUSE_COVER'
@@ -54,8 +60,7 @@ COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA)
def cover_open_to_code(config, action_id, arg_type): def cover_open_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_open_action(template_arg) rhs = var.make_open_action(template_arg)
@@ -70,8 +75,7 @@ COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA)
def cover_close_to_code(config, action_id, arg_type): def cover_close_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_close_action(template_arg) rhs = var.make_close_action(template_arg)
@@ -86,8 +90,7 @@ COVER_STOP_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA)
def cover_stop_to_code(config, action_id, arg_type): def cover_stop_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_stop_action(template_arg) rhs = var.make_stop_action(template_arg)

View File

@@ -0,0 +1,88 @@
import voluptuous as vol
from esphome import automation
from esphome.automation import ACTION_REGISTRY
from esphome.components import cover
import esphome.config_validation as cv
from esphome.const import CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_ID, CONF_LAMBDA, CONF_NAME, \
CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_STATE, CONF_STOP_ACTION
from esphome.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import Action, App, NoArg, optional
from esphome.py_compat import string_types
TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover)
CoverPublishAction = cover.cover_ns.class_('CoverPublishAction', Action)
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateCover),
vol.Optional(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_ASSUMED_STATE): 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),
}).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config):
rhs = App.make_template_cover(config[CONF_NAME])
var = Pvariable(config[CONF_ID], rhs)
cover.setup_cover(var, config)
setup_component(var, config)
if CONF_LAMBDA in config:
for template_ in process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(cover.CoverState)):
yield
add(var.set_state_lambda(template_))
if CONF_OPEN_ACTION in config:
automation.build_automation(var.get_open_trigger(), NoArg,
config[CONF_OPEN_ACTION])
if CONF_CLOSE_ACTION in config:
automation.build_automation(var.get_close_trigger(), NoArg,
config[CONF_CLOSE_ACTION])
if CONF_STOP_ACTION in config:
automation.build_automation(var.get_stop_trigger(), NoArg,
config[CONF_STOP_ACTION])
if CONF_OPTIMISTIC in config:
add(var.set_optimistic(config[CONF_OPTIMISTIC]))
if CONF_ASSUMED_STATE in config:
add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
BUILD_FLAGS = '-DUSE_TEMPLATE_COVER'
CONF_COVER_TEMPLATE_PUBLISH = 'cover.template.publish'
COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.use_variable_id(cover.Cover),
vol.Required(CONF_STATE): cv.templatable(cover.validate_cover_state),
})
@ACTION_REGISTRY.register(CONF_COVER_TEMPLATE_PUBLISH,
COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA)
def cover_template_publish_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
rhs = var.make_cover_publish_action(template_arg)
type = CoverPublishAction.template(arg_type)
action = Pvariable(action_id, rhs, type=type)
state = config[CONF_STATE]
if isinstance(state, string_types):
template_ = cover.COVER_STATES[state]
else:
for template_ in templatable(state, arg_type, cover.CoverState):
yield None
add(action.set_state(template_))
yield action
def to_hass_config(data, config):
ret = cover.core_to_hass_config(data, config)
if ret is None:
return None
if CONF_OPTIMISTIC in config:
ret['optimistic'] = config[CONF_OPTIMISTIC]
return ret

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml import config_validation as cv, pins from esphome import config_validation as cv, pins
from esphomeyaml.automation import ACTION_REGISTRY, maybe_simple_id from esphome.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_RUN_CYCLES, CONF_RUN_DURATION, \ from esphome.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \
CONF_SLEEP_DURATION, CONF_WAKEUP_PIN, CONF_MODE, CONF_PINS CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN
from esphomeyaml.helpers import Action, App, Component, Pvariable, TemplateArguments, add, \ from esphome.cpp_generator import Pvariable, StructInitializer, add, get_variable
esphomelib_ns, get_variable, gpio_input_pin_expression, setup_component, global_ns, \ from esphome.cpp_helpers import gpio_input_pin_expression, setup_component
StructInitializer from esphome.cpp_types import Action, App, Component, esphome_ns, global_ns
def validate_pin_number(value): def validate_pin_number(value):
@@ -17,11 +17,11 @@ def validate_pin_number(value):
return value return value
DeepSleepComponent = esphomelib_ns.class_('DeepSleepComponent', Component) DeepSleepComponent = esphome_ns.class_('DeepSleepComponent', Component)
EnterDeepSleepAction = esphomelib_ns.class_('EnterDeepSleepAction', Action) EnterDeepSleepAction = esphome_ns.class_('EnterDeepSleepAction', Action)
PreventDeepSleepAction = esphomelib_ns.class_('PreventDeepSleepAction', Action) PreventDeepSleepAction = esphome_ns.class_('PreventDeepSleepAction', Action)
WakeupPinMode = esphomelib_ns.enum('WakeupPinMode') WakeupPinMode = esphome_ns.enum('WakeupPinMode')
WAKEUP_PIN_MODES = { WAKEUP_PIN_MODES = {
'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE, 'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE,
'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE, 'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE,
@@ -29,7 +29,7 @@ WAKEUP_PIN_MODES = {
} }
esp_sleep_ext1_wakeup_mode_t = global_ns.enum('esp_sleep_ext1_wakeup_mode_t') esp_sleep_ext1_wakeup_mode_t = global_ns.enum('esp_sleep_ext1_wakeup_mode_t')
Ext1Wakeup = esphomelib_ns.struct('Ext1Wakeup') Ext1Wakeup = esphome_ns.struct('Ext1Wakeup')
EXT1_WAKEUP_MODES = { EXT1_WAKEUP_MODES = {
'ALL_LOW': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW, 'ALL_LOW': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW,
'ANY_HIGH': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH, 'ANY_HIGH': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH,
@@ -43,15 +43,17 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema, vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema,
validate_pin_number), validate_pin_number),
vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32, vol.Upper, vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32,
cv.one_of(*WAKEUP_PIN_MODES)), cv.one_of(*WAKEUP_PIN_MODES), upper=True),
vol.Optional(CONF_ESP32_EXT1_WAKEUP): vol.All(cv.only_on_esp32, vol.Schema({ vol.Optional(CONF_ESP32_EXT1_WAKEUP): vol.All(cv.only_on_esp32, vol.Schema({
vol.Required(CONF_PINS): vol.All(cv.ensure_list, [pins.shorthand_input_pin], vol.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number),
[validate_pin_number]), vol.Required(CONF_MODE): cv.one_of(*EXT1_WAKEUP_MODES, upper=True),
vol.Required(CONF_MODE): vol.All(vol.Upper, cv.one_of(*EXT1_WAKEUP_MODES)),
})), })),
vol.Optional(CONF_RUN_CYCLES): cv.positive_int,
vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
vol.Optional(CONF_RUN_CYCLES): cv.invalid("The run_cycles option has been removed in 1.11.0 as "
"it was essentially the same as a run_duration of 0s."
"Please use run_duration now.")
}).extend(cv.COMPONENT_SCHEMA.schema) }).extend(cv.COMPONENT_SCHEMA.schema)
@@ -66,8 +68,6 @@ def to_code(config):
add(deep_sleep.set_wakeup_pin(pin)) add(deep_sleep.set_wakeup_pin(pin))
if CONF_WAKEUP_PIN_MODE in config: if CONF_WAKEUP_PIN_MODE in config:
add(deep_sleep.set_wakeup_pin_mode(WAKEUP_PIN_MODES[config[CONF_WAKEUP_PIN_MODE]])) 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: if CONF_RUN_DURATION in config:
add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION])) add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION]))
@@ -95,8 +95,7 @@ DEEP_SLEEP_ENTER_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_DEEP_SLEEP_ENTER, DEEP_SLEEP_ENTER_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_DEEP_SLEEP_ENTER, DEEP_SLEEP_ENTER_ACTION_SCHEMA)
def deep_sleep_enter_to_code(config, action_id, arg_type): def deep_sleep_enter_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_enter_deep_sleep_action(template_arg) rhs = var.make_enter_deep_sleep_action(template_arg)
@@ -111,8 +110,7 @@ DEEP_SLEEP_PREVENT_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_DEEP_SLEEP_PREVENT, DEEP_SLEEP_PREVENT_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_DEEP_SLEEP_PREVENT, DEEP_SLEEP_PREVENT_ACTION_SCHEMA)
def deep_sleep_prevent_to_code(config, action_id, arg_type): def deep_sleep_prevent_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_prevent_deep_sleep_action(template_arg) rhs = var.make_prevent_deep_sleep_action(template_arg)

View File

@@ -0,0 +1,127 @@
# coding=utf-8
import voluptuous as vol
from esphome import core
from esphome.automation import ACTION_REGISTRY, maybe_simple_id
import esphome.config_validation as cv
from esphome.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL, CONF_PAGES, CONF_ID
from esphome.core import CORE
from esphome.cpp_generator import add, process_lambda, Pvariable, templatable, get_variable
from esphome.cpp_types import esphome_ns, void, Action
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
display_ns = esphome_ns.namespace('display')
DisplayBuffer = display_ns.class_('DisplayBuffer')
DisplayPage = display_ns.class_('DisplayPage')
DisplayPagePtr = DisplayPage.operator('ptr')
DisplayBufferRef = DisplayBuffer.operator('ref')
DisplayPageShowAction = display_ns.class_('DisplayPageShowAction', Action)
DisplayPageShowNextAction = display_ns.class_('DisplayPageShowNextAction', Action)
DisplayPageShowPrevAction = display_ns.class_('DisplayPageShowPrevAction', Action)
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,
vol.Optional(CONF_PAGES): vol.All(cv.ensure_list({
cv.GenerateID(): cv.declare_variable_id(DisplayPage),
vol.Required(CONF_LAMBDA): cv.lambda_,
}), vol.Length(min=1)),
})
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]]))
if CONF_PAGES in config:
pages = []
for conf in config[CONF_PAGES]:
for lambda_ in process_lambda(conf[CONF_LAMBDA], [(DisplayBufferRef, 'it')],
return_type=void):
yield
var = Pvariable(conf[CONF_ID], DisplayPage.new(lambda_))
pages.append(var)
add(display_var.set_pages(pages))
CONF_DISPLAY_PAGE_SHOW = 'display.page.show'
DISPLAY_PAGE_SHOW_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.templatable(cv.use_variable_id(DisplayPage)),
})
@ACTION_REGISTRY.register(CONF_DISPLAY_PAGE_SHOW, DISPLAY_PAGE_SHOW_ACTION_SCHEMA)
def display_page_show_to_code(config, action_id, arg_type, template_arg):
type = DisplayPageShowAction.template(arg_type)
action = Pvariable(action_id, type.new(), type=type)
if isinstance(config[CONF_ID], core.Lambda):
for template_ in templatable(config[CONF_ID], arg_type, DisplayPagePtr):
yield None
add(action.set_page(template_))
else:
for var in get_variable(config[CONF_ID]):
yield None
add(action.set_page(var))
yield action
CONF_DISPLAY_PAGE_SHOW_NEXT = 'display.page.show_next'
DISPLAY_PAGE_SHOW_NEXT_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.templatable(cv.use_variable_id(DisplayBuffer)),
})
@ACTION_REGISTRY.register(CONF_DISPLAY_PAGE_SHOW_NEXT, DISPLAY_PAGE_SHOW_NEXT_ACTION_SCHEMA)
def display_page_show_next_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
type = DisplayPageShowNextAction.template(arg_type)
yield Pvariable(action_id, type.new(var), type=type)
CONF_DISPLAY_PAGE_SHOW_PREVIOUS = 'display.page.show_previous'
DISPLAY_PAGE_SHOW_PREVIOUS_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.templatable(cv.use_variable_id(DisplayBuffer)),
})
@ACTION_REGISTRY.register(CONF_DISPLAY_PAGE_SHOW_PREVIOUS, DISPLAY_PAGE_SHOW_PREVIOUS_ACTION_SCHEMA)
def display_page_show_previous_to_code(config, action_id, arg_type, template_arg):
for var in get_variable(config[CONF_ID]):
yield None
type = DisplayPageShowPrevAction.template(arg_type)
yield Pvariable(action_id, type.new(var), type=type)
def setup_display(display_var, config):
CORE.add_job(setup_display_core_, display_var, config)
BUILD_FLAGS = '-DUSE_DISPLAY'

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,25 +1,26 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome import pins
from esphomeyaml import pins from esphome.components import display
from esphomeyaml.components import display from esphome.components.display import ssd1306_spi
from esphomeyaml.components.display import ssd1306_spi import esphome.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, \ from esphome.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, CONF_LAMBDA, CONF_MODEL, \
CONF_MODEL, CONF_RESET_PIN, CONF_LAMBDA CONF_PAGES, CONF_RESET_PIN
from esphomeyaml.helpers import App, Pvariable, add, \ from esphome.cpp_generator import Pvariable, add, process_lambda
gpio_output_pin_expression, process_lambda, setup_component from esphome.cpp_helpers import gpio_output_pin_expression, setup_component
from esphome.cpp_types import App, void
DEPENDENCIES = ['i2c'] DEPENDENCIES = ['i2c']
I2CSSD1306 = display.display_ns.class_('I2CSSD1306', ssd1306_spi.SSD1306) I2CSSD1306 = display.display_ns.class_('I2CSSD1306', ssd1306_spi.SSD1306)
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(I2CSSD1306), cv.GenerateID(): cv.declare_variable_id(I2CSSD1306),
vol.Required(CONF_MODEL): ssd1306_spi.SSD1306_MODEL, vol.Required(CONF_MODEL): ssd1306_spi.SSD1306_MODEL,
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean, vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
vol.Optional(CONF_ADDRESS): cv.i2c_address, vol.Optional(CONF_ADDRESS): cv.i2c_address,
}).extend(cv.COMPONENT_SCHEMA.schema) }).extend(cv.COMPONENT_SCHEMA.schema), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA))
def to_code(config): def to_code(config):
@@ -36,7 +37,7 @@ def to_code(config):
add(ssd.set_address(config[CONF_ADDRESS])) add(ssd.set_address(config[CONF_ADDRESS]))
if CONF_LAMBDA in config: if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], for lambda_ in process_lambda(config[CONF_LAMBDA],
[(display.DisplayBufferRef, 'it')]): [(display.DisplayBufferRef, 'it')], return_type=void):
yield yield
add(ssd.set_writer(lambda_)) add(ssd.set_writer(lambda_))

View File

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

View File

@@ -1,13 +1,15 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome import pins
from esphomeyaml import pins from esphome.components import display, spi
from esphomeyaml.components import display, spi from esphome.components.spi import SPIComponent
from esphomeyaml.components.spi import SPIComponent import esphome.config_validation as cv
from esphomeyaml.const import CONF_BUSY_PIN, CONF_CS_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \ from esphome.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 CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN, CONF_SPI_ID
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_input_pin_expression, \ from esphome.cpp_generator import Pvariable, add, get_variable, process_lambda
gpio_output_pin_expression, process_lambda, setup_component, PollingComponent from esphome.cpp_helpers import gpio_input_pin_expression, gpio_output_pin_expression, \
setup_component
from esphome.cpp_types import App, PollingComponent, void
DEPENDENCIES = ['spi'] DEPENDENCIES = ['spi']
@@ -15,7 +17,6 @@ WaveshareEPaperTypeA = display.display_ns.WaveshareEPaperTypeA
WaveshareEPaper = display.display_ns.class_('WaveshareEPaper', WaveshareEPaper = display.display_ns.class_('WaveshareEPaper',
PollingComponent, spi.SPIDevice, display.DisplayBuffer) PollingComponent, spi.SPIDevice, display.DisplayBuffer)
WaveshareEPaperTypeAModel = display.display_ns.enum('WaveshareEPaperTypeAModel') WaveshareEPaperTypeAModel = display.display_ns.enum('WaveshareEPaperTypeAModel')
WaveshareEPaperTypeBModel = display.display_ns.enum('WaveshareEPaperTypeBModel') WaveshareEPaperTypeBModel = display.display_ns.enum('WaveshareEPaperTypeBModel')
@@ -43,11 +44,12 @@ PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
vol.Required(CONF_MODEL): vol.All(vol.Lower, cv.one_of(*MODELS)), vol.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True),
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
vol.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, vol.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
vol.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t, vol.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t,
}).extend(cv.COMPONENT_SCHEMA.schema), validate_full_update_every_only_type_a) }).extend(cv.COMPONENT_SCHEMA.schema), validate_full_update_every_only_type_a,
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA))
def to_code(config): def to_code(config):
@@ -69,7 +71,8 @@ def to_code(config):
raise NotImplementedError() raise NotImplementedError()
if CONF_LAMBDA in config: if CONF_LAMBDA in config:
for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')]): for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')],
return_type=void):
yield yield
add(epaper.set_writer(lambda_)) add(epaper.set_writer(lambda_))
if CONF_RESET_PIN in config: if CONF_RESET_PIN in config:

View File

@@ -1,20 +1,21 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml import config_validation as cv from esphome import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32 from esphome.const import CONF_ID, CONF_SCAN_INTERVAL, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, ArrayInitializer, Component, Pvariable, RawExpression, add, \ from esphome.cpp_generator import Pvariable, RawExpression, add
esphomelib_ns, setup_component from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component, esphome_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
ESP32BLEBeacon = esphomelib_ns.class_('ESP32BLEBeacon', Component) ESP32BLEBeacon = esphome_ns.class_('ESP32BLEBeacon', Component)
CONF_MAJOR = 'major' CONF_MAJOR = 'major'
CONF_MINOR = 'minor' CONF_MINOR = 'minor'
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon), cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon),
vol.Required(CONF_TYPE): vol.All(vol.Upper, cv.one_of('IBEACON')), vol.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True),
vol.Required(CONF_UUID): cv.uuid, vol.Required(CONF_UUID): cv.uuid,
vol.Optional(CONF_MAJOR): cv.uint16_t, vol.Optional(CONF_MAJOR): cv.uint16_t,
vol.Optional(CONF_MINOR): cv.uint16_t, vol.Optional(CONF_MINOR): cv.uint16_t,
@@ -25,7 +26,7 @@ CONFIG_SCHEMA = vol.Schema({
def to_code(config): def to_code(config):
uuid = config[CONF_UUID].hex uuid = config[CONF_UUID].hex
uuid_arr = [RawExpression('0x{}'.format(uuid[i:i + 2])) for i in range(0, len(uuid), 2)] 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)) rhs = App.make_esp32_ble_beacon(uuid_arr)
ble = Pvariable(config[CONF_ID], rhs) ble = Pvariable(config[CONF_ID], rhs)
if CONF_MAJOR in config: if CONF_MAJOR in config:
add(ble.set_major(config[CONF_MAJOR])) add(ble.set_major(config[CONF_MAJOR]))

View File

@@ -0,0 +1,40 @@
import voluptuous as vol
from esphome import config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
from esphome.core import HexInt
from esphome.cpp_generator import Pvariable, add
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component, esphome_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONF_ESP32_BLE_ID = 'esp32_ble_id'
ESP32BLETracker = esphome_ns.class_('ESP32BLETracker', Component)
XiaomiSensor = esphome_ns.class_('XiaomiSensor', sensor.Sensor)
XiaomiDevice = esphome_ns.class_('XiaomiDevice')
XIAOMI_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(XiaomiSensor)
})
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker),
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_seconds,
}).extend(cv.COMPONENT_SCHEMA.schema)
def make_address_array(address):
return [HexInt(i) for i in address.parts]
def to_code(config):
rhs = App.make_esp32_ble_tracker()
ble = Pvariable(config[CONF_ID], rhs)
if CONF_SCAN_INTERVAL in config:
add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
setup_component(ble, config)
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'

View File

@@ -1,12 +1,14 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml import config_validation as cv from esphome import config_validation as cv
from esphomeyaml.components import binary_sensor from esphome.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_SETUP_MODE, CONF_IIR_FILTER, \ from esphome.const import CONF_HIGH_VOLTAGE_REFERENCE, CONF_ID, CONF_IIR_FILTER, \
CONF_SLEEP_DURATION, CONF_MEASUREMENT_DURATION, CONF_LOW_VOLTAGE_REFERENCE, \ CONF_LOW_VOLTAGE_REFERENCE, CONF_MEASUREMENT_DURATION, CONF_SETUP_MODE, CONF_SLEEP_DURATION, \
CONF_HIGH_VOLTAGE_REFERENCE, CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32 CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
from esphomeyaml.core import TimePeriod from esphome.core import TimePeriod
from esphomeyaml.helpers import App, Pvariable, add, global_ns, setup_component, Component from esphome.cpp_generator import Pvariable, add
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component, global_ns
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
@@ -19,6 +21,7 @@ def validate_voltage(values):
if not value.endswith('V'): if not value.endswith('V'):
value += 'V' value += 'V'
return cv.one_of(*values)(value) return cv.one_of(*values)(value)
return validator return validator

View File

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

View File

@@ -1,19 +1,22 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml.automation import maybe_simple_id, ACTION_REGISTRY from esphome.automation import ACTION_REGISTRY, maybe_simple_id
from esphomeyaml.components import mqtt from esphome.components import mqtt
import esphomeyaml.config_validation as cv from esphome.components.mqtt import setup_mqtt_component
from esphomeyaml.const import CONF_ID, CONF_MQTT_ID, CONF_OSCILLATION_COMMAND_TOPIC, \ import esphome.config_validation as cv
CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_INTERNAL, \ from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_NAME, CONF_OSCILLATING, \
CONF_SPEED, CONF_OSCILLATING, CONF_OSCILLATION_OUTPUT, CONF_NAME CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_OUTPUT, CONF_OSCILLATION_STATE_TOPIC, \
from esphomeyaml.helpers import Application, Pvariable, add, esphomelib_ns, setup_mqtt_component, \ CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC
TemplateArguments, get_variable, templatable, bool_, Action, Nameable, Component from esphome.core import CORE
from esphome.cpp_generator import Pvariable, add, get_variable, templatable
from esphome.cpp_types import Action, Application, Component, Nameable, bool_, esphome_ns
from esphome.py_compat import string_types
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
}) })
fan_ns = esphomelib_ns.namespace('fan') fan_ns = esphome_ns.namespace('fan')
FanState = fan_ns.class_('FanState', Nameable, Component) FanState = fan_ns.class_('FanState', Nameable, Component)
MQTTFanComponent = fan_ns.class_('MQTTFanComponent', mqtt.MQTTComponent) MQTTFanComponent = fan_ns.class_('MQTTFanComponent', mqtt.MQTTComponent)
MakeFan = Application.struct('MakeFan') MakeFan = Application.struct('MakeFan')
@@ -32,13 +35,14 @@ FAN_SPEED_HIGH = FanSpeed.FAN_SPEED_HIGH
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(FanState), cv.GenerateID(): cv.declare_variable_id(FanState),
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTFanComponent), cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTFanComponent),
vol.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.publish_topic, vol.Optional(CONF_OSCILLATION_STATE_TOPIC): vol.All(cv.requires_component('mqtt'),
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.subscribe_topic, cv.publish_topic),
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): vol.All(cv.requires_component('mqtt'),
cv.subscribe_topic),
}) })
FAN_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FAN_SCHEMA.schema) FAN_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FAN_SCHEMA.schema)
FAN_SPEEDS = { FAN_SPEEDS = {
'OFF': FAN_SPEED_OFF, 'OFF': FAN_SPEED_OFF,
'LOW': FAN_SPEED_LOW, 'LOW': FAN_SPEED_LOW,
@@ -47,34 +51,29 @@ FAN_SPEEDS = {
} }
def validate_fan_speed(value): def setup_fan_core_(fan_var, config):
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: if CONF_INTERNAL in config:
add(fan_var.set_internal(config[CONF_INTERNAL])) add(fan_var.set_internal(config[CONF_INTERNAL]))
mqtt_ = fan_var.Pget_mqtt()
if CONF_OSCILLATION_STATE_TOPIC in config: if CONF_OSCILLATION_STATE_TOPIC in config:
add(mqtt_var.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC])) add(mqtt_.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
if CONF_OSCILLATION_COMMAND_TOPIC in config: if CONF_OSCILLATION_COMMAND_TOPIC in config:
add(mqtt_var.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC])) add(mqtt_.set_custom_oscillation_command_topic(config[CONF_OSCILLATION_COMMAND_TOPIC]))
if CONF_SPEED_STATE_TOPIC in config: if CONF_SPEED_STATE_TOPIC in config:
add(mqtt_var.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC])) add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
if CONF_SPEED_COMMAND_TOPIC in config: if CONF_SPEED_COMMAND_TOPIC in config:
add(mqtt_var.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC])) add(mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
setup_mqtt_component(mqtt_var, config) setup_mqtt_component(mqtt_, config)
def setup_fan(fan_obj, mqtt_obj, config): def setup_fan(fan_obj, config):
fan_var = Pvariable(config[CONF_ID], fan_obj, has_side_effects=False) 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) CORE.add_job(setup_fan_core_, fan_var, config)
setup_fan_core_(fan_var, mqtt_var, config)
BUILD_FLAGS = '-DUSE_FAN' BUILD_FLAGS = '-DUSE_FAN'
CONF_FAN_TOGGLE = 'fan.toggle' CONF_FAN_TOGGLE = 'fan.toggle'
FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({ FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(FanState), vol.Required(CONF_ID): cv.use_variable_id(FanState),
@@ -82,8 +81,7 @@ FAN_TOGGLE_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_FAN_TOGGLE, FAN_TOGGLE_ACTION_SCHEMA)
def fan_toggle_to_code(config, action_id, arg_type): def fan_toggle_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_toggle_action(template_arg) rhs = var.make_toggle_action(template_arg)
@@ -98,8 +96,7 @@ FAN_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({
@ACTION_REGISTRY.register(CONF_FAN_TURN_OFF, FAN_TURN_OFF_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_FAN_TURN_OFF, FAN_TURN_OFF_ACTION_SCHEMA)
def fan_turn_off_to_code(config, action_id, arg_type): def fan_turn_off_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_turn_off_action(template_arg) rhs = var.make_turn_off_action(template_arg)
@@ -111,13 +108,12 @@ CONF_FAN_TURN_ON = 'fan.turn_on'
FAN_TURN_ON_ACTION_SCHEMA = maybe_simple_id({ FAN_TURN_ON_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(FanState), vol.Required(CONF_ID): cv.use_variable_id(FanState),
vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), vol.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
vol.Optional(CONF_SPEED): cv.templatable(validate_fan_speed), vol.Optional(CONF_SPEED): cv.templatable(cv.one_of(*FAN_SPEEDS, upper=True)),
}) })
@ACTION_REGISTRY.register(CONF_FAN_TURN_ON, FAN_TURN_ON_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_FAN_TURN_ON, FAN_TURN_ON_ACTION_SCHEMA)
def fan_turn_on_to_code(config, action_id, arg_type): def fan_turn_on_to_code(config, action_id, arg_type, template_arg):
template_arg = TemplateArguments(arg_type)
for var in get_variable(config[CONF_ID]): for var in get_variable(config[CONF_ID]):
yield None yield None
rhs = var.make_turn_on_action(template_arg) rhs = var.make_turn_on_action(template_arg)
@@ -130,6 +126,8 @@ def fan_turn_on_to_code(config, action_id, arg_type):
if CONF_SPEED in config: if CONF_SPEED in config:
for template_ in templatable(config[CONF_SPEED], arg_type, FanSpeed): for template_ in templatable(config[CONF_SPEED], arg_type, FanSpeed):
yield None yield None
if isinstance(template_, string_types):
template_ = FAN_SPEEDS[template_]
add(action.set_speed(template_)) add(action.set_speed(template_))
yield action yield action

View File

@@ -1,9 +1,11 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import fan, output
from esphomeyaml.components import fan, output import esphome.config_validation as cv
from esphomeyaml.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT from esphome.const import CONF_MAKE_ID, CONF_NAME, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
from esphomeyaml.helpers import App, add, get_variable, variable, setup_component from esphome.cpp_generator import add, get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
@@ -24,7 +26,7 @@ def to_code(config):
yield yield
add(fan_struct.Poutput.set_oscillation(oscillation_output)) add(fan_struct.Poutput.set_oscillation(oscillation_output))
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config) fan.setup_fan(fan_struct.Pstate, config)
setup_component(fan_struct.Poutput, config) setup_component(fan_struct.Poutput, config)

View File

@@ -1,11 +1,12 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import fan, mqtt, output
from esphomeyaml.components import fan, mqtt, output import esphome.config_validation as cv
from esphomeyaml.const import CONF_HIGH, CONF_LOW, CONF_MAKE_ID, CONF_MEDIUM, CONF_NAME, \ from esphome.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_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_SPEED, CONF_SPEED_COMMAND_TOPIC, \
CONF_SPEED_STATE_TOPIC CONF_SPEED_STATE_TOPIC
from esphomeyaml.helpers import App, add, get_variable, variable from esphome.cpp_generator import add, get_variable, variable
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(fan.MakeFan),
@@ -40,7 +41,7 @@ def to_code(config):
yield yield
add(fan_struct.Poutput.set_oscillation(oscillation_output)) add(fan_struct.Poutput.set_oscillation(oscillation_output))
fan.setup_fan(fan_struct.Pstate, fan_struct.Pmqtt, config) fan.setup_fan(fan_struct.Pstate, config)
def to_hass_config(data, config): def to_hass_config(data, config):

119
esphome/components/font.py Normal file
View File

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

View File

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

View File

@@ -1,18 +1,20 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome import pins
from esphomeyaml import pins import esphome.config_validation as cv
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID, \ from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_RECEIVE_TIMEOUT, CONF_SCAN, CONF_SCL, \
CONF_RECEIVE_TIMEOUT CONF_SDA
from esphomeyaml.helpers import App, add, Pvariable, esphomelib_ns, setup_component, Component from esphome.cpp_generator import Pvariable, add
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component, esphome_ns
I2CComponent = esphomelib_ns.class_('I2CComponent', Component) I2CComponent = esphome_ns.class_('I2CComponent', Component)
I2CDevice = pins.I2CDevice I2CDevice = pins.I2CDevice
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(I2CComponent), cv.GenerateID(): cv.declare_variable_id(I2CComponent),
vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin, vol.Optional(CONF_SDA, default='SDA'): pins.input_pullup_pin,
vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin, vol.Optional(CONF_SCL, default='SCL'): pins.input_pullup_pin,
vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=0, min_included=False)), vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=0, min_included=False)),
vol.Optional(CONF_SCAN): cv.boolean, vol.Optional(CONF_SCAN): cv.boolean,

View File

@@ -0,0 +1,63 @@
# coding=utf-8
import logging
import voluptuous as vol
from esphome import core
from esphome.components import display, font
import esphome.config_validation as cv
from esphome.const import CONF_FILE, CONF_ID, CONF_RESIZE
from esphome.core import CORE, HexInt
from esphome.cpp_generator import Pvariable, progmem_array, safe_exp
from esphome.cpp_types import App, uint8
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['display']
MULTI_CONF = True
Image_ = display.display_ns.class_('Image')
CONF_RAW_DATA_ID = 'raw_data_id'
IMAGE_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.declare_variable_id(Image_),
vol.Required(CONF_FILE): cv.file_,
vol.Optional(CONF_RESIZE): cv.dimensions,
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(uint8),
})
CONFIG_SCHEMA = vol.All(font.validate_pillow_installed, IMAGE_SCHEMA)
def to_code(config):
from PIL import Image
path = CORE.relative_path(config[CONF_FILE])
try:
image = Image.open(path)
except Exception as e:
raise core.EsphomeError(u"Could not load image file {}: {}".format(path, e))
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
image = image.convert('1', dither=Image.NONE)
width, height = image.size
if width > 500 or height > 500:
_LOGGER.warning("The image you requested is very big. Please consider using the resize "
"parameter")
width8 = ((width + 7) // 8) * 8
data = [0 for _ in range(height * width8 // 8)]
for y in range(height):
for x in range(width):
if image.getpixel((x, y)):
continue
pos = x + y * width8
data[pos // 8] |= 0x80 >> (pos % 8)
rhs = safe_exp([HexInt(x) for x in data])
prog_arr = progmem_array(config[CONF_RAW_DATA_ID], rhs)
rhs = App.make_image(prog_arr, width, height)
Pvariable(config[CONF_ID], rhs)

View File

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

View File

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

View File

@@ -1,9 +1,11 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml.components import light, output from esphome.components import light, output
import esphomeyaml.config_validation as cv import esphome.config_validation as cv
from esphomeyaml.const import CONF_EFFECTS, CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT from esphome.const import CONF_EFFECTS, CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, setup_component, variable from esphome.cpp_generator import get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
@@ -17,7 +19,7 @@ def to_code(config):
yield yield
rhs = App.make_binary_light(config[CONF_NAME], output_) rhs = App.make_binary_light(config[CONF_NAME], output_)
light_struct = variable(config[CONF_MAKE_ID], rhs) light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) light.setup_light(light_struct.Pstate, config)
setup_component(light_struct.Pstate, config) setup_component(light_struct.Pstate, config)

View File

@@ -1,13 +1,15 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import light, output
from esphomeyaml.components import light, output from esphome.components.light.rgbww import validate_cold_white_colder, \
from esphomeyaml.components.light.rgbww import validate_cold_white_colder, \
validate_color_temperature validate_color_temperature
from esphomeyaml.const import CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \ import esphome.config_validation as cv
from esphome.const import CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
CONF_NAME, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE CONF_NAME, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
from esphomeyaml.helpers import App, get_variable, variable, setup_component from esphome.cpp_generator import get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
@@ -31,7 +33,7 @@ def to_code(config):
config[CONF_WARM_WHITE_COLOR_TEMPERATURE], config[CONF_WARM_WHITE_COLOR_TEMPERATURE],
cold_white, warm_white) cold_white, warm_white)
light_struct = variable(config[CONF_MAKE_ID], rhs) light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) light.setup_light(light_struct.Pstate, config)
setup_component(light_struct.Pstate, config) setup_component(light_struct.Pstate, config)

View File

@@ -1,14 +1,15 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome import pins
from esphomeyaml import pins from esphome.components import light
from esphomeyaml.components import light from esphome.components.power_supply import PowerSupplyComponent
from esphomeyaml.components.power_supply import PowerSupplyComponent import esphome.config_validation as cv
from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ from esphome.const import CONF_CHIPSET, CONF_COLOR_CORRECT, CONF_DEFAULT_TRANSITION_LENGTH, \
CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, \ CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, \
CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT CONF_NUM_LEDS, CONF_PIN, CONF_POWER_SUPPLY, CONF_RGB_ORDER
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \ from esphome.cpp_generator import RawExpression, TemplateArguments, add, get_variable, variable
get_variable, variable, setup_component from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Application
TYPES = [ TYPES = [
'NEOPIXEL', 'NEOPIXEL',
@@ -56,20 +57,21 @@ def validate(value):
MakeFastLEDLight = Application.struct('MakeFastLEDLight') MakeFastLEDLight = Application.struct('MakeFastLEDLight')
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(light.AddressableLightState),
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight),
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*TYPES)), vol.Required(CONF_CHIPSET): cv.one_of(*TYPES, upper=True),
vol.Required(CONF_PIN): pins.output_pin, vol.Required(CONF_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int, vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)), vol.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_COLOR_CORRECT): vol.All([cv.percentage], vol.Length(min=3, max=3)), 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_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS), vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
}).extend(cv.COMPONENT_SCHEMA.schema), validate) }).extend(cv.COMPONENT_SCHEMA.schema), validate)
@@ -97,11 +99,13 @@ def to_code(config):
r, g, b = config[CONF_COLOR_CORRECT] r, g, b = config[CONF_COLOR_CORRECT]
add(fast_led.set_correction(r, g, b)) add(fast_led.set_correction(r, g, b))
light.setup_light(make.Pstate, make.Pmqtt, config) light.setup_light(make.Pstate, config)
setup_component(fast_led, config) setup_component(fast_led, config)
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' REQUIRED_BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
LIB_DEPS = 'FastLED@3.2.0'
def to_hass_config(data, config): def to_hass_config(data, config):

View File

@@ -1,14 +1,15 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome import pins
from esphomeyaml import pins from esphome.components import light
from esphomeyaml.components import light from esphome.components.power_supply import PowerSupplyComponent
from esphomeyaml.components.power_supply import PowerSupplyComponent import esphome.config_validation as cv
from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \ from esphome.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_COLOR_CORRECT, CONF_DATA_PIN, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_MAKE_ID, CONF_MAX_REFRESH_RATE, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_MAKE_ID, \
CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER, CONF_EFFECTS, CONF_COLOR_CORRECT CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_POWER_SUPPLY, CONF_RGB_ORDER
from esphomeyaml.helpers import App, Application, RawExpression, TemplateArguments, add, \ from esphome.cpp_generator import RawExpression, TemplateArguments, add, get_variable, variable
get_variable, variable, setup_component from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Application
CHIPSETS = [ CHIPSETS = [
'LPD8806', 'LPD8806',
@@ -33,21 +34,22 @@ RGB_ORDERS = [
MakeFastLEDLight = Application.struct('MakeFastLEDLight') MakeFastLEDLight = Application.struct('MakeFastLEDLight')
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(light.AddressableLightState),
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeFastLEDLight),
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, cv.one_of(*CHIPSETS)), vol.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
vol.Required(CONF_DATA_PIN): pins.output_pin, vol.Required(CONF_DATA_PIN): pins.output_pin,
vol.Required(CONF_CLOCK_PIN): pins.output_pin, vol.Required(CONF_CLOCK_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int, vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, cv.one_of(*RGB_ORDERS)), vol.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, 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_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_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
vol.Optional(CONF_EFFECTS): light.validate_effects(light.FASTLED_EFFECTS), vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
}).extend(cv.COMPONENT_SCHEMA.schema)) }).extend(cv.COMPONENT_SCHEMA.schema))
@@ -77,11 +79,13 @@ def to_code(config):
r, g, b = config[CONF_COLOR_CORRECT] r, g, b = config[CONF_COLOR_CORRECT]
add(fast_led.set_correction(r, g, b)) add(fast_led.set_correction(r, g, b))
light.setup_light(make.Pstate, make.Pmqtt, config) light.setup_light(make.Pstate, config)
setup_component(fast_led, config) setup_component(fast_led, config)
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT' REQUIRED_BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'
LIB_DEPS = 'FastLED@3.2.0'
def to_hass_config(data, config): def to_hass_config(data, config):

View File

@@ -1,10 +1,12 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml.components import light, output from esphome.components import light, output
import esphomeyaml.config_validation as cv import esphome.config_validation as cv
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, \ from esphome.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, \
CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT CONF_MAKE_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, setup_component, variable from esphome.cpp_generator import get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
@@ -20,7 +22,7 @@ def to_code(config):
yield yield
rhs = App.make_monochromatic_light(config[CONF_NAME], output_) rhs = App.make_monochromatic_light(config[CONF_NAME], output_)
light_struct = variable(config[CONF_MAKE_ID], rhs) light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) light.setup_light(light_struct.Pstate, config)
setup_component(light_struct.Pstate, config) setup_component(light_struct.Pstate, config)

View File

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

View File

@@ -0,0 +1,52 @@
import voluptuous as vol
from esphome.components import light
from esphome.components.light import AddressableLight
import esphome.config_validation as cv
from esphome.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_FROM, CONF_ID, \
CONF_MAKE_ID, CONF_NAME, CONF_SEGMENTS, CONF_TO
from esphome.cpp_generator import get_variable, variable
from esphome.cpp_types import App, Application
AddressableSegment = light.light_ns.class_('AddressableSegment')
PartitionLightOutput = light.light_ns.class_('PartitionLightOutput', AddressableLight)
MakePartitionLight = Application.struct('MakePartitionLight')
def validate_from_to(value):
if value[CONF_FROM] > value[CONF_TO]:
raise vol.Invalid(u"From ({}) must not be larger than to ({})"
u"".format(value[CONF_FROM], value[CONF_TO]))
return value
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(light.AddressableLightState),
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakePartitionLight),
vol.Required(CONF_SEGMENTS): vol.All(cv.ensure_list({
vol.Required(CONF_ID): cv.use_variable_id(light.AddressableLightState),
vol.Required(CONF_FROM): cv.positive_int,
vol.Required(CONF_TO): cv.positive_int,
}, validate_from_to), vol.Length(min=1)),
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
vol.Optional(CONF_EFFECTS): light.validate_effects(light.ADDRESSABLE_EFFECTS),
}))
def to_code(config):
segments = []
for conf in config[CONF_SEGMENTS]:
for var in get_variable(conf[CONF_ID]):
yield
segments.append(AddressableSegment(var, conf[CONF_FROM],
conf[CONF_TO] - conf[CONF_FROM] + 1))
rhs = App.make_partition_light(config[CONF_NAME], segments)
make = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(make.Pstate, config)
def to_hass_config(data, config):
return light.core_to_hass_config(data, config, brightness=True, rgb=True, color_temp=False)

View File

@@ -1,10 +1,12 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import light, output
from esphomeyaml.components import light, output import esphome.config_validation as cv
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ from esphome.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, \
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_EFFECTS CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED
from esphomeyaml.helpers import App, get_variable, variable, setup_component from esphome.cpp_generator import get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
@@ -26,7 +28,7 @@ def to_code(config):
yield yield
rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue) rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue)
light_struct = variable(config[CONF_MAKE_ID], rhs) light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) light.setup_light(light_struct.Pstate, config)
setup_component(light_struct.Pstate, config) setup_component(light_struct.Pstate, config)

View File

@@ -1,10 +1,12 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import light, output
from esphomeyaml.components import light, output import esphome.config_validation as cv
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ from esphome.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, \
CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE, CONF_EFFECTS CONF_GAMMA_CORRECT, CONF_GREEN, CONF_MAKE_ID, CONF_NAME, CONF_RED, CONF_WHITE
from esphomeyaml.helpers import App, get_variable, variable, setup_component from esphome.cpp_generator import get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(light.LIGHT_PLATFORM_SCHEMA.extend({
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight), cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(light.MakeLight),
@@ -29,7 +31,7 @@ def to_code(config):
yield yield
rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white) rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white)
light_struct = variable(config[CONF_MAKE_ID], rhs) light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) light.setup_light(light_struct.Pstate, config)
setup_component(light_struct.Pstate, config) setup_component(light_struct.Pstate, config)

View File

@@ -1,11 +1,13 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import light, output
from esphomeyaml.components import light, output import esphome.config_validation as cv
from esphomeyaml.const import CONF_BLUE, CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \ from esphome.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_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 CONF_NAME, CONF_RED, CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE
from esphomeyaml.helpers import App, get_variable, variable, setup_component from esphome.cpp_generator import get_variable, variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App
def validate_color_temperature(value): def validate_color_temperature(value):
@@ -59,7 +61,7 @@ def to_code(config):
config[CONF_WARM_WHITE_COLOR_TEMPERATURE], config[CONF_WARM_WHITE_COLOR_TEMPERATURE],
red, green, blue, cold_white, warm_white) red, green, blue, cold_white, warm_white)
light_struct = variable(config[CONF_MAKE_ID], rhs) light_struct = variable(config[CONF_MAKE_ID], rhs)
light.setup_light(light_struct.Pstate, light_struct.Pmqtt, config) light.setup_light(light_struct.Pstate, config)
setup_component(light_struct.Pstate, config) setup_component(light_struct.Pstate, config)

View File

@@ -0,0 +1,180 @@
import re
import voluptuous as vol
from esphome.automation import ACTION_REGISTRY, LambdaAction
import esphome.config_validation as cv
from esphome.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_HARDWARE_UART, CONF_ID, \
CONF_LEVEL, CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE
from esphome.core import CORE, EsphomeError, Lambda
from esphome.cpp_generator import Pvariable, RawExpression, add, process_lambda, statement
from esphome.cpp_types import App, Component, esphome_ns, global_ns, void
from esphome.py_compat import text_type
LOG_LEVELS = {
'NONE': global_ns.ESPHOME_LOG_LEVEL_NONE,
'ERROR': global_ns.ESPHOME_LOG_LEVEL_ERROR,
'WARN': global_ns.ESPHOME_LOG_LEVEL_WARN,
'INFO': global_ns.ESPHOME_LOG_LEVEL_INFO,
'DEBUG': global_ns.ESPHOME_LOG_LEVEL_DEBUG,
'VERBOSE': global_ns.ESPHOME_LOG_LEVEL_VERBOSE,
'VERY_VERBOSE': global_ns.ESPHOME_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']
UART_SELECTION_ESP32 = ['UART0', 'UART1', 'UART2']
UART_SELECTION_ESP8266 = ['UART0', 'UART0_SWAP', 'UART1']
HARDWARE_UART_TO_UART_SELECTION = {
'UART0': global_ns.UART_SELECTION_UART0,
'UART0_SWAP': global_ns.UART_SELECTION_UART0_SWAP,
'UART1': global_ns.UART_SELECTION_UART1,
'UART2': global_ns.UART_SELECTION_UART2,
}
HARDWARE_UART_TO_SERIAL = {
'UART0': 'Serial',
'UART0_SWAP': 'Serial',
'UART1': 'Serial1',
'UART2': 'Serial2',
}
# pylint: disable=invalid-name
is_log_level = cv.one_of(*LOG_LEVELS, upper=True)
def uart_selection(value):
if CORE.is_esp32:
return cv.one_of(*UART_SELECTION_ESP32, upper=True)(value)
if CORE.is_esp8266:
return cv.one_of(*UART_SELECTION_ESP8266, upper=True)(value)
raise NotImplementedError
def validate_local_no_higher_than_global(value):
global_level = value.get(CONF_LEVEL, 'DEBUG')
for tag, level in value.get(CONF_LOGS, {}).items():
if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
raise EsphomeError(u"The local log level {} for {} must be less severe than the "
u"global log level {}.".format(level, tag, global_level))
return value
LogComponent = esphome_ns.class_('LogComponent', Component)
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, default=512): cv.validate_bytes,
vol.Optional(CONF_HARDWARE_UART, default='UART0'): uart_selection,
vol.Optional(CONF_LEVEL): is_log_level,
vol.Optional(CONF_LOGS): vol.Schema({
cv.string: is_log_level,
})
}), validate_local_no_higher_than_global)
def to_code(config):
rhs = App.init_log(config.get(CONF_BAUD_RATE),
config.get(CONF_TX_BUFFER_SIZE),
HARDWARE_UART_TO_UART_SELECTION[config.get(CONF_HARDWARE_UART)])
log = Pvariable(config[CONF_ID], rhs)
if CONF_LEVEL in config:
add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]]))
for tag, level in config.get(CONF_LOGS, {}).items():
add(log.set_log_level(tag, LOG_LEVELS[level]))
def required_build_flags(config):
flags = []
if CONF_LEVEL in config:
flags.append(u'-DESPHOME_LOG_LEVEL={}'.format(str(LOG_LEVELS[config[CONF_LEVEL]])))
this_severity = LOG_LEVEL_SEVERITY.index(config[CONF_LEVEL])
verbose_severity = LOG_LEVEL_SEVERITY.index('VERBOSE')
is_at_least_verbose = this_severity >= verbose_severity
has_serial_logging = config.get(CONF_BAUD_RATE) != 0
if CORE.is_esp8266 and has_serial_logging and is_at_least_verbose:
debug_serial_port = HARDWARE_UART_TO_SERIAL[config.get(CONF_HARDWARE_UART)]
flags.append(u"-DDEBUG_ESP_PORT={}".format(debug_serial_port))
flags.append(u"-DLWIP_DEBUG")
DEBUG_COMPONENTS = {
'HTTP_CLIENT',
'HTTP_SERVER',
'HTTP_UPDATE',
'OTA',
'SSL',
'TLS_MEM',
'UPDATER',
'WIFI',
}
for comp in DEBUG_COMPONENTS:
flags.append(u"-DDEBUG_ESP_{}".format(comp))
if CORE.is_esp32 and is_at_least_verbose:
flags.append('-DCORE_DEBUG_LEVEL=5')
return flags
def maybe_simple_message(schema):
def validator(value):
if isinstance(value, dict):
return vol.Schema(schema)(value)
return vol.Schema(schema)({CONF_FORMAT: value})
return validator
def validate_printf(value):
# https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
# pylint: disable=anomalous-backslash-in-string
cfmt = u"""\
( # start of capture group 1
% # literal "%"
(?: # first option
(?:[-+0 #]{0,5}) # optional flags
(?:\d+|\*)? # width
(?:\.(?:\d+|\*))? # precision
(?:h|l|ll|w|I|I32|I64)? # size
[cCdiouxXeEfgGaAnpsSZ] # type
) | # OR
%%) # literal "%%"
""" # noqa
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X)
if len(matches) != len(value[CONF_ARGS]):
raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!"
u"".format(len(matches), u', '.join(matches), len(value[CONF_ARGS])))
return value
CONF_LOGGER_LOG = 'logger.log'
LOGGER_LOG_ACTION_SCHEMA = vol.All(maybe_simple_message({
vol.Required(CONF_FORMAT): cv.string,
vol.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
vol.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(*LOG_LEVEL_TO_ESP_LOG, upper=True),
vol.Optional(CONF_TAG, default="main"): cv.string,
}), validate_printf)
@ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA)
def logger_log_action_to_code(config, action_id, arg_type, template_arg):
esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]]
args = [RawExpression(text_type(x)) for x in config[CONF_ARGS]]
text = text_type(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')], return_type=void):
yield None
rhs = LambdaAction.new(template_arg, lambda_)
type = LambdaAction.template(template_arg)
yield Pvariable(action_id, rhs, type=type)

View File

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

View File

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

View File

@@ -2,16 +2,15 @@ import logging
import voluptuous as vol import voluptuous as vol
from esphomeyaml import core import esphome.config_validation as cv
import esphomeyaml.config_validation as cv from esphome.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \ from esphome.core import CORE
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 from esphome.cpp_generator import Pvariable, add
from esphomeyaml.core import ESPHomeYAMLError from esphome.cpp_types import App, Component, esphome_ns
from esphomeyaml.helpers import App, Pvariable, add, esphomelib_ns, Component
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
OTAComponent = esphomelib_ns.class_('OTAComponent', Component) OTAComponent = esphome_ns.class_('OTAComponent', Component)
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(): cv.declare_variable_id(OTAComponent), cv.GenerateID(): cv.declare_variable_id(OTAComponent),
@@ -35,11 +34,11 @@ def to_code(config):
def get_port(config): def get_port(config):
if CONF_PORT in config[CONF_OTA]: if CONF_PORT in config[CONF_OTA]:
return config[CONF_OTA][CONF_PORT] return config[CONF_OTA][CONF_PORT]
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: if CORE.is_esp32:
return 3232 return 3232
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266: if CORE.is_esp8266:
return 8266 return 8266
raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.") raise NotImplementedError
def get_auth(config): def get_auth(config):
@@ -51,6 +50,8 @@ REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA'
def lib_deps(config): def lib_deps(config):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: if CORE.is_esp32:
return ['ArduinoOTA', 'Update', 'ESPmDNS'] return ['Update', 'ESPmDNS']
return ['Hash', 'ESP8266mDNS', 'ArduinoOTA'] if CORE.is_esp8266:
return ['Hash', 'ESP8266mDNS']
raise NotImplementedError

View File

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

View File

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

View File

@@ -1,18 +1,19 @@
import voluptuous as vol import voluptuous as vol
from esphomeyaml import pins from esphome import pins
from esphomeyaml.components import output from esphome.components import output
import esphomeyaml.config_validation as cv import esphome.config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266, CONF_FREQUENCY from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import App, Component, Pvariable, gpio_output_pin_expression, \ from esphome.cpp_generator import Pvariable, add
setup_component, add from esphome.cpp_helpers import gpio_output_pin_expression, setup_component
from esphome.cpp_types import App, Component
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
def valid_pwm_pin(value): def valid_pwm_pin(value):
num = value[CONF_NUMBER] num = value[CONF_NUMBER]
cv.one_of(0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16)(num) cv.one_of(0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15, 16)(num)
return value return value

View File

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

View File

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

View File

@@ -1,10 +1,11 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv from esphome.components import output
from esphomeyaml.components import output from esphome.components.my9231 import MY9231OutputComponent
from esphomeyaml.components.my9231 import MY9231OutputComponent import esphome.config_validation as cv
from esphomeyaml.const import CONF_CHANNEL, CONF_ID, CONF_MY9231_ID, CONF_POWER_SUPPLY from esphome.const import CONF_CHANNEL, CONF_ID, CONF_MY9231_ID, CONF_POWER_SUPPLY
from esphomeyaml.helpers import Pvariable, get_variable, setup_component from esphome.cpp_generator import Pvariable, get_variable
from esphome.cpp_helpers import setup_component
DEPENDENCIES = ['my9231'] DEPENDENCIES = ['my9231']

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