mirror of
https://github.com/esphome/esphome.git
synced 2025-11-01 15:41:52 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f18f8444c7 | ||
|
|
10607f2a51 | ||
|
|
d3ac5bfb27 | ||
|
|
4b9bb2b731 | ||
|
|
8639eb1b27 | ||
|
|
9979ee6ddf | ||
|
|
ee502a7aaa | ||
|
|
dc516f7537 | ||
|
|
44f2b582b5 | ||
|
|
262855ff62 | ||
|
|
eec163644d | ||
|
|
e65a4d50e5 | ||
|
|
a88aad2179 | ||
|
|
49736c8c6d | ||
|
|
7915e420f4 | ||
|
|
595aa5e92d | ||
|
|
ef1aa16627 | ||
|
|
3540b3fbb0 | ||
|
|
ac5ab33975 | ||
|
|
633d20d023 | ||
|
|
9e5548324b | ||
|
|
c31c5c4041 | ||
|
|
34605f19ee | ||
|
|
58e1b8454d | ||
|
|
0ab63dc4d4 | ||
|
|
51c856e65e | ||
|
|
de766a0100 | ||
|
|
d5b4971d81 | ||
|
|
8195981bc6 | ||
|
|
7f09d4f23d |
10
.travis.yml
Normal file
10
.travis.yml
Normal 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
|
||||
@@ -11,13 +11,8 @@ RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY docker/platformio.ini /usr/src/app/
|
||||
RUN platformio settings set enable_telemetry No && \
|
||||
platformio lib --global install esphomelib && \
|
||||
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 . .
|
||||
RUN pip install -e .
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# esphomeyaml for [esphomelib](https://github.com/OttoWinter/esphomelib)
|
||||
|
||||
Getting Started Guide: https://esphomlib.com/esphomeyaml/getting-started.html
|
||||
Available Components: https://esphomelib.com/esphomeyaml/index.html
|
||||
### Getting Started Guide: https://esphomelib.com/esphomeyaml/getting-started.html
|
||||
|
||||
### Available Components: https://esphomelib.com/esphomeyaml/index.html
|
||||
|
||||
esphomeyaml is the solution for your ESP8266/ESP32 projects with Home Assistant. It allows you to create **custom firmwares** for your microcontrollers with no programming experience required. All you need to know is the YAML configuration format which is also used by Home Assistant.
|
||||
|
||||
|
||||
@@ -6,19 +6,16 @@ import os
|
||||
import random
|
||||
import sys
|
||||
|
||||
from esphomeyaml import helpers, mqtt, writer, yaml_util, wizard
|
||||
from esphomeyaml.config import add_component_task, read_config
|
||||
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_MANUAL_IP, CONF_NAME, \
|
||||
CONF_STATIC_IP, \
|
||||
CONF_WIFI, CONF_LOGGER, CONF_BAUD_RATE
|
||||
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, \
|
||||
get_variable, indent, quote, statement
|
||||
from esphomeyaml import core, mqtt, wizard, writer, yaml_util, const
|
||||
from esphomeyaml.config import core_to_code, get_component, iter_components, read_config
|
||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_LOGGER, \
|
||||
CONF_MANUAL_IP, CONF_NAME, CONF_STATIC_IP, CONF_WIFI
|
||||
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, add_task, \
|
||||
color, get_variable, indent, quote, statement, Expression
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'i2c']
|
||||
|
||||
CONFIG_PATH = None
|
||||
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ota', 'mqtt', 'web_server', 'i2c']
|
||||
|
||||
|
||||
def get_name(config):
|
||||
@@ -26,7 +23,7 @@ def get_name(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():
|
||||
@@ -36,21 +33,42 @@ def discover_serial_ports():
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
result = None
|
||||
for p, d, h in comports():
|
||||
if not p:
|
||||
result = []
|
||||
descs = []
|
||||
for port, desc, info in comports():
|
||||
if not port:
|
||||
continue
|
||||
if "VID:PID" in h:
|
||||
if result is not None:
|
||||
return None
|
||||
result = p
|
||||
if "VID:PID" in info:
|
||||
result.append(port)
|
||||
descs.append(desc)
|
||||
|
||||
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 mock_exit(rc):
|
||||
raise SystemExit(rc)
|
||||
def mock_exit(return_code):
|
||||
raise SystemExit(return_code)
|
||||
|
||||
orig_argv = sys.argv
|
||||
orig_exit = sys.exit # mock sys.exit
|
||||
@@ -63,10 +81,10 @@ def run_platformio(*cmd):
|
||||
return platformio.__main__.main()
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
except SystemExit as e:
|
||||
return e.args[0]
|
||||
except Exception as e:
|
||||
_LOGGER.error(u"Running platformio failed: %s", e)
|
||||
except SystemExit as err:
|
||||
return err.args[0]
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
_LOGGER.error(u"Running platformio failed: %s", err)
|
||||
_LOGGER.error(u"Please try running %s locally.", full_cmd)
|
||||
finally:
|
||||
sys.argv = orig_argv
|
||||
@@ -84,18 +102,24 @@ def run_miniterm(config, port):
|
||||
|
||||
def write_cpp(config):
|
||||
_LOGGER.info("Generating C++ source...")
|
||||
|
||||
add_task(core_to_code, config[CONF_ESPHOMEYAML])
|
||||
for domain in PRE_INITIALIZE:
|
||||
if domain == CONF_ESPHOMEYAML:
|
||||
continue
|
||||
if domain in config:
|
||||
add_component_task(domain, config[domain])
|
||||
add_task(get_component(domain).to_code, config[domain])
|
||||
|
||||
# Clear queue
|
||||
get_variable(None)
|
||||
add(RawStatement(''))
|
||||
|
||||
for domain, conf in config.iteritems():
|
||||
for domain, component, conf in iter_components(config):
|
||||
if domain in PRE_INITIALIZE:
|
||||
continue
|
||||
add_component_task(domain, conf)
|
||||
if not hasattr(component, 'to_code'):
|
||||
continue
|
||||
add_task(component.to_code, conf)
|
||||
|
||||
# Clear queue
|
||||
get_variable(None)
|
||||
@@ -104,15 +128,18 @@ def write_cpp(config):
|
||||
|
||||
all_code = []
|
||||
for exp in _EXPRESSIONS:
|
||||
if helpers.SIMPLIFY and isinstance(exp, AssignmentExpression) and exp.obj.usages == 0:
|
||||
exp = exp.rhs
|
||||
if core.SIMPLIFY:
|
||||
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)))
|
||||
|
||||
platformio_ini_s = writer.get_ini_content(config)
|
||||
ini_path = os.path.join(get_base_path(config), 'platformio.ini')
|
||||
writer.write_platformio_ini(platformio_ini_s, ini_path)
|
||||
|
||||
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')
|
||||
writer.write_cpp(code_s, cpp_path)
|
||||
return 0
|
||||
@@ -125,19 +152,14 @@ def compile_program(config):
|
||||
|
||||
def upload_program(config, args, port):
|
||||
_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:
|
||||
_LOGGER.info("Serial device discovered, using it for upload")
|
||||
return run_platformio('platformio', 'run', '-d', get_base_path(config),
|
||||
'-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]:
|
||||
host = str(config[CONF_WIFI][CONF_MANUAL_IP][CONF_STATIC_IP])
|
||||
elif CONF_HOSTNAME in config[CONF_WIFI]:
|
||||
@@ -160,7 +182,7 @@ def upload_program(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)
|
||||
return 0
|
||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
|
||||
@@ -197,25 +219,20 @@ def setup_log():
|
||||
|
||||
|
||||
def main():
|
||||
global CONFIG_PATH
|
||||
|
||||
setup_log()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='esphomeyaml')
|
||||
parser.add_argument('configuration', help='Your YAML configuration file.')
|
||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
||||
subparsers.required = True
|
||||
parser_config = subparsers.add_parser('config',
|
||||
help='Validate the configuration and spit it out.')
|
||||
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
||||
|
||||
parser_compile = subparsers.add_parser('compile',
|
||||
help='Read the configuration and compile a program.')
|
||||
subparsers.add_parser('compile', help='Read the configuration and compile a program.')
|
||||
|
||||
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
||||
'and upload the latest binary.')
|
||||
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUAR.",
|
||||
nargs='?', const='HELLO')
|
||||
"For example /dev/cu.SLAB_USBtoUART.")
|
||||
parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
|
||||
|
||||
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('--password', help='Manually set the password.')
|
||||
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, '
|
||||
'upload it, and start MQTT logs.')
|
||||
parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUAR.",
|
||||
nargs='?', const='HELLO')
|
||||
"For example /dev/cu.SLAB_USBtoUART.")
|
||||
parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
|
||||
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
|
||||
action='store_true')
|
||||
@@ -245,21 +263,26 @@ def main():
|
||||
parser_clean.add_argument('--password', help='Manually set the password.')
|
||||
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 "
|
||||
"you through setting up esphomeyaml.")
|
||||
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
|
||||
"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()
|
||||
|
||||
if args.command == 'wizard':
|
||||
return wizard.wizard(args.configuration)
|
||||
|
||||
CONFIG_PATH = args.configuration
|
||||
config = read_config(CONFIG_PATH)
|
||||
core.CONFIG_PATH = args.configuration
|
||||
|
||||
config = read_config(core.CONFIG_PATH)
|
||||
if config is None:
|
||||
return 1
|
||||
|
||||
if args.command == 'config':
|
||||
print(yaml_util.dump(config))
|
||||
return 0
|
||||
elif args.command == 'compile':
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
@@ -270,17 +293,19 @@ def main():
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
return 0
|
||||
elif args.command == 'upload':
|
||||
port = discover_serial_ports()
|
||||
port = args.upload_port or discover_serial_ports()
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
return 0
|
||||
elif args.command == 'logs':
|
||||
port = discover_serial_ports()
|
||||
port = args.serial_port or discover_serial_ports()
|
||||
return show_logs(config, args, port)
|
||||
elif args.command == 'clean-mqtt':
|
||||
return clean_mqtt(config, args)
|
||||
elif args.command == 'mqtt-fingerprint':
|
||||
return mqtt.get_fingerprint(config)
|
||||
elif args.command == 'run':
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
@@ -289,17 +314,19 @@ def main():
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
if args.no_logs:
|
||||
return
|
||||
port = discover_serial_ports()
|
||||
port = args.upload_port or discover_serial_ports()
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
if args.no_logs:
|
||||
return 0
|
||||
return show_logs(config, args, port)
|
||||
else:
|
||||
print(u"Unknown command {}".format(args.command))
|
||||
return 1
|
||||
elif args.command == 'version':
|
||||
print(u"Version: {}".format(const.__version__))
|
||||
return 0
|
||||
print(u"Unknown command {}".format(args.command))
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,27 +2,19 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
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']
|
||||
|
||||
ADS1115_COMPONENT_CLASS = 'sensor::ADS1115Component'
|
||||
|
||||
RATES = {
|
||||
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',
|
||||
}
|
||||
RATE_REMOVE_MESSAGE = """The rate option has been removed in 1.5.0 and is no longer required."""
|
||||
|
||||
ADS1115_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('ads1115'): cv.register_variable_id,
|
||||
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])
|
||||
@@ -30,8 +22,8 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [ADS1115_SCHEMA])
|
||||
|
||||
def to_code(config):
|
||||
for conf in config:
|
||||
address = HexIntLiteral(conf[CONF_ADDRESS])
|
||||
rhs = App.make_ads1115_component(address)
|
||||
ads1115 = Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
if CONF_RATE in conf:
|
||||
add(ads1115.set_rate(RawExpression(RATES[conf[CONF_RATE]])))
|
||||
rhs = App.make_ads1115_component(conf[CONF_ADDRESS])
|
||||
Pvariable(ADS1115_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED
|
||||
from esphomeyaml.helpers import add, setup_mqtt_component
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
})
|
||||
from esphomeyaml.const import CONF_DEVICE_CLASS, CONF_INVERTED, CONF_MQTT_ID
|
||||
from esphomeyaml.helpers import add, setup_mqtt_component, App, Pvariable
|
||||
|
||||
DEVICE_CLASSES = [
|
||||
'', '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))
|
||||
|
||||
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.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]))
|
||||
if CONF_INVERTED in config:
|
||||
add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
|
||||
|
||||
def setup_mqtt_binary_sensor(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'
|
||||
|
||||
44
esphomeyaml/components/binary_sensor/esp32_ble.py
Normal file
44
esphomeyaml/components/binary_sensor/esp32_ble.py
Normal 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'
|
||||
49
esphomeyaml/components/binary_sensor/esp32_touch.py
Normal file
49
esphomeyaml/components/binary_sensor/esp32_touch.py
Normal 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'
|
||||
@@ -3,7 +3,7 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_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
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
@@ -13,9 +13,12 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_gpio_binary_sensor(exp_gpio_input_pin(config[CONF_PIN]),
|
||||
config[CONF_NAME], config.get(CONF_DEVICE_CLASS))
|
||||
gpio = variable('Application::SimpleBinarySensor', config[CONF_ID], rhs)
|
||||
rhs = App.make_gpio_binary_sensor(config[CONF_NAME], exp_gpio_input_pin(config[CONF_PIN]))
|
||||
gpio = variable('Application::MakeGPIOBinarySensor', config[CONF_ID], rhs)
|
||||
if CONF_INVERTED in config:
|
||||
add(gpio.Pgpio.set_inverted(config[CONF_INVERTED]))
|
||||
binary_sensor.setup_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'
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import binary_sensor
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME
|
||||
from esphomeyaml.helpers import App, Pvariable
|
||||
from esphomeyaml.helpers import App, variable
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('status_binary_sensor'): cv.register_variable_id,
|
||||
@@ -10,5 +12,9 @@ PLATFORM_SCHEMA = binary_sensor.PLATFORM_SCHEMA.extend({
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_status_binary_sensor(config[CONF_NAME])
|
||||
gpio = Pvariable('binary_sensor::MQTTBinarySensorComponent', config[CONF_ID], rhs)
|
||||
binary_sensor.setup_mqtt_binary_sensor(gpio.Pmqtt, config)
|
||||
status = variable('Application::MakeStatusBinarySensor', config[CONF_ID], rhs)
|
||||
binary_sensor.setup_binary_sensor(status.Pstatus, config)
|
||||
binary_sensor.setup_mqtt_binary_sensor(status.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_STATUS_BINARY_SENSOR'
|
||||
|
||||
@@ -10,7 +10,7 @@ DALLAS_COMPONENT_CLASS = 'sensor::DallasComponent'
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
||||
cv.GenerateID('dallas'): cv.register_variable_id,
|
||||
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:
|
||||
rhs = App.make_dallas_component(conf[CONF_PIN], conf.get(CONF_UPDATE_INTERVAL))
|
||||
Pvariable(DALLAS_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
|
||||
|
||||
14
esphomeyaml/components/debug.py
Normal file
14
esphomeyaml/components/debug.py
Normal 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'
|
||||
41
esphomeyaml/components/deep_sleep.py
Normal file
41
esphomeyaml/components/deep_sleep.py
Normal 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'
|
||||
22
esphomeyaml/components/esp32_ble.py
Normal file
22
esphomeyaml/components/esp32_ble.py
Normal 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'
|
||||
83
esphomeyaml/components/esp32_touch.py
Normal file
83
esphomeyaml/components/esp32_touch.py
Normal 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'
|
||||
@@ -21,3 +21,6 @@ def setup_mqtt_fan(obj, config):
|
||||
if CONF_SPEED_COMMAND_TOPIC in config:
|
||||
add(obj.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
|
||||
setup_mqtt_component(obj, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_FAN'
|
||||
|
||||
@@ -15,7 +15,7 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
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))
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
|
||||
@@ -24,7 +24,7 @@ PLATFORM_SCHEMA = fan.PLATFORM_SCHEMA.extend({
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
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:
|
||||
speeds = config[CONF_SPEED]
|
||||
add(fan_struct.Poutput.set_speed(output, 0.0,
|
||||
|
||||
@@ -2,15 +2,29 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA
|
||||
from esphomeyaml.helpers import App, add
|
||||
from esphomeyaml.const import CONF_FREQUENCY, CONF_SCL, CONF_SDA, CONF_SCAN, CONF_ID, \
|
||||
CONF_RECEIVE_TIMEOUT
|
||||
from esphomeyaml.helpers import App, add, Pvariable
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID('i2c'): cv.register_variable_id,
|
||||
vol.Required(CONF_SDA, default='SDA'): 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):
|
||||
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'
|
||||
|
||||
@@ -2,17 +2,16 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, 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
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
IR_TRANSMITTER_COMPONENT_CLASS = 'switch_::IRTransmitterComponent'
|
||||
|
||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
||||
cv.GenerateID('ir_transmitter'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
|
||||
vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(vol.Coerce(int), vol.Range(min=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])
|
||||
rhs = App.make_ir_transmitter(pin, conf.get(CONF_CARRIER_DUTY_PERCENT))
|
||||
Pvariable(IR_TRANSMITTER_COMPONENT_CLASS, conf[CONF_ID], rhs)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_IR_TRANSMITTER'
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH
|
||||
from esphomeyaml.helpers import add, setup_mqtt_component
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT
|
||||
from esphomeyaml.helpers import add
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
}).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:
|
||||
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'
|
||||
|
||||
@@ -4,7 +4,7 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_ID, CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, get_variable, variable
|
||||
from esphomeyaml.helpers import App, get_variable, variable, setup_mqtt_component
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('binary_light'): cv.register_variable_id,
|
||||
@@ -15,5 +15,6 @@ PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
def to_code(config):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
rhs = App.make_binary_light(config[CONF_NAME], output)
|
||||
light_struct = variable('Application::LightStruct', config[CONF_ID], rhs)
|
||||
light.setup_mqtt_light_component(light_struct.Pmqtt, config)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
|
||||
88
esphomeyaml/components/light/fastled_clockless.py
Normal file
88
esphomeyaml/components/light/fastled_clockless.py
Normal 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'
|
||||
69
esphomeyaml/components/light/fastled_spi.py
Normal file
69
esphomeyaml/components/light/fastled_spi.py
Normal 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'
|
||||
@@ -4,20 +4,19 @@ import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, \
|
||||
CONF_NAME, CONF_OUTPUT
|
||||
from esphomeyaml.helpers import App, add, get_variable, variable
|
||||
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('monochromatic_light'): cv.register_variable_id,
|
||||
vol.Required(CONF_OUTPUT): cv.variable_id,
|
||||
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):
|
||||
output = get_variable(config[CONF_OUTPUT])
|
||||
rhs = App.make_monochromatic_light(config[CONF_NAME], output)
|
||||
light_struct = variable('Application::LightStruct', config[CONF_ID], rhs)
|
||||
if CONF_GAMMA_CORRECT in config:
|
||||
add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
|
||||
light.setup_mqtt_light_component(light_struct.Pmqtt, config)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
|
||||
@@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED
|
||||
from esphomeyaml.helpers import App, add, get_variable, variable
|
||||
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
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_BLUE): cv.variable_id,
|
||||
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])
|
||||
blue = get_variable(config[CONF_BLUE])
|
||||
rhs = App.make_rgb_light(config[CONF_NAME], red, green, blue)
|
||||
light_struct = variable('Application::LightStruct', config[CONF_ID], rhs)
|
||||
if CONF_GAMMA_CORRECT in config:
|
||||
add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
|
||||
light.setup_mqtt_light_component(light_struct.Pmqtt, config)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
|
||||
@@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import light
|
||||
from esphomeyaml.const import CONF_BLUE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, \
|
||||
CONF_GREEN, CONF_ID, CONF_NAME, CONF_RED, CONF_WHITE
|
||||
from esphomeyaml.helpers import App, get_variable, variable, add
|
||||
from esphomeyaml.helpers import App, get_variable, setup_mqtt_component, variable
|
||||
|
||||
PLATFORM_SCHEMA = light.PLATFORM_SCHEMA.extend({
|
||||
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_WHITE): cv.variable_id,
|
||||
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])
|
||||
white = get_variable(config[CONF_WHITE])
|
||||
rhs = App.make_rgbw_light(config[CONF_NAME], red, green, blue, white)
|
||||
light_struct = variable('Application::LightStruct', config[CONF_ID], rhs)
|
||||
if CONF_GAMMA_CORRECT in config:
|
||||
add(light_struct.Poutput.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
|
||||
light.setup_mqtt_light_component(light_struct.Pmqtt, config)
|
||||
light_struct = variable('Application::MakeLight', config[CONF_ID], rhs)
|
||||
setup_mqtt_component(light_struct.Pmqtt, config)
|
||||
light.setup_light_component(light_struct.Pstate, config)
|
||||
|
||||
@@ -2,18 +2,18 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
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.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))
|
||||
|
||||
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_LOGGER): cv.register_variable_id,
|
||||
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_LEVEL): is_log_level,
|
||||
vol.Optional(CONF_LOGS): vol.Schema({
|
||||
@@ -31,16 +31,7 @@ def exp_log_level(level):
|
||||
|
||||
|
||||
def to_code(config):
|
||||
baud_rate = 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)
|
||||
rhs = App.init_log(config.get(CONF_BAUD_RATE))
|
||||
log = Pvariable(u'LogComponent', config[CONF_ID], rhs)
|
||||
if CONF_TX_BUFFER_SIZE in config:
|
||||
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)))
|
||||
|
||||
|
||||
def get_build_flags(config):
|
||||
def required_build_flags(config):
|
||||
if CONF_LEVEL in config:
|
||||
return u'-DESPHOMELIB_LOG_LEVEL={}'.format(esphomelib_log_level(config[CONF_LEVEL]))
|
||||
return u''
|
||||
return None
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_DISCOVERY, \
|
||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_MQTT, CONF_PASSWORD, \
|
||||
CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME, \
|
||||
CONF_WILL_MESSAGE, CONF_CLIENT_ID
|
||||
from esphomeyaml.helpers import App, Pvariable, StructInitializer, add, exp_empty_optional
|
||||
from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, CONF_DISCOVERY, \
|
||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_SSL_FINGERPRINTS, CONF_ID, CONF_LOG_TOPIC, \
|
||||
CONF_MQTT, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, CONF_RETAIN, CONF_TOPIC, \
|
||||
CONF_TOPIC_PREFIX, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_KEEPALIVE
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, StructInitializer, add, \
|
||||
exp_empty_optional, RawExpression
|
||||
|
||||
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_PAYLOAD): cv.mqtt_payload,
|
||||
vol.Optional(CONF_QOS, default=0): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
|
||||
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,13 +33,22 @@ def validate_broker(value):
|
||||
value = cv.string_strict(value)
|
||||
if value.endswith(u'.local'):
|
||||
raise vol.Invalid(u"MQTT server addresses ending with '.local' are currently unsupported."
|
||||
u" Please specify the static IP instead.")
|
||||
u" Please use the static IP instead.")
|
||||
if u':' in value:
|
||||
raise vol.Invalid(u"Please specify the port using the port: option")
|
||||
if not value:
|
||||
raise vol.Invalid(u"Broker cannot be empty")
|
||||
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,
|
||||
vol.Required(CONF_BROKER): validate_broker,
|
||||
vol.Optional(CONF_PORT, default=1883): cv.port,
|
||||
@@ -35,9 +58,13 @@ CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
||||
vol.Optional(CONF_DISCOVERY): cv.boolean,
|
||||
vol.Optional(CONF_DISCOVERY_RETAIN): cv.boolean,
|
||||
vol.Optional(CONF_DISCOVERY_PREFIX): cv.publish_topic,
|
||||
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
|
||||
vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
|
||||
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
|
||||
vol.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
@@ -47,7 +74,7 @@ def exp_mqtt_message(config):
|
||||
exp = StructInitializer(
|
||||
'mqtt::MQTTMessage',
|
||||
('topic', config[CONF_TOPIC]),
|
||||
('payload', config[CONF_PAYLOAD]),
|
||||
('payload', config.get(CONF_PAYLOAD, "")),
|
||||
('qos', config[CONF_QOS]),
|
||||
('retain', config[CONF_RETAIN])
|
||||
)
|
||||
@@ -64,11 +91,37 @@ def to_code(config):
|
||||
discovery_retain = config.get(CONF_DISCOVERY_RETAIN, True)
|
||||
discovery_prefix = config.get(CONF_DISCOVERY_PREFIX, 'homeassistant')
|
||||
add(mqtt.set_discovery_info(discovery_prefix, discovery_retain))
|
||||
if CONF_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:
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -4,14 +4,15 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
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, \
|
||||
ESP_PLATFORM_ESP8266, ESP_PLATFORM_ESP32
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, Pvariable, add
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
cv.GenerateID(CONF_OTA): cv.register_variable_id,
|
||||
vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
|
||||
# TODO Num attempts + wait time
|
||||
@@ -24,8 +25,8 @@ def to_code(config):
|
||||
rhs = App.init_ota()
|
||||
ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
|
||||
if CONF_PASSWORD in config:
|
||||
h = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
||||
add(ota.set_auth_password_hash(h))
|
||||
hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
||||
add(ota.set_auth_password_hash(hash_))
|
||||
if config[CONF_SAFE_MODE]:
|
||||
add(ota.start_safe_mode())
|
||||
|
||||
@@ -33,12 +34,21 @@ def to_code(config):
|
||||
def get_port(config):
|
||||
if CONF_PORT in config[CONF_OTA]:
|
||||
return config[CONF_OTA][CONF_PORT]
|
||||
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
return 3232
|
||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
return 8266
|
||||
raise ESPHomeYAMLError(u"Invalid ESP Platform for ESP OTA port.")
|
||||
|
||||
|
||||
def get_auth(config):
|
||||
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']
|
||||
|
||||
@@ -23,3 +23,6 @@ def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
add(obj.set_power_supply(power_supply))
|
||||
if CONF_MAX_POWER in config:
|
||||
add(obj.set_max_power(config[CONF_MAX_POWER]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT'
|
||||
|
||||
@@ -2,23 +2,30 @@ import voluptuous as vol
|
||||
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import output
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.const import CONF_ID, CONF_PIN, ESP_PLATFORM_ESP8266
|
||||
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]
|
||||
|
||||
|
||||
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({
|
||||
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):
|
||||
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])
|
||||
rhs = App.make_esp8266_pwm_output(pin)
|
||||
gpio = Pvariable('output::ESP8266PWMOutput', config[CONF_ID], rhs)
|
||||
output.setup_output_platform(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT'
|
||||
|
||||
@@ -15,3 +15,6 @@ def to_code(config):
|
||||
rhs = App.make_gpio_output(pin)
|
||||
gpio = Pvariable('output::GPIOBinaryOutputComponent', config[CONF_ID], rhs)
|
||||
output.setup_output_platform(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_OUTPUT'
|
||||
|
||||
@@ -36,3 +36,6 @@ def to_code(config):
|
||||
if CONF_CHANNEL in config:
|
||||
add(ledc.set_channel(config[CONF_CHANNEL]))
|
||||
output.setup_output_platform(ledc, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_LEDC_OUTPUT'
|
||||
|
||||
@@ -23,3 +23,6 @@ def to_code(config):
|
||||
rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
|
||||
out = Pvariable('output::PCA9685OutputComponent::Channel', config[CONF_ID], rhs)
|
||||
output.setup_output_platform(out, config, skip_power_supply=True)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
|
||||
|
||||
@@ -10,12 +10,16 @@ PHASE_BALANCERS = ['None', 'Linear', 'Weaved']
|
||||
|
||||
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({
|
||||
cv.GenerateID('pca9685'): cv.register_variable_id,
|
||||
vol.Required(CONF_FREQUENCY): vol.All(cv.frequency,
|
||||
vol.Range(min=24, max=1526)),
|
||||
vol.Optional(CONF_PHASE_BALANCER): vol.All(vol.Title, vol.Any(*PHASE_BALANCERS)),
|
||||
vol.Range(min=23.84, max=1525.88)),
|
||||
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])
|
||||
@@ -31,3 +35,6 @@ def to_code(config):
|
||||
phase_balancer = RawExpression(u'PCA9685_PhaseBalancer_{}'.format(
|
||||
conf[CONF_PHASE_BALANCER]))
|
||||
add(pca9685.set_phase_balancer(phase_balancer))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
|
||||
|
||||
24
esphomeyaml/components/pcf8574.py
Normal file
24
esphomeyaml/components/pcf8574.py
Normal 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'
|
||||
@@ -7,8 +7,8 @@ from esphomeyaml.helpers import App, Pvariable, add, exp_gpio_output_pin
|
||||
|
||||
POWER_SUPPLY_SCHEMA = cv.REQUIRED_ID_SCHEMA.extend({
|
||||
vol.Required(CONF_PIN): pins.GPIO_OUTPUT_PIN_SCHEMA,
|
||||
vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period,
|
||||
vol.Optional(CONF_KEEP_ON_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_milliseconds,
|
||||
})
|
||||
|
||||
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]))
|
||||
if CONF_KEEP_ON_TIME in conf:
|
||||
add(psu.set_keep_on_time(conf[CONF_KEEP_ON_TIME]))
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT'
|
||||
|
||||
@@ -3,8 +3,8 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
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_ID, CONF_LAMBDA, CONF_MULTIPLY, CONF_NAME, CONF_OFFSET, CONF_SEND_EVERY, \
|
||||
CONF_SLIDING_WINDOW_MOVING_AVERAGE, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE
|
||||
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_ID
|
||||
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add, \
|
||||
setup_mqtt_component
|
||||
|
||||
@@ -13,7 +13,6 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
})
|
||||
|
||||
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_MULTIPLY): 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_ICON): cv.icon,
|
||||
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
|
||||
})
|
||||
|
||||
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')
|
||||
MultiplyFilter = MockObj('new sensor::MultiplyFilter')
|
||||
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
|
||||
@@ -71,30 +71,37 @@ def setup_filter(config):
|
||||
conf = config[CONF_EXPONENTIAL_MOVING_AVERAGE]
|
||||
return ExponentialMovingAverageFilter(conf[CONF_ALPHA], conf[CONF_SEND_EVERY])
|
||||
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))
|
||||
raise ValueError("Filter unsupported: {}".format(config))
|
||||
raise ValueError(u"Filter unsupported: {}".format(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:
|
||||
add(obj.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||
if CONF_ICON in config:
|
||||
add(obj.set_icon(config[CONF_ICON]))
|
||||
if CONF_ACCURACY_DECIMALS in config:
|
||||
add(obj.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
|
||||
if CONF_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:
|
||||
filters = [setup_filter(x) for x in config[CONF_FILTERS]]
|
||||
add(obj.set_filters(ArrayInitializer(*filters)))
|
||||
setup_mqtt_component(obj, config)
|
||||
|
||||
|
||||
def make_mqtt_sensor_for(exp, config):
|
||||
rhs = App.make_mqtt_sensor_for(exp, config[CONF_NAME])
|
||||
mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_ID], rhs)
|
||||
def register_sensor(var, config):
|
||||
setup_sensor(var, config)
|
||||
rhs = App.register_sensor(var)
|
||||
mqtt_sensor = Pvariable('sensor::MQTTSensorComponent', config[CONF_MQTT_ID], rhs)
|
||||
setup_mqtt_sensor_component(mqtt_sensor, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SENSOR'
|
||||
|
||||
@@ -20,16 +20,20 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('adc'): cv.register_variable_id,
|
||||
vol.Required(CONF_PIN): pins.analog_pin,
|
||||
vol.Optional(CONF_ATTENUATION): vol.All(cv.only_on_esp32, ATTENUATION_MODE_SCHEMA),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
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))
|
||||
make = variable('Application::MakeADCSensor', config[CONF_ID], rhs)
|
||||
adc = make.Padc
|
||||
if CONF_ATTENUATION in config:
|
||||
attenuation = ATTENUATION_MODES[config[CONF_ATTENUATION]]
|
||||
add(adc.set_attenuation(RawExpression(attenuation)))
|
||||
sensor.setup_sensor(adc, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADC_SENSOR'
|
||||
|
||||
@@ -2,29 +2,30 @@ import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import get_variable, RawExpression
|
||||
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \
|
||||
CONF_NAME, CONF_ID
|
||||
from esphomeyaml.helpers import RawExpression, get_variable, Pvariable
|
||||
|
||||
DEPENDENCIES = ['ads1115']
|
||||
|
||||
MUX = {
|
||||
'A0_A1': 'ADS1115_MUX_P0_N1',
|
||||
'A0_A3': 'ADS1115_MUX_P0_N3',
|
||||
'A1_A3': 'ADS1115_MUX_P1_N3',
|
||||
'A2_A3': 'ADS1115_MUX_P2_N3',
|
||||
'A0_GND': 'ADS1115_MUX_P0_NG',
|
||||
'A1_GND': 'ADS1115_MUX_P1_NG',
|
||||
'A2_GND': 'ADS1115_MUX_P2_NG',
|
||||
'A3_GND': 'ADS1115_MUX_P3_NG',
|
||||
'A0_A1': 'sensor::ADS1115_MULTIPLEXER_P0_N1',
|
||||
'A0_A3': 'sensor::ADS1115_MULTIPLEXER_P0_N3',
|
||||
'A1_A3': 'sensor::ADS1115_MULTIPLEXER_P1_N3',
|
||||
'A2_A3': 'sensor::ADS1115_MULTIPLEXER_P2_N3',
|
||||
'A0_GND': 'sensor::ADS1115_MULTIPLEXER_P0_NG',
|
||||
'A1_GND': 'sensor::ADS1115_MULTIPLEXER_P1_NG',
|
||||
'A2_GND': 'sensor::ADS1115_MULTIPLEXER_P2_NG',
|
||||
'A3_GND': 'sensor::ADS1115_MULTIPLEXER_P3_NG',
|
||||
}
|
||||
|
||||
GAIN = {
|
||||
'6.144': 'ADS1115_PGA_6P144',
|
||||
'4.096': 'ADS1115_PGA_6P096',
|
||||
'2.048': 'ADS1115_PGA_2P048',
|
||||
'1.024': 'ADS1115_PGA_1P024',
|
||||
'0.512': 'ADS1115_PGA_0P512',
|
||||
'0.256': 'ADS1115_PGA_0P256',
|
||||
'6.144': 'sensor::ADS1115_GAIN_6P144',
|
||||
'4.096': 'sensor::ADS1115_GAIN_6P096',
|
||||
'2.048': 'sensor::ADS1115_GAIN_2P048',
|
||||
'1.024': 'sensor::ADS1115_GAIN_1P024',
|
||||
'0.512': 'sensor::ADS1115_GAIN_0P512',
|
||||
'0.256': 'sensor::ADS1115_GAIN_0P256',
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +41,11 @@ def validate_gain(value):
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('ads1115_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_MULTIPLEXER): vol.All(vol.Upper, vol.Any(*list(MUX.keys()))),
|
||||
vol.Required(CONF_GAIN): validate_gain,
|
||||
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)
|
||||
|
||||
|
||||
@@ -52,5 +54,9 @@ def to_code(config):
|
||||
|
||||
mux = RawExpression(MUX[config[CONF_MULTIPLEXER]])
|
||||
gain = RawExpression(GAIN[config[CONF_GAIN]])
|
||||
sensor_ = hub.get_sensor(mux, gain, config.get(CONF_UPDATE_INTERVAL))
|
||||
sensor.make_mqtt_sensor_for(sensor_, config)
|
||||
rhs = hub.get_sensor(config[CONF_NAME], mux, gain, config.get(CONF_UPDATE_INTERVAL))
|
||||
sensor_ = Pvariable('sensor::ADS1115Sensor', config[CONF_ID], rhs)
|
||||
sensor.register_sensor(sensor_, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ADS1115_SENSOR'
|
||||
|
||||
37
esphomeyaml/components/sensor/bh1750.py
Normal file
37
esphomeyaml/components/sensor/bh1750.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_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'
|
||||
75
esphomeyaml/components/sensor/bme280.py
Normal file
75
esphomeyaml/components/sensor/bme280.py
Normal 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'
|
||||
84
esphomeyaml/components/sensor/bme680.py
Normal file
84
esphomeyaml/components/sensor/bme680.py
Normal 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'
|
||||
@@ -14,7 +14,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_PRESSURE): MQTT_SENSOR_SCHEMA,
|
||||
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],
|
||||
config[CONF_PRESSURE][CONF_NAME],
|
||||
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:
|
||||
add(bmp.Pbmp.set_address(HexIntLiteral(config[CONF_ADDRESS])))
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(bmp.Pbmp.Pget_pressure_sensor(), config[CONF_PRESSURE])
|
||||
sensor.setup_mqtt_sensor_component(bmp.Pmqtt_pressure, config[CONF_PRESSURE])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_BMP085_SENSOR'
|
||||
|
||||
@@ -3,16 +3,18 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.dallas import DALLAS_COMPONENT_CLASS
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, \
|
||||
CONF_UPDATE_INTERVAL
|
||||
from esphomeyaml.helpers import HexIntLiteral, get_variable
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \
|
||||
CONF_RESOLUTION, \
|
||||
CONF_UPDATE_INTERVAL, CONF_ID
|
||||
from esphomeyaml.helpers import HexIntLiteral, get_variable, Pvariable
|
||||
|
||||
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_INDEX, 'dallas'): cv.positive_int,
|
||||
vol.Optional(CONF_DALLAS_ID): cv.variable_id,
|
||||
vol.Optional(CONF_RESOLUTION): vol.All(vol.Coerce(int), vol.Range(min=8, max=12)),
|
||||
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema)
|
||||
}).extend(sensor.MQTT_SENSOR_ID_SCHEMA.schema), cv.has_at_least_one_key(CONF_ADDRESS, CONF_INDEX))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -23,9 +25,13 @@ def to_code(config):
|
||||
|
||||
if CONF_ADDRESS in config:
|
||||
address = HexIntLiteral(config[CONF_ADDRESS])
|
||||
sensor_ = hub.Pget_sensor_by_address(address, update_interval,
|
||||
config.get(CONF_RESOLUTION))
|
||||
rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, update_interval,
|
||||
config.get(CONF_RESOLUTION))
|
||||
else:
|
||||
sensor_ = hub.Pget_sensor_by_index(config[CONF_INDEX], update_interval,
|
||||
config.get(CONF_RESOLUTION))
|
||||
sensor.make_mqtt_sensor_for(sensor_, config)
|
||||
rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX],
|
||||
update_interval, config.get(CONF_RESOLUTION))
|
||||
sensor_ = Pvariable('sensor::DallasTemperatureSensor', config[CONF_ID], rhs)
|
||||
sensor.register_sensor(sensor_, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DALLAS_SENSOR'
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import pins
|
||||
from esphomeyaml.components import sensor
|
||||
from esphomeyaml.components.sensor import MQTT_SENSOR_SCHEMA
|
||||
from esphomeyaml.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, CONF_PIN, \
|
||||
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({
|
||||
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_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
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):
|
||||
rhs = App.make_dht_sensor(config[CONF_PIN], config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME], config.get(CONF_UPDATE_INTERVAL))
|
||||
dht = variable('Application::MakeDHTComponent', config[CONF_ID], rhs)
|
||||
pin = exp_gpio_output_pin(config[CONF_PIN])
|
||||
rhs = App.make_dht_sensor(config[CONF_TEMPERATURE][CONF_NAME],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
pin, config.get(CONF_UPDATE_INTERVAL))
|
||||
dht = variable('Application::MakeDHTSensor', config[CONF_ID], rhs)
|
||||
if CONF_MODEL in config:
|
||||
model = RawExpression('DHT::{}'.format(config[CONF_MODEL]))
|
||||
add(dht.Pdht.set_dht_model(model))
|
||||
constant = DHT_MODELS[config[CONF_MODEL]]
|
||||
add(dht.Pdht.set_dht_model(RawExpression(constant)))
|
||||
sensor.setup_sensor(dht.Pdht.Pget_temperature_sensor(), config[CONF_TEMPERATURE])
|
||||
sensor.setup_mqtt_sensor_component(dht.Pmqtt_temperature, config[CONF_TEMPERATURE])
|
||||
sensor.setup_sensor(dht.Pdht.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(dht.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_DHT_SENSOR'
|
||||
|
||||
@@ -13,7 +13,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('dht_sensor'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_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],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
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_sensor(hdc1080.Phdc1080.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(hdc1080.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HDC1080_SENSOR'
|
||||
|
||||
@@ -13,7 +13,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('htu21d'): cv.register_variable_id,
|
||||
vol.Required(CONF_TEMPERATURE): MQTT_SENSOR_SCHEMA,
|
||||
vol.Required(CONF_HUMIDITY): MQTT_SENSOR_SCHEMA,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_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],
|
||||
config[CONF_HUMIDITY][CONF_NAME],
|
||||
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_sensor(htu21d.Phtu21d.Pget_humidity_sensor(), config[CONF_HUMIDITY])
|
||||
sensor.setup_mqtt_sensor_component(htu21d.Pmqtt_humidity, config[CONF_HUMIDITY])
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_HTU21D_SENSOR'
|
||||
|
||||
73
esphomeyaml/components/sensor/mpu6050.py
Normal file
73
esphomeyaml/components/sensor/mpu6050.py
Normal 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'
|
||||
@@ -36,14 +36,14 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA,
|
||||
}),
|
||||
vol.Optional(CONF_INTERNAL_FILTER): vol.All(vol.Coerce(int), vol.Range(min=0, max=1023)),
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
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))
|
||||
make = variable('Application::MakePulseCounter', config[CONF_ID], rhs)
|
||||
make = variable('Application::MakePulseCounterSensor', config[CONF_ID], rhs)
|
||||
pcnt = make.Ppcnt
|
||||
if CONF_PULL_MODE in config:
|
||||
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)))
|
||||
if CONF_INTERNAL_FILTER in config:
|
||||
add(pcnt.set_filter(config[CONF_INTERNAL_FILTER]))
|
||||
sensor.setup_sensor(pcnt, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PULSE_COUNTER_SENSOR'
|
||||
|
||||
45
esphomeyaml/components/sensor/sht3xd.py
Normal file
45
esphomeyaml/components/sensor/sht3xd.py
Normal 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'
|
||||
58
esphomeyaml/components/sensor/tsl2561.py
Normal file
58
esphomeyaml/components/sensor/tsl2561.py
Normal 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'
|
||||
@@ -13,15 +13,15 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_TRIGGER_PIN): pins.GPIO_OUTPUT_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_TIME, 'timeout'): cv.positive_int,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_not_null_time_period,
|
||||
vol.Exclusive(CONF_TIMEOUT_TIME, 'timeout'): cv.positive_time_period_microseconds,
|
||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}).extend(sensor.MQTT_SENSOR_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
trigger = exp_gpio_output_pin(config[CONF_TRIGGER_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))
|
||||
make = variable('Application::MakeUltrasonicSensor', config[CONF_ID], rhs)
|
||||
ultrasonic = make.Pultrasonic
|
||||
@@ -29,4 +29,8 @@ def to_code(config):
|
||||
add(ultrasonic.set_timeout_us(config[CONF_TIMEOUT_TIME]))
|
||||
elif CONF_TIMEOUT_METER in config:
|
||||
add(ultrasonic.set_timeout_m(config[CONF_TIMEOUT_METER]))
|
||||
sensor.setup_sensor(ultrasonic, config)
|
||||
sensor.setup_mqtt_sensor_component(make.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ULTRASONIC_SENSOR'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
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):
|
||||
if CONF_ICON in config:
|
||||
add(obj.set_icon(config[CONF_ICON]))
|
||||
setup_mqtt_component(obj, config)
|
||||
|
||||
|
||||
def make_mqtt_switch_for(exp, config):
|
||||
rhs = App.make_mqtt_switch_for(exp, config[CONF_NAME])
|
||||
mqtt_switch = Pvariable('switch_::MQTTSwitchComponent', config[CONF_ID], rhs)
|
||||
def setup_switch(obj, config):
|
||||
if CONF_ICON in config:
|
||||
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)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_SWITCH'
|
||||
|
||||
@@ -13,6 +13,10 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_gpio_switch(exp_gpio_output_pin(config[CONF_PIN]), config[CONF_NAME])
|
||||
gpio = variable('Application::GPIOSwitchStruct', config[CONF_ID], rhs)
|
||||
rhs = App.make_gpio_switch(config[CONF_NAME], exp_gpio_output_pin(config[CONF_PIN]))
|
||||
gpio = variable('Application::MakeGPIOSwitch', config[CONF_ID], rhs)
|
||||
switch.setup_switch(gpio.Pswitch_, config)
|
||||
switch.setup_mqtt_switch(gpio.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_SWITCH'
|
||||
|
||||
@@ -3,14 +3,21 @@ import voluptuous as vol
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
from esphomeyaml.components.ir_transmitter import IR_TRANSMITTER_COMPONENT_CLASS
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_COMMAND, CONF_DATA, CONF_IR_TRANSMITTER_ID, \
|
||||
CONF_LG, CONF_NBITS, CONF_NEC, CONF_PANASONIC, CONF_REPEAT, CONF_SONY, CONF_TIMES, \
|
||||
CONF_WAIT_TIME_US, CONF_RAW, CONF_CARRIER_FREQUENCY
|
||||
from esphomeyaml.const import CONF_ADDRESS, CONF_CARRIER_FREQUENCY, CONF_COMMAND, CONF_DATA, \
|
||||
CONF_ID, CONF_IR_TRANSMITTER_ID, CONF_LG, CONF_NAME, CONF_NBITS, CONF_NEC, CONF_PANASONIC, \
|
||||
CONF_RAW, CONF_REPEAT, CONF_SONY, CONF_TIMES, CONF_WAIT_TIME
|
||||
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({
|
||||
cv.GenerateID('ir_transmitter'): cv.register_variable_id,
|
||||
DEPENDENCIES = ['ir_transmitter']
|
||||
|
||||
IR_KEYS = [CONF_NEC, CONF_LG, CONF_SONY, CONF_PANASONIC, CONF_RAW]
|
||||
|
||||
WAIT_TIME_MESSAGE = "The wait_time_us option has been renamed to wait_time in order to decrease " \
|
||||
"ambiguity. "
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(switch.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID('ir_transmitter_switch'): cv.register_variable_id,
|
||||
vol.Exclusive(CONF_NEC, 'code'): vol.Schema({
|
||||
vol.Required(CONF_ADDRESS): cv.hex_uint16_t,
|
||||
vol.Required(CONF_COMMAND): cv.hex_uint16_t,
|
||||
@@ -33,11 +40,14 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
}),
|
||||
vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({
|
||||
vol.Required(CONF_TIMES): cv.positive_not_null_int,
|
||||
vol.Required(CONF_WAIT_TIME_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,
|
||||
}).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', '::')
|
||||
|
||||
|
||||
@@ -75,7 +85,7 @@ def exp_send_data(config):
|
||||
wait_us = None
|
||||
else:
|
||||
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 = base.repeat(times, wait_us)
|
||||
return base
|
||||
@@ -84,4 +94,10 @@ def exp_send_data(config):
|
||||
def to_code(config):
|
||||
ir = get_variable(config.get(CONF_IR_TRANSMITTER_ID), IR_TRANSMITTER_COMPONENT_CLASS)
|
||||
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'
|
||||
|
||||
22
esphomeyaml/components/switch/output.py
Normal file
22
esphomeyaml/components/switch/output.py
Normal 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'
|
||||
@@ -1,7 +1,7 @@
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.components import switch
|
||||
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({
|
||||
cv.GenerateID('restart_switch'): cv.register_variable_id,
|
||||
@@ -10,5 +10,9 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
||||
|
||||
def to_code(config):
|
||||
rhs = App.make_restart_switch(config[CONF_NAME])
|
||||
mqtt = Pvariable('switch_::MQTTSwitchComponent', config[CONF_ID], rhs)
|
||||
switch.setup_mqtt_switch(mqtt, config)
|
||||
restart = variable('Application::MakeRestartSwitch', config[CONF_ID], rhs)
|
||||
switch.setup_switch(restart.Prestart, config)
|
||||
switch.setup_mqtt_switch(restart.Pmqtt, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_RESTART_SWITCH'
|
||||
|
||||
19
esphomeyaml/components/switch/shutdown.py
Normal file
19
esphomeyaml/components/switch/shutdown.py
Normal 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'
|
||||
35
esphomeyaml/components/web_server.py
Normal file
35
esphomeyaml/components/web_server.py
Normal 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 ''
|
||||
@@ -1,24 +1,50 @@
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import CONF_DNS1, CONF_DNS2, CONF_GATEWAY, CONF_HOSTNAME, CONF_ID, \
|
||||
CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, CONF_WIFI
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_AP, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_GATEWAY, \
|
||||
CONF_HOSTNAME, CONF_ID, CONF_MANUAL_IP, CONF_PASSWORD, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.helpers import App, MockObj, Pvariable, StructInitializer, add
|
||||
|
||||
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_WIFI): cv.register_variable_id,
|
||||
vol.Required(CONF_SSID): cv.ssid,
|
||||
vol.Optional(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_MANUAL_IP): vol.Schema({
|
||||
vol.Required(CONF_STATIC_IP): cv.ipv4,
|
||||
vol.Required(CONF_GATEWAY): cv.ipv4,
|
||||
vol.Required(CONF_SUBNET): cv.ipv4,
|
||||
vol.Inclusive(CONF_DNS1, 'dns'): cv.ipv4,
|
||||
vol.Inclusive(CONF_DNS2, 'dns'): cv.ipv4,
|
||||
|
||||
def validate_password(value):
|
||||
value = cv.string(value)
|
||||
if not value:
|
||||
return value
|
||||
if len(value) < 8:
|
||||
raise vol.Invalid(u"WPA password must be at least 8 characters long")
|
||||
if len(value) > 63:
|
||||
raise vol.Invalid(u"WPA password must be at most 63 characters long")
|
||||
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,
|
||||
})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
IPAddress = MockObj('IPAddress')
|
||||
|
||||
|
||||
@@ -28,19 +54,44 @@ def safe_ip(ip):
|
||||
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):
|
||||
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)
|
||||
if CONF_MANUAL_IP in config:
|
||||
manual_ip = config[CONF_MANUAL_IP]
|
||||
exp = StructInitializer(
|
||||
'ManualIP',
|
||||
('static_ip', safe_ip(manual_ip[CONF_STATIC_IP])),
|
||||
('gateway', safe_ip(manual_ip[CONF_GATEWAY])),
|
||||
('subnet', safe_ip(manual_ip[CONF_SUBNET])),
|
||||
('dns1', safe_ip(manual_ip.get(CONF_DNS1))),
|
||||
('dns2', safe_ip(manual_ip.get(CONF_DNS2))),
|
||||
)
|
||||
add(wifi.set_manual_ip(exp))
|
||||
|
||||
if sta and CONF_MANUAL_IP in config:
|
||||
add(wifi.set_sta_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
|
||||
|
||||
if ap:
|
||||
conf = config[CONF_AP]
|
||||
password = config.get(CONF_PASSWORD)
|
||||
if password is None and CONF_CHANNEL in conf:
|
||||
password = u""
|
||||
add(wifi.set_ap(conf[CONF_SSID], password, conf.get(CONF_CHANNEL)))
|
||||
|
||||
if CONF_MANUAL_IP in conf:
|
||||
add(wifi.set_ap_manual_ip(manual_ip(conf[CONF_MANUAL_IP])))
|
||||
|
||||
if CONF_HOSTNAME in config:
|
||||
add(wifi.set_hostname(config[CONF_HOSTNAME]))
|
||||
|
||||
|
||||
def lib_deps(config):
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
return 'ESP8266WiFi'
|
||||
return None
|
||||
|
||||
@@ -8,29 +8,32 @@ import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml import helpers, yaml_util
|
||||
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_MQTT, \
|
||||
CONF_NAME, \
|
||||
CONF_PLATFORM, CONF_SIMPLIFY, CONF_WIFI, ESP_PLATFORMS, ESP_PLATFORM_ESP32, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml import core, yaml_util
|
||||
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
|
||||
CONF_LIBRARY_URI, \
|
||||
CONF_NAME, CONF_PLATFORM, CONF_SIMPLIFY, CONF_USE_BUILD_FLAGS, CONF_WIFI, ESP_PLATFORMS, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
from esphomeyaml.helpers import App, add, add_task, color
|
||||
from esphomeyaml.helpers import App, add, color
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_LIBRARY_URI = u'esphomelib'
|
||||
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.5.1'
|
||||
|
||||
BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout']
|
||||
|
||||
CORE_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.valid_name,
|
||||
vol.Required(CONF_PLATFORM): vol.All(
|
||||
vol.Upper, vol.Any(ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266)),
|
||||
vol.Required(CONF_PLATFORM): cv.string,
|
||||
vol.Required(CONF_BOARD): cv.string,
|
||||
vol.Optional(CONF_LIBRARY_URI, default=DEFAULT_LIBRARY_URI): cv.string,
|
||||
vol.Optional(CONF_SIMPLIFY, default=True): cv.boolean,
|
||||
vol.Optional(CONF_USE_BUILD_FLAGS, default=True): cv.boolean,
|
||||
vol.Optional(CONF_BOARD_FLASH_MODE): vol.All(vol.Lower, vol.Any(*BUILD_FLASH_MODES)),
|
||||
})
|
||||
|
||||
REQUIRED_COMPONENTS = [
|
||||
CONF_ESPHOMEYAML, CONF_WIFI, CONF_MQTT
|
||||
CONF_ESPHOMEYAML, CONF_WIFI
|
||||
]
|
||||
|
||||
_COMPONENT_CACHE = {}
|
||||
@@ -50,7 +53,6 @@ def get_component(domain):
|
||||
module = importlib.import_module(path)
|
||||
except ImportError as err:
|
||||
_LOGGER.debug(err)
|
||||
pass
|
||||
else:
|
||||
_COMPONENT_CACHE[domain] = module
|
||||
return module
|
||||
@@ -67,8 +69,17 @@ def is_platform_component(component):
|
||||
return hasattr(component, 'PLATFORM_SCHEMA')
|
||||
|
||||
|
||||
def validate_schema(config, schema):
|
||||
return schema(config)
|
||||
def iter_components(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):
|
||||
@@ -87,7 +98,7 @@ def validate_config(config):
|
||||
|
||||
for req in REQUIRED_COMPONENTS:
|
||||
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())
|
||||
|
||||
@@ -97,7 +108,7 @@ def validate_config(config):
|
||||
result.add_error(_format_config_error(ex, domain, config), domain, config)
|
||||
|
||||
try:
|
||||
result[CONF_ESPHOMEYAML] = validate_schema(config[CONF_ESPHOMEYAML], CORE_SCHEMA)
|
||||
result[CONF_ESPHOMEYAML] = CORE_SCHEMA(config[CONF_ESPHOMEYAML])
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, CONF_ESPHOMEYAML, config)
|
||||
|
||||
@@ -112,8 +123,8 @@ def validate_config(config):
|
||||
continue
|
||||
|
||||
esp_platforms = getattr(component, 'ESP_PLATFORMS', ESP_PLATFORMS)
|
||||
if cv.ESP_PLATFORM not in esp_platforms:
|
||||
result.add_error(u"Component {} doesn't support {}.".format(domain, cv.ESP_PLATFORM))
|
||||
if core.ESP_PLATFORM not in esp_platforms:
|
||||
result.add_error(u"Component {} doesn't support {}.".format(domain, core.ESP_PLATFORM))
|
||||
continue
|
||||
|
||||
success = True
|
||||
@@ -130,16 +141,16 @@ def validate_config(config):
|
||||
validated = component.CONFIG_SCHEMA(conf)
|
||||
result[domain] = validated
|
||||
except vol.Invalid as ex:
|
||||
_comp_error(ex, domain, config)
|
||||
_comp_error(ex, domain, conf)
|
||||
continue
|
||||
|
||||
if not hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
continue
|
||||
|
||||
platforms = []
|
||||
for i, p_config in enumerate(conf):
|
||||
for p_config in conf:
|
||||
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
|
||||
p_name = p_config.get(u'platform')
|
||||
if p_name is None:
|
||||
@@ -150,6 +161,16 @@ def validate_config(config):
|
||||
result.add_error(u"Platform not found: {}.{}")
|
||||
continue
|
||||
|
||||
success = True
|
||||
dependencies = getattr(platform, 'DEPENDENCIES', [])
|
||||
for dependency in dependencies:
|
||||
if dependency not in _ALL_COMPONENTS:
|
||||
result.add_error(u"Platform {}.{} requires {}".format(domain, p_name,
|
||||
dependency))
|
||||
success = False
|
||||
if not success:
|
||||
continue
|
||||
|
||||
if hasattr(platform, u'PLATFORM_SCHEMA'):
|
||||
try:
|
||||
p_validated = platform.PLATFORM_SCHEMA(p_config)
|
||||
@@ -161,7 +182,7 @@ def validate_config(config):
|
||||
return result
|
||||
|
||||
|
||||
REQUIRED = ['esphomeyaml', 'wifi', 'mqtt']
|
||||
REQUIRED = ['esphomeyaml', 'wifi']
|
||||
|
||||
|
||||
def _format_config_error(ex, domain, config):
|
||||
@@ -173,6 +194,9 @@ def _format_config_error(ex, domain, config):
|
||||
else:
|
||||
message += u'{}.'.format(humanize_error(config, ex))
|
||||
|
||||
if isinstance(config, list):
|
||||
return message
|
||||
|
||||
domain_config = config.get(domain, config)
|
||||
message += u" (See {}, line {}). ".format(
|
||||
getattr(domain_config, '__config_file__', '?'),
|
||||
@@ -186,46 +210,30 @@ def load_config(path):
|
||||
config = yaml_util.load_yaml(path)
|
||||
except OSError:
|
||||
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()
|
||||
if esp_platform not in (ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266):
|
||||
raise ESPHomeYAMLError(u"Invalid ESP Platform {}".format(esp_platform))
|
||||
cv.ESP_PLATFORM = esp_platform
|
||||
cv.BOARD = unicode(config.get(CONF_ESPHOMEYAML, {}).get(CONF_BOARD, u""))
|
||||
helpers.SIMPLIFY = cv.boolean(config.get(CONF_SIMPLIFY, True))
|
||||
if '8266' in esp_platform:
|
||||
esp_platform = ESP_PLATFORM_ESP8266
|
||||
if '32' in esp_platform:
|
||||
esp_platform = ESP_PLATFORM_ESP32
|
||||
core.ESP_PLATFORM = esp_platform
|
||||
core.BOARD = unicode(core_conf.get(CONF_BOARD, u""))
|
||||
core.SIMPLIFY = cv.boolean(core_conf.get(CONF_SIMPLIFY, True))
|
||||
|
||||
try:
|
||||
result = validate_config(config)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
print(u"Unexpected exception while reading configuration:")
|
||||
raise
|
||||
|
||||
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):
|
||||
"""Display line config source."""
|
||||
if hasattr(obj, '__config_file__'):
|
||||
@@ -262,7 +270,11 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
|
||||
|
||||
def read_config(path):
|
||||
_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 = {}
|
||||
for err in res.errors:
|
||||
domain = err[1] or u"General Error"
|
||||
|
||||
@@ -3,28 +3,29 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
import re
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \
|
||||
CONF_NAME, CONF_PAYLOAD_AVAILABLE, \
|
||||
CONF_PAYLOAD_NOT_AVAILABLE, CONF_PLATFORM, CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import HexInt, IPAddress
|
||||
from esphomeyaml.core import HexInt, IPAddress, TimePeriod, TimePeriodMilliseconds, \
|
||||
TimePeriodMicroseconds, TimePeriodSeconds
|
||||
from esphomeyaml.helpers import ensure_unique_string
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
|
||||
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_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
|
||||
|
||||
ESP_PLATFORM = ''
|
||||
BOARD = ''
|
||||
|
||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
|
||||
|
||||
RESERVED_IDS = [
|
||||
@@ -57,7 +58,7 @@ def alphanumeric(value):
|
||||
def valid_name(value):
|
||||
value = string_strict(value)
|
||||
if not all(c in ALLOWED_NAME_CHARS for c in value):
|
||||
raise vol.Invalid(u"Valid characters for name are %s", ALLOWED_NAME_CHARS)
|
||||
raise vol.Invalid(u"Valid characters for name are {}".format(ALLOWED_NAME_CHARS))
|
||||
return value
|
||||
|
||||
|
||||
@@ -71,7 +72,7 @@ def string(value):
|
||||
|
||||
def string_strict(value):
|
||||
"""Strictly only allow strings."""
|
||||
if isinstance(value, str) or isinstance(value, unicode):
|
||||
if isinstance(value, (str, unicode)):
|
||||
return value
|
||||
raise vol.Invalid("Must be string, did you forget putting quotes "
|
||||
"around the value?")
|
||||
@@ -148,8 +149,7 @@ def only_on(platforms):
|
||||
platforms = [platforms]
|
||||
|
||||
def validator_(obj):
|
||||
print(obj)
|
||||
if ESP_PLATFORM not in platforms:
|
||||
if core.ESP_PLATFORM not in platforms:
|
||||
raise vol.Invalid(u"This feature is only available on {}".format(platforms))
|
||||
return obj
|
||||
|
||||
@@ -178,82 +178,153 @@ def has_at_least_one_key(*keys):
|
||||
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(
|
||||
dict, vol.Schema({
|
||||
'days': vol.Coerce(int),
|
||||
'hours': vol.Coerce(int),
|
||||
'minutes': vol.Coerce(int),
|
||||
'seconds': vol.Coerce(int),
|
||||
'milliseconds': vol.Coerce(int),
|
||||
'days': vol.Coerce(float),
|
||||
'hours': vol.Coerce(float),
|
||||
'minutes': vol.Coerce(float),
|
||||
'seconds': vol.Coerce(float),
|
||||
'milliseconds': vol.Coerce(float),
|
||||
'microseconds': vol.Coerce(float),
|
||||
}),
|
||||
has_at_least_one_key('days', 'hours', 'minutes',
|
||||
'seconds', 'milliseconds'),
|
||||
lambda value: timedelta(**value))
|
||||
'seconds', 'milliseconds', 'microseconds'),
|
||||
lambda value: TimePeriod(**value))
|
||||
|
||||
|
||||
def time_period_str(value):
|
||||
"""Validate and transform time offset."""
|
||||
TIME_PERIOD_EXPLICIT_MESSAGE = ("The old way of being able to write time values without a "
|
||||
"time unit (like \"1000\" for 1000 milliseconds) has been "
|
||||
"removed in 1.5.0 as it was ambiguous in some places. Please "
|
||||
"now explicitly specify the time unit (like \"1000ms\"). See "
|
||||
"https://esphomelib.com/esphomeyaml/configuration-types.html#time "
|
||||
"for more information.")
|
||||
|
||||
|
||||
def time_period_str_colon(value):
|
||||
"""Validate and transform time offset with format HH:MM[:SS]."""
|
||||
if isinstance(value, int):
|
||||
raise vol.Invalid("Make sure you wrap time values in quotes")
|
||||
elif not isinstance(value, (str, unicode)):
|
||||
raise vol.Invalid('Make sure you wrap time values in quotes')
|
||||
elif not isinstance(value, str):
|
||||
raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
|
||||
|
||||
value = unicode(value)
|
||||
if value.endswith(u'ms'):
|
||||
return vol.Coerce(int)(value[:-2])
|
||||
elif value.endswith(u's'):
|
||||
return vol.Coerce(float)(value[:-1]) * 1000
|
||||
elif value.endswith(u'min'):
|
||||
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))
|
||||
negative_offset = False
|
||||
if value.startswith('-'):
|
||||
negative_offset = True
|
||||
value = value[1:]
|
||||
elif value.startswith('+'):
|
||||
value = value[1:]
|
||||
|
||||
|
||||
def time_period_milliseconds(value):
|
||||
try:
|
||||
return timedelta(milliseconds=int(value))
|
||||
except (ValueError, TypeError):
|
||||
raise vol.Invalid('Expected milliseconds, got {}'.format(value))
|
||||
parsed = [int(x) for x in value.split(':')]
|
||||
except ValueError:
|
||||
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):
|
||||
if isinstance(value, (int, long)):
|
||||
return value
|
||||
if isinstance(value, float):
|
||||
return int(value)
|
||||
return value / timedelta(milliseconds=1)
|
||||
def time_period_str_unit(value):
|
||||
"""Validate and transform time period with time unit and integer value."""
|
||||
if isinstance(value, int):
|
||||
value = str(value)
|
||||
elif not isinstance(value, (str, unicode)):
|
||||
raise vol.Invalid("Expected string for time period with unit.")
|
||||
|
||||
try:
|
||||
float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise vol.Invalid(TIME_PERIOD_EXPLICIT_MESSAGE)
|
||||
|
||||
unit_to_kwarg = {
|
||||
'us': 'microseconds',
|
||||
'microseconds': 'microseconds',
|
||||
'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,
|
||||
time_period_milliseconds), time_period_to_milliseconds)
|
||||
positive_time_period = vol.All(time_period, vol.Range(min=0))
|
||||
positive_not_null_time_period = vol.All(time_period, vol.Range(min=0, min_included=False))
|
||||
def time_period_in_milliseconds(value):
|
||||
if value.microseconds is not None and value.microseconds != 0:
|
||||
raise vol.Invalid("Maximum precision is milliseconds")
|
||||
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 = {
|
||||
'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,
|
||||
'': 1
|
||||
}
|
||||
|
||||
|
||||
def frequency(value):
|
||||
value = string(value).replace(' ', '').lower()
|
||||
if value.endswith('Hz') or value.endswith('hz') or value.endswith('HZ'):
|
||||
value = value[:-2]
|
||||
if not value:
|
||||
raise vol.Invalid(u"Frequency must have value")
|
||||
multiplier = 1
|
||||
if value[:-1] in METRIC_SUFFIXES:
|
||||
multiplier = METRIC_SUFFIXES[value[:-1]]
|
||||
value = value[:-1]
|
||||
elif len(value) >= 2 and value[:-2] in METRIC_SUFFIXES:
|
||||
multiplier = METRIC_SUFFIXES[value[:-2]]
|
||||
value = value[:-2]
|
||||
float_val = vol.Coerce(float)(value)
|
||||
return float_val * multiplier
|
||||
value = string(value)
|
||||
match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)(?:Hz|HZ|hz)?$", value)
|
||||
|
||||
if match is None:
|
||||
raise vol.Invalid(u"Expected frequency with unit, "
|
||||
u"got {}".format(value))
|
||||
|
||||
mantissa = float(match.group(1))
|
||||
if match.group(2) not in METRIC_SUFFIXES:
|
||||
raise vol.Invalid(u"Invalid frequency suffix {}".format(match.group(2)))
|
||||
|
||||
multiplier = METRIC_SUFFIXES[match.group(2)]
|
||||
return mantissa * multiplier
|
||||
|
||||
|
||||
def hostname(value):
|
||||
@@ -273,8 +344,8 @@ def ssid(value):
|
||||
raise vol.Invalid("SSID must be a string. Did you wrap it in quotes?")
|
||||
if not value:
|
||||
raise vol.Invalid("SSID can't be empty.")
|
||||
if len(value) > 32:
|
||||
raise vol.Invalid("SSID can't be longer than 32 characters")
|
||||
if len(value) > 31:
|
||||
raise vol.Invalid("SSID can't be longer than 31 characters")
|
||||
return value
|
||||
|
||||
|
||||
@@ -295,15 +366,62 @@ def ipv4(value):
|
||||
return IPAddress(*parts_)
|
||||
|
||||
|
||||
def publish_topic(value):
|
||||
value = string_strict(value)
|
||||
if value.endswith('/'):
|
||||
raise vol.Invalid("Publish topic can't end with '/'")
|
||||
def _valid_topic(value):
|
||||
"""Validate that this is a valid topic name/filter."""
|
||||
if isinstance(value, dict):
|
||||
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
|
||||
|
||||
|
||||
subscribe_topic = string_strict # TODO improve this
|
||||
mqtt_payload = string # TODO improve this
|
||||
def subscribe_topic(value):
|
||||
"""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))
|
||||
uint16_t = vol.All(int_, vol.Range(min=0, max=65535))
|
||||
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
|
||||
|
||||
|
||||
def invalid(value):
|
||||
raise vol.Invalid("This shouldn't happen.")
|
||||
def invalid(message):
|
||||
def validator(value):
|
||||
raise vol.Invalid(message)
|
||||
return validator
|
||||
|
||||
|
||||
def valid(value):
|
||||
@@ -333,23 +453,19 @@ def register_variable_id(value):
|
||||
|
||||
|
||||
class GenerateID(vol.Optional):
|
||||
def __init__(self, basename):
|
||||
def __init__(self, basename, key=CONF_ID):
|
||||
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):
|
||||
return ensure_unique_string(self._basename, REGISTERED_IDS)
|
||||
|
||||
|
||||
ID_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): invalid,
|
||||
})
|
||||
|
||||
REQUIRED_ID_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_ID): register_variable_id,
|
||||
})
|
||||
|
||||
PLATFORM_SCHEMA = ID_SCHEMA.extend({
|
||||
PLATFORM_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): valid,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Constants used by esphomeyaml."""
|
||||
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 1
|
||||
PATCH_VERSION = '0'
|
||||
MAJOR_VERSION = 1
|
||||
MINOR_VERSION = 5
|
||||
PATCH_VERSION = '2'
|
||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||
|
||||
@@ -17,6 +17,7 @@ CONF_NAME = 'name'
|
||||
CONF_PLATFORM = 'platform'
|
||||
CONF_BOARD = 'board'
|
||||
CONF_SIMPLIFY = 'simplify'
|
||||
CONF_USE_BUILD_FLAGS = 'use_build_flags'
|
||||
CONF_LIBRARY_URI = 'library_uri'
|
||||
CONF_LOGGER = 'logger'
|
||||
CONF_WIFI = 'wifi'
|
||||
@@ -32,6 +33,7 @@ CONF_BROKER = 'broker'
|
||||
CONF_USERNAME = 'username'
|
||||
CONF_POWER_SUPPLY = 'power_supply'
|
||||
CONF_ID = 'id'
|
||||
CONF_MQTT_ID = 'mqtt_id'
|
||||
CONF_PIN = 'pin'
|
||||
CONF_NUMBER = 'number'
|
||||
CONF_INVERTED = 'inverted'
|
||||
@@ -133,7 +135,7 @@ CONF_SONY = 'sony'
|
||||
CONF_PANASONIC = 'panasonic'
|
||||
CONF_REPEAT = 'repeat'
|
||||
CONF_TIMES = 'times'
|
||||
CONF_WAIT_TIME_US = 'wait_time_us'
|
||||
CONF_WAIT_TIME = 'wait_time'
|
||||
CONF_OSCILLATION_OUTPUT = 'oscillation_output'
|
||||
CONF_SPEED = 'speed'
|
||||
CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic'
|
||||
@@ -151,6 +153,39 @@ CONF_RATE = 'rate'
|
||||
CONF_ADS1115_ID = 'ads1115_id'
|
||||
CONF_MULTIPLEXER = 'multiplexer'
|
||||
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'
|
||||
CONF_BOARD_FLASH_MODE = 'board_flash_mode'
|
||||
|
||||
ESP32_BOARDS = [
|
||||
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import math
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class ESPHomeYAMLError(Exception):
|
||||
"""General esphomeyaml exception occurred."""
|
||||
pass
|
||||
@@ -16,3 +20,159 @@ class IPAddress(object):
|
||||
|
||||
def __str__(self):
|
||||
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
|
||||
|
||||
@@ -43,6 +43,8 @@ import random
|
||||
import socket
|
||||
import sys
|
||||
|
||||
# pylint: disable=no-member
|
||||
|
||||
# Commands
|
||||
FLASH = 0
|
||||
SPIFFS = 100
|
||||
@@ -62,7 +64,7 @@ def update_progress(progress):
|
||||
:return:
|
||||
"""
|
||||
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 = ""
|
||||
if isinstance(progress, int):
|
||||
progress = float(progress)
|
||||
@@ -75,8 +77,8 @@ def update_progress(progress):
|
||||
if progress >= 1:
|
||||
progress = 1
|
||||
status = "Done...\r\n"
|
||||
block = int(round(barLength * progress))
|
||||
text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (barLength - block),
|
||||
block = int(round(bar_length * progress))
|
||||
text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block),
|
||||
int(progress * 100), status)
|
||||
sys.stderr.write(text)
|
||||
sys.stderr.flush()
|
||||
@@ -93,14 +95,14 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
||||
try:
|
||||
sock.bind(server_address)
|
||||
sock.listen(1)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.error("Listen Failed")
|
||||
return 1
|
||||
|
||||
content_size = os.path.getsize(filename)
|
||||
f = open(filename, 'rb')
|
||||
file_md5 = hashlib.md5(f.read()).hexdigest()
|
||||
f.close()
|
||||
f_handle = open(filename, 'rb')
|
||||
file_md5 = hashlib.md5(f_handle.read()).hexdigest()
|
||||
f_handle.close()
|
||||
_LOGGER.info('Upload size: %d', content_size)
|
||||
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)
|
||||
try:
|
||||
sock2.sendto(message.encode(), remote_address)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.error('Failed')
|
||||
sock2.close()
|
||||
_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:
|
||||
data = sock2.recv(37).decode()
|
||||
break
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
sys.stderr.write('.')
|
||||
sys.stderr.flush()
|
||||
sock2.close()
|
||||
@@ -148,7 +150,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
||||
sock2.settimeout(10)
|
||||
try:
|
||||
data = sock2.recv(32).decode()
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.error('FAIL: No Answer to our Authentication')
|
||||
sock2.close()
|
||||
return 1
|
||||
@@ -166,35 +168,36 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
||||
_LOGGER.info('Waiting for device...')
|
||||
try:
|
||||
sock.settimeout(10)
|
||||
connection, client_address = sock.accept()
|
||||
connection, _ = sock.accept()
|
||||
sock.settimeout(None)
|
||||
connection.settimeout(None)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.error('No response from device')
|
||||
sock.close()
|
||||
return 1
|
||||
|
||||
try:
|
||||
f = open(filename, "rb")
|
||||
f_handle = open(filename, "rb")
|
||||
if PROGRESS:
|
||||
update_progress(0)
|
||||
else:
|
||||
_LOGGER.info('Uploading...')
|
||||
offset = 0
|
||||
while True:
|
||||
chunk = f.read(1024)
|
||||
if not chunk: break
|
||||
chunk = f_handle.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
offset += len(chunk)
|
||||
update_progress(offset / float(content_size))
|
||||
connection.settimeout(10)
|
||||
try:
|
||||
connection.sendall(chunk)
|
||||
connection.recv(10)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
sys.stderr.write('\n')
|
||||
_LOGGER.error('Error Uploading')
|
||||
connection.close()
|
||||
f.close()
|
||||
f_handle.close()
|
||||
sock.close()
|
||||
return 1
|
||||
|
||||
@@ -207,26 +210,26 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
||||
break
|
||||
_LOGGER.info('Result: OK')
|
||||
connection.close()
|
||||
f.close()
|
||||
f_handle.close()
|
||||
sock.close()
|
||||
if data != "OK":
|
||||
_LOGGER.error('%s', data)
|
||||
return 1
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.error('No Result!')
|
||||
connection.close()
|
||||
f.close()
|
||||
f_handle.close()
|
||||
sock.close()
|
||||
return 1
|
||||
|
||||
finally:
|
||||
connection.close()
|
||||
f.close()
|
||||
f_handle.close()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def parser(unparsed_args):
|
||||
def parse_args(unparsed_args):
|
||||
parser = optparse.OptionParser(
|
||||
usage="%prog [options]",
|
||||
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
|
||||
group = optparse.OptionGroup(parser, "Destination")
|
||||
group.add_option("-i", "--ip",
|
||||
dest="esp_ip",
|
||||
action="store",
|
||||
help="ESP8266 IP Address.",
|
||||
default=False
|
||||
)
|
||||
group.add_option("-I", "--host_ip",
|
||||
dest="host_ip",
|
||||
action="store",
|
||||
help="Host IP Address.",
|
||||
default="0.0.0.0"
|
||||
)
|
||||
group.add_option("-p", "--port",
|
||||
dest="esp_port",
|
||||
type="int",
|
||||
help="ESP8266 ota Port. Default 8266",
|
||||
default=8266
|
||||
)
|
||||
group.add_option("-P", "--host_port",
|
||||
dest="host_port",
|
||||
type="int",
|
||||
help="Host server ota Port. Default random 10000-60000",
|
||||
default=random.randint(10000, 60000)
|
||||
)
|
||||
group.add_option(
|
||||
"-i", "--ip",
|
||||
dest="esp_ip",
|
||||
action="store",
|
||||
help="ESP8266 IP Address.",
|
||||
default=False
|
||||
)
|
||||
group.add_option(
|
||||
"-I", "--host_ip",
|
||||
dest="host_ip",
|
||||
action="store",
|
||||
help="Host IP Address.",
|
||||
default="0.0.0.0"
|
||||
)
|
||||
group.add_option(
|
||||
"-p", "--port",
|
||||
dest="esp_port",
|
||||
type="int",
|
||||
help="ESP8266 ota Port. Default 8266",
|
||||
default=8266
|
||||
)
|
||||
group.add_option(
|
||||
"-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)
|
||||
|
||||
# auth
|
||||
group = optparse.OptionGroup(parser, "Authentication")
|
||||
group.add_option("-a", "--auth",
|
||||
dest="auth",
|
||||
help="Set authentication password.",
|
||||
action="store",
|
||||
default=""
|
||||
)
|
||||
group.add_option(
|
||||
"-a", "--auth",
|
||||
dest="auth",
|
||||
help="Set authentication password.",
|
||||
action="store",
|
||||
default=""
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
# image
|
||||
group = optparse.OptionGroup(parser, "Image")
|
||||
group.add_option("-f", "--file",
|
||||
dest="image",
|
||||
help="Image file.",
|
||||
metavar="FILE",
|
||||
default=None
|
||||
)
|
||||
group.add_option("-s", "--spiffs",
|
||||
dest="spiffs",
|
||||
action="store_true",
|
||||
help="Use this option to transmit a SPIFFS image and do not flash the "
|
||||
"module.",
|
||||
default=False
|
||||
)
|
||||
group.add_option(
|
||||
"-f", "--file",
|
||||
dest="image",
|
||||
help="Image file.",
|
||||
metavar="FILE",
|
||||
default=None
|
||||
)
|
||||
group.add_option(
|
||||
"-s", "--spiffs",
|
||||
dest="spiffs",
|
||||
action="store_true",
|
||||
help="Use this option to transmit a SPIFFS image and do not flash the "
|
||||
"module.",
|
||||
default=False
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
# output group
|
||||
group = optparse.OptionGroup(parser, "Output")
|
||||
group.add_option("-d", "--debug",
|
||||
dest="debug",
|
||||
help="Show debug output. And override loglevel with debug.",
|
||||
action="store_true",
|
||||
default=False
|
||||
)
|
||||
group.add_option("-r", "--progress",
|
||||
dest="progress",
|
||||
help="Show progress output. Does not work for ArduinoIDE",
|
||||
action="store_true",
|
||||
default=False
|
||||
)
|
||||
group.add_option(
|
||||
"-d", "--debug",
|
||||
dest="debug",
|
||||
help="Show debug output. And override loglevel with debug.",
|
||||
action="store_true",
|
||||
default=False
|
||||
)
|
||||
group.add_option(
|
||||
"-r", "--progress",
|
||||
dest="progress",
|
||||
help="Show progress output. Does not work for ArduinoIDE",
|
||||
action="store_true",
|
||||
default=False
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
(options, args) = parser.parse_args(unparsed_args)
|
||||
options, _ = parser.parse_args(unparsed_args)
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def main(args):
|
||||
options = parser(args)
|
||||
options = parse_args(args)
|
||||
_LOGGER.debug("Options: %s", str(options))
|
||||
|
||||
# check options
|
||||
|
||||
@@ -4,16 +4,16 @@ import logging
|
||||
import re
|
||||
from collections import OrderedDict, deque
|
||||
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, \
|
||||
CONF_INVERTED, \
|
||||
CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, \
|
||||
CONF_STATE_TOPIC, CONF_TOPIC
|
||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt
|
||||
CONF_MODE, CONF_NUMBER, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PCF8574, \
|
||||
CONF_RETAIN, CONF_STATE_TOPIC, CONF_TOPIC
|
||||
from esphomeyaml.core import ESPHomeYAMLError, HexInt, TimePeriodMicroseconds, \
|
||||
TimePeriodMilliseconds, TimePeriodSeconds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SIMPLIFY = False
|
||||
|
||||
|
||||
def ensure_unique_string(preferred_string, current_strings):
|
||||
test_string = preferred_string
|
||||
@@ -45,10 +45,18 @@ def indent(text, padding=u' '):
|
||||
|
||||
class Expression(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
self.requires = []
|
||||
self.required = False
|
||||
|
||||
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):
|
||||
@@ -60,15 +68,22 @@ class RawExpression(Expression):
|
||||
return self.text
|
||||
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
class AssignmentExpression(Expression):
|
||||
def __init__(self, lhs, rhs, obj):
|
||||
def __init__(self, type, modifier, name, rhs, obj):
|
||||
super(AssignmentExpression, self).__init__()
|
||||
self.obj = obj
|
||||
self.lhs = safe_exp(lhs)
|
||||
self.type = type
|
||||
self.modifier = modifier
|
||||
self.name = name
|
||||
self.rhs = safe_exp(rhs)
|
||||
self.requires.append(self.rhs)
|
||||
self.obj = obj
|
||||
|
||||
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):
|
||||
@@ -78,20 +93,43 @@ class ExpressionList(Expression):
|
||||
args = list(args)
|
||||
while args and args[-1] is None:
|
||||
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):
|
||||
text = u", ".join(unicode(x) for x in self.args)
|
||||
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):
|
||||
def __init__(self, base, *args):
|
||||
super(CallExpression, self).__init__()
|
||||
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.requires.append(self.args)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -103,44 +141,70 @@ class StructInitializer(Expression):
|
||||
args = OrderedDict(args)
|
||||
self.args = OrderedDict()
|
||||
for key, value in args.iteritems():
|
||||
if value is not None:
|
||||
self.args[key] = safe_exp(value)
|
||||
if value is None:
|
||||
continue
|
||||
exp = safe_exp(value)
|
||||
self.args[key] = exp
|
||||
self.requires.append(exp)
|
||||
|
||||
def __str__(self):
|
||||
s = u'{}{{\n'.format(self.base)
|
||||
cpp = u'{}{{\n'.format(self.base)
|
||||
for key, value in self.args.iteritems():
|
||||
s += u' .{} = {},\n'.format(key, value)
|
||||
s += u'}'
|
||||
return s
|
||||
cpp += u' .{} = {},\n'.format(key, value)
|
||||
cpp += u'}'
|
||||
return cpp
|
||||
|
||||
|
||||
class ArrayInitializer(Expression):
|
||||
def __init__(self, *args):
|
||||
def __init__(self, *args, **kwargs):
|
||||
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):
|
||||
if not self.args:
|
||||
return u'{}'
|
||||
s = u'{\n'
|
||||
for arg in self.args:
|
||||
s += u' {},\n'.format(arg)
|
||||
s += u'}'
|
||||
return s
|
||||
if self.multiline:
|
||||
cpp = u'{\n'
|
||||
for arg in self.args:
|
||||
cpp += u' {},\n'.format(arg)
|
||||
cpp += u'}'
|
||||
else:
|
||||
cpp = u'{' + u', '.join(str(arg) for arg in self.args) + u'}'
|
||||
return cpp
|
||||
|
||||
|
||||
class Literal(Expression):
|
||||
def __init__(self):
|
||||
super(Literal, self).__init__()
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
# From https://stackoverflow.com/a/14945195/8924614
|
||||
def cpp_string_escape(s, encoding='utf-8'):
|
||||
if isinstance(s, unicode):
|
||||
s = s.encode(encoding)
|
||||
result = ''
|
||||
for c in s:
|
||||
if not (32 <= ord(c) < 127) or c in ('\\', '"'):
|
||||
result += '\\%03o' % ord(c)
|
||||
else:
|
||||
result += c
|
||||
return '"' + result + '"'
|
||||
|
||||
|
||||
class StringLiteral(Literal):
|
||||
def __init__(self, s):
|
||||
def __init__(self, string):
|
||||
super(StringLiteral, self).__init__()
|
||||
self.s = s
|
||||
self.string = string
|
||||
|
||||
def __str__(self):
|
||||
return u'"{}"'.format(self.s)
|
||||
return u'{}'.format(cpp_string_escape(self.string))
|
||||
|
||||
|
||||
class IntLiteral(Literal):
|
||||
@@ -153,12 +217,12 @@ class IntLiteral(Literal):
|
||||
|
||||
|
||||
class BoolLiteral(Literal):
|
||||
def __init__(self, b):
|
||||
def __init__(self, binary):
|
||||
super(BoolLiteral, self).__init__()
|
||||
self.b = b
|
||||
self.binary = binary
|
||||
|
||||
def __str__(self):
|
||||
return u"true" if self.b else u"false"
|
||||
return u"true" if self.binary else u"false"
|
||||
|
||||
|
||||
class HexIntLiteral(Literal):
|
||||
@@ -171,12 +235,12 @@ class HexIntLiteral(Literal):
|
||||
|
||||
|
||||
class FloatLiteral(Literal):
|
||||
def __init__(self, f):
|
||||
def __init__(self, float_):
|
||||
super(FloatLiteral, self).__init__()
|
||||
self.f = f
|
||||
self.float_ = float_
|
||||
|
||||
def __str__(self):
|
||||
return u"{:f}f".format(self.f)
|
||||
return u"{:f}f".format(self.float_)
|
||||
|
||||
|
||||
def safe_exp(obj):
|
||||
@@ -184,12 +248,20 @@ def safe_exp(obj):
|
||||
return obj
|
||||
elif isinstance(obj, bool):
|
||||
return BoolLiteral(obj)
|
||||
elif isinstance(obj, str) or isinstance(obj, unicode):
|
||||
elif isinstance(obj, (str, unicode)):
|
||||
return StringLiteral(obj)
|
||||
elif isinstance(obj, HexInt):
|
||||
return HexIntLiteral(obj)
|
||||
elif isinstance(obj, (int, long)):
|
||||
return IntLiteral(obj)
|
||||
elif isinstance(obj, float):
|
||||
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)
|
||||
|
||||
|
||||
@@ -198,7 +270,7 @@ class Statement(object):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplemented
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class RawStatement(Statement):
|
||||
@@ -225,21 +297,24 @@ def statement(expression):
|
||||
return ExpressionStatement(expression)
|
||||
|
||||
|
||||
# pylint: disable=redefined-builtin, invalid-name
|
||||
def variable(type, id, rhs):
|
||||
lhs = RawExpression(u'{} {}'.format(type if not SIMPLIFY else u'auto', id))
|
||||
rhs = safe_exp(rhs)
|
||||
obj = MockObj(id, u'.')
|
||||
add(AssignmentExpression(lhs, rhs, obj))
|
||||
assignment = AssignmentExpression(type, '', id, rhs, obj)
|
||||
add(assignment)
|
||||
_VARIABLES[id] = obj, type
|
||||
obj.requires.append(assignment)
|
||||
return obj
|
||||
|
||||
|
||||
def Pvariable(type, id, rhs):
|
||||
lhs = RawExpression(u'{} *{}'.format(type if not SIMPLIFY else u'auto', id))
|
||||
rhs = safe_exp(rhs)
|
||||
obj = MockObj(id, u'->')
|
||||
add(AssignmentExpression(lhs, rhs, obj))
|
||||
assignment = AssignmentExpression(type, '*', id, rhs, obj)
|
||||
add(assignment)
|
||||
_VARIABLES[id] = obj, type
|
||||
obj.requires.append(assignment)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -265,13 +340,13 @@ def get_variable(id, type=None):
|
||||
return None
|
||||
if result is None:
|
||||
if id is not None:
|
||||
result = _VARIABLES[id][0]
|
||||
if id in _VARIABLES:
|
||||
result = _VARIABLES[id][0]
|
||||
elif type is not None:
|
||||
result = next((x[0] for x in _VARIABLES.itervalues() if x[1] == type), None)
|
||||
|
||||
if result is None:
|
||||
raise ESPHomeYAMLError(u"Couldn't find ID '{}' with type {}".format(id, type))
|
||||
result.usages += 1
|
||||
return result
|
||||
|
||||
|
||||
@@ -279,17 +354,17 @@ def add_task(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)
|
||||
return expression
|
||||
|
||||
|
||||
class MockObj(Expression):
|
||||
def __init__(self, base, op=u'.', parent=None):
|
||||
def __init__(self, base, op=u'.'):
|
||||
self.base = base
|
||||
self.op = op
|
||||
self.usages = 0
|
||||
self.parent = parent
|
||||
super(MockObj, self).__init__()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
@@ -298,18 +373,26 @@ class MockObj(Expression):
|
||||
attr = attr[1:]
|
||||
next_op = u'->'
|
||||
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):
|
||||
self.usages += 1
|
||||
it = self.parent
|
||||
while it is not None:
|
||||
it.usages += 1
|
||||
it = it.parent
|
||||
return CallExpression(self.base, *args)
|
||||
call = CallExpression(self.base, *args)
|
||||
obj = MockObj(call, self.op)
|
||||
obj.requires.append(self)
|
||||
obj.requires.append(call)
|
||||
return obj
|
||||
|
||||
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')
|
||||
@@ -328,8 +411,20 @@ def get_gpio_pin_number(conf):
|
||||
def exp_gpio_pin_(obj, conf, default_mode):
|
||||
if isinstance(conf, int):
|
||||
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:
|
||||
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)),
|
||||
conf[CONF_INVERTED])
|
||||
|
||||
@@ -357,13 +452,8 @@ def setup_mqtt_component(obj, config):
|
||||
add(obj.set_custom_command_topic(config[CONF_COMMAND_TOPIC]))
|
||||
if CONF_AVAILABILITY in config:
|
||||
availability = config[CONF_AVAILABILITY]
|
||||
exp = StructInitializer(
|
||||
u'mqtt::Availability',
|
||||
(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))
|
||||
add(obj.set_availability(availability[CONF_TOPIC], availability[CONF_PAYLOAD_AVAILABLE],
|
||||
availability[CONF_PAYLOAD_NOT_AVAILABLE]))
|
||||
|
||||
|
||||
def exp_empty_optional(type):
|
||||
@@ -392,7 +482,7 @@ def quote(s):
|
||||
return u"'" + s.replace(u"'", u"'\"'\"'") + u"'"
|
||||
|
||||
|
||||
def color(the_color, message = '', reset=None):
|
||||
def color(the_color, message='', reset=None):
|
||||
"""Color helper."""
|
||||
from colorlog.escape_codes import escape_codes, parse_colors
|
||||
if not message:
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, CONF_LOGGER, \
|
||||
CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYAML, \
|
||||
CONF_LOG_TOPIC, \
|
||||
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
|
||||
CONF_USERNAME
|
||||
from esphomeyaml.helpers import color
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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:
|
||||
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):
|
||||
if topic is not None:
|
||||
pass # already have topic
|
||||
elif CONF_LOG_TOPIC in config.get(CONF_LOGGER, {}):
|
||||
topic = config[CONF_LOGGER][CONF_LOG_TOPIC]
|
||||
elif CONF_TOPIC_PREFIX in config[CONF_MQTT]:
|
||||
topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + u'/debug'
|
||||
elif CONF_MQTT in config:
|
||||
conf = config[CONF_MQTT]
|
||||
if CONF_LOG_TOPIC in conf:
|
||||
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:
|
||||
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)
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
t = datetime.now().time().strftime(u'[%H:%M:%S] ')
|
||||
print(t + msg.payload)
|
||||
time = datetime.now().time().strftime(u'[%H:%M:%S]')
|
||||
print(time + msg.payload)
|
||||
|
||||
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')
|
||||
name = config[CONF_ESPHOMEYAML][CONF_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):
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
@@ -3,8 +3,9 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
from esphomeyaml.const import ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266, CONF_NUMBER, CONF_MODE, \
|
||||
CONF_INVERTED
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_PCF8574, \
|
||||
ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
|
||||
_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,
|
||||
})
|
||||
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,
|
||||
})
|
||||
ESP8266_THING_PINS = dict(ESP8266_PINS, **{
|
||||
@@ -71,46 +72,46 @@ def _translate_pin(value):
|
||||
except ValueError:
|
||||
pass
|
||||
if value.startswith('GPIO'):
|
||||
return vol.Coerce(int)(value[len('GPIO'):])
|
||||
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
return vol.Coerce(int)(value[len('GPIO'):].strip())
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
if value in ESP32_PINS:
|
||||
return ESP32_PINS[value]
|
||||
if 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 "
|
||||
u"pin {}.".format(cv.BOARD, value))
|
||||
if value not in ESP32_BOARD_TO_PINS[cv.BOARD]:
|
||||
raise vol.Invalid(u"ESP32: Board {} doesn't have"
|
||||
u"pin {}".format(cv.BOARD, value))
|
||||
return ESP32_BOARD_TO_PINS[cv.BOARD][value]
|
||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
u"pin {}.".format(core.BOARD, value))
|
||||
if value not in ESP32_BOARD_TO_PINS[core.BOARD]:
|
||||
raise vol.Invalid(u"ESP32: Board {} doesn't have "
|
||||
u"pin {}".format(core.BOARD, value))
|
||||
return ESP32_BOARD_TO_PINS[core.BOARD][value]
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
if value in ESP8266_PINS:
|
||||
return ESP8266_PINS[value]
|
||||
if 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 "
|
||||
u"pin {}.".format(cv.BOARD, value))
|
||||
if value not in ESP8266_BOARD_TO_PINS[cv.BOARD]:
|
||||
raise vol.Invalid(u"ESP8266: Board {} doesn't have"
|
||||
u"pin {}".format(cv.BOARD, value))
|
||||
return ESP8266_BOARD_TO_PINS[cv.BOARD][value]
|
||||
u"pin {}.".format(core.BOARD, value))
|
||||
if value not in ESP8266_BOARD_TO_PINS[core.BOARD]:
|
||||
raise vol.Invalid(u"ESP8266: Board {} doesn't have "
|
||||
u"pin {}".format(core.BOARD, value))
|
||||
return ESP8266_BOARD_TO_PINS[core.BOARD][value]
|
||||
raise vol.Invalid(u"Invalid ESP platform.")
|
||||
|
||||
|
||||
def _validate_gpio_pin(value):
|
||||
def validate_gpio_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:
|
||||
raise vol.Invalid(u"ESP32: Invalid pin number: {}".format(value))
|
||||
if 6 <= value <= 11:
|
||||
_LOGGER.warning(u"ESP32: Pin {} (6-11) might already be used by the "
|
||||
u"flash interface. Be warned.".format(value))
|
||||
_LOGGER.warning(u"ESP32: Pin %s (6-11) might already be used by the "
|
||||
u"flash interface. Be warned.", value)
|
||||
if value in (20, 24, 28, 29, 30, 31):
|
||||
_LOGGER.warning(u"ESP32: Pin {} (20, 24, 28-31) can usually not be used. "
|
||||
u"Be warned.".format(value))
|
||||
_LOGGER.warning(u"ESP32: Pin %s (20, 24, 28-31) can usually not be used. "
|
||||
u"Be warned.", value)
|
||||
return value
|
||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
if 6 <= value <= 11:
|
||||
_LOGGER.warning(u"ESP8266: Pin {} (6-11) might already be used by the "
|
||||
u"flash interface. Be warned.".format(value))
|
||||
_LOGGER.warning(u"ESP8266: Pin %s (6-11) might already be used by the "
|
||||
u"flash interface. Be warned.", value)
|
||||
if value < 0 or value > 17:
|
||||
raise vol.Invalid(u"ESP8266: Invalid pin number: {}".format(value))
|
||||
return value
|
||||
@@ -118,41 +119,40 @@ def _validate_gpio_pin(value):
|
||||
|
||||
|
||||
def input_pin(value):
|
||||
value = _validate_gpio_pin(value)
|
||||
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
value = validate_gpio_pin(value)
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
return value
|
||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
return value
|
||||
raise vol.Invalid(u"Invalid ESP platform.")
|
||||
|
||||
|
||||
def output_pin(value):
|
||||
value = _validate_gpio_pin(value)
|
||||
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
value = validate_gpio_pin(value)
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
if 34 <= value <= 39:
|
||||
raise vol.Invalid(u"ESP32: Pin {} (34-39) can only be used as "
|
||||
u"input pins.".format(value))
|
||||
return value
|
||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
if value == 16:
|
||||
raise vol.Invalid(u"Pin {} doesn't support output mode".format(value))
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
return value
|
||||
raise vol.Invalid("Invalid ESP platform.")
|
||||
|
||||
|
||||
def analog_pin(value):
|
||||
value = _validate_gpio_pin(value)
|
||||
if cv.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
value = validate_gpio_pin(value)
|
||||
if core.ESP_PLATFORM == ESP_PLATFORM_ESP32:
|
||||
if 32 <= value <= 39: # ADC1
|
||||
return value
|
||||
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
|
||||
return value
|
||||
raise vol.Invalid(u"ESP8266: Only pin A0 (17) supports ADC.")
|
||||
raise vol.Invalid(u"Invalid ESP platform.")
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
input_output_pin = vol.All(input_pin, output_pin)
|
||||
gpio_pin = vol.Any(input_pin, output_pin)
|
||||
PIN_MODES_ESP8266 = [
|
||||
@@ -170,27 +170,42 @@ PIN_MODES_ESP32 = [
|
||||
|
||||
def pin_mode(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)
|
||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
elif core.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||
return vol.Any(*PIN_MODES_ESP8266)(value)
|
||||
raise vol.Invalid(u"Invalid ESP platform.")
|
||||
|
||||
|
||||
GPIO_PIN_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_NUMBER): gpio_pin,
|
||||
vol.Required(CONF_MODE): pin_mode,
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PCF8574): cv.variable_id,
|
||||
vol.Required(CONF_NUMBER): vol.Coerce(int),
|
||||
vol.Optional(CONF_INVERTED, default=False): 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.Optional(CONF_MODE): pin_mode,
|
||||
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.Optional(CONF_MODE): pin_mode,
|
||||
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
|
||||
|
||||
@@ -2,49 +2,51 @@ from __future__ import print_function
|
||||
|
||||
import codecs
|
||||
import os
|
||||
from time import sleep
|
||||
import unicodedata
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import esphomeyaml.config_validation as cv
|
||||
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, \
|
||||
ESP_PLATFORM_ESP8266
|
||||
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 +70,12 @@ logger:
|
||||
|
||||
"""
|
||||
|
||||
if os.getenv('ESPHOMEYAML_QUICKWIZARD', False):
|
||||
def sleep(time):
|
||||
pass
|
||||
else:
|
||||
from time import sleep
|
||||
|
||||
|
||||
def print_step(step, big):
|
||||
print()
|
||||
@@ -85,8 +93,8 @@ def default_input(text, default):
|
||||
|
||||
|
||||
# From https://stackoverflow.com/a/518232/8924614
|
||||
def strip_accents(s):
|
||||
return u''.join(c for c in unicodedata.normalize('NFD', unicode(s))
|
||||
def strip_accents(string):
|
||||
return u''.join(c for c in unicodedata.normalize('NFD', unicode(string))
|
||||
if unicodedata.category(c) != 'Mn')
|
||||
|
||||
|
||||
@@ -134,10 +142,10 @@ def wizard(path):
|
||||
print("Great! Your node is now called \"{}\".".format(color('cyan', name)))
|
||||
sleep(1)
|
||||
print_step(2, ESP_BIG)
|
||||
print("Now I'd like to know which *board* you're using so that I can compile "
|
||||
print("Now I'd like to know what microcontroller you're using so that I can compile "
|
||||
"firmwares for it.")
|
||||
print("Are you using an " + color('green', 'ESP32') + " or " +
|
||||
color('green', 'ESP8266') + " based board?")
|
||||
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
|
||||
while True:
|
||||
sleep(0.5)
|
||||
print()
|
||||
@@ -161,6 +169,8 @@ def wizard(path):
|
||||
print("Next, I need to know what " + color('green', 'board') + " you're using.")
|
||||
sleep(0.5)
|
||||
print("Please go to {} and choose a board.".format(color('green', board_link)))
|
||||
if platform == ESP_PLATFORM_ESP8266:
|
||||
print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
|
||||
print()
|
||||
# Don't sleep because user needs to copy link
|
||||
if platform == ESP_PLATFORM_ESP32:
|
||||
@@ -190,7 +200,7 @@ def wizard(path):
|
||||
print()
|
||||
sleep(1)
|
||||
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)
|
||||
print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
|
||||
while True:
|
||||
@@ -200,7 +210,7 @@ def wizard(path):
|
||||
break
|
||||
except vol.Invalid:
|
||||
print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
|
||||
"Please try again.".format(ssid)))
|
||||
"Please try again.".format(ssid)))
|
||||
print()
|
||||
sleep(1)
|
||||
|
||||
@@ -210,7 +220,7 @@ def wizard(path):
|
||||
sleep(0.75)
|
||||
|
||||
print("Now please state the " + color('green', 'password') +
|
||||
" of the WiFi network so that I can connect to it.")
|
||||
" of the WiFi network so that I can connect to it (Leave empty for no password)")
|
||||
print()
|
||||
print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
|
||||
sleep(0.5)
|
||||
@@ -230,9 +240,9 @@ def wizard(path):
|
||||
try:
|
||||
broker = mqtt.validate_broker(broker)
|
||||
break
|
||||
except vol.Invalid as e:
|
||||
except vol.Invalid as err:
|
||||
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
|
||||
broker, e)))
|
||||
broker, err)))
|
||||
print("Please try again.")
|
||||
print()
|
||||
sleep(1)
|
||||
@@ -267,12 +277,12 @@ def wizard(path):
|
||||
mqtt_username=mqtt_username, mqtt_password=mqtt_password)
|
||||
|
||||
if ota_password:
|
||||
config += "ota:\n password: '{}'".format(ota_password)
|
||||
config += "ota:\n password: '{}'\n".format(ota_password)
|
||||
else:
|
||||
config += "ota:\n"
|
||||
|
||||
with codecs.open(path, 'w') as f:
|
||||
f.write(config)
|
||||
with codecs.open(path, 'w') as f_handle:
|
||||
f_handle.write(config)
|
||||
|
||||
print()
|
||||
print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
||||
@@ -290,4 +300,3 @@ def wizard(path):
|
||||
print(" > Then follow the rest of the getting started guide:")
|
||||
print(" > https://esphomelib.com/esphomeyaml/getting-started.html")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ import codecs
|
||||
import errno
|
||||
import os
|
||||
|
||||
from esphomeyaml.config import get_component
|
||||
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_LOGGER, \
|
||||
CONF_NAME, CONF_OTA, CONF_PLATFORM, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml import core
|
||||
from esphomeyaml.config import iter_components
|
||||
from esphomeyaml.const import CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_ESPHOMEYAML, \
|
||||
CONF_LIBRARY_URI, \
|
||||
CONF_NAME, CONF_PLATFORM, CONF_USE_BUILD_FLAGS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||
from esphomeyaml.core import ESPHomeYAMLError
|
||||
|
||||
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
|
||||
@@ -35,9 +37,9 @@ void loop() {
|
||||
INI_BASE_FORMAT = (u"""; Auto generated code by esphomeyaml
|
||||
|
||||
[common]
|
||||
lib_deps =
|
||||
build_flags =
|
||||
upload_flags =
|
||||
lib_deps =
|
||||
build_flags =
|
||||
upload_flags =
|
||||
|
||||
; ===== DO NOT EDIT ANYTHING BELOW THIS LINE =====
|
||||
""", u"""
|
||||
@@ -50,9 +52,10 @@ platform = {platform}
|
||||
board = {board}
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
{esphomeyaml_uri}
|
||||
{lib_deps}
|
||||
${{common.lib_deps}}
|
||||
build_flags ={build_flags}
|
||||
build_flags =
|
||||
{build_flags}
|
||||
${{common.build_flags}}
|
||||
"""
|
||||
|
||||
@@ -62,19 +65,63 @@ 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):
|
||||
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'platform': PLATFORM_TO_PLATFORMIO[config[CONF_ESPHOMEYAML][CONF_PLATFORM]],
|
||||
u'platform': platform,
|
||||
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
|
||||
u'esphomeyaml_uri': config[CONF_ESPHOMEYAML][CONF_LIBRARY_URI],
|
||||
u'build_flags': u'',
|
||||
}
|
||||
if CONF_LOGGER in config:
|
||||
build_flags = get_component(CONF_LOGGER).get_build_flags(config[CONF_LOGGER])
|
||||
if build_flags:
|
||||
d[u'build_flags'] = u'\n ' + build_flags
|
||||
return INI_CONTENT_FORMAT.format(**d)
|
||||
build_flags = set()
|
||||
if config[CONF_ESPHOMEYAML][CONF_USE_BUILD_FLAGS]:
|
||||
build_flags |= get_build_flags(config, 'build_flags')
|
||||
build_flags |= get_build_flags(config, 'BUILD_FLAGS')
|
||||
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)
|
||||
|
||||
content = INI_CONTENT_FORMAT.format(**options)
|
||||
if CONF_BOARD_FLASH_MODE in config[CONF_ESPHOMEYAML]:
|
||||
flash_mode = config[CONF_ESPHOMEYAML][CONF_BOARD_FLASH_MODE]
|
||||
content += "board_flash_mode = {}\n".format(flash_mode)
|
||||
return content
|
||||
|
||||
|
||||
def mkdir_p(path):
|
||||
@@ -109,8 +156,8 @@ def find_begin_end(text, begin_s, end_s):
|
||||
def write_platformio_ini(content, path):
|
||||
if os.path.isfile(path):
|
||||
try:
|
||||
with codecs.open(path, 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
with codecs.open(path, 'r', encoding='utf-8') as f_handle:
|
||||
text = f_handle.read()
|
||||
except OSError:
|
||||
raise ESPHomeYAMLError(u"Could not read ini file at {}".format(path))
|
||||
prev_file = text
|
||||
@@ -120,18 +167,18 @@ def write_platformio_ini(content, path):
|
||||
mkdir_p(os.path.dirname(path))
|
||||
content_format = INI_BASE_FORMAT
|
||||
full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + \
|
||||
content + INI_AUTO_GENERATE_END + content_format[1]
|
||||
content + INI_AUTO_GENERATE_END + content_format[1]
|
||||
if prev_file == full_file:
|
||||
return
|
||||
with codecs.open(path, mode='w+', encoding='utf-8') as f:
|
||||
f.write(full_file)
|
||||
with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
|
||||
f_handle.write(full_file)
|
||||
|
||||
|
||||
def write_cpp(code_s, path):
|
||||
if os.path.isfile(path):
|
||||
try:
|
||||
with codecs.open(path, 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
with codecs.open(path, 'r', encoding='utf-8') as f_handle:
|
||||
text = f_handle.read()
|
||||
except OSError:
|
||||
raise ESPHomeYAMLError(u"Could not read C++ file at {}".format(path))
|
||||
prev_file = text
|
||||
@@ -142,8 +189,8 @@ def write_cpp(code_s, path):
|
||||
code_format = CPP_BASE_FORMAT
|
||||
|
||||
full_file = code_format[0] + CPP_AUTO_GENERATE_BEGIN + '\n' + \
|
||||
code_s + CPP_AUTO_GENERATE_END + code_format[1]
|
||||
code_s + CPP_AUTO_GENERATE_END + code_format[1]
|
||||
if prev_file == full_file:
|
||||
return
|
||||
with codecs.open(path, 'w+', encoding='utf-8') as f:
|
||||
f.write(full_file)
|
||||
with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
|
||||
f_handle.write(full_file)
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import codecs
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
|
||||
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__)
|
||||
|
||||
# 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):
|
||||
"""Wrapper class to be able to add attributes on a list."""
|
||||
@@ -22,7 +31,7 @@ class NodeStrClass(unicode):
|
||||
pass
|
||||
|
||||
|
||||
class SafeLineLoader(yaml.SafeLoader):
|
||||
class SafeLineLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
|
||||
"""Loader class that keeps track of line numbers."""
|
||||
|
||||
def compose_node(self, parent, index):
|
||||
@@ -72,9 +81,8 @@ def _ordered_dict(loader, node):
|
||||
|
||||
if key in seen:
|
||||
fname = getattr(loader.stream, 'name', '')
|
||||
_LOGGER.error(
|
||||
u'YAML file %s contains duplicate key "%s". '
|
||||
u'Check lines %d and %d.', fname, key, seen[key], line)
|
||||
raise ESPHomeYAMLError(u'YAML file {} contains duplicate key "{}". '
|
||||
u'Check lines {} and {}.'.format(fname, key, seen[key], line))
|
||||
seen[key] = line
|
||||
|
||||
return _add_reference(OrderedDict(nodes), loader, node)
|
||||
@@ -97,8 +105,107 @@ def _add_reference(obj, loader, node):
|
||||
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_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
|
||||
@@ -129,21 +236,40 @@ def represent_odict(dump, tag, mapping, flow_style=None):
|
||||
return node
|
||||
|
||||
|
||||
def unicode_representer(dumper, uni):
|
||||
def unicode_representer(_, uni):
|
||||
node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=uni)
|
||||
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))
|
||||
return node
|
||||
|
||||
|
||||
def ipaddress_representer(dumper, data):
|
||||
def stringify_representer(_, data):
|
||||
node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=str(data))
|
||||
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(
|
||||
OrderedDict,
|
||||
lambda dumper, value:
|
||||
@@ -158,4 +284,9 @@ yaml.SafeDumper.add_representer(
|
||||
|
||||
yaml.SafeDumper.add_representer(unicode, unicode_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
22
pylintrc
Normal 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
|
||||
6
setup.cfg
Normal file
6
setup.cfg
Normal file
@@ -0,0 +1,6 @@
|
||||
[metadata]
|
||||
description-file = README.md
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
builtins = unicode, long, raw_input
|
||||
9
setup.py
9
setup.py
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
"""esphomeyaml setup script."""
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
from esphomeyaml import const
|
||||
|
||||
@@ -9,11 +9,11 @@ PROJECT_PACKAGE_NAME = 'esphomeyaml'
|
||||
PROJECT_LICENSE = 'MIT'
|
||||
PROJECT_AUTHOR = 'Otto Winter'
|
||||
PROJECT_COPYRIGHT = '2018, Otto Winter'
|
||||
PROJECT_URL = 'http://esphomelib.com/'
|
||||
PROJECT_URL = 'https://esphomelib.com/esphomeyaml/index.html'
|
||||
PROJECT_EMAIL = 'contact@otto-winter.com'
|
||||
|
||||
PROJECT_GITHUB_USERNAME = 'OttoWinter'
|
||||
PROJECT_GITHUB_REPOSITORY = 'esphomelib'
|
||||
PROJECT_GITHUB_REPOSITORY = 'esphomeyaml'
|
||||
|
||||
PYPI_URL = 'https://pypi.python.org/pypi/{}'.format(PROJECT_PACKAGE_NAME)
|
||||
GITHUB_PATH = '{}/{}'.format(PROJECT_GITHUB_USERNAME, PROJECT_GITHUB_REPOSITORY)
|
||||
@@ -59,5 +59,6 @@ setup(
|
||||
'console_scripts': [
|
||||
'esphomeyaml = esphomeyaml.__main__:main'
|
||||
]
|
||||
}
|
||||
},
|
||||
packages=find_packages()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user