Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OSO Energy integration #70365

Merged
merged 37 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2eb0db0
Add OSO Energy integration
osohotwateriot Apr 21, 2022
f442086
Add min/max for v40 level and bump pyosoenergyapi to 1.0.2
osohotwateriot May 11, 2022
9a3e0e6
OSO Energy address review comments
osohotwateriot Jul 14, 2022
7af68d2
Bump pyosoenergyapi to 1.0.3 and remove scan interval
osohotwateriot Aug 4, 2022
1d5ba58
Remove unnecessary code
osohotwateriot Sep 28, 2022
3c4843f
Merge branch 'dev' into osoenergy
osohotwateriot Dec 6, 2022
3a24b08
Merge branch 'dev' into osoenergy
osohotwateriot Dec 8, 2022
9befade
Merge branch 'dev' into osoenergy
osohotwateriot Dec 14, 2022
bda3176
Merge branch 'dev' into osoenergy
osohotwateriot Jan 19, 2023
5192d2d
Merge branch 'dev' into osoenergy
Danielhiversen Apr 28, 2023
ac51123
Update homeassistant/components/osoenergy/__init__.py
osohotwateriot May 2, 2023
88e7d5d
Merge branch 'dev' into osoenergy
osohotwateriot May 2, 2023
d188f7e
Merge branch 'dev' into osoenergy
osohotwateriot May 2, 2023
ee7828d
Fixes to latest version
osohotwateriot May 2, 2023
3acfcea
Add support to set temperature
osohotwateriot May 3, 2023
50ace86
Update homeassistant/components/osoenergy/config_flow.py
osohotwateriot May 4, 2023
ee3d69e
Fixes after review
osohotwateriot May 4, 2023
c53d8be
Merge branch 'dev' into osoenergy
osohotwateriot May 9, 2023
5c6c760
Remove unused code
osohotwateriot May 9, 2023
d0245e4
Merge branch 'dev' into osoenergy
edenhaus Aug 7, 2023
12217db
Merge branch 'dev' into osoenergy
osohotwateriot Aug 14, 2023
bf233f8
Add support for translations and modify services
osohotwateriot Aug 15, 2023
15bf6e7
Apply suggestions from code review
osohotwateriot Aug 18, 2023
ca207e3
Refactor services and constants according to the PR suggestions
osohotwateriot Aug 18, 2023
52e998b
Remove unnecessary code
osohotwateriot Aug 31, 2023
cd67848
Merge branch 'dev' into osoenergy
osohotwateriot Sep 12, 2023
0543afc
Remove unused import in constants
osohotwateriot Sep 12, 2023
bae6dbb
Merge branch 'dev' into osoenergy
osohotwateriot Sep 25, 2023
d1b79af
Refactoring and support for multiple instances
osohotwateriot Sep 25, 2023
8840296
Merge remote-tracking branch 'refs/remotes/origin/osoenergy' into oso…
osohotwateriot Sep 25, 2023
6d4edff
Apply suggestions from code review
osohotwateriot Oct 2, 2023
71e3711
Refactor code and apply review suggestions
osohotwateriot Oct 2, 2023
72412d8
Bump pyosoenergyapi to 1.0.5
osohotwateriot Oct 4, 2023
d20c24a
Merge branch 'dev' into osoenergy
osohotwateriot Oct 6, 2023
dc93b29
Remove services to reduce initial PR
osohotwateriot Oct 6, 2023
1ac0b29
Merge branch 'dev' into osoenergy
osohotwateriot Oct 31, 2023
a1bec70
Remove extra state attributes and make OSO Entity generic
osohotwateriot Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,9 @@ omit =
homeassistant/components/opple/light.py
homeassistant/components/oru/*
homeassistant/components/orvibo/switch.py
homeassistant/components/osoenergy/__init__.py
homeassistant/components/osoenergy/const.py
homeassistant/components/osoenergy/water_heater.py
homeassistant/components/osramlightify/light.py
homeassistant/components/otp/sensor.py
homeassistant/components/overkiz/__init__.py
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/oralb/ @bdraco @Lash-L
/tests/components/oralb/ @bdraco @Lash-L
/homeassistant/components/oru/ @bvlaicu
/homeassistant/components/osoenergy/ @osohotwateriot
/tests/components/osoenergy/ @osohotwateriot
/homeassistant/components/otbr/ @home-assistant/core
/tests/components/otbr/ @home-assistant/core
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev
Expand Down
72 changes: 72 additions & 0 deletions homeassistant/components/osoenergy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Support for the OSO Energy devices and services."""
from typing import Any

from aiohttp.web_exceptions import HTTPException
from apyosoenergyapi import OSOEnergy
from apyosoenergyapi.helper.osoenergy_exceptions import OSOEnergyReauthRequired

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.entity import Entity

from .const import DOMAIN

PLATFORMS = [
Platform.WATER_HEATER,
]
PLATFORM_LOOKUP = {
Platform.WATER_HEATER: "water_heater",
}


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up OSO Energy from a config entry."""
subscription_key = entry.data[CONF_API_KEY]
websession = aiohttp_client.async_get_clientsession(hass)
osoenergy = OSOEnergy(subscription_key, websession)

osoenergy_config = dict(entry.data)

hass.data.setdefault(DOMAIN, {})

try:
devices: Any = await osoenergy.session.start_session(osoenergy_config)
except HTTPException as error:
raise ConfigEntryNotReady() from error
except OSOEnergyReauthRequired as err:
raise ConfigEntryAuthFailed from err

osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
hass.data[DOMAIN][entry.entry_id] = osoenergy

platforms = set()
for ha_type, oso_type in PLATFORM_LOOKUP.items():
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
device_list = devices.get(oso_type, [])
if device_list:
platforms.add(ha_type)
if platforms:
await hass.config_entries.async_forward_entry_setups(entry, platforms)
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will error if the platform wasn't setup.

if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


class OSOEnergyEntity(Entity):
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
"""Initiate OSO Energy Base Class."""

_attr_has_entity_name = True

def __init__(self, osoenergy, osoenergy_device) -> None:
"""Initialize the instance."""
self.osoenergy = osoenergy
self.device = osoenergy_device
self._attr_unique_id = osoenergy_device["device_id"]
75 changes: 75 additions & 0 deletions homeassistant/components/osoenergy/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Config Flow for OSO Energy."""
from collections.abc import Mapping
import logging
from typing import Any

from apyosoenergyapi import OSOEnergy
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import aiohttp_client

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)
_SCHEMA_STEP_USER = vol.Schema({vol.Required(CONF_API_KEY): str})

osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved

class OSOEnergyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a OSO Energy config flow."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is legacy. It's set in the manifest.json nowadays.


def __init__(self) -> None:
"""Initialize."""
self.entry: ConfigEntry | None = None

async def async_step_user(self, user_input=None) -> FlowResult:
"""Handle a flow initialized by the user."""
errors = {}

if user_input is not None:
# Verify Subscription key
if user_email := await self.get_user_email(user_input[CONF_API_KEY]):
await self.async_set_unique_id(user_email)

if (
self.context["source"] == config_entries.SOURCE_REAUTH
and self.entry
):
self.hass.config_entries.async_update_entry(
self.entry, title=user_email, data=user_input
)
await self.hass.config_entries.async_reload(self.entry.entry_id)
return self.async_abort(reason="reauth_successful")

self._abort_if_unique_id_configured()
return self.async_create_entry(title=user_email, data=user_input)
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved

errors["base"] = "invalid_auth"

return self.async_show_form(
step_id="user",
data_schema=_SCHEMA_STEP_USER,
errors=errors,
)

async def get_user_email(self, subscription_key: str) -> str | None:
"""Return true if credentials is valid."""
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
try:
websession = aiohttp_client.async_get_clientsession(self.hass)
client = OSOEnergy(subscription_key, websession)
return await client.get_user_email()
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unknown error occurred")
return None

async def async_step_reauth(self, user_input: Mapping[str, Any]) -> FlowResult:
"""Re Authenticate a user."""
self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
data = {CONF_API_KEY: user_input[CONF_API_KEY]}
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
return await self.async_step_user(data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't pass the old config entry data as input to the user step. Then we'll try to authenticate again with the old data when we know it's already invalid.

3 changes: 3 additions & 0 deletions homeassistant/components/osoenergy/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Constants for OSO Energy."""

DOMAIN = "osoenergy"
9 changes: 9 additions & 0 deletions homeassistant/components/osoenergy/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"domain": "osoenergy",
"name": "OSO Energy",
"codeowners": ["@osohotwateriot"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/osoenergy",
"iot_class": "cloud_polling",
"requirements": ["pyosoenergyapi==1.0.4"]
osohotwateriot marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading