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 new integration for Dio Chacon cover devices #116267

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/diagnostics/ @home-assistant/core
/tests/components/diagnostics/ @home-assistant/core
/homeassistant/components/digital_ocean/ @fabaff
/homeassistant/components/dio_chacon/ @cnico
/tests/components/dio_chacon/ @cnico
/homeassistant/components/discogs/ @thibmaek
/homeassistant/components/discord/ @tkdrob
/tests/components/discord/ @tkdrob
Expand Down
98 changes: 98 additions & 0 deletions homeassistant/components/dio_chacon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""The dio_chacon integration."""

import logging
from typing import Any

from dio_chacon_wifi_api import DIOChaconAPIClient

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_PASSWORD,
CONF_UNIQUE_ID,
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import Event, HomeAssistant, ServiceCall

from .const import (
DOMAIN,
EVENT_DIO_CHACON_DEVICE_STATE_CHANGED,
EVENT_DIO_CHACON_DEVICE_STATE_RELOAD,
)

SERVICE_RELOAD_STATE = "reload_state"

_LOGGER = logging.getLogger(__name__)

PLATFORMS: list[Platform] = [Platform.COVER]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up dio_chacon from a config entry."""

_LOGGER.debug("Start of async_setup_entry for dio_chacon integration")

hass.data.setdefault(DOMAIN, {})

config = entry.data

def callback_device_state(data: Any) -> None:
"""Receive callback for device state notification pushed from the server."""

hass.bus.fire(EVENT_DIO_CHACON_DEVICE_STATE_CHANGED, data)

# Authentication verification and login
dio_chacon_client = DIOChaconAPIClient(
config[CONF_USERNAME],
config[CONF_PASSWORD],
config[CONF_UNIQUE_ID],
callback_device_state,
)

# Store an API object for the platforms to access
hass.data[DOMAIN][entry.entry_id] = dio_chacon_client

# Disconnects the permanent websocket connection of home assistant shutdown
async def call_disconnect(event: Event) -> None:
await dio_chacon_client.disconnect()

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, call_disconnect)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

await hass.async_add_executor_job(setup_dio_chacon_service, hass)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""

_LOGGER.debug("Start of async_unload_entry for dio_chacon integration")

hass.services.async_remove(DOMAIN, SERVICE_RELOAD_STATE)

dio_chacon_client = hass.data[DOMAIN][entry.entry_id]
await dio_chacon_client.disconnect()

if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


def setup_dio_chacon_service(hass: HomeAssistant) -> None:
"""Implement a custom service.

This service allows user to reload all devices from the server.
"""

def reload_devices_states(call: ServiceCall) -> None:
"""Trigger a reload of the states of the devices."""
# No data input to call the service

_LOGGER.debug("Call to the reload service for all dio chacon devices")
hass.bus.fire(EVENT_DIO_CHACON_DEVICE_STATE_RELOAD)

hass.services.register(DOMAIN, SERVICE_RELOAD_STATE, reload_devices_states)
86 changes: 86 additions & 0 deletions homeassistant/components/dio_chacon/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Config flow for dio_chacon integration."""

from __future__ import annotations

import logging
from typing import Any

from dio_chacon_wifi_api import DIOChaconAPIClient
from dio_chacon_wifi_api.exceptions import DIOChaconAPIError, DIOChaconInvalidAuthError
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.config_entries import ConfigFlowResult
from homeassistant.const import CONF_PASSWORD, CONF_UNIQUE_ID, CONF_USERNAME

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


class DioChaconConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for dio_chacon."""

_username: str
_password: str
_user_id: str

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
if user_input is None:
return self._show_setup_form()

self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD]

errors: dict[str, str] = {}
try:
self._user_id = await self._authenticate_and_search_user_id()
except DIOChaconAPIError:
errors["base"] = "cannot_connect"
except DIOChaconInvalidAuthError:
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"

if errors:
return self._show_setup_form(errors)

entry_id: str = "dio_chacon_" + self._user_id

# Check if already configured
await self.async_set_unique_id(entry_id)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title="Dio Chacon " + self._username,
data={
CONF_USERNAME: self._username,
CONF_PASSWORD: self._password,
CONF_UNIQUE_ID: self._user_id,
},
)

def _show_setup_form(self, errors=None):
"""Show the setup form to the user."""
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
),
errors=errors,
)

async def _authenticate_and_search_user_id(self) -> str:
"""Validate the user name and password and retrieve the technical user id."""

client = DIOChaconAPIClient(self._username, self._password)

user_id: str = await client.get_user_id()

await client.disconnect()

return user_id
8 changes: 8 additions & 0 deletions homeassistant/components/dio_chacon/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Constants for the dio_chacon integration."""

DOMAIN = "dio_chacon"

MANUFACTURER = "Chacon"

EVENT_DIO_CHACON_DEVICE_STATE_CHANGED = "event_dio_chacon_device_state"
EVENT_DIO_CHACON_DEVICE_STATE_RELOAD = "event_dio_chacon_reload_device_state"