mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Improve Smappee integration (#37087)
This commit is contained in:
parent
fe5bf96e5d
commit
680f8f8d5a
@ -5,7 +5,7 @@ from pysmappee import Smappee
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_PLATFORM
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv
|
from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
@ -40,6 +40,15 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
if DOMAIN not in config:
|
if DOMAIN not in config:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# decide platform
|
||||||
|
platform = "PRODUCTION"
|
||||||
|
if config[DOMAIN][CONF_CLIENT_ID] == "homeassistant_f2":
|
||||||
|
platform = "ACCEPTANCE"
|
||||||
|
elif config[DOMAIN][CONF_CLIENT_ID] == "homeassistant_f3":
|
||||||
|
platform = "DEVELOPMENT"
|
||||||
|
|
||||||
|
hass.data[DOMAIN][CONF_PLATFORM] = platform
|
||||||
|
|
||||||
config_flow.SmappeeFlowHandler.async_register_implementation(
|
config_flow.SmappeeFlowHandler.async_register_implementation(
|
||||||
hass,
|
hass,
|
||||||
config_entry_oauth2_flow.LocalOAuth2Implementation(
|
config_entry_oauth2_flow.LocalOAuth2Implementation(
|
||||||
@ -47,8 +56,8 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
config[DOMAIN][CONF_CLIENT_ID],
|
config[DOMAIN][CONF_CLIENT_ID],
|
||||||
config[DOMAIN][CONF_CLIENT_SECRET],
|
config[DOMAIN][CONF_CLIENT_SECRET],
|
||||||
AUTHORIZE_URL,
|
AUTHORIZE_URL[platform],
|
||||||
TOKEN_URL,
|
TOKEN_URL[platform],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,6 +98,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
|
|
||||||
if unload_ok:
|
if unload_ok:
|
||||||
hass.data[DOMAIN].pop(BASE, None)
|
hass.data[DOMAIN].pop(BASE, None)
|
||||||
|
hass.data[DOMAIN].pop(CONF_PLATFORM, None)
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
@ -4,8 +4,11 @@ from asyncio import run_coroutine_threadsafe
|
|||||||
from pysmappee import api
|
from pysmappee import api
|
||||||
|
|
||||||
from homeassistant import config_entries, core
|
from homeassistant import config_entries, core
|
||||||
|
from homeassistant.const import CONF_PLATFORM
|
||||||
from homeassistant.helpers import config_entry_oauth2_flow
|
from homeassistant.helpers import config_entry_oauth2_flow
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
class ConfigEntrySmappeeApi(api.SmappeeApi):
|
class ConfigEntrySmappeeApi(api.SmappeeApi):
|
||||||
"""Provide Smappee authentication tied to an OAuth2 based config entry."""
|
"""Provide Smappee authentication tied to an OAuth2 based config entry."""
|
||||||
@ -22,7 +25,18 @@ class ConfigEntrySmappeeApi(api.SmappeeApi):
|
|||||||
self.session = config_entry_oauth2_flow.OAuth2Session(
|
self.session = config_entry_oauth2_flow.OAuth2Session(
|
||||||
hass, config_entry, implementation
|
hass, config_entry, implementation
|
||||||
)
|
)
|
||||||
super().__init__(None, None, token=self.session.token)
|
|
||||||
|
platform_to_farm = {
|
||||||
|
"PRODUCTION": 1,
|
||||||
|
"ACCEPTANCE": 2,
|
||||||
|
"DEVELOPMENT": 3,
|
||||||
|
}
|
||||||
|
super().__init__(
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
token=self.session.token,
|
||||||
|
farm=platform_to_farm[hass.data[DOMAIN][CONF_PLATFORM]],
|
||||||
|
)
|
||||||
|
|
||||||
def refresh_tokens(self) -> dict:
|
def refresh_tokens(self) -> dict:
|
||||||
"""Refresh and return new Smappee tokens using Home Assistant OAuth2 session."""
|
"""Refresh and return new Smappee tokens using Home Assistant OAuth2 session."""
|
||||||
|
@ -139,12 +139,6 @@ class SmappeeAppliance(BinarySensorEntity):
|
|||||||
}
|
}
|
||||||
return icon_mapping.get(self._appliance_type)
|
return icon_mapping.get(self._appliance_type)
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
|
||||||
# Only lights can be mapped onto the generic list of binary sensors
|
|
||||||
return "light" if self._appliance_type == "Lights" else "power"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self,):
|
def unique_id(self,):
|
||||||
"""Return the unique ID for this binary sensor."""
|
"""Return the unique ID for this binary sensor."""
|
||||||
|
@ -21,10 +21,3 @@ class SmappeeFlowHandler(
|
|||||||
def logger(self) -> logging.Logger:
|
def logger(self) -> logging.Logger:
|
||||||
"""Return logger."""
|
"""Return logger."""
|
||||||
return logging.getLogger(__name__)
|
return logging.getLogger(__name__)
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
|
||||||
"""Handle a flow start."""
|
|
||||||
if self.hass.config_entries.async_entries(DOMAIN):
|
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
|
||||||
|
|
||||||
return await super().async_step_user(user_input)
|
|
||||||
|
@ -11,5 +11,13 @@ SMAPPEE_PLATFORMS = ["binary_sensor", "sensor", "switch"]
|
|||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
|
||||||
|
|
||||||
AUTHORIZE_URL = "https://app1pub.smappee.net/dev/v1/oauth2/authorize"
|
AUTHORIZE_URL = {
|
||||||
TOKEN_URL = "https://app1pub.smappee.net/dev/v3/oauth2/token"
|
"PRODUCTION": "https://app1pub.smappee.net/dev/v1/oauth2/authorize",
|
||||||
|
"ACCEPTANCE": "https://farm2pub.smappee.net/dev/v1/oauth2/authorize",
|
||||||
|
"DEVELOPMENT": "https://farm3pub.smappee.net/dev/v1/oauth2/authorize",
|
||||||
|
}
|
||||||
|
TOKEN_URL = {
|
||||||
|
"PRODUCTION": "https://app1pub.smappee.net/dev/v3/oauth2/token",
|
||||||
|
"ACCEPTANCE": "https://farm2pub.smappee.net/dev/v3/oauth2/token",
|
||||||
|
"DEVELOPMENT": "https://farm3pub.smappee.net/dev/v3/oauth2/token",
|
||||||
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"authorize_url_timeout": "Timeout generating authorize url.",
|
"authorize_url_timeout": "Timeout generating authorize url.",
|
||||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
|
||||||
"missing_configuration": "The component is not configured. Please follow the documentation."
|
"missing_configuration": "The component is not configured. Please follow the documentation."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,15 @@
|
|||||||
"""Test the Smappee config flow."""
|
"""Test the Smappee config flow."""
|
||||||
from homeassistant import config_entries, data_entry_flow, setup
|
from homeassistant import config_entries, setup
|
||||||
from homeassistant.components.smappee.const import AUTHORIZE_URL, DOMAIN, TOKEN_URL
|
from homeassistant.components.smappee.const import AUTHORIZE_URL, DOMAIN, TOKEN_URL
|
||||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||||
from homeassistant.helpers import config_entry_oauth2_flow
|
from homeassistant.helpers import config_entry_oauth2_flow
|
||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
from tests.common import MockConfigEntry
|
|
||||||
|
|
||||||
CLIENT_ID = "1234"
|
CLIENT_ID = "1234"
|
||||||
CLIENT_SECRET = "5678"
|
CLIENT_SECRET = "5678"
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_if_existing_entry(hass):
|
|
||||||
"""Check flow abort when an entry already exist."""
|
|
||||||
MockConfigEntry(domain=DOMAIN).add_to_hass(hass)
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
||||||
assert result["reason"] == "single_instance_allowed"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_full_flow(hass, aiohttp_client, aioclient_mock):
|
async def test_full_flow(hass, aiohttp_client, aioclient_mock):
|
||||||
"""Check full flow."""
|
"""Check full flow."""
|
||||||
assert await setup.async_setup_component(
|
assert await setup.async_setup_component(
|
||||||
@ -39,7 +27,7 @@ async def test_full_flow(hass, aiohttp_client, aioclient_mock):
|
|||||||
state = config_entry_oauth2_flow._encode_jwt(hass, {"flow_id": result["flow_id"]})
|
state = config_entry_oauth2_flow._encode_jwt(hass, {"flow_id": result["flow_id"]})
|
||||||
|
|
||||||
assert result["url"] == (
|
assert result["url"] == (
|
||||||
f"{AUTHORIZE_URL}?response_type=code&client_id={CLIENT_ID}"
|
f"{AUTHORIZE_URL['PRODUCTION']}?response_type=code&client_id={CLIENT_ID}"
|
||||||
"&redirect_uri=https://example.com/auth/external/callback"
|
"&redirect_uri=https://example.com/auth/external/callback"
|
||||||
f"&state={state}"
|
f"&state={state}"
|
||||||
)
|
)
|
||||||
@ -50,7 +38,7 @@ async def test_full_flow(hass, aiohttp_client, aioclient_mock):
|
|||||||
assert resp.headers["content-type"] == "text/html; charset=utf-8"
|
assert resp.headers["content-type"] == "text/html; charset=utf-8"
|
||||||
|
|
||||||
aioclient_mock.post(
|
aioclient_mock.post(
|
||||||
TOKEN_URL,
|
TOKEN_URL["PRODUCTION"],
|
||||||
json={
|
json={
|
||||||
"refresh_token": "mock-refresh-token",
|
"refresh_token": "mock-refresh-token",
|
||||||
"access_token": "mock-access-token",
|
"access_token": "mock-access-token",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user