Skip to content

Commit

Permalink
feat: Separated Home mini electricity and gas refresh rates
Browse files Browse the repository at this point in the history
Co-authored-by: David Kendall <david@rocketmakers.com>
  • Loading branch information
BottlecapDave and DaveK-Rocketmakers committed Sep 22, 2023
1 parent 4799400 commit 6651ebf
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 74 deletions.
28 changes: 28 additions & 0 deletions custom_components/octopus_energy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
from .const import (
DOMAIN,

CONFIG_KIND,
CONFIG_MAIN_API_KEY,
CONFIG_MAIN_ACCOUNT_ID,
CONFIG_MAIN_ELECTRICITY_PRICE_CAP,
CONFIG_MAIN_GAS_PRICE_CAP,
CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES,

CONFIG_TARGET_NAME,

Expand All @@ -33,6 +36,31 @@

SCAN_INTERVAL = timedelta(minutes=1)

async def async_migrate_entry(hass, config_entry):
"""Migrate old entry."""
_LOGGER.debug("Migrating from version %s", config_entry.version)

if config_entry.version == 1:

new = {**config_entry.data}

if CONFIG_MAIN_ACCOUNT_ID in config_entry.data:
new[CONFIG_KIND] = "account"

if "live_consumption_refresh_in_minutes" in new:

new[CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES] = new["live_consumption_refresh_in_minutes"]
new[CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES] = new["live_consumption_refresh_in_minutes"]
else:
new[CONFIG_KIND] = "target_rate"

config_entry.version = 2
hass.config_entries.async_update_entry(config_entry, data=new)

_LOGGER.debug("Migration to version %s successful", config_entry.version)

return True

async def async_setup_entry(hass, entry):
"""This is called from the config flow."""
hass.data.setdefault(DOMAIN, {})
Expand Down
22 changes: 22 additions & 0 deletions custom_components/octopus_energy/config/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from ..const import CONFIG_MAIN_ACCOUNT_ID, CONFIG_MAIN_API_KEY, CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES, CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES, CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION
from ..api_client import OctopusEnergyApiClient


async def async_validate_main_config(data):
errors = {}

client = OctopusEnergyApiClient(data[CONFIG_MAIN_API_KEY])
account_info = await client.async_get_account(data[CONFIG_MAIN_ACCOUNT_ID])

if (account_info is None):
errors[CONFIG_MAIN_API_KEY] = "account_not_found"

if data[CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION] == True:

if data[CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES] < 1:
errors[CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES] = "value_greater_than_zero"

if data[CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES] < 1:
errors[CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES] = "value_greater_than_zero"

return errors
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def is_time_frame_long_enough(hours, start_time, end_time):

return available_minutes >= target_minutes


agile_start = parse_datetime(f"2023-08-01T16:00:00Z")
agile_end = parse_datetime(f"2023-08-01T23:00:00Z")

Expand Down
119 changes: 60 additions & 59 deletions custom_components/octopus_energy/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import voluptuous as vol
import logging

Expand All @@ -7,14 +6,15 @@
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv

from .target_rates.config import validate_target_rate_config
from .config.target_rates import validate_target_rate_config
from .config.main import async_validate_main_config
from .const import (
CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES,
DOMAIN,

CONFIG_MAIN_API_KEY,
CONFIG_MAIN_ACCOUNT_ID,
CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION,
CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_MAIN_CALORIFIC_VALUE,
CONFIG_MAIN_ELECTRICITY_PRICE_CAP,
CONFIG_MAIN_CLEAR_ELECTRICITY_PRICE_CAP,
Expand All @@ -37,8 +37,6 @@
DATA_ACCOUNT_ID,
)

from .api_client import OctopusEnergyApiClient

from .utils import get_active_tariff_code

_LOGGER = logging.getLogger(__name__)
Expand All @@ -63,32 +61,21 @@ def get_target_rate_meters(account_info, now):
class OctopusEnergyConfigFlow(ConfigFlow, domain=DOMAIN):
"""Config flow."""

VERSION = 1
VERSION = 2

async def async_setup_initial_account(self, user_input):
"""Setup the initial account based on the provided user input"""
errors = {}

electricity_price_cap = None
if CONFIG_MAIN_ELECTRICITY_PRICE_CAP in user_input:
electricity_price_cap = user_input[CONFIG_MAIN_ELECTRICITY_PRICE_CAP]

gas_price_cap = None
if CONFIG_MAIN_GAS_PRICE_CAP in user_input:
gas_price_cap = user_input[CONFIG_MAIN_GAS_PRICE_CAP]
errors = await async_validate_main_config(user_input)

client = OctopusEnergyApiClient(user_input[CONFIG_MAIN_API_KEY], electricity_price_cap, gas_price_cap)
account_info = await client.async_get_account(user_input[CONFIG_MAIN_ACCOUNT_ID])
if (account_info is None):
errors[CONFIG_MAIN_ACCOUNT_ID] = "account_not_found"
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA_ACCOUNT, errors=errors
if len(errors) < 1:
# Setup our basic sensors
return self.async_create_entry(
title="Account",
data=user_input
)

# Setup our basic sensors
return self.async_create_entry(
title="Account",
data=user_input
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA_ACCOUNT, errors=errors
)

async def async_setup_target_rate_schema(self):
Expand Down Expand Up @@ -234,6 +221,46 @@ async def __async_setup_target_rate_schema(self, config, errors):
}),
errors=errors
)

async def __async_setup_main_schema(self, config, errors):
supports_live_consumption = False
if CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION in config:
supports_live_consumption = config[CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION]

live_electricity_consumption_refresh_in_minutes = 1
if CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_electricity_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES]

live_gas_consumption_refresh_in_minutes = 2
if CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_gas_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES]

calorific_value = 40
if CONFIG_MAIN_CALORIFIC_VALUE in config:
calorific_value = config[CONFIG_MAIN_CALORIFIC_VALUE]

electricity_price_cap_key = vol.Optional(CONFIG_MAIN_ELECTRICITY_PRICE_CAP)
if (CONFIG_MAIN_ELECTRICITY_PRICE_CAP in config):
electricity_price_cap_key = vol.Optional(CONFIG_MAIN_ELECTRICITY_PRICE_CAP, default=config[CONFIG_MAIN_ELECTRICITY_PRICE_CAP])

gas_price_cap_key = vol.Optional(CONFIG_MAIN_GAS_PRICE_CAP)
if (CONFIG_MAIN_GAS_PRICE_CAP in config):
gas_price_cap_key = vol.Optional(CONFIG_MAIN_GAS_PRICE_CAP, default=config[CONFIG_MAIN_GAS_PRICE_CAP])

return self.async_show_form(
step_id="user", data_schema=vol.Schema({
vol.Required(CONFIG_MAIN_API_KEY, default=config[CONFIG_MAIN_API_KEY]): str,
vol.Required(CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION, default=supports_live_consumption): bool,
vol.Required(CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES, default=live_electricity_consumption_refresh_in_minutes): cv.positive_int,
vol.Required(CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES, default=live_gas_consumption_refresh_in_minutes): cv.positive_int,
vol.Required(CONFIG_MAIN_CALORIFIC_VALUE, default=calorific_value): cv.positive_float,
electricity_price_cap_key: cv.positive_float,
vol.Required(CONFIG_MAIN_CLEAR_ELECTRICITY_PRICE_CAP): bool,
gas_price_cap_key: cv.positive_float,
vol.Required(CONFIG_MAIN_CLEAR_GAS_PRICE_CAP): bool,
}),
errors=errors
)

async def async_step_init(self, user_input):
"""Manage the options for the custom component."""
Expand All @@ -243,38 +270,7 @@ async def async_step_init(self, user_input):
if self._entry.options is not None:
config.update(self._entry.options)

supports_live_consumption = False
if CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION in config:
supports_live_consumption = config[CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION]

live_consumption_refresh_in_minutes = 1
if CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES]

calorific_value = 40
if CONFIG_MAIN_CALORIFIC_VALUE in config:
calorific_value = config[CONFIG_MAIN_CALORIFIC_VALUE]

electricity_price_cap_key = vol.Optional(CONFIG_MAIN_ELECTRICITY_PRICE_CAP)
if (CONFIG_MAIN_ELECTRICITY_PRICE_CAP in config):
electricity_price_cap_key = vol.Optional(CONFIG_MAIN_ELECTRICITY_PRICE_CAP, default=config[CONFIG_MAIN_ELECTRICITY_PRICE_CAP])

gas_price_cap_key = vol.Optional(CONFIG_MAIN_GAS_PRICE_CAP)
if (CONFIG_MAIN_GAS_PRICE_CAP in config):
gas_price_cap_key = vol.Optional(CONFIG_MAIN_GAS_PRICE_CAP, default=config[CONFIG_MAIN_GAS_PRICE_CAP])

return self.async_show_form(
step_id="user", data_schema=vol.Schema({
vol.Required(CONFIG_MAIN_API_KEY, default=config[CONFIG_MAIN_API_KEY]): str,
vol.Required(CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION, default=supports_live_consumption): bool,
vol.Required(CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES, default=live_consumption_refresh_in_minutes): cv.positive_int,
vol.Required(CONFIG_MAIN_CALORIFIC_VALUE, default=calorific_value): cv.positive_float,
electricity_price_cap_key: cv.positive_float,
vol.Required(CONFIG_MAIN_CLEAR_ELECTRICITY_PRICE_CAP): bool,
gas_price_cap_key: cv.positive_float,
vol.Required(CONFIG_MAIN_CLEAR_GAS_PRICE_CAP): bool,
})
)
return await self.__async_setup_main_schema(config, {})
elif CONFIG_TARGET_TYPE in self._entry.data:
config = dict(self._entry.data)
if self._entry.options is not None:
Expand All @@ -297,6 +293,11 @@ async def async_step_user(self, user_input):
if config[CONFIG_MAIN_CLEAR_GAS_PRICE_CAP] == True:
del config[CONFIG_MAIN_GAS_PRICE_CAP]

errors = await async_validate_main_config(config)

if (len(errors) > 0):
return await self.__async_setup_target_rate_schema(config, errors)

return self.async_create_entry(title="", data=config)

return self.async_abort(reason="not_supported")
Expand All @@ -315,7 +316,7 @@ async def async_step_target_rate(self, user_input):
errors = validate_target_rate_config(user_input, account_info, now)

if (len(errors) > 0):
return await self.__async_setup_target_rate_schema(config, errors)
return await self.__async_setup_main_schema(config, errors)

return self.async_create_entry(title="", data=config)

Expand Down
10 changes: 8 additions & 2 deletions custom_components/octopus_energy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@

DOMAIN = "octopus_energy"

CONFIG_KIND = "kind"
CONFIG_MAIN_API_KEY = "Api key"
CONFIG_MAIN_ACCOUNT_ID = "Account Id"
CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION = "supports_live_consumption"
CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES = "live_consumption_refresh_in_minutes"
CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES = "live_electricity_consumption_refresh_in_minutes"
CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES = "live_gas_consumption_refresh_in_minutes"
CONFIG_MAIN_CALORIFIC_VALUE = "calorific_value"
CONFIG_MAIN_ELECTRICITY_PRICE_CAP = "electricity_price_cap"
CONFIG_MAIN_CLEAR_ELECTRICITY_PRICE_CAP = "clear_electricity_price_cap"
CONFIG_MAIN_GAS_PRICE_CAP = "gas_price_cap"
CONFIG_MAIN_CLEAR_GAS_PRICE_CAP = "clear_gas_price_cap"

CONFIG_DEFAULT_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES = 1
CONFIG_DEFAULT_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES = 2

CONFIG_TARGET_NAME = "Name"
CONFIG_TARGET_HOURS = "Hours"
CONFIG_TARGET_TYPE = "Type"
Expand Down Expand Up @@ -64,7 +69,8 @@
vol.Required(CONFIG_MAIN_API_KEY): str,
vol.Required(CONFIG_MAIN_ACCOUNT_ID): str,
vol.Required(CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION): bool,
vol.Required(CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES, default=1): cv.positive_int,
vol.Required(CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES, default=CONFIG_DEFAULT_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES): cv.positive_int,
vol.Required(CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES, default=CONFIG_DEFAULT_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES): cv.positive_int,
vol.Required(CONFIG_MAIN_CALORIFIC_VALUE, default=40.0): cv.positive_float,
vol.Optional(CONFIG_MAIN_ELECTRICITY_PRICE_CAP): cv.positive_float,
vol.Optional(CONFIG_MAIN_GAS_PRICE_CAP): cv.positive_float
Expand Down
17 changes: 10 additions & 7 deletions custom_components/octopus_energy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@

from .utils import (get_active_tariff_code)
from .const import (
CONFIG_DEFAULT_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_DEFAULT_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES,
DOMAIN,

CONFIG_MAIN_API_KEY,
CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION,
CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES,
CONFIG_MAIN_CALORIFIC_VALUE,
CONFIG_MAIN_ELECTRICITY_PRICE_CAP,
CONFIG_MAIN_GAS_PRICE_CAP,
Expand Down Expand Up @@ -123,9 +126,9 @@ async def async_setup_default_sensors(hass: HomeAssistant, entry, async_add_enti
entities.append(OctopusEnergyPreviousAccumulativeElectricityCostOverride(hass, previous_consumption_coordinator, client, electricity_tariff_code, meter, point))

if meter["is_export"] == False and CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION in config and config[CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION] == True:
live_consumption_refresh_in_minutes = 1
if CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES]
live_consumption_refresh_in_minutes = CONFIG_DEFAULT_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES
if CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_ELECTRICITY_CONSUMPTION_REFRESH_IN_MINUTES]

consumption_coordinator = await async_create_current_consumption_coordinator(hass, client, meter["device_id"], True, live_consumption_refresh_in_minutes)
entities.append(OctopusEnergyCurrentElectricityConsumption(hass, consumption_coordinator, meter, point))
Expand Down Expand Up @@ -182,9 +185,9 @@ async def async_setup_default_sensors(hass: HomeAssistant, entry, async_add_enti
entities.append(OctopusEnergyPreviousAccumulativeGasCostOverride(hass, previous_consumption_coordinator, client, gas_tariff_code, meter, point, calorific_value))

if CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION in config and config[CONFIG_MAIN_SUPPORTS_LIVE_CONSUMPTION] == True:
live_consumption_refresh_in_minutes = 1
if CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_CONSUMPTION_REFRESH_IN_MINUTES]
live_consumption_refresh_in_minutes = CONFIG_DEFAULT_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES
if CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES in config:
live_consumption_refresh_in_minutes = config[CONFIG_MAIN_LIVE_GAS_CONSUMPTION_REFRESH_IN_MINUTES]

consumption_coordinator = await async_create_current_consumption_coordinator(hass, client, meter["device_id"], False, live_consumption_refresh_in_minutes)
entities.append(OctopusEnergyCurrentGasConsumption(hass, consumption_coordinator, meter, point))
Expand Down
2 changes: 1 addition & 1 deletion custom_components/octopus_energy/target_rates/repairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from homeassistant.helpers import issue_registry as ir

from ..const import CONFIG_TARGET_NAME, DOMAIN
from .config import validate_target_rate_config
from ..config.target_rates import validate_target_rate_config

def check_for_errors(hass, config, account_info, now: datetime):
if account_info is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
get_target_rate_info
)

from .config import validate_target_rate_config
from ..config.target_rates import validate_target_rate_config
from ..target_rates.repairs import check_for_errors

_LOGGER = logging.getLogger(__name__)
Expand Down
10 changes: 8 additions & 2 deletions custom_components/octopus_energy/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"Account Id": "Your account Id (e.g. A-AAAA1111)",
"supports_live_consumption": "I have a Home Mini - https://octopus.energy/blog/octopus-home-mini/",
"live_consumption_refresh_in_minutes": "Home Mini refresh rate in minutes",
"live_electricity_consumption_refresh_in_minutes": "Home Mini electricity refresh rate in minutes",
"live_gas_consumption_refresh_in_minutes": "Home Mini gas refresh rate in minutes",
"calorific_value": "Gas calorific value. This can be found on your gas statement and can change from time to time.",
"electricity_price_cap": "Optional electricity price cap in pence",
"gas_price_cap": "Optional gas price cap in pence"
Expand All @@ -31,7 +33,8 @@
}
},
"error": {
"account_not_found": "Account information was not found",
"account_not_found": "Invalid API key or account id specified",
"value_greater_than_zero": "Value must be greater or equal to 1",
"invalid_target_hours": "Target hours must be in half hour increments (e.g. 0.5 = 30 minutes; 1 = 60 minutes).",
"invalid_target_name": "Name must only include lower case alpha characters and underscore (e.g. my_target)",
"invalid_target_time": "Must be in the format HH:MM",
Expand All @@ -52,7 +55,8 @@
"data": {
"Api key": "Api key",
"supports_live_consumption": "I have a Home Mini - https://octopus.energy/blog/octopus-home-mini/",
"live_consumption_refresh_in_minutes": "Home Mini refresh rate in minutes",
"live_electricity_consumption_refresh_in_minutes": "Home Mini electricity refresh rate in minutes",
"live_gas_consumption_refresh_in_minutes": "Home Mini gas refresh rate in minutes",
"calorific_value": "Gas calorific value. This can be found on your gas statement and can change from time to time.",
"electricity_price_cap": "Optional electricity price cap in pence",
"clear_electricity_price_cap": "Clear electricity price cap",
Expand All @@ -76,6 +80,8 @@
}
},
"error": {
"account_not_found": "Invalid API key or account id specified",
"value_greater_than_zero": "Value must be greater or equal to 1",
"invalid_target_hours": "Target hours must be in half hour increments (e.g. 0.5 = 30 minutes; 1 = 60 minutes).",
"invalid_target_time": "Must be in the format HH:MM",
"invalid_offset": "Offset must be in the form of HH:MM:SS with an optional negative symbol",
Expand Down

0 comments on commit 6651ebf

Please sign in to comment.