mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Create PoolSense integration (#35561)
* Created integration for PoolSense - a device to maintain your pool * Updated poolsense integration with changes due to code review comments. * Update poolsense with lint fix (logging-not-lazy) * Update poolsense with lint fix (f string missing placeholders) * Update homeassistant/components/poolsense/config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Added test for poolsense component. Updated config_flow to better follow the guidelines. * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update tests/components/poolsense/test_config_flow.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Removed uneccessary functions. * Added local venv to gitignore * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update to strings to allow for translations. Also some coding convention updates. * Removed space in icon return Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Removed space in icon return Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Removed space in icon return Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/__init__.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/__init__.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/__init__.py Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update homeassistant/components/poolsense/strings.json Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Updated to include some error checks for pypi package * Apply suggestions from code review * Apply suggestions from code review * Apply suggestions from code review * Update tests/components/poolsense/test_config_flow.py * Update homeassistant/components/poolsense/sensor.py * Apply suggestions from code review * Update homeassistant/components/poolsense/__init__.py * Apply suggestions from code review * Apply suggestions from code review * Apply suggestions from code review * Update homeassistant/components/poolsense/sensor.py * Apply suggestions from code review * Update homeassistant/components/poolsense/__init__.py * Update homeassistant/components/poolsense/sensor.py Co-authored-by: Chris Talkington <chris@talkingtontech.com>
This commit is contained in:
parent
352c572e5d
commit
10786bbe7f
@ -624,6 +624,8 @@ omit =
|
|||||||
homeassistant/components/plum_lightpad/light.py
|
homeassistant/components/plum_lightpad/light.py
|
||||||
homeassistant/components/pocketcasts/sensor.py
|
homeassistant/components/pocketcasts/sensor.py
|
||||||
homeassistant/components/point/*
|
homeassistant/components/point/*
|
||||||
|
homeassistant/components/poolsense/__init__.py
|
||||||
|
homeassistant/components/poolsense/sensor.py
|
||||||
homeassistant/components/prezzibenzina/sensor.py
|
homeassistant/components/prezzibenzina/sensor.py
|
||||||
homeassistant/components/proliphix/climate.py
|
homeassistant/components/proliphix/climate.py
|
||||||
homeassistant/components/prometheus/*
|
homeassistant/components/prometheus/*
|
||||||
|
@ -318,6 +318,7 @@ homeassistant/components/plex/* @jjlawren
|
|||||||
homeassistant/components/plugwise/* @CoMPaTech @bouwew
|
homeassistant/components/plugwise/* @CoMPaTech @bouwew
|
||||||
homeassistant/components/plum_lightpad/* @ColinHarrington @prystupa
|
homeassistant/components/plum_lightpad/* @ColinHarrington @prystupa
|
||||||
homeassistant/components/point/* @fredrike
|
homeassistant/components/point/* @fredrike
|
||||||
|
homeassistant/components/poolsense/* @haemishkyd
|
||||||
homeassistant/components/powerwall/* @bdraco @jrester
|
homeassistant/components/powerwall/* @bdraco @jrester
|
||||||
homeassistant/components/prometheus/* @knyar
|
homeassistant/components/prometheus/* @knyar
|
||||||
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe
|
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe
|
||||||
|
103
homeassistant/components/poolsense/__init__.py
Normal file
103
homeassistant/components/poolsense/__init__.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
"""The PoolSense integration."""
|
||||||
|
import asyncio
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import async_timeout
|
||||||
|
from poolsense import PoolSense
|
||||||
|
from poolsense.exceptions import PoolSenseError
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
from homeassistant.helpers import aiohttp_client, update_coordinator
|
||||||
|
from homeassistant.helpers.update_coordinator import UpdateFailed
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
PLATFORMS = ["sensor"]
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass: HomeAssistant, config: dict):
|
||||||
|
"""Set up the PoolSense component."""
|
||||||
|
# Make sure coordinator is initialized.
|
||||||
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Set up PoolSense from a config entry."""
|
||||||
|
poolsense = PoolSense()
|
||||||
|
auth_valid = await poolsense.test_poolsense_credentials(
|
||||||
|
aiohttp_client.async_get_clientsession(hass),
|
||||||
|
entry.data[CONF_EMAIL],
|
||||||
|
entry.data[CONF_PASSWORD],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not auth_valid:
|
||||||
|
_LOGGER.error("Invalid authentication")
|
||||||
|
return False
|
||||||
|
|
||||||
|
coordinator = await get_coordinator(hass, entry)
|
||||||
|
|
||||||
|
await hass.data[DOMAIN][entry.entry_id].async_refresh()
|
||||||
|
|
||||||
|
if not coordinator.last_update_success:
|
||||||
|
raise ConfigEntryNotReady
|
||||||
|
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
|
for component in PLATFORMS:
|
||||||
|
hass.async_create_task(
|
||||||
|
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Unload a config entry."""
|
||||||
|
unload_ok = all(
|
||||||
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
hass.config_entries.async_forward_entry_unload(entry, component)
|
||||||
|
for component in PLATFORMS
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if unload_ok:
|
||||||
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
async def get_coordinator(hass, entry):
|
||||||
|
"""Get the data update coordinator."""
|
||||||
|
|
||||||
|
async def async_get_data():
|
||||||
|
_LOGGER.info("Run query to server")
|
||||||
|
poolsense = PoolSense()
|
||||||
|
return_data = {}
|
||||||
|
with async_timeout.timeout(10):
|
||||||
|
try:
|
||||||
|
return_data = await poolsense.get_poolsense_data(
|
||||||
|
aiohttp_client.async_get_clientsession(hass),
|
||||||
|
entry.data[CONF_EMAIL],
|
||||||
|
entry.data[CONF_PASSWORD],
|
||||||
|
)
|
||||||
|
except (PoolSenseError) as error:
|
||||||
|
raise UpdateFailed(error)
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return update_coordinator.DataUpdateCoordinator(
|
||||||
|
hass,
|
||||||
|
logging.getLogger(__name__),
|
||||||
|
name=DOMAIN,
|
||||||
|
update_method=async_get_data,
|
||||||
|
update_interval=timedelta(hours=1),
|
||||||
|
)
|
71
homeassistant/components/poolsense/config_flow.py
Normal file
71
homeassistant/components/poolsense/config_flow.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
"""Config flow for PoolSense integration."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from poolsense import PoolSense
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
||||||
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
|
||||||
|
from .const import DOMAIN # pylint:disable=unused-import
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PoolSenseConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for PoolSense."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||||
|
|
||||||
|
_options = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize PoolSense config flow."""
|
||||||
|
self._email = None
|
||||||
|
self._password = None
|
||||||
|
self._errors = {}
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Handle the initial step."""
|
||||||
|
self._errors = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
await self.async_set_unique_id(user_input[CONF_EMAIL])
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
self._email = user_input[CONF_EMAIL]
|
||||||
|
self._password = user_input[CONF_PASSWORD]
|
||||||
|
_LOGGER.debug("Configuring user: %s - Password hidden.", self._email)
|
||||||
|
|
||||||
|
poolsense = PoolSense()
|
||||||
|
api_key_valid = await poolsense.test_poolsense_credentials(
|
||||||
|
aiohttp_client.async_get_clientsession(self.hass),
|
||||||
|
self._email,
|
||||||
|
self._password,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not api_key_valid:
|
||||||
|
self._errors["base"] = "auth"
|
||||||
|
|
||||||
|
if not self._errors:
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=self._email,
|
||||||
|
data={CONF_EMAIL: self._email, CONF_PASSWORD: self._password},
|
||||||
|
)
|
||||||
|
|
||||||
|
return await self._show_setup_form(user_input, self._errors)
|
||||||
|
|
||||||
|
async def _show_setup_form(self, user_input=None, errors=None):
|
||||||
|
"""Show the setup form to the user."""
|
||||||
|
if user_input is None:
|
||||||
|
user_input = {}
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str}
|
||||||
|
),
|
||||||
|
errors=errors or {},
|
||||||
|
)
|
4
homeassistant/components/poolsense/const.py
Normal file
4
homeassistant/components/poolsense/const.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
"""Constants for the PoolSense integration."""
|
||||||
|
|
||||||
|
DOMAIN = "poolsense"
|
||||||
|
ATTRIBUTION = "PoolSense Data"
|
12
homeassistant/components/poolsense/manifest.json
Normal file
12
homeassistant/components/poolsense/manifest.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"domain": "poolsense",
|
||||||
|
"name": "PoolSense",
|
||||||
|
"config_flow": true,
|
||||||
|
"documentation": "https://www.home-assistant.io/integrations/poolsense",
|
||||||
|
"requirements": [
|
||||||
|
"poolsense==0.0.5"
|
||||||
|
],
|
||||||
|
"codeowners": [
|
||||||
|
"@haemishkyd"
|
||||||
|
]
|
||||||
|
}
|
172
homeassistant/components/poolsense/sensor.py
Normal file
172
homeassistant/components/poolsense/sensor.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
"""Sensor platform for the PoolSense sensor."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ATTRIBUTION,
|
||||||
|
CONF_EMAIL,
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_TIMESTAMP,
|
||||||
|
STATE_OK,
|
||||||
|
STATE_PROBLEM,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
UNIT_PERCENTAGE,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
from .const import ATTRIBUTION, DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SENSORS = {
|
||||||
|
"Chlorine": {
|
||||||
|
"unit": "mV",
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "Chlorine",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
"pH": {"unit": None, "icon": "mdi:pool", "name": "pH", "device_class": None},
|
||||||
|
"Battery": {
|
||||||
|
"unit": UNIT_PERCENTAGE,
|
||||||
|
"icon": "mdi:battery",
|
||||||
|
"name": "Battery",
|
||||||
|
"device_class": DEVICE_CLASS_BATTERY,
|
||||||
|
},
|
||||||
|
"Water Temp": {
|
||||||
|
"unit": TEMP_CELSIUS,
|
||||||
|
"icon": "mdi:coolant-temperature",
|
||||||
|
"name": "Temperature",
|
||||||
|
"device_class": DEVICE_CLASS_TEMPERATURE,
|
||||||
|
},
|
||||||
|
"Last Seen": {
|
||||||
|
"unit": None,
|
||||||
|
"icon": "mdi:clock",
|
||||||
|
"name": "Last Seen",
|
||||||
|
"device_class": DEVICE_CLASS_TIMESTAMP,
|
||||||
|
},
|
||||||
|
"Chlorine High": {
|
||||||
|
"unit": "mV",
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "Chlorine High",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
"Chlorine Low": {
|
||||||
|
"unit": "mV",
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "Chlorine Low",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
"pH High": {
|
||||||
|
"unit": None,
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "pH High",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
"pH Low": {
|
||||||
|
"unit": None,
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "pH Low",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
"pH Status": {
|
||||||
|
"unit": None,
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "pH Status",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
"Chlorine Status": {
|
||||||
|
"unit": None,
|
||||||
|
"icon": "mdi:pool",
|
||||||
|
"name": "Chlorine Status",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
"""Defer sensor setup to the shared sensor module."""
|
||||||
|
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
PoolSenseSensor(coordinator, config_entry.data[CONF_EMAIL], info_type)
|
||||||
|
for info_type in SENSORS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PoolSenseSensor(Entity):
|
||||||
|
"""Sensor representing poolsense data."""
|
||||||
|
|
||||||
|
unique_id = None
|
||||||
|
|
||||||
|
def __init__(self, coordinator, email, info_type):
|
||||||
|
"""Initialize poolsense sensor."""
|
||||||
|
self._email = email
|
||||||
|
self.unique_id = f"{email}-{info_type}"
|
||||||
|
self.coordinator = coordinator
|
||||||
|
self.info_type = info_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
"""Return if sensor is available."""
|
||||||
|
return self.coordinator.last_update_success
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the particular component."""
|
||||||
|
return "PoolSense {}".format(SENSORS[self.info_type]["name"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""Return False, updates are controlled via coordinator."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""State of the sensor."""
|
||||||
|
if self.info_type == "pH Status":
|
||||||
|
if self.coordinator.data[self.info_type] == "red":
|
||||||
|
return STATE_PROBLEM
|
||||||
|
return STATE_OK
|
||||||
|
if self.info_type == "Chlorine Status":
|
||||||
|
if self.coordinator.data[self.info_type] == "red":
|
||||||
|
return STATE_PROBLEM
|
||||||
|
return STATE_OK
|
||||||
|
return self.coordinator.data[self.info_type]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the device class."""
|
||||||
|
return SENSORS[self.info_type]["device_class"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
"""Return the icon."""
|
||||||
|
if self.info_type == "pH Status":
|
||||||
|
if self.coordinator.data[self.info_type] == "red":
|
||||||
|
return "mdi:thumb-down"
|
||||||
|
return "mdi:thumb-up"
|
||||||
|
if self.info_type == "Chlorine Status":
|
||||||
|
if self.coordinator.data[self.info_type] == "red":
|
||||||
|
return "mdi:thumb-down"
|
||||||
|
return "mdi:thumb-up"
|
||||||
|
return SENSORS[self.info_type]["icon"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return unit of measurement."""
|
||||||
|
return SENSORS[self.info_type]["unit"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return device attributes."""
|
||||||
|
return {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Update status of sensor."""
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""When entity is added to hass."""
|
||||||
|
self.async_on_remove(
|
||||||
|
self.coordinator.async_add_listener(self.async_write_ha_state)
|
||||||
|
)
|
22
homeassistant/components/poolsense/strings.json
Normal file
22
homeassistant/components/poolsense/strings.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "PoolSense",
|
||||||
|
"description": "[%key:common::config_flow::description%]",
|
||||||
|
"data": {
|
||||||
|
"email": "[%key:common::config_flow::data::email%]",
|
||||||
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
homeassistant/components/poolsense/translations/en.json
Normal file
22
homeassistant/components/poolsense/translations/en.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "PoolSense",
|
||||||
|
"description": "Set up PoolSense integration. Register on the dedicated app to get your username and password. Serial is optional.",
|
||||||
|
"data": {
|
||||||
|
"email": "Email",
|
||||||
|
"password": "Password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "Can't connect to PoolSense.",
|
||||||
|
"invalid_auth": "Invalid authorisation details.",
|
||||||
|
"unknown": "Unknown Error."
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Device already configured."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -124,6 +124,7 @@ FLOWS = [
|
|||||||
"plugwise",
|
"plugwise",
|
||||||
"plum_lightpad",
|
"plum_lightpad",
|
||||||
"point",
|
"point",
|
||||||
|
"poolsense",
|
||||||
"powerwall",
|
"powerwall",
|
||||||
"ps4",
|
"ps4",
|
||||||
"pvpc_hourly_pricing",
|
"pvpc_hourly_pricing",
|
||||||
|
@ -1106,6 +1106,9 @@ pmsensor==0.4
|
|||||||
# homeassistant.components.pocketcasts
|
# homeassistant.components.pocketcasts
|
||||||
pocketcasts==0.1
|
pocketcasts==0.1
|
||||||
|
|
||||||
|
# homeassistant.components.poolsense
|
||||||
|
poolsense==0.0.5
|
||||||
|
|
||||||
# homeassistant.components.reddit
|
# homeassistant.components.reddit
|
||||||
praw==6.5.1
|
praw==6.5.1
|
||||||
|
|
||||||
|
@ -496,6 +496,9 @@ plumlightpad==0.0.11
|
|||||||
# homeassistant.components.serial_pm
|
# homeassistant.components.serial_pm
|
||||||
pmsensor==0.4
|
pmsensor==0.4
|
||||||
|
|
||||||
|
# homeassistant.components.poolsense
|
||||||
|
poolsense==0.0.5
|
||||||
|
|
||||||
# homeassistant.components.reddit
|
# homeassistant.components.reddit
|
||||||
praw==6.5.1
|
praw==6.5.1
|
||||||
|
|
||||||
|
1
tests/components/poolsense/__init__.py
Normal file
1
tests/components/poolsense/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for the PoolSense integration."""
|
55
tests/components/poolsense/test_config_flow.py
Normal file
55
tests/components/poolsense/test_config_flow.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""Test the PoolSense config flow."""
|
||||||
|
from homeassistant import data_entry_flow
|
||||||
|
from homeassistant.components.poolsense.const import DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
|
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
||||||
|
|
||||||
|
from tests.async_mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
async def test_show_form(hass):
|
||||||
|
"""Test that the form is served with no input."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == SOURCE_USER
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_credentials(hass):
|
||||||
|
"""Test we handle invalid credentials."""
|
||||||
|
with patch(
|
||||||
|
"poolsense.PoolSense.test_poolsense_credentials", return_value=False,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={CONF_EMAIL: "test-email", CONF_PASSWORD: "test-password"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["errors"] == {"base": "auth"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_valid_credentials(hass):
|
||||||
|
"""Test we handle invalid credentials."""
|
||||||
|
with patch(
|
||||||
|
"poolsense.PoolSense.test_poolsense_credentials", return_value=True
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.poolsense.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.poolsense.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={CONF_EMAIL: "test-email", CONF_PASSWORD: "test-password"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "test-email"
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
Loading…
x
Reference in New Issue
Block a user