diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index ad4d30358c2..5c461075f85 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -5,19 +5,18 @@ import logging from aiohttp import ClientConnectionError from async_timeout import timeout -from pydaikin.appliance import Appliance +from pydaikin.daikin_base import Appliance import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import CONF_HOST, CONF_HOSTS +from homeassistant.const import CONF_HOST, CONF_HOSTS, CONF_PASSWORD from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util import Throttle from . import config_flow # noqa: F401 -from .const import TIMEOUT +from .const import CONF_KEY, CONF_UUID, TIMEOUT _LOGGER = logging.getLogger(__name__) @@ -62,7 +61,13 @@ async def async_setup(hass, config): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Establish connection with Daikin.""" conf = entry.data - daikin_api = await daikin_api_setup(hass, conf[CONF_HOST]) + daikin_api = await daikin_api_setup( + hass, + conf[CONF_HOST], + conf.get(CONF_KEY), + conf.get(CONF_UUID), + conf.get(CONF_PASSWORD), + ) if not daikin_api: return False hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api}) @@ -87,14 +92,15 @@ async def async_unload_entry(hass, config_entry): return True -async def daikin_api_setup(hass, host): +async def daikin_api_setup(hass, host, key, uuid, password): """Create a Daikin instance only once.""" session = hass.helpers.aiohttp_client.async_get_clientsession() try: with timeout(TIMEOUT): - device = Appliance(host, session) - await device.init() + device = await Appliance.factory( + host, session, key=key, uuid=uuid, password=password + ) except asyncio.TimeoutError: _LOGGER.debug("Connection to %s timed out", host) raise ConfigEntryNotReady @@ -116,8 +122,8 @@ class DaikinApi: def __init__(self, device): """Initialize the Daikin Handle.""" self.device = device - self.name = device.values["name"] - self.ip_address = device.ip + self.name = device.values.get("name", "Daikin AC") + self.ip_address = device.device_ip self._available = True @Throttle(MIN_TIME_BETWEEN_UPDATES) @@ -135,20 +141,14 @@ class DaikinApi: """Return True if entity is available.""" return self._available - @property - def mac(self): - """Return mac-address of device.""" - return self.device.values.get(CONNECTION_NETWORK_MAC) - @property def device_info(self): """Return a device description for device registry.""" info = self.device.values return { - "connections": {(CONNECTION_NETWORK_MAC, self.mac)}, - "identifieres": self.mac, + "identifieres": self.device.mac, "manufacturer": "Daikin", "model": info.get("model"), "name": info.get("name"), - "sw_version": info.get("ver").replace("_", "."), + "sw_version": info.get("ver", "").replace("_", "."), } diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index ebf909dcbda..60a126c182b 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -1,7 +1,6 @@ """Support for the Daikin HVAC.""" import logging -from pydaikin import appliance import voluptuous as vol from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity @@ -96,12 +95,7 @@ class DaikinClimate(ClimateEntity): self._list = { ATTR_HVAC_MODE: list(HA_STATE_TO_DAIKIN), ATTR_FAN_MODE: self._api.device.fan_rate, - ATTR_SWING_MODE: list( - map( - str.title, - appliance.daikin_values(HA_ATTR_TO_DAIKIN[ATTR_SWING_MODE]), - ) - ), + ATTR_SWING_MODE: self._api.device.swing_modes, } self._supported_features = SUPPORT_TARGET_TEMPERATURE @@ -156,7 +150,7 @@ class DaikinClimate(ClimateEntity): @property def unique_id(self): """Return a unique ID.""" - return self._api.mac + return self._api.device.mac @property def temperature_unit(self): diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index 35f21ef3e0d..600c27b1619 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -1,16 +1,17 @@ """Config flow for the Daikin platform.""" import asyncio import logging +from uuid import uuid4 from aiohttp import ClientError from async_timeout import timeout -from pydaikin.appliance import Appliance +from pydaikin.daikin_base import Appliance import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_PASSWORD -from .const import KEY_IP, KEY_MAC, TIMEOUT +from .const import CONF_KEY, CONF_UUID, KEY_IP, KEY_MAC, TIMEOUT _LOGGER = logging.getLogger(__name__) @@ -22,24 +23,46 @@ class FlowHandler(config_entries.ConfigFlow): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL - async def _create_entry(self, host, mac): + def _create_entry(self, host, mac, key=None, uuid=None, password=None): """Register new entry.""" # Check if mac already is registered for entry in self._async_current_entries(): if entry.data[KEY_MAC] == mac: return self.async_abort(reason="already_configured") - return self.async_create_entry(title=host, data={CONF_HOST: host, KEY_MAC: mac}) + return self.async_create_entry( + title=host, + data={ + CONF_HOST: host, + KEY_MAC: mac, + CONF_KEY: key, + CONF_UUID: uuid, + CONF_PASSWORD: password, + }, + ) - async def _create_device(self, host): + async def _create_device(self, host, key=None, password=None): """Create device.""" + # BRP07Cxx devices needs uuid together with key + if key: + uuid = str(uuid4()) + else: + uuid = None + key = None + + if not password: + password = None + try: - device = Appliance( - host, self.hass.helpers.aiohttp_client.async_get_clientsession() - ) with timeout(TIMEOUT): - await device.init() + device = await Appliance.factory( + host, + self.hass.helpers.aiohttp_client.async_get_clientsession(), + key=key, + uuid=uuid, + password=password, + ) except asyncio.TimeoutError: return self.async_abort(reason="device_timeout") except ClientError: @@ -49,16 +72,27 @@ class FlowHandler(config_entries.ConfigFlow): _LOGGER.exception("Unexpected error creating device") return self.async_abort(reason="device_fail") - mac = device.values.get("mac") - return await self._create_entry(host, mac) + mac = device.mac + return self._create_entry(host, mac, key, uuid, password) async def async_step_user(self, user_input=None): """User initiated config flow.""" if user_input is None: return self.async_show_form( - step_id="user", data_schema=vol.Schema({vol.Required(CONF_HOST): str}) + step_id="user", + data_schema=vol.Schema( + { + vol.Required(CONF_HOST): str, + vol.Optional(CONF_KEY): str, + vol.Optional(CONF_PASSWORD): str, + } + ), ) - return await self._create_device(user_input[CONF_HOST]) + return await self._create_device( + user_input[CONF_HOST], + user_input.get(CONF_KEY), + user_input.get(CONF_PASSWORD), + ) async def async_step_import(self, user_input): """Import a config entry.""" @@ -70,4 +104,4 @@ class FlowHandler(config_entries.ConfigFlow): async def async_step_discovery(self, user_input): """Initialize step from discovery.""" _LOGGER.info("Discovered device: %s", user_input) - return await self._create_entry(user_input[KEY_IP], user_input[KEY_MAC]) + return self._create_entry(user_input[KEY_IP], user_input[KEY_MAC]) diff --git a/homeassistant/components/daikin/const.py b/homeassistant/components/daikin/const.py index 15ae5321bf3..452331ff734 100644 --- a/homeassistant/components/daikin/const.py +++ b/homeassistant/components/daikin/const.py @@ -23,6 +23,9 @@ SENSOR_TYPES = { }, } +CONF_KEY = "key" +CONF_UUID = "uuid" + KEY_MAC = "mac" KEY_IP = "ip" diff --git a/homeassistant/components/daikin/manifest.json b/homeassistant/components/daikin/manifest.json index c501fa7c120..ec993ed9b13 100644 --- a/homeassistant/components/daikin/manifest.json +++ b/homeassistant/components/daikin/manifest.json @@ -3,7 +3,7 @@ "name": "Daikin AC", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/daikin", - "requirements": ["pydaikin==1.6.3"], + "requirements": ["pydaikin==2.0.0"], "codeowners": ["@fredrike"], "quality_scale": "platinum" } diff --git a/homeassistant/components/daikin/sensor.py b/homeassistant/components/daikin/sensor.py index d0d8e4b0fda..c389566396d 100644 --- a/homeassistant/components/daikin/sensor.py +++ b/homeassistant/components/daikin/sensor.py @@ -40,7 +40,7 @@ class DaikinClimateSensor(Entity): @property def unique_id(self): """Return a unique ID.""" - return f"{self._api.mac}-{self._device_attribute}" + return f"{self._api.device.mac}-{self._device_attribute}" @property def icon(self): diff --git a/homeassistant/components/daikin/strings.json b/homeassistant/components/daikin/strings.json index 1e82d285eee..8d2862123d4 100644 --- a/homeassistant/components/daikin/strings.json +++ b/homeassistant/components/daikin/strings.json @@ -4,7 +4,11 @@ "user": { "title": "Configure Daikin AC", "description": "Enter IP address of your Daikin AC.", - "data": { "host": "Host" } + "data": { + "host": "Host", + "key": "Authentication key (only used by BRP072C/Zena devices)", + "password": "Device password (only used by SKYFi devices)" + } } }, "abort": { diff --git a/homeassistant/components/daikin/switch.py b/homeassistant/components/daikin/switch.py index b7131c29bdd..0dae8848d39 100644 --- a/homeassistant/components/daikin/switch.py +++ b/homeassistant/components/daikin/switch.py @@ -43,7 +43,7 @@ class DaikinZoneSwitch(ToggleEntity): @property def unique_id(self): """Return a unique ID.""" - return f"{self._api.mac}-zone{self._zone_id}" + return f"{self._api.device.mac}-zone{self._zone_id}" @property def icon(self): diff --git a/homeassistant/components/daikin/translations/en.json b/homeassistant/components/daikin/translations/en.json index f66f360d096..14bb34d5a2a 100644 --- a/homeassistant/components/daikin/translations/en.json +++ b/homeassistant/components/daikin/translations/en.json @@ -8,7 +8,9 @@ "step": { "user": { "data": { - "host": "Host" + "host": "Host", + "key": "Authentication key (only used by BRP072C/Zena devices)", + "password": "Device password (only used by SKYFi devices)" }, "description": "Enter IP address of your Daikin AC.", "title": "Configure Daikin AC" diff --git a/requirements_all.txt b/requirements_all.txt index 69d5475afcc..d9453fec99e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1248,7 +1248,7 @@ pycsspeechtts==1.0.3 # pycups==1.9.73 # homeassistant.components.daikin -pydaikin==1.6.3 +pydaikin==2.0.0 # homeassistant.components.danfoss_air pydanfossair==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0a26be5d527..082b77e245c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -518,7 +518,7 @@ pychromecast==5.0.0 pycoolmasternet==0.0.4 # homeassistant.components.daikin -pydaikin==1.6.3 +pydaikin==2.0.0 # homeassistant.components.deconz pydeconz==70 diff --git a/tests/components/daikin/test_config_flow.py b/tests/components/daikin/test_config_flow.py index 996aef3db4b..db2de0bb268 100644 --- a/tests/components/daikin/test_config_flow.py +++ b/tests/components/daikin/test_config_flow.py @@ -9,7 +9,7 @@ from homeassistant.components.daikin import config_flow from homeassistant.components.daikin.const import KEY_IP, KEY_MAC from homeassistant.const import CONF_HOST -from tests.async_mock import patch +from tests.async_mock import PropertyMock, patch from tests.common import MockConfigEntry MAC = "AABBCCDDEEFF" @@ -27,13 +27,13 @@ def init_config_flow(hass): def mock_daikin(): """Mock pydaikin.""" - async def mock_daikin_init(): + async def mock_daikin_factory(*args, **kwargs): """Mock the init function in pydaikin.""" - pass + return Appliance with patch("homeassistant.components.daikin.config_flow.Appliance") as Appliance: - Appliance().values.get.return_value = "AABBCCDDEEFF" - Appliance().init = mock_daikin_init + type(Appliance).mac = PropertyMock(return_value="AABBCCDDEEFF") + Appliance.factory.side_effect = mock_daikin_factory yield Appliance @@ -95,7 +95,7 @@ async def test_discovery(hass, mock_daikin): async def test_device_abort(hass, mock_daikin, s_effect, reason): """Test device abort.""" flow = init_config_flow(hass) - mock_daikin.side_effect = s_effect + mock_daikin.factory.side_effect = s_effect result = await flow.async_step_user({CONF_HOST: HOST}) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT