1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-07 02:21:51 +00:00

Compare commits

..

19 Commits

Author SHA1 Message Date
Otto Winter
dc516f7537 Fixes 2018-05-14 21:13:51 +02:00
Otto Winter
44f2b582b5 FastLED fixes 2018-05-14 17:34:43 +02:00
Otto Winter
262855ff62 Preparations for 1.5.0 2018-05-14 11:50:56 +02:00
Otto Winter
eec163644d Bump version to 1.4.0 2018-05-06 18:23:23 +02:00
Otto Winter
e65a4d50e5 Fix time config validation 2018-05-06 17:40:37 +02:00
Otto Winter
a88aad2179 Fix generic output 2018-05-06 17:40:27 +02:00
Otto Winter
49736c8c6d Update for 1.4.0 2018-05-06 15:56:12 +02:00
Otto Winter
7915e420f4 Fix Wemos D1 Mini Pin Numbering (fixes #9) 2018-04-24 21:52:59 +02:00
Otto Winter
595aa5e92d Bump version to 1.3.0 2018-04-18 19:33:53 +02:00
Otto Winter
ef1aa16627 Fix build 2018-04-18 18:44:37 +02:00
Otto Winter
3540b3fbb0 Web server (#7)
* Web Server

* Preparations for 1.3

* Fixes

* Fix Lint
2018-04-18 18:43:13 +02:00
Jimmy Hedman
ac5ab33975 Cleaned some low hanging pyling warnings. (#6) 2018-04-12 12:56:03 +02:00
Jimmy Hedman
633d20d023 Remove sleeps. (#5)
* Remove sleeps.

- the sleeps is not needed.

* Make sleep depending on environment variable.

- set QUICKWIZARD to true to disable sleeps.

* Changed env-name and made it work without env.

- It only worked when environment variable was defined. Now it works
  with variable unset, which should be the normal case.
- Added ESPHOMEYAML_ as prefix so it's ESPHOMEYAML_QUICKWIZARD.
2018-04-11 22:51:56 +02:00
Otto Winter
9e5548324b Fix lint error 2018-04-11 18:29:21 +02:00
Otto Winter
c31c5c4041 Bump version to 1.2.2 2018-04-10 20:43:19 +02:00
Otto Winter
34605f19ee Secret and Include directives in confg (#4) 2018-04-10 20:18:02 +02:00
Otto Winter
58e1b8454d Handle multiple serial ports better 2018-04-10 20:17:20 +02:00
Otto Winter
0ab63dc4d4 Enable Travis Linting (#3)
* Flake8 Travis Job

* Fix flake8 warnings

* Fix pylint errors

* Fix travis file
2018-04-10 17:17:46 +02:00
Jimmy Hedman
51c856e65e It now complies with flake8 --ignore=E501,W291 (#1)
- Not changning long lines or lines ending with space.
2018-04-10 16:21:32 +02:00
74 changed files with 2540 additions and 699 deletions

10
.travis.yml Normal file
View File

@@ -0,0 +1,10 @@
sudo: false
language: python
python:
- "2.7"
install:
- pip install -r requirements.txt
- pip install flake8==3.5.0 pylint==1.8.4
script:
- flake8 esphomeyaml
- pylint esphomeyaml

View File

@@ -11,13 +11,8 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY docker/platformio.ini /usr/src/app/ COPY docker/platformio.ini /usr/src/app/
RUN platformio settings set enable_telemetry No && \ RUN platformio settings set enable_telemetry No && \
platformio lib --global install esphomelib=https://github.com/OttoWinter/esphomelib.git#v1.2.1 && \
platformio run -e espressif32 -e espressif8266; exit 0 platformio run -e espressif32 -e espressif8266; exit 0
# Fix issue with static IP on ESP32: https://github.com/espressif/arduino-esp32/issues/1081
RUN curl https://github.com/espressif/arduino-esp32/commit/144480637a718844b8f48f4392da8d4f622f2e5e.patch | \
patch /root/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiGeneric.cpp
COPY . . COPY . .
RUN pip install -e . RUN pip install -e .

View File

@@ -6,19 +6,16 @@ import os
import random import random
import sys import sys
from esphomeyaml import helpers, mqtt, writer, yaml_util, wizard from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const
from esphomeyaml.config import add_component_task, read_config from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_MANUAL_IP, CONF_NAME, \ from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOGGER, \
CONF_STATIC_IP, \ CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI
CONF_WIFI, CONF_LOGGER, CONF_BAUD_RATE from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, \ color, get_variable, indent, quote, statement, Expression
get_variable, indent, quote, statement
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'i2c'] PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c']
CONFIG_PATH = None
def get_name(config): def get_name(config):
@@ -26,7 +23,7 @@ def get_name(config):
def get_base_path(config): def get_base_path(config):
return os.path.join(os.path.dirname(CONFIG_PATH), get_name(config)) return os.path.join(os.path.dirname(core.CONFIG_PATH), get_name(config))
def discover_serial_ports(): def discover_serial_ports():
@@ -36,21 +33,42 @@ def discover_serial_ports():
except ImportError: except ImportError:
return None return None
result = None result = []
for p, d, h in comports(): descs = []
if not p: for port, desc, info in comports():
if not port:
continue continue
if "VID:PID" in h: if "VID:PID" in info:
if result is not None: result.append(port)
return None descs.append(desc)
result = p
return result if not result:
return None
print(u"Found multiple serial port options, please choose one:")
for i, (res, desc) in enumerate(zip(result, descs)):
print(u" [{}] {} ({})".format(i, res, desc))
print(u" [{}] Over The Air".format(len(result)))
print()
while True:
opt = raw_input('(number): ')
if opt in result:
opt = result.index(opt)
break
try:
opt = int(opt)
if opt < 0 or opt > len(result):
raise ValueError
break
except ValueError:
print(color('red', u"Invalid option: '{}'".format(opt)))
if opt == len(result):
return None
return result[opt]
def run_platformio(*cmd): def run_platformio(*cmd):
def mock_exit(rc): def mock_exit(return_code):
raise SystemExit(rc) raise SystemExit(return_code)
orig_argv = sys.argv orig_argv = sys.argv
orig_exit = sys.exit # mock sys.exit orig_exit = sys.exit # mock sys.exit
@@ -63,10 +81,10 @@ def run_platformio(*cmd):
return platformio.__main__.main() return platformio.__main__.main()
except KeyboardInterrupt: except KeyboardInterrupt:
return 1 return 1
except SystemExit as e: except SystemExit as err:
return e.args[0] return err.args[0]
except Exception as e: except Exception as err: # pylint: disable=broad-except
_LOGGER.error(u"Running platformio failed: %s", e) _LOGGER.error(u"Running platformio failed: %s", err)
_LOGGER.error(u"Please try running %s locally.", full_cmd) _LOGGER.error(u"Please try running %s locally.", full_cmd)
finally: finally:
sys.argv = orig_argv sys.argv = orig_argv
@@ -84,18 +102,24 @@ def run_miniterm(config, port):
def write_cpp(config): def write_cpp(config):
_LOGGER.info("Generating C++ source...") _LOGGER.info("Generating C++ source...")
add_task(core_to_code, config[CONF_ESPHOMEYAML])
for domain in PRE_INITIALIZE: for domain in PRE_INITIALIZE:
if domain == CONF_ESPHOMEYAML:
continue
if domain in config: if domain in config:
add_component_task(domain, config[domain]) add_task(get_component(domain).to_code, config[domain])
# Clear queue # Clear queue
get_variable(None) get_variable(None)
add(RawStatement('')) add(RawStatement(''))
for domain, conf in config.iteritems(): for domain, component, conf in iter_components(config):
if domain in PRE_INITIALIZE: if domain in PRE_INITIALIZE:
continue continue
add_component_task(domain, conf) if not hasattr(component, 'to_code'):
continue
add_task(component.to_code, conf)
# Clear queue # Clear queue
get_variable(None) get_variable(None)
@@ -104,15 +128,18 @@ def write_cpp(config):
all_code = [] all_code = []
for exp in _EXPRESSIONS: for exp in _EXPRESSIONS:
if helpers.SIMPLIFY and isinstance(exp, AssignmentExpression) and exp.obj.usages == 0: if core.SIMPLIFY:
exp = exp.rhs if isinstance(exp, Expression) and not exp.required:
continue
if isinstance(exp, AssignmentExpression) and not exp.obj.required:
exp = exp.rhs
all_code.append(unicode(statement(exp))) all_code.append(unicode(statement(exp)))
platformio_ini_s = writer.get_ini_content(config) platformio_ini_s = writer.get_ini_content(config)
ini_path = os.path.join(get_base_path(config), 'platformio.ini') ini_path = os.path.join(get_base_path(config), 'platformio.ini')
writer.write_platformio_ini(platformio_ini_s, ini_path) writer.write_platformio_ini(platformio_ini_s, ini_path)
code_s = indent('\n'.join(all_code)) code_s = indent('\n'.join(line.rstrip() for line in all_code))
cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp') cpp_path = os.path.join(get_base_path(config), 'src', 'main.cpp')
writer.write_cpp(code_s, cpp_path) writer.write_cpp(code_s, cpp_path)
return 0 return 0
@@ -125,19 +152,14 @@ def compile_program(config):
def upload_program(config, args, port): def upload_program(config, args, port):
_LOGGER.info("Uploading binary...") _LOGGER.info("Uploading binary...")
if args.upload_port is not None:
if args.upload_port == 'HELLO':
return run_platformio('platformio', 'run', '-d', get_base_path(config),
'-t', 'upload')
else:
return run_platformio('platformio', 'run', '-d', get_base_path(config),
'-t', 'upload', '--upload-port', args.upload_port)
if port is not None: if port is not None:
_LOGGER.info("Serial device discovered, using it for upload")
return run_platformio('platformio', 'run', '-d', get_base_path(config), return run_platformio('platformio', 'run', '-d', get_base_path(config),
'-t', 'upload', '--upload-port', port) '-t', 'upload', '--upload-port', port)
if 'ota' not in config:
_LOGGER.error("No serial port found and OTA not enabled. Can't upload!")
return -1
if CONF_MANUAL_IP in config[CONF_WIFI]: if CONF_MANUAL_IP in config[CONF_WIFI]:
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP]) host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
elif CONF_HOSTNAME in config[CONF_WIFI]: elif CONF_HOSTNAME in config[CONF_WIFI]:
@@ -160,7 +182,7 @@ def upload_program(config, args, port):
def show_logs(config, args, port): def show_logs(config, args, port):
if port is not None: if port is not None and port != 'OTA':
run_miniterm(config, port) run_miniterm(config, port)
return 0 return 0
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
@@ -197,25 +219,20 @@ def setup_log():
def main(): def main():
global CONFIG_PATH
setup_log() setup_log()
parser = argparse.ArgumentParser(prog='esphomeyaml') parser = argparse.ArgumentParser(prog='esphomeyaml')
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')
subparsers.required = True subparsers.required = True
parser_config = subparsers.add_parser('config', subparsers.add_parser('config', help='Validate the configuration and spit it out.')
help='Validate the configuration and spit it out.')
parser_compile = subparsers.add_parser('compile', subparsers.add_parser('compile', help='Read the configuration and compile a program.')
help='Read the configuration and compile a program.')
parser_upload = subparsers.add_parser('upload', help='Validate the configuration ' parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
'and upload the latest binary.') 'and upload the latest binary.')
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_USBtoUAR.", "For example /dev/cu.SLAB_USBtoUART.")
nargs='?', const='HELLO')
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_logs = subparsers.add_parser('logs', help='Validate the configuration ' parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
@@ -224,12 +241,13 @@ def main():
parser_logs.add_argument('--username', help='Manually set the username.') parser_logs.add_argument('--username', help='Manually set the username.')
parser_logs.add_argument('--password', help='Manually set the password.') parser_logs.add_argument('--password', help='Manually set the password.')
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"
"For example /dev/cu.SLAB_USBtoUART.")
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.')
parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. " parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. "
"For example /dev/cu.SLAB_USBtoUAR.", "For example /dev/cu.SLAB_USBtoUART.")
nargs='?', const='HELLO')
parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int) parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.', parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
action='store_true') action='store_true')
@@ -245,21 +263,26 @@ def main():
parser_clean.add_argument('--password', help='Manually set the password.') parser_clean.add_argument('--password', help='Manually set the password.')
parser_clean.add_argument('--client-id', help='Manually set the client id.') parser_clean.add_argument('--client-id', help='Manually set the client id.')
parser_wizard = 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 esphomeyaml.")
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
args = parser.parse_args() args = parser.parse_args()
if args.command == 'wizard': if args.command == 'wizard':
return wizard.wizard(args.configuration) return wizard.wizard(args.configuration)
CONFIG_PATH = args.configuration core.CONFIG_PATH = args.configuration
config = read_config(CONFIG_PATH)
config = read_config(core.CONFIG_PATH)
if config is None: if config is None:
return 1 return 1
if args.command == 'config': if args.command == 'config':
print(yaml_util.dump(config)) print(yaml_util.dump(config))
return 0
elif args.command == 'compile': elif args.command == 'compile':
exit_code = write_cpp(config) exit_code = write_cpp(config)
if exit_code != 0: if exit_code != 0:
@@ -270,17 +293,19 @@ def main():
_LOGGER.info(u"Successfully compiled program.") _LOGGER.info(u"Successfully compiled program.")
return 0 return 0
elif args.command == 'upload': elif args.command == 'upload':
port = discover_serial_ports() port = args.upload_port or discover_serial_ports()
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.")
return 0 return 0
elif args.command == 'logs': elif args.command == 'logs':
port = discover_serial_ports() port = args.serial_port or discover_serial_ports()
return show_logs(config, args, port) return show_logs(config, args, port)
elif args.command == 'clean-mqtt': elif args.command == 'clean-mqtt':
return clean_mqtt(config, args) return clean_mqtt(config, args)
elif args.command == 'mqtt-fingerprint':
return mqtt.get_fingerprint(config)
elif args.command == 'run': elif args.command == 'run':
exit_code = write_cpp(config) exit_code = write_cpp(config)
if exit_code != 0: if exit_code != 0:
@@ -289,17 +314,19 @@ def main():
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.")
if args.no_logs: port = args.upload_port or discover_serial_ports()
return
port = discover_serial_ports()
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:
return 0
return show_logs(config, args, port) return show_logs(config, args, port)
else: elif args.command == 'version':
print(u"Unknown command {}".format(args.command)) print(u"Version: {}".format(const.__version__))
return 1 return 0
print(u"Unknown command {}".format(args.command))
return 1
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -2,27 +2,19 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_RATE
from esphomeyaml.helpers import App, Pvariable, RawExpression, add, HexIntLiteral from esphomeyaml.helpers import App, Pvariable
DEPENDENCIES = ['i2c'] DEPENDENCIES = ['i2c']
ADS1115_COMPONENT_CLASS = 'sensor::ADS1115Component' ADS1115_COMPONENT_CLASS = 'sensor::ADS1115Component'
RATES = { RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required."""
8: 'ADS1115_RATE_8',
16: 'ADS1115_RATE_16',
32: 'ADS1115_RATE_32',
64: 'ADS1115_RATE_64',
128: 'ADS1115_RATE_128',
250: 'ADS1115_RATE_250',
475: 'ADS1115_RATE_475',
860: 'ADS1115_RATE_860',
}
ADS1115_SCHEMA = vol.Schema({ ADS1115_SCHEMA = vol.Schema({
cv.GenerateID('ads1115'): cv.register_variable_id, cv.GenerateID('ads1115'): cv.register_variable_id,
vol.Required(CONF_ADDRESS): cv.i2c_address, vol.Required(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_RATE): vol.All(vol.Coerce(int), vol.Any(*list(RATES.keys()))),
vol.Optional(CONF_RATE): cv.invalid(RATE_REMOVE_MESSAGE)
}) })
CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA]) CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
@@ -30,8 +22,8 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
def to_code(config): def to_code(config):
for conf in config: for conf in config:
address = HexIntLiteral(conf[CONF_ADDRESS]) rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
rhs = App.make_ads1115_component(address) Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
ads1115 = Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
if CONF_RATE in conf:
add(ads1115.set_rate(RawExpression(RATES[conf[CONF_RATE]]))) BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View File

@@ -1,12 +1,8 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED, CONF_MQTT_ID
from esphomeyaml.helpers import add, setup_mqtt_component from esphomeyaml.helpers import add, setup_mqtt_component, App, Pvariable
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
vol.Optional(CONF_INVERTED): cv.boolean,
})
DEVICE_CLASSES = [ DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
@@ -17,13 +13,37 @@ DEVICE_CLASSES = [
DEVICE_CLASSES_MSG = "Unknown device class. Must be one of {}".format(', '.join(DEVICE_CLASSES)) DEVICE_CLASSES_MSG = "Unknown device class. Must be one of {}".format(', '.join(DEVICE_CLASSES))
MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
vol.Optional(CONF_INVERTED): cv.boolean,
vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower, vol.Optional(CONF_DEVICE_CLASS): vol.All(vol.Lower,
vol.Any(*DEVICE_CLASSES, msg=DEVICE_CLASSES_MSG)), vol.Any(*DEVICE_CLASSES, msg=DEVICE_CLASSES_MSG)),
}) })
MQTT_BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
def setup_mqtt_binary_sensor(obj, config, skip_device_class=False): })
if not skip_device_class and CONF_DEVICE_CLASS in config:
MQTT_BINARY_SENSOR_ID_SCHEMA = MQTT_BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID('mqtt_binary_sensor', CONF_MQTT_ID): cv.register_variable_id,
})
def setup_binary_sensor(obj, config):
if CONF_DEVICE_CLASS in config:
add(obj.set_device_class(config[CONF_DEVICE_CLASS])) add(obj.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_INVERTED in config:
add(obj.set_inverted(config[CONF_INVERTED]))
def setup_mqtt_binary_sensor(obj, config):
setup_mqtt_component(obj, config) setup_mqtt_component(obj, config)
def register_binary_sensor(var, config):
setup_binary_sensor(var, config)
rhs = App.register_binary_sensor(var)
mqtt_sensor = Pvariable('binary_sensor::MQTTBinarySensorComponent', config[CONF_MQTT_ID], rhs)
setup_mqtt_binary_sensor(mqtt_sensor, config)
BUILD_FLAGS = '-DUSE_BINARY_SENSOR'

View File

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

View File

@@ -0,0 +1,49 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import Pvariable, RawExpression, get_variable
from esphomeyaml.pins import validate_gpio_pin
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
DEPENDENCIES = ['esp32_touch']
TOUCH_PADS = {
4: 'TOUCH_PAD_NUM0',
0: 'TOUCH_PAD_NUM1',
2: 'TOUCH_PAD_NUM2',
15: 'TOUCH_PAD_NUM3',
13: 'TOUCH_PAD_NUM4',
12: 'TOUCH_PAD_NUM5',
14: 'TOUCH_PAD_NUM6',
27: 'TOUCH_PAD_NUM7',
33: 'TOUCH_PAD_NUM8',
32: 'TOUCH_PAD_NUM9',
}
def validate_touch_pad(value):
value = validate_gpio_pin(value)
if value not in TOUCH_PADS:
raise vol.Invalid("Pin {} does not support touch pads.".format(value))
return value
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('esp32_touch_pad'): cv.register_variable_id,
vol.Required(CONF_PIN): validate_touch_pad,
vol.Required(CONF_THRESHOLD): cv.uint16_t,
}).extend(binary_sensor.MQTT_BINARY_SENSOR_ID_SCHEMA.schema)
def to_code(config):
hub = get_variable(None, type='binary_sensor::ESP32TouchComponent')
touch_pad = RawExpression(TOUCH_PADS[config[CONF_PIN]])
rhs = hub.make_touch_pad(config[CONF_NAME], touch_pad, config[CONF_THRESHOLD])
device = Pvariable('ESP32TouchBinarySensor', config[CONF_ID], rhs)
binary_sensor.register_binary_sensor(device, config)
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'

View File

@@ -3,7 +3,7 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml import pins from esphomeyaml import pins
from esphomeyaml.components import binary_sensor from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN from esphomeyaml.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_PIN
from esphomeyaml.helpers import App, add, exp_gpio_input_pin, variable from esphomeyaml.helpers import App, add, exp_gpio_input_pin, variable
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
@@ -13,9 +13,12 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
def to_code(config): def to_code(config):
rhs = App.make_gpio_binary_sensor(exp_gpio_input_pin(config[CONF_PIN]), rhs = App.make_gpio_binary_sensor(config[CONF_NAME], exp_gpio_input_pin(config[CONF_PIN]))
config[CONF_NAME], config.get(CONF_DEVICE_CLASS)) gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs)
gpio = variable('Application::SimpleBinarySensor', config[CONF_ID], rhs)
if CONF_INVERTED in config: if CONF_INVERTED in config:
add(gpio.Pgpio.set_inverted(config[CONF_INVERTED])) add(gpio.Pgpio.set_inverted(config[CONF_INVERTED]))
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config, skip_device_class=True) binary_sensor.setup_binary_sensor(gpio.Pgpio, config)
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_GPIO_BINARY_SENSOR'

View File

@@ -1,7 +1,9 @@
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import binary_sensor from esphomeyaml.components import binary_sensor
from esphomeyaml.const import CONF_ID, CONF_NAME from esphomeyaml.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, Pvariable from esphomeyaml.helpers import App, variable
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('status_binary_sensor'): cv.register_variable_id, cv.GenerateID('status_binary_sensor'): cv.register_variable_id,
@@ -10,5 +12,9 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
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])
gpio = Pvariable('binary_sensor::MQTTBinarySensorComponent', config[CONF_ID], rhs) status = variable('Application::MakeStatusBinarySensor', config[CONF_ID], rhs)
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config) binary_sensor.setup_binary_sensor(status.Pstatus, config)
binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config)
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'

View File

@@ -10,7 +10,7 @@ DALLAS_COMPONENT_CLASS = 'sensor::DallasComponent'
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID('dallas'): cv.register_variable_id, cv.GenerateID('dallas'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.input_output_pin, vol.Required(CONF_PIN): pins.input_output_pin,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
})]) })])
@@ -18,3 +18,6 @@ def to_code(config):
for conf in config: for conf in config:
rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL)) rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL))
Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs) Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View File

@@ -0,0 +1,14 @@
import voluptuous as vol
from esphomeyaml.helpers import App, add
DEPENDENCIES = ['logger']
CONFIG_SCHEMA = vol.Schema({})
def to_code(config):
add(App.make_debug_component())
BUILD_FLAGS = '-DUSE_DEBUG_COMPONENT'

View File

@@ -0,0 +1,41 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv, pins
from esphomeyaml.const import CONF_ID, CONF_RUN_CYCLES, CONF_RUN_DURATION, CONF_SLEEP_DURATION, \
CONF_WAKEUP_PIN
from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_input_pin
def validate_pin_number(value):
valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 39]
if value not in valid_pins:
raise vol.Invalid(u"Only pins {} support wakeup"
u"".format(', '.join(str(x) for x in valid_pins)))
return value
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('deep_sleep'): cv.register_variable_id,
vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.GPIO_INPUT_PIN_SCHEMA,
pins.schema_validate_number(validate_pin_number)),
vol.Optional(CONF_RUN_CYCLES): cv.positive_int,
vol.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_deep_sleep_component()
deep_sleep = Pvariable('DeepSleepComponent', config[CONF_ID], rhs)
if CONF_SLEEP_DURATION in config:
add(deep_sleep.set_sleep_duration(config[CONF_SLEEP_DURATION]))
if CONF_WAKEUP_PIN in config:
pin = exp_gpio_input_pin(config[CONF_WAKEUP_PIN])
add(deep_sleep.set_wakeup_pin(pin))
if CONF_RUN_CYCLES in config:
add(deep_sleep.set_run_cycles(config[CONF_RUN_CYCLES]))
if CONF_RUN_DURATION in config:
add(deep_sleep.set_run_duration(config[CONF_RUN_DURATION]))
BUILD_FLAGS = '-DUSE_DEEP_SLEEP'

View File

@@ -0,0 +1,22 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, Pvariable, add
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('esp32_ble'): cv.register_variable_id,
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_esp32_ble_tracker()
ble = Pvariable('ESP32BLETracker', config[CONF_ID], rhs)
if CONF_SCAN_INTERVAL in config:
add(ble.set_scan_interval(config[CONF_SCAN_INTERVAL]))
BUILD_FLAGS = '-DUSE_ESP32_BLE_TRACKER'

View File

@@ -0,0 +1,83 @@
import voluptuous as vol
from esphomeyaml import config_validation as cv
from esphomeyaml.const import CONF_ID, CONF_SETUP_MODE, CONF_IIR_FILTER, \
CONF_SLEEP_DURATION, CONF_MEASUREMENT_DURATION, CONF_LOW_VOLTAGE_REFERENCE, \
CONF_HIGH_VOLTAGE_REFERENCE, CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
from esphomeyaml.core import TimePeriod
from esphomeyaml.helpers import App, Pvariable, add
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
def validate_voltage(values):
def validator(value):
if isinstance(value, float) and value.is_integer():
value = int(value)
value = cv.string(value)
if not value.endswith('V'):
value += 'V'
if value not in values:
raise vol.Invalid('Must be one of {}'.format(values))
return value
return validator
LOW_VOLTAGE_REFERENCE = {
'0.5V': 'TOUCH_LVOLT_0V5',
'0.6V': 'TOUCH_LVOLT_0V6',
'0.7V': 'TOUCH_LVOLT_0V7',
'0.8V': 'TOUCH_LVOLT_0V8',
}
HIGH_VOLTAGE_REFERENCE = {
'2.4V': 'TOUCH_HVOLT_2V4',
'2.5V': 'TOUCH_HVOLT_2V5',
'2.6V': 'TOUCH_HVOLT_2V6',
'2.7V': 'TOUCH_HVOLT_2V7',
}
VOLTAGE_ATTENUATION = {
'1.5V': 'TOUCH_HVOLT_ATTEN_1V5',
'1V': 'TOUCH_HVOLT_ATTEN_1V',
'0.5V': 'TOUCH_HVOLT_ATTEN_0V5',
'0V': 'TOUCH_HVOLT_ATTEN_0V',
}
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('esp32_ble'): cv.register_variable_id,
vol.Optional(CONF_SETUP_MODE): cv.boolean,
vol.Optional(CONF_IIR_FILTER): cv.positive_time_period_milliseconds,
vol.Optional(CONF_SLEEP_DURATION):
vol.All(cv.positive_time_period, vol.Range(max=TimePeriod(microseconds=436906))),
vol.Optional(CONF_MEASUREMENT_DURATION):
vol.All(cv.positive_time_period, vol.Range(max=TimePeriod(microseconds=8192))),
vol.Optional(CONF_LOW_VOLTAGE_REFERENCE): validate_voltage(LOW_VOLTAGE_REFERENCE),
vol.Optional(CONF_HIGH_VOLTAGE_REFERENCE): validate_voltage(HIGH_VOLTAGE_REFERENCE),
vol.Optional(CONF_VOLTAGE_ATTENUATION): validate_voltage(VOLTAGE_ATTENUATION),
})
def to_code(config):
rhs = App.make_esp32_touch_component()
touch = Pvariable('binary_sensor::ESP32TouchComponent', config[CONF_ID], rhs)
if CONF_SETUP_MODE in config:
add(touch.set_setup_mode(config[CONF_SETUP_MODE]))
if CONF_IIR_FILTER in config:
add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
if CONF_SLEEP_DURATION in config:
sleep_duration = int(config[CONF_SLEEP_DURATION].total_microseconds * 6.6667)
add(touch.set_sleep_duration(sleep_duration))
if CONF_MEASUREMENT_DURATION in config:
measurement_duration = int(config[CONF_MEASUREMENT_DURATION].total_microseconds * 0.125)
add(touch.set_measurement_duration(measurement_duration))
if CONF_LOW_VOLTAGE_REFERENCE in config:
value = LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]]
add(touch.set_low_voltage_reference(value))
if CONF_HIGH_VOLTAGE_REFERENCE in config:
value = HIGH_VOLTAGE_REFERENCE[config[CONF_HIGH_VOLTAGE_REFERENCE]]
add(touch.set_high_voltage_reference(value))
if CONF_VOLTAGE_ATTENUATION in config:
value = VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]]
add(touch.set_voltage_attenuation(value))
BUILD_FLAGS = '-DUSE_ESP32_TOUCH_BINARY_SENSOR'

View File

@@ -21,3 +21,6 @@ def setup_mqtt_fan(obj, config):
if CONF_SPEED_COMMAND_TOPIC in config: if CONF_SPEED_COMMAND_TOPIC in config:
add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC])) add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
setup_mqtt_component(obj, config) setup_mqtt_component(obj, config)
BUILD_FLAGS = '-DUSE_FAN'

View File

@@ -15,7 +15,7 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
def to_code(config): def to_code(config):
output = get_variable(config[CONF_OUTPUT]) output = get_variable(config[CONF_OUTPUT])
rhs = App.make_fan(config[CONF_NAME]) rhs = App.make_fan(config[CONF_NAME])
fan_struct = variable('Application::FanStruct', config[CONF_ID], rhs) fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
add(fan_struct.Poutput.set_binary(output)) add(fan_struct.Poutput.set_binary(output))
if CONF_OSCILLATION_OUTPUT in config: if CONF_OSCILLATION_OUTPUT in config:
oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT]) oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])

View File

@@ -24,7 +24,7 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
def to_code(config): def to_code(config):
output = get_variable(config[CONF_OUTPUT]) output = get_variable(config[CONF_OUTPUT])
rhs = App.make_fan(config[CONF_NAME]) rhs = App.make_fan(config[CONF_NAME])
fan_struct = variable('Application::FanStruct', config[CONF_ID], rhs) fan_struct = variable('Application::MakeFan', config[CONF_ID], rhs)
if CONF_SPEED in config: if CONF_SPEED in config:
speeds = config[CONF_SPEED] speeds = config[CONF_SPEED]
add(fan_struct.Poutput.set_speed(output, 0.0, add(fan_struct.Poutput.set_speed(output, 0.0,

View File

@@ -2,15 +2,29 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml import pins from esphomeyaml import pins
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID, \
from esphomeyaml.helpers import App, add CONF_RECEIVE_TIMEOUT
from esphomeyaml.helpers import App, add, Pvariable
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('i2c'): cv.register_variable_id,
vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin, vol.Required(CONF_SDA, default='SDA'): pins.input_output_pin,
vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin, vol.Required(CONF_SCL, default='SCL'): pins.input_output_pin,
vol.Optional(CONF_FREQUENCY): vol.All(cv.only_on_esp32, cv.positive_int), vol.Optional(CONF_FREQUENCY): cv.positive_int,
vol.Optional(CONF_RECEIVE_TIMEOUT): cv.positive_time_period_milliseconds,
vol.Optional(CONF_SCAN): cv.boolean,
}) })
def to_code(config): def to_code(config):
add(App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_FREQUENCY))) rhs = App.init_i2c(config[CONF_SDA], config[CONF_SCL], config.get(CONF_SCAN))
i2c = Pvariable('I2CComponent', config[CONF_ID], rhs)
if CONF_FREQUENCY in config:
add(i2c.set_frequency(config[CONF_FREQUENCY]))
if CONF_RECEIVE_TIMEOUT in config:
add(i2c.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT]))
BUILD_FLAGS = '-DUSE_I2C'
LIB_DEPS = 'Wire'

View File

@@ -2,17 +2,16 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml import pins from esphomeyaml import pins
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32 from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN
from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent' IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent'
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
cv.GenerateID('ir_transmitter'): cv.register_variable_id, cv.GenerateID('ir_transmitter'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int),
vol.Range(min=1, max=100)),
})]) })])
@@ -21,3 +20,6 @@ def to_code(config):
pin = exp_gpio_output_pin(conf[CONF_PIN]) pin = exp_gpio_output_pin(conf[CONF_PIN])
rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT)) rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT))
Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs) Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs)
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'

View File

@@ -1,13 +1,17 @@
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT
from esphomeyaml.helpers import add, setup_mqtt_component from esphomeyaml.helpers import add
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
}).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema) }).extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA.schema)
def setup_mqtt_light_component(obj, config): def setup_light_component(obj, config):
if CONF_DEFAULT_TRANSITION_LENGTH in config: if CONF_DEFAULT_TRANSITION_LENGTH in config:
add(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH])) add(obj.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
setup_mqtt_component(obj, config) if CONF_GAMMA_CORRECT in config:
add(obj.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
BUILD_FLAGS = '-DUSE_LIGHT'

View File

@@ -4,7 +4,7 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import light from esphomeyaml.components import light
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, variable from esphomeyaml.helpers import App, get_variable, variable, setup_mqtt_component
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('binary_light'): cv.register_variable_id, cv.GenerateID('binary_light'): cv.register_variable_id,
@@ -15,5 +15,6 @@ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
def to_code(config): def to_code(config):
output = get_variable(config[CONF_OUTPUT]) output = get_variable(config[CONF_OUTPUT])
rhs = App.make_binary_light(config[CONF_NAME], output) rhs = App.make_binary_light(config[CONF_NAME], output)
light_struct = variable('Application::LightStruct', config[CONF_ID], rhs) light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
light.setup_mqtt_light_component(light_struct.Pmqtt, config) setup_mqtt_component(light_struct.Pmqtt, config)
light.setup_light_component(light_struct.Pstate, config)

View File

@@ -0,0 +1,88 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import light
from esphomeyaml.const import CONF_CHIPSET, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER
from esphomeyaml.helpers import App, RawExpression, TemplateArguments, add, setup_mqtt_component, \
variable
TYPES = [
'NEOPIXEL',
'TM1829',
'TM1809',
'TM1804',
'TM1803',
'UCS1903',
'UCS1903B',
'UCS1904',
'UCS2903',
'WS2812',
'WS2852',
'WS2812B',
'SK6812',
'SK6822',
'APA106',
'PL9823',
'WS2811',
'WS2813',
'APA104',
'WS2811_400',
'GW6205',
'GW6205_400',
'LPD1886',
'LPD1886_8BIT',
]
RGB_ORDERS = [
'RGB',
'RBG',
'GRB',
'GBR',
'BRG',
'BGR',
]
def validate(value):
if value[CONF_CHIPSET] == 'NEOPIXEL' and CONF_RGB_ORDER in value:
raise vol.Invalid("NEOPIXEL doesn't support RGB order")
return value
PLATFORM_SCHEMA = vol.All(light.PLATFORM_SCHEMA.extend({
cv.GenerateID('fast_led_clockless_light'): cv.register_variable_id,
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, vol.Any(*TYPES)),
vol.Required(CONF_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, vol.Any(*RGB_ORDERS)),
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
}), validate)
def to_code(config):
rhs = App.make_fast_led_light(config[CONF_NAME])
make = variable('Application::MakeFastLEDLight', config[CONF_ID], rhs)
fast_led = make.Pfast_led
rgb_order = None
if CONF_RGB_ORDER in config:
rgb_order = RawExpression(config[CONF_RGB_ORDER])
template_args = TemplateArguments(RawExpression(config[CONF_CHIPSET]),
config[CONF_PIN], rgb_order)
add(fast_led.add_leds(template_args, config[CONF_NUM_LEDS]))
if CONF_MAX_REFRESH_RATE in config:
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
setup_mqtt_component(make.Pmqtt, config)
light.setup_light_component(make.Pstate, config)
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'

View File

@@ -0,0 +1,69 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import light
from esphomeyaml.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, CONF_MAX_REFRESH_RATE, CONF_NAME, \
CONF_NUM_LEDS, CONF_RGB_ORDER
from esphomeyaml.helpers import App, TemplateArguments, add, setup_mqtt_component, variable, \
RawExpression
CHIPSETS = [
'LPD8806',
'WS2801',
'WS2803',
'SM16716',
'P9813',
'APA102',
'SK9822',
'DOTSTAR',
]
RGB_ORDERS = [
'RGB',
'RBG',
'GRB',
'GBR',
'BRG',
'BGR',
]
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('fast_led_spi_light'): cv.register_variable_id,
vol.Required(CONF_CHIPSET): vol.All(vol.Upper, vol.Any(*CHIPSETS)),
vol.Required(CONF_DATA_PIN): pins.output_pin,
vol.Required(CONF_CLOCK_PIN): pins.output_pin,
vol.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
vol.Optional(CONF_RGB_ORDER): vol.All(vol.Upper, vol.Any(*RGB_ORDERS)),
vol.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_fast_led_light(config[CONF_NAME])
make = variable('Application::MakeFastLEDLight', config[CONF_ID], rhs)
fast_led = make.Pfast_led
rgb_order = None
if CONF_RGB_ORDER in config:
rgb_order = RawExpression(config[CONF_RGB_ORDER])
template_args = TemplateArguments(RawExpression(config[CONF_CHIPSET]),
config[CONF_DATA_PIN],
config[CONF_CLOCK_PIN],
rgb_order)
add(fast_led.add_leds(template_args, config[CONF_NUM_LEDS]))
if CONF_MAX_REFRESH_RATE in config:
add(fast_led.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
setup_mqtt_component(make.Pmqtt, config)
light.setup_light_component(make.Pstate, config)
BUILD_FLAGS = '-DUSE_FAST_LED_LIGHT'

View File

@@ -4,20 +4,19 @@ import esphomeyaml.config_validation as cv
from esphomeyaml.components import light from esphomeyaml.components import light
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \ from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
CONF_NAME, CONF_OUTPUT CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, add, get_variable, variable from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('monochromatic_light'): cv.register_variable_id, cv.GenerateID('monochromatic_light'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id, vol.Required(CONF_OUTPUT): cv.variable_id,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
}) })
def to_code(config): def to_code(config):
output = get_variable(config[CONF_OUTPUT]) output = get_variable(config[CONF_OUTPUT])
rhs = App.make_monochromatic_light(config[CONF_NAME], output) rhs = App.make_monochromatic_light(config[CONF_NAME], output)
light_struct = variable('Application::LightStruct', config[CONF_ID], rhs) light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
if CONF_GAMMA_CORRECT in config: setup_mqtt_component(light_struct.Pmqtt, config)
add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT])) light.setup_light_component(light_struct.Pstate, config)
light.setup_mqtt_light_component(light_struct.Pmqtt, config)

View File

@@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv
from esphomeyaml.components import light from esphomeyaml.components import light
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED
from esphomeyaml.helpers import App, add, get_variable, variable from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('rgb_light'): cv.register_variable_id, cv.GenerateID('rgb_light'): cv.register_variable_id,
@@ -12,7 +12,7 @@ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
vol.Required(CONF_GREEN): cv.variable_id, vol.Required(CONF_GREEN): cv.variable_id,
vol.Required(CONF_BLUE): cv.variable_id, vol.Required(CONF_BLUE): cv.variable_id,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
}) })
@@ -21,7 +21,6 @@ def to_code(config):
green = get_variable(config[CONF_GREEN]) green = get_variable(config[CONF_GREEN])
blue = get_variable(config[CONF_BLUE]) blue = get_variable(config[CONF_BLUE])
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('Application::LightStruct', config[CONF_ID], rhs) light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
if CONF_GAMMA_CORRECT in config: setup_mqtt_component(light_struct.Pmqtt, config)
add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT])) light.setup_light_component(light_struct.Pstate, config)
light.setup_mqtt_light_component(light_struct.Pmqtt, config)

View File

@@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv
from esphomeyaml.components import light from esphomeyaml.components import light
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \ from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE
from esphomeyaml.helpers import App, get_variable, variable, add from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
cv.GenerateID('rgbw_light'): cv.register_variable_id, cv.GenerateID('rgbw_light'): cv.register_variable_id,
@@ -13,7 +13,7 @@ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
vol.Required(CONF_BLUE): cv.variable_id, vol.Required(CONF_BLUE): cv.variable_id,
vol.Required(CONF_WHITE): cv.variable_id, vol.Required(CONF_WHITE): cv.variable_id,
vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float, vol.Optional(CONF_GAMMA_CORRECT): cv.positive_float,
vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period, vol.Optional(CONF_DEFAULT_TRANSITION_LENGTH): cv.positive_time_period_milliseconds,
}) })
@@ -23,7 +23,6 @@ def to_code(config):
blue = get_variable(config[CONF_BLUE]) blue = get_variable(config[CONF_BLUE])
white = get_variable(config[CONF_WHITE]) white = get_variable(config[CONF_WHITE])
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('Application::LightStruct', config[CONF_ID], rhs) light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
if CONF_GAMMA_CORRECT in config: setup_mqtt_component(light_struct.Pmqtt, config)
add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT])) light.setup_light_component(light_struct.Pstate, config)
light.setup_mqtt_light_component(light_struct.Pmqtt, config)

View File

@@ -2,18 +2,18 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGGER, CONF_LOGS, \ from esphomeyaml.const import CONF_BAUD_RATE, CONF_ID, CONF_LEVEL, CONF_LOGGER, CONF_LOGS, \
CONF_LOG_TOPIC, CONF_TX_BUFFER_SIZE CONF_TX_BUFFER_SIZE
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, Pvariable, RawExpression, add, exp_empty_optional from esphomeyaml.helpers import App, Pvariable, RawExpression, add
LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE'] LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
# pylint: disable=invalid-name
is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS)) is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS))
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(CONF_LOGGER): cv.register_variable_id, cv.GenerateID(CONF_LOGGER): cv.register_variable_id,
vol.Optional(CONF_BAUD_RATE): cv.positive_int, vol.Optional(CONF_BAUD_RATE): cv.positive_int,
vol.Optional(CONF_LOG_TOPIC): vol.Any(None, '', cv.publish_topic),
vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int, vol.Optional(CONF_TX_BUFFER_SIZE): cv.positive_int,
vol.Optional(CONF_LEVEL): is_log_level, vol.Optional(CONF_LEVEL): is_log_level,
vol.Optional(CONF_LOGS): vol.Schema({ vol.Optional(CONF_LOGS): vol.Schema({
@@ -31,16 +31,7 @@ def exp_log_level(level):
def to_code(config): def to_code(config):
baud_rate = config.get(CONF_BAUD_RATE) rhs = App.init_log(config.get(CONF_BAUD_RATE))
if baud_rate is None and CONF_LOG_TOPIC in config:
baud_rate = 115200
log_topic = None
if CONF_LOG_TOPIC in config:
if not config[CONF_LOG_TOPIC]:
log_topic = exp_empty_optional(u'std::string')
else:
log_topic = config[CONF_LOG_TOPIC]
rhs = App.init_log(baud_rate, log_topic)
log = Pvariable(u'LogComponent', config[CONF_ID], rhs) log = Pvariable(u'LogComponent', config[CONF_ID], rhs)
if CONF_TX_BUFFER_SIZE in config: if CONF_TX_BUFFER_SIZE in config:
add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE])) add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
@@ -54,7 +45,7 @@ def to_code(config):
add(log.set_log_level(tag, exp_log_level(level))) add(log.set_log_level(tag, exp_log_level(level)))
def get_build_flags(config): def required_build_flags(config):
if CONF_LEVEL in config: if CONF_LEVEL in config:
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL])) return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL]))
return u'' return None

View File

@@ -1,17 +1,31 @@
import re
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_DISCOVERY, \ from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_MQTT, CONF_PASSWORD, \ CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_SSL_FINGERPRINTS, CONF_ID, CONF_LOG_TOPIC, \
CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME, \ CONF_MQTT, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, \
CONF_WILL_MESSAGE, CONF_CLIENT_ID CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_KEEPALIVE
from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, exp_empty_optional from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, StructInitializer, add, \
exp_empty_optional, RawExpression
MQTT_WILL_BIRTH_SCHEMA = vol.Any(None, vol.Schema({
def validate_message_just_topic(value):
value = cv.publish_topic(value)
return {CONF_TOPIC: value}
MQTT_MESSAGE_BASE = vol.Schema({
vol.Required(CONF_TOPIC): cv.publish_topic, vol.Required(CONF_TOPIC): cv.publish_topic,
vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])), vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
vol.Optional(CONF_RETAIN, default=True): cv.boolean, vol.Optional(CONF_RETAIN, default=True): cv.boolean,
})
MQTT_MESSAGE_TEMPLATE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE, validate_message_just_topic)
MQTT_MESSAGE_SCHEMA = vol.Any(None, MQTT_MESSAGE_BASE.extend({
vol.Required(CONF_PAYLOAD): cv.mqtt_payload,
})) }))
@@ -19,7 +33,7 @@ def validate_broker(value):
value = cv.string_strict(value) value = cv.string_strict(value)
if value.endswith(u'.local'): if value.endswith(u'.local'):
raise vol.Invalid(u"MQTT server addresses ending with '.local' are currently unsupported." raise vol.Invalid(u"MQTT server addresses ending with '.local' are currently unsupported."
u" Please specify the static IP instead.") u" Please use the static IP instead.")
if u':' in value: if u':' in value:
raise vol.Invalid(u"Please specify the port using the port: option") raise vol.Invalid(u"Please specify the port using the port: option")
if not value: if not value:
@@ -27,7 +41,14 @@ def validate_broker(value):
return value return value
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ def validate_fingerprint(value):
value = cv.string(value)
if re.match(r'^[0-9a-f]{40}$', value) is None:
raise vol.Invalid(u"fingerprint must be valid SHA1 hash")
return value
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(CONF_MQTT): cv.register_variable_id, cv.GenerateID(CONF_MQTT): cv.register_variable_id,
vol.Required(CONF_BROKER): validate_broker, vol.Required(CONF_BROKER): validate_broker,
vol.Optional(CONF_PORT, default=1883): cv.port, vol.Optional(CONF_PORT, default=1883): cv.port,
@@ -37,9 +58,13 @@ CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
vol.Optional(CONF_DISCOVERY): cv.boolean, vol.Optional(CONF_DISCOVERY): cv.boolean,
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_WILL_BIRTH_SCHEMA, vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic, vol.Optional(CONF_TOPIC_PREFIX): cv.publish_topic,
vol.Optional(CONF_LOG_TOPIC): MQTT_MESSAGE_TEMPLATE_SCHEMA,
vol.Optional(CONF_SSL_FINGERPRINTS): vol.All(cv.only_on_esp8266,
cv.ensure_list, [validate_fingerprint]),
vol.Optional(CONF_KEEPALIVE): cv.positive_time_period_seconds,
}) })
@@ -49,7 +74,7 @@ def exp_mqtt_message(config):
exp = StructInitializer( exp = StructInitializer(
'mqtt::MQTTMessage', 'mqtt::MQTTMessage',
('topic', config[CONF_TOPIC]), ('topic', config[CONF_TOPIC]),
('payload', config[CONF_PAYLOAD]), ('payload', config.get(CONF_PAYLOAD, "")),
('qos', config[CONF_QOS]), ('qos', config[CONF_QOS]),
('retain', config[CONF_RETAIN]) ('retain', config[CONF_RETAIN])
) )
@@ -66,11 +91,37 @@ def to_code(config):
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True) discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant') 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_BIRTH_MESSAGE in config:
add(mqtt.set_birth_message(config[CONF_BIRTH_MESSAGE]))
if CONF_WILL_MESSAGE in config:
add(mqtt.set_last_will(config[CONF_WILL_MESSAGE]))
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]))
if CONF_BIRTH_MESSAGE in config:
birth_message = config[CONF_BIRTH_MESSAGE]
if birth_message is None:
add(mqtt.disable_birth_message())
else:
add(mqtt.set_birth_message(exp_mqtt_message(birth_message)))
if CONF_WILL_MESSAGE in config:
will_message = config[CONF_WILL_MESSAGE]
if will_message is None:
add(mqtt.disable_last_will())
else:
add(mqtt.set_last_will(exp_mqtt_message(will_message)))
if CONF_CLIENT_ID in config: if CONF_CLIENT_ID in config:
add(mqtt.set_client_id(config[CONF_CLIENT_ID])) add(mqtt.set_client_id(config[CONF_CLIENT_ID]))
if CONF_LOG_TOPIC in config:
log_topic = config[CONF_LOG_TOPIC]
if log_topic is None:
add(mqtt.disable_log_message())
else:
add(mqtt.set_log_topic(exp_mqtt_message(log_topic)))
if CONF_SSL_FINGERPRINTS in config:
for fingerprint in config[CONF_SSL_FINGERPRINTS]:
arr = [RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)]
add(mqtt.add_ssl_fingerprint(ArrayInitializer(*arr, multiline=False)))
if CONF_KEEPALIVE in config:
add(mqtt.set_keep_alive(config[CONF_KEEPALIVE]))
def required_build_flags(config):
if CONF_SSL_FINGERPRINTS in config:
return '-DASYNC_TCP_SSL_ENABLED=1'
return None

View File

@@ -4,14 +4,15 @@ import logging
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \ from esphomeyaml.const import CONF_ID, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE, \
ESP_PLATFORM_ESP8266, ESP_PLATFORM_ESP32 ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, Pvariable, add from esphomeyaml.helpers import App, Pvariable, add
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({ CONFIG_SCHEMA = vol.Schema({
cv.GenerateID(CONF_OTA): cv.register_variable_id, cv.GenerateID(CONF_OTA): cv.register_variable_id,
vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean, vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
# TODO Num attempts + wait time # TODO Num attempts + wait time
@@ -24,8 +25,8 @@ def to_code(config):
rhs = App.init_ota() rhs = App.init_ota()
ota = Pvariable('OTAComponent', config[CONF_ID], rhs) ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
if CONF_PASSWORD in config: if CONF_PASSWORD in config:
h = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest() hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
add(ota.set_auth_password_hash(h)) add(ota.set_auth_password_hash(hash_))
if config[CONF_SAFE_MODE]: if config[CONF_SAFE_MODE]:
add(ota.start_safe_mode()) add(ota.start_safe_mode())
@@ -33,12 +34,21 @@ 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 cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return 3232 return 3232
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
return 8266 return 8266
raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.") raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.")
def get_auth(config): def get_auth(config):
return config[CONF_OTA].get(CONF_PASSWORD, '') return config[CONF_OTA].get(CONF_PASSWORD, '')
BUILD_FLAGS = '-DUSE_OTA'
def lib_deps(config):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return ['ArduinoOTA', 'Update', 'ESPmDNS']
return ['Hash', 'ESP8266mDNS', 'ArduinoOTA']

View File

@@ -23,3 +23,6 @@ def setup_output_platform(obj, config, skip_power_supply=False):
add(obj.set_power_supply(power_supply)) add(obj.set_power_supply(power_supply))
if CONF_MAX_POWER in config: if CONF_MAX_POWER in config:
add(obj.set_max_power(config[CONF_MAX_POWER])) add(obj.set_max_power(config[CONF_MAX_POWER]))
BUILD_FLAGS = '-DUSE_OUTPUT'

View File

@@ -2,23 +2,30 @@ import voluptuous as vol
from esphomeyaml import pins from esphomeyaml import pins
from esphomeyaml.components import output from esphomeyaml.components import output
from esphomeyaml.const import CONF_ID, CONF_PIN, \ from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266
ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin, get_gpio_pin_number from esphomeyaml.helpers import App, Pvariable, exp_gpio_output_pin
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
def valid_pwm_pin(value):
if value >= 16:
raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.")
return value
PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = output.FLOAT_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, vol.Required(CONF_PIN): vol.All(pins.GPIO_OUTPUT_PIN_SCHEMA,
pins.schema_validate_number(valid_pwm_pin)),
}) })
def to_code(config): def to_code(config):
if get_gpio_pin_number(config[CONF_PIN]) >= 16:
# Too difficult to do in config validation
raise ESPHomeYAMLError(u"ESP8266: Only pins 0-16 support PWM.")
pin = exp_gpio_output_pin(config[CONF_PIN]) pin = exp_gpio_output_pin(config[CONF_PIN])
rhs = App.make_esp8266_pwm_output(pin) rhs = App.make_esp8266_pwm_output(pin)
gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs) gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs)
output.setup_output_platform(gpio, config) output.setup_output_platform(gpio, config)
BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT'

View File

@@ -15,3 +15,6 @@ def to_code(config):
rhs = App.make_gpio_output(pin) rhs = App.make_gpio_output(pin)
gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs) gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs)
output.setup_output_platform(gpio, config) output.setup_output_platform(gpio, config)
BUILD_FLAGS = '-DUSE_GPIO_OUTPUT'

View File

@@ -36,3 +36,6 @@ def to_code(config):
if CONF_CHANNEL in config: if CONF_CHANNEL in config:
add(ledc.set_channel(config[CONF_CHANNEL])) add(ledc.set_channel(config[CONF_CHANNEL]))
output.setup_output_platform(ledc, config) output.setup_output_platform(ledc, config)
BUILD_FLAGS = '-DUSE_LEDC_OUTPUT'

View File

@@ -23,3 +23,6 @@ def to_code(config):
rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply) rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs) out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs)
output.setup_output_platform(out, config, skip_power_supply=True) output.setup_output_platform(out, config, skip_power_supply=True)
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'

View File

@@ -10,12 +10,16 @@ PHASE_BALANCERS = ['None', 'Linear', 'Weaved']
PCA9685_COMPONENT_TYPE = 'output::PCA9685OutputComponent' PCA9685_COMPONENT_TYPE = 'output::PCA9685OutputComponent'
PHASE_BALANCER_MESSAGE = ("The phase_balancer option has been removed in version 1.5.0. "
"esphomelib will now automatically choose a suitable phase balancer.")
PCA9685_SCHEMA = vol.Schema({ PCA9685_SCHEMA = vol.Schema({
cv.GenerateID('pca9685'): cv.register_variable_id, cv.GenerateID('pca9685'): cv.register_variable_id,
vol.Required(CONF_FREQUENCY): vol.All(cv.frequency, vol.Required(CONF_FREQUENCY): vol.All(cv.frequency,
vol.Range(min=24, max=1526)), vol.Range(min=23.84, max=1525.88)),
vol.Optional(CONF_PHASE_BALANCER): vol.All(vol.Title, vol.Any(*PHASE_BALANCERS)),
vol.Optional(CONF_ADDRESS): cv.i2c_address, vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_PHASE_BALANCER): cv.invalid(PHASE_BALANCER_MESSAGE),
}) })
CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA]) CONFIG_SCHEMA = vol.All(cv.ensure_list, [PCA9685_SCHEMA])
@@ -31,3 +35,6 @@ def to_code(config):
phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format( phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format(
conf[CONF_PHASE_BALANCER])) conf[CONF_PHASE_BALANCER]))
add(pca9685.set_phase_balancer(phase_balancer)) add(pca9685.set_phase_balancer(phase_balancer))
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'

View File

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

View File

@@ -7,8 +7,8 @@ from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_output_pin
POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({ POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period, vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds,
vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period, vol.Optional(CONF_KEEP_ON_TIME): cv.positive_time_period_milliseconds,
}) })
CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA]) CONFIG_SCHEMA = vol.All(cv.ensure_list, [POWER_SUPPLY_SCHEMA])
@@ -23,3 +23,6 @@ def to_code(config):
add(psu.set_enable_time(conf[CONF_ENABLE_TIME])) add(psu.set_enable_time(conf[CONF_ENABLE_TIME]))
if CONF_KEEP_ON_TIME in conf: if CONF_KEEP_ON_TIME in conf:
add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME])) add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME]))
BUILD_FLAGS = '-DUSE_OUTPUT'

View File

@@ -3,8 +3,8 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_EXPIRE_AFTER, \ from esphomeyaml.const import CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_EXPIRE_AFTER, \
CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_ICON, \ CONF_EXPONENTIAL_MOVING_AVERAGE, CONF_FILTERS, CONF_FILTER_NAN, CONF_FILTER_OUT, CONF_ICON, \
CONF_ID, CONF_LAMBDA, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_SEND_EVERY, \ CONF_LAMBDA, CONF_MQTT_ID, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_SEND_EVERY, \
CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, CONF_ID
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \ from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
setup_mqtt_component setup_mqtt_component
@@ -13,7 +13,6 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
}) })
FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.Any( FILTERS_SCHEMA = vol.All(cv.ensure_list, [vol.Any(
# TODO Fix weird voluptuous error messages
vol.Schema({vol.Required(CONF_OFFSET): vol.Coerce(float)}), vol.Schema({vol.Required(CONF_OFFSET): vol.Coerce(float)}),
vol.Schema({vol.Required(CONF_MULTIPLY): vol.Coerce(float)}), vol.Schema({vol.Required(CONF_MULTIPLY): vol.Coerce(float)}),
vol.Schema({vol.Required(CONF_FILTER_OUT): vol.Coerce(float)}), vol.Schema({vol.Required(CONF_FILTER_OUT): vol.Coerce(float)}),
@@ -38,14 +37,15 @@ MQTT_SENSOR_SCHEMA = vol.Schema({
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict, vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict,
vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int), vol.Optional(CONF_ACCURACY_DECIMALS): vol.Coerce(int),
vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period), vol.Optional(CONF_EXPIRE_AFTER): vol.Any(None, cv.positive_time_period_milliseconds),
vol.Optional(CONF_FILTERS): FILTERS_SCHEMA vol.Optional(CONF_FILTERS): FILTERS_SCHEMA
}) })
MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({ MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
cv.GenerateID('mqtt_sensor'): cv.register_variable_id, cv.GenerateID('mqtt_sensor', CONF_MQTT_ID): cv.register_variable_id,
}) })
# pylint: disable=invalid-name
OffsetFilter = MockObj('new sensor::OffsetFilter') OffsetFilter = MockObj('new sensor::OffsetFilter')
MultiplyFilter = MockObj('new sensor::MultiplyFilter') MultiplyFilter = MockObj('new sensor::MultiplyFilter')
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter') FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
@@ -71,30 +71,37 @@ def setup_filter(config):
conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE] conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE]
return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY]) return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
if CONF_LAMBDA in config: if CONF_LAMBDA in config:
s = '[](float x) -> Optional<float> {{ return {}; }}'.format(config[CONF_LAMBDA]) s = u'[](float x) -> Optional<float> {{ return {}; }}'.format(config[CONF_LAMBDA])
return LambdaFilter(RawExpression(s)) return LambdaFilter(RawExpression(s))
raise ValueError("Filter unsupported: {}".format(config)) raise ValueError(u"Filter unsupported: {}".format(config))
def setup_mqtt_sensor_component(obj, config): def setup_mqtt_sensor_component(obj, config):
if CONF_EXPIRE_AFTER in config:
if config[CONF_EXPIRE_AFTER] is None:
add(obj.disable_expire_after())
else:
add(obj.set_expire_after(config[CONF_EXPIRE_AFTER]))
setup_mqtt_component(obj, config)
def setup_sensor(obj, config):
if CONF_UNIT_OF_MEASUREMENT in config: if CONF_UNIT_OF_MEASUREMENT in config:
add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT])) add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
if CONF_ICON in config: if CONF_ICON in config:
add(obj.set_icon(config[CONF_ICON])) add(obj.set_icon(config[CONF_ICON]))
if CONF_ACCURACY_DECIMALS in config: if CONF_ACCURACY_DECIMALS in config:
add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS])) add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
if CONF_EXPIRE_AFTER in config:
if config[CONF_EXPIRE_AFTER] is None:
add(obj.disable_expire_after())
else:
add(obj.set_expire_after(config[CONF_EXPIRE_AFTER]))
if CONF_FILTERS in config: if CONF_FILTERS in config:
filters = [setup_filter(x) for x in config[CONF_FILTERS]] filters = [setup_filter(x) for x in config[CONF_FILTERS]]
add(obj.set_filters(ArrayInitializer(*filters))) add(obj.set_filters(ArrayInitializer(*filters)))
setup_mqtt_component(obj, config)
def make_mqtt_sensor_for(exp, config): def register_sensor(var, config):
rhs = App.make_mqtt_sensor_for(exp, config[CONF_NAME]) setup_sensor(var, config)
mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_ID], rhs) rhs = App.register_sensor(var)
mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_MQTT_ID], rhs)
setup_mqtt_sensor_component(mqtt_sensor, config) setup_mqtt_sensor_component(mqtt_sensor, config)
BUILD_FLAGS = '-DUSE_SENSOR'

View File

@@ -20,16 +20,20 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('adc'): cv.register_variable_id, cv.GenerateID('adc'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.analog_pin, vol.Required(CONF_PIN): pins.analog_pin,
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, ATTENUATION_MODE_SCHEMA), vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, ATTENUATION_MODE_SCHEMA),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema) }).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
def to_code(config): def to_code(config):
rhs = App.make_adc_sensor(config[CONF_PIN], config[CONF_NAME], rhs = App.make_adc_sensor(config[CONF_NAME], config[CONF_PIN],
config.get(CONF_UPDATE_INTERVAL)) config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeADCSensor', config[CONF_ID], rhs) make = variable('Application::MakeADCSensor', config[CONF_ID], rhs)
adc = make.Padc adc = make.Padc
if CONF_ATTENUATION in config: if CONF_ATTENUATION in config:
attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]] attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]]
add(adc.set_attenuation(RawExpression(attenuation))) add(adc.set_attenuation(RawExpression(attenuation)))
sensor.setup_sensor(adc, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config) sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_ADC_SENSOR'

View File

@@ -2,29 +2,30 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor from esphomeyaml.components import sensor
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \
from esphomeyaml.helpers import get_variable, RawExpression CONF_NAME, CONF_ID
from esphomeyaml.helpers import RawExpression, get_variable, Pvariable
DEPENDENCIES = ['ads1115'] DEPENDENCIES = ['ads1115']
MUX = { MUX = {
'A0_A1': 'ADS1115_MUX_P0_N1', 'A0_A1': 'sensor::ADS1115_MULTIPLEXER_P0_N1',
'A0_A3': 'ADS1115_MUX_P0_N3', 'A0_A3': 'sensor::ADS1115_MULTIPLEXER_P0_N3',
'A1_A3': 'ADS1115_MUX_P1_N3', 'A1_A3': 'sensor::ADS1115_MULTIPLEXER_P1_N3',
'A2_A3': 'ADS1115_MUX_P2_N3', 'A2_A3': 'sensor::ADS1115_MULTIPLEXER_P2_N3',
'A0_GND': 'ADS1115_MUX_P0_NG', 'A0_GND': 'sensor::ADS1115_MULTIPLEXER_P0_NG',
'A1_GND': 'ADS1115_MUX_P1_NG', 'A1_GND': 'sensor::ADS1115_MULTIPLEXER_P1_NG',
'A2_GND': 'ADS1115_MUX_P2_NG', 'A2_GND': 'sensor::ADS1115_MULTIPLEXER_P2_NG',
'A3_GND': 'ADS1115_MUX_P3_NG', 'A3_GND': 'sensor::ADS1115_MULTIPLEXER_P3_NG',
} }
GAIN = { GAIN = {
'6.144': 'ADS1115_PGA_6P144', '6.144': 'sensor::ADS1115_GAIN_6P144',
'4.096': 'ADS1115_PGA_6P096', '4.096': 'sensor::ADS1115_GAIN_6P096',
'2.048': 'ADS1115_PGA_2P048', '2.048': 'sensor::ADS1115_GAIN_2P048',
'1.024': 'ADS1115_PGA_1P024', '1.024': 'sensor::ADS1115_GAIN_1P024',
'0.512': 'ADS1115_PGA_0P512', '0.512': 'sensor::ADS1115_GAIN_0P512',
'0.256': 'ADS1115_PGA_0P256', '0.256': 'sensor::ADS1115_GAIN_0P256',
} }
@@ -40,10 +41,11 @@ def validate_gain(value):
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('ads1115_sensor'): cv.register_variable_id,
vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))), vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))),
vol.Required(CONF_GAIN): validate_gain, vol.Required(CONF_GAIN): validate_gain,
vol.Optional(CONF_ADS1115_ID): cv.variable_id, vol.Optional(CONF_ADS1115_ID): cv.variable_id,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema) }).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema)
@@ -52,5 +54,9 @@ def to_code(config):
mux = RawExpression(MUX[config[CONF_MULTIPLEXER]]) mux = RawExpression(MUX[config[CONF_MULTIPLEXER]])
gain = RawExpression(GAIN[config[CONF_GAIN]]) gain = RawExpression(GAIN[config[CONF_GAIN]])
sensor_ = hub.get_sensor(mux, gain, config.get(CONF_UPDATE_INTERVAL)) rhs = hub.get_sensor(config[CONF_NAME], mux, gain, config.get(CONF_UPDATE_INTERVAL))
sensor.make_mqtt_sensor_for(sensor_, config) sensor_ = Pvariable('sensor::ADS1115Sensor', config[CONF_ID], rhs)
sensor.register_sensor(sensor_, config)
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'

View File

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

View File

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

View File

@@ -0,0 +1,84 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_NAME, \
CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, CONF_GAS_RESISTANCE
from esphomeyaml.helpers import App, RawExpression, add, variable
DEPENDENCIES = ['i2c']
OVERSAMPLING_OPTIONS = {
'NONE': 'sensor::BME680_OVERSAMPLING_NONE',
'1X': 'sensor::BME680_OVERSAMPLING_1X',
'2X': 'sensor::BME680_OVERSAMPLING_2X',
'4X': 'sensor::BME680_OVERSAMPLING_4X',
'8X': 'sensor::BME680_OVERSAMPLING_8X',
'16X': 'sensor::BME680_OVERSAMPLING_16X',
}
IIR_FILTER_OPTIONS = {
'OFF': 'sensor::BME680_IIR_FILTER_OFF',
'1X': 'sensor::BME680_IIR_FILTER_1X',
'3X': 'sensor::BME680_IIR_FILTER_3X',
'7X': 'sensor::BME680_IIR_FILTER_7X',
'15X': 'sensor::BME680_IIR_FILTER_15X',
'31X': 'sensor::BME680_IIR_FILTER_31X',
'63X': 'sensor::BME680_IIR_FILTER_63X',
'127X': 'sensor::BME680_IIR_FILTER_127X',
}
BME680_OVERSAMPLING_SENSOR_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
vol.Optional(CONF_OVERSAMPLING): vol.All(vol.Upper, vol.Any(*OVERSAMPLING_OPTIONS)),
})
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('bme680'): cv.register_variable_id,
vol.Optional(CONF_ADDRESS, default=0x76): cv.i2c_address,
vol.Required(CONF_TEMPERATURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_PRESSURE): BME680_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): BME680_OVERSAMPLING_SENSOR_SCHEMA,
vol.Required(CONF_GAS_RESISTANCE): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_IIR_FILTER): vol.All(vol.Upper, vol.Any(*IIR_FILTER_OPTIONS)),
# TODO: Heater
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_bme680_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_PRESSURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
config[CONF_GAS_RESISTANCE][CONF_NAME],
config[CONF_ADDRESS],
config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeBME680Sensor', config[CONF_ID], rhs)
bme680 = make.Pbme680
if CONF_OVERSAMPLING in config[CONF_TEMPERATURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_TEMPERATURE][CONF_OVERSAMPLING]]
add(bme680.set_temperature_oversampling(RawExpression(constant)))
if CONF_OVERSAMPLING in config[CONF_PRESSURE]:
constant = OVERSAMPLING_OPTIONS[config[CONF_PRESSURE][CONF_OVERSAMPLING]]
add(bme680.set_pressure_oversampling(RawExpression(constant)))
if CONF_OVERSAMPLING in config[CONF_HUMIDITY]:
constant = OVERSAMPLING_OPTIONS[config[CONF_HUMIDITY][CONF_OVERSAMPLING]]
add(bme680.set_humidity_oversampling(RawExpression(constant)))
if CONF_IIR_FILTER in config:
constant = IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]
add(bme680.set_iir_filter(RawExpression(constant)))
sensor.setup_sensor(bme680.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(bme680.Pget_pressure_sensor(), config[CONF_PRESSURE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_pressure, config[CONF_PRESSURE])
sensor.setup_sensor(bme680.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(make.Pmqtt_humidity, config[CONF_HUMIDITY])
sensor.setup_sensor(bme680.Pget_gas_resistance_sensor(), config[CONF_GAS_RESISTANCE])
sensor.setup_mqtt_sensor_component(make.Pmqtt_gas_resistance, config[CONF_GAS_RESISTANCE])
BUILD_FLAGS = '-DUSE_BME680'

View File

@@ -14,7 +14,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA, vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_PRESSURE): MQTT_SENSOR_SCHEMA, vol.Required(CONF_PRESSURE): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_ADDRESS): cv.i2c_address, vol.Optional(CONF_ADDRESS): cv.i2c_address,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}) })
@@ -22,8 +22,13 @@ def to_code(config):
rhs = App.make_bmp085_sensor(config[CONF_TEMPERATURE][CONF_NAME], rhs = App.make_bmp085_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_PRESSURE][CONF_NAME], config[CONF_PRESSURE][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL)) config.get(CONF_UPDATE_INTERVAL))
bmp = variable('Application::MakeBMP085Component', config[CONF_ID], rhs) bmp = variable('Application::MakeBMP085Sensor', config[CONF_ID], rhs)
if CONF_ADDRESS in config: if CONF_ADDRESS in config:
add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS]))) add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS])))
sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), config[CONF_PRESSURE])
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE]) sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE])
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'

View File

@@ -3,16 +3,18 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor from esphomeyaml.components import sensor
from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, \ from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \
CONF_UPDATE_INTERVAL CONF_RESOLUTION, \
from esphomeyaml.helpers import HexIntLiteral, get_variable CONF_UPDATE_INTERVAL, CONF_ID
from esphomeyaml.helpers import HexIntLiteral, get_variable, Pvariable
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = vol.All(sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dallas_sensor'): cv.register_variable_id,
vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int, vol.Exclusive(CONF_ADDRESS, 'dallas'): cv.hex_int,
vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int, vol.Exclusive(CONF_INDEX, 'dallas'): cv.positive_int,
vol.Optional(CONF_DALLAS_ID): cv.variable_id, vol.Optional(CONF_DALLAS_ID): cv.variable_id,
vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=8, max=12)), vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=8, max=12)),
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema) }).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
def to_code(config): def to_code(config):
@@ -23,9 +25,13 @@ def to_code(config):
if CONF_ADDRESS in config: if CONF_ADDRESS in config:
address = HexIntLiteral(config[CONF_ADDRESS]) address = HexIntLiteral(config[CONF_ADDRESS])
sensor_ = hub.Pget_sensor_by_address(address, update_interval, rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, update_interval,
config.get(CONF_RESOLUTION)) config.get(CONF_RESOLUTION))
else: else:
sensor_ = hub.Pget_sensor_by_index(config[CONF_INDEX], update_interval, rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX],
config.get(CONF_RESOLUTION)) update_interval, config.get(CONF_RESOLUTION))
sensor.make_mqtt_sensor_for(sensor_, config) sensor_ = Pvariable('sensor::DallasTemperatureSensor', config[CONF_ID], rhs)
sensor.register_sensor(sensor_, config)
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'

View File

@@ -1,31 +1,44 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml import pins
from esphomeyaml.components import sensor from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \ from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, RawExpression, add, variable from esphomeyaml.helpers import App, RawExpression, add, variable, exp_gpio_output_pin
from esphomeyaml.pins import GPIO_OUTPUT_PIN_SCHEMA
DHT_MODELS = ['AUTO_DETECT', 'DHT11', 'DHT22', 'AM2302', 'RHT03'] DHT_MODELS = {
'AUTO_DETECT': 'sensor::DHT_MODEL_AUTO_DETECT',
'DHT11': 'sensor::DHT_MODEL_DHT11',
'DHT22': 'sensor::DHT_MODEL_DHT22',
'AM2302': 'sensor::DHT_MODEL_AM2302',
'RHT03': 'sensor::DHT_MODEL_RHT03',
}
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dht_sensor'): cv.register_variable_id, cv.GenerateID('dht_sensor'): cv.register_variable_id,
vol.Required(CONF_PIN): pins.input_output_pin, vol.Required(CONF_PIN): GPIO_OUTPUT_PIN_SCHEMA,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA, vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA, vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_MODEL): vol.All(vol.Upper, vol.Any(*DHT_MODELS)), vol.Optional(CONF_MODEL): vol.All(vol.Upper, vol.Any(*DHT_MODELS)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}) })
def to_code(config): def to_code(config):
rhs = App.make_dht_sensor(config[CONF_PIN], config[CONF_TEMPERATURE][CONF_NAME], pin = exp_gpio_output_pin(config[CONF_PIN])
config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL)) rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME],
dht = variable('Application::MakeDHTComponent', config[CONF_ID], rhs) config[CONF_HUMIDITY][CONF_NAME],
pin, config.get(CONF_UPDATE_INTERVAL))
dht = variable('Application::MakeDHTSensor', config[CONF_ID], rhs)
if CONF_MODEL in config: if CONF_MODEL in config:
model = RawExpression('DHT::{}'.format(config[CONF_MODEL])) constant = DHT_MODELS[config[CONF_MODEL]]
add(dht.Pdht.set_dht_model(model)) add(dht.Pdht.set_dht_model(RawExpression(constant)))
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY]) sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_DHT_SENSOR'

View File

@@ -13,7 +13,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('dht_sensor'): cv.register_variable_id, cv.GenerateID('dht_sensor'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA, vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA, vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}) })
@@ -21,6 +21,11 @@ def to_code(config):
rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME], rhs = App.make_hdc1080_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL)) config.get(CONF_UPDATE_INTERVAL))
hdc1080 = variable('Application::MakeHDC1080Component', config[CONF_ID], rhs) hdc1080 = variable('Application::MakeHDC1080Sensor', config[CONF_ID], rhs)
sensor.setup_sensor(hdc1080.Phdc1080.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_temperature, config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY]) sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'

View File

@@ -13,7 +13,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('htu21d'): cv.register_variable_id, cv.GenerateID('htu21d'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA, vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA, vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}) })
@@ -21,6 +21,11 @@ def to_code(config):
rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME], rhs = App.make_htu21d_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME], config[CONF_HUMIDITY][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL)) config.get(CONF_UPDATE_INTERVAL))
htu21d = variable('Application::MakeHTU21DComponent', config[CONF_ID], rhs) htu21d = variable('Application::MakeHTU21DSensor', config[CONF_ID], rhs)
sensor.setup_sensor(htu21d.Phtu21d.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_temperature, config[CONF_TEMPERATURE]) sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY]) sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'

View File

@@ -0,0 +1,73 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_ID_SCHEMA
from esphomeyaml.const import CONF_ADDRESS, CONF_ID, CONF_MQTT_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL
from esphomeyaml.helpers import App, Pvariable
DEPENDENCIES = ['i2c']
CONF_ACCEL_X = 'accel_x'
CONF_ACCEL_Y = 'accel_y'
CONF_ACCEL_Z = 'accel_z'
CONF_GYRO_X = 'gyro_x'
CONF_GYRO_Y = 'gyro_y'
CONF_GYRO_Z = 'gyro_z'
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('mpu6050'): cv.register_variable_id,
vol.Optional(CONF_ADDRESS, default=0x68): cv.i2c_address,
vol.Optional(CONF_ACCEL_X): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_ACCEL_Y): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_ACCEL_Z): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_X): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_Y): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_GYRO_Z): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_TEMPERATURE): MQTT_SENSOR_ID_SCHEMA,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_mpu6050_sensor(config[CONF_ADDRESS], config.get(CONF_UPDATE_INTERVAL))
mpu = Pvariable('sensor::MPU6050Component', config[CONF_ID], rhs)
if CONF_ACCEL_X in config:
conf = config[CONF_ACCEL_X]
rhs = mpu.Pmake_accel_x_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_ACCEL_Y in config:
conf = config[CONF_ACCEL_Y]
rhs = mpu.Pmake_accel_y_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_ACCEL_Z in config:
conf = config[CONF_ACCEL_Z]
rhs = mpu.Pmake_accel_z_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050AccelSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_GYRO_X in config:
conf = config[CONF_GYRO_X]
rhs = mpu.Pmake_gyro_x_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_GYRO_Y in config:
conf = config[CONF_GYRO_Y]
rhs = mpu.Pmake_gyro_y_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_GYRO_Z in config:
conf = config[CONF_GYRO_Z]
rhs = mpu.Pmake_gyro_z_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050GyroSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE]
rhs = mpu.Pmake_temperature_sensor(conf[CONF_NAME])
sensor_ = Pvariable('sensor::MPU6050TemperatureSensor', conf[CONF_MQTT_ID], rhs)
sensor.register_sensor(sensor_, conf)
BUILD_FLAGS = '-DUSE_MPU6050'

View File

@@ -36,14 +36,14 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA, vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA,
}), }),
vol.Optional(CONF_INTERNAL_FILTER): vol.All(vol.Coerce(int), vol.Range(min=0, max=1023)), vol.Optional(CONF_INTERNAL_FILTER): vol.All(vol.Coerce(int), vol.Range(min=0, max=1023)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema) }).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
def to_code(config): def to_code(config):
rhs = App.make_pulse_counter_sensor(config[CONF_PIN], config[CONF_NAME], rhs = App.make_pulse_counter_sensor(config[CONF_NAME], config[CONF_PIN],
config.get(CONF_UPDATE_INTERVAL)) config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakePulseCounter', config[CONF_ID], rhs) make = variable('Application::MakePulseCounterSensor', config[CONF_ID], rhs)
pcnt = make.Ppcnt pcnt = make.Ppcnt
if CONF_PULL_MODE in config: if CONF_PULL_MODE in config:
pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]] pull_mode = GPIO_PULL_MODES[config[CONF_PULL_MODE]]
@@ -55,4 +55,8 @@ def to_code(config):
add(pcnt.set_edge_mode(RawExpression(rising_edge), RawExpression(falling_edge))) add(pcnt.set_edge_mode(RawExpression(rising_edge), RawExpression(falling_edge)))
if CONF_INTERNAL_FILTER in config: if CONF_INTERNAL_FILTER in config:
add(pcnt.set_filter(config[CONF_INTERNAL_FILTER])) add(pcnt.set_filter(config[CONF_INTERNAL_FILTER]))
sensor.setup_sensor(pcnt, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config) sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'

View File

@@ -0,0 +1,45 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import sensor
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_NAME, CONF_TEMPERATURE, \
CONF_UPDATE_INTERVAL, CONF_ADDRESS, CONF_ACCURACY
from esphomeyaml.helpers import App, variable, RawExpression, add
DEPENDENCIES = ['i2c']
SHT_ACCURACIES = {
'LOW': 'sensor::SHT3XD_ACCURACY_LOW',
'MEDIUM': 'sensor::SHT3XD_ACCURACY_MEDIUM',
'HIGH': 'sensor::SHT3XD_ACCURACY_HIGH',
}
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
cv.GenerateID('sht3xd'): cv.register_variable_id,
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
vol.Optional(CONF_ADDRESS, default=0x44): cv.i2c_address,
vol.Optional(CONF_ACCURACY): vol.All(vol.Upper, vol.Any(*SHT_ACCURACIES)),
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
})
def to_code(config):
rhs = App.make_sht3xd_sensor(config[CONF_TEMPERATURE][CONF_NAME],
config[CONF_HUMIDITY][CONF_NAME],
config.get(CONF_UPDATE_INTERVAL))
sht3xd = variable('Application::MakeSHT3XDSensor', config[CONF_ID], rhs)
if CONF_ACCURACY in config:
constant = RawExpression(SHT_ACCURACIES[config[CONF_ACCURACY]])
add(sht3xd.Psht3xd.set_accuracy(constant))
sensor.setup_sensor(sht3xd.Psht3xd.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
sensor.setup_mqtt_sensor_component(sht3xd.Pmqtt_temperature, config[CONF_TEMPERATURE])
sensor.setup_sensor(sht3xd.PPsht3xd.Pget_humidity_sensor(), config[CONF_HUMIDITY])
sensor.setup_mqtt_sensor_component(sht3xd.Pmqtt_humidity, config[CONF_HUMIDITY])
BUILD_FLAGS = '-DUSE_SHT3XD'

View File

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

View File

@@ -13,15 +13,15 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
vol.Required(CONF_TRIGGER_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA, vol.Required(CONF_TRIGGER_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
vol.Required(CONF_ECHO_PIN): pins.GPIO_INPUT_PIN_SCHEMA, vol.Required(CONF_ECHO_PIN): pins.GPIO_INPUT_PIN_SCHEMA,
vol.Exclusive(CONF_TIMEOUT_METER, 'timeout'): cv.positive_float, vol.Exclusive(CONF_TIMEOUT_METER, 'timeout'): cv.positive_float,
vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_int, vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_time_period_microseconds,
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema) }).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
def to_code(config): def to_code(config):
trigger = exp_gpio_output_pin(config[CONF_TRIGGER_PIN]) trigger = exp_gpio_output_pin(config[CONF_TRIGGER_PIN])
echo = exp_gpio_input_pin(config[CONF_ECHO_PIN]) echo = exp_gpio_input_pin(config[CONF_ECHO_PIN])
rhs = App.make_ultrasonic_sensor(trigger, echo, config[CONF_NAME], rhs = App.make_ultrasonic_sensor(config[CONF_NAME], trigger, echo,
config.get(CONF_UPDATE_INTERVAL)) config.get(CONF_UPDATE_INTERVAL))
make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs) make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs)
ultrasonic = make.Pultrasonic ultrasonic = make.Pultrasonic
@@ -29,4 +29,8 @@ def to_code(config):
add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME])) add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME]))
elif CONF_TIMEOUT_METER in config: elif CONF_TIMEOUT_METER in config:
add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER])) add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER]))
sensor.setup_sensor(ultrasonic, config)
sensor.setup_mqtt_sensor_component(make.Pmqtt, config) sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'

View File

@@ -1,7 +1,7 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_NAME from esphomeyaml.const import CONF_ICON, CONF_ID, CONF_NAME, CONF_MQTT_ID
from esphomeyaml.helpers import App, Pvariable, add, setup_mqtt_component from esphomeyaml.helpers import App, Pvariable, add, setup_mqtt_component
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
@@ -12,14 +12,25 @@ MQTT_SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_ICON): cv.icon,
}) })
MQTT_SWITCH_ID_SCHEMA = MQTT_SWITCH_SCHEMA.extend({
cv.GenerateID('mqtt_switch', CONF_MQTT_ID): cv.register_variable_id,
})
def setup_mqtt_switch(obj, config): def setup_mqtt_switch(obj, config):
if CONF_ICON in config:
add(obj.set_icon(config[CONF_ICON]))
setup_mqtt_component(obj, config) setup_mqtt_component(obj, config)
def make_mqtt_switch_for(exp, config): def setup_switch(obj, config):
rhs = App.make_mqtt_switch_for(exp, config[CONF_NAME]) if CONF_ICON in config:
mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_ID], rhs) add(obj.set_icon(config[CONF_ICON]))
def register_switch(var, config):
setup_switch(var, config)
rhs = App.register_switch(var)
mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_MQTT_ID], rhs)
setup_mqtt_switch(mqtt_switch, config) setup_mqtt_switch(mqtt_switch, config)
BUILD_FLAGS = '-DUSE_SWITCH'

View File

@@ -13,6 +13,10 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
def to_code(config): def to_code(config):
rhs = App.make_gpio_switch(exp_gpio_output_pin(config[CONF_PIN]), config[CONF_NAME]) rhs = App.make_gpio_switch(config[CONF_NAME], exp_gpio_output_pin(config[CONF_PIN]))
gpio = variable('Application::GPIOSwitchStruct', config[CONF_ID], rhs) gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs)
switch.setup_switch(gpio.Pswitch_, config)
switch.setup_mqtt_switch(gpio.Pmqtt, config) switch.setup_mqtt_switch(gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'

View File

@@ -3,14 +3,21 @@ import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch from esphomeyaml.components import switch
from esphomeyaml.components.ir_transmitter import IR_TRANSMITTER_COMPONENT_CLASS from esphomeyaml.components.ir_transmitter import IR_TRANSMITTER_COMPONENT_CLASS
from esphomeyaml.const import CONF_ADDRESS, CONF_COMMAND, CONF_DATA, CONF_IR_TRANSMITTER_ID, \ from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \
CONF_LG, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_REPEAT, CONF_SONY, CONF_TIMES, \ CONF_ID, CONF_IR_TRANSMITTER_ID, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, \
CONF_WAIT_TIME_US, CONF_RAW, CONF_CARRIER_FREQUENCY CONF_RAW, CONF_REPEAT, CONF_SONY, CONF_TIMES, CONF_WAIT_TIME
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import HexIntLiteral, MockObj, get_variable, ArrayInitializer from esphomeyaml.helpers import ArrayInitializer, HexIntLiteral, MockObj, Pvariable, get_variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ DEPENDENCIES = ['ir_transmitter']
cv.GenerateID('ir_transmitter'): cv.register_variable_id,
IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW]
WAIT_TIME_MESSAGE = "The wait_time_us option has been renamed to wait_time in order to decrease " \
"ambiguity. "
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('ir_transmitter_switch'): cv.register_variable_id,
vol.Exclusive(CONF_NEC, 'code'): vol.Schema({ vol.Exclusive(CONF_NEC, 'code'): vol.Schema({
vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
vol.Required(CONF_COMMAND): cv.hex_uint16_t, vol.Required(CONF_COMMAND): cv.hex_uint16_t,
@@ -33,11 +40,14 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
}), }),
vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({ vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
vol.Required(CONF_TIMES): cv.positive_not_null_int, vol.Required(CONF_TIMES): cv.positive_not_null_int,
vol.Required(CONF_WAIT_TIME_US): cv.uint32_t, vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds,
vol.Optional('wait_time_us'): cv.invalid(WAIT_TIME_MESSAGE),
})), })),
vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id, vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema) }).extend(switch.MQTT_SWITCH_ID_SCHEMA.schema), cv.has_at_least_one_key(*IR_KEYS))
# pylint: disable=invalid-name
SendData = MockObj('switch_::ir::SendData', '::') SendData = MockObj('switch_::ir::SendData', '::')
@@ -75,7 +85,7 @@ def exp_send_data(config):
wait_us = None wait_us = None
else: else:
times = config[CONF_REPEAT][CONF_TIMES] times = config[CONF_REPEAT][CONF_TIMES]
wait_us = config[CONF_REPEAT][CONF_WAIT_TIME_US] wait_us = config[CONF_REPEAT][CONF_WAIT_TIME]
base = MockObj(unicode(base), u'.') base = MockObj(unicode(base), u'.')
base = base.repeat(times, wait_us) base = base.repeat(times, wait_us)
return base return base
@@ -84,4 +94,10 @@ def exp_send_data(config):
def to_code(config): def to_code(config):
ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS) ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS)
send_data = exp_send_data(config) send_data = exp_send_data(config)
switch.make_mqtt_switch_for(ir.create_transmitter(send_data), config) rhs = ir.create_transmitter(config[CONF_NAME], send_data)
switch_ = Pvariable(IR_TRANSMITTER_COMPONENT_CLASS + '::DataTransmitter', config[CONF_ID],
rhs)
switch.register_switch(switch_, config)
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'

View File

@@ -0,0 +1,22 @@
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
from esphomeyaml.helpers import App, get_variable, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('output_switch'): cv.register_variable_id,
vol.Required(CONF_OUTPUT): cv.variable_id,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
def to_code(config):
output = get_variable(config[CONF_OUTPUT])
rhs = App.make_simple_switch(config[CONF_NAME], output)
gpio = variable('Application::MakeSimpleSwitch', config[CONF_ID], rhs)
switch.setup_switch(gpio.Pswitch_, config)
switch.setup_mqtt_switch(gpio.Pmqtt, config)
BUILD_FLAGS = '-DUSE_SIMPLE_SWITCH'

View File

@@ -1,7 +1,7 @@
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME from esphomeyaml.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, Pvariable from esphomeyaml.helpers import App, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('restart_switch'): cv.register_variable_id, cv.GenerateID('restart_switch'): cv.register_variable_id,
@@ -10,5 +10,9 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
def to_code(config): def to_code(config):
rhs = App.make_restart_switch(config[CONF_NAME]) rhs = App.make_restart_switch(config[CONF_NAME])
mqtt = Pvariable('switch_::MQTTSwitchComponent', config[CONF_ID], rhs) restart = variable('Application::MakeRestartSwitch', config[CONF_ID], rhs)
switch.setup_mqtt_switch(mqtt, config) switch.setup_switch(restart.Prestart, config)
switch.setup_mqtt_switch(restart.Pmqtt, config)
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'

View File

@@ -0,0 +1,19 @@
import esphomeyaml.config_validation as cv
from esphomeyaml.components import switch
from esphomeyaml.const import CONF_ID, CONF_NAME
from esphomeyaml.helpers import App, variable
PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
cv.GenerateID('shutdown_switch'): cv.register_variable_id,
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
def to_code(config):
rhs = App.make_shutdown_switch(config[CONF_NAME])
shutdown = variable('Application::MakeShutdownSwitch', config[CONF_ID],
rhs)
switch.setup_switch(shutdown.Pshutdown, config)
switch.setup_mqtt_switch(shutdown.Pmqtt, config)
BUILD_FLAGS = '-DUSE_SHUTDOWN_SWITCH'

View File

@@ -0,0 +1,35 @@
import logging
import voluptuous as vol
import esphomeyaml.config_validation as cv
from esphomeyaml import core
from esphomeyaml.const import CONF_PORT, CONF_JS_URL, CONF_CSS_URL, CONF_ID, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import App, add, Pvariable
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('web_server'): cv.register_variable_id,
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_CSS_URL): vol.Url,
vol.Optional(CONF_JS_URL): vol.Url,
})
def to_code(config):
rhs = App.init_web_server(config.get(CONF_PORT))
web_server = Pvariable('WebServer', config[CONF_ID], rhs)
if CONF_CSS_URL in config:
add(web_server.set_css_url(config[CONF_CSS_URL]))
if CONF_JS_URL in config:
add(web_server.set_js_url(config[CONF_JS_URL]))
BUILD_FLAGS = '-DUSE_WEB_SERVER'
def lib_deps(config):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return 'FS'
return ''

View File

@@ -1,24 +1,50 @@
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_DNS1, CONF_DNS2, CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, \ from esphomeyaml import core
CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, CONF_WIFI from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_GATEWAY, \
CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, \
ESP_PLATFORM_ESP8266
from esphomeyaml.helpers import App, MockObj, Pvariable, StructInitializer, add from esphomeyaml.helpers import App, MockObj, Pvariable, StructInitializer, add
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
cv.GenerateID(CONF_WIFI): cv.register_variable_id, def validate_password(value):
vol.Required(CONF_SSID): cv.ssid, value = cv.string(value)
vol.Optional(CONF_PASSWORD): cv.string, if not value:
vol.Optional(CONF_MANUAL_IP): vol.Schema({ return value
vol.Required(CONF_STATIC_IP): cv.ipv4, if len(value) < 8:
vol.Required(CONF_GATEWAY): cv.ipv4, raise vol.Invalid(u"WPA password must be at least 8 characters long")
vol.Required(CONF_SUBNET): cv.ipv4, if len(value) > 63:
vol.Inclusive(CONF_DNS1, 'dns'): cv.ipv4, raise vol.Invalid(u"WPA password must be at most 63 characters long")
vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4, return value
AP_MANUAL_IP_SCHEMA = vol.Schema({
vol.Required(CONF_STATIC_IP): cv.ipv4,
vol.Required(CONF_GATEWAY): cv.ipv4,
vol.Required(CONF_SUBNET): cv.ipv4,
})
STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({
vol.Inclusive(CONF_DNS1, 'dns'): cv.ipv4,
vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4,
})
CONFIG_SCHEMA = vol.Schema({
cv.GenerateID('wifi'): cv.register_variable_id,
vol.Optional(CONF_SSID): cv.ssid,
vol.Optional(CONF_PASSWORD): validate_password,
vol.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA,
vol.Optional(CONF_AP): vol.Schema({
vol.Required(CONF_SSID): cv.ssid,
vol.Optional(CONF_PASSWORD): validate_password,
vol.Optional(CONF_CHANNEL): vol.All(cv.positive_int, vol.Range(min=1, max=14)),
vol.Optional(CONF_MANUAL_IP): AP_MANUAL_IP_SCHEMA,
}), }),
vol.Optional(CONF_HOSTNAME): cv.hostname, vol.Optional(CONF_HOSTNAME): cv.hostname,
}) })
# pylint: disable=invalid-name
IPAddress = MockObj('IPAddress') IPAddress = MockObj('IPAddress')
@@ -28,19 +54,44 @@ def safe_ip(ip):
return IPAddress(*ip.args) return IPAddress(*ip.args)
def manual_ip(config):
return StructInitializer(
'ManualIP',
('static_ip', safe_ip(config[CONF_STATIC_IP])),
('gateway', safe_ip(config[CONF_GATEWAY])),
('subnet', safe_ip(config[CONF_SUBNET])),
('dns1', safe_ip(config.get(CONF_DNS1))),
('dns2', safe_ip(config.get(CONF_DNS2))),
)
def to_code(config): def to_code(config):
rhs = App.init_wifi(config[CONF_SSID], config.get(CONF_PASSWORD)) sta = CONF_SSID in config
ap = CONF_AP in config
if sta:
rhs = App.init_wifi(config[CONF_SSID], config.get(CONF_PASSWORD))
else:
rhs = App.init_wifi()
wifi = Pvariable('WiFiComponent', config[CONF_ID], rhs) wifi = Pvariable('WiFiComponent', config[CONF_ID], rhs)
if CONF_MANUAL_IP in config:
manual_ip = config[CONF_MANUAL_IP] if sta and CONF_MANUAL_IP in config:
exp = StructInitializer( add(wifi.set_sta_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
'ManualIP',
('static_ip', safe_ip(manual_ip[CONF_STATIC_IP])), if ap:
('gateway', safe_ip(manual_ip[CONF_GATEWAY])), conf = config[CONF_AP]
('subnet', safe_ip(manual_ip[CONF_SUBNET])), password = config.get(CONF_PASSWORD)
('dns1', safe_ip(manual_ip.get(CONF_DNS1))), if password is None and CONF_CHANNEL in conf:
('dns2', safe_ip(manual_ip.get(CONF_DNS2))), password = u""
) add(wifi.set_ap(conf[CONF_SSID], password, conf.get(CONF_CHANNEL)))
add(wifi.set_manual_ip(exp))
if CONF_MANUAL_IP in conf:
add(wifi.set_ap_manual_ip(manual_ip(conf[CONF_MANUAL_IP])))
if CONF_HOSTNAME in config: if CONF_HOSTNAME in config:
add(wifi.set_hostname(config[CONF_HOSTNAME])) add(wifi.set_hostname(config[CONF_HOSTNAME]))
def lib_deps(config):
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
return 'ESP8266WiFi'
return None

View File

@@ -8,29 +8,28 @@ import voluptuous as vol
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml import helpers, yaml_util from esphomeyaml import core, yaml_util
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_MQTT, \ from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_NAME, \
CONF_NAME, \ CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, ESP_PLATFORMS, \
CONF_PLATFORM, CONF_SIMPLIFY, CONF_WIFI, ESP_PLATFORMS, ESP_PLATFORM_ESP32, \ ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
from esphomeyaml.helpers import App, add, add_task, color from esphomeyaml.helpers import App, add, color
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.2.1' DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.4.0'
CORE_SCHEMA = vol.Schema({ CORE_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.valid_name, vol.Required(CONF_NAME): cv.valid_name,
vol.Required(CONF_PLATFORM): vol.All( vol.Required(CONF_PLATFORM): cv.string,
vol.Upper, vol.Any(ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266)),
vol.Required(CONF_BOARD): cv.string, vol.Required(CONF_BOARD): cv.string,
vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string, vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string,
vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean, vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean,
vol.Optional(CONF_USE_BUILD_FLAGS, default=True): cv.boolean,
}) })
REQUIRED_COMPONENTS = [ REQUIRED_COMPONENTS = [
CONF_ESPHOMEYAML, CONF_WIFI, CONF_MQTT CONF_ESPHOMEYAML, CONF_WIFI
] ]
_COMPONENT_CACHE = {} _COMPONENT_CACHE = {}
@@ -50,7 +49,6 @@ def get_component(domain):
module = importlib.import_module(path) module = importlib.import_module(path)
except ImportError as err: except ImportError as err:
_LOGGER.debug(err) _LOGGER.debug(err)
pass
else: else:
_COMPONENT_CACHE[domain] = module _COMPONENT_CACHE[domain] = module
return module return module
@@ -67,8 +65,17 @@ def is_platform_component(component):
return hasattr(component, 'PLATFORM_SCHEMA') return hasattr(component, 'PLATFORM_SCHEMA')
def validate_schema(config, schema): def iter_components(config):
return schema(config) for domain, conf in config.iteritems():
if domain == CONF_ESPHOMEYAML:
continue
component = get_component(domain)
yield domain, component, conf
if is_platform_component(component):
for p_config in conf:
p_name = u"{}.{}".format(domain, p_config[CONF_PLATFORM])
platform = get_component(p_name)
yield p_name, platform, p_config
class Config(OrderedDict): class Config(OrderedDict):
@@ -87,7 +94,7 @@ def validate_config(config):
for req in REQUIRED_COMPONENTS: for req in REQUIRED_COMPONENTS:
if req not in config: if req not in config:
raise ESPHomeYAMLError("Component %s is required for esphomeyaml.", req) raise ESPHomeYAMLError("Component {} is required for esphomeyaml.".format(req))
_ALL_COMPONENTS = list(config.keys()) _ALL_COMPONENTS = list(config.keys())
@@ -97,7 +104,7 @@ def validate_config(config):
result.add_error(_format_config_error(ex, domain, config), domain, config) result.add_error(_format_config_error(ex, domain, config), domain, config)
try: try:
result[CONF_ESPHOMEYAML] = validate_schema(config[CONF_ESPHOMEYAML], CORE_SCHEMA) result[CONF_ESPHOMEYAML] = CORE_SCHEMA(config[CONF_ESPHOMEYAML])
except vol.Invalid as ex: except vol.Invalid as ex:
_comp_error(ex, CONF_ESPHOMEYAML, config) _comp_error(ex, CONF_ESPHOMEYAML, config)
@@ -112,8 +119,8 @@ def validate_config(config):
continue continue
esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS) esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS)
if cv.ESP_PLATFORM not in esp_platforms: if core.ESP_PLATFORM not in esp_platforms:
result.add_error(u"Component {} doesn't support {}.".format(domain, cv.ESP_PLATFORM)) result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM))
continue continue
success = True success = True
@@ -130,16 +137,16 @@ def validate_config(config):
validated = component.CONFIG_SCHEMA(conf) validated = component.CONFIG_SCHEMA(conf)
result[domain] = validated result[domain] = validated
except vol.Invalid as ex: except vol.Invalid as ex:
_comp_error(ex, domain, config) _comp_error(ex, domain, conf)
continue continue
if not hasattr(component, 'PLATFORM_SCHEMA'): if not hasattr(component, 'PLATFORM_SCHEMA'):
continue continue
platforms = [] platforms = []
for i, p_config in enumerate(conf): for p_config in conf:
if not isinstance(p_config, dict): if not isinstance(p_config, dict):
result.add_error(u"Platform schemas mus have 'platform:' key") result.add_error(u"Platform schemas must have 'platform:' key")
continue continue
p_name = p_config.get(u'platform') p_name = p_config.get(u'platform')
if p_name is None: if p_name is None:
@@ -150,6 +157,16 @@ def validate_config(config):
result.add_error(u"Platform not found: {}.{}") result.add_error(u"Platform not found: {}.{}")
continue continue
success = True
dependencies = getattr(platform, 'DEPENDENCIES', [])
for dependency in dependencies:
if dependency not in _ALL_COMPONENTS:
result.add_error(u"Platform {}.{} requires {}".format(domain, p_name,
dependency))
success = False
if not success:
continue
if hasattr(platform, u'PLATFORM_SCHEMA'): if hasattr(platform, u'PLATFORM_SCHEMA'):
try: try:
p_validated = platform.PLATFORM_SCHEMA(p_config) p_validated = platform.PLATFORM_SCHEMA(p_config)
@@ -161,7 +178,7 @@ def validate_config(config):
return result return result
REQUIRED = ['esphomeyaml', 'wifi', 'mqtt'] REQUIRED = ['esphomeyaml', 'wifi']
def _format_config_error(ex, domain, config): def _format_config_error(ex, domain, config):
@@ -173,6 +190,9 @@ def _format_config_error(ex, domain, config):
else: else:
message += u'{}.'.format(humanize_error(config, ex)) message += u'{}.'.format(humanize_error(config, ex))
if isinstance(config, list):
return message
domain_config = config.get(domain, config) domain_config = config.get(domain, config)
message += u" (See {}, line {}). ".format( message += u" (See {}, line {}). ".format(
getattr(domain_config, '__config_file__', '?'), getattr(domain_config, '__config_file__', '?'),
@@ -186,46 +206,30 @@ def load_config(path):
config = yaml_util.load_yaml(path) config = yaml_util.load_yaml(path)
except OSError: except OSError:
raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path)) raise ESPHomeYAMLError(u"Could not read configuration file at {}".format(path))
core.RAW_CONFIG = config
esp_platform = unicode(config.get(CONF_ESPHOMEYAML, {}).get(CONF_PLATFORM, u"")) if CONF_ESPHOMEYAML not in config:
raise ESPHomeYAMLError(u"No esphomeyaml section in config")
core_conf = config[CONF_ESPHOMEYAML]
esp_platform = unicode(core_conf.get(CONF_PLATFORM, u""))
esp_platform = esp_platform.upper() esp_platform = esp_platform.upper()
if esp_platform not in (ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266): if '8266' in esp_platform:
raise ESPHomeYAMLError(u"Invalid ESP Platform {}".format(esp_platform)) esp_platform = ESP_PLATFORM_ESP8266
cv.ESP_PLATFORM = esp_platform if '32' in esp_platform:
cv.BOARD = unicode(config.get(CONF_ESPHOMEYAML, {}).get(CONF_BOARD, u"")) esp_platform = ESP_PLATFORM_ESP32
helpers.SIMPLIFY = cv.boolean(config.get(CONF_SIMPLIFY, True)) core.ESP_PLATFORM = esp_platform
core.BOARD = unicode(core_conf.get(CONF_BOARD, u""))
core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True))
try: try:
result = validate_config(config) result = validate_config(config)
except Exception as e: except Exception:
print(u"Unexpected exception while reading configuration:") print(u"Unexpected exception while reading configuration:")
raise raise
return result return result
def add_platform_task(domain, config):
platform_ = config[CONF_PLATFORM]
platform = get_platform(domain, platform_)
if not hasattr(platform, 'to_code'):
raise ESPHomeYAMLError(u"Platform '{}.{}' doesn't have to_code.".format(domain, platform_))
add_task(platform.to_code, config)
def add_component_task(domain, config):
if domain == CONF_ESPHOMEYAML:
add_task(core_to_code, config)
return
component = get_component(domain)
if is_platform_component(component):
for conf in config:
add_platform_task(domain, conf)
else:
if not hasattr(component, 'to_code'):
raise ESPHomeYAMLError(u"Component '{}' doesn't have to_code.".format(domain))
add_task(component.to_code, config)
def line_info(obj, **kwargs): def line_info(obj, **kwargs):
"""Display line config source.""" """Display line config source."""
if hasattr(obj, '__config_file__'): if hasattr(obj, '__config_file__'):
@@ -262,7 +266,11 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
def read_config(path): def read_config(path):
_LOGGER.debug("Reading configuration...") _LOGGER.debug("Reading configuration...")
res = load_config(path) try:
res = load_config(path)
except ESPHomeYAMLError as err:
_LOGGER.error(u"Error while reading config: %s", err)
return None
excepts = {} excepts = {}
for err in res.errors: for err in res.errors:
domain = err[1] or u"General Error" domain = err[1] or u"General Error"

View File

@@ -3,28 +3,29 @@
from __future__ import print_function from __future__ import print_function
import logging import logging
from datetime import timedelta import re
import voluptuous as vol import voluptuous as vol
from esphomeyaml import core
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \
CONF_NAME, CONF_PAYLOAD_AVAILABLE, \ CONF_NAME, CONF_PAYLOAD_AVAILABLE, \
CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \ CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.core import HexInt, IPAddress from esphomeyaml.core import HexInt, IPAddress, TimePeriod, TimePeriodMilliseconds, \
TimePeriodMicroseconds, TimePeriodSeconds
from esphomeyaml.helpers import ensure_unique_string from esphomeyaml.helpers import ensure_unique_string
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
# pylint: disable=invalid-name
port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
positive_float = vol.All(vol.Coerce(float), vol.Range(min=0)) positive_float = vol.All(vol.Coerce(float), vol.Range(min=0))
zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1)), zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0)) positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False)) positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
ESP_PLATFORM = ''
BOARD = ''
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
RESERVED_IDS = [ RESERVED_IDS = [
@@ -57,7 +58,7 @@ def alphanumeric(value):
def valid_name(value): def valid_name(value):
value = string_strict(value) value = string_strict(value)
if not all(c in ALLOWED_NAME_CHARS for c in value): if not all(c in ALLOWED_NAME_CHARS for c in value):
raise vol.Invalid(u"Valid characters for name are %s", ALLOWED_NAME_CHARS) raise vol.Invalid(u"Valid characters for name are {}".format(ALLOWED_NAME_CHARS))
return value return value
@@ -71,7 +72,7 @@ def string(value):
def string_strict(value): def string_strict(value):
"""Strictly only allow strings.""" """Strictly only allow strings."""
if isinstance(value, str) or isinstance(value, unicode): if isinstance(value, (str, unicode)):
return value return value
raise vol.Invalid("Must be string, did you forget putting quotes " raise vol.Invalid("Must be string, did you forget putting quotes "
"around the value?") "around the value?")
@@ -148,8 +149,7 @@ def only_on(platforms):
platforms = [platforms] platforms = [platforms]
def validator_(obj): def validator_(obj):
print(obj) if core.ESP_PLATFORM not in platforms:
if ESP_PLATFORM not in platforms:
raise vol.Invalid(u"This feature is only available on {}".format(platforms)) raise vol.Invalid(u"This feature is only available on {}".format(platforms))
return obj return obj
@@ -178,82 +178,153 @@ def has_at_least_one_key(*keys):
return validate return validate
TIME_PERIOD_ERROR = "Time period {} should be format 5ms, 5s, 5min, 5h" TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h"
time_period_dict = vol.All( time_period_dict = vol.All(
dict, vol.Schema({ dict, vol.Schema({
'days': vol.Coerce(int), 'days': vol.Coerce(float),
'hours': vol.Coerce(int), 'hours': vol.Coerce(float),
'minutes': vol.Coerce(int), 'minutes': vol.Coerce(float),
'seconds': vol.Coerce(int), 'seconds': vol.Coerce(float),
'milliseconds': vol.Coerce(int), 'milliseconds': vol.Coerce(float),
'microseconds': vol.Coerce(float),
}), }),
has_at_least_one_key('days', 'hours', 'minutes', has_at_least_one_key('days', 'hours', 'minutes',
'seconds', 'milliseconds'), 'seconds', 'milliseconds', 'microseconds'),
lambda value: timedelta(**value)) lambda value: TimePeriod(**value))
def time_period_str(value): TIME_PERIOD_EXPLICIT_MESSAGE = ("The old way of being able to write time values without a "
"""Validate and transform time offset.""" "time unit (like \"1000\" for 1000 milliseconds) has been "
"removed in 1.5.0 as it was ambiguous in some places. Please "
"now explicitly specify the time unit (like \"1000ms\"). See "
"https://esphomelib.com/esphomeyaml/configuration-types.html#time "
"for more information.")
def time_period_str_colon(value):
"""Validate and transform time offset with format HH:MM[:SS]."""
if isinstance(value, int): if isinstance(value, int):
raise vol.Invalid("Make sure you wrap time values in quotes") raise vol.Invalid('Make sure you wrap time values in quotes')
elif not isinstance(value, (str, unicode)): elif not isinstance(value, str):
raise vol.Invalid(TIME_PERIOD_ERROR.format(value)) raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
value = unicode(value) negative_offset = False
if value.endswith(u'ms'): if value.startswith('-'):
return vol.Coerce(int)(value[:-2]) negative_offset = True
elif value.endswith(u's'): value = value[1:]
return vol.Coerce(float)(value[:-1]) * 1000 elif value.startswith('+'):
elif value.endswith(u'min'): value = value[1:]
return vol.Coerce(float)(value[:-3]) * 1000 * 60
elif value.endswith(u'h'):
return vol.Coerce(float)(value[:-1]) * 1000 * 60 * 60
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
def time_period_milliseconds(value):
try: try:
return timedelta(milliseconds=int(value)) parsed = [int(x) for x in value.split(':')]
except (ValueError, TypeError): except ValueError:
raise vol.Invalid('Expected milliseconds, got {}'.format(value)) raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
if len(parsed) == 2:
hour, minute = parsed
second = 0
elif len(parsed) == 3:
hour, minute, second = parsed
else:
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
offset = TimePeriod(hours=hour, minutes=minute, seconds=second)
if negative_offset:
offset *= -1
return offset
def time_period_to_milliseconds(value): def time_period_str_unit(value):
if isinstance(value, (int, long)): """Validate and transform time period with time unit and integer value."""
return value if isinstance(value, int):
if isinstance(value, float): value = str(value)
return int(value) elif not isinstance(value, (str, unicode)):
return value / timedelta(milliseconds=1) raise vol.Invalid("Expected string for time period with unit.")
try:
float(value)
except ValueError:
pass
else:
raise vol.Invalid(TIME_PERIOD_EXPLICIT_MESSAGE)
unit_to_kwarg = {
'us': 'microseconds',
'microseconds': 'microseconds',
'ms': 'milliseconds',
'milliseconds': 'milliseconds',
's': 'seconds',
'sec': 'seconds',
'seconds': 'seconds',
'min': 'minutes',
'minutes': 'minutes',
'h': 'hours',
'hours': 'hours',
'd': 'days',
'days': 'days',
}
match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*)$", value)
if match is None or match.group(2) not in unit_to_kwarg:
raise vol.Invalid(u"Expected time period with unit, "
u"got {}".format(value))
kwarg = unit_to_kwarg[match.group(2)]
return TimePeriod(**{kwarg: float(match.group(1))})
time_period = vol.All(vol.Any(time_period_str, timedelta, time_period_dict, def time_period_in_milliseconds(value):
time_period_milliseconds), time_period_to_milliseconds) if value.microseconds is not None and value.microseconds != 0:
positive_time_period = vol.All(time_period, vol.Range(min=0)) raise vol.Invalid("Maximum precision is milliseconds")
positive_not_null_time_period = vol.All(time_period, vol.Range(min=0, min_included=False)) return TimePeriodMilliseconds(**value.as_dict())
def time_period_in_microseconds(value):
return TimePeriodMicroseconds(**value.as_dict())
def time_period_in_seconds(value):
if value.microseconds is not None and value.microseconds != 0:
raise vol.Invalid("Maximum precision is seconds")
if value.milliseconds is not None and value.milliseconds != 0:
raise vol.Invalid("Maximum precision is seconds")
return TimePeriodSeconds(**value.as_dict())
time_period = vol.Any(time_period_str_unit, time_period_str_colon, time_period_dict)
positive_time_period = vol.All(time_period, vol.Range(min=TimePeriod()))
positive_time_period_milliseconds = vol.All(positive_time_period, time_period_in_milliseconds)
positive_time_period_seconds = vol.All(positive_time_period, time_period_in_seconds)
positive_time_period_microseconds = vol.All(positive_time_period, time_period_in_microseconds)
positive_not_null_time_period = vol.All(time_period,
vol.Range(min=TimePeriod(), min_included=False))
METRIC_SUFFIXES = { METRIC_SUFFIXES = {
'E': 1e18, 'P': 1e15, 'T': 1e12, 'G': 1e9, 'M': 1e6, 'k': 1e3, 'da': 10, 'd': 1e-1, 'E': 1e18, 'P': 1e15, 'T': 1e12, 'G': 1e9, 'M': 1e6, 'k': 1e3, 'da': 10, 'd': 1e-1,
'c': 1e-2, 'm': 0.001, u'µ': 1e-6, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12, 'f': 1e-15, 'a': 1e-18, 'c': 1e-2, 'm': 0.001, u'µ': 1e-6, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12, 'f': 1e-15, 'a': 1e-18,
'': 1
} }
def frequency(value): def frequency(value):
value = string(value).replace(' ', '').lower() value = string(value)
if value.endswith('Hz') or value.endswith('hz') or value.endswith('HZ'): match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)(?:Hz|HZ|hz)?$", value)
value = value[:-2]
if not value: if match is None:
raise vol.Invalid(u"Frequency must have value") raise vol.Invalid(u"Expected frequency with unit, "
multiplier = 1 u"got {}".format(value))
if value[:-1] in METRIC_SUFFIXES:
multiplier = METRIC_SUFFIXES[value[:-1]] mantissa = float(match.group(1))
value = value[:-1] if match.group(2) not in METRIC_SUFFIXES:
elif len(value) >= 2 and value[:-2] in METRIC_SUFFIXES: raise vol.Invalid(u"Invalid frequency suffix {}".format(match.group(2)))
multiplier = METRIC_SUFFIXES[value[:-2]]
value = value[:-2] multiplier = METRIC_SUFFIXES[match.group(2)]
float_val = vol.Coerce(float)(value) return mantissa * multiplier
return float_val * multiplier
def hostname(value): def hostname(value):
@@ -273,8 +344,8 @@ def ssid(value):
raise vol.Invalid("SSID must be a string. Did you wrap it in quotes?") raise vol.Invalid("SSID must be a string. Did you wrap it in quotes?")
if not value: if not value:
raise vol.Invalid("SSID can't be empty.") raise vol.Invalid("SSID can't be empty.")
if len(value) > 32: if len(value) > 31:
raise vol.Invalid("SSID can't be longer than 32 characters") raise vol.Invalid("SSID can't be longer than 31 characters")
return value return value
@@ -295,15 +366,62 @@ def ipv4(value):
return IPAddress(*parts_) return IPAddress(*parts_)
def publish_topic(value): def _valid_topic(value):
value = string_strict(value) """Validate that this is a valid topic name/filter."""
if value.endswith('/'): if isinstance(value, dict):
raise vol.Invalid("Publish topic can't end with '/'") raise vol.Invalid("Can't use dictionary with topic")
value = string(value)
try:
raw_value = value.encode('utf-8')
except UnicodeError:
raise vol.Invalid("MQTT topic name/filter must be valid UTF-8 string.")
if not raw_value:
raise vol.Invalid("MQTT topic name/filter must not be empty.")
if len(raw_value) > 65535:
raise vol.Invalid("MQTT topic name/filter must not be longer than "
"65535 encoded bytes.")
if '\0' in value:
raise vol.Invalid("MQTT topic name/filter must not contain null "
"character.")
return value return value
subscribe_topic = string_strict # TODO improve this def subscribe_topic(value):
mqtt_payload = string # TODO improve this """Validate that we can subscribe using this MQTT topic."""
value = _valid_topic(value)
for i in (i for i, c in enumerate(value) if c == '+'):
if (i > 0 and value[i - 1] != '/') or \
(i < len(value) - 1 and value[i + 1] != '/'):
raise vol.Invalid("Single-level wildcard must occupy an entire "
"level of the filter")
index = value.find('#')
if index != -1:
if index != len(value) - 1:
# If there are multiple wildcards, this will also trigger
raise vol.Invalid("Multi-level wildcard must be the last "
"character in the topic filter.")
if len(value) > 1 and value[index - 1] != '/':
raise vol.Invalid("Multi-level wildcard must be after a topic "
"level separator.")
return value
def publish_topic(value):
"""Validate that we can publish using this MQTT topic."""
value = _valid_topic(value)
if '+' in value or '#' in value:
raise vol.Invalid("Wildcards can not be used in topic names")
return value
def mqtt_payload(value):
if value is None:
return ''
return string(value)
uint8_t = vol.All(int_, vol.Range(min=0, max=255)) uint8_t = vol.All(int_, vol.Range(min=0, max=255))
uint16_t = vol.All(int_, vol.Range(min=0, max=65535)) uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295)) uint32_t = vol.All(int_, vol.Range(min=0, max=4294967295))
@@ -313,8 +431,10 @@ hex_uint32_t = vol.All(hex_int, vol.Range(min=0, max=4294967295))
i2c_address = hex_uint8_t i2c_address = hex_uint8_t
def invalid(value): def invalid(message):
raise vol.Invalid("This shouldn't happen.") def validator(value):
raise vol.Invalid(message)
return validator
def valid(value): def valid(value):
@@ -333,23 +453,19 @@ def register_variable_id(value):
class GenerateID(vol.Optional): class GenerateID(vol.Optional):
def __init__(self, basename): def __init__(self, basename, key=CONF_ID):
self._basename = basename self._basename = basename
super(GenerateID, self).__init__(CONF_ID, default=self.default_variable_id) super(GenerateID, self).__init__(key, default=self.default_variable_id)
def default_variable_id(self): def default_variable_id(self):
return ensure_unique_string(self._basename, REGISTERED_IDS) return ensure_unique_string(self._basename, REGISTERED_IDS)
ID_SCHEMA = vol.Schema({
vol.Required(CONF_ID): invalid,
})
REQUIRED_ID_SCHEMA = vol.Schema({ REQUIRED_ID_SCHEMA = vol.Schema({
vol.Required(CONF_ID): register_variable_id, vol.Required(CONF_ID): register_variable_id,
}) })
PLATFORM_SCHEMA = ID_SCHEMA.extend({ PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): valid, vol.Required(CONF_PLATFORM): valid,
}) })

View File

@@ -1,8 +1,8 @@
"""Constants used by esphomeyaml.""" """Constants used by esphomeyaml."""
MAJOR_VERSION = 1 MAJOR_VERSION = 1
MINOR_VERSION = 2 MINOR_VERSION = 4
PATCH_VERSION = '1' PATCH_VERSION = '0'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
@@ -17,6 +17,7 @@ CONF_NAME = 'name'
CONF_PLATFORM = 'platform' CONF_PLATFORM = 'platform'
CONF_BOARD = 'board' CONF_BOARD = 'board'
CONF_SIMPLIFY = 'simplify' CONF_SIMPLIFY = 'simplify'
CONF_USE_BUILD_FLAGS = 'use_build_flags'
CONF_LIBRARY_URI = 'library_uri' CONF_LIBRARY_URI = 'library_uri'
CONF_LOGGER = 'logger' CONF_LOGGER = 'logger'
CONF_WIFI = 'wifi' CONF_WIFI = 'wifi'
@@ -32,6 +33,7 @@ CONF_BROKER = 'broker'
CONF_USERNAME = 'username' CONF_USERNAME = 'username'
CONF_POWER_SUPPLY = 'power_supply' CONF_POWER_SUPPLY = 'power_supply'
CONF_ID = 'id' CONF_ID = 'id'
CONF_MQTT_ID = 'mqtt_id'
CONF_PIN = 'pin' CONF_PIN = 'pin'
CONF_NUMBER = 'number' CONF_NUMBER = 'number'
CONF_INVERTED = 'inverted' CONF_INVERTED = 'inverted'
@@ -133,7 +135,7 @@ CONF_SONY = 'sony'
CONF_PANASONIC = 'panasonic' CONF_PANASONIC = 'panasonic'
CONF_REPEAT = 'repeat' CONF_REPEAT = 'repeat'
CONF_TIMES = 'times' CONF_TIMES = 'times'
CONF_WAIT_TIME_US = 'wait_time_us' CONF_WAIT_TIME = 'wait_time'
CONF_OSCILLATION_OUTPUT = 'oscillation_output' CONF_OSCILLATION_OUTPUT = 'oscillation_output'
CONF_SPEED = 'speed' CONF_SPEED = 'speed'
CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic' CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic'
@@ -151,6 +153,38 @@ CONF_RATE = 'rate'
CONF_ADS1115_ID = 'ads1115_id' CONF_ADS1115_ID = 'ads1115_id'
CONF_MULTIPLEXER = 'multiplexer' CONF_MULTIPLEXER = 'multiplexer'
CONF_GAIN = 'gain' CONF_GAIN = 'gain'
CONF_SLEEP_DURATION = 'sleep_duration'
CONF_WAKEUP_PIN = 'wakeup_pin'
CONF_RUN_CYCLES = 'run_cycles'
CONF_RUN_DURATION = 'run_duration'
CONF_AP = 'ap'
CONF_CSS_URL = 'css_url'
CONF_JS_URL = 'js_url'
CONF_SSL_FINGERPRINTS = 'ssl_fingerprints'
CONF_PCF8574 = 'pcf8574'
CONF_PCF8575 = 'pcf8575'
CONF_SCAN = 'scan'
CONF_KEEPALIVE = 'keepalive'
CONF_INTEGRATION_TIME = 'integration_time'
CONF_RECEIVE_TIMEOUT = 'receive_timeout'
CONF_SCAN_INTERVAL = 'scan_interval'
CONF_MAC_ADDRESS = 'mac_address'
CONF_SETUP_MODE = 'setup_mode'
CONF_IIR_FILTER = 'iir_filter'
CONF_MEASUREMENT_DURATION = 'measurement_duration'
CONF_LOW_VOLTAGE_REFERENCE = 'low_voltage_reference'
CONF_HIGH_VOLTAGE_REFERENCE = 'high_voltage_reference'
CONF_VOLTAGE_ATTENUATION = 'voltage_attenuation'
CONF_THRESHOLD = 'threshold'
CONF_OVERSAMPLING = 'oversampling'
CONF_GAS_RESISTANCE = 'gas_resistance'
CONF_NUM_LEDS = 'num_leds'
CONF_MAX_REFRESH_RATE = 'max_refresh_rate'
CONF_CHIPSET = 'chipset'
CONF_DATA_PIN = 'data_pin'
CONF_CLOCK_PIN = 'clock_pin'
CONF_RGB_ORDER = 'rgb_order'
CONF_ACCURACY = 'accuracy'
ESP32_BOARDS = [ ESP32_BOARDS = [
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1', 'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',

View File

@@ -1,3 +1,7 @@
import math
from collections import OrderedDict
class ESPHomeYAMLError(Exception): class ESPHomeYAMLError(Exception):
"""General esphomeyaml exception occurred.""" """General esphomeyaml exception occurred."""
pass pass
@@ -16,3 +20,159 @@ class IPAddress(object):
def __str__(self): def __str__(self):
return '.'.join(str(x) for x in self.args) return '.'.join(str(x) for x in self.args)
class MACAddress(object):
def __init__(self, *parts):
if len(parts) != 6:
raise ValueError(u"MAC Address must consist of 6 items")
self.parts = parts
def __str__(self):
return ':'.join('{:02X}'.format(part) for part in self.parts)
def is_approximately_integer(value):
if isinstance(value, (int, long)):
return True
return abs(value - round(value)) < 0.001
class TimePeriod(object):
def __init__(self, microseconds=None, milliseconds=None, seconds=None,
minutes=None, hours=None, days=None):
if days is not None:
if not is_approximately_integer(days):
frac_days, days = math.modf(days)
hours = (hours or 0) + frac_days * 24
self.days = int(round(days))
else:
self.days = None
if hours is not None:
if not is_approximately_integer(hours):
frac_hours, hours = math.modf(hours)
minutes = (minutes or 0) + frac_hours * 60
self.hours = int(round(hours))
else:
self.hours = None
if minutes is not None:
if not is_approximately_integer(minutes):
frac_minutes, minutes = math.modf(minutes)
seconds = (seconds or 0) + frac_minutes * 60
self.minutes = int(round(minutes))
else:
self.minutes = None
if seconds is not None:
if not is_approximately_integer(seconds):
frac_seconds, seconds = math.modf(seconds)
milliseconds = (milliseconds or 0) + frac_seconds * 1000
self.seconds = int(round(seconds))
else:
self.seconds = None
if milliseconds is not None:
if not is_approximately_integer(milliseconds):
frac_milliseconds, milliseconds = math.modf(milliseconds)
microseconds = (microseconds or 0) + frac_milliseconds * 1000
self.milliseconds = int(round(milliseconds))
else:
self.milliseconds = None
if microseconds is not None:
if not is_approximately_integer(microseconds):
raise ValueError("Maximum precision is microseconds")
self.microseconds = int(round(microseconds))
else:
self.microseconds = None
def as_dict(self):
out = OrderedDict()
if self.microseconds is not None:
out['microseconds'] = self.microseconds
if self.milliseconds is not None:
out['milliseconds'] = self.milliseconds
if self.seconds is not None:
out['seconds'] = self.seconds
if self.minutes is not None:
out['minutes'] = self.minutes
if self.hours is not None:
out['hours'] = self.hours
if self.days is not None:
out['days'] = self.days
return out
@property
def total_microseconds(self):
return self.total_milliseconds * 1000 + (self.microseconds or 0)
@property
def total_milliseconds(self):
return self.total_seconds * 1000 + (self.milliseconds or 0)
@property
def total_seconds(self):
return self.total_minutes * 60 + (self.seconds or 0)
@property
def total_minutes(self):
return self.total_hours * 60 + (self.minutes or 0)
@property
def total_hours(self):
return self.total_days * 24 + (self.hours or 0)
@property
def total_days(self):
return self.days or 0
def __eq__(self, other):
if not isinstance(other, TimePeriod):
raise ValueError("other must be TimePeriod")
return self.total_microseconds == other.total_microseconds
def __ne__(self, other):
if not isinstance(other, TimePeriod):
raise ValueError("other must be TimePeriod")
return self.total_microseconds != other.total_microseconds
def __lt__(self, other):
if not isinstance(other, TimePeriod):
raise ValueError("other must be TimePeriod")
return self.total_microseconds < other.total_microseconds
def __gt__(self, other):
if not isinstance(other, TimePeriod):
raise ValueError("other must be TimePeriod")
return self.total_microseconds > other.total_microseconds
def __le__(self, other):
if not isinstance(other, TimePeriod):
raise ValueError("other must be TimePeriod")
return self.total_microseconds <= other.total_microseconds
def __ge__(self, other):
if not isinstance(other, TimePeriod):
raise ValueError("other must be TimePeriod")
return self.total_microseconds >= other.total_microseconds
class TimePeriodMicroseconds(TimePeriod):
pass
class TimePeriodMilliseconds(TimePeriod):
pass
class TimePeriodSeconds(TimePeriod):
pass
CONFIG_PATH = None
SIMPLIFY = True
ESP_PLATFORM = ''
BOARD = ''
RAW_CONFIG = None

View File

@@ -43,6 +43,8 @@ import random
import socket import socket
import sys import sys
# pylint: disable=no-member
# Commands # Commands
FLASH = 0 FLASH = 0
SPIFFS = 100 SPIFFS = 100
@@ -62,7 +64,7 @@ def update_progress(progress):
:return: :return:
""" """
if PROGRESS: if PROGRESS:
barLength = 60 # Modify this to change the length of the progress bar bar_length = 60 # Modify this to change the length of the progress bar
status = "" status = ""
if isinstance(progress, int): if isinstance(progress, int):
progress = float(progress) progress = float(progress)
@@ -75,8 +77,8 @@ def update_progress(progress):
if progress >= 1: if progress >= 1:
progress = 1 progress = 1
status = "Done...\r\n" status = "Done...\r\n"
block = int(round(barLength * progress)) block = int(round(bar_length * progress))
text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (barLength - block), text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block),
int(progress * 100), status) int(progress * 100), status)
sys.stderr.write(text) sys.stderr.write(text)
sys.stderr.flush() sys.stderr.flush()
@@ -93,14 +95,14 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
try: try:
sock.bind(server_address) sock.bind(server_address)
sock.listen(1) sock.listen(1)
except Exception: except Exception: # pylint: disable=broad-except
_LOGGER.error("Listen Failed") _LOGGER.error("Listen Failed")
return 1 return 1
content_size = os.path.getsize(filename) content_size = os.path.getsize(filename)
f = open(filename, 'rb') f_handle = open(filename, 'rb')
file_md5 = hashlib.md5(f.read()).hexdigest() file_md5 = hashlib.md5(f_handle.read()).hexdigest()
f.close() f_handle.close()
_LOGGER.info('Upload size: %d', content_size) _LOGGER.info('Upload size: %d', content_size)
message = '%d %d %d %s\n' % (command, local_port, content_size, file_md5) message = '%d %d %d %s\n' % (command, local_port, content_size, file_md5)
@@ -116,7 +118,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try: try:
sock2.sendto(message.encode(), remote_address) sock2.sendto(message.encode(), remote_address)
except Exception: except Exception: # pylint: disable=broad-except
_LOGGER.error('Failed') _LOGGER.error('Failed')
sock2.close() sock2.close()
_LOGGER.error('Host %s Not Found', remote_host) _LOGGER.error('Host %s Not Found', remote_host)
@@ -125,7 +127,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
try: try:
data = sock2.recv(37).decode() data = sock2.recv(37).decode()
break break
except Exception: except Exception: # pylint: disable=broad-except
sys.stderr.write('.') sys.stderr.write('.')
sys.stderr.flush() sys.stderr.flush()
sock2.close() sock2.close()
@@ -148,7 +150,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
sock2.settimeout(10) sock2.settimeout(10)
try: try:
data = sock2.recv(32).decode() data = sock2.recv(32).decode()
except Exception: except Exception: # pylint: disable=broad-except
_LOGGER.error('FAIL: No Answer to our Authentication') _LOGGER.error('FAIL: No Answer to our Authentication')
sock2.close() sock2.close()
return 1 return 1
@@ -166,35 +168,36 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
_LOGGER.info('Waiting for device...') _LOGGER.info('Waiting for device...')
try: try:
sock.settimeout(10) sock.settimeout(10)
connection, client_address = sock.accept() connection, _ = sock.accept()
sock.settimeout(None) sock.settimeout(None)
connection.settimeout(None) connection.settimeout(None)
except Exception: except Exception: # pylint: disable=broad-except
_LOGGER.error('No response from device') _LOGGER.error('No response from device')
sock.close() sock.close()
return 1 return 1
try: try:
f = open(filename, "rb") f_handle = open(filename, "rb")
if PROGRESS: if PROGRESS:
update_progress(0) update_progress(0)
else: else:
_LOGGER.info('Uploading...') _LOGGER.info('Uploading...')
offset = 0 offset = 0
while True: while True:
chunk = f.read(1024) chunk = f_handle.read(1024)
if not chunk: break if not chunk:
break
offset += len(chunk) offset += len(chunk)
update_progress(offset / float(content_size)) update_progress(offset / float(content_size))
connection.settimeout(10) connection.settimeout(10)
try: try:
connection.sendall(chunk) connection.sendall(chunk)
connection.recv(10) connection.recv(10)
except Exception: except Exception: # pylint: disable=broad-except
sys.stderr.write('\n') sys.stderr.write('\n')
_LOGGER.error('Error Uploading') _LOGGER.error('Error Uploading')
connection.close() connection.close()
f.close() f_handle.close()
sock.close() sock.close()
return 1 return 1
@@ -207,26 +210,26 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
break break
_LOGGER.info('Result: OK') _LOGGER.info('Result: OK')
connection.close() connection.close()
f.close() f_handle.close()
sock.close() sock.close()
if data != "OK": if data != "OK":
_LOGGER.error('%s', data) _LOGGER.error('%s', data)
return 1 return 1
except Exception: except Exception: # pylint: disable=broad-except
_LOGGER.error('No Result!') _LOGGER.error('No Result!')
connection.close() connection.close()
f.close() f_handle.close()
sock.close() sock.close()
return 1 return 1
finally: finally:
connection.close() connection.close()
f.close() f_handle.close()
return 0 return 0
def parser(unparsed_args): def parse_args(unparsed_args):
parser = optparse.OptionParser( parser = optparse.OptionParser(
usage="%prog [options]", usage="%prog [options]",
description="Transmit image over the air to the esp8266 module with OTA support." description="Transmit image over the air to the esp8266 module with OTA support."
@@ -234,82 +237,91 @@ def parser(unparsed_args):
# destination ip and port # destination ip and port
group = optparse.OptionGroup(parser, "Destination") group = optparse.OptionGroup(parser, "Destination")
group.add_option("-i", "--ip", group.add_option(
dest="esp_ip", "-i", "--ip",
action="store", dest="esp_ip",
help="ESP8266 IP Address.", action="store",
default=False help="ESP8266 IP Address.",
) default=False
group.add_option("-I", "--host_ip", )
dest="host_ip", group.add_option(
action="store", "-I", "--host_ip",
help="Host IP Address.", dest="host_ip",
default="0.0.0.0" action="store",
) help="Host IP Address.",
group.add_option("-p", "--port", default="0.0.0.0"
dest="esp_port", )
type="int", group.add_option(
help="ESP8266 ota Port. Default 8266", "-p", "--port",
default=8266 dest="esp_port",
) type="int",
group.add_option("-P", "--host_port", help="ESP8266 ota Port. Default 8266",
dest="host_port", default=8266
type="int", )
help="Host server ota Port. Default random 10000-60000", group.add_option(
default=random.randint(10000, 60000) "-P", "--host_port",
) dest="host_port",
type="int",
help="Host server ota Port. Default random 10000-60000",
default=random.randint(10000, 60000)
)
parser.add_option_group(group) parser.add_option_group(group)
# auth # auth
group = optparse.OptionGroup(parser, "Authentication") group = optparse.OptionGroup(parser, "Authentication")
group.add_option("-a", "--auth", group.add_option(
dest="auth", "-a", "--auth",
help="Set authentication password.", dest="auth",
action="store", help="Set authentication password.",
default="" action="store",
) default=""
)
parser.add_option_group(group) parser.add_option_group(group)
# image # image
group = optparse.OptionGroup(parser, "Image") group = optparse.OptionGroup(parser, "Image")
group.add_option("-f", "--file", group.add_option(
dest="image", "-f", "--file",
help="Image file.", dest="image",
metavar="FILE", help="Image file.",
default=None metavar="FILE",
) default=None
group.add_option("-s", "--spiffs", )
dest="spiffs", group.add_option(
action="store_true", "-s", "--spiffs",
help="Use this option to transmit a SPIFFS image and do not flash the " dest="spiffs",
"module.", action="store_true",
default=False help="Use this option to transmit a SPIFFS image and do not flash the "
) "module.",
default=False
)
parser.add_option_group(group) parser.add_option_group(group)
# output group # output group
group = optparse.OptionGroup(parser, "Output") group = optparse.OptionGroup(parser, "Output")
group.add_option("-d", "--debug", group.add_option(
dest="debug", "-d", "--debug",
help="Show debug output. And override loglevel with debug.", dest="debug",
action="store_true", help="Show debug output. And override loglevel with debug.",
default=False action="store_true",
) default=False
group.add_option("-r", "--progress", )
dest="progress", group.add_option(
help="Show progress output. Does not work for ArduinoIDE", "-r", "--progress",
action="store_true", dest="progress",
default=False help="Show progress output. Does not work for ArduinoIDE",
) action="store_true",
default=False
)
parser.add_option_group(group) parser.add_option_group(group)
(options, args) = parser.parse_args(unparsed_args) options, _ = parser.parse_args(unparsed_args)
return options return options
def main(args): def main(args):
options = parser(args) options = parse_args(args)
_LOGGER.debug("Options: %s", str(options)) _LOGGER.debug("Options: %s", str(options))
# check options # check options

View File

@@ -4,16 +4,16 @@ import logging
import re import re
from collections import OrderedDict, deque from collections import OrderedDict, deque
from esphomeyaml import core
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \
CONF_INVERTED, \ CONF_INVERTED, \
CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, \ CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, \
CONF_STATE_TOPIC, CONF_TOPIC CONF_STATE_TOPIC, CONF_TOPIC, CONF_PCF8574
from esphomeyaml.core import ESPHomeYAMLError, HexInt from esphomeyaml.core import ESPHomeYAMLError, HexInt, TimePeriodMicroseconds, \
TimePeriodMilliseconds, TimePeriodSeconds
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
SIMPLIFY = False
def ensure_unique_string(preferred_string, current_strings): def ensure_unique_string(preferred_string, current_strings):
test_string = preferred_string test_string = preferred_string
@@ -45,10 +45,18 @@ def indent(text, padding=u' '):
class Expression(object): class Expression(object):
def __init__(self): def __init__(self):
pass self.requires = []
self.required = False
def __str__(self): def __str__(self):
raise NotImplemented raise NotImplementedError
def require(self):
self.required = True
for require in self.requires:
if require.required:
continue
require.require()
class RawExpression(Expression): class RawExpression(Expression):
@@ -60,15 +68,22 @@ class RawExpression(Expression):
return self.text return self.text
# pylint: disable=redefined-builtin
class AssignmentExpression(Expression): class AssignmentExpression(Expression):
def __init__(self, lhs, rhs, obj): def __init__(self, type, modifier, name, rhs, obj):
super(AssignmentExpression, self).__init__() super(AssignmentExpression, self).__init__()
self.obj = obj self.type = type
self.lhs = safe_exp(lhs) self.modifier = modifier
self.name = name
self.rhs = safe_exp(rhs) self.rhs = safe_exp(rhs)
self.requires.append(self.rhs)
self.obj = obj
def __str__(self): def __str__(self):
return u"{} = {}".format(self.lhs, self.rhs) type_ = self.type
if core.SIMPLIFY:
type_ = u'auto'
return u"{} {}{} = {}".format(type_, self.modifier, self.name, self.rhs)
class ExpressionList(Expression): class ExpressionList(Expression):
@@ -78,20 +93,43 @@ class ExpressionList(Expression):
args = list(args) args = list(args)
while args and args[-1] is None: while args and args[-1] is None:
args.pop() args.pop()
self.args = [safe_exp(x) for x in args] self.args = []
for arg in args:
exp = safe_exp(arg)
self.requires.append(exp)
self.args.append(exp)
def __str__(self): def __str__(self):
text = u", ".join(unicode(x) for x in self.args) text = u", ".join(unicode(x) for x in self.args)
return indent_all_but_first_and_last(text) return indent_all_but_first_and_last(text)
class TemplateArguments(Expression):
def __init__(self, *args):
super(TemplateArguments, self).__init__()
self.args = ExpressionList(*args)
self.requires.append(self.args)
def __str__(self):
return u'<{}>'.format(self.args)
class CallExpression(Expression): class CallExpression(Expression):
def __init__(self, base, *args): def __init__(self, base, *args):
super(CallExpression, self).__init__() super(CallExpression, self).__init__()
self.base = base self.base = base
if args and isinstance(args[0], TemplateArguments):
self.template_args = args[0]
self.requires.append(self.template_args)
args = args[1:]
else:
self.template_args = None
self.args = ExpressionList(*args) self.args = ExpressionList(*args)
self.requires.append(self.args)
def __str__(self): def __str__(self):
if self.template_args is not None:
return u'{}{}({})'.format(self.base, self.template_args, self.args)
return u'{}({})'.format(self.base, self.args) return u'{}({})'.format(self.base, self.args)
@@ -103,44 +141,57 @@ class StructInitializer(Expression):
args = OrderedDict(args) args = OrderedDict(args)
self.args = OrderedDict() self.args = OrderedDict()
for key, value in args.iteritems(): for key, value in args.iteritems():
if value is not None: if value is None:
self.args[key] = safe_exp(value) continue
exp = safe_exp(value)
self.args[key] = exp
self.requires.append(exp)
def __str__(self): def __str__(self):
s = u'{}{{\n'.format(self.base) cpp = u'{}{{\n'.format(self.base)
for key, value in self.args.iteritems(): for key, value in self.args.iteritems():
s += u' .{} = {},\n'.format(key, value) cpp += u' .{} = {},\n'.format(key, value)
s += u'}' cpp += u'}'
return s return cpp
class ArrayInitializer(Expression): class ArrayInitializer(Expression):
def __init__(self, *args): def __init__(self, *args, **kwargs):
super(ArrayInitializer, self).__init__() super(ArrayInitializer, self).__init__()
self.args = [safe_exp(x) for x in args if x is not None] self.multiline = kwargs.get('multiline', True)
self.args = []
for arg in args:
if arg is None:
continue
exp = safe_exp(arg)
self.args.append(exp)
self.requires.append(exp)
def __str__(self): def __str__(self):
if not self.args: if not self.args:
return u'{}' return u'{}'
s = u'{\n' if self.multiline:
for arg in self.args: cpp = u'{\n'
s += u' {},\n'.format(arg) for arg in self.args:
s += u'}' cpp += u' {},\n'.format(arg)
return s cpp += u'}'
else:
cpp = u'{' + u', '.join(str(arg) for arg in self.args) + u'}'
return cpp
class Literal(Expression): class Literal(Expression):
def __init__(self): def __str__(self):
super(Literal, self).__init__() raise NotImplementedError
class StringLiteral(Literal): class StringLiteral(Literal):
def __init__(self, s): def __init__(self, string):
super(StringLiteral, self).__init__() super(StringLiteral, self).__init__()
self.s = s self.string = string
def __str__(self): def __str__(self):
return u'"{}"'.format(self.s) return u'"{}"'.format(self.string)
class IntLiteral(Literal): class IntLiteral(Literal):
@@ -153,12 +204,12 @@ class IntLiteral(Literal):
class BoolLiteral(Literal): class BoolLiteral(Literal):
def __init__(self, b): def __init__(self, binary):
super(BoolLiteral, self).__init__() super(BoolLiteral, self).__init__()
self.b = b self.binary = binary
def __str__(self): def __str__(self):
return u"true" if self.b else u"false" return u"true" if self.binary else u"false"
class HexIntLiteral(Literal): class HexIntLiteral(Literal):
@@ -171,12 +222,12 @@ class HexIntLiteral(Literal):
class FloatLiteral(Literal): class FloatLiteral(Literal):
def __init__(self, f): def __init__(self, float_):
super(FloatLiteral, self).__init__() super(FloatLiteral, self).__init__()
self.f = f self.float_ = float_
def __str__(self): def __str__(self):
return u"{:f}f".format(self.f) return u"{:f}f".format(self.float_)
def safe_exp(obj): def safe_exp(obj):
@@ -184,12 +235,20 @@ def safe_exp(obj):
return obj return obj
elif isinstance(obj, bool): elif isinstance(obj, bool):
return BoolLiteral(obj) return BoolLiteral(obj)
elif isinstance(obj, str) or isinstance(obj, unicode): elif isinstance(obj, (str, unicode)):
return StringLiteral(obj) return StringLiteral(obj)
elif isinstance(obj, HexInt):
return HexIntLiteral(obj)
elif isinstance(obj, (int, long)): elif isinstance(obj, (int, long)):
return IntLiteral(obj) return IntLiteral(obj)
elif isinstance(obj, float): elif isinstance(obj, float):
return FloatLiteral(obj) return FloatLiteral(obj)
elif isinstance(obj, TimePeriodMicroseconds):
return IntLiteral(int(obj.total_microseconds))
elif isinstance(obj, TimePeriodMilliseconds):
return IntLiteral(int(obj.total_milliseconds))
elif isinstance(obj, TimePeriodSeconds):
return IntLiteral(int(obj.total_seconds))
raise ValueError(u"Object is not an expression", obj) raise ValueError(u"Object is not an expression", obj)
@@ -198,7 +257,7 @@ class Statement(object):
pass pass
def __str__(self): def __str__(self):
raise NotImplemented raise NotImplementedError
class RawStatement(Statement): class RawStatement(Statement):
@@ -225,21 +284,24 @@ def statement(expression):
return ExpressionStatement(expression) return ExpressionStatement(expression)
# pylint: disable=redefined-builtin, invalid-name
def variable(type, id, rhs): def variable(type, id, rhs):
lhs = RawExpression(u'{} {}'.format(type if not SIMPLIFY else u'auto', id))
rhs = safe_exp(rhs) rhs = safe_exp(rhs)
obj = MockObj(id, u'.') obj = MockObj(id, u'.')
add(AssignmentExpression(lhs, rhs, obj)) assignment = AssignmentExpression(type, '', id, rhs, obj)
add(assignment)
_VARIABLES[id] = obj, type _VARIABLES[id] = obj, type
obj.requires.append(assignment)
return obj return obj
def Pvariable(type, id, rhs): def Pvariable(type, id, rhs):
lhs = RawExpression(u'{} *{}'.format(type if not SIMPLIFY else u'auto', id))
rhs = safe_exp(rhs) rhs = safe_exp(rhs)
obj = MockObj(id, u'->') obj = MockObj(id, u'->')
add(AssignmentExpression(lhs, rhs, obj)) assignment = AssignmentExpression(type, '*', id, rhs, obj)
add(assignment)
_VARIABLES[id] = obj, type _VARIABLES[id] = obj, type
obj.requires.append(assignment)
return obj return obj
@@ -265,13 +327,13 @@ def get_variable(id, type=None):
return None return None
if result is None: if result is None:
if id is not None: if id is not None:
result = _VARIABLES[id][0] if id in _VARIABLES:
result = _VARIABLES[id][0]
elif type is not None: elif type is not None:
result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None) result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
if result is None: if result is None:
raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type)) raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type))
result.usages += 1
return result return result
@@ -279,17 +341,17 @@ def add_task(func, config):
_QUEUE.append((func, config)) _QUEUE.append((func, config))
def add(expression): def add(expression, require=True):
if require and isinstance(expression, Expression):
expression.require()
_EXPRESSIONS.append(expression) _EXPRESSIONS.append(expression)
return expression return expression
class MockObj(Expression): class MockObj(Expression):
def __init__(self, base, op=u'.', parent=None): def __init__(self, base, op=u'.'):
self.base = base self.base = base
self.op = op self.op = op
self.usages = 0
self.parent = parent
super(MockObj, self).__init__() super(MockObj, self).__init__()
def __getattr__(self, attr): def __getattr__(self, attr):
@@ -298,18 +360,26 @@ class MockObj(Expression):
attr = attr[1:] attr = attr[1:]
next_op = u'->' next_op = u'->'
op = self.op op = self.op
return MockObj(u'{}{}{}'.format(self.base, op, attr), next_op, self) obj = MockObj(u'{}{}{}'.format(self.base, op, attr), next_op)
obj.requires.append(self)
return obj
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
self.usages += 1 call = CallExpression(self.base, *args)
it = self.parent obj = MockObj(call, self.op)
while it is not None: obj.requires.append(self)
it.usages += 1 obj.requires.append(call)
it = it.parent return obj
return CallExpression(self.base, *args)
def __str__(self): def __str__(self):
return self.base return unicode(self.base)
def require(self):
self.required = True
for require in self.requires:
if require.required:
continue
require.require()
App = MockObj(u'App') App = MockObj(u'App')
@@ -328,6 +398,18 @@ def get_gpio_pin_number(conf):
def exp_gpio_pin_(obj, conf, default_mode): def exp_gpio_pin_(obj, conf, default_mode):
if isinstance(conf, int): if isinstance(conf, int):
return conf return conf
if CONF_PCF8574 in conf:
hub = get_variable(conf[CONF_PCF8574], 'io::PCF8574Component')
if default_mode == u'INPUT':
return hub.make_input_pin(conf[CONF_NUMBER],
RawExpression('PCF8574_' + conf[CONF_MODE]),
conf[CONF_INVERTED])
elif default_mode == u'OUTPUT':
return hub.make_output_pin(conf[CONF_NUMBER], conf[CONF_INVERTED])
else:
raise ESPHomeYAMLError(u"Unknown default mode {}".format(default_mode))
if conf.get(CONF_INVERTED) is None: if conf.get(CONF_INVERTED) is None:
return obj(conf[CONF_NUMBER], conf.get(CONF_MODE)) return obj(conf[CONF_NUMBER], conf.get(CONF_MODE))
return obj(conf[CONF_NUMBER], RawExpression(conf.get(CONF_MODE, default_mode)), return obj(conf[CONF_NUMBER], RawExpression(conf.get(CONF_MODE, default_mode)),
@@ -357,13 +439,8 @@ def setup_mqtt_component(obj, config):
add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC])) add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC]))
if CONF_AVAILABILITY in config: if CONF_AVAILABILITY in config:
availability = config[CONF_AVAILABILITY] availability = config[CONF_AVAILABILITY]
exp = StructInitializer( add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
u'mqtt::Availability', availability[CONF_PAYLOAD_NOT_AVAILABLE]))
(u'topic', availability[CONF_TOPIC]),
(u'payload_available', availability[CONF_PAYLOAD_AVAILABLE]),
(u'payload_not_available', availability[CONF_PAYLOAD_NOT_AVAILABLE]),
)
add(obj.set_availability(exp))
def exp_empty_optional(type): def exp_empty_optional(type):
@@ -392,7 +469,7 @@ def quote(s):
return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" return u"'" + s.replace(u"'", u"'\"'\"'") + u"'"
def color(the_color, message = '', reset=None): def color(the_color, message='', reset=None):
"""Color helper.""" """Color helper."""
from colorlog.escape_codes import escape_codes, parse_colors from colorlog.escape_codes import escape_codes, parse_colors
if not message: if not message:

View File

@@ -1,19 +1,23 @@
from __future__ import print_function from __future__ import print_function
import hashlib
import logging import logging
from datetime import datetime from datetime import datetime
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, CONF_LOGGER, \ from esphomeyaml import core
CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \ from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \
CONF_LOG_TOPIC, \
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
CONF_USERNAME CONF_USERNAME
from esphomeyaml.helpers import color
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def initialize(config, subscriptions, on_message, username, password, client_id): def initialize(config, subscriptions, on_message, username, password, client_id):
def on_connect(client, userdata, flags, rc): def on_connect(client, userdata, flags, return_code):
for topic in subscriptions: for topic in subscriptions:
client.subscribe(topic) client.subscribe(topic)
@@ -38,17 +42,22 @@ def initialize(config, subscriptions, on_message, username, password, client_id)
def show_logs(config, topic=None, username=None, password=None, client_id=None): def show_logs(config, topic=None, username=None, password=None, client_id=None):
if topic is not None: if topic is not None:
pass # already have topic pass # already have topic
elif CONF_LOG_TOPIC in config.get(CONF_LOGGER, {}): elif CONF_MQTT in config:
topic = config[CONF_LOGGER][CONF_LOG_TOPIC] conf = config[CONF_MQTT]
elif CONF_TOPIC_PREFIX in config[CONF_MQTT]: if CONF_LOG_TOPIC in conf:
topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + u'/debug' topic = config[CONF_MQTT][CONF_LOG_TOPIC]
elif CONF_TOPIC_PREFIX in config[CONF_MQTT]:
topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + u'/debug'
else:
topic = config[CONF_ESPHOMEYAML][CONF_NAME] + u'/debug'
else: else:
topic = config[CONF_ESPHOMEYAML][CONF_NAME] + u'/debug' _LOGGER.error(u"MQTT isn't setup, can't start MQTT logs")
return 1
_LOGGER.info(u"Starting log output from %s", topic) _LOGGER.info(u"Starting log output from %s", topic)
def on_message(client, userdata, msg): def on_message(client, userdata, msg):
t = datetime.now().time().strftime(u'[%H:%M:%S] ') time = datetime.now().time().strftime(u'[%H:%M:%S]')
print(t + msg.payload) print(time + msg.payload)
return initialize(config, [topic], on_message, username, password, client_id) return initialize(config, [topic], on_message, username, password, client_id)
@@ -58,7 +67,7 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant') discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant')
name = config[CONF_ESPHOMEYAML][CONF_NAME] name = config[CONF_ESPHOMEYAML][CONF_NAME]
topic = u'{}/+/{}/#'.format(discovery_prefix, name) topic = u'{}/+/{}/#'.format(discovery_prefix, name)
_LOGGER.info(u"Clearing messages from {}".format(topic)) _LOGGER.info(u"Clearing messages from %s", topic)
def on_message(client, userdata, msg): def on_message(client, userdata, msg):
if not msg.payload: if not msg.payload:
@@ -67,3 +76,23 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
client.publish(msg.topic, None, retain=True) client.publish(msg.topic, None, retain=True)
return initialize(config, [topic], on_message, username, password, client_id) return initialize(config, [topic], on_message, username, password, client_id)
# From marvinroger/async-mqtt-client -> scripts/get-fingerprint/get-fingerprint.py
def get_fingerprint(config):
import ssl
addr = config[CONF_MQTT][CONF_BROKER], config[CONF_MQTT][CONF_PORT]
_LOGGER.info("Getting fingerprint from %s:%s", addr[0], addr[1])
try:
cert_pem = ssl.get_server_certificate(addr)
except IOError as err:
_LOGGER.error("Unable to connect to server: %s", err)
return 1
cert_der = ssl.PEM_cert_to_DER_cert(cert_pem)
sha1 = hashlib.sha1(cert_der).hexdigest()
print(u"SHA1 Fingerprint: " + color('cyan', sha1))
print(u"Copy above string into mqtt.ssl_fingerprints section of {}".format(core.CONFIG_PATH))
return 0

View File

@@ -3,8 +3,9 @@ import logging
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.const import ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_NUMBER, CONF_MODE, \ from esphomeyaml import core
CONF_INVERTED from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -20,7 +21,7 @@ ESP8266_D1_PINS = dict(ESP8266_PINS, **{
'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2, 'SDA': 4, 'SCL': 5, 'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2, 'SDA': 4, 'SCL': 5,
}) })
ESP8266_D1_MINI_PINS = dict(ESP8266_PINS, **{ ESP8266_D1_MINI_PINS = dict(ESP8266_PINS, **{
'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 0, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'RX': 3, 'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 15, 'RX': 3,
'TX': 1, 'LED': 2, 'SDA': 4, 'SCL': 5, 'TX': 1, 'LED': 2, 'SDA': 4, 'SCL': 5,
}) })
ESP8266_THING_PINS = dict(ESP8266_PINS, **{ ESP8266_THING_PINS = dict(ESP8266_PINS, **{
@@ -71,46 +72,46 @@ def _translate_pin(value):
except ValueError: except ValueError:
pass pass
if value.startswith('GPIO'): if value.startswith('GPIO'):
return vol.Coerce(int)(value[len('GPIO'):]) return vol.Coerce(int)(value[len('GPIO'):].strip())
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if value in ESP32_PINS: if value in ESP32_PINS:
return ESP32_PINS[value] return ESP32_PINS[value]
if cv.BOARD not in ESP32_BOARD_TO_PINS: if core.BOARD not in ESP32_BOARD_TO_PINS:
raise vol.Invalid(u"ESP32: Unknown board {} with unknown " raise vol.Invalid(u"ESP32: Unknown board {} with unknown "
u"pin {}.".format(cv.BOARD, value)) u"pin {}.".format(core.BOARD, value))
if value not in ESP32_BOARD_TO_PINS[cv.BOARD]: if value not in ESP32_BOARD_TO_PINS[core.BOARD]:
raise vol.Invalid(u"ESP32: Board {} doesn't have" raise vol.Invalid(u"ESP32: Board {} doesn't have "
u"pin {}".format(cv.BOARD, value)) u"pin {}".format(core.BOARD, value))
return ESP32_BOARD_TO_PINS[cv.BOARD][value] return ESP32_BOARD_TO_PINS[core.BOARD][value]
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if value in ESP8266_PINS: if value in ESP8266_PINS:
return ESP8266_PINS[value] return ESP8266_PINS[value]
if cv.BOARD not in ESP8266_BOARD_TO_PINS: if core.BOARD not in ESP8266_BOARD_TO_PINS:
raise vol.Invalid(u"ESP8266: Unknown board {} with unknown " raise vol.Invalid(u"ESP8266: Unknown board {} with unknown "
u"pin {}.".format(cv.BOARD, value)) u"pin {}.".format(core.BOARD, value))
if value not in ESP8266_BOARD_TO_PINS[cv.BOARD]: if value not in ESP8266_BOARD_TO_PINS[core.BOARD]:
raise vol.Invalid(u"ESP8266: Board {} doesn't have" raise vol.Invalid(u"ESP8266: Board {} doesn't have "
u"pin {}".format(cv.BOARD, value)) u"pin {}".format(core.BOARD, value))
return ESP8266_BOARD_TO_PINS[cv.BOARD][value] return ESP8266_BOARD_TO_PINS[core.BOARD][value]
raise vol.Invalid(u"Invalid ESP platform.") raise vol.Invalid(u"Invalid ESP platform.")
def _validate_gpio_pin(value): def validate_gpio_pin(value):
value = _translate_pin(value) value = _translate_pin(value)
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if value < 0 or value > 39: if value < 0 or value > 39:
raise vol.Invalid(u"ESP32: Invalid pin number: {}".format(value)) raise vol.Invalid(u"ESP32: Invalid pin number: {}".format(value))
if 6 <= value <= 11: if 6 <= value <= 11:
_LOGGER.warning(u"ESP32: Pin {} (6-11) might already be used by the " _LOGGER.warning(u"ESP32: Pin %s (6-11) might already be used by the "
u"flash interface. Be warned.".format(value)) u"flash interface. Be warned.", value)
if value in (20, 24, 28, 29, 30, 31): if value in (20, 24, 28, 29, 30, 31):
_LOGGER.warning(u"ESP32: Pin {} (20, 24, 28-31) can usually not be used. " _LOGGER.warning(u"ESP32: Pin %s (20, 24, 28-31) can usually not be used. "
u"Be warned.".format(value)) u"Be warned.", value)
return value return value
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if 6 <= value <= 11: if 6 <= value <= 11:
_LOGGER.warning(u"ESP8266: Pin {} (6-11) might already be used by the " _LOGGER.warning(u"ESP8266: Pin %s (6-11) might already be used by the "
u"flash interface. Be warned.".format(value)) u"flash interface. Be warned.", value)
if value < 0 or value > 17: if value < 0 or value > 17:
raise vol.Invalid(u"ESP8266: Invalid pin number: {}".format(value)) raise vol.Invalid(u"ESP8266: Invalid pin number: {}".format(value))
return value return value
@@ -118,41 +119,40 @@ def _validate_gpio_pin(value):
def input_pin(value): def input_pin(value):
value = _validate_gpio_pin(value) value = validate_gpio_pin(value)
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return value return value
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
return value return value
raise vol.Invalid(u"Invalid ESP platform.") raise vol.Invalid(u"Invalid ESP platform.")
def output_pin(value): def output_pin(value):
value = _validate_gpio_pin(value) value = validate_gpio_pin(value)
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if 34 <= value <= 39: if 34 <= value <= 39:
raise vol.Invalid(u"ESP32: Pin {} (34-39) can only be used as " raise vol.Invalid(u"ESP32: Pin {} (34-39) can only be used as "
u"input pins.".format(value)) u"input pins.".format(value))
return value return value
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if value == 16:
raise vol.Invalid(u"Pin {} doesn't support output mode".format(value))
return value return value
raise vol.Invalid("Invalid ESP platform.") raise vol.Invalid("Invalid ESP platform.")
def analog_pin(value): def analog_pin(value):
value = _validate_gpio_pin(value) value = validate_gpio_pin(value)
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
if 32 <= value <= 39: # ADC1 if 32 <= value <= 39: # ADC1
return value return value
raise vol.Invalid(u"ESP32: Only pins 32 though 39 support ADC.") raise vol.Invalid(u"ESP32: Only pins 32 though 39 support ADC.")
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
if value == 17: # A0 if value == 17: # A0
return value return value
raise vol.Invalid(u"ESP8266: Only pin A0 (17) supports ADC.") raise vol.Invalid(u"ESP8266: Only pin A0 (17) supports ADC.")
raise vol.Invalid(u"Invalid ESP platform.") raise vol.Invalid(u"Invalid ESP platform.")
# pylint: disable=invalid-name
input_output_pin = vol.All(input_pin, output_pin) input_output_pin = vol.All(input_pin, output_pin)
gpio_pin = vol.Any(input_pin, output_pin) gpio_pin = vol.Any(input_pin, output_pin)
PIN_MODES_ESP8266 = [ PIN_MODES_ESP8266 = [
@@ -170,27 +170,42 @@ PIN_MODES_ESP32 = [
def pin_mode(value): def pin_mode(value):
value = vol.All(vol.Coerce(str), vol.Upper)(value) value = vol.All(vol.Coerce(str), vol.Upper)(value)
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32: if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
return vol.Any(*PIN_MODES_ESP32)(value) return vol.Any(*PIN_MODES_ESP32)(value)
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266: elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
return vol.Any(*PIN_MODES_ESP8266)(value) return vol.Any(*PIN_MODES_ESP8266)(value)
raise vol.Invalid(u"Invalid ESP platform.") raise vol.Invalid(u"Invalid ESP platform.")
GPIO_PIN_SCHEMA = vol.Schema({ PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({
vol.Required(CONF_NUMBER): gpio_pin, vol.Required(CONF_PCF8574): cv.variable_id,
vol.Required(CONF_MODE): pin_mode, vol.Required(CONF_NUMBER): vol.Coerce(int),
vol.Optional(CONF_INVERTED): cv.boolean, vol.Optional(CONF_INVERTED): cv.boolean,
}) })
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(output_pin, vol.Schema({ PCF8574_INPUT_PIN_SCHEMA = PCF8574_OUTPUT_PIN_SCHEMA.extend({
vol.Optional(CONF_MODE, default='INPUT'): vol.All(vol.Upper, vol.Any("INPUT", "INPUT_PULLUP")),
})
GPIO_OUTPUT_PIN_SCHEMA = vol.Any(output_pin, PCF8574_OUTPUT_PIN_SCHEMA, vol.Schema({
vol.Required(CONF_NUMBER): output_pin, vol.Required(CONF_NUMBER): output_pin,
vol.Optional(CONF_MODE): pin_mode, vol.Optional(CONF_MODE): pin_mode,
vol.Optional(CONF_INVERTED): cv.boolean, vol.Optional(CONF_INVERTED): cv.boolean,
})) }))
GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, vol.Schema({ GPIO_INPUT_PIN_SCHEMA = vol.Any(input_pin, PCF8574_INPUT_PIN_SCHEMA, vol.Schema({
vol.Required(CONF_NUMBER): input_pin, vol.Required(CONF_NUMBER): input_pin,
vol.Optional(CONF_MODE): pin_mode, vol.Optional(CONF_MODE): pin_mode,
vol.Optional(CONF_INVERTED): cv.boolean, vol.Optional(CONF_INVERTED): cv.boolean,
})) }))
def schema_validate_number(validator):
def valid(value):
if isinstance(value, dict):
value[CONF_NUMBER] = validator(value[CONF_NUMBER])
else:
value = validator(value)
return value
return valid

View File

@@ -2,49 +2,50 @@ from __future__ import print_function
import codecs import codecs
import os import os
from time import sleep
import unicodedata import unicodedata
import voluptuous as vol import voluptuous as vol
import esphomeyaml.config_validation as cv import esphomeyaml.config_validation as cv
from esphomeyaml.components import mqtt from esphomeyaml.components import mqtt
from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_BOARDS_FOR_PLATFORM from esphomeyaml.const import ESP_BOARDS_FOR_PLATFORM, ESP_PLATFORMS, ESP_PLATFORM_ESP32
from esphomeyaml.helpers import color from esphomeyaml.helpers import color
CORE_BIG = """ _____ ____ _____ ______
# pylint: disable=anomalous-backslash-in-string
CORE_BIG = """ _____ ____ _____ ______
/ ____/ __ \| __ \| ____| / ____/ __ \| __ \| ____|
| | | | | | |__) | |__ | | | | | | |__) | |__
| | | | | | _ /| __| | | | | | | _ /| __|
| |___| |__| | | \ \| |____ | |___| |__| | | \ \| |____
\_____\____/|_| \_\______| \_____\____/|_| \_\______|
""" """
ESP_BIG = """ ______ _____ _____ ESP_BIG = """ ______ _____ _____
| ____|/ ____| __ \ | ____|/ ____| __ \\
| |__ | (___ | |__) | | |__ | (___ | |__) |
| __| \___ \| ___/ | __| \___ \| ___/
| |____ ____) | | | |____ ____) | |
|______|_____/|_| |______|_____/|_|
""" """
WIFI_BIG = """ __ ___ ______ _ WIFI_BIG = """ __ ___ ______ _
\ \ / (_) ____(_) \ \ / (_) ____(_)
\ \ /\ / / _| |__ _ \ \ /\ / / _| |__ _
\ \/ \/ / | | __| | | \ \/ \/ / | | __| | |
\ /\ / | | | | | \ /\ / | | | | |
\/ \/ |_|_| |_| \/ \/ |_|_| |_|
""" """
MQTT_BIG = """ __ __ ____ _______ _______ MQTT_BIG = """ __ __ ____ _______ _______
| \/ |/ __ \__ __|__ __| | \/ |/ __ \__ __|__ __|
| \ / | | | | | | | | | \ / | | | | | | | |
| |\/| | | | | | | | | | |\/| | | | | | | | |
| | | | |__| | | | | | | | | | |__| | | | | |
|_| |_|\___\_\ |_| |_| |_| |_|\___\_\ |_| |_|
""" """
OTA_BIG = """ ____ _______ OTA_BIG = """ ____ _______
/ __ \__ __|/\ / __ \__ __|/\\
| | | | | | / \ | | | | | | / \\
| | | | | | / /\ \ | | | | | | / /\ \\
| |__| | | |/ ____ \ | |__| | | |/ ____ \\
\____/ |_/_/ \_\\ \____/ |_/_/ \_\\
""" """
@@ -68,6 +69,12 @@ logger:
""" """
if os.getenv('ESPHOMEYAML_QUICKWIZARD', False):
def sleep(time):
pass
else:
from time import sleep
def print_step(step, big): def print_step(step, big):
print() print()
@@ -85,8 +92,8 @@ def default_input(text, default):
# From https://stackoverflow.com/a/518232/8924614 # From https://stackoverflow.com/a/518232/8924614
def strip_accents(s): def strip_accents(string):
return u''.join(c for c in unicodedata.normalize('NFD', unicode(s)) return u''.join(c for c in unicodedata.normalize('NFD', unicode(string))
if unicodedata.category(c) != 'Mn') if unicodedata.category(c) != 'Mn')
@@ -190,7 +197,7 @@ def wizard(path):
print() print()
sleep(1) sleep(1)
print("First, what's the " + color('green', 'SSID') + " (the name) of the WiFi network {} " print("First, what's the " + color('green', 'SSID') + " (the name) of the WiFi network {} "
"I should connect to?".format(name)) "I should connect to?".format(name))
sleep(1.5) sleep(1.5)
print("For example \"{}\".".format(color('bold_white', "Abraham Linksys"))) print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
while True: while True:
@@ -200,7 +207,7 @@ def wizard(path):
break break
except vol.Invalid: except vol.Invalid:
print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. " print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
"Please try again.".format(ssid))) "Please try again.".format(ssid)))
print() print()
sleep(1) sleep(1)
@@ -230,9 +237,9 @@ def wizard(path):
try: try:
broker = mqtt.validate_broker(broker) broker = mqtt.validate_broker(broker)
break break
except vol.Invalid as e: except vol.Invalid as err:
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format( print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
broker, e))) broker, err)))
print("Please try again.") print("Please try again.")
print() print()
sleep(1) sleep(1)
@@ -271,8 +278,8 @@ def wizard(path):
else: else:
config += "ota:\n" config += "ota:\n"
with codecs.open(path, 'w') as f: with codecs.open(path, 'w') as f_handle:
f.write(config) f_handle.write(config)
print() print()
print(color('cyan', "DONE! I've now written a new configuration file to ") + print(color('cyan', "DONE! I've now written a new configuration file to ") +
@@ -290,4 +297,3 @@ def wizard(path):
print(" > Then follow the rest of the getting started guide:") print(" > Then follow the rest of the getting started guide:")
print(" > https://esphomelib.com/esphomeyaml/getting-started.html") print(" > https://esphomelib.com/esphomeyaml/getting-started.html")
return 0 return 0

View File

@@ -4,9 +4,10 @@ import codecs
import errno import errno
import os import os
from esphomeyaml.config import get_component from esphomeyaml import core
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_LOGGER, \ from esphomeyaml.config import iter_components
CONF_NAME, CONF_OTA, CONF_PLATFORM, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266 from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_NAME, \
CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
from esphomeyaml.core import ESPHomeYAMLError from esphomeyaml.core import ESPHomeYAMLError
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ===========' CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
@@ -35,9 +36,9 @@ void loop() {
INI_BASE_FORMAT = (u"""; Auto generated code by esphomeyaml INI_BASE_FORMAT = (u"""; Auto generated code by esphomeyaml
[common] [common]
lib_deps = lib_deps =
build_flags = build_flags =
upload_flags = upload_flags =
; ===== DO NOT EDIT ANYTHING BELOW THIS LINE ===== ; ===== DO NOT EDIT ANYTHING BELOW THIS LINE =====
""", u""" """, u"""
@@ -50,9 +51,10 @@ platform = {platform}
board = {board} board = {board}
framework = arduino framework = arduino
lib_deps = lib_deps =
{esphomeyaml_uri} {lib_deps}
${{common.lib_deps}} ${{common.lib_deps}}
build_flags ={build_flags} build_flags =
{build_flags}
${{common.build_flags}} ${{common.build_flags}}
""" """
@@ -62,19 +64,59 @@ PLATFORM_TO_PLATFORMIO = {
} }
def get_build_flags(config, key):
build_flags = set()
for _, component, conf in iter_components(config):
if not hasattr(component, key):
continue
flags = getattr(component, key)
if callable(flags):
flags = flags(conf)
if flags is None:
continue
if isinstance(flags, (str, unicode)):
flags = [flags]
build_flags |= set(flags)
return build_flags
def get_ini_content(config): def get_ini_content(config):
d = { platform = config[CONF_ESPHOMEYAML][CONF_PLATFORM]
if platform in PLATFORM_TO_PLATFORMIO:
platform = PLATFORM_TO_PLATFORMIO[platform]
options = {
u'env': config[CONF_ESPHOMEYAML][CONF_NAME], u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
u'platform': PLATFORM_TO_PLATFORMIO[config[CONF_ESPHOMEYAML][CONF_PLATFORM]], u'platform': platform,
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD], u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
u'esphomeyaml_uri': config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI],
u'build_flags': u'', u'build_flags': u'',
} }
if CONF_LOGGER in config: build_flags = set()
build_flags = get_component(CONF_LOGGER).get_build_flags(config[CONF_LOGGER]) if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]:
if build_flags: build_flags |= get_build_flags(config, 'build_flags')
d[u'build_flags'] = u'\n ' + build_flags build_flags |= get_build_flags(config, 'BUILD_FLAGS')
return INI_CONTENT_FORMAT.format(**d) build_flags.add(u"-DESPHOMEYAML_USE")
build_flags |= get_build_flags(config, 'required_build_flags')
build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS')
# avoid changing build flags order
build_flags = sorted(list(build_flags))
if build_flags:
options[u'build_flags'] = u'\n '.join(build_flags)
lib_deps = set()
lib_deps.add(config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI])
lib_deps |= get_build_flags(config, 'LIB_DEPS')
lib_deps |= get_build_flags(config, 'lib_deps')
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
lib_deps |= {
'Preferences', # Preferences helper
}
# avoid changing build flags order
lib_deps = sorted(x for x in lib_deps if x)
if lib_deps:
options[u'lib_deps'] = u'\n '.join(lib_deps)
return INI_CONTENT_FORMAT.format(**options)
def mkdir_p(path): def mkdir_p(path):
@@ -109,8 +151,8 @@ def find_begin_end(text, begin_s, end_s):
def write_platformio_ini(content, path): def write_platformio_ini(content, path):
if os.path.isfile(path): if os.path.isfile(path):
try: try:
with codecs.open(path, 'r', encoding='utf-8') as f: with codecs.open(path, 'r', encoding='utf-8') as f_handle:
text = f.read() text = f_handle.read()
except OSError: except OSError:
raise ESPHomeYAMLError(u"Could not read ini file at {}".format(path)) raise ESPHomeYAMLError(u"Could not read ini file at {}".format(path))
prev_file = text prev_file = text
@@ -120,18 +162,18 @@ def write_platformio_ini(content, path):
mkdir_p(os.path.dirname(path)) mkdir_p(os.path.dirname(path))
content_format = INI_BASE_FORMAT content_format = INI_BASE_FORMAT
full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \ full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \
content + INI_AUTO_GENERATE_END + content_format[1] content + INI_AUTO_GENERATE_END + content_format[1]
if prev_file == full_file: if prev_file == full_file:
return return
with codecs.open(path, mode='w+', encoding='utf-8') as f: with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
f.write(full_file) f_handle.write(full_file)
def write_cpp(code_s, path): def write_cpp(code_s, path):
if os.path.isfile(path): if os.path.isfile(path):
try: try:
with codecs.open(path, 'r', encoding='utf-8') as f: with codecs.open(path, 'r', encoding='utf-8') as f_handle:
text = f.read() text = f_handle.read()
except OSError: except OSError:
raise ESPHomeYAMLError(u"Could not read C++ file at {}".format(path)) raise ESPHomeYAMLError(u"Could not read C++ file at {}".format(path))
prev_file = text prev_file = text
@@ -142,8 +184,8 @@ def write_cpp(code_s, path):
code_format = CPP_BASE_FORMAT code_format = CPP_BASE_FORMAT
full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \ full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \
code_s + CPP_AUTO_GENERATE_END + code_format[1] code_s + CPP_AUTO_GENERATE_END + code_format[1]
if prev_file == full_file: if prev_file == full_file:
return return
with codecs.open(path, 'w+', encoding='utf-8') as f: with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
f.write(full_file) f_handle.write(full_file)

View File

@@ -1,14 +1,23 @@
from __future__ import print_function from __future__ import print_function
import codecs import codecs
import fnmatch
import logging import logging
import os
from collections import OrderedDict from collections import OrderedDict
import yaml import yaml
from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress, MACAddress, TimePeriod, \
TimePeriodMicroseconds, TimePeriodMilliseconds, TimePeriodSeconds
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
# Mostly copied from Home Assistant because that code works fine and
# let's not reinvent the wheel here
SECRET_YAML = u'secrets.yaml'
class NodeListClass(list): class NodeListClass(list):
"""Wrapper class to be able to add attributes on a list.""" """Wrapper class to be able to add attributes on a list."""
@@ -22,7 +31,7 @@ class NodeStrClass(unicode):
pass pass
class SafeLineLoader(yaml.SafeLoader): class SafeLineLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
"""Loader class that keeps track of line numbers.""" """Loader class that keeps track of line numbers."""
def compose_node(self, parent, index): def compose_node(self, parent, index):
@@ -72,9 +81,8 @@ def _ordered_dict(loader, node):
if key in seen: if key in seen:
fname = getattr(loader.stream, 'name', '') fname = getattr(loader.stream, 'name', '')
_LOGGER.error( raise ESPHomeYAMLError(u'YAML file {} contains duplicate key "{}". '
u'YAML file %s contains duplicate key "%s". ' u'Check lines {} and {}.'.format(fname, key, seen[key], line))
u'Check lines %d and %d.', fname, key, seen[key], line)
seen[key] = line seen[key] = line
return _add_reference(OrderedDict(nodes), loader, node) return _add_reference(OrderedDict(nodes), loader, node)
@@ -97,8 +105,107 @@ def _add_reference(obj, loader, node):
return obj return obj
def _env_var_yaml(_, node):
"""Load environment variables and embed it into the configuration YAML."""
args = node.value.split()
# Check for a default value
if len(args) > 1:
return os.getenv(args[0], u' '.join(args[1:]))
elif args[0] in os.environ:
return os.environ[args[0]]
raise ESPHomeYAMLError(u"Environment variable {} not defined.".format(node.value))
def _include_yaml(loader, node):
"""Load another YAML file and embeds it using the !include tag.
Example:
device_tracker: !include device_tracker.yaml
"""
fname = os.path.join(os.path.dirname(loader.name), node.value)
return _add_reference(load_yaml(fname), loader, node)
def _is_file_valid(name):
"""Decide if a file is valid."""
return not name.startswith(u'.')
def _find_files(directory, pattern):
"""Recursively load files in a directory."""
for root, dirs, files in os.walk(directory, topdown=True):
dirs[:] = [d for d in dirs if _is_file_valid(d)]
for basename in files:
if _is_file_valid(basename) and fnmatch.fnmatch(basename, pattern):
filename = os.path.join(root, basename)
yield filename
def _include_dir_named_yaml(loader, node):
"""Load multiple files from directory as a dictionary."""
mapping = OrderedDict() # type: OrderedDict
loc = os.path.join(os.path.dirname(loader.name), node.value)
for fname in _find_files(loc, '*.yaml'):
filename = os.path.splitext(os.path.basename(fname))[0]
mapping[filename] = load_yaml(fname)
return _add_reference(mapping, loader, node)
def _include_dir_merge_named_yaml(loader, node):
"""Load multiple files from directory as a merged dictionary."""
mapping = OrderedDict() # type: OrderedDict
loc = os.path.join(os.path.dirname(loader.name), node.value)
for fname in _find_files(loc, '*.yaml'):
if os.path.basename(fname) == SECRET_YAML:
continue
loaded_yaml = load_yaml(fname)
if isinstance(loaded_yaml, dict):
mapping.update(loaded_yaml)
return _add_reference(mapping, loader, node)
def _include_dir_list_yaml(loader, node):
"""Load multiple files from directory as a list."""
loc = os.path.join(os.path.dirname(loader.name), node.value)
return [load_yaml(f) for f in _find_files(loc, '*.yaml')
if os.path.basename(f) != SECRET_YAML]
def _include_dir_merge_list_yaml(loader, node):
"""Load multiple files from directory as a merged list."""
path = os.path.join(os.path.dirname(loader.name), node.value)
merged_list = []
for fname in _find_files(path, '*.yaml'):
if os.path.basename(fname) == SECRET_YAML:
continue
loaded_yaml = load_yaml(fname)
if isinstance(loaded_yaml, list):
merged_list.extend(loaded_yaml)
return _add_reference(merged_list, loader, node)
# pylint: disable=protected-access
def _secret_yaml(loader, node):
"""Load secrets and embed it into the configuration YAML."""
secret_path = os.path.join(os.path.dirname(loader.name), SECRET_YAML)
secrets = load_yaml(secret_path)
if node.value not in secrets:
raise ESPHomeYAMLError(u"Secret {} not defined".format(node.value))
return secrets[node.value]
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict) yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict)
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq) yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
yaml.SafeLoader.add_constructor('!env_var', _env_var_yaml)
yaml.SafeLoader.add_constructor('!secret', _secret_yaml)
yaml.SafeLoader.add_constructor('!include', _include_yaml)
yaml.SafeLoader.add_constructor('!include_dir_list', _include_dir_list_yaml)
yaml.SafeLoader.add_constructor('!include_dir_merge_list',
_include_dir_merge_list_yaml)
yaml.SafeLoader.add_constructor('!include_dir_named', _include_dir_named_yaml)
yaml.SafeLoader.add_constructor('!include_dir_merge_named',
_include_dir_merge_named_yaml)
# From: https://gist.github.com/miracle2k/3184458 # From: https://gist.github.com/miracle2k/3184458
@@ -129,21 +236,40 @@ def represent_odict(dump, tag, mapping, flow_style=None):
return node return node
def unicode_representer(dumper, uni): def unicode_representer(_, uni):
node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=uni) node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=uni)
return node return node
def hex_int_representer(dumper, data): def hex_int_representer(_, data):
node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:int', value=str(data)) node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:int', value=str(data))
return node return node
def ipaddress_representer(dumper, data): def stringify_representer(_, data):
node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=str(data)) node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=str(data))
return node return node
TIME_PERIOD_UNIT_MAP = {
'microseconds': 'us',
'milliseconds': 'ms',
'seconds': 's',
'minutes': 'min',
'hours': 'h',
'days': 'd',
}
def represent_time_period(dumper, data):
dictionary = data.as_dict()
if len(dictionary) == 1:
unit, value = dictionary.popitem()
out = '{}{}'.format(value, TIME_PERIOD_UNIT_MAP[unit])
return yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=out)
return represent_odict(dumper, 'tag:yaml.org,2002:map', dictionary)
yaml.SafeDumper.add_representer( yaml.SafeDumper.add_representer(
OrderedDict, OrderedDict,
lambda dumper, value: lambda dumper, value:
@@ -158,4 +284,9 @@ yaml.SafeDumper.add_representer(
yaml.SafeDumper.add_representer(unicode, unicode_representer) yaml.SafeDumper.add_representer(unicode, unicode_representer)
yaml.SafeDumper.add_representer(HexInt, hex_int_representer) yaml.SafeDumper.add_representer(HexInt, hex_int_representer)
yaml.SafeDumper.add_representer(IPAddress, ipaddress_representer) yaml.SafeDumper.add_representer(IPAddress, stringify_representer)
yaml.SafeDumper.add_representer(MACAddress, stringify_representer)
yaml.SafeDumper.add_representer(TimePeriod, represent_time_period)
yaml.SafeDumper.add_representer(TimePeriodMicroseconds, represent_time_period)
yaml.SafeDumper.add_representer(TimePeriodMilliseconds, represent_time_period)
yaml.SafeDumper.add_representer(TimePeriodSeconds, represent_time_period)

22
pylintrc Normal file
View File

@@ -0,0 +1,22 @@
[MASTER]
reports=no
disable=
missing-docstring,
fixme,
unused-argument,
global-statement,
too-few-public-methods,
too-many-locals,
too-many-ancestors,
too-many-branches,
too-many-statements,
too-many-arguments,
too-many-return-statements,
duplicate-code,
additional-builtins=
unicode,
long,
raw_input

View File

@@ -1,2 +1,6 @@
[metadata] [metadata]
description-file = README.md description-file = README.md
[flake8]
max-line-length = 120
builtins = unicode, long, raw_input