mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 06:58:11 +01:00
Add MQTT publish JSON action and subscribe JSON trigger (#193)
* Add MQTT publish JSON action and subscribe JSON trigger * Lint
This commit is contained in:
parent
81bc400340
commit
629f2b128e
11 changed files with 233 additions and 140 deletions
|
@ -16,6 +16,7 @@ from esphomeyaml.core import ESPHomeYAMLError
|
||||||
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \
|
from esphomeyaml.helpers import AssignmentExpression, Expression, RawStatement, \
|
||||||
_EXPRESSIONS, add, \
|
_EXPRESSIONS, add, \
|
||||||
add_job, color, flush_tasks, indent, quote, statement, relative_path
|
add_job, color, flush_tasks, indent, quote, statement, relative_path
|
||||||
|
from esphomeyaml.util import safe_print
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -39,11 +40,11 @@ def choose_serial_port(config):
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return 'OTA'
|
return 'OTA'
|
||||||
print(u"Found multiple serial port options, please choose one:")
|
safe_print(u"Found multiple serial port options, please choose one:")
|
||||||
for i, (res, desc) in enumerate(result):
|
for i, (res, desc) in enumerate(result):
|
||||||
print(u" [{}] {} ({})".format(i, res, desc))
|
safe_print(u" [{}] {} ({})".format(i, res, desc))
|
||||||
print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
|
safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config)))
|
||||||
print()
|
safe_print()
|
||||||
while True:
|
while True:
|
||||||
opt = raw_input('(number): ')
|
opt = raw_input('(number): ')
|
||||||
if opt in result:
|
if opt in result:
|
||||||
|
@ -55,7 +56,7 @@ def choose_serial_port(config):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(color('red', u"Invalid option: '{}'".format(opt)))
|
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||||
if opt == len(result):
|
if opt == len(result):
|
||||||
return 'OTA'
|
return 'OTA'
|
||||||
return result[opt][0]
|
return result[opt][0]
|
||||||
|
@ -111,10 +112,7 @@ def run_miniterm(config, port, escape=False):
|
||||||
message = time + line
|
message = time + line
|
||||||
if escape:
|
if escape:
|
||||||
message = message.replace('\033', '\\033')
|
message = message.replace('\033', '\\033')
|
||||||
try:
|
safe_print(message)
|
||||||
print(message)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
print(message.encode('ascii', 'backslashreplace'))
|
|
||||||
|
|
||||||
|
|
||||||
def write_cpp(config):
|
def write_cpp(config):
|
||||||
|
@ -295,7 +293,7 @@ def strip_default_ids(config):
|
||||||
def command_config(args, config):
|
def command_config(args, config):
|
||||||
if not args.verbose:
|
if not args.verbose:
|
||||||
config = strip_default_ids(config)
|
config = strip_default_ids(config)
|
||||||
print(yaml_util.dump(config))
|
safe_print(yaml_util.dump(config))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -354,7 +352,7 @@ def command_mqtt_fingerprint(args, config):
|
||||||
|
|
||||||
|
|
||||||
def command_version(args):
|
def command_version(args):
|
||||||
print(u"Version: {}".format(const.__version__))
|
safe_print(u"Version: {}".format(const.__version__))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,7 +494,7 @@ def run_esphomeyaml(argv):
|
||||||
except ESPHomeYAMLError as e:
|
except ESPHomeYAMLError as e:
|
||||||
_LOGGER.error(e)
|
_LOGGER.error(e)
|
||||||
return 1
|
return 1
|
||||||
print(u"Unknown command {}".format(args.command))
|
safe_print(u"Unknown command {}".format(args.command))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ from esphomeyaml.const import CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, C
|
||||||
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \
|
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, \
|
||||||
CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \
|
CONF_LOG_TOPIC, CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_QOS, \
|
||||||
CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \
|
CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_TOPIC, \
|
||||||
CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE
|
CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, CONF_WILL_MESSAGE, CONF_ON_JSON_MESSAGE
|
||||||
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
|
from esphomeyaml.helpers import App, ArrayInitializer, Pvariable, RawExpression, \
|
||||||
StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \
|
StructInitializer, TemplateArguments, add, esphomelib_ns, optional, std_string, templatable, \
|
||||||
uint8, bool_
|
uint8, bool_, JsonObjectRef, process_lambda, JsonObjectConstRef
|
||||||
|
|
||||||
|
|
||||||
def validate_message_just_topic(value):
|
def validate_message_just_topic(value):
|
||||||
|
@ -37,7 +37,9 @@ mqtt_ns = esphomelib_ns.namespace('mqtt')
|
||||||
MQTTMessage = mqtt_ns.MQTTMessage
|
MQTTMessage = mqtt_ns.MQTTMessage
|
||||||
MQTTClientComponent = mqtt_ns.MQTTClientComponent
|
MQTTClientComponent = mqtt_ns.MQTTClientComponent
|
||||||
MQTTPublishAction = mqtt_ns.MQTTPublishAction
|
MQTTPublishAction = mqtt_ns.MQTTPublishAction
|
||||||
|
MQTTPublishJsonAction = mqtt_ns.MQTTPublishJsonAction
|
||||||
MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger
|
MQTTMessageTrigger = mqtt_ns.MQTTMessageTrigger
|
||||||
|
MQTTJsonMessageTrigger = mqtt_ns.MQTTJsonMessageTrigger
|
||||||
|
|
||||||
|
|
||||||
def validate_broker(value):
|
def validate_broker(value):
|
||||||
|
@ -79,9 +81,14 @@ CONFIG_SCHEMA = vol.Schema({
|
||||||
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
|
vol.Optional(CONF_REBOOT_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||||
vol.Optional(CONF_ON_MESSAGE): vol.All(cv.ensure_list, [automation.validate_automation({
|
vol.Optional(CONF_ON_MESSAGE): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger),
|
||||||
vol.Required(CONF_TOPIC): cv.publish_topic,
|
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||||
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
|
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
|
||||||
})])
|
})]),
|
||||||
|
vol.Optional(CONF_ON_JSON_MESSAGE): vol.All(cv.ensure_list, [automation.validate_automation({
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTJsonMessageTrigger),
|
||||||
|
vol.Required(CONF_TOPIC): cv.subscribe_topic,
|
||||||
|
vol.Optional(CONF_QOS, default=0): cv.mqtt_qos,
|
||||||
|
})]),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,6 +167,11 @@ def to_code(config):
|
||||||
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||||
automation.build_automation(trigger, std_string, conf)
|
automation.build_automation(trigger, std_string, conf)
|
||||||
|
|
||||||
|
for conf in config.get(CONF_ON_JSON_MESSAGE, []):
|
||||||
|
rhs = mqtt.make_json_message_trigger(conf[CONF_TOPIC], conf[CONF_QOS])
|
||||||
|
trigger = Pvariable(conf[CONF_TRIGGER_ID], rhs)
|
||||||
|
automation.build_automation(trigger, JsonObjectConstRef, conf)
|
||||||
|
|
||||||
|
|
||||||
CONF_MQTT_PUBLISH = 'mqtt.publish'
|
CONF_MQTT_PUBLISH = 'mqtt.publish'
|
||||||
MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({
|
MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({
|
||||||
|
@ -194,6 +206,35 @@ def mqtt_publish_action_to_code(config, action_id, arg_type):
|
||||||
yield action
|
yield action
|
||||||
|
|
||||||
|
|
||||||
|
CONF_MQTT_PUBLISH_JSON = 'mqtt.publish_json'
|
||||||
|
MQTT_PUBLISH_JSON_ACTION_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic),
|
||||||
|
vol.Required(CONF_PAYLOAD): cv.lambda_,
|
||||||
|
vol.Optional(CONF_QOS): cv.mqtt_qos,
|
||||||
|
vol.Optional(CONF_RETAIN): cv.boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ACTION_REGISTRY.register(CONF_MQTT_PUBLISH_JSON, MQTT_PUBLISH_JSON_ACTION_SCHEMA)
|
||||||
|
def mqtt_publish_json_action_to_code(config, action_id, arg_type):
|
||||||
|
template_arg = TemplateArguments(arg_type)
|
||||||
|
rhs = App.Pget_mqtt_client().Pmake_publish_json_action(template_arg)
|
||||||
|
type = MQTTPublishJsonAction.template(template_arg)
|
||||||
|
action = Pvariable(action_id, rhs, type=type)
|
||||||
|
for template_ in templatable(config[CONF_TOPIC], arg_type, std_string):
|
||||||
|
yield None
|
||||||
|
add(action.set_topic(template_))
|
||||||
|
|
||||||
|
for lambda_ in process_lambda(config[CONF_PAYLOAD], [(arg_type, 'x'), (JsonObjectRef, 'root')]):
|
||||||
|
yield None
|
||||||
|
add(action.set_payload(lambda_))
|
||||||
|
if CONF_QOS in config:
|
||||||
|
add(action.set_qos(config[CONF_QOS]))
|
||||||
|
if CONF_RETAIN in config:
|
||||||
|
add(action.set_retain(config[CONF_RETAIN]))
|
||||||
|
yield action
|
||||||
|
|
||||||
|
|
||||||
def required_build_flags(config):
|
def required_build_flags(config):
|
||||||
if CONF_SSL_FINGERPRINTS in config:
|
if CONF_SSL_FINGERPRINTS in config:
|
||||||
return '-DASYNC_TCP_SSL_ENABLED=1'
|
return '-DASYNC_TCP_SSL_ENABLED=1'
|
||||||
|
|
|
@ -11,6 +11,7 @@ from esphomeyaml import core, yaml_util, core_config
|
||||||
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, CONF_WIFI, ESP_PLATFORMS
|
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, CONF_WIFI, ESP_PLATFORMS
|
||||||
from esphomeyaml.core import ESPHomeYAMLError
|
from esphomeyaml.core import ESPHomeYAMLError
|
||||||
from esphomeyaml.helpers import color
|
from esphomeyaml.helpers import color
|
||||||
|
from esphomeyaml.util import safe_print
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -306,17 +307,17 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
|
||||||
if isinstance(layer, dict):
|
if isinstance(layer, dict):
|
||||||
for key, value in sorted(layer.items(), key=sort_dict_key):
|
for key, value in sorted(layer.items(), key=sort_dict_key):
|
||||||
if isinstance(value, (dict, list)):
|
if isinstance(value, (dict, list)):
|
||||||
print(indent_str, key + ':', line_info(value, **kwargs))
|
safe_print(u"{} {}: {}".format(indent_str, key, line_info(value, **kwargs)))
|
||||||
dump_dict(value, indent_count + 2)
|
dump_dict(value, indent_count + 2)
|
||||||
else:
|
else:
|
||||||
print(indent_str, key + ':', value)
|
safe_print(u"{} {}: {}".format(indent_str, key, value))
|
||||||
indent_str = indent_count * ' '
|
indent_str = indent_count * ' '
|
||||||
if isinstance(layer, (list, tuple)):
|
if isinstance(layer, (list, tuple)):
|
||||||
for i in layer:
|
for i in layer:
|
||||||
if isinstance(i, dict):
|
if isinstance(i, dict):
|
||||||
dump_dict(i, indent_count + 2, True)
|
dump_dict(i, indent_count + 2, True)
|
||||||
else:
|
else:
|
||||||
print(' ', indent_str, i)
|
safe_print(u" {} {}".format(indent_str, i))
|
||||||
|
|
||||||
|
|
||||||
def read_config(path):
|
def read_config(path):
|
||||||
|
@ -334,10 +335,11 @@ def read_config(path):
|
||||||
excepts[domain].append(config)
|
excepts[domain].append(config)
|
||||||
|
|
||||||
if excepts:
|
if excepts:
|
||||||
print(color('bold_white', u"Failed config"))
|
safe_print(color('bold_white', u"Failed config"))
|
||||||
for domain, config in excepts.iteritems():
|
for domain, config in excepts.iteritems():
|
||||||
print(' ', color('bold_red', domain + ':'), color('red', '', reset='red'))
|
safe_print(u' {} {}'.format(color('bold_red', domain + u':'),
|
||||||
|
color('red', '', reset='red')))
|
||||||
dump_dict(config, reset='red')
|
dump_dict(config, reset='red')
|
||||||
print(color('reset'))
|
safe_print(color('reset'))
|
||||||
return None
|
return None
|
||||||
return OrderedDict(res)
|
return OrderedDict(res)
|
||||||
|
|
|
@ -342,6 +342,7 @@ CONF_CRON = 'cron'
|
||||||
CONF_POWER_SAVE_MODE = 'power_save_mode'
|
CONF_POWER_SAVE_MODE = 'power_save_mode'
|
||||||
CONF_POWER_ON_VALUE = 'power_on_value'
|
CONF_POWER_ON_VALUE = 'power_on_value'
|
||||||
CONF_ON_TAG = 'on_tag'
|
CONF_ON_TAG = 'on_tag'
|
||||||
|
CONF_ON_JSON_MESSAGE = 'on_json_message'
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||||
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
|
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
|
||||||
|
|
|
@ -536,6 +536,10 @@ class MockObj(Expression):
|
||||||
obj = MockObj(u'{} &'.format(self.base), u'')
|
obj = MockObj(u'{} &'.format(self.base), u'')
|
||||||
obj.requires.append(self)
|
obj.requires.append(self)
|
||||||
return obj
|
return obj
|
||||||
|
if name == "const":
|
||||||
|
obj = MockObj(u'const {}'.format(self.base), u'')
|
||||||
|
obj.requires.append(self)
|
||||||
|
return obj
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def has_side_effects(self):
|
def has_side_effects(self):
|
||||||
|
@ -568,6 +572,10 @@ NoArg = esphomelib_ns.NoArg
|
||||||
App = esphomelib_ns.App
|
App = esphomelib_ns.App
|
||||||
Application = esphomelib_ns.namespace('Application')
|
Application = esphomelib_ns.namespace('Application')
|
||||||
optional = esphomelib_ns.optional
|
optional = esphomelib_ns.optional
|
||||||
|
arduino_json_ns = global_ns.namespace('ArduinoJson')
|
||||||
|
JsonObject = arduino_json_ns.JsonObject
|
||||||
|
JsonObjectRef = JsonObject.operator('ref')
|
||||||
|
JsonObjectConstRef = JsonObjectRef.operator('const')
|
||||||
|
|
||||||
GPIOPin = esphomelib_ns.GPIOPin
|
GPIOPin = esphomelib_ns.GPIOPin
|
||||||
GPIOOutputPin = esphomelib_ns.GPIOOutputPin
|
GPIOOutputPin = esphomelib_ns.GPIOOutputPin
|
||||||
|
|
|
@ -14,6 +14,7 @@ from esphomeyaml.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOMEYA
|
||||||
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
|
CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TOPIC_PREFIX, \
|
||||||
CONF_USERNAME, CONF_TOPIC, CONF_SSL_FINGERPRINTS
|
CONF_USERNAME, CONF_TOPIC, CONF_SSL_FINGERPRINTS
|
||||||
from esphomeyaml.helpers import color
|
from esphomeyaml.helpers import color
|
||||||
|
from esphomeyaml.util import safe_print
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -70,10 +71,7 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None,
|
||||||
message = time + msg.payload
|
message = time + msg.payload
|
||||||
if escape:
|
if escape:
|
||||||
message = message.replace('\033', '\\033')
|
message = message.replace('\033', '\\033')
|
||||||
try:
|
safe_print(message)
|
||||||
print(message)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
print(message.encode('ascii', 'backslashreplace'))
|
|
||||||
|
|
||||||
return initialize(config, [topic], on_message, username, password, client_id)
|
return initialize(config, [topic], on_message, username, password, client_id)
|
||||||
|
|
||||||
|
@ -113,6 +111,7 @@ def get_fingerprint(config):
|
||||||
|
|
||||||
sha1 = hashlib.sha1(cert_der).hexdigest()
|
sha1 = hashlib.sha1(cert_der).hexdigest()
|
||||||
|
|
||||||
print(u"SHA1 Fingerprint: " + color('cyan', sha1))
|
safe_print(u"SHA1 Fingerprint: " + color('cyan', sha1))
|
||||||
print(u"Copy above string into mqtt.ssl_fingerprints section of {}".format(core.CONFIG_PATH))
|
safe_print(u"Copy the string above into mqtt.ssl_fingerprints section of {}"
|
||||||
|
u"".format(core.CONFIG_PATH))
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
class Registry(dict):
|
class Registry(dict):
|
||||||
def register(self, name):
|
def register(self, name):
|
||||||
def decorator(fun):
|
def decorator(fun):
|
||||||
|
@ -14,3 +17,16 @@ class ServiceRegistry(dict):
|
||||||
return fun
|
return fun
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def safe_print(message=""):
|
||||||
|
try:
|
||||||
|
print(message)
|
||||||
|
return
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(message.encode('ascii', 'backslashreplace'))
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
print("Cannot print line because of invalid locale!")
|
||||||
|
|
|
@ -6,14 +6,13 @@ import unicodedata
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import esphomeyaml.config_validation as cv
|
|
||||||
from esphomeyaml.components import mqtt
|
from esphomeyaml.components import mqtt
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||||
from esphomeyaml.helpers import color
|
from esphomeyaml.helpers import color
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=anomalous-backslash-in-string
|
# pylint: disable=anomalous-backslash-in-string
|
||||||
from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
|
from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
|
||||||
|
from esphomeyaml.util import safe_print
|
||||||
|
|
||||||
CORE_BIG = """ _____ ____ _____ ______
|
CORE_BIG = """ _____ ____ _____ ______
|
||||||
/ ____/ __ \| __ \| ____|
|
/ ____/ __ \| __ \| ____|
|
||||||
|
@ -52,7 +51,7 @@ OTA_BIG = """ ____ _______
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO handle escaping
|
# TODO handle escaping
|
||||||
BASE_CONFIG = """esphomeyaml:
|
BASE_CONFIG = u"""esphomeyaml:
|
||||||
name: {name}
|
name: {name}
|
||||||
platform: {platform}
|
platform: {platform}
|
||||||
board: {board}
|
board: {board}
|
||||||
|
@ -76,9 +75,9 @@ def wizard_file(**kwargs):
|
||||||
config = BASE_CONFIG.format(**kwargs)
|
config = BASE_CONFIG.format(**kwargs)
|
||||||
|
|
||||||
if kwargs['ota_password']:
|
if kwargs['ota_password']:
|
||||||
config += "ota:\n password: '{}'\n".format(kwargs['ota_password'])
|
config += u"ota:\n password: '{}'\n".format(kwargs['ota_password'])
|
||||||
else:
|
else:
|
||||||
config += "ota:\n"
|
config += u"ota:\n"
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
@ -90,18 +89,18 @@ else:
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
def print_step(step, big):
|
def safe_print_step(step, big):
|
||||||
print()
|
safe_print()
|
||||||
print()
|
safe_print()
|
||||||
print("============= STEP {} =============".format(step))
|
safe_print("============= STEP {} =============".format(step))
|
||||||
print(big)
|
safe_print(big)
|
||||||
print("===================================")
|
safe_print("===================================")
|
||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
|
|
||||||
|
|
||||||
def default_input(text, default):
|
def default_input(text, default):
|
||||||
print()
|
safe_print()
|
||||||
print("Press ENTER for default ({})".format(default))
|
safe_print(u"Press ENTER for default ({})".format(default))
|
||||||
return raw_input(text.format(default)) or default
|
return raw_input(text.format(default)) or default
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,30 +112,30 @@ def strip_accents(string):
|
||||||
|
|
||||||
def wizard(path):
|
def wizard(path):
|
||||||
if not path.endswith('.yaml') and not path.endswith('.yml'):
|
if not path.endswith('.yaml') and not path.endswith('.yml'):
|
||||||
print("Please make your configuration file {} have the extension .yaml or .yml".format(
|
safe_print(u"Please make your configuration file {} have the extension .yaml or .yml"
|
||||||
color('cyan', path)))
|
u"".format(color('cyan', path)))
|
||||||
return 1
|
return 1
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
print("Uh oh, it seems like {} already exists, please delete that file first "
|
safe_print(u"Uh oh, it seems like {} already exists, please delete that file first "
|
||||||
"or chose another configuration file.".format(color('cyan', path)))
|
u"or chose another configuration file.".format(color('cyan', path)))
|
||||||
return 1
|
return 1
|
||||||
print("Hi there!")
|
safe_print("Hi there!")
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
print("I'm the wizard of esphomeyaml :)")
|
safe_print("I'm the wizard of esphomeyaml :)")
|
||||||
sleep(1.25)
|
sleep(1.25)
|
||||||
print("And I'm here to help you get started with esphomeyaml.")
|
safe_print("And I'm here to help you get started with esphomeyaml.")
|
||||||
sleep(2.0)
|
sleep(2.0)
|
||||||
print("In 5 steps I'm going to guide you through creating a basic "
|
safe_print("In 5 steps I'm going to guide you through creating a basic "
|
||||||
"configuration file for your custom ESP8266/ESP32 firmware. Yay!")
|
"configuration file for your custom ESP8266/ESP32 firmware. Yay!")
|
||||||
sleep(3.0)
|
sleep(3.0)
|
||||||
print()
|
safe_print()
|
||||||
print_step(1, CORE_BIG)
|
safe_print_step(1, CORE_BIG)
|
||||||
print("First up, please choose a " + color('green', 'name') + " for your node.")
|
safe_print("First up, please choose a " + color('green', 'name') + " for your node.")
|
||||||
print("It should be a unique name that can be used to identify the device later.")
|
safe_print("It should be a unique name that can be used to identify the device later.")
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("For example, I like calling the node in my living room {}.".format(
|
safe_print("For example, I like calling the node in my living room {}.".format(
|
||||||
color('bold_white', "livingroom")))
|
color('bold_white', "livingroom")))
|
||||||
print()
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
name = raw_input(color("bold_white", "(name): "))
|
name = raw_input(color("bold_white", "(name): "))
|
||||||
while True:
|
while True:
|
||||||
|
@ -144,34 +143,34 @@ def wizard(path):
|
||||||
name = cv.valid_name(name)
|
name = cv.valid_name(name)
|
||||||
break
|
break
|
||||||
except vol.Invalid:
|
except vol.Invalid:
|
||||||
print(color("red", "Oh noes, \"{}\" isn't a valid name. Names can only include "
|
safe_print(color("red", u"Oh noes, \"{}\" isn't a valid name. Names can only include "
|
||||||
"numbers, letters and underscores.".format(name)))
|
u"numbers, letters and underscores.".format(name)))
|
||||||
name = strip_accents(name).replace(' ', '_')
|
name = strip_accents(name).replace(' ', '_')
|
||||||
name = u''.join(c for c in name if c in cv.ALLOWED_NAME_CHARS)
|
name = u''.join(c for c in name if c in cv.ALLOWED_NAME_CHARS)
|
||||||
print("Shall I use \"{}\" as the name instead?".format(color('cyan', name)))
|
safe_print(u"Shall I use \"{}\" as the name instead?".format(color('cyan', name)))
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
name = default_input("(name [{}]): ", name)
|
name = default_input(u"(name [{}]): ", name)
|
||||||
|
|
||||||
print("Great! Your node is now called \"{}\".".format(color('cyan', name)))
|
safe_print(u"Great! Your node is now called \"{}\".".format(color('cyan', name)))
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print_step(2, ESP_BIG)
|
safe_print_step(2, ESP_BIG)
|
||||||
print("Now I'd like to know what microcontroller you're using so that I can compile "
|
safe_print("Now I'd like to know what microcontroller you're using so that I can compile "
|
||||||
"firmwares for it.")
|
"firmwares for it.")
|
||||||
print("Are you using an " + color('green', 'ESP32') + " or " +
|
safe_print("Are you using an " + color('green', 'ESP32') + " or " +
|
||||||
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
|
color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)")
|
||||||
while True:
|
while True:
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
print()
|
safe_print()
|
||||||
print("Please enter either ESP32 or ESP8266.")
|
safe_print("Please enter either ESP32 or ESP8266.")
|
||||||
platform = raw_input(color("bold_white", "(ESP32/ESP8266): "))
|
platform = raw_input(color("bold_white", "(ESP32/ESP8266): "))
|
||||||
try:
|
try:
|
||||||
platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform)
|
platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform)
|
||||||
break
|
break
|
||||||
except vol.Invalid:
|
except vol.Invalid:
|
||||||
print("Unfortunately, I can't find an espressif microcontroller called "
|
safe_print(u"Unfortunately, I can't find an espressif microcontroller called "
|
||||||
"\"{}\". Please try again.".format(platform))
|
u"\"{}\". Please try again.".format(platform))
|
||||||
print("Thanks! You've chosen {} as your platform.".format(color('cyan', platform)))
|
safe_print(u"Thanks! You've chosen {} as your platform.".format(color('cyan', platform)))
|
||||||
print()
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
if platform == ESP_PLATFORM_ESP32:
|
if platform == ESP_PLATFORM_ESP32:
|
||||||
|
@ -179,18 +178,18 @@ def wizard(path):
|
||||||
else:
|
else:
|
||||||
board_link = 'http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards'
|
board_link = 'http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards'
|
||||||
|
|
||||||
print("Next, I need to know what " + color('green', 'board') + " you're using.")
|
safe_print("Next, I need to know what " + color('green', 'board') + " you're using.")
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
print("Please go to {} and choose a board.".format(color('green', board_link)))
|
safe_print("Please go to {} and choose a board.".format(color('green', board_link)))
|
||||||
if platform == ESP_PLATFORM_ESP8266:
|
if platform == ESP_PLATFORM_ESP8266:
|
||||||
print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
|
safe_print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)")
|
||||||
print()
|
safe_print()
|
||||||
# Don't sleep because user needs to copy link
|
# Don't sleep because user needs to copy link
|
||||||
if platform == ESP_PLATFORM_ESP32:
|
if platform == ESP_PLATFORM_ESP32:
|
||||||
print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s')))
|
safe_print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s')))
|
||||||
boards = list(ESP32_BOARD_PINS.keys())
|
boards = list(ESP32_BOARD_PINS.keys())
|
||||||
else:
|
else:
|
||||||
print("For example \"{}\".".format(color("bold_white", 'nodemcuv2')))
|
safe_print("For example \"{}\".".format(color("bold_white", 'nodemcuv2')))
|
||||||
boards = list(ESP8266_BOARD_PINS.keys())
|
boards = list(ESP8266_BOARD_PINS.keys())
|
||||||
while True:
|
while True:
|
||||||
board = raw_input(color("bold_white", "(board): "))
|
board = raw_input(color("bold_white", "(board): "))
|
||||||
|
@ -198,71 +197,73 @@ def wizard(path):
|
||||||
board = vol.All(vol.Lower, vol.Any(*boards))(board)
|
board = vol.All(vol.Lower, vol.Any(*boards))(board)
|
||||||
break
|
break
|
||||||
except vol.Invalid:
|
except vol.Invalid:
|
||||||
print(color('red', "Sorry, I don't think the board \"{}\" exists."))
|
safe_print(color('red', "Sorry, I don't think the board \"{}\" exists."))
|
||||||
print()
|
safe_print()
|
||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
print("Possible options are {}".format(', '.join(boards)))
|
safe_print("Possible options are {}".format(', '.join(boards)))
|
||||||
print()
|
safe_print()
|
||||||
|
|
||||||
print("Way to go! You've chosen {} as your board.".format(color('cyan', board)))
|
safe_print(u"Way to go! You've chosen {} as your board.".format(color('cyan', board)))
|
||||||
print()
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
print_step(3, WIFI_BIG)
|
safe_print_step(3, WIFI_BIG)
|
||||||
print("In this step, I'm going to create the configuration for "
|
safe_print("In this step, I'm going to create the configuration for "
|
||||||
"WiFi.")
|
"WiFi.")
|
||||||
print()
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("First, what's the " + color('green', 'SSID') + " (the name) of the WiFi network {} "
|
safe_print("First, what's the " + color('green', 'SSID') +
|
||||||
"I should connect to?".format(name))
|
u" (the name) of the WiFi network {} I should connect to?".format(name))
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
|
safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
|
||||||
while True:
|
while True:
|
||||||
ssid = raw_input(color('bold_white', "(ssid): "))
|
ssid = raw_input(color('bold_white', "(ssid): "))
|
||||||
try:
|
try:
|
||||||
ssid = cv.ssid(ssid)
|
ssid = cv.ssid(ssid)
|
||||||
break
|
break
|
||||||
except vol.Invalid:
|
except vol.Invalid:
|
||||||
print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
|
safe_print(color('red', u"Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
|
||||||
"Please try again.".format(ssid)))
|
u"Please try again.".format(ssid)))
|
||||||
print()
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
print("Thank you very much! You've just chosen \"{}\" as your SSID.".format(
|
safe_print(u"Thank you very much! You've just chosen \"{}\" as your SSID."
|
||||||
color('cyan', ssid)))
|
u"".format(color('cyan', ssid)))
|
||||||
print()
|
safe_print()
|
||||||
sleep(0.75)
|
sleep(0.75)
|
||||||
|
|
||||||
print("Now please state the " + color('green', 'password') +
|
safe_print("Now please state the " + color('green', 'password') +
|
||||||
" of the WiFi network so that I can connect to it (Leave empty for no password)")
|
" of the WiFi network so that I can connect to it (Leave empty for no password)")
|
||||||
print()
|
safe_print()
|
||||||
print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
|
safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
psk = raw_input(color('bold_white', '(PSK): '))
|
psk = raw_input(color('bold_white', '(PSK): '))
|
||||||
print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
|
safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
|
|
||||||
print_step(4, MQTT_BIG)
|
safe_print_step(4, MQTT_BIG)
|
||||||
print("Almost there! Now let's setup MQTT so that your node can connect to the outside world.")
|
safe_print("Almost there! Now let's setup MQTT so that your node can connect to the "
|
||||||
print()
|
"outside world.")
|
||||||
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("Please enter the " + color('green', 'address') + " of your MQTT broker.")
|
safe_print("Please enter the " + color('green', 'address') + " of your MQTT broker.")
|
||||||
print()
|
safe_print()
|
||||||
print("For example \"{}\".".format(color('bold_white', '192.168.178.84')))
|
safe_print("For example \"{}\".".format(color('bold_white', '192.168.178.84')))
|
||||||
while True:
|
while True:
|
||||||
broker = raw_input(color('bold_white', "(broker): "))
|
broker = raw_input(color('bold_white', "(broker): "))
|
||||||
try:
|
try:
|
||||||
broker = mqtt.validate_broker(broker)
|
broker = mqtt.validate_broker(broker)
|
||||||
break
|
break
|
||||||
except vol.Invalid as err:
|
except vol.Invalid as err:
|
||||||
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
|
safe_print(color('red', u"The broker address \"{}\" seems to be invalid: {} :("
|
||||||
broker, err)))
|
u"".format(broker, err)))
|
||||||
print("Please try again.")
|
safe_print("Please try again.")
|
||||||
print()
|
safe_print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
print("Thanks! Now enter the " + color('green', 'username') + " and " +
|
safe_print("Thanks! Now enter the " + color('green', 'username') + " and " +
|
||||||
color('green', 'password') + " for the MQTT broker. Leave empty for no authentication.")
|
color('green', 'password') +
|
||||||
|
" for the MQTT broker. Leave empty for no authentication.")
|
||||||
mqtt_username = raw_input(color('bold_white', '(username): '))
|
mqtt_username = raw_input(color('bold_white', '(username): '))
|
||||||
mqtt_password = ''
|
mqtt_password = ''
|
||||||
if mqtt_username:
|
if mqtt_username:
|
||||||
|
@ -271,19 +272,19 @@ def wizard(path):
|
||||||
show = '*' * len(mqtt_password)
|
show = '*' * len(mqtt_password)
|
||||||
if len(mqtt_password) >= 2:
|
if len(mqtt_password) >= 2:
|
||||||
show = mqtt_password[:2] + '*' * len(mqtt_password)
|
show = mqtt_password[:2] + '*' * len(mqtt_password)
|
||||||
print("MQTT Username: \"{}\"; Password: \"{}\"".format(
|
safe_print(u"MQTT Username: \"{}\"; Password: \"{}\""
|
||||||
color('cyan', mqtt_username), color('cyan', show)))
|
u"".format(color('cyan', mqtt_username), color('cyan', show)))
|
||||||
else:
|
else:
|
||||||
print("No authentication for MQTT")
|
safe_print("No authentication for MQTT")
|
||||||
|
|
||||||
print_step(5, OTA_BIG)
|
safe_print_step(5, OTA_BIG)
|
||||||
print("Last step! esphomeyaml can automatically upload custom firmwares over WiFi "
|
safe_print("Last step! esphomeyaml can automatically upload custom firmwares over WiFi "
|
||||||
"(over the air).")
|
"(over the air).")
|
||||||
print("This can be insecure if you do not trust the WiFi network. Do you want to set "
|
safe_print("This can be insecure if you do not trust the WiFi network. Do you want to set "
|
||||||
"an " + color('green', 'OTA password') + " for remote updates?")
|
"an " + color('green', 'OTA password') + " for remote updates?")
|
||||||
print()
|
safe_print()
|
||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
print("Press ENTER for no password")
|
safe_print("Press ENTER for no password")
|
||||||
ota_password = raw_input(color('bold_white', '(password): '))
|
ota_password = raw_input(color('bold_white', '(password): '))
|
||||||
|
|
||||||
config = wizard_file(name=name, platform=platform, board=board,
|
config = wizard_file(name=name, platform=platform, board=board,
|
||||||
|
@ -294,19 +295,19 @@ def wizard(path):
|
||||||
with codecs.open(path, 'w') as f_handle:
|
with codecs.open(path, 'w') as f_handle:
|
||||||
f_handle.write(config)
|
f_handle.write(config)
|
||||||
|
|
||||||
print()
|
safe_print()
|
||||||
print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
safe_print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
||||||
color('bold_cyan', path))
|
color('bold_cyan', path))
|
||||||
print()
|
safe_print()
|
||||||
print("Next steps:")
|
safe_print("Next steps:")
|
||||||
print(" > If you haven't already, enable MQTT discovery in Home Assistant:")
|
safe_print(" > If you haven't already, enable MQTT discovery in Home Assistant:")
|
||||||
print()
|
safe_print()
|
||||||
print(color('bold_white', "# In your configuration.yaml"))
|
safe_print(color('bold_white', "# In your configuration.yaml"))
|
||||||
print(color('bold_white', "mqtt:"))
|
safe_print(color('bold_white', "mqtt:"))
|
||||||
print(color('bold_white', " broker: {}".format(broker)))
|
safe_print(color('bold_white', u" broker: {}".format(broker)))
|
||||||
print(color('bold_white', " # ..."))
|
safe_print(color('bold_white', " # ..."))
|
||||||
print(color('bold_white', " discovery: True"))
|
safe_print(color('bold_white', " discovery: True"))
|
||||||
print()
|
safe_print()
|
||||||
print(" > Then follow the rest of the getting started guide:")
|
safe_print(" > Then follow the rest of the getting started guide:")
|
||||||
print(" > https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html")
|
safe_print(" > https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html")
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -102,6 +102,7 @@ def get_ini_content(config, path):
|
||||||
build_flags |= get_build_flags(config, 'build_flags')
|
build_flags |= get_build_flags(config, 'build_flags')
|
||||||
build_flags |= get_build_flags(config, 'BUILD_FLAGS')
|
build_flags |= get_build_flags(config, 'BUILD_FLAGS')
|
||||||
build_flags.add(u"-DESPHOMEYAML_USE")
|
build_flags.add(u"-DESPHOMEYAML_USE")
|
||||||
|
build_flags.add("-Wno-unused-variable")
|
||||||
build_flags |= get_build_flags(config, 'required_build_flags')
|
build_flags |= get_build_flags(config, 'required_build_flags')
|
||||||
build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS')
|
build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS')
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,24 @@ mqtt:
|
||||||
then:
|
then:
|
||||||
- deep_sleep.enter:
|
- deep_sleep.enter:
|
||||||
id: deep_sleep_1
|
id: deep_sleep_1
|
||||||
|
on_json_message:
|
||||||
|
topic: the/topic
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
int data = x["my_data"];
|
||||||
|
ESP_LOGD("main", "The data is: %d", data);
|
||||||
|
- light.turn_on:
|
||||||
|
id: living_room_lights
|
||||||
|
transition_length: !lambda |-
|
||||||
|
int length = 1000;
|
||||||
|
if (x.containsKey("length"))
|
||||||
|
length = x["length"];
|
||||||
|
return length;
|
||||||
|
effect: !lambda |-
|
||||||
|
const char *effect = "None";
|
||||||
|
if (x.containsKey("effect"))
|
||||||
|
effect = x["effect"];
|
||||||
|
return effect;
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
sda: 21
|
sda: 21
|
||||||
|
@ -599,6 +617,7 @@ light:
|
||||||
state = 0;
|
state = 0;
|
||||||
- platform: rgb
|
- platform: rgb
|
||||||
name: "Living Room Lights"
|
name: "Living Room Lights"
|
||||||
|
id: living_room_lights
|
||||||
red: pca_0
|
red: pca_0
|
||||||
green: pca_1
|
green: pca_1
|
||||||
blue: pca_2
|
blue: pca_2
|
||||||
|
|
|
@ -76,7 +76,14 @@ sensor:
|
||||||
- platform: mqtt_subscribe
|
- platform: mqtt_subscribe
|
||||||
name: "MQTT Subscribe Sensor 1"
|
name: "MQTT Subscribe Sensor 1"
|
||||||
topic: "mqtt/topic"
|
topic: "mqtt/topic"
|
||||||
|
id: the_sensor
|
||||||
qos: 2
|
qos: 2
|
||||||
|
on_value:
|
||||||
|
- mqtt.publish_json:
|
||||||
|
topic: the/topic
|
||||||
|
payload: |-
|
||||||
|
root["key"] = id(the_sensor).value;
|
||||||
|
root["greeting"] = "Hello World";
|
||||||
- platform: cse7766
|
- platform: cse7766
|
||||||
voltage:
|
voltage:
|
||||||
name: "CSE7766 Voltage"
|
name: "CSE7766 Voltage"
|
||||||
|
|
Loading…
Reference in a new issue