diff --git a/custom_components/emporia_vue/__init__.py b/custom_components/emporia_vue/__init__.py index d57c1e4..71afb4a 100644 --- a/custom_components/emporia_vue/__init__.py +++ b/custom_components/emporia_vue/__init__.py @@ -38,9 +38,7 @@ ENABLE_1D, ENABLE_1M, ENABLE_1MON, - MERGED_ABS, - MERGED_BEHAVIOR, - MERGED_INVERT, + SOLAR_INVERT, VUE_DATA, ) @@ -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.""" @@ -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( @@ -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], }, @@ -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, @@ -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"]: diff --git a/custom_components/emporia_vue/config_flow.py b/custom_components/emporia_vue/config_flow.py index afb3b37..72489da 100644 --- a/custom_components/emporia_vue/config_flow.py +++ b/custom_components/emporia_vue/config_flow.py @@ -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, @@ -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__) @@ -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 { @@ -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], } @@ -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]) @@ -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], } @@ -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), @@ -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), @@ -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: @@ -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.""" diff --git a/custom_components/emporia_vue/const.py b/custom_components/emporia_vue/const.py index 249314f..86205a6 100644 --- a/custom_components/emporia_vue/const.py +++ b/custom_components/emporia_vue/const.py @@ -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" - diff --git a/custom_components/emporia_vue/strings.json b/custom_components/emporia_vue/strings.json index 30fe5fe..8c3baf3 100644 --- a/custom_components/emporia_vue/strings.json +++ b/custom_components/emporia_vue/strings.json @@ -8,7 +8,7 @@ "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": { @@ -16,7 +16,7 @@ "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": { diff --git a/custom_components/emporia_vue/translations/en.json b/custom_components/emporia_vue/translations/en.json index 6936fc3..b8d0dc6 100644 --- a/custom_components/emporia_vue/translations/en.json +++ b/custom_components/emporia_vue/translations/en.json @@ -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": { @@ -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" } } }