Skip to content

Commit

Permalink
Swap from checking for merged, to checking for solar id and optional …
Browse files Browse the repository at this point in the history
…inverting
  • Loading branch information
magico13 committed Feb 2, 2025
1 parent 5ba3493 commit 3340189
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 58 deletions.
41 changes: 23 additions & 18 deletions custom_components/emporia_vue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@
ENABLE_1D,
ENABLE_1M,
ENABLE_1MON,
MERGED_ABS,
MERGED_BEHAVIOR,
MERGED_INVERT,
SOLAR_INVERT,
VUE_DATA,
)

Expand All @@ -54,7 +52,8 @@
LAST_MINUTE_DATA: dict[str, Any] = {}
LAST_DAY_DATA: dict[str, Any] = {}
LAST_DAY_UPDATE: datetime | None = None
CONF_MERGED_BEHAVIOR: str = MERGED_INVERT
INVERT_SOLAR: bool = True


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Emporia Vue component."""
Expand All @@ -63,9 +62,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if not conf:
return True

global CONF_MERGED_BEHAVIOR
if MERGED_BEHAVIOR in conf:
CONF_MERGED_BEHAVIOR = conf[MERGED_BEHAVIOR]
global INVERT_SOLAR
if SOLAR_INVERT in conf:
INVERT_SOLAR = conf[SOLAR_INVERT]

hass.async_create_task(
hass.config_entries.flow.async_init(
Expand All @@ -77,7 +76,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
ENABLE_1M: conf[ENABLE_1M],
ENABLE_1D: conf[ENABLE_1D],
ENABLE_1MON: conf[ENABLE_1MON],
MERGED_BEHAVIOR: conf[MERGED_BEHAVIOR],
INVERT_SOLAR: conf[SOLAR_INVERT],
CUSTOMER_GID: conf[CUSTOMER_GID],
CONFIG_TITLE: conf[CONFIG_TITLE],
},
Expand Down Expand Up @@ -491,8 +490,10 @@ async def parse_flattened_usage_data(
)

bidirectional = "bidirectional" in info_channel.type.lower()
merged = "merged" in info_channel.type.lower()
fixed_usage = fix_usage_sign(channel_num, fixed_usage, bidirectional, merged, CONF_MERGED_BEHAVIOR)
is_solar = info_channel.channel_type_gid == 13
fixed_usage = fix_usage_sign(
channel_num, fixed_usage, bidirectional, is_solar, INVERT_SOLAR
)

data[identifier] = {
"device_gid": gid,
Expand Down Expand Up @@ -572,19 +573,23 @@ def make_channel_id(channel: VueDeviceChannel, scale: str) -> str:
return f"{channel.device_gid}-{channel.channel_num}-{scale}"


def fix_usage_sign(channel_num: str, usage: float, bidirectional: bool, merged:bool, merged_behavior: str) -> float:
def fix_usage_sign(
channel_num: str,
usage: float,
bidirectional: bool,
is_solar: bool,
invert_solar: str,
) -> float:
"""If the channel is not '1,2,3' or 'Balance' we need it to be positive.
Merged circuits are a special case, as they can be inverted, abs'd, or left alone to support different solar setups.
Solar circuits are up to the user to decide. Positive is recommended for the energy dashboard.
(see https://github.com/magico13/ha-emporia-vue/issues/57)
"""
if merged:
# for merged channels, we need to either invert or abs the value as requested
if merged_behavior == MERGED_ABS:
return abs(usage)
if merged_behavior == MERGED_INVERT:
return -usage
if is_solar:
# Energy dashboard wants solar to be positive, Emporia usually provides negative
if usage and invert_solar:
return -1 * usage
return usage

if usage and not bidirectional and channel_num not in ["1,2,3", "Balance"]:
Expand Down
61 changes: 32 additions & 29 deletions custom_components/emporia_vue/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from homeassistant import config_entries, exceptions
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import selector

from .const import (
CONFIG_TITLE,
Expand All @@ -20,10 +19,7 @@
ENABLE_1D,
ENABLE_1M,
ENABLE_1MON,
MERGED_ABS,
MERGED_BEHAVIOR,
MERGED_INVERT,
MERGED_NONE,
SOLAR_INVERT,
)

_LOGGER: logging.Logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -66,8 +62,8 @@ async def validate_input(data: dict | Mapping[str, Any]) -> dict[str, Any]:

new_data = dict(data)

if MERGED_BEHAVIOR not in new_data:
new_data[MERGED_BEHAVIOR] = MERGED_INVERT
if SOLAR_INVERT not in new_data:
new_data[SOLAR_INVERT] = True

# Return info that you want to store in the config entry.
return {
Expand All @@ -76,7 +72,7 @@ async def validate_input(data: dict | Mapping[str, Any]) -> dict[str, Any]:
ENABLE_1M: new_data[ENABLE_1M],
ENABLE_1D: new_data[ENABLE_1D],
ENABLE_1MON: new_data[ENABLE_1MON],
MERGED_BEHAVIOR: new_data[MERGED_BEHAVIOR],
SOLAR_INVERT: new_data[SOLAR_INVERT],
CONF_EMAIL: new_data[CONF_EMAIL],
CONF_PASSWORD: new_data[CONF_PASSWORD],
}
Expand Down Expand Up @@ -115,27 +111,29 @@ async def async_step_user(self, user_input=None) -> config_entries.ConfigFlowRes
vol.Optional(ENABLE_1M, default=True): cv.boolean,
vol.Optional(ENABLE_1D, default=True): cv.boolean,
vol.Optional(ENABLE_1MON, default=True): cv.boolean,
vol.Optional(SOLAR_INVERT, default=True): cv.boolean,
}

config_schema[vol.Required(MERGED_BEHAVIOR)] = selector({
"select": {
"options": [MERGED_INVERT, MERGED_ABS, MERGED_NONE],
}
})

return self.async_show_form(
step_id="user", data_schema=vol.Schema(config_schema), errors=errors
)

async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None) -> config_entries.ConfigFlowResult:
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> config_entries.ConfigFlowResult:
"""Handle the reconfiguration step."""
current_config = self._get_reconfigure_entry()
if user_input is not None:
_LOGGER.warning("User input on reconfigure was the following: %s", user_input)
_LOGGER.warning(
"User input on reconfigure was the following: %s", user_input
)
_LOGGER.warning("Current config is: %s", current_config.data)
info = current_config.data
# if gid is not in current config, reauth and get gid again
if CUSTOMER_GID not in current_config.data or not current_config.data[CUSTOMER_GID]:
if (
CUSTOMER_GID not in current_config.data
or not current_config.data[CUSTOMER_GID]
):
info = await validate_input(current_config.data)

await self.async_set_unique_id(info[CUSTOMER_GID])
Expand All @@ -144,7 +142,7 @@ async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None)
ENABLE_1M: user_input[ENABLE_1M],
ENABLE_1D: user_input[ENABLE_1D],
ENABLE_1MON: user_input[ENABLE_1MON],
MERGED_BEHAVIOR: user_input[MERGED_BEHAVIOR],
SOLAR_INVERT: user_input[SOLAR_INVERT],
CUSTOMER_GID: info[CUSTOMER_GID],
CONFIG_TITLE: info[CONFIG_TITLE],
}
Expand All @@ -153,7 +151,7 @@ async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None)
data_updates=data,
)

data_schema : dict[vol.Optional | vol.Required, Any] = {
data_schema: dict[vol.Optional | vol.Required, Any] = {
vol.Optional(
ENABLE_1M,
default=current_config.data.get(ENABLE_1M, True),
Expand All @@ -166,14 +164,12 @@ async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None)
ENABLE_1MON,
default=current_config.data.get(ENABLE_1MON, True),
): cv.boolean,
vol.Optional(
SOLAR_INVERT,
default=current_config.data.get(SOLAR_INVERT, True),
): cv.boolean,
}

data_schema[vol.Required(MERGED_BEHAVIOR)] = selector({
"select": {
"options": [MERGED_INVERT, MERGED_ABS, MERGED_NONE],
}
})

return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema(data_schema),
Expand All @@ -195,9 +191,12 @@ async def async_step_reauth_confirm(
gid = 0
try:
hub = VueHub()
if not await hub.authenticate(
user_input[CONF_EMAIL], user_input[CONF_PASSWORD]
) or not hub.vue.customer:
if (
not await hub.authenticate(
user_input[CONF_EMAIL], user_input[CONF_PASSWORD]
)
or not hub.vue.customer
):
raise InvalidAuth
gid = hub.vue.customer.customer_gid
except InvalidAuth:
Expand All @@ -216,15 +215,19 @@ async def async_step_reauth_confirm(
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_EMAIL, default=existing_entry.data[CONF_EMAIL]): cv.string,
vol.Required(
CONF_EMAIL, default=existing_entry.data[CONF_EMAIL]
): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
}
),
errors=errors,
)


class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""


class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth."""
7 changes: 1 addition & 6 deletions custom_components/emporia_vue/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@
ENABLE_1M = "enable_1m"
ENABLE_1D = "enable_1d"
ENABLE_1MON = "enable_1mon"
MERGED_BEHAVIOR = "merged_behavior"
SOLAR_INVERT = "solar_invert"
CUSTOMER_GID = "customer_gid"
CONFIG_TITLE = "title"

MERGED_INVERT = "Invert"
MERGED_ABS = "Absolute Value"
MERGED_NONE = "None"

4 changes: 2 additions & 2 deletions custom_components/emporia_vue/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
"enable_1m": "Power Minute Average Sensor",
"enable_1d": "Energy Today Sensor",
"enable_1mon": "Energy This Month Sensor",
"merged_behavior": "Merged Circuit Operation"
"solar_invert": "Invert Values for Solar Circuits"
}
},
"reconfigure": {
"data": {
"enable_1m": "[%key:component::emporia_vue::config::step::user::data::enable_1m%]",
"enable_1d": "[%key:component::emporia_vue::config::step::user::data::enable_1d%]",
"enable_1mon": "[%key:component::emporia_vue::config::step::user::data::enable_1mon%]",
"merged_behavior": "[%key:component::emporia_vue::config::step::user::data::merged_behavior%]"
"solar_invert": "[%key:component::emporia_vue::config::step::user::data::solar_invert%]"
}
},
"reauth_confirm": {
Expand Down
6 changes: 3 additions & 3 deletions custom_components/emporia_vue/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"enable_1d": "Energy Today Sensor",
"enable_1m": "Power Minute Average Sensor",
"enable_1mon": "Energy This Month Sensor",
"merged_behavior": "Merged Circuit Operation"
"solar_invert": "Invert Values for Solar Circuits"
}
},
"user": {
Expand All @@ -33,8 +33,8 @@
"enable_1d": "Energy Today Sensor",
"enable_1m": "Power Minute Average Sensor",
"enable_1mon": "Energy This Month Sensor",
"merged_behavior": "Merged Circuit Operation",
"password": "Password"
"password": "Password",
"solar_invert": "Invert Values for Solar Circuits"
}
}
}
Expand Down

0 comments on commit 3340189

Please sign in to comment.