コード例 #1
0
ファイル: platformio_api.py プロジェクト: krahabb/esphome
def _load_idedata(config):
    platformio_ini = Path(CORE.relative_build_path("platformio.ini"))
    temp_idedata = Path(
        CORE.relative_internal_path("idedata", f"{CORE.name}.json"))

    changed = False
    if not platformio_ini.is_file() or not temp_idedata.is_file():
        changed = True
    elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime:
        changed = True

    if not changed:
        try:
            return json.loads(temp_idedata.read_text(encoding="utf-8"))
        except ValueError:
            pass

    temp_idedata.parent.mkdir(exist_ok=True, parents=True)

    data = _run_idedata(config)

    temp_idedata.write_text(json.dumps(data, indent=2) + "\n",
                            encoding="utf-8")
    return data
コード例 #2
0
async def to_code(config):
    cg.add_global(cg.global_ns.namespace("esphome").using)
    cg.add(
        cg.App.pre_setup(
            config[CONF_NAME],
            cg.RawExpression('__DATE__ ", " __TIME__'),
            config[CONF_NAME_ADD_MAC_SUFFIX],
        ))

    CORE.add_job(_add_automations, config)

    # Set LWIP build constants for ESP8266
    if CORE.is_esp8266:
        CORE.add_job(_esp8266_add_lwip_type)

    cg.add_build_flag("-fno-exceptions")

    # Libraries
    for lib in config[CONF_LIBRARIES]:
        if "@" in lib:
            name, vers = lib.split("@", 1)
            cg.add_library(name, vers)
        elif "://" in lib:
            # Repository...
            if "=" in lib:
                name, repo = lib.split("=", 1)
                cg.add_library(name, None, repo)
            else:
                cg.add_library(None, None, lib)

        else:
            cg.add_library(lib, None)

    if CORE.is_esp8266:
        # Arduino 2 has a non-standards conformant new that returns a nullptr instead of failing when
        # out of memory and exceptions are disabled. Since Arduino 2.6.0, this flag can be used to make
        # new abort instead. Use it so that OOM fails early (on allocation) instead of on dereference of
        # a NULL pointer (so the stacktrace makes more sense), and for consistency with Arduino 3,
        # which always aborts if exceptions are disabled.
        # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;`
        cg.add_build_flag("-DNEW_OOM_ABORT")

    cg.add_build_flag("-Wno-unused-variable")
    cg.add_build_flag("-Wno-unused-but-set-variable")
    cg.add_build_flag("-Wno-sign-compare")
    if config.get(CONF_ESP8266_RESTORE_FROM_FLASH, False):
        cg.add_define("USE_ESP8266_PREFERENCES_FLASH")

    if config[CONF_INCLUDES]:
        CORE.add_job(add_includes, config[CONF_INCLUDES])

    cg.add_define("ESPHOME_BOARD", CORE.board)
    if CONF_PROJECT in config:
        cg.add_define("ESPHOME_PROJECT_NAME", config[CONF_PROJECT][CONF_NAME])
        cg.add_define("ESPHOME_PROJECT_VERSION",
                      config[CONF_PROJECT][CONF_VERSION])
コード例 #3
0
def variable(
        id,  # type: ID
        rhs,  # type: SafeExpType
        type=None  # type: MockObj
):
    # type: (...) -> MockObj
    """Declare a new variable (not pointer type) in the code generation.

    :param id: The ID used to declare the variable.
    :param rhs: The expression to place on the right hand side of the assignment.
    :param type: Manually define a type for the variable, only use this when it's not possible
      to do so during config validation phase (for example because of template arguments).

    :returns The new variable as a MockObj.
    """
    assert isinstance(id, ID)
    rhs = safe_exp(rhs)
    obj = MockObj(id, u'.')
    if type is not None:
        id.type = type
    assignment = AssignmentExpression(id.type, '', id, rhs, obj)
    CORE.add(assignment)
    CORE.register_variable(id, obj)
    return obj
コード例 #4
0
ファイル: writer.py プロジェクト: yevgenb/esphome-1
def copy_src_tree():
    source_files = {}
    for _, component, _ in iter_components(CORE.config):
        source_files.update(component.source_files)

    # Convert to list and sort
    source_files_l = list(source_files.items())
    source_files_l.sort()

    # Build #include list for esphome.h
    include_l = []
    for target, path in source_files_l:
        if os.path.splitext(path)[1] in HEADER_FILE_EXTENSIONS:
            include_l.append(f'#include "{target}"')
    include_l.append('')
    include_s = '\n'.join(include_l)

    source_files_copy = source_files.copy()
    source_files_copy.pop(DEFINES_H_TARGET)

    for path in walk_files(CORE.relative_src_path('esphome')):
        if os.path.splitext(path)[1] not in SOURCE_FILE_EXTENSIONS:
            # Not a source file, ignore
            continue
        # Transform path to target path name
        target = os.path.relpath(path, CORE.relative_src_path()).replace(os.path.sep, '/')
        if target in (DEFINES_H_TARGET, VERSION_H_TARGET):
            # Ignore defines.h, will be dealt with later
            continue
        if target not in source_files_copy:
            # Source file removed, delete target
            os.remove(path)
        else:
            src_path = source_files_copy.pop(target)
            copy_file_if_changed(src_path, path)

    # Now copy new files
    for target, src_path in source_files_copy.items():
        dst_path = CORE.relative_src_path(*target.split('/'))
        copy_file_if_changed(src_path, dst_path)

    # Finally copy defines
    write_file_if_changed(CORE.relative_src_path('esphome', 'core', 'defines.h'),
                          generate_defines_h())
    write_file_if_changed(CORE.relative_src_path('esphome', 'README.txt'),
                          ESPHOME_README_TXT)
    write_file_if_changed(CORE.relative_src_path('esphome.h'),
                          ESPHOME_H_FORMAT.format(include_s))
    write_file_if_changed(CORE.relative_src_path('esphome', 'core', 'version.h'),
                          VERSION_H_FORMAT.format(__version__))
コード例 #5
0
def preload_core_config(config):
    core_key = 'esphome'
    if 'esphomeyaml' in config:
        _LOGGER.warning("The esphomeyaml section has been renamed to esphome in 1.11.0. "
                        "Please replace 'esphomeyaml:' in your configuration with 'esphome:'.")
        config[CONF_ESPHOME] = config.pop('esphomeyaml')
        core_key = 'esphomeyaml'
    if CONF_ESPHOME not in config:
        raise cv.RequiredFieldInvalid("required key not provided", CONF_ESPHOME)
    with cv.prepend_path(core_key):
        out = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME])
    CORE.name = out[CONF_NAME]
    CORE.esp_platform = out[CONF_PLATFORM]
    with cv.prepend_path(core_key):
        out2 = PRELOAD_CONFIG_SCHEMA2(config[CONF_ESPHOME])
    CORE.board = out2[CONF_BOARD]
    CORE.build_path = CORE.relative_config_path(out2[CONF_BUILD_PATH])
コード例 #6
0
def write_cpp(code_s):
    path = CORE.relative_src_path('main.cpp')
    if os.path.isfile(path):
        text = read_file(path)
        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:
        code_format = CPP_BASE_FORMAT

    copy_src_tree()
    global_s = u'#include "esphome.h"\n'
    global_s += CORE.cpp_global_section

    full_file = code_format[0] + CPP_INCLUDE_BEGIN + u'\n' + global_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]
    write_file_if_changed(full_file, path)
コード例 #7
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

    content = read_file(main_cpp)

    if CPP_INCLUDE_BEGIN in content:
        return

    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 + "\n" + CPP_INCLUDE_END,
        )
        if count == 0:
            _LOGGER.error(
                "Migration failed. ESPHome 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)

    write_file_if_changed(main_cpp, content)
コード例 #8
0
def process_lambda(
    value: Lambda,
    parameters: List[Tuple[SafeExpType, str]],
    capture: str = "=",
    return_type: SafeExpType = None,
) -> Generator[LambdaExpression, None, None]:
    """Process the given lambda value into a LambdaExpression.

    This is a coroutine because lambdas can depend on other IDs,
    you need to await it with 'yield'!

    :param value: The lambda to process.
    :param parameters: The parameters to pass to the Lambda, list of tuples
    :param capture: The capture expression for the lambda, usually ''.
    :param return_type: The return type of the lambda.
    :return: The generated lambda expression.
    """
    from esphome.components.globals import GlobalsComponent

    if value is None:
        yield
        return
    parts = value.parts[:]
    for i, id in enumerate(value.requires_ids):
        full_id, var = yield CORE.get_variable_with_full_id(id)
        if (full_id is not None and isinstance(full_id.type, MockObjClass)
                and full_id.type.inherits_from(GlobalsComponent)):
            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] = ""

    if isinstance(value, ESPHomeDataBase) and value.esp_range is not None:
        location = value.esp_range.start_mark
        location.line += value.content_offset
    else:
        location = None
    yield LambdaExpression(parts, parameters, capture, return_type, location)
コード例 #9
0
ファイル: writer.py プロジェクト: liuse2ee/esphome-tools-dc1
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()

    if CPP_INCLUDE_BEGIN in content:
        return

    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. ESPHome 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)
コード例 #10
0
ファイル: writer.py プロジェクト: rnauber/esphome
def get_ini_content():
    lib_deps = gather_lib_deps()
    build_flags = gather_build_flags()

    data = {
        'platform': CORE.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 CORE.is_esp32:
        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_ESPHOME]:
        flash_mode = CORE.config[CONF_ESPHOME][CONF_BOARD_FLASH_MODE]
        data['board_build.flash_mode'] = flash_mode

    # Ignore libraries that are not explicitly used, but may
    # be added by LDF
    # data['lib_ldf_mode'] = 'chain'
    data.update(CORE.config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS, {}))

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

    return content
コード例 #11
0
ファイル: font.py プロジェクト: liuse2ee/esphome-tools-dc1
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.EsphomeError(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

    rhs = safe_exp([HexInt(x) for x in data])
    prog_arr = progmem_array(config[CONF_RAW_DATA_ID], rhs)

    glyphs = []
    for glyph in config[CONF_GLYPHS]:
        glyphs.append(Glyph(glyph, prog_arr, *glyph_args[glyph]))

    rhs = App.make_font(glyphs, ascent, ascent + descent)
    Pvariable(config[CONF_ID], rhs)
コード例 #12
0
def process_lambda(
        value,  # type: Lambda
        parameters,  # type: List[Tuple[SafeExpType, str]]
        capture='=',  # type: str
        return_type=None  # type: Optional[SafeExpType]
):
    # type: (...) -> Generator[LambdaExpression]
    """Process the given lambda value into a LambdaExpression.

    This is a coroutine because lambdas can depend on other IDs,
    you need to await it with 'yield'!

    :param value: The lambda to process.
    :param parameters: The parameters to pass to the Lambda, list of tuples
    :param capture: The capture expression for the lambda, usually ''.
    :param return_type: The return type of the lambda.
    :return: The generated lambda expression.
    """
    from esphome.components.globals import GlobalsComponent

    if value is None:
        yield
        return
    parts = value.parts[:]
    for i, id in enumerate(value.requires_ids):
        full_id, var = yield CORE.get_variable_with_full_id(id)
        if full_id is not None and isinstance(full_id.type, MockObjClass) and \
                full_id.type.inherits_from(GlobalsComponent):
            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)
コード例 #13
0
def file_(value):
    import json

    value = string(value)
    path = CORE.relative_config_path(value)

    if CORE.vscode and (
        not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
    ):
        print(
            json.dumps(
                {
                    "type": "check_file_exists",
                    "path": path,
                }
            )
        )
        data = json.loads(input())
        assert data["type"] == "file_exists_response"
        if data["content"]:
            return value
        raise Invalid(
            "Could not find file '{}'. Please make sure it exists (full path: {})."
            "".format(path, os.path.abspath(path))
        )

    if not os.path.exists(path):
        raise Invalid(
            "Could not find file '{}'. Please make sure it exists (full path: {})."
            "".format(path, os.path.abspath(path))
        )
    if not os.path.isfile(path):
        raise Invalid(
            "Path '{}' is not a file (full path: {})."
            "".format(path, os.path.abspath(path))
        )
    return value
コード例 #14
0
def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj":
    """Declare a new pointer variable in the code generation.

    :param id_: The ID used to declare the variable.
    :param rhs: The expression to place on the right hand side of the assignment.
    :param type_: Manually define a type for the variable, only use this when it's not possible
      to do so during config validation phase (for example because of template arguments).

    :returns The new variable as a MockObj.
    """
    rhs = safe_exp(rhs)
    obj = MockObj(id_, "->")
    if type_ is not None:
        id_.type = type_
    decl = VariableDeclarationExpression(id_.type, "*", id_)
    CORE.add_global(decl)
    assignment = AssignmentExpression(None, None, id_, rhs, obj)
    CORE.add(assignment)
    CORE.register_variable(id_, obj)
    return obj
コード例 #15
0
def to_code(config):
    cg.add_global(cg.global_ns.namespace("esphome").using)
    cg.add(
        cg.App.pre_setup(
            config[CONF_NAME],
            cg.RawExpression('__DATE__ ", " __TIME__'),
            config[CONF_NAME_ADD_MAC_SUFFIX],
        )
    )

    CORE.add_job(_add_automations, config)

    # Set LWIP build constants for ESP8266
    if CORE.is_esp8266:
        CORE.add_job(_esp8266_add_lwip_type)

    cg.add_build_flag("-fno-exceptions")

    # Libraries
    if CORE.is_esp32:
        cg.add_library("ESPmDNS", None)
    elif CORE.is_esp8266:
        cg.add_library("ESP8266WiFi", None)
        cg.add_library("ESP8266mDNS", None)

    for lib in config[CONF_LIBRARIES]:
        if "@" in lib:
            name, vers = lib.split("@", 1)
            cg.add_library(name, vers)
        else:
            cg.add_library(lib, None)

    cg.add_build_flag("-Wno-unused-variable")
    cg.add_build_flag("-Wno-unused-but-set-variable")
    cg.add_build_flag("-Wno-sign-compare")
    if config.get(CONF_ESP8266_RESTORE_FROM_FLASH, False):
        cg.add_define("USE_ESP8266_PREFERENCES_FLASH")

    if config[CONF_INCLUDES]:
        CORE.add_job(add_includes, config[CONF_INCLUDES])
コード例 #16
0
ファイル: __init__.py プロジェクト: warthog9/esphome
async def register_text_sensor(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    cg.add(cg.App.register_text_sensor(var))
    await setup_text_sensor_core_(var, config)
コード例 #17
0
def register_binary_sensor(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    cg.add(cg.App.register_binary_sensor(var))
    yield setup_binary_sensor_core_(var, config)
コード例 #18
0
ファイル: __init__.py プロジェクト: yevgenb/esphome-1
def register_fan(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    cg.add(cg.App.register_fan(var))
    yield cg.register_component(var, config)
    yield setup_fan_core_(var, config)
コード例 #19
0
async def register_canbus(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.new_Pvariable(config[CONF_ID], var)
    await setup_canbus_core_(var, config)
コード例 #20
0
def register_text_sensor(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = Pvariable(config[CONF_ID], var, has_side_effects=True)
    add(App.register_text_sensor(var))
    CORE.add_job(setup_text_sensor_core_, var, config)
コード例 #21
0
def setup_text_sensor(text_sensor_obj, config):
    if not CORE.has_id(config[CONF_ID]):
        text_sensor_obj = Pvariable(config[CONF_ID],
                                    text_sensor_obj,
                                    has_side_effects=True)
    CORE.add_job(setup_text_sensor_core_, text_sensor_obj, config)
コード例 #22
0
def storage_path():  # type: () -> str
    return CORE.relative_path('.esphome',
                              '{}.json'.format(CORE.config_filename))
コード例 #23
0
async def register_stepper(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    await setup_stepper_core_(var, config)
コード例 #24
0
def write_gitignore():
    path = CORE.relative_config_path(".gitignore")
    if not os.path.isfile(path):
        with open(path, "w") as f:
            f.write(GITIGNORE_CONTENT)
コード例 #25
0
ファイル: __init__.py プロジェクト: krahabb/esphome
async def register_select(var, config, *, options: List[str]):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    cg.add(cg.App.register_select(var))
    await setup_select_core_(var, config, options=options)
コード例 #26
0
def setup_component(obj, config):
    if CONF_SETUP_PRIORITY in config:
        CORE.add(obj.set_setup_priority(config[CONF_SETUP_PRIORITY]))
コード例 #27
0
def register_output(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    yield setup_output_platform_(var, config)
コード例 #28
0
def setup_time(time_var, config):
    CORE.add_job(setup_time_core_, time_var, config)
コード例 #29
0
def read_relative_config_path(value):
    return Path(CORE.relative_config_path(value)).read_text()
コード例 #30
0
ファイル: __init__.py プロジェクト: krahabb/esphome
async def register_button(var, config):
    if not CORE.has_id(config[CONF_ID]):
        var = cg.Pvariable(config[CONF_ID], var)
    cg.add(cg.App.register_button(var))
    await setup_button_core_(var, config)