def safe_print_step(step, big): safe_print() safe_print() safe_print("============= STEP {} =============".format(step)) safe_print(big) safe_print("===================================") sleep(0.25)
def update_esphomelib_repo(config): esphomelib_version = config[CONF_ESPHOMELIB_VERSION] if CONF_REPOSITORY not in esphomelib_version: return build_path = relative_path(config[CONF_BUILD_PATH]) esphomelib_path = os.path.join(build_path, '.piolibdeps', 'esphomelib') is_default_branch = all(x not in esphomelib_version for x in (CONF_BRANCH, CONF_TAG, CONF_COMMIT)) if not (CONF_BRANCH in esphomelib_version or is_default_branch): # Git commit hash or tag cannot be updated return rc, _, _ = run_command('git', '-C', esphomelib_path, '--help') if rc != 0: # git not installed or repo not downloaded yet return rc, _, _ = run_command('git', '-C', esphomelib_path, 'diff-index', '--quiet', 'HEAD', '--') if rc != 0: # local changes, cannot update _LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.") return _LOGGER.info("Updating esphomelib copy from git (%s)", esphomelib_path) rc, stdout, _ = run_command('git', '-c', 'color.ui=always', '-C', esphomelib_path, 'pull', '--stat') if rc != 0: _LOGGER.warn("Couldn't auto-update local git copy of esphomelib.") return stdout = stdout.strip() if 'Already up to date' in stdout: return safe_print(stdout)
def choose_serial_port(config): result = get_serial_ports() if not result: return 'OTA' safe_print(u"Found multiple serial port options, please choose one:") for i, (res, desc) in enumerate(result): safe_print(u" [{}] {} ({})".format(i, res, desc)) safe_print(u" [{}] Over The Air ({})".format(len(result), get_upload_host(config))) safe_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: safe_print(color('red', u"Invalid option: '{}'".format(opt))) if opt == len(result): return 'OTA' return result[opt][0]
def update_esphomelib_repo(): if CONF_REPOSITORY not in CORE.esphomelib_version: return if CONF_BRANCH not in CORE.esphomelib_version: # Git commit hash or tag cannot be updated return esphomelib_path = CORE.relative_build_path('.piolibdeps', 'esphomelib') rc, _, _ = run_system_command('git', '-C', esphomelib_path, '--help') if rc != 0: # git not installed or repo not downloaded yet return rc, _, _ = run_system_command('git', '-C', esphomelib_path, 'diff-index', '--quiet', 'HEAD', '--') if rc != 0: # local changes, cannot update _LOGGER.warning("Local changes in esphomelib copy from git. Will not auto-update.") return _LOGGER.info("Updating esphomelib copy from git (%s)", esphomelib_path) rc, stdout, _ = run_system_command('git', '-c', 'color.ui=always', '-C', esphomelib_path, 'pull', '--stat') if rc != 0: _LOGGER.warning("Couldn't auto-update local git copy of esphomelib.") return if IS_PY3: stdout = stdout.decode('utf-8', 'backslashreplace') safe_print(stdout.strip())
def run_esphomeyaml(argv): args = parse_args(argv) CORE.dashboard = args.dashboard setup_log(args.verbose) if args.command in PRE_CONFIG_ACTIONS: try: return PRE_CONFIG_ACTIONS[args.command](args) except EsphomeyamlError as e: _LOGGER.error(e) return 1 CORE.config_path = args.configuration config = read_config(args.verbose) if config is None: return 1 CORE.config = config if args.command in POST_CONFIG_ACTIONS: try: return POST_CONFIG_ACTIONS[args.command](args, config) except EsphomeyamlError as e: _LOGGER.error(e) return 1 safe_print(u"Unknown command {}".format(args.command)) return 1
def run_miniterm(config, port, escape=False): import serial if CONF_LOGGER not in config: _LOGGER.info("Logger is not enabled. Not starting UART logs.") return baud_rate = config['logger'][CONF_BAUD_RATE] if baud_rate == 0: _LOGGER.info( "UART logging is disabled (baud_rate=0). Not starting UART logs.") _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate) backtrace_state = False with serial.Serial(port, baudrate=baud_rate) as ser: while True: try: raw = ser.readline() except serial.SerialException: _LOGGER.error("Serial port closed!") return line = raw.replace('\r', '').replace('\n', '') time = datetime.now().time().strftime('[%H:%M:%S]') message = time + line if escape: message = message.replace('\033', '\\033') safe_print(message) backtrace_state = platformio_api.process_stacktrace( config, line, backtrace_state=backtrace_state)
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)
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
def command_hass_config(args, config): from esphomeyaml.components import mqtt as mqtt_component _LOGGER.info( "This is what you should put in your Home Assistant YAML configuration." ) _LOGGER.info( "Please note this is only necessary if you're not using MQTT discovery." ) data = mqtt_component.GenerateHassConfigData(config) hass_config = OrderedDict() for domain, component, conf in iter_components(config): if not hasattr(component, 'to_hass_config'): continue func = getattr(component, 'to_hass_config') ret = func(data, conf) if not isinstance(ret, (list, tuple)): ret = [ret] ret = [x for x in ret if x is not None] domain_conf = hass_config.setdefault(domain.split('.')[0], []) domain_conf += ret safe_print(yaml_util.dump(hass_config)) return 0
def run_esphomeyaml(argv): args = parse_args(argv) setup_log(args.verbose) if args.command in PRE_CONFIG_ACTIONS: try: return PRE_CONFIG_ACTIONS[args.command](args) except ESPHomeYAMLError as e: _LOGGER.error(e) return 1 core.CONFIG_PATH = args.configuration config = read_config(core.CONFIG_PATH) if config is None: return 1 if args.command in POST_CONFIG_ACTIONS: try: return POST_CONFIG_ACTIONS[args.command](args, config) except ESPHomeYAMLError as e: _LOGGER.error(e) return 1 safe_print(u"Unknown command {}".format(args.command)) return 1
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)
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: safe_print(color('bold_white', u"Failed config")) for domain, config in excepts.iteritems(): safe_print(u' {} {}'.format(color('bold_red', domain + u':'), color('red', '', reset='red'))) dump_dict(config, reset='red') safe_print(color('reset')) return None return OrderedDict(res)
def dump_dict(layer, indent_count=3, listi=False, **kwargs): def sort_dict_key(val): """Return the dict key for sorting.""" key = str.lower(val[0]) return '0' if key == 'platform' else key indent_str = indent_count * ' ' if listi or isinstance(layer, list): indent_str = indent_str[:-1] + '-' if isinstance(layer, dict): for key, value in sorted(layer.items(), key=sort_dict_key): if isinstance(value, (dict, list)): safe_print(u"{} {}: {}".format(indent_str, key, line_info(value, **kwargs))) dump_dict(value, indent_count + 2) else: safe_print(u"{} {}: {}".format(indent_str, key, value)) indent_str = indent_count * ' ' if isinstance(layer, (list, tuple)): for i in layer: if isinstance(i, dict): dump_dict(i, indent_count + 2, True) else: safe_print(u" {} {}".format(indent_str, i))
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]
def on_message(client, userdata, msg): time_ = datetime.now().time().strftime(u'[%H:%M:%S]') message = time_ + msg.payload safe_print(message)
def command_config(args, config): _LOGGER.info("Configuration is valid!") if not args.verbose: config = strip_default_ids(config) safe_print(yaml_util.dump(config)) return 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 esphomeyaml :)") sleep(1.25) safe_print("And I'm here to help you get started with esphomeyaml.") sleep(2.0) safe_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) 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 = raw_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 = raw_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()) while True: board = raw_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("Possible options are {}".format(', '.join(boards))) 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 = raw_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 = raw_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, MQTT_BIG) safe_print( "Almost there! Now let's setup MQTT so that your node can connect to the " "outside world.") safe_print() sleep(1) safe_print("Please enter the " + color('green', 'address') + " of your MQTT broker.") safe_print() safe_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: safe_print( color( 'red', u"The broker address \"{}\" seems to be invalid: {} :(" u"".format(broker, err))) safe_print("Please try again.") safe_print() sleep(1) safe_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) safe_print(u"MQTT Username: \"{}\"; Password: \"{}\"" u"".format(color('cyan', mqtt_username), color('cyan', show))) else: safe_print("No authentication for MQTT") safe_print_step(5, OTA_BIG) safe_print( "Last step! esphomeyaml can automatically upload custom firmwares over WiFi " "(over the air).") 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?") safe_print() sleep(0.25) safe_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) 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( " > If you haven't already, enable MQTT discovery in Home Assistant:") safe_print() safe_print(color('bold_white', "# In your configuration.yaml")) safe_print(color('bold_white', "mqtt:")) safe_print(color('bold_white', u" broker: {}".format(broker))) safe_print(color('bold_white', " # ...")) safe_print(color('bold_white', " discovery: True")) safe_print() 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
def command_version(args): safe_print(u"Version: {}".format(const.__version__)) return 0
def command_config(args, config): if not args.verbose: config = strip_default_ids(config) safe_print(yaml_util.dump(config)) return 0
def on_message(client, userdata, msg): time = datetime.now().time().strftime(u'[%H:%M:%S]') message = time + msg.payload if escape: message = message.replace('\033', '\\033') safe_print(message)
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
def default_input(text, default): safe_print() safe_print(u"Press ENTER for default ({})".format(default)) return safe_input(text.format(default)) or default