Ejemplo n.º 1
0
class ElecPricesDataUpdateCoordinator(DataUpdateCoordinator[Mapping[datetime,
                                                                    float]]):
    """Class to manage fetching Electricity prices data from API."""
    def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
        """Initialize."""
        self.api = PVPCData(
            session=async_get_clientsession(hass),
            tariff=entry.data[ATTR_TARIFF],
            local_timezone=hass.config.time_zone,
            power=entry.data[ATTR_POWER],
            power_valley=entry.data[ATTR_POWER_P3],
        )
        super().__init__(hass,
                         _LOGGER,
                         name=DOMAIN,
                         update_interval=timedelta(minutes=30))
        self._entry = entry

    @property
    def entry_id(self) -> str:
        """Return entry ID."""
        return self._entry.entry_id

    async def _async_update_data(self) -> Mapping[datetime, float]:
        """Update electricity prices from the ESIOS API."""
        prices = await self.api.async_update_prices(dt_util.utcnow())
        self.api.process_state_and_attributes(dt_util.utcnow())
        if not prices:
            raise UpdateFailed

        return prices
Ejemplo n.º 2
0
async def test_bad_downloads(
    available,
    day_str,
    num_log_msgs,
    status,
    exception,
    caplog,
):
    """Test data parsing of official API files."""
    day = datetime.fromisoformat(day_str)
    mock_session = MockAsyncSession(status=status, exc=exception)
    with caplog.at_level(logging.DEBUG):
        pvpc_data = PVPCData(
            local_timezone=REFERENCE_TZ,
            tariff="normal",
            websession=mock_session,
        )
        pvpc_data.source_available = available
        assert not pvpc_data.process_state_and_attributes(day)
        prices = await pvpc_data.async_update_prices(day)
        assert not prices
        assert not pvpc_data.process_state_and_attributes(day)
        assert len(caplog.messages) == num_log_msgs
    assert mock_session.call_count == 1
    assert len(prices) == 0
Ejemplo n.º 3
0
async def test_real_download_today_async():
    async with ClientSession() as session:
        pvpc_handler = PVPCData("discrimination", websession=session)
        prices = await pvpc_handler.async_update_prices(datetime.utcnow())
    assert 22 < len(prices) < 49

    # Check error without session
    pvpc_handler_bad = PVPCData("discriminacion")
    with pytest.raises(AssertionError):
        await pvpc_handler_bad.async_update_prices(datetime.utcnow())
Ejemplo n.º 4
0
def test_real_download_range():
    # No async
    pvpc_handler = PVPCData("normal")
    start = datetime(2019, 10, 26, 15)
    end = datetime(2019, 10, 28, 13)
    prices = pvpc_handler.download_prices_for_range(start, end)
    assert len(prices) == 48

    no_prices = pvpc_handler.download_prices_for_range(
        datetime(2010, 8, 26, 23), datetime(2010, 8, 27, 22))
    assert len(no_prices) == 0
Ejemplo n.º 5
0
 def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
     """Initialize."""
     self.api = PVPCData(
         session=async_get_clientsession(hass),
         tariff=entry.data[ATTR_TARIFF],
         local_timezone=hass.config.time_zone,
         power=entry.data[ATTR_POWER],
         power_valley=entry.data[ATTR_POWER_P3],
     )
     super().__init__(
         hass, _LOGGER, name=DOMAIN, update_interval=timedelta(minutes=30)
     )
     self._entry = entry
Ejemplo n.º 6
0
async def test_real_download_range_async():
    start = datetime(2019, 10, 26, 15)
    end = datetime(2019, 10, 28, 13)
    async with ClientSession() as session:
        pvpc_handler = PVPCData("normal", websession=session)
        prices = await pvpc_handler.async_download_prices_for_range(start, end)
    assert len(prices) == 48

    # without session also works, creating one for each download range call
    pvpc_handler_no_s = PVPCData("normal")
    prices2 = await pvpc_handler_no_s.async_download_prices_for_range(
        start, end)
    assert len(prices2) == 48
    assert prices == prices2
Ejemplo n.º 7
0
async def test_download_range(caplog):
    """Test retrieval of full PVPC data in a day range."""
    start = datetime(2019, 10, 26, 15)
    end = datetime(2019, 10, 28, 13)
    mock_session = MockAsyncSession()

    with caplog.at_level(logging.WARNING):
        pvpc_data = PVPCData(tariff="electric_car",
                             local_timezone=_TZ_TEST,
                             websession=mock_session)
        prices = await pvpc_data.async_download_prices_for_range(start, end)
        assert mock_session.call_count == 3
        assert len(prices) == 33
        assert len(caplog.messages) == 2

        no_prices = await pvpc_data.async_download_prices_for_range(
            datetime(2010, 8, 26, 23), datetime(2010, 8, 27, 22))
        assert len(no_prices) == 0
        assert len(caplog.messages) == 4

    first_price = min(prices)
    assert first_price.hour == 14 and first_price.tzname() == "UTC"
    # Check only tariff values are retrieved
    assert isinstance(prices[first_price], float)
    assert prices[first_price] < 1
Ejemplo n.º 8
0
async def test_price_extract(day_str, num_prices, num_calls, num_prices_8h,
                             available_8h):
    """Test data parsing of official API files."""
    day = datetime.fromisoformat(day_str)
    mock_session = MockAsyncSession()

    pvpc_data = PVPCData(
        local_timezone=_TZ_TEST,
        tariff="discrimination",
        websession=mock_session,
    )

    pvpc_data.source_available = True
    assert not pvpc_data.process_state_and_attributes(day)

    await pvpc_data.async_update_prices(day)
    has_prices = pvpc_data.process_state_and_attributes(day)
    assert len(pvpc_data._current_prices) == num_prices
    assert mock_session.call_count == num_calls
    assert has_prices

    has_prices = pvpc_data.process_state_and_attributes(day +
                                                        timedelta(hours=10))
    assert len(pvpc_data._current_prices) == num_prices_8h
    assert has_prices == available_8h
Ejemplo n.º 9
0
def test_full_data_download_range():
    """Test retrieval of full PVPC data in a day range."""
    start = _TZ_TEST.localize(datetime(2019, 10, 26, 15))
    end = _TZ_TEST.localize(datetime(2019, 10, 27, 13))

    with patch("aiohttp.ClientSession", MockAsyncSession):
        pvpc_data = PVPCData()
        prices = pvpc_data.download_prices_for_range(start, end)

    assert len(prices) == 24
    first_price = min(prices)
    last_price = max(prices)
    assert first_price.hour == 14 and first_price.tzname() == "UTC"
    assert last_price.hour == 13 and last_price.tzname() == "UTC"
    data_first_hour = prices[first_price]

    # Check full PVPC data is retrieved
    assert len(data_first_hour) == 30
    assert all(tag in data_first_hour for tag in ESIOS_TARIFFS)

    # Check units have not changed in full data retrieval (they are in €/MWh)
    assert all(data_first_hour[tag] > 1 for tag in ESIOS_TARIFFS)
Ejemplo n.º 10
0
def test_full_data_download_range(timezone, start, end):
    """Test retrieval of full PVPC data in a day range."""
    with patch("aiohttp.ClientSession", MockAsyncSession):
        pvpc_data = PVPCData(local_timezone=timezone)
        prices = pvpc_data.download_prices_for_range(start, end)

    assert len(prices) == 24
    first_price = min(prices)
    last_price = max(prices)
    data_first_hour = prices[first_price]

    # Check full PVPC data is retrieved
    assert len(data_first_hour) == 30
    assert all(tag in data_first_hour for tag in ESIOS_TARIFFS)

    # Check units have not changed in full data retrieval (they are in €/MWh)
    assert all(data_first_hour[tag] > 1 for tag in ESIOS_TARIFFS)

    # check tz-alignment (price at 15h is tz-independent)
    assert prices[first_price]["NOC"] == 119.16
    assert first_price.astimezone(timezone).hour == 15
    assert last_price.astimezone(timezone).hour == 13
Ejemplo n.º 11
0
async def async_setup_entry(hass: HomeAssistant,
                            config_entry: config_entries.ConfigEntry,
                            async_add_entities):
    """Set up the electricity price sensor from config_entry."""
    name = config_entry.data[CONF_NAME]
    pvpc_data_handler = PVPCData(
        tariff=config_entry.data[ATTR_TARIFF],
        local_timezone=hass.config.time_zone,
        websession=async_get_clientsession(hass),
        logger=_LOGGER,
        timeout=_DEFAULT_TIMEOUT,
    )
    async_add_entities(
        [ElecPriceSensor(name, config_entry.unique_id, pvpc_data_handler)],
        False)
Ejemplo n.º 12
0
async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up the electricity price sensor from config_entry."""
    name = config_entry.data[CONF_NAME]
    pvpc_data_handler = PVPCData(
        tariff=config_entry.data[ATTR_TARIFF],
        power=config_entry.data[ATTR_POWER],
        power_valley=config_entry.data[ATTR_POWER_P3],
        local_timezone=hass.config.time_zone,
        websession=async_get_clientsession(hass),
        timeout=_DEFAULT_TIMEOUT,
    )
    async_add_entities(
        [ElecPriceSensor(name, config_entry.unique_id, pvpc_data_handler)],
        False)
Ejemplo n.º 13
0
async def get_pvpc_data(
        consumo: pd.Series,
        path_csv_pvpc_store: Optional[Union[Path,
                                            str]] = None) -> pd.DataFrame:
    """
    Download PVPC data for the given consumption series using `aiopvpc`.

    If a path for a PVPC local csv store is given, it'll try to use pre-loaded data,
     and it'll update the local file with new data.
    """
    df_store = pd.DataFrame()
    path_pvpc_csv = None

    if path_csv_pvpc_store is not None:
        # Use PVPC local storage
        path_pvpc_csv = Path(path_csv_pvpc_store)

    # check if already have it
    if path_csv_pvpc_store is not None and path_pvpc_csv.exists():
        df_store = _load_stored_csv(path_pvpc_csv)
        df = df_store.loc[consumo.index[0]:consumo.index[-1]]
        if not df.empty and df.shape[0] == consumo.shape[0]:
            print("USING cached data ;-)")
            return df

    # proceed to download PVPC range
    pvpc_handler = PVPCData()
    data = await pvpc_handler.async_download_prices_for_range(
        consumo.index[0], consumo.index[-1])
    df = pd.DataFrame(data).T.reindex(consumo.index)
    assert df.index.equals(consumo.index)

    if path_csv_pvpc_store is None:
        return df

    if df_store.empty:
        df.round(12).to_csv(path_pvpc_csv)
        return df

    # TODO check drop duplicates!
    df_store = df_store.append(df).drop_duplicates().sort_index()
    df_store.round(12).to_csv(path_pvpc_csv)
    return df