Beispiel #1
0
def line_info(obj, **kwargs):
    """Display line config source."""
    if hasattr(obj, '__config_file__'):
        return color(
            'cyan', "[source {}:{}]".format(obj.__config_file__, obj.__line__
                                            or '?'), **kwargs)
    return '?'
Beispiel #2
0
def choose_serial_port(config):
    result = get_serial_ports()

    if not result:
        return 'OTA'
    print(u"Found multiple serial port options, please choose one:")
    for i, (res, desc) in enumerate(result):
        print(u"  [{}] {} ({})".format(i, res, desc))
    print(u"  [{}] Over The Air ({})".format(len(result),
                                             get_upload_host(config)))
    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 'OTA'
    return result[opt][0]
Beispiel #3
0
def line_info(obj, highlight=True):
    """Display line config source."""
    if not highlight:
        return None
    if hasattr(obj, '__config_file__'):
        return color('cyan', "[source {}:{}]"
                     .format(obj.__config_file__, obj.__line__ or '?'))
    return None
Beispiel #4
0
 def on_log(msg):
     time_ = datetime.now().time().strftime(u'[%H:%M:%S]')
     text = msg.message
     if msg.send_failed:
         text = color(
             'white', '(Message skipped because it was too big to fit in '
             'TCP buffer - This is only cosmetic)')
     safe_print(time_ + text)
Beispiel #5
0
def read_config(verbose):
    _LOGGER.info("Reading configuration...")
    try:
        res = load_config()
    except EsphomeyamlError as err:
        _LOGGER.error(u"Error while reading config: %s", err)
        return None
    if res.errors:
        if not verbose:
            res = strip_default_ids(res)

        safe_print(color('bold_red', u"Failed config"))
        safe_print('')
        for path, domain in res.domains:
            if not res.is_in_error_path(path):
                continue

            safe_print(color('bold_red', u'{}:'.format(domain)) + u' ' +
                       (line_info(res.nested_item(path)) or u''))
            safe_print(indent(dump_dict(res, path)[0]))
        return None
    return OrderedDict(res)
Beispiel #6
0
def read_config(path):
    _LOGGER.info("Reading configuration...")
    try:
        res = load_config(path)
    except ESPHomeYAMLError as err:
        _LOGGER.error(u"Error while reading config: %s", err)
        return None
    excepts = {}
    for message, domain, config in res.errors:
        domain = domain or u"General Error"
        excepts.setdefault(domain, []).append(message)
        if config is not None:
            excepts[domain].append(config)

    if excepts:
        print(color('bold_white', u"Failed config"))
        for domain, config in excepts.iteritems():
            print(' ', color('bold_red', domain + ':'), color('red', '', reset='red'))
            dump_dict(config, reset='red')
            print(color('reset'))
        return None
    return OrderedDict(res)
Beispiel #7
0
def get_fingerprint(config):
    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()

    safe_print(u"SHA1 Fingerprint: " + color('cyan', sha1))
    safe_print(u"Copy the string above into mqtt.ssl_fingerprints section of {}"
               u"".format(CORE.config_path))
    return 0
Beispiel #8
0
def discover_serial_ports():
    # from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
    try:
        from serial.tools.list_ports import comports
    except ImportError:
        return None

    result = []
    descs = []
    for port, desc, info in comports():
        if not port:
            continue
        if "VID:PID" in info:
            result.append(port)
            descs.append(desc)

    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]
Beispiel #9
0
def choose_prompt(options):
    if not options:
        raise ValueError

    if len(options) == 1:
        return options[0][1]

    safe_print(u"Found multiple options, please choose one:")
    for i, (desc, _) in enumerate(options):
        safe_print(u"  [{}] {}".format(i + 1, desc))

    while True:
        opt = safe_input('(number): ')
        if opt in options:
            opt = options.index(opt)
            break
        try:
            opt = int(opt)
            if opt < 1 or opt > len(options):
                raise ValueError
            break
        except ValueError:
            safe_print(color('red', u"Invalid option: '{}'".format(opt)))
    return options[opt - 1][1]
Beispiel #10
0
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
Beispiel #11
0
def wizard(path):
    if not path.endswith('.yaml') and not path.endswith('.yml'):
        safe_print(
            u"Please make your configuration file {} have the extension .yaml or .yml"
            u"".format(color('cyan', path)))
        return 1
    if os.path.exists(path):
        safe_print(
            u"Uh oh, it seems like {} already exists, please delete that file first "
            u"or chose another configuration file.".format(color('cyan',
                                                                 path)))
        return 1
    safe_print("Hi there!")
    sleep(1.5)
    safe_print("I'm the wizard of ESPHome :)")
    sleep(1.25)
    safe_print("And I'm here to help you get started with ESPHome.")
    sleep(2.0)
    safe_print(
        "In 4 steps I'm going to guide you through creating a basic "
        "configuration file for your custom ESP8266/ESP32 firmware. Yay!")
    sleep(3.0)
    safe_print()
    safe_print_step(1, CORE_BIG)
    safe_print("First up, please choose a " + color('green', 'name') +
               " for your node.")
    safe_print(
        "It should be a unique name that can be used to identify the device later."
    )
    sleep(1)
    safe_print(
        "For example, I like calling the node in my living room {}.".format(
            color('bold_white', "livingroom")))
    safe_print()
    sleep(1)
    name = safe_input(color("bold_white", "(name): "))
    while True:
        try:
            name = cv.valid_name(name)
            break
        except vol.Invalid:
            safe_print(
                color(
                    "red",
                    u"Oh noes, \"{}\" isn't a valid name. Names can only include "
                    u"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)
            safe_print(u"Shall I use \"{}\" as the name instead?".format(
                color('cyan', name)))
            sleep(0.5)
            name = default_input(u"(name [{}]): ", name)

    safe_print(u"Great! Your node is now called \"{}\".".format(
        color('cyan', name)))
    sleep(1)
    safe_print_step(2, ESP_BIG)
    safe_print(
        "Now I'd like to know what microcontroller you're using so that I can compile "
        "firmwares for it.")
    safe_print("Are you using an " + color('green', 'ESP32') + " or " +
               color('green', 'ESP8266') +
               " platform? (Choose ESP8266 for Sonoff devices)")
    while True:
        sleep(0.5)
        safe_print()
        safe_print("Please enter either ESP32 or ESP8266.")
        platform = safe_input(color("bold_white", "(ESP32/ESP8266): "))
        try:
            platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform)
            break
        except vol.Invalid:
            safe_print(
                u"Unfortunately, I can't find an espressif microcontroller called "
                u"\"{}\". Please try again.".format(platform))
    safe_print(u"Thanks! You've chosen {} as your platform.".format(
        color('cyan', platform)))
    safe_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'

    safe_print("Next, I need to know what " + color('green', 'board') +
               " you're using.")
    sleep(0.5)
    safe_print("Please go to {} and choose a board.".format(
        color('green', board_link)))
    if platform == ESP_PLATFORM_ESP8266:
        safe_print("(Type " + color('green', 'esp01_1m') +
                   " for Sonoff devices)")
    safe_print()
    # Don't sleep because user needs to copy link
    if platform == ESP_PLATFORM_ESP32:
        safe_print("For example \"{}\".".format(
            color("bold_white", 'nodemcu-32s')))
        boards = list(ESP32_BOARD_PINS.keys())
    else:
        safe_print("For example \"{}\".".format(
            color("bold_white", 'nodemcuv2')))
        boards = list(ESP8266_BOARD_PINS.keys())
    safe_print("Options: {}".format(', '.join(boards)))

    while True:
        board = safe_input(color("bold_white", "(board): "))
        try:
            board = vol.All(vol.Lower, vol.Any(*boards))(board)
            break
        except vol.Invalid:
            safe_print(
                color('red', "Sorry, I don't think the board \"{}\" exists."))
            safe_print()
            sleep(0.25)
            safe_print()

    safe_print(u"Way to go! You've chosen {} as your board.".format(
        color('cyan', board)))
    safe_print()
    sleep(1)

    safe_print_step(3, WIFI_BIG)
    safe_print("In this step, I'm going to create the configuration for "
               "WiFi.")
    safe_print()
    sleep(1)
    safe_print("First, what's the " + color('green', 'SSID') +
               u" (the name) of the WiFi network {} I should connect to?".
               format(name))
    sleep(1.5)
    safe_print("For example \"{}\".".format(
        color('bold_white', "Abraham Linksys")))
    while True:
        ssid = safe_input(color('bold_white', "(ssid): "))
        try:
            ssid = cv.ssid(ssid)
            break
        except vol.Invalid:
            safe_print(
                color(
                    'red',
                    u"Unfortunately, \"{}\" doesn't seem to be a valid SSID. "
                    u"Please try again.".format(ssid)))
            safe_print()
            sleep(1)

    safe_print(u"Thank you very much! You've just chosen \"{}\" as your SSID."
               u"".format(color('cyan', ssid)))
    safe_print()
    sleep(0.75)

    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)"
    )
    safe_print()
    safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
    sleep(0.5)
    psk = safe_input(color('bold_white', '(PSK): '))
    safe_print(
        "Perfect! WiFi is now set up (you can create static IPs and so on later)."
    )
    sleep(1.5)

    safe_print_step(4, OTA_BIG)
    safe_print(
        "Almost there! ESPHome can automatically upload custom firmwares over WiFi "
        "(over the air) and integrates into Home Assistant with a native API.")
    safe_print(
        "This can be insecure if you do not trust the WiFi network. Do you want to set "
        "a " + color('green', 'password') + " for connecting to this ESP?")
    safe_print()
    sleep(0.25)
    safe_print("Press ENTER for no password")
    password = safe_input(color('bold_white', '(password): '))

    wizard_write(path=path,
                 name=name,
                 platform=platform,
                 board=board,
                 ssid=ssid,
                 psk=psk,
                 password=password)

    safe_print()
    safe_print(
        color('cyan', "DONE! I've now written a new configuration file to ") +
        color('bold_cyan', path))
    safe_print()
    safe_print("Next steps:")
    safe_print(
        "  > Check your Home Assistant \"integrations\" screen. If all goes well, you "
        "should see your ESP being discovered automatically.")
    safe_print("  > Then follow the rest of the getting started guide:")
    safe_print(
        "  > https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html"
    )
    return 0
Beispiel #12
0
def dump_dict(config, path, at_root=True):
    # type: (Config, ConfigPath, bool) -> Tuple[unicode, bool]
    conf = config.nested_item(path)
    ret = u''
    multiline = False

    if at_root:
        error = config.get_error_for_path(path)
        if error is not None:
            ret += u'\n' + color('bold_red', error) + u'\n'

    if isinstance(conf, (list, tuple)):
        multiline = True
        if not conf:
            ret += u'[]'
            multiline = False

        for i in range(len(conf)):
            path_ = path + [i]
            error = config.get_error_for_path(path_)
            if error is not None:
                ret += u'\n' + color('bold_red', error) + u'\n'

            sep = u'- '
            if config.is_in_error_path(path_):
                sep = color('red', sep)
            msg, _ = dump_dict(config, path_, at_root=False)
            msg = indent(msg)
            inf = line_info(config.nested_item(path_),
                            highlight=config.is_in_error_path(path_))
            if inf is not None:
                msg = inf + u'\n' + msg
            elif msg:
                msg = msg[2:]
            ret += sep + msg + u'\n'
    elif isinstance(conf, dict):
        multiline = True
        if not conf:
            ret += u'{}'
            multiline = False

        for k in conf.keys():
            path_ = path + [k]
            error = config.get_error_for_path(path_)
            if error is not None:
                ret += u'\n' + color('bold_red', error) + u'\n'

            st = u'{}: '.format(k)
            if config.is_in_error_path(path_):
                st = color('red', st)
            msg, m = dump_dict(config, path_, at_root=False)

            inf = line_info(config.nested_item(path_),
                            highlight=config.is_in_error_path(path_))
            if m:
                msg = u'\n' + indent(msg)

            if inf is not None:
                if m:
                    msg = u' ' + inf + msg
                else:
                    msg = msg + u' ' + inf
            ret += st + msg + u'\n'
    elif isinstance(conf, str):
        if not conf:
            conf += u"''"

        if len(conf) > 80:
            conf = u'|-\n' + indent(conf)
        error = config.get_error_for_path(path)
        col = 'bold_red' if error else 'white'
        ret += color(col, text_type(conf))
    elif isinstance(conf, core.Lambda):
        conf = u'!lambda |-\n' + indent(text_type(conf.value))
        error = config.get_error_for_path(path)
        col = 'bold_red' if error else 'white'
        ret += color(col, conf)
    elif conf is None:
        pass
    else:
        error = config.get_error_for_path(path)
        col = 'bold_red' if error else 'white'
        ret += color(col, text_type(conf))
        multiline = u'\n' in ret

    return ret, multiline