def to_code(config): from PIL import Image for conf in config: path = relative_path(conf[CONF_FILE]) try: image = Image.open(path) except Exception as e: raise core.ESPHomeYAMLError(u"Could not load image file {}: {}".format(path, e)) if CONF_RESIZE in conf: image.thumbnail(conf[CONF_RESIZE]) image = image.convert('1', dither=Image.NONE) width, height = image.size if width > 500 or height > 500: _LOGGER.warning("The image you requested is very big. Please consider using the resize " "parameter") width8 = ((width + 7) // 8) * 8 data = [0 for _ in range(height * width8 // 8)] for y in range(height): for x in range(width): if image.getpixel((x, y)): continue pos = x + y * width8 data[pos // 8] |= 0x80 >> (pos % 8) raw_data = MockObj(conf[CONF_RAW_DATA_ID]) add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format( raw_data, len(data), ArrayInitializer(*[HexInt(x) for x in data], multiline=False)))) rhs = App.make_image(raw_data, width, height) Pvariable(conf[CONF_ID], rhs)
def run_platformio_cli_run(config, verbose, *args, **kwargs): build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) command = ['run', '-d', build_path] if verbose: command += ['-v'] command += list(args) return run_platformio_cli(*command, **kwargs)
def compile_program(args, config): _LOGGER.info("Compiling app...") build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) command = ['platformio', 'run', '-d', build_path] if args.verbose: command.append('-v') return run_platformio(*command)
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 rc, _, _ = run_command('git', '-C', esphomelib_path, 'pull') if rc != 0: _LOGGER.warn("Couldn't auto-update local git copy of esphomelib.") return
def write_cpp(config): _LOGGER.info("Generating C++ source...") add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml') for domain in PRE_INITIALIZE: if domain == CONF_ESPHOMEYAML or domain not in config: continue add_job(get_component(domain).to_code, config[domain], domain=domain) for domain, component, conf in iter_components(config): if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'): continue add_job(component.to_code, conf, domain=domain) flush_tasks() add(RawStatement('')) add(RawStatement('')) all_code = [] for exp in _EXPRESSIONS: if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]: if isinstance(exp, Expression) and not exp.required: continue if isinstance(exp, AssignmentExpression) and not exp.obj.required: if not exp.has_side_effects(): continue exp = exp.rhs all_code.append(unicode(statement(exp))) build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) writer.write_platformio_project(config, build_path) code_s = indent('\n'.join(line.rstrip() for line in all_code)) cpp_path = os.path.join(build_path, 'src', 'main.cpp') writer.write_cpp(code_s, cpp_path) return 0
def validate_local_esphomelib_version(value): value = cv.directory(value) path = relative_path(value) library_json = os.path.join(path, 'library.json') if not os.path.exists(library_json): raise vol.Invalid(u"Could not find '{}' file. '{}' does not seem to point to an " u"esphomelib copy.".format(library_json, value)) return value
def command_clean(args, config): build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) try: writer.clean_build(build_path) except OSError as err: _LOGGER.error("Error deleting build files: %s", err) return 1 _LOGGER.info("Done!") return 0
def upload_using_esptool(config, port): import esptool build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') # pylint: disable=protected-access return run_platformio('esptool.py', '--before', 'default_reset', '--after', 'hard_reset', '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path, main=esptool._main)
def file_(value): value = string(value) path = helpers.relative_path(value) if not os.path.exists(path): raise vol.Invalid( u"Could not find file '{}'. Please make sure it exists.".format( path)) if not os.path.isfile(path): raise vol.Invalid(u"Path '{}' is not a file.".format(path)) return value
def directory(value): value = string(value) path = helpers.relative_path(value) if not os.path.exists(path): raise vol.Invalid( u"Could not find directory '{}'. Please make sure it exists.". format(path)) if not os.path.isdir(path): raise vol.Invalid(u"Path '{}' is not a directory.".format(path)) return value
def upload_program(config, args, port): build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) # if upload is to a serial port use platformio, otherwise assume ota serial_port = port.startswith('/') or port.startswith('COM') if port != 'OTA' and serial_port: if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy: return upload_using_esptool(config, port) command = [ 'platformio', 'run', '-d', build_path, '-t', 'upload', '--upload-port', port ] if args.verbose: command.append('-v') return run_platformio(*command) if 'ota' not in config: _LOGGER.error( "No serial port found and OTA not enabled. Can't upload!") return -1 # If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf # hostname. This is to support use cases where zeroconf (hostname.local) does not work. if port != 'OTA': host = port else: host = get_upload_host(config) from esphomeyaml.components import ota from esphomeyaml import espota2 bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') if args.host_port is not None: host_port = args.host_port else: host_port = int( os.getenv('ESPHOMEYAML_OTA_HOST_PORT', random.randint(10000, 60000))) verbose = args.verbose remote_port = ota.get_port(config) password = ota.get_auth(config) res = espota2.run_ota(host, remote_port, password, bin_file) if res == 0: return res _LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...") return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, bin_file)
def upload_program(config, args, port): _LOGGER.info("Uploading binary...") build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) # if upload is to a serial port use platformio, otherwise assume ota serial_port = port.startswith('/') or port.startswith('COM') if port != 'OTA' and serial_port: if core.ESP_PLATFORM == ESP_PLATFORM_ESP8266 and args.use_esptoolpy: return upload_using_esptool(config, port) command = [ 'platformio', 'run', '-d', build_path, '-t', 'upload', '--upload-port', port ] if args.verbose: command.append('-v') return run_platformio(*command) if 'ota' not in config: _LOGGER.error( "No serial port found and OTA not enabled. Can't upload!") return -1 # If hostname/ip is explicitly provided as upload-port argument, use this instead of zeroconf # hostname. This is to support use cases where zeroconf (hostname.local) does not work. if port != 'OTA': host = port else: host = get_upload_host(config) from esphomeyaml.components import ota from esphomeyaml import espota bin_file = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') if args.host_port is not None: host_port = args.host_port else: host_port = int( os.getenv('ESPHOMEYAML_OTA_HOST_PORT', random.randint(10000, 60000))) espota_args = [ 'espota.py', '--debug', '--progress', '-i', host, '-p', str(ota.get_port(config)), '-f', bin_file, '-a', ota.get_auth(config), '-P', str(host_port) ] if args.verbose: espota_args.append('-d') return espota.main(espota_args)
def to_code(config): from PIL import ImageFont for conf in config: path = relative_path(conf[CONF_FILE]) try: font = ImageFont.truetype(path, conf[CONF_SIZE]) except Exception as e: raise core.ESPHomeYAMLError( u"Could not load truetype file {}: {}".format(path, e)) ascent, descent = font.getmetrics() glyph_args = {} data = [] for glyph in conf[CONF_GLYPHS]: mask = font.getmask(glyph, mode='1') _, (offset_x, offset_y) = font.font.getsize(glyph) width, height = mask.size width8 = ((width + 7) // 8) * 8 glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812 for y in range(height): for x in range(width): if not mask.getpixel((x, y)): continue pos = x + y * width8 glyph_data[pos // 8] |= 0x80 >> (pos % 8) glyph_args[glyph] = (len(data), offset_x, offset_y, width, height) data += glyph_data raw_data = MockObj(conf[CONF_RAW_DATA_ID]) add( RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format( raw_data, len(data), ArrayInitializer(*[HexInt(x) for x in data], multiline=False)))) glyphs = [] for glyph in conf[CONF_GLYPHS]: glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph])) rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent) Pvariable(conf[CONF_ID], rhs)
def get(self): if not self.is_authenticated(): self.redirect('/login') return configuration = self.get_argument('configuration') config_file = os.path.join(CONFIG_DIR, configuration) core.CONFIG_PATH = config_file config = __main__.read_config(core.CONFIG_PATH) build_path = relative_path(config[CONF_ESPHOMEYAML][CONF_BUILD_PATH]) path = os.path.join(build_path, '.pioenvs', core.NAME, 'firmware.bin') self.set_header('Content-Type', 'application/octet-stream') self.set_header("Content-Disposition", 'attachment; filename="{}.bin"'.format(core.NAME)) with open(path, 'rb') as f: while 1: data = f.read(16384) # or some other nice-sized chunk if not data: break self.write(data) self.finish()
def get_ini_content(config, path): version_specific_settings = determine_platformio_version_settings() options = { u'env': config[CONF_ESPHOMEYAML][CONF_NAME], u'platform': config[CONF_ESPHOMEYAML][CONF_ARDUINO_VERSION], u'board': config[CONF_ESPHOMEYAML][CONF_BOARD], u'build_flags': u'', u'upload_speed': UPLOAD_SPEED_OVERRIDE.get(core.BOARD, 115200), } build_flags = set() if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]: build_flags |= get_build_flags(config, 'build_flags') build_flags |= get_build_flags(config, 'BUILD_FLAGS') build_flags.add(u"-DESPHOMEYAML_USE") build_flags |= get_build_flags(config, 'required_build_flags') build_flags |= get_build_flags(config, 'REQUIRED_BUILD_FLAGS') # avoid changing build flags order build_flags = sorted(list(build_flags)) if build_flags: options[u'build_flags'] = u'\n '.join(build_flags) lib_deps = set() lib_version = config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION] lib_path = os.path.join(path, 'lib') dst_path = os.path.join(lib_path, 'esphomelib') this_version = None if CONF_REPOSITORY in lib_version: tag = next((lib_version[x] for x in (CONF_COMMIT, CONF_BRANCH, CONF_TAG) if x in lib_version), None) this_version = lib_version[CONF_REPOSITORY] if tag is not None: this_version += '#' + tag lib_deps.add(this_version) if os.path.islink(dst_path): os.unlink(dst_path) elif CONF_LOCAL in lib_version: this_version = lib_version[CONF_LOCAL] src_path = relative_path(this_version) do_write = True if os.path.islink(dst_path): old_path = os.path.join(os.readlink(dst_path), lib_path) if old_path != lib_path: os.unlink(dst_path) else: do_write = False if do_write: mkdir_p(lib_path) os.symlink(src_path, dst_path) # Manually add lib_deps because platformio seems to ignore them inside libs/ library_json_path = os.path.join(src_path, 'library.json') with codecs.open(library_json_path, 'r', encoding='utf-8') as f_handle: library_json_text = f_handle.read() library_json = json.loads(library_json_text) for dep in library_json.get('dependencies', []): if 'version' in dep and VERSION_REGEX.match(dep['version']) is not None: lib_deps.add(dep['name'] + '@' + dep['version']) else: lib_deps.add(dep['version']) else: this_version = lib_version lib_deps.add(lib_version) version_file = os.path.join(path, '.esphomelib_version') version = None if os.path.isfile(version_file): with open(version_file, 'r') as ver_f: version = ver_f.read() if version != this_version: _LOGGER.info("Esphomelib version change detected. Cleaning build files...") try: clean_build(path) except OSError as err: _LOGGER.warn("Error deleting build files (%s)! Ignoring...", err) with open(version_file, 'w') as ver_f: ver_f.write(this_version) lib_deps |= get_build_flags(config, 'LIB_DEPS') lib_deps |= get_build_flags(config, 'lib_deps') if core.ESP_PLATFORM == ESP_PLATFORM_ESP32: lib_deps |= { 'Preferences', # Preferences helper } # Manual fix for AsyncTCP if config[CONF_ESPHOMEYAML].get(CONF_ARDUINO_VERSION) == ARDUINO_VERSION_ESP32_DEV: lib_deps.add('https://github.com/me-no-dev/AsyncTCP.git#idf-update') # avoid changing build flags order lib_deps = sorted(x for x in lib_deps if x) if lib_deps: options[u'lib_deps'] = u'\n '.join(lib_deps) content = INI_CONTENT_FORMAT.format(**options) if CONF_BOARD_FLASH_MODE in config[CONF_ESPHOMEYAML]: flash_mode_key = version_specific_settings['flash_mode_key'] flash_mode = config[CONF_ESPHOMEYAML][CONF_BOARD_FLASH_MODE] content += "{} = {}\n".format(flash_mode_key, flash_mode) return content