Example #1
0
def includes(config):
    ret = []
    for include in config.get(CONF_INCLUDES, []):
        path = CORE.relative_path(include)
        res = os.path.relpath(path, CORE.relative_build_path('src', 'main.cpp'))
        ret.append(u'#include "{}"'.format(res))
    return ret
Example #2
0
def setup_text_sensor(text_sensor_obj, mqtt_obj, config):
    sensor_var = Pvariable(config[CONF_ID],
                           text_sensor_obj,
                           has_side_effects=False)
    mqtt_var = Pvariable(config[CONF_MQTT_ID],
                         mqtt_obj,
                         has_side_effects=False)
    CORE.add_job(setup_text_sensor_core_, sensor_var, mqtt_var, config)
Example #3
0
def setup_binary_sensor(binary_sensor_obj, mqtt_obj, config):
    binary_sensor_var = Pvariable(config[CONF_ID],
                                  binary_sensor_obj,
                                  has_side_effects=False)
    mqtt_var = Pvariable(config[CONF_MQTT_ID],
                         mqtt_obj,
                         has_side_effects=False)
    CORE.add_job(setup_binary_sensor_core_, binary_sensor_var, mqtt_var,
                 config)
Example #4
0
def variable(
        id,  # type: ID
        rhs,  # type: Expression
        type=None  # type: MockObj
):
    # type: (...) -> MockObj
    rhs = safe_exp(rhs)
    obj = MockObj(id, u'.')
    id.type = type or id.type
    assignment = AssignmentExpression(id.type, '', id, rhs, obj)
    CORE.add(assignment)
    CORE.register_variable(id, obj)
    obj.requires.append(assignment)
    return obj
Example #5
0
def write_cpp(config):
    _LOGGER.info("Generating C++ source...")

    CORE.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
        CORE.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
        CORE.add_job(component.to_code, conf, domain=domain)

    CORE.flush_tasks()
    add(RawStatement(''))
    add(RawStatement(''))
    all_code = []
    for exp in CORE.expressions:
        if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
            if isinstance(exp, Expression) and not exp.required:
                continue
        all_code.append(text_type(statement(exp)))

    writer.write_platformio_project()

    code_s = indent('\n'.join(line.rstrip() for line in all_code))
    writer.write_cpp(code_s)
    return 0
Example #6
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())
Example #7
0
def clean_build():
    for directory in ('.piolibdeps', '.pioenvs'):
        dir_path = CORE.relative_build_path(directory)
        if not os.path.isdir(dir_path):
            continue
        _LOGGER.info("Deleting %s", dir_path)
        shutil.rmtree(dir_path)
Example #8
0
def write_cpp(code_s):
    path = CORE.relative_build_path('src', 'main.cpp')
    if os.path.isfile(path):
        try:
            with codecs.open(path, 'r', encoding='utf-8') as f_handle:
                text = f_handle.read()
        except OSError:
            raise EsphomeyamlError(u"Could not read C++ file at {}".format(path))
        prev_file = text
        code_format = find_begin_end(text, CPP_AUTO_GENERATE_BEGIN, CPP_AUTO_GENERATE_END)
        code_format_ = find_begin_end(code_format[0], CPP_INCLUDE_BEGIN, CPP_INCLUDE_END)
        code_format = (code_format_[0], code_format_[1], code_format[1])
    else:
        prev_file = None
        mkdir_p(os.path.dirname(path))
        code_format = CPP_BASE_FORMAT

    include_s = get_include_text()

    full_file = code_format[0] + CPP_INCLUDE_BEGIN + u'\n' + include_s + CPP_INCLUDE_END
    full_file += code_format[1] + CPP_AUTO_GENERATE_BEGIN + u'\n' + code_s + CPP_AUTO_GENERATE_END
    full_file += code_format[2]
    if prev_file == full_file:
        return
    with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
        f_handle.write(full_file)
Example #9
0
def write_platformio_project():
    mkdir_p(CORE.build_path)

    platformio_ini = CORE.relative_build_path('platformio.ini')
    content = get_ini_content()
    write_gitignore()
    write_platformio_ini(content, platformio_ini)
Example #10
0
def migrate_src_version_0_to_1():
    main_cpp = CORE.relative_build_path('src', 'main.cpp')
    if not os.path.isfile(main_cpp):
        return

    with codecs.open(main_cpp, 'r', encoding='utf-8') as f_handle:
        content = orig_content = f_handle.read()

    content, count = replace_file_content(content, r'\s*delay\((?:16|20)\);', '')
    if count != 0:
        _LOGGER.info("Migration: Removed %s occurrence of 'delay(16);' in %s", count, main_cpp)

    content, count = replace_file_content(content, r'using namespace esphomelib;', '')
    if count != 0:
        _LOGGER.info("Migration: Removed %s occurrence of 'using namespace esphomelib;' "
                     "in %s", count, main_cpp)

    if CPP_INCLUDE_BEGIN not in content:
        content, count = replace_file_content(content, r'#include "esphomelib/application.h"',
                                              CPP_INCLUDE_BEGIN + u'\n' + CPP_INCLUDE_END)
        if count == 0:
            _LOGGER.error("Migration failed. esphomeyaml 1.10.0 needs to have a new auto-generated "
                          "include section in the %s file. Please remove %s and let it be "
                          "auto-generated again.", main_cpp, main_cpp)
        _LOGGER.info("Migration: Added include section to %s", main_cpp)

    if orig_content == content:
        return
    with codecs.open(main_cpp, 'w', encoding='utf-8') as f_handle:
        f_handle.write(content)
Example #11
0
def process_lambda(
        value,  # type: Lambda
        parameters,  # type: List[Tuple[Expression, str]]
        capture='=',  # type: str
        return_type=None  # type: Optional[Expression]
):
    # type: (...) -> Generator[LambdaExpression]
    from esphomeyaml.components.globals import GlobalVariableComponent

    if value is None:
        yield
        return
    parts = value.parts[:]
    for i, id in enumerate(value.requires_ids):
        for full_id, var in CORE.get_variable_with_full_id(id):
            yield
        if full_id is not None and isinstance(full_id.type, MockObjClass) and \
                full_id.type.inherits_from(GlobalVariableComponent):
            parts[i * 3 + 1] = var.value()
            continue

        if parts[i * 3 + 2] == '.':
            parts[i * 3 + 1] = var._
        else:
            parts[i * 3 + 1] = var
        parts[i * 3 + 2] = ''
    yield LambdaExpression(parts, parameters, capture, return_type)
Example #12
0
def generic_gpio_pin_expression_(conf, mock_obj, default_mode):
    if conf is None:
        return
    number = conf[CONF_NUMBER]
    inverted = conf.get(CONF_INVERTED)
    if CONF_PCF8574 in conf:
        from esphomeyaml.components import pcf8574

        for hub in CORE.get_variable(conf[CONF_PCF8574]):
            yield None

        if default_mode == u'INPUT':
            mode = pcf8574.PCF8675_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')]
            yield hub.make_input_pin(number, mode, inverted)
            return
        if default_mode == u'OUTPUT':
            yield hub.make_output_pin(number, inverted)
            return

        raise EsphomeyamlError(u"Unknown default mode {}".format(default_mode))
    if len(conf) == 1:
        yield IntLiteral(number)
        return
    mode = RawExpression(conf.get(CONF_MODE, default_mode))
    yield mock_obj(number, mode, inverted)
Example #13
0
def validate_local_esphomelib_version(value):
    value = cv.directory(value)
    path = CORE.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
Example #14
0
def write_platformio_project():
    mkdir_p(CORE.build_path)

    platformio_ini = CORE.relative_build_path('platformio.ini')
    content = get_ini_content()
    if 'esp32_ble_beacon' in CORE.config or 'esp32_ble_tracker' in CORE.config:
        content += 'board_build.partitions = partitions.csv\n'
        partitions_csv = CORE.relative_build_path('partitions.csv')
        if not os.path.isfile(partitions_csv):
            with open(partitions_csv, "w") as f:
                f.write("nvs,      data, nvs,     0x009000, 0x005000,\n")
                f.write("otadata,  data, ota,     0x00e000, 0x002000,\n")
                f.write("app0,     app,  ota_0,   0x010000, 0x190000,\n")
                f.write("app1,     app,  ota_1,   0x200000, 0x190000,\n")
                f.write("eeprom,   data, 0x99,    0x390000, 0x001000,\n")
                f.write("spiffs,   data, spiffs,  0x391000, 0x00F000\n")
    write_gitignore()
    write_platformio_ini(content, platformio_ini)
Example #15
0
def Pvariable(
        id,  # type: ID
        rhs,  # type: Expression
        has_side_effects=True,  # type: bool
        type=None  # type: MockObj
):
    # type: (...) -> MockObj
    rhs = safe_exp(rhs)
    if not has_side_effects and hasattr(rhs, '_has_side_effects'):
        # pylint: disable=attribute-defined-outside-init, protected-access
        rhs._has_side_effects = False
    obj = MockObj(id, u'->', has_side_effects=has_side_effects)
    id.type = type or id.type
    assignment = AssignmentExpression(id.type, '*', id, rhs, obj)
    CORE.add(assignment)
    CORE.register_variable(id, obj)
    obj.requires.append(assignment)
    return obj
Example #16
0
def directory(value):
    value = string(value)
    path = CORE.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
Example #17
0
def file_(value):
    value = string(value)
    path = CORE.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
Example #18
0
def symlink_esphomelib_version(esphomelib_version):
    lib_path = CORE.relative_build_path('lib')
    dst_path = CORE.relative_build_path('lib', 'esphomelib')
    if CORE.is_local_esphomelib_copy:
        src_path = CORE.relative_path(esphomelib_version[CONF_LOCAL])
        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)
    else:
        # Remove symlink when changing back from local version
        if os.path.islink(dst_path):
            os.unlink(dst_path)
Example #19
0
def get_ini_content():
    lib_deps = gather_lib_deps()
    build_flags = gather_build_flags()

    if CORE.is_esp8266 and CORE.board in ESP8266_FLASH_SIZES:
        flash_size = ESP8266_FLASH_SIZES[CORE.board]
        ld_scripts = ESP8266_LD_SCRIPTS[flash_size]
        ld_script = None

        if CORE.arduino_version in ('[email protected]', '[email protected]',
                                    '[email protected]', '[email protected]'):
            ld_script = ld_scripts[0]
        elif CORE.arduino_version == ARDUINO_VERSION_ESP8266_DEV:
            ld_script = ld_scripts[1]

        if ld_script is not None:
            build_flags.append('-Wl,-T{}'.format(ld_script))

    data = {
        'platform': CORE.config[CONF_ESPHOMEYAML][CONF_ARDUINO_VERSION],
        'board': CORE.board,
        'framework': 'arduino',
        'lib_deps': lib_deps + ['${common.lib_deps}'],
        'build_flags': build_flags + ['${common.build_flags}'],
        'upload_speed': UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200),
    }

    if 'esp32_ble_beacon' in CORE.config or 'esp32_ble_tracker' in CORE.config:
        data['board_build.partitions'] = "partitions.csv"
        partitions_csv = CORE.relative_build_path('partitions.csv')
        if not os.path.isfile(partitions_csv):
            with open(partitions_csv, "w") as f:
                f.write("nvs,      data, nvs,     0x009000, 0x005000,\n")
                f.write("otadata,  data, ota,     0x00e000, 0x002000,\n")
                f.write("app0,     app,  ota_0,   0x010000, 0x190000,\n")
                f.write("app1,     app,  ota_1,   0x200000, 0x190000,\n")
                f.write("eeprom,   data, 0x99,    0x390000, 0x001000,\n")
                f.write("spiffs,   data, spiffs,  0x391000, 0x00F000\n")

    if CONF_BOARD_FLASH_MODE in CORE.config[CONF_ESPHOMEYAML]:
        flash_mode = CORE.config[CONF_ESPHOMEYAML][CONF_BOARD_FLASH_MODE]
        data['board_build.flash_mode'] = flash_mode

    data.update(CORE.config[CONF_ESPHOMEYAML].get(CONF_PLATFORMIO_OPTIONS, {}))

    content = u'[env:{}]\n'.format(CORE.name)
    content += format_ini(data)

    return content
Example #20
0
def gather_lib_deps():
    lib_deps = set()
    esphomelib_version = CORE.config[CONF_ESPHOMEYAML][CONF_ESPHOMELIB_VERSION]
    if CONF_REPOSITORY in esphomelib_version:
        repo = esphomelib_version[CONF_REPOSITORY]
        ref = next((esphomelib_version[x]
                    for x in (CONF_COMMIT, CONF_BRANCH, CONF_TAG)
                    if x in esphomelib_version), None)
        if CONF_TAG in esphomelib_version and repo == LIBRARY_URI_REPO:
            this_version = GITHUB_ARCHIVE_ZIP.format(ref)
        elif ref is not None:
            this_version = repo + '#' + ref
        lib_deps.add(this_version)
    elif CORE.is_local_esphomelib_copy:
        src_path = CORE.relative_path(esphomelib_version[CONF_LOCAL])
        # 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'])
        lib_deps.add('esphomelib')
    else:
        lib_deps.add(esphomelib_version)

    lib_deps |= get_build_flags('LIB_DEPS')
    lib_deps |= get_build_flags('lib_deps')
    if CORE.is_esp32:
        lib_deps |= {
            'Preferences',  # Preferences helper
        }
        # Manual fix for AsyncTCP
        if CORE.config[CONF_ESPHOMEYAML].get(
                CONF_ARDUINO_VERSION) == ARDUINO_VERSION_ESP32_DEV:
            lib_deps.add(
                'https://github.com/me-no-dev/AsyncTCP.git#idf-update')
            lib_deps.discard('[email protected]')
    # avoid changing build flags order
    return list(sorted(x for x in lib_deps if x))
Example #21
0
def preload_core_config(config):
    if CONF_ESPHOMEYAML not in config:
        raise EsphomeyamlError(u"No esphomeyaml section in config")
    core_conf = config[CONF_ESPHOMEYAML]
    if CONF_PLATFORM not in core_conf:
        raise EsphomeyamlError("esphomeyaml.platform not specified.")
    if CONF_BOARD not in core_conf:
        raise EsphomeyamlError("esphomeyaml.board not specified.")
    if CONF_NAME not in core_conf:
        raise EsphomeyamlError("esphomeyaml.name not specified.")

    try:
        CORE.esp_platform = validate_platform(core_conf[CONF_PLATFORM])
        CORE.board = validate_board(core_conf[CONF_BOARD])
        CORE.name = cv.valid_name(core_conf[CONF_NAME])
        CORE.build_path = CORE.relative_path(
            cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path())))
    except vol.Invalid as e:
        raise EsphomeyamlError(text_type(e))
Example #22
0
def to_code(config):
    from PIL import ImageFont

    path = CORE.relative_path(config[CONF_FILE])
    try:
        font = ImageFont.truetype(path, config[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 config[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(config[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 config[CONF_GLYPHS]:
        glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))

    rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
    Pvariable(config[CONF_ID], rhs)
Example #23
0
def to_code(config):
    from PIL import Image

    path = CORE.relative_path(config[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 config:
        image.thumbnail(config[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(config[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(config[CONF_ID], rhs)
Example #24
0
def setup_time(time_var, config):
    CORE.add_job(setup_time_core_, time_var, config)
Example #25
0
def storage_path():  # type: () -> str
    return CORE.relative_path('.esphomeyaml',
                              '{}.json'.format(CORE.config_filename))
Example #26
0
def setup_display(display_var, config):
    CORE.add_job(setup_display_core_, display_var, config)
Example #27
0
def register_sensor(var, config):
    sensor_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
    rhs = App.register_sensor(sensor_var)
    mqtt_var = Pvariable(config[CONF_MQTT_ID], rhs, has_side_effects=True)
    CORE.add_job(setup_sensor_core_, sensor_var, mqtt_var, config)
Example #28
0
def setup_stepper(stepper_var, config):
    CORE.add_job(setup_stepper_core_, stepper_var, config)
Example #29
0
def setup_light(light_obj, mqtt_obj, config):
    light_var = Pvariable(config[CONF_ID], light_obj, has_side_effects=False)
    mqtt_var = Pvariable(config[CONF_MQTT_ID], mqtt_obj, has_side_effects=False)
    CORE.add_job(setup_light_core_, light_var, mqtt_var, config)
Example #30
0
def build_automation(trigger, arg_type, config):
    CORE.add_job(build_automation_, trigger, arg_type, config)