mirror of
https://github.com/home-assistant/core.git
synced 2025-06-24 17:11:53 +02:00
* Initial commit for VegeHub integration * Moved several pieces to library, continuing. * All device contact moved to library * Updated documentation link * Fixed an error in strings.json * Removed commented out code and unused file * Removed unneeded info logging, and a few missed lines of commented code * Added/removed comments for clarity * Converted integration to use webhooks. * Update __init__.py to remove unnecessary code. Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com> * Remove unnecessary code from config_flow.py Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com> * Simplify unique_id assertion. * Switch to CONF_ constant for user input * Added explanation for passing exception. * Got rid of try-except, since I don't really handle the exceptions her anyway. * Moved data transform to vegehub library * Changed references to use HA constants. * Fixed assigning and returning _attr properties. * Moved temperature sensor transform to the library. * Moved sensor names to strings.json * Made webhook names unique to avoid collisions when multiple devices are added. * Converted to using entry.runtime_data * Removed options flow for first PR * Removed switch support to limit PR to one platform * Removed/updated outdated tests * Update homeassistant/components/vegehub/__init__.py Co-authored-by: Josef Zweck <josef@zweck.dev> * Got rid of strings in favor of constants. * Got rid of unnecessary check * Imported constant directly. * Added custom type for entry * Expanded CONF_ constants into sensor.py * Get rid of extra `str` and `get` Co-authored-by: Josef Zweck <josef@zweck.dev> * Added type to errors * Added try/except to MAC address retrieval * Moved functionality out of ConfigFlow that shouldn't have been there * Removed IP:MAC tracking from ConfigFlow * Added retries to VegeHub PyPI package, and implemented them in integration * Removed different sensor types for now * Fixed typo * Changed abort to error * Fixed error reporting in config flow * Further simplify sensor.py to handle all sensors the same * Added comment to clarify * Got rid of unused constants * Removed unused strings in strings.json * Added quality_scale.yaml * Fixed problems in sensor init * Moved config url and sw version storage into vegehub package * Get rid of extra declaration Co-authored-by: Josef Zweck <josef@zweck.dev> * Removed unnecessary task * Fix type for entry * Added a test before setup * Fixed tests and got test coverage of config flow to 100% * Fixed test descriptions * Implemented a coordinator * Removed unused property * Fixed a few minor issues with the coordinator implementation * Removed unused function * Fixed some tests * Trying to fix a problem with re-initialization when server reboots. Mostly working. * Moved hub.setup from async_setup_entry to config flow to avoid running it on system reboot * Delete tests/testing_config/.storage/http.auth * Fixed errors in coordinator.py * Added IP validation for manual input IP addresses * Moved data into self._discovered to simplify * Removed redundant typing * Shortened sensor unique ID and added coordinator handler * Added call to super()._handle_coordinator_update() so state gets handled correctly * Fixed == and is * Got rid of "slot" and moved functionality to lib * Got rid of mocked aiohttp calls in favor of just mocking the vegehub library * Rewrote config flow to make more sense. * Changed order of data and data_description * Changes to sensor.py * Got rid of async_update_data in coordinator and moved async_set_updated_data into webhook callback * Changed sensor updates so that they keep using last known values if update doesn't contain data for them * Changed config flow to use homeassistant.helpers.service_info zeroconf instead of homeassistant.components zeroconf * Added types to test parameters * Changes and notes in config_flow.py * Minor fix to get existing tests working before making changes to tests * Removed unused data and simplified data passing * Fixed tests, removed unused data, moved sensor tests to snapshots * Mocked async_setup_entry and async_unload_entry * Eliminated retry step so that retries just happen in the user flow or zeroconf_confirm * Bumped the library version * Bumped library version again * Changed test-before-setup test * Improved use of coordinator * Almost done reworking tests. A few more changes still needed. * Added via device to sensor.py and key reference to strings.json * Webhook tests are almost, but not quite, working * Fully functional again * Change error to assert * made identifiers and via_device the same * made the via_device just be the mac * Fixed strings.json and updated translations * Fixed test_sensor.py * Cleaned up tests and added autouse to several fixtures to simplify * Switched from error to assert, and added exemption to quality scale. * Cleaned up some tests and added update of IP if unique ID of discovered device is the same. * Improved zeroconfig to update IP and hostname, and added a test to make sure those work. * Fixed a comment. * Improved ip/hostname update test. * Changed Hub to VegeHub in strings.json for clarity. * Switched to using a base entity to simplify and make adding platforms in the future easier. * Moved the vegehub object into the coordinator to simplify. * Removed actuators from sensors, and added unique name for battery sensor * Changed coordinator to manage its own data, changed sensors to use descriptions and return their value as a property * Updated data retrieval keys * Minor updates to several files * Fixed a few things for pytest * Reverted to explicit check for None for pytest * Fixed a comment and a variable name * Fixed a comment * Fix * Bumped depenency version to eliminate pytest from dependencies. --------- Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com> Co-authored-by: Josef Zweck <josef@zweck.dev> Co-authored-by: Joostlek <joostlek@outlook.com>
169 lines
5.6 KiB
Python
169 lines
5.6 KiB
Python
"""Config flow for the VegeHub integration."""
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from vegehub import VegeHub
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.webhook import (
|
|
async_generate_id as webhook_generate_id,
|
|
async_generate_url as webhook_generate_url,
|
|
)
|
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
|
from homeassistant.const import (
|
|
CONF_DEVICE,
|
|
CONF_HOST,
|
|
CONF_IP_ADDRESS,
|
|
CONF_MAC,
|
|
CONF_WEBHOOK_ID,
|
|
)
|
|
from homeassistant.helpers.service_info import zeroconf
|
|
from homeassistant.util.network import is_ip_address
|
|
|
|
from .const import DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class VegeHubConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
"""Handle a config flow for VegeHub integration."""
|
|
|
|
_hub: VegeHub
|
|
_hostname: str
|
|
webhook_id: str
|
|
|
|
async def async_step_user(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Handle a flow initiated by the user."""
|
|
errors: dict[str, str] = {}
|
|
|
|
if user_input is not None:
|
|
if not is_ip_address(user_input[CONF_IP_ADDRESS]):
|
|
# User-supplied IP address is invalid.
|
|
errors["base"] = "invalid_ip"
|
|
|
|
if not errors:
|
|
self._hub = VegeHub(user_input[CONF_IP_ADDRESS])
|
|
self._hostname = self._hub.ip_address
|
|
errors = await self._setup_device()
|
|
if not errors:
|
|
# Proceed to create the config entry
|
|
return await self._create_entry()
|
|
|
|
# Show the form to allow the user to manually enter the IP address
|
|
return self.async_show_form(
|
|
step_id="user",
|
|
data_schema=vol.Schema(
|
|
{
|
|
vol.Required(CONF_IP_ADDRESS): str,
|
|
}
|
|
),
|
|
errors=errors,
|
|
)
|
|
|
|
async def async_step_zeroconf(
|
|
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
|
) -> ConfigFlowResult:
|
|
"""Handle zeroconf discovery."""
|
|
|
|
# Extract the IP address from the zeroconf discovery info
|
|
device_ip = discovery_info.host
|
|
|
|
self._async_abort_entries_match({CONF_IP_ADDRESS: device_ip})
|
|
|
|
self._hostname = discovery_info.hostname.removesuffix(".local.")
|
|
config_url = f"http://{discovery_info.hostname[:-1]}:{discovery_info.port}"
|
|
|
|
# Create a VegeHub object to interact with the device
|
|
self._hub = VegeHub(device_ip)
|
|
|
|
try:
|
|
await self._hub.retrieve_mac_address(retries=2)
|
|
except ConnectionError:
|
|
return self.async_abort(reason="cannot_connect")
|
|
except TimeoutError:
|
|
return self.async_abort(reason="timeout_connect")
|
|
|
|
if not self._hub.mac_address:
|
|
return self.async_abort(reason="cannot_connect")
|
|
|
|
# Check if this device already exists
|
|
await self.async_set_unique_id(self._hub.mac_address)
|
|
self._abort_if_unique_id_configured(
|
|
updates={CONF_IP_ADDRESS: device_ip, CONF_HOST: self._hostname}
|
|
)
|
|
|
|
# Add title and configuration URL to the context so that the device discovery
|
|
# tile has the correct title, and a "Visit Device" link available.
|
|
self.context.update(
|
|
{
|
|
"title_placeholders": {"host": self._hostname + " (" + device_ip + ")"},
|
|
"configuration_url": (config_url),
|
|
}
|
|
)
|
|
|
|
# If the device is new, allow the user to continue setup
|
|
return await self.async_step_zeroconf_confirm()
|
|
|
|
async def async_step_zeroconf_confirm(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Handle user confirmation for a discovered device."""
|
|
errors: dict[str, str] = {}
|
|
if user_input is not None:
|
|
errors = await self._setup_device()
|
|
if not errors:
|
|
return await self._create_entry()
|
|
|
|
# Show the confirmation form
|
|
self._set_confirm_only()
|
|
return self.async_show_form(step_id="zeroconf_confirm", errors=errors)
|
|
|
|
async def _setup_device(self) -> dict[str, str]:
|
|
"""Set up the VegeHub device."""
|
|
errors: dict[str, str] = {}
|
|
self.webhook_id = webhook_generate_id()
|
|
webhook_url = webhook_generate_url(
|
|
self.hass,
|
|
self.webhook_id,
|
|
allow_external=False,
|
|
allow_ip=True,
|
|
)
|
|
|
|
# Send the webhook address to the hub as its server target.
|
|
# This step can happen in the init, because that gets executed
|
|
# every time Home Assistant starts up, and this step should
|
|
# only happen in the initial setup of the VegeHub.
|
|
try:
|
|
await self._hub.setup("", webhook_url, retries=1)
|
|
except ConnectionError:
|
|
errors["base"] = "cannot_connect"
|
|
except TimeoutError:
|
|
errors["base"] = "timeout_connect"
|
|
|
|
if not self._hub.mac_address:
|
|
errors["base"] = "cannot_connect"
|
|
|
|
return errors
|
|
|
|
async def _create_entry(self) -> ConfigFlowResult:
|
|
"""Create a config entry for the device."""
|
|
|
|
# Check if this device already exists
|
|
await self.async_set_unique_id(self._hub.mac_address)
|
|
self._abort_if_unique_id_configured()
|
|
|
|
# Save Hub info to be used later when defining the VegeHub object
|
|
info_data = {
|
|
CONF_IP_ADDRESS: self._hub.ip_address,
|
|
CONF_HOST: self._hostname,
|
|
CONF_MAC: self._hub.mac_address,
|
|
CONF_DEVICE: self._hub.info,
|
|
CONF_WEBHOOK_ID: self.webhook_id,
|
|
}
|
|
|
|
# Create the config entry for the new device
|
|
return self.async_create_entry(title=self._hostname, data=info_data)
|