Enable SolarEdge config entries (#26282)
* Initial commit for the solaredge configflow * rerun the hassfest script * Adding testcases * Rerun hassfest, problem with black? * Requirements for the tests * Remove CONF_MONITORED_CONDITIONS from configuration.yaml * Remove the options flow strings * Resolve some comments * Comments * More comments * Move the config from the sensor platform to the component itself * More comments * More comments * Added solaredge __init__ * Added more test to increase coverage
This commit is contained in:
parent
0983367abe
commit
28beebac61
13 changed files with 412 additions and 89 deletions
|
@ -576,6 +576,7 @@ omit =
|
||||||
homeassistant/components/snmp/*
|
homeassistant/components/snmp/*
|
||||||
homeassistant/components/sochain/sensor.py
|
homeassistant/components/sochain/sensor.py
|
||||||
homeassistant/components/socialblade/sensor.py
|
homeassistant/components/socialblade/sensor.py
|
||||||
|
homeassistant/components/solaredge/__init__.py
|
||||||
homeassistant/components/solaredge/sensor.py
|
homeassistant/components/solaredge/sensor.py
|
||||||
homeassistant/components/solaredge_local/sensor.py
|
homeassistant/components/solaredge_local/sensor.py
|
||||||
homeassistant/components/solax/sensor.py
|
homeassistant/components/solax/sensor.py
|
||||||
|
|
21
homeassistant/components/solaredge/.translations/en.json
Normal file
21
homeassistant/components/solaredge/.translations/en.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "SolarEdge",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Define the API parameters for this installation",
|
||||||
|
"data": {
|
||||||
|
"name": "The name of this installation",
|
||||||
|
"site_id": "The SolarEdge site-id",
|
||||||
|
"api_key": "The API key for this site"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"site_exists": "This site_id is already configured"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"site_exists": "This site_id is already configured"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,43 @@
|
||||||
"""The solaredge component."""
|
"""The solaredge component."""
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
|
from homeassistant.const import CONF_API_KEY, CONF_NAME
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
|
from .const import DEFAULT_NAME, DOMAIN, CONF_SITE_ID
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
DOMAIN: vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
|
vol.Required(CONF_API_KEY): cv.string,
|
||||||
|
vol.Required(CONF_SITE_ID): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass, config):
|
||||||
|
"""Platform setup, do nothing."""
|
||||||
|
if DOMAIN not in config:
|
||||||
|
return True
|
||||||
|
|
||||||
|
hass.async_create_task(
|
||||||
|
hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_IMPORT}, data=dict(config[DOMAIN])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||||
|
"""Load the saved entities."""
|
||||||
|
hass.async_create_task(
|
||||||
|
hass.config_entries.async_forward_entry_setup(entry, "sensor")
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
98
homeassistant/components/solaredge/config_flow.py
Normal file
98
homeassistant/components/solaredge/config_flow.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
"""Config flow for the SolarEdge platform."""
|
||||||
|
import solaredge
|
||||||
|
import voluptuous as vol
|
||||||
|
from requests.exceptions import HTTPError, ConnectTimeout
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.const import CONF_API_KEY, CONF_NAME
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
|
from .const import DOMAIN, DEFAULT_NAME, CONF_SITE_ID
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def solaredge_entries(hass: HomeAssistant):
|
||||||
|
"""Return the site_ids for the domain."""
|
||||||
|
return set(
|
||||||
|
(entry.data[CONF_SITE_ID])
|
||||||
|
for entry in hass.config_entries.async_entries(DOMAIN)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SolarEdgeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Initialize the config flow."""
|
||||||
|
self._errors = {}
|
||||||
|
|
||||||
|
def _site_in_configuration_exists(self, site_id) -> bool:
|
||||||
|
"""Return True if site_id exists in configuration."""
|
||||||
|
if site_id in solaredge_entries(self.hass):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _check_site(self, site_id, api_key) -> bool:
|
||||||
|
"""Check if we can connect to the soleredge api service."""
|
||||||
|
api = solaredge.Solaredge(api_key)
|
||||||
|
try:
|
||||||
|
response = api.get_details(site_id)
|
||||||
|
except (ConnectTimeout, HTTPError):
|
||||||
|
self._errors[CONF_SITE_ID] = "could_not_connect"
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
if response["details"]["status"].lower() != "active":
|
||||||
|
self._errors[CONF_SITE_ID] = "site_not_active"
|
||||||
|
return False
|
||||||
|
except KeyError:
|
||||||
|
self._errors[CONF_SITE_ID] = "api_failure"
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Step when user intializes a integration."""
|
||||||
|
self._errors = {}
|
||||||
|
if user_input is not None:
|
||||||
|
name = slugify(user_input.get(CONF_NAME, DEFAULT_NAME))
|
||||||
|
if self._site_in_configuration_exists(user_input[CONF_SITE_ID]):
|
||||||
|
self._errors[CONF_SITE_ID] = "site_exists"
|
||||||
|
else:
|
||||||
|
site = user_input[CONF_SITE_ID]
|
||||||
|
api = user_input[CONF_API_KEY]
|
||||||
|
can_connect = await self.hass.async_add_executor_job(
|
||||||
|
self._check_site, site, api
|
||||||
|
)
|
||||||
|
if can_connect:
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=name, data={CONF_SITE_ID: site, CONF_API_KEY: api}
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
user_input = {}
|
||||||
|
user_input[CONF_NAME] = DEFAULT_NAME
|
||||||
|
user_input[CONF_SITE_ID] = ""
|
||||||
|
user_input[CONF_API_KEY] = ""
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(
|
||||||
|
CONF_NAME, default=user_input.get(CONF_NAME, DEFAULT_NAME)
|
||||||
|
): str,
|
||||||
|
vol.Required(CONF_SITE_ID, default=user_input[CONF_SITE_ID]): str,
|
||||||
|
vol.Required(CONF_API_KEY, default=user_input[CONF_API_KEY]): str,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
errors=self._errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_import(self, user_input=None):
|
||||||
|
"""Import a config entry."""
|
||||||
|
if self._site_in_configuration_exists(user_input[CONF_SITE_ID]):
|
||||||
|
return self.async_abort(reason="site_exists")
|
||||||
|
return await self.async_step_user(user_input)
|
68
homeassistant/components/solaredge/const.py
Normal file
68
homeassistant/components/solaredge/const.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
"""Constants for the SolarEdge Monitoring API."""
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from homeassistant.const import POWER_WATT, ENERGY_WATT_HOUR
|
||||||
|
|
||||||
|
DOMAIN = "solaredge"
|
||||||
|
|
||||||
|
# Config for solaredge monitoring api requests.
|
||||||
|
CONF_SITE_ID = "site_id"
|
||||||
|
|
||||||
|
DEFAULT_NAME = "SolarEdge"
|
||||||
|
|
||||||
|
OVERVIEW_UPDATE_DELAY = timedelta(minutes=10)
|
||||||
|
DETAILS_UPDATE_DELAY = timedelta(hours=12)
|
||||||
|
INVENTORY_UPDATE_DELAY = timedelta(hours=12)
|
||||||
|
POWER_FLOW_UPDATE_DELAY = timedelta(minutes=10)
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(minutes=10)
|
||||||
|
|
||||||
|
# Supported overview sensor types:
|
||||||
|
# Key: ['json_key', 'name', unit, icon, default]
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
"lifetime_energy": [
|
||||||
|
"lifeTimeData",
|
||||||
|
"Lifetime energy",
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
"mdi:solar-power",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
"energy_this_year": [
|
||||||
|
"lastYearData",
|
||||||
|
"Energy this year",
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
"mdi:solar-power",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
"energy_this_month": [
|
||||||
|
"lastMonthData",
|
||||||
|
"Energy this month",
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
"mdi:solar-power",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
"energy_today": [
|
||||||
|
"lastDayData",
|
||||||
|
"Energy today",
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
"mdi:solar-power",
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
"current_power": [
|
||||||
|
"currentPower",
|
||||||
|
"Current Power",
|
||||||
|
POWER_WATT,
|
||||||
|
"mdi:solar-power",
|
||||||
|
True,
|
||||||
|
],
|
||||||
|
"site_details": [None, "Site details", None, None, False],
|
||||||
|
"meters": ["meters", "Meters", None, None, False],
|
||||||
|
"sensors": ["sensors", "Sensors", None, None, False],
|
||||||
|
"gateways": ["gateways", "Gateways", None, None, False],
|
||||||
|
"batteries": ["batteries", "Batteries", None, None, False],
|
||||||
|
"inverters": ["inverters", "Inverters", None, None, False],
|
||||||
|
"power_consumption": ["LOAD", "Power Consumption", None, "mdi:flash", False],
|
||||||
|
"solar_power": ["PV", "Solar Power", None, "mdi:solar-power", False],
|
||||||
|
"grid_power": ["GRID", "Grid Power", None, "mdi:power-plug", False],
|
||||||
|
"storage_power": ["STORAGE", "Storage Power", None, "mdi:car-battery", False],
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
"solaredge==0.0.2",
|
"solaredge==0.0.2",
|
||||||
"stringcase==1.2.0"
|
"stringcase==1.2.0"
|
||||||
],
|
],
|
||||||
|
"config_flow": true,
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": []
|
"codeowners": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +1,39 @@
|
||||||
"""Support for SolarEdge Monitoring API."""
|
"""Support for SolarEdge Monitoring API."""
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
|
import solaredge
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from requests.exceptions import HTTPError, ConnectTimeout
|
from requests.exceptions import HTTPError, ConnectTimeout
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.const import CONF_API_KEY
|
||||||
from homeassistant.const import (
|
|
||||||
CONF_API_KEY,
|
|
||||||
CONF_MONITORED_CONDITIONS,
|
|
||||||
CONF_NAME,
|
|
||||||
POWER_WATT,
|
|
||||||
ENERGY_WATT_HOUR,
|
|
||||||
)
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
# Config for solaredge monitoring api requests.
|
from .const import (
|
||||||
CONF_SITE_ID = "site_id"
|
CONF_SITE_ID,
|
||||||
|
OVERVIEW_UPDATE_DELAY,
|
||||||
OVERVIEW_UPDATE_DELAY = timedelta(minutes=10)
|
DETAILS_UPDATE_DELAY,
|
||||||
DETAILS_UPDATE_DELAY = timedelta(hours=12)
|
INVENTORY_UPDATE_DELAY,
|
||||||
INVENTORY_UPDATE_DELAY = timedelta(hours=12)
|
POWER_FLOW_UPDATE_DELAY,
|
||||||
POWER_FLOW_UPDATE_DELAY = timedelta(minutes=10)
|
SENSOR_TYPES,
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(minutes=10)
|
|
||||||
|
|
||||||
# Supported overview sensor types:
|
|
||||||
# Key: ['json_key', 'name', unit, icon]
|
|
||||||
SENSOR_TYPES = {
|
|
||||||
"lifetime_energy": [
|
|
||||||
"lifeTimeData",
|
|
||||||
"Lifetime energy",
|
|
||||||
ENERGY_WATT_HOUR,
|
|
||||||
"mdi:solar-power",
|
|
||||||
],
|
|
||||||
"energy_this_year": [
|
|
||||||
"lastYearData",
|
|
||||||
"Energy this year",
|
|
||||||
ENERGY_WATT_HOUR,
|
|
||||||
"mdi:solar-power",
|
|
||||||
],
|
|
||||||
"energy_this_month": [
|
|
||||||
"lastMonthData",
|
|
||||||
"Energy this month",
|
|
||||||
ENERGY_WATT_HOUR,
|
|
||||||
"mdi:solar-power",
|
|
||||||
],
|
|
||||||
"energy_today": [
|
|
||||||
"lastDayData",
|
|
||||||
"Energy today",
|
|
||||||
ENERGY_WATT_HOUR,
|
|
||||||
"mdi:solar-power",
|
|
||||||
],
|
|
||||||
"current_power": ["currentPower", "Current Power", POWER_WATT, "mdi:solar-power"],
|
|
||||||
"site_details": [None, "Site details", None, None],
|
|
||||||
"meters": ["meters", "Meters", None, None],
|
|
||||||
"sensors": ["sensors", "Sensors", None, None],
|
|
||||||
"gateways": ["gateways", "Gateways", None, None],
|
|
||||||
"batteries": ["batteries", "Batteries", None, None],
|
|
||||||
"inverters": ["inverters", "Inverters", None, None],
|
|
||||||
"power_consumption": ["LOAD", "Power Consumption", None, "mdi:flash"],
|
|
||||||
"solar_power": ["PV", "Solar Power", None, "mdi:solar-power"],
|
|
||||||
"grid_power": ["GRID", "Grid Power", None, "mdi:power-plug"],
|
|
||||||
"storage_power": ["STORAGE", "Storage Power", None, "mdi:car-battery"],
|
|
||||||
}
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|
||||||
{
|
|
||||||
vol.Required(CONF_API_KEY): cv.string,
|
|
||||||
vol.Required(CONF_SITE_ID): cv.string,
|
|
||||||
vol.Optional(CONF_NAME, default="SolarEdge"): cv.string,
|
|
||||||
vol.Optional(CONF_MONITORED_CONDITIONS, default=["current_power"]): vol.All(
|
|
||||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Create the SolarEdge Monitoring API sensor."""
|
"""Old configuration."""
|
||||||
import solaredge
|
pass
|
||||||
|
|
||||||
api_key = config[CONF_API_KEY]
|
|
||||||
site_id = config[CONF_SITE_ID]
|
|
||||||
platform_name = config[CONF_NAME]
|
|
||||||
|
|
||||||
# Create new SolarEdge object to retrieve data
|
async def async_setup_entry(hass, entry, async_add_entities):
|
||||||
api = solaredge.Solaredge(api_key)
|
"""Add an solarEdge entry."""
|
||||||
|
# Add the needed sensors to hass
|
||||||
|
api = solaredge.Solaredge(entry.data[CONF_API_KEY])
|
||||||
|
|
||||||
# Check if api can be reached and site is active
|
# Check if api can be reached and site is active
|
||||||
try:
|
try:
|
||||||
response = api.get_details(site_id)
|
response = await hass.async_add_executor_job(
|
||||||
|
api.get_details, entry.data[CONF_SITE_ID]
|
||||||
|
)
|
||||||
if response["details"]["status"].lower() != "active":
|
if response["details"]["status"].lower() != "active":
|
||||||
_LOGGER.error("SolarEdge site is not active")
|
_LOGGER.error("SolarEdge site is not active")
|
||||||
return
|
return
|
||||||
|
@ -108,17 +45,13 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
_LOGGER.error("Could not retrieve details from SolarEdge API")
|
_LOGGER.error("Could not retrieve details from SolarEdge API")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create sensor factory that will create sensors based on sensor_key.
|
sensor_factory = SolarEdgeSensorFactory(entry.title, entry.data[CONF_SITE_ID], api)
|
||||||
sensor_factory = SolarEdgeSensorFactory(platform_name, site_id, api)
|
|
||||||
|
|
||||||
# Create a new sensor for each sensor type.
|
|
||||||
entities = []
|
entities = []
|
||||||
for sensor_key in config[CONF_MONITORED_CONDITIONS]:
|
for sensor_key in SENSOR_TYPES:
|
||||||
sensor = sensor_factory.create_sensor(sensor_key)
|
sensor = sensor_factory.create_sensor(sensor_key)
|
||||||
if sensor is not None:
|
if sensor is not None:
|
||||||
entities.append(sensor)
|
entities.append(sensor)
|
||||||
|
async_add_entities(entities)
|
||||||
add_entities(entities, True)
|
|
||||||
|
|
||||||
|
|
||||||
class SolarEdgeSensorFactory:
|
class SolarEdgeSensorFactory:
|
||||||
|
|
21
homeassistant/components/solaredge/strings.json
Normal file
21
homeassistant/components/solaredge/strings.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "SolarEdge",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Define the API parameters for this installation",
|
||||||
|
"data": {
|
||||||
|
"name": "The name of this installation",
|
||||||
|
"site_id": "The SolarEdge site-id",
|
||||||
|
"api_key": "The API key for this site"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"site_exists": "This site_id is already configured"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"site_exists": "This site_id is already configured"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ FLOWS = [
|
||||||
"simplisafe",
|
"simplisafe",
|
||||||
"smartthings",
|
"smartthings",
|
||||||
"smhi",
|
"smhi",
|
||||||
|
"solaredge",
|
||||||
"somfy",
|
"somfy",
|
||||||
"sonos",
|
"sonos",
|
||||||
"tellduslive",
|
"tellduslive",
|
||||||
|
|
|
@ -386,6 +386,9 @@ sleepyq==0.7
|
||||||
# homeassistant.components.smhi
|
# homeassistant.components.smhi
|
||||||
smhi-pkg==1.0.10
|
smhi-pkg==1.0.10
|
||||||
|
|
||||||
|
# homeassistant.components.solaredge
|
||||||
|
solaredge==0.0.2
|
||||||
|
|
||||||
# homeassistant.components.honeywell
|
# homeassistant.components.honeywell
|
||||||
somecomfort==0.5.2
|
somecomfort==0.5.2
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,7 @@ TEST_REQUIREMENTS = (
|
||||||
"simplisafe-python",
|
"simplisafe-python",
|
||||||
"sleepyq",
|
"sleepyq",
|
||||||
"smhi-pkg",
|
"smhi-pkg",
|
||||||
|
"solaredge",
|
||||||
"somecomfort",
|
"somecomfort",
|
||||||
"sqlalchemy",
|
"sqlalchemy",
|
||||||
"srpenergy",
|
"srpenergy",
|
||||||
|
|
1
tests/components/solaredge/__init__.py
Normal file
1
tests/components/solaredge/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""Tests for the SolarEdge component."""
|
132
tests/components/solaredge/test_config_flow.py
Normal file
132
tests/components/solaredge/test_config_flow.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
"""Tests for the SolarEdge config flow."""
|
||||||
|
import pytest
|
||||||
|
from requests.exceptions import HTTPError, ConnectTimeout
|
||||||
|
from unittest.mock import patch, Mock
|
||||||
|
|
||||||
|
from homeassistant import data_entry_flow
|
||||||
|
from homeassistant.components.solaredge import config_flow
|
||||||
|
from homeassistant.components.solaredge.const import CONF_SITE_ID, DEFAULT_NAME
|
||||||
|
from homeassistant.const import CONF_NAME, CONF_API_KEY
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
NAME = "solaredge site 1 2 3"
|
||||||
|
SITE_ID = "1a2b3c4d5e6f7g8h"
|
||||||
|
API_KEY = "a1b2c3d4e5f6g7h8"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="test_api")
|
||||||
|
def mock_controller():
|
||||||
|
"""Mock a successfull Solaredge API."""
|
||||||
|
api = Mock()
|
||||||
|
api.get_details.return_value = {"details": {"status": "active"}}
|
||||||
|
with patch("solaredge.Solaredge", return_value=api):
|
||||||
|
yield api
|
||||||
|
|
||||||
|
|
||||||
|
def init_config_flow(hass):
|
||||||
|
"""Init a configuration flow."""
|
||||||
|
flow = config_flow.SolarEdgeConfigFlow()
|
||||||
|
flow.hass = hass
|
||||||
|
return flow
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user(hass, test_api):
|
||||||
|
"""Test user config."""
|
||||||
|
flow = init_config_flow(hass)
|
||||||
|
|
||||||
|
result = await flow.async_step_user()
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
# tets with all provided
|
||||||
|
result = await flow.async_step_user(
|
||||||
|
{CONF_NAME: NAME, CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "solaredge_site_1_2_3"
|
||||||
|
assert result["data"][CONF_SITE_ID] == SITE_ID
|
||||||
|
assert result["data"][CONF_API_KEY] == API_KEY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import(hass, test_api):
|
||||||
|
"""Test import step."""
|
||||||
|
flow = init_config_flow(hass)
|
||||||
|
|
||||||
|
# import with site_id and api_key
|
||||||
|
result = await flow.async_step_import(
|
||||||
|
{CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "solaredge"
|
||||||
|
assert result["data"][CONF_SITE_ID] == SITE_ID
|
||||||
|
assert result["data"][CONF_API_KEY] == API_KEY
|
||||||
|
|
||||||
|
# import with all
|
||||||
|
result = await flow.async_step_import(
|
||||||
|
{CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID, CONF_NAME: NAME}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "solaredge_site_1_2_3"
|
||||||
|
assert result["data"][CONF_SITE_ID] == SITE_ID
|
||||||
|
assert result["data"][CONF_API_KEY] == API_KEY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_abort_if_already_setup(hass, test_api):
|
||||||
|
"""Test we abort if the site_id is already setup."""
|
||||||
|
flow = init_config_flow(hass)
|
||||||
|
MockConfigEntry(
|
||||||
|
domain="solaredge",
|
||||||
|
data={CONF_NAME: DEFAULT_NAME, CONF_SITE_ID: SITE_ID, CONF_API_KEY: API_KEY},
|
||||||
|
).add_to_hass(hass)
|
||||||
|
|
||||||
|
# import: Should fail, same SITE_ID
|
||||||
|
result = await flow.async_step_import(
|
||||||
|
{CONF_NAME: DEFAULT_NAME, CONF_SITE_ID: SITE_ID, CONF_API_KEY: API_KEY}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "site_exists"
|
||||||
|
|
||||||
|
# user: Should fail, same SITE_ID
|
||||||
|
result = await flow.async_step_user(
|
||||||
|
{CONF_NAME: "test", CONF_SITE_ID: SITE_ID, CONF_API_KEY: "test"}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] == {CONF_SITE_ID: "site_exists"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_asserts(hass, test_api):
|
||||||
|
"""Test the _site_in_configuration_exists method."""
|
||||||
|
flow = init_config_flow(hass)
|
||||||
|
|
||||||
|
# test with inactive site
|
||||||
|
test_api.get_details.return_value = {"details": {"status": "NOK"}}
|
||||||
|
result = await flow.async_step_user(
|
||||||
|
{CONF_NAME: NAME, CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] == {CONF_SITE_ID: "site_not_active"}
|
||||||
|
|
||||||
|
# test with api_failure
|
||||||
|
test_api.get_details.return_value = {}
|
||||||
|
result = await flow.async_step_user(
|
||||||
|
{CONF_NAME: NAME, CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] == {CONF_SITE_ID: "api_failure"}
|
||||||
|
|
||||||
|
# test with ConnectionTimeout
|
||||||
|
test_api.get_details.side_effect = ConnectTimeout()
|
||||||
|
result = await flow.async_step_user(
|
||||||
|
{CONF_NAME: NAME, CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] == {CONF_SITE_ID: "could_not_connect"}
|
||||||
|
|
||||||
|
# test with HTTPError
|
||||||
|
test_api.get_details.side_effect = HTTPError()
|
||||||
|
result = await flow.async_step_user(
|
||||||
|
{CONF_NAME: NAME, CONF_API_KEY: API_KEY, CONF_SITE_ID: SITE_ID}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] == {CONF_SITE_ID: "could_not_connect"}
|
Loading…
Add table
Reference in a new issue