Пример #1
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Enphase Envoy from a config entry."""

    config = entry.data
    name = config[CONF_NAME]

    envoy_reader = EnvoyReader(
        config[CONF_HOST],
        config[CONF_USERNAME],
        config[CONF_PASSWORD],
        inverters=True,
        async_client=get_async_client(hass),
    )

    async def async_update_data():
        """Fetch data from API endpoint."""
        data = {}
        async with async_timeout.timeout(30):
            try:
                await envoy_reader.getData()
            except httpx.HTTPStatusError as err:
                raise ConfigEntryAuthFailed from err
            except httpx.HTTPError as err:
                raise UpdateFailed(
                    f"Error communicating with API: {err}") from err

            for condition in SENSORS:
                if condition != "inverters":
                    data[condition] = await getattr(envoy_reader, condition)()
                else:
                    data[
                        "inverters_production"] = await envoy_reader.inverters_production(
                        )

            _LOGGER.debug("Retrieved data from API: %s", data)

            return data

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=f"envoy {name}",
        update_method=async_update_data,
        update_interval=SCAN_INTERVAL,
    )

    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryAuthFailed:
        envoy_reader.get_inverters = False
        await coordinator.async_config_entry_first_refresh()

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        COORDINATOR: coordinator,
        NAME: name,
    }

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Пример #2
0
async def test_with_4_2_27_firmware():
    """Verify with 4.2.27 firmware."""
    version = "4.2.27"
    respx.get("/info.xml").mock(return_value=Response(200, text=""))
    respx.get("/production.json").mock(
        return_value=Response(200, json=_load_json_fixture(version, "production.json"))
    )
    respx.get("/api/v1/production").mock(
        return_value=Response(
            200, json=_load_json_fixture(version, "api_v1_production")
        )
    )

    reader = EnvoyReader("127.0.0.1", inverters=False)
    await reader.getData()

    assert await reader.consumption() == 5811
    assert await reader.production() == 5891
    assert await reader.daily_consumption() == 0
    assert await reader.daily_production() == 17920
    assert await reader.seven_days_consumption() == 0
    assert await reader.seven_days_production() == 276614
    assert await reader.lifetime_consumption() == 0
    assert await reader.lifetime_production() == 10279087
    assert await reader.inverters_production() is None
Пример #3
0
    async def async_update(self):
        """Get the energy production data from the Enphase Envoy."""

        if self._type != "inverters":
            _state = await getattr(EnvoyReader(self._ip_address), self._type)()
            if isinstance(_state, int):
                self._state = _state
            else:
                _LOGGER.error(_state)
                self._state = None

        elif self._type == "inverters":
            inverters = await (EnvoyReader(
                self._ip_address).inverters_production())
            if isinstance(inverters, dict):
                serial_number = self._name.split(" ")[2]
                self._state = inverters[serial_number]
            else:
                self._state = None
Пример #4
0
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
    """Validate the user input allows us to connect."""
    envoy_reader = EnvoyReader(
        data[CONF_HOST],
        data[CONF_USERNAME],
        data[CONF_PASSWORD],
        inverters=False,
        async_client=get_async_client(hass),
    )

    try:
        await envoy_reader.getData()
    except httpx.HTTPStatusError as err:
        raise InvalidAuth from err
    except (RuntimeError, httpx.HTTPError) as err:
        raise CannotConnect from err
Пример #5
0
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Set up the Enphase Envoy sensor."""
    ip_address = config[CONF_IP_ADDRESS]
    monitored_conditions = config[CONF_MONITORED_CONDITIONS]
    name = config[CONF_NAME]
    username = config[CONF_USERNAME]
    password = config[CONF_PASSWORD]

    envoy_reader = EnvoyReader(ip_address, username, password)

    entities = []
    # Iterate through the list of sensors
    for condition in monitored_conditions:
        if condition == "inverters":
            try:
                inverters = await envoy_reader.inverters_production()
            except requests.exceptions.HTTPError:
                _LOGGER.warning(
                    "Authentication for Inverter data failed during setup: %s",
                    ip_address,
                )
                continue

            if isinstance(inverters, dict):
                for inverter in inverters:
                    entities.append(
                        Envoy(
                            envoy_reader,
                            condition,
                            f"{name}{SENSORS[condition][0]} {inverter}",
                            SENSORS[condition][1],
                        )
                    )

        else:
            entities.append(
                Envoy(
                    envoy_reader,
                    condition,
                    f"{name}{SENSORS[condition][0]}",
                    SENSORS[condition][1],
                )
            )
    async_add_entities(entities)
Пример #6
0
async def test_with_5_0_49_firmware():
    """Verify with 5.0.49 firmware."""
    version = "5.0.49"

    respx.get("/info.xml").mock(return_value=Response(200, text=""))
    respx.get("/production.json").mock(
        return_value=Response(200, json=_load_json_fixture(version, "production.json"))
    )
    respx.get("/api/v1/production").mock(
        return_value=Response(
            200, json=_load_json_fixture(version, "api_v1_production")
        )
    )
    respx.get("/api/v1/production/inverters").mock(
        return_value=Response(
            200, json=_load_json_fixture(version, "api_v1_production_inverters")
        )
    )
    reader = EnvoyReader("127.0.0.1", inverters=True)
    await reader.getData()

    assert (
        await reader.consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.production() == 4859
    assert (
        await reader.daily_consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.daily_production() == 5046
    assert (
        await reader.seven_days_consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.seven_days_production() == 445686
    assert (
        await reader.lifetime_consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.lifetime_production() == 88742152
    assert isinstance(await reader.inverters_production(), dict)
Пример #7
0
async def test_with_3_9_36_firmware():
    """Verify with 3.9.36 firmware."""
    version = "3.9.36"

    respx.get("/info.xml").mock(return_value=Response(200, text=""))
    respx.get("/production.json").mock(return_value=Response(404))
    respx.get("/api/v1/production").mock(
        return_value=Response(
            200, json=_load_json_fixture(version, "api_v1_production")
        )
    )
    respx.get("/api/v1/production/inverters").mock(
        return_value=Response(
            200, json=_load_json_fixture(version, "api_v1_production_inverters")
        )
    )
    reader = EnvoyReader("127.0.0.1", inverters=True)
    await reader.getData()

    assert (
        await reader.consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.production() == 1271
    assert (
        await reader.daily_consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.daily_production() == 1460
    assert (
        await reader.seven_days_consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.seven_days_production() == 130349
    assert (
        await reader.lifetime_consumption()
        == "Consumption data not available for your Envoy device."
    )
    assert await reader.lifetime_production() == 6012540
    assert isinstance(await reader.inverters_production(), dict)
Пример #8
0
async def async_setup_platform(homeassistant,
                               config,
                               async_add_entities,
                               discovery_info=None):
    """Set up the Enphase Envoy sensor."""
    ip_address = config[CONF_IP_ADDRESS]
    monitored_conditions = config[CONF_MONITORED_CONDITIONS]
    name = config[CONF_NAME]
    username = config[CONF_USERNAME]
    password = config[CONF_PASSWORD]

    if "inverters" in monitored_conditions:
        envoy_reader = EnvoyReader(ip_address,
                                   username,
                                   password,
                                   inverters=True)
    else:
        envoy_reader = EnvoyReader(ip_address, username, password)

    try:
        await envoy_reader.getData()
    except httpx.HTTPStatusError as err:
        _LOGGER.error("Authentication failure during setup: %s", err)
        return
    except httpx.HTTPError as err:
        raise PlatformNotReady from err

    async def async_update_data():
        """Fetch data from API endpoint."""
        data = {}
        async with async_timeout.timeout(30):
            try:
                await envoy_reader.getData()
            except httpx.HTTPError as err:
                raise UpdateFailed(
                    f"Error communicating with API: {err}") from err

            for condition in monitored_conditions:
                if condition != "inverters":
                    data[condition] = await getattr(envoy_reader, condition)()
                else:
                    data["inverters_production"] = await getattr(
                        envoy_reader, "inverters_production")()

            _LOGGER.debug("Retrieved data from API: %s", data)

            return data

    coordinator = DataUpdateCoordinator(
        homeassistant,
        _LOGGER,
        name="sensor",
        update_method=async_update_data,
        update_interval=SCAN_INTERVAL,
    )

    await coordinator.async_refresh()

    if coordinator.data is None:
        raise PlatformNotReady

    entities = []
    for condition in monitored_conditions:
        entity_name = ""
        if (condition == "inverters"
                and coordinator.data.get("inverters_production") is not None):
            for inverter in coordinator.data["inverters_production"]:
                entity_name = f"{name}{SENSORS[condition][0]} {inverter}"
                split_name = entity_name.split(" ")
                serial_number = split_name[-1]
                entities.append(
                    Envoy(
                        condition,
                        entity_name,
                        serial_number,
                        SENSORS[condition][1],
                        coordinator,
                    ))
        elif condition != "inverters":
            entity_name = f"{name}{SENSORS[condition][0]}"
            entities.append(
                Envoy(
                    condition,
                    entity_name,
                    None,
                    SENSORS[condition][1],
                    coordinator,
                ))

    async_add_entities(entities)
Пример #9
0
def main():
    metric_prefix = "envoy"
    port = 9433

    parser = argparse.ArgumentParser(description="envoy-logger")
    parser.add_argument('-v',
                        "--verbose",
                        action="count",
                        help="Increase verbosity of outut",
                        default=0)
    args = parser.parse_args()

    if args.verbose:
        print(args)
    # As long as we have httpx 0.19 installed and use the explicit username/password, things seems to work
    reader = EnvoyReader("envoy", "envoy", "058800", inverters=True)
    # Defaults to using envoy as the username and the last six of the
    # serial number as the password
    #reader=EnvoyReader("envoy", inverters=True)

    production = Gauge(f"{metric_prefix}_production",
                       "Current system power production (W)")
    daily_production = Gauge(f"{metric_prefix}_daily_production",
                             "Current day's energy production (Wh)")
    seven_days_production = Gauge(f"{metric_prefix}_seven_day_production",
                                  "Current seven day energy production (Wh)")
    lifetime_production = Gauge(f"{metric_prefix}_lifetime_production",
                                "Lifetime energy production (Wh)")
    inverter = Gauge(f"{metric_prefix}_inverter",
                     f"Inverter current power production (W)",
                     ['serial_number'])

    start_http_server(port)

    while True:
        loop = asyncio.get_event_loop()
        data_results = loop.run_until_complete(
            asyncio.gather(reader.getData(), return_exceptions=True))
        if args.verbose > 1: print(f"data_results:{data_results}")
        loop = asyncio.get_event_loop()
        results = loop.run_until_complete(
            asyncio.gather(reader.production(),
                           reader.daily_production(),
                           reader.seven_days_production(),
                           reader.lifetime_production(),
                           reader.inverters_production(),
                           return_exceptions=True))
        if args.verbose > 1: print(f"results:{results}")
        # results:
        # [13764,
        # 28698,
        # 642343,
        # 4162674,
        # {'202028033658': [120, '2021-03-11 11:30:02'],
        #  '202029005102': [116, '2021-03-11 11:30:10'],
        #  '202028035455': [120, '2021-03-11 11:30:18'],
        #  '202029001566': [117, '2021-03-11 11:30:24'],
        # ...

        try:
            production.set(results[0])
            daily_production.set(results[1])
            seven_days_production.set(results[2])
            lifetime_production.set(results[3])

            if "401" in str(data_results):
                print(
                    "inverters_production:    Unable to retrieve inverter data - Authentication failure"
                )
            elif results[4] is None:
                print(
                    "inverters_production:    Inverter data not available from Envoy."
                )
            else:
                for sn, status in results[4].items():
                    inverter.labels([sn]).set(status[0])
        except:
            print("Error parsing results")
            print(results)

        time.sleep(60)
Пример #10
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Enphase Envoy from a config entry."""

    config = entry.data
    name = config[CONF_NAME]

    envoy_reader = EnvoyReader(
        config[CONF_HOST],
        config[CONF_USERNAME],
        config[CONF_PASSWORD],
        inverters=True,
        async_client=get_async_client(hass),
    )

    async def async_update_data():
        """Fetch data from API endpoint."""
        async with async_timeout.timeout(30):
            try:
                await envoy_reader.getData()
            except httpx.HTTPStatusError as err:
                raise ConfigEntryAuthFailed from err
            except httpx.HTTPError as err:
                raise UpdateFailed(f"Error communicating with API: {err}") from err

            data = {
                description.key: await getattr(envoy_reader, description.key)()
                for description in SENSORS
            }
            data["inverters_production"] = await envoy_reader.inverters_production()

            _LOGGER.debug("Retrieved data from API: %s", data)

            return data

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=f"envoy {name}",
        update_method=async_update_data,
        update_interval=SCAN_INTERVAL,
    )

    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryAuthFailed:
        envoy_reader.get_inverters = False
        await coordinator.async_config_entry_first_refresh()

    if not entry.unique_id:
        try:
            serial = await envoy_reader.get_full_serial_number()
        except httpx.HTTPError as ex:
            raise ConfigEntryNotReady(
                f"Could not obtain serial number from envoy: {ex}"
            ) from ex
        else:
            hass.config_entries.async_update_entry(entry, unique_id=serial)

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        COORDINATOR: coordinator,
        NAME: name,
    }

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    return True