mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Adopt new electricity tariffs in pvpc hourly pricing (#51789)
This commit is contained in:
parent
d4ac5bf048
commit
b7c1df7864
@ -1,17 +1,38 @@
|
||||
"""The pvpc_hourly_pricing integration to collect Spain official electric prices."""
|
||||
import logging
|
||||
|
||||
from aiopvpc import DEFAULT_POWER_KW, TARIFFS
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_registry import (
|
||||
EntityRegistry,
|
||||
async_get,
|
||||
async_migrate_entries,
|
||||
)
|
||||
|
||||
from .const import ATTR_TARIFF, DEFAULT_NAME, DEFAULT_TARIFF, DOMAIN, PLATFORMS, TARIFFS
|
||||
from .const import (
|
||||
ATTR_POWER,
|
||||
ATTR_POWER_P3,
|
||||
ATTR_TARIFF,
|
||||
DEFAULT_NAME,
|
||||
DOMAIN,
|
||||
PLATFORMS,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_DEFAULT_TARIFF = TARIFFS[0]
|
||||
VALID_POWER = vol.All(vol.Coerce(float), vol.Range(min=1.0, max=15.0))
|
||||
VALID_TARIFF = vol.In(TARIFFS)
|
||||
UI_CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME, default=DEFAULT_NAME): str,
|
||||
vol.Required(ATTR_TARIFF, default=DEFAULT_TARIFF): vol.In(TARIFFS),
|
||||
vol.Required(ATTR_TARIFF, default=_DEFAULT_TARIFF): VALID_TARIFF,
|
||||
vol.Required(ATTR_POWER, default=DEFAULT_POWER_KW): VALID_POWER,
|
||||
vol.Required(ATTR_POWER_P3, default=DEFAULT_POWER_KW): VALID_POWER,
|
||||
}
|
||||
)
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
@ -20,19 +41,8 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: dict):
|
||||
"""
|
||||
Set up the electricity price sensor from configuration.yaml.
|
||||
|
||||
```yaml
|
||||
pvpc_hourly_pricing:
|
||||
- name: PVPC manual ve
|
||||
tariff: electric_car
|
||||
- name: PVPC manual nocturna
|
||||
tariff: discrimination
|
||||
timeout: 3
|
||||
```
|
||||
"""
|
||||
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
||||
"""Set up the electricity price sensor from configuration.yaml."""
|
||||
for conf in config.get(DOMAIN, []):
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
@ -45,10 +55,67 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up pvpc hourly pricing from a config entry."""
|
||||
if len(entry.data) == 2:
|
||||
defaults = {
|
||||
ATTR_TARIFF: _DEFAULT_TARIFF,
|
||||
ATTR_POWER: DEFAULT_POWER_KW,
|
||||
ATTR_POWER_P3: DEFAULT_POWER_KW,
|
||||
}
|
||||
data = {**entry.data, **defaults}
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, unique_id=_DEFAULT_TARIFF, data=data, options=defaults
|
||||
)
|
||||
|
||||
@callback
|
||||
def update_unique_id(reg_entry):
|
||||
"""Change unique id for sensor entity, pointing to new tariff."""
|
||||
return {"new_unique_id": _DEFAULT_TARIFF}
|
||||
|
||||
try:
|
||||
await async_migrate_entries(hass, entry.entry_id, update_unique_id)
|
||||
_LOGGER.warning(
|
||||
"Migrating PVPC sensor from old tariff '%s' to new '%s'. "
|
||||
"Configure the integration to set your contracted power, "
|
||||
"and select prices for Ceuta/Melilla, "
|
||||
"if that is your case",
|
||||
entry.data[ATTR_TARIFF],
|
||||
_DEFAULT_TARIFF,
|
||||
)
|
||||
except ValueError:
|
||||
# there were multiple sensors (with different old tariffs, up to 3),
|
||||
# so we leave just one and remove the others
|
||||
ent_reg: EntityRegistry = async_get(hass)
|
||||
for entity_id, reg_entry in ent_reg.entities.items():
|
||||
if reg_entry.config_entry_id == entry.entry_id:
|
||||
ent_reg.async_remove(entity_id)
|
||||
_LOGGER.warning(
|
||||
"Old PVPC Sensor %s is removed "
|
||||
"(another one already exists, using the same tariff)",
|
||||
entity_id,
|
||||
)
|
||||
break
|
||||
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
return False
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
entry.async_on_unload(entry.add_update_listener(async_update_options))
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
if any(
|
||||
entry.data.get(attrib) != entry.options.get(attrib)
|
||||
for attrib in (ATTR_TARIFF, ATTR_POWER, ATTR_POWER_P3)
|
||||
):
|
||||
# update entry replacing data with new options
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, data={**entry.data, **entry.options}
|
||||
)
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
@ -1,17 +1,24 @@
|
||||
"""Config flow for pvpc_hourly_pricing."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import CONF_NAME, UI_CONFIG_SCHEMA
|
||||
from .const import ATTR_TARIFF, DOMAIN
|
||||
|
||||
_DOMAIN_NAME = DOMAIN
|
||||
from . import CONF_NAME, UI_CONFIG_SCHEMA, VALID_POWER, VALID_TARIFF
|
||||
from .const import ATTR_POWER, ATTR_POWER_P3, ATTR_TARIFF, DOMAIN
|
||||
|
||||
|
||||
class TariffSelectorConfigFlow(config_entries.ConfigFlow, domain=_DOMAIN_NAME):
|
||||
"""Handle a config flow for `pvpc_hourly_pricing` to select the tariff."""
|
||||
class TariffSelectorConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle config flow for `pvpc_hourly_pricing`."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for this handler."""
|
||||
return PVPCOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the initial step."""
|
||||
if user_input is not None:
|
||||
@ -24,3 +31,35 @@ class TariffSelectorConfigFlow(config_entries.ConfigFlow, domain=_DOMAIN_NAME):
|
||||
async def async_step_import(self, import_info):
|
||||
"""Handle import from config file."""
|
||||
return await self.async_step_user(import_info)
|
||||
|
||||
|
||||
class PVPCOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle PVPC options."""
|
||||
|
||||
def __init__(self, config_entry):
|
||||
"""Initialize options flow."""
|
||||
self.config_entry = config_entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
# Fill options with entry data
|
||||
tariff = self.config_entry.options.get(
|
||||
ATTR_TARIFF, self.config_entry.data[ATTR_TARIFF]
|
||||
)
|
||||
power = self.config_entry.options.get(
|
||||
ATTR_POWER, self.config_entry.data[ATTR_POWER]
|
||||
)
|
||||
power_valley = self.config_entry.options.get(
|
||||
ATTR_POWER_P3, self.config_entry.data[ATTR_POWER_P3]
|
||||
)
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_TARIFF, default=tariff): VALID_TARIFF,
|
||||
vol.Required(ATTR_POWER, default=power): VALID_POWER,
|
||||
vol.Required(ATTR_POWER_P3, default=power_valley): VALID_POWER,
|
||||
}
|
||||
)
|
||||
return self.async_show_form(step_id="init", data_schema=schema)
|
||||
|
@ -1,8 +1,7 @@
|
||||
"""Constant values for pvpc_hourly_pricing."""
|
||||
from aiopvpc import TARIFFS
|
||||
|
||||
DOMAIN = "pvpc_hourly_pricing"
|
||||
PLATFORMS = ["sensor"]
|
||||
ATTR_POWER = "power"
|
||||
ATTR_POWER_P3 = "power_p3"
|
||||
ATTR_TARIFF = "tariff"
|
||||
DEFAULT_NAME = "PVPC"
|
||||
DEFAULT_TARIFF = TARIFFS[1]
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "Spain electricity hourly pricing (PVPC)",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/pvpc_hourly_pricing",
|
||||
"requirements": ["aiopvpc==2.1.2"],
|
||||
"requirements": ["aiopvpc==2.2.0"],
|
||||
"codeowners": ["@azogue"],
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "cloud_polling"
|
||||
|
@ -3,19 +3,21 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from random import randint
|
||||
from typing import Any
|
||||
|
||||
from aiopvpc import PVPCData
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, CURRENCY_EURO, ENERGY_KILO_WATT_HOUR
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_call_later, async_track_time_change
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .const import ATTR_TARIFF
|
||||
from .const import ATTR_POWER, ATTR_POWER_P3, ATTR_TARIFF
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -27,15 +29,18 @@ _DEFAULT_TIMEOUT = 10
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: config_entries.ConfigEntry, async_add_entities
|
||||
):
|
||||
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),
|
||||
logger=_LOGGER,
|
||||
timeout=_DEFAULT_TIMEOUT,
|
||||
)
|
||||
async_add_entities(
|
||||
@ -57,15 +62,7 @@ class ElecPriceSensor(RestoreEntity, SensorEntity):
|
||||
self._pvpc_data = pvpc_data_handler
|
||||
self._num_retries = 0
|
||||
|
||||
self._hourly_tracker = None
|
||||
self._price_tracker = None
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Cancel listeners for sensor updates."""
|
||||
self._hourly_tracker()
|
||||
self._price_tracker()
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Handle entity which will be added."""
|
||||
await super().async_added_to_hass()
|
||||
state = await self.async_get_last_state()
|
||||
@ -73,14 +70,18 @@ class ElecPriceSensor(RestoreEntity, SensorEntity):
|
||||
self._pvpc_data.state = state.state
|
||||
|
||||
# Update 'state' value in hour changes
|
||||
self._hourly_tracker = async_track_time_change(
|
||||
self.hass, self.update_current_price, second=[0], minute=[0]
|
||||
self.async_on_remove(
|
||||
async_track_time_change(
|
||||
self.hass, self.update_current_price, second=[0], minute=[0]
|
||||
)
|
||||
)
|
||||
# Update prices at random time, 2 times/hour (don't want to upset API)
|
||||
random_minute = randint(1, 29)
|
||||
mins_update = [random_minute, random_minute + 30]
|
||||
self._price_tracker = async_track_time_change(
|
||||
self.hass, self.async_update_prices, second=[0], minute=mins_update
|
||||
self.async_on_remove(
|
||||
async_track_time_change(
|
||||
self.hass, self.async_update_prices, second=[0], minute=mins_update
|
||||
)
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Setup of price sensor %s (%s) with tariff '%s', "
|
||||
@ -90,8 +91,9 @@ class ElecPriceSensor(RestoreEntity, SensorEntity):
|
||||
self._pvpc_data.tariff,
|
||||
mins_update,
|
||||
)
|
||||
await self.async_update_prices(dt_util.utcnow())
|
||||
self.update_current_price(dt_util.utcnow())
|
||||
now = dt_util.utcnow()
|
||||
await self.async_update_prices(now)
|
||||
self.update_current_price(now)
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str | None:
|
||||
@ -99,12 +101,12 @@ class ElecPriceSensor(RestoreEntity, SensorEntity):
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
def name(self) -> str:
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def state(self) -> float:
|
||||
"""Return the state of the sensor."""
|
||||
return self._pvpc_data.state
|
||||
|
||||
@ -114,7 +116,7 @@ class ElecPriceSensor(RestoreEntity, SensorEntity):
|
||||
return self._pvpc_data.state_available
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
return self._pvpc_data.attributes
|
||||
|
||||
|
@ -2,16 +2,31 @@
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Tariff selection",
|
||||
"description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).\n\nSelect the contracted rate based on the number of billing periods per day:\n- 1 period: normal\n- 2 periods: discrimination (nightly rate)\n- 3 periods: electric car (nightly rate of 3 periods)",
|
||||
"title": "Sensor setup",
|
||||
"description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).",
|
||||
"data": {
|
||||
"name": "Sensor Name",
|
||||
"tariff": "Contracted tariff (1, 2, or 3 periods)"
|
||||
"tariff": "Applicable tariff by geographic zone",
|
||||
"power": "Contracted power (kW)",
|
||||
"power_p3": "Contracted power for valley period P3 (kW)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Sensor setup",
|
||||
"description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).",
|
||||
"data": {
|
||||
"tariff": "Applicable tariff by geographic zone",
|
||||
"power": "Contracted power (kW)",
|
||||
"power_p3": "Contracted power for valley period P3 (kW)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,25 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"name": "Sensor Name",
|
||||
"tariff": "Contracted tariff (1, 2, or 3 periods)"
|
||||
"power": "Contracted power (kW)",
|
||||
"power_p3": "Contracted power for valley period P3 (kW)",
|
||||
"tariff": "Applicable tariff by geographic zone"
|
||||
},
|
||||
"description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).\n\nSelect the contracted rate based on the number of billing periods per day:\n- 1 period: normal\n- 2 periods: discrimination (nightly rate)\n- 3 periods: electric car (nightly rate of 3 periods)",
|
||||
"title": "Tariff selection"
|
||||
"description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).",
|
||||
"title": "Sensor setup"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"power": "Contracted power (kW)",
|
||||
"power_p3": "Contracted power for valley period P3 (kW)",
|
||||
"tariff": "Applicable tariff by geographic zone"
|
||||
},
|
||||
"description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).",
|
||||
"title": "Sensor setup"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ aiopulse==0.4.2
|
||||
aiopvapi==1.6.14
|
||||
|
||||
# homeassistant.components.pvpc_hourly_pricing
|
||||
aiopvpc==2.1.2
|
||||
aiopvpc==2.2.0
|
||||
|
||||
# homeassistant.components.webostv
|
||||
aiopylgtv==0.4.0
|
||||
|
@ -146,7 +146,7 @@ aiopulse==0.4.2
|
||||
aiopvapi==1.6.14
|
||||
|
||||
# homeassistant.components.pvpc_hourly_pricing
|
||||
aiopvpc==2.1.2
|
||||
aiopvpc==2.2.0
|
||||
|
||||
# homeassistant.components.webostv
|
||||
aiopylgtv==0.4.0
|
||||
|
@ -14,6 +14,7 @@ from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
FIXTURE_JSON_DATA_2019_10_26 = "PVPC_CURV_DD_2019_10_26.json"
|
||||
FIXTURE_JSON_DATA_2019_10_27 = "PVPC_CURV_DD_2019_10_27.json"
|
||||
FIXTURE_JSON_DATA_2019_10_29 = "PVPC_CURV_DD_2019_10_29.json"
|
||||
FIXTURE_JSON_DATA_2021_06_01 = "PVPC_CURV_DD_2021_06_01.json"
|
||||
|
||||
|
||||
def check_valid_state(state, tariff: str, value=None, key_attr=None):
|
||||
@ -60,4 +61,10 @@ def pvpc_aioclient_mock(aioclient_mock: AiohttpClientMocker):
|
||||
text=load_fixture(f"{DOMAIN}/{FIXTURE_JSON_DATA_2019_10_29}"),
|
||||
)
|
||||
|
||||
# new format for prices >= 2021-06-01
|
||||
aioclient_mock.get(
|
||||
"https://api.esios.ree.es/archives/70/download_json?locale=es&date=2021-06-01",
|
||||
text=load_fixture(f"{DOMAIN}/{FIXTURE_JSON_DATA_2021_06_01}"),
|
||||
)
|
||||
|
||||
return aioclient_mock
|
||||
|
@ -3,7 +3,13 @@ from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.pvpc_hourly_pricing import ATTR_TARIFF, DOMAIN
|
||||
from homeassistant.components.pvpc_hourly_pricing import (
|
||||
ATTR_POWER,
|
||||
ATTR_POWER_P3,
|
||||
ATTR_TARIFF,
|
||||
DOMAIN,
|
||||
TARIFFS,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util import dt as dt_util
|
||||
@ -20,13 +26,20 @@ async def test_config_flow(
|
||||
"""
|
||||
Test config flow for pvpc_hourly_pricing.
|
||||
|
||||
- Create a new entry with tariff "normal"
|
||||
- Create a new entry with tariff "2.0TD (Ceuta/Melilla)"
|
||||
- Check state and attributes
|
||||
- Check abort when trying to config another with same tariff
|
||||
- Check removal and add again to check state restoration
|
||||
- Configure options to change power and tariff to "2.0TD"
|
||||
"""
|
||||
hass.config.time_zone = dt_util.get_time_zone("Europe/Madrid")
|
||||
mock_data = {"return_time": datetime(2019, 10, 26, 14, 0, tzinfo=date_util.UTC)}
|
||||
tst_config = {
|
||||
CONF_NAME: "test",
|
||||
ATTR_TARIFF: TARIFFS[1],
|
||||
ATTR_POWER: 4.6,
|
||||
ATTR_POWER_P3: 5.75,
|
||||
}
|
||||
mock_data = {"return_time": datetime(2021, 6, 1, 12, 0, tzinfo=date_util.UTC)}
|
||||
|
||||
def mock_now():
|
||||
return mock_data["return_time"]
|
||||
@ -38,13 +51,13 @@ async def test_config_flow(
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_NAME: "test", ATTR_TARIFF: "normal"}
|
||||
result["flow_id"], tst_config
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test")
|
||||
check_valid_state(state, tariff="normal")
|
||||
check_valid_state(state, tariff=TARIFFS[1])
|
||||
assert pvpc_aioclient_mock.call_count == 1
|
||||
|
||||
# Check abort when configuring another with same tariff
|
||||
@ -53,7 +66,7 @@ async def test_config_flow(
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_NAME: "test", ATTR_TARIFF: "normal"}
|
||||
result["flow_id"], tst_config
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert pvpc_aioclient_mock.call_count == 1
|
||||
@ -70,11 +83,38 @@ async def test_config_flow(
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_NAME: "test", ATTR_TARIFF: "normal"}
|
||||
result["flow_id"], tst_config
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test")
|
||||
check_valid_state(state, tariff="normal")
|
||||
check_valid_state(state, tariff=TARIFFS[1])
|
||||
price_pbc = state.state
|
||||
assert pvpc_aioclient_mock.call_count == 2
|
||||
assert state.attributes["period"] == "P2"
|
||||
assert state.attributes["next_period"] == "P1"
|
||||
assert state.attributes["available_power"] == 4600
|
||||
|
||||
# check options flow
|
||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(current_entries) == 1
|
||||
config_entry = current_entries[0]
|
||||
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={ATTR_TARIFF: TARIFFS[0], ATTR_POWER: 3.0, ATTR_POWER_P3: 4.6},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test")
|
||||
price_cym = state.state
|
||||
check_valid_state(state, tariff=TARIFFS[0])
|
||||
assert pvpc_aioclient_mock.call_count == 3
|
||||
assert state.attributes["period"] == "P2"
|
||||
assert state.attributes["next_period"] == "P1"
|
||||
assert state.attributes["available_power"] == 3000
|
||||
assert price_cym < price_pbc
|
||||
|
@ -3,15 +3,20 @@ from datetime import datetime, timedelta
|
||||
import logging
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.pvpc_hourly_pricing import ATTR_TARIFF, DOMAIN
|
||||
from homeassistant.components.pvpc_hourly_pricing import (
|
||||
ATTR_POWER,
|
||||
ATTR_POWER_P3,
|
||||
ATTR_TARIFF,
|
||||
DOMAIN,
|
||||
TARIFFS,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import ATTR_NOW, EVENT_TIME_CHANGED
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .conftest import check_valid_state
|
||||
|
||||
from tests.common import date_util
|
||||
from tests.common import MockConfigEntry, date_util, mock_registry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
@ -32,14 +37,27 @@ async def test_sensor_availability(
|
||||
):
|
||||
"""Test sensor availability and handling of cloud access."""
|
||||
hass.config.time_zone = dt_util.get_time_zone("Europe/Madrid")
|
||||
config = {DOMAIN: [{CONF_NAME: "test_dst", ATTR_TARIFF: "discrimination"}]}
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_NAME: "test_dst", ATTR_TARIFF: "discrimination"}
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
mock_data = {"return_time": datetime(2019, 10, 27, 20, 0, 0, tzinfo=date_util.UTC)}
|
||||
|
||||
def mock_now():
|
||||
return mock_data["return_time"]
|
||||
|
||||
with patch("homeassistant.util.dt.utcnow", new=mock_now):
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
# check migration
|
||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(current_entries) == 1
|
||||
migrated_entry = current_entries[0]
|
||||
assert migrated_entry.version == 1
|
||||
assert migrated_entry.data[ATTR_POWER] == migrated_entry.data[ATTR_POWER_P3]
|
||||
assert migrated_entry.data[ATTR_TARIFF] == TARIFFS[0]
|
||||
|
||||
await hass.async_block_till_done()
|
||||
caplog.clear()
|
||||
assert pvpc_aioclient_mock.call_count == 2
|
||||
@ -85,3 +103,64 @@ async def test_sensor_availability(
|
||||
assert pvpc_aioclient_mock.call_count == 33
|
||||
assert len(caplog.messages) == 1
|
||||
assert caplog.records[0].levelno == logging.WARNING
|
||||
|
||||
|
||||
async def test_multi_sensor_migration(
|
||||
hass, caplog, legacy_patchable_time, pvpc_aioclient_mock: AiohttpClientMocker
|
||||
):
|
||||
"""Test tariff migration when there are >1 old sensors."""
|
||||
entity_reg = mock_registry(hass)
|
||||
hass.config.time_zone = dt_util.get_time_zone("Europe/Madrid")
|
||||
uid_1 = "discrimination"
|
||||
uid_2 = "normal"
|
||||
old_conf_1 = {CONF_NAME: "test_pvpc_1", ATTR_TARIFF: uid_1}
|
||||
old_conf_2 = {CONF_NAME: "test_pvpc_2", ATTR_TARIFF: uid_2}
|
||||
|
||||
config_entry_1 = MockConfigEntry(domain=DOMAIN, data=old_conf_1, unique_id=uid_1)
|
||||
config_entry_1.add_to_hass(hass)
|
||||
entity1 = entity_reg.async_get_or_create(
|
||||
domain="sensor",
|
||||
platform=DOMAIN,
|
||||
unique_id=uid_1,
|
||||
config_entry=config_entry_1,
|
||||
suggested_object_id="test_pvpc_1",
|
||||
)
|
||||
|
||||
config_entry_2 = MockConfigEntry(domain=DOMAIN, data=old_conf_2, unique_id=uid_2)
|
||||
config_entry_2.add_to_hass(hass)
|
||||
entity2 = entity_reg.async_get_or_create(
|
||||
domain="sensor",
|
||||
platform=DOMAIN,
|
||||
unique_id=uid_2,
|
||||
config_entry=config_entry_2,
|
||||
suggested_object_id="test_pvpc_2",
|
||||
)
|
||||
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 2
|
||||
assert len(entity_reg.entities) == 2
|
||||
|
||||
mock_data = {"return_time": datetime(2019, 10, 27, 20, tzinfo=date_util.UTC)}
|
||||
|
||||
def mock_now():
|
||||
return mock_data["return_time"]
|
||||
|
||||
caplog.clear()
|
||||
with caplog.at_level(logging.WARNING):
|
||||
with patch("homeassistant.util.dt.utcnow", new=mock_now):
|
||||
assert await hass.config_entries.async_setup(config_entry_1.entry_id)
|
||||
assert len(caplog.messages) == 2
|
||||
|
||||
# check migration with removal of extra sensors
|
||||
assert len(entity_reg.entities) == 1
|
||||
assert entity1.entity_id in entity_reg.entities
|
||||
assert entity2.entity_id not in entity_reg.entities
|
||||
|
||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(current_entries) == 1
|
||||
migrated_entry = current_entries[0]
|
||||
assert migrated_entry.version == 1
|
||||
assert migrated_entry.data[ATTR_POWER] == migrated_entry.data[ATTR_POWER_P3]
|
||||
assert migrated_entry.data[ATTR_TARIFF] == TARIFFS[0]
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert pvpc_aioclient_mock.call_count == 2
|
||||
|
604
tests/fixtures/pvpc_hourly_pricing/PVPC_CURV_DD_2021_06_01.json
vendored
Normal file
604
tests/fixtures/pvpc_hourly_pricing/PVPC_CURV_DD_2021_06_01.json
vendored
Normal file
@ -0,0 +1,604 @@
|
||||
{
|
||||
"PVPC": [
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "00-01",
|
||||
"PCB": "116,33",
|
||||
"CYM": "116,33",
|
||||
"COF2TD": "0,000088075182000000",
|
||||
"PMHPCB": "104,00",
|
||||
"PMHCYM": "104,00",
|
||||
"SAHPCB": "3,56",
|
||||
"SAHCYM": "3,56",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,57",
|
||||
"CCVCYM": "2,57",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "01-02",
|
||||
"PCB": "115,95",
|
||||
"CYM": "115,95",
|
||||
"COF2TD": "0,000073094842000000",
|
||||
"PMHPCB": "103,18",
|
||||
"PMHCYM": "103,18",
|
||||
"SAHPCB": "3,99",
|
||||
"SAHCYM": "3,99",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,58",
|
||||
"CCVCYM": "2,58",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "02-03",
|
||||
"PCB": "114,89",
|
||||
"CYM": "114,89",
|
||||
"COF2TD": "0,000065114032000000",
|
||||
"PMHPCB": "101,87",
|
||||
"PMHCYM": "101,87",
|
||||
"SAHPCB": "4,25",
|
||||
"SAHCYM": "4,25",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,56",
|
||||
"CCVCYM": "2,56",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "03-04",
|
||||
"PCB": "114,96",
|
||||
"CYM": "114,96",
|
||||
"COF2TD": "0,000061272596000000",
|
||||
"PMHPCB": "102,01",
|
||||
"PMHCYM": "102,01",
|
||||
"SAHPCB": "4,19",
|
||||
"SAHCYM": "4,19",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,57",
|
||||
"CCVCYM": "2,57",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "04-05",
|
||||
"PCB": "114,84",
|
||||
"CYM": "114,84",
|
||||
"COF2TD": "0,000059563056000000",
|
||||
"PMHPCB": "101,87",
|
||||
"PMHCYM": "101,87",
|
||||
"SAHPCB": "4,21",
|
||||
"SAHCYM": "4,21",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,56",
|
||||
"CCVCYM": "2,56",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "05-06",
|
||||
"PCB": "116,03",
|
||||
"CYM": "116,03",
|
||||
"COF2TD": "0,000059907686000000",
|
||||
"PMHPCB": "103,14",
|
||||
"PMHCYM": "103,14",
|
||||
"SAHPCB": "4,11",
|
||||
"SAHCYM": "4,11",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,58",
|
||||
"CCVCYM": "2,58",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "06-07",
|
||||
"PCB": "116,29",
|
||||
"CYM": "116,29",
|
||||
"COF2TD": "0,000062818713000000",
|
||||
"PMHPCB": "103,64",
|
||||
"PMHCYM": "103,64",
|
||||
"SAHPCB": "3,88",
|
||||
"SAHCYM": "3,88",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,57",
|
||||
"CCVCYM": "2,57",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "07-08",
|
||||
"PCB": "115,70",
|
||||
"CYM": "115,70",
|
||||
"COF2TD": "0,000072575564000000",
|
||||
"PMHPCB": "103,85",
|
||||
"PMHCYM": "103,85",
|
||||
"SAHPCB": "3,10",
|
||||
"SAHCYM": "3,10",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,00",
|
||||
"PCAPCYM": "0,00",
|
||||
"TEUPCB": "6,00",
|
||||
"TEUCYM": "6,00",
|
||||
"CCVPCB": "2,55",
|
||||
"CCVCYM": "2,55",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "08-09",
|
||||
"PCB": "152,89",
|
||||
"CYM": "152,89",
|
||||
"COF2TD": "0,000086825264000000",
|
||||
"PMHPCB": "105,65",
|
||||
"PMHCYM": "105,65",
|
||||
"SAHPCB": "2,36",
|
||||
"SAHCYM": "2,36",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,34",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,57",
|
||||
"CCVCYM": "2,57",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "09-10",
|
||||
"PCB": "150,83",
|
||||
"CYM": "150,83",
|
||||
"COF2TD": "0,000095768317000000",
|
||||
"PMHPCB": "103,77",
|
||||
"PMHCYM": "103,77",
|
||||
"SAHPCB": "2,24",
|
||||
"SAHCYM": "2,24",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,34",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,53",
|
||||
"CCVCYM": "2,53",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "10-11",
|
||||
"PCB": "242,62",
|
||||
"CYM": "149,28",
|
||||
"COF2TD": "0,000102672431000000",
|
||||
"PMHPCB": "102,38",
|
||||
"PMHCYM": "102,11",
|
||||
"SAHPCB": "2,38",
|
||||
"SAHCYM": "2,37",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,01",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,54",
|
||||
"CCVCYM": "2,51",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "11-12",
|
||||
"PCB": "240,50",
|
||||
"CYM": "240,50",
|
||||
"COF2TD": "0,000105691470000000",
|
||||
"PMHPCB": "100,14",
|
||||
"PMHCYM": "100,14",
|
||||
"SAHPCB": "2,52",
|
||||
"SAHCYM": "2,52",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,02",
|
||||
"PCAPCYM": "2,02",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,51",
|
||||
"CCVCYM": "2,51",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "12-13",
|
||||
"PCB": "238,09",
|
||||
"CYM": "238,09",
|
||||
"COF2TD": "0,000110462952000000",
|
||||
"PMHPCB": "97,58",
|
||||
"PMHCYM": "97,58",
|
||||
"SAHPCB": "2,71",
|
||||
"SAHCYM": "2,71",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,02",
|
||||
"PCAPCYM": "2,02",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,47",
|
||||
"CCVCYM": "2,47",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "13-14",
|
||||
"PCB": "235,30",
|
||||
"CYM": "235,30",
|
||||
"COF2TD": "0,000119052052000000",
|
||||
"PMHPCB": "94,65",
|
||||
"PMHCYM": "94,65",
|
||||
"SAHPCB": "2,89",
|
||||
"SAHCYM": "2,89",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,02",
|
||||
"PCAPCYM": "2,02",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,43",
|
||||
"CCVCYM": "2,43",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "14-15",
|
||||
"PCB": "137,96",
|
||||
"CYM": "231,28",
|
||||
"COF2TD": "0,000117990009000000",
|
||||
"PMHPCB": "89,95",
|
||||
"PMHCYM": "90,19",
|
||||
"SAHPCB": "3,37",
|
||||
"SAHCYM": "3,38",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,34",
|
||||
"PCAPCYM": "2,03",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,34",
|
||||
"CCVCYM": "2,37",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "15-16",
|
||||
"PCB": "132,88",
|
||||
"CYM": "132,88",
|
||||
"COF2TD": "0,000108598330000000",
|
||||
"PMHPCB": "84,43",
|
||||
"PMHCYM": "84,43",
|
||||
"SAHPCB": "3,89",
|
||||
"SAHCYM": "3,89",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,34",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,26",
|
||||
"CCVCYM": "2,26",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "16-17",
|
||||
"PCB": "131,93",
|
||||
"CYM": "131,93",
|
||||
"COF2TD": "0,000104114191000000",
|
||||
"PMHPCB": "83,66",
|
||||
"PMHCYM": "83,66",
|
||||
"SAHPCB": "3,73",
|
||||
"SAHCYM": "3,73",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,34",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,25",
|
||||
"CCVCYM": "2,25",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "17-18",
|
||||
"PCB": "135,99",
|
||||
"CYM": "135,99",
|
||||
"COF2TD": "0,000105171071000000",
|
||||
"PMHPCB": "88,07",
|
||||
"PMHCYM": "88,07",
|
||||
"SAHPCB": "3,31",
|
||||
"SAHCYM": "3,31",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,34",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,31",
|
||||
"CCVCYM": "2,31",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "18-19",
|
||||
"PCB": "231,44",
|
||||
"CYM": "138,13",
|
||||
"COF2TD": "0,000106417649000000",
|
||||
"PMHPCB": "90,57",
|
||||
"PMHCYM": "90,33",
|
||||
"SAHPCB": "3,16",
|
||||
"SAHCYM": "3,15",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,16",
|
||||
"FOSCYM": "0,16",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,02",
|
||||
"PCAPCYM": "0,34",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,37",
|
||||
"CCVCYM": "2,34",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "19-20",
|
||||
"PCB": "240,40",
|
||||
"CYM": "240,40",
|
||||
"COF2TD": "0,000108017615000000",
|
||||
"PMHPCB": "99,53",
|
||||
"PMHCYM": "99,53",
|
||||
"SAHPCB": "3,00",
|
||||
"SAHCYM": "3,00",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,04",
|
||||
"PCAPCYM": "2,04",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,52",
|
||||
"CCVCYM": "2,52",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "20-21",
|
||||
"PCB": "246,20",
|
||||
"CYM": "246,20",
|
||||
"COF2TD": "0,000114631042000000",
|
||||
"PMHPCB": "104,32",
|
||||
"PMHCYM": "104,32",
|
||||
"SAHPCB": "3,90",
|
||||
"SAHCYM": "3,90",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,05",
|
||||
"PCAPCYM": "2,05",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,61",
|
||||
"CCVCYM": "2,61",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "21-22",
|
||||
"PCB": "248,08",
|
||||
"CYM": "248,08",
|
||||
"COF2TD": "0,000127585671000000",
|
||||
"PMHPCB": "107,28",
|
||||
"PMHCYM": "107,28",
|
||||
"SAHPCB": "2,78",
|
||||
"SAHCYM": "2,78",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "2,06",
|
||||
"PCAPCYM": "2,06",
|
||||
"TEUPCB": "133,12",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,64",
|
||||
"CCVCYM": "2,64",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "22-23",
|
||||
"PCB": "155,91",
|
||||
"CYM": "249,41",
|
||||
"COF2TD": "0,000130129026000000",
|
||||
"PMHPCB": "108,02",
|
||||
"PMHCYM": "108,39",
|
||||
"SAHPCB": "2,93",
|
||||
"SAHCYM": "2,94",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,35",
|
||||
"PCAPCYM": "2,09",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "133,12",
|
||||
"CCVPCB": "2,64",
|
||||
"CCVCYM": "2,67",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
},
|
||||
{
|
||||
"Dia": "01/06/2021",
|
||||
"Hora": "23-24",
|
||||
"PCB": "156,50",
|
||||
"CYM": "156,50",
|
||||
"COF2TD": "0,000110367990000000",
|
||||
"PMHPCB": "108,02",
|
||||
"PMHCYM": "108,02",
|
||||
"SAHPCB": "3,50",
|
||||
"SAHCYM": "3,50",
|
||||
"FOMPCB": "0,03",
|
||||
"FOMCYM": "0,03",
|
||||
"FOSPCB": "0,17",
|
||||
"FOSCYM": "0,17",
|
||||
"INTPCB": "0,00",
|
||||
"INTCYM": "0,00",
|
||||
"PCAPPCB": "0,35",
|
||||
"PCAPCYM": "0,35",
|
||||
"TEUPCB": "41,77",
|
||||
"TEUCYM": "41,77",
|
||||
"CCVPCB": "2,66",
|
||||
"CCVCYM": "2,66",
|
||||
"EDSRPCB": "0,00",
|
||||
"EDSRCYM": "0,00"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user