esphome/esphomeyaml/wizard.py
2018-06-03 19:22:25 +02:00

310 lines
11 KiB
Python

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