Update pydaikin to 2.0.0 (#34807)

This commit is contained in:
Fredrik Erlandsson 2020-05-06 13:59:07 +02:00 committed by GitHub
parent c71a7b901f
commit d8222a8eb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 91 additions and 54 deletions

View file

@ -5,19 +5,18 @@ import logging
from aiohttp import ClientConnectionError from aiohttp import ClientConnectionError
from async_timeout import timeout from async_timeout import timeout
from pydaikin.appliance import Appliance from pydaikin.daikin_base import Appliance
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry 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 from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import Throttle from homeassistant.util import Throttle
from . import config_flow # noqa: F401 from . import config_flow # noqa: F401
from .const import TIMEOUT from .const import CONF_KEY, CONF_UUID, TIMEOUT
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -62,7 +61,13 @@ async def async_setup(hass, config):
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Establish connection with Daikin.""" """Establish connection with Daikin."""
conf = entry.data 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: if not daikin_api:
return False return False
hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api}) hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api})
@ -87,14 +92,15 @@ async def async_unload_entry(hass, config_entry):
return True 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.""" """Create a Daikin instance only once."""
session = hass.helpers.aiohttp_client.async_get_clientsession() session = hass.helpers.aiohttp_client.async_get_clientsession()
try: try:
with timeout(TIMEOUT): with timeout(TIMEOUT):
device = Appliance(host, session) device = await Appliance.factory(
await device.init() host, session, key=key, uuid=uuid, password=password
)
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.debug("Connection to %s timed out", host) _LOGGER.debug("Connection to %s timed out", host)
raise ConfigEntryNotReady raise ConfigEntryNotReady
@ -116,8 +122,8 @@ class DaikinApi:
def __init__(self, device): def __init__(self, device):
"""Initialize the Daikin Handle.""" """Initialize the Daikin Handle."""
self.device = device self.device = device
self.name = device.values["name"] self.name = device.values.get("name", "Daikin AC")
self.ip_address = device.ip self.ip_address = device.device_ip
self._available = True self._available = True
@Throttle(MIN_TIME_BETWEEN_UPDATES) @Throttle(MIN_TIME_BETWEEN_UPDATES)
@ -135,20 +141,14 @@ class DaikinApi:
"""Return True if entity is available.""" """Return True if entity is available."""
return self._available return self._available
@property
def mac(self):
"""Return mac-address of device."""
return self.device.values.get(CONNECTION_NETWORK_MAC)
@property @property
def device_info(self): def device_info(self):
"""Return a device description for device registry.""" """Return a device description for device registry."""
info = self.device.values info = self.device.values
return { return {
"connections": {(CONNECTION_NETWORK_MAC, self.mac)}, "identifieres": self.device.mac,
"identifieres": self.mac,
"manufacturer": "Daikin", "manufacturer": "Daikin",
"model": info.get("model"), "model": info.get("model"),
"name": info.get("name"), "name": info.get("name"),
"sw_version": info.get("ver").replace("_", "."), "sw_version": info.get("ver", "").replace("_", "."),
} }

View file

@ -1,7 +1,6 @@
"""Support for the Daikin HVAC.""" """Support for the Daikin HVAC."""
import logging import logging
from pydaikin import appliance
import voluptuous as vol import voluptuous as vol
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
@ -96,12 +95,7 @@ class DaikinClimate(ClimateEntity):
self._list = { self._list = {
ATTR_HVAC_MODE: list(HA_STATE_TO_DAIKIN), ATTR_HVAC_MODE: list(HA_STATE_TO_DAIKIN),
ATTR_FAN_MODE: self._api.device.fan_rate, ATTR_FAN_MODE: self._api.device.fan_rate,
ATTR_SWING_MODE: list( ATTR_SWING_MODE: self._api.device.swing_modes,
map(
str.title,
appliance.daikin_values(HA_ATTR_TO_DAIKIN[ATTR_SWING_MODE]),
)
),
} }
self._supported_features = SUPPORT_TARGET_TEMPERATURE self._supported_features = SUPPORT_TARGET_TEMPERATURE
@ -156,7 +150,7 @@ class DaikinClimate(ClimateEntity):
@property @property
def unique_id(self): def unique_id(self):
"""Return a unique ID.""" """Return a unique ID."""
return self._api.mac return self._api.device.mac
@property @property
def temperature_unit(self): def temperature_unit(self):

View file

@ -1,16 +1,17 @@
"""Config flow for the Daikin platform.""" """Config flow for the Daikin platform."""
import asyncio import asyncio
import logging import logging
from uuid import uuid4
from aiohttp import ClientError from aiohttp import ClientError
from async_timeout import timeout from async_timeout import timeout
from pydaikin.appliance import Appliance from pydaikin.daikin_base import Appliance
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries 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__) _LOGGER = logging.getLogger(__name__)
@ -22,24 +23,46 @@ class FlowHandler(config_entries.ConfigFlow):
VERSION = 1 VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL 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.""" """Register new entry."""
# Check if mac already is registered # Check if mac already is registered
for entry in self._async_current_entries(): for entry in self._async_current_entries():
if entry.data[KEY_MAC] == mac: if entry.data[KEY_MAC] == mac:
return self.async_abort(reason="already_configured") 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.""" """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: try:
device = Appliance(
host, self.hass.helpers.aiohttp_client.async_get_clientsession()
)
with timeout(TIMEOUT): 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: except asyncio.TimeoutError:
return self.async_abort(reason="device_timeout") return self.async_abort(reason="device_timeout")
except ClientError: except ClientError:
@ -49,16 +72,27 @@ class FlowHandler(config_entries.ConfigFlow):
_LOGGER.exception("Unexpected error creating device") _LOGGER.exception("Unexpected error creating device")
return self.async_abort(reason="device_fail") return self.async_abort(reason="device_fail")
mac = device.values.get("mac") mac = device.mac
return await self._create_entry(host, mac) return self._create_entry(host, mac, key, uuid, password)
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""User initiated config flow.""" """User initiated config flow."""
if user_input is None: if user_input is None:
return self.async_show_form( 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],
user_input.get(CONF_KEY),
user_input.get(CONF_PASSWORD),
) )
return await self._create_device(user_input[CONF_HOST])
async def async_step_import(self, user_input): async def async_step_import(self, user_input):
"""Import a config entry.""" """Import a config entry."""
@ -70,4 +104,4 @@ class FlowHandler(config_entries.ConfigFlow):
async def async_step_discovery(self, user_input): async def async_step_discovery(self, user_input):
"""Initialize step from discovery.""" """Initialize step from discovery."""
_LOGGER.info("Discovered device: %s", user_input) _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])

View file

@ -23,6 +23,9 @@ SENSOR_TYPES = {
}, },
} }
CONF_KEY = "key"
CONF_UUID = "uuid"
KEY_MAC = "mac" KEY_MAC = "mac"
KEY_IP = "ip" KEY_IP = "ip"

View file

@ -3,7 +3,7 @@
"name": "Daikin AC", "name": "Daikin AC",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/daikin", "documentation": "https://www.home-assistant.io/integrations/daikin",
"requirements": ["pydaikin==1.6.3"], "requirements": ["pydaikin==2.0.0"],
"codeowners": ["@fredrike"], "codeowners": ["@fredrike"],
"quality_scale": "platinum" "quality_scale": "platinum"
} }

View file

@ -40,7 +40,7 @@ class DaikinClimateSensor(Entity):
@property @property
def unique_id(self): def unique_id(self):
"""Return a unique ID.""" """Return a unique ID."""
return f"{self._api.mac}-{self._device_attribute}" return f"{self._api.device.mac}-{self._device_attribute}"
@property @property
def icon(self): def icon(self):

View file

@ -4,7 +4,11 @@
"user": { "user": {
"title": "Configure Daikin AC", "title": "Configure Daikin AC",
"description": "Enter IP address of your 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": { "abort": {

View file

@ -43,7 +43,7 @@ class DaikinZoneSwitch(ToggleEntity):
@property @property
def unique_id(self): def unique_id(self):
"""Return a unique ID.""" """Return a unique ID."""
return f"{self._api.mac}-zone{self._zone_id}" return f"{self._api.device.mac}-zone{self._zone_id}"
@property @property
def icon(self): def icon(self):

View file

@ -8,7 +8,9 @@
"step": { "step": {
"user": { "user": {
"data": { "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.", "description": "Enter IP address of your Daikin AC.",
"title": "Configure Daikin AC" "title": "Configure Daikin AC"

View file

@ -1248,7 +1248,7 @@ pycsspeechtts==1.0.3
# pycups==1.9.73 # pycups==1.9.73
# homeassistant.components.daikin # homeassistant.components.daikin
pydaikin==1.6.3 pydaikin==2.0.0
# homeassistant.components.danfoss_air # homeassistant.components.danfoss_air
pydanfossair==0.1.0 pydanfossair==0.1.0

View file

@ -518,7 +518,7 @@ pychromecast==5.0.0
pycoolmasternet==0.0.4 pycoolmasternet==0.0.4
# homeassistant.components.daikin # homeassistant.components.daikin
pydaikin==1.6.3 pydaikin==2.0.0
# homeassistant.components.deconz # homeassistant.components.deconz
pydeconz==70 pydeconz==70

View file

@ -9,7 +9,7 @@ from homeassistant.components.daikin import config_flow
from homeassistant.components.daikin.const import KEY_IP, KEY_MAC from homeassistant.components.daikin.const import KEY_IP, KEY_MAC
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from tests.async_mock import patch from tests.async_mock import PropertyMock, patch
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
MAC = "AABBCCDDEEFF" MAC = "AABBCCDDEEFF"
@ -27,13 +27,13 @@ def init_config_flow(hass):
def mock_daikin(): def mock_daikin():
"""Mock pydaikin.""" """Mock pydaikin."""
async def mock_daikin_init(): async def mock_daikin_factory(*args, **kwargs):
"""Mock the init function in pydaikin.""" """Mock the init function in pydaikin."""
pass return Appliance
with patch("homeassistant.components.daikin.config_flow.Appliance") as Appliance: with patch("homeassistant.components.daikin.config_flow.Appliance") as Appliance:
Appliance().values.get.return_value = "AABBCCDDEEFF" type(Appliance).mac = PropertyMock(return_value="AABBCCDDEEFF")
Appliance().init = mock_daikin_init Appliance.factory.side_effect = mock_daikin_factory
yield Appliance yield Appliance
@ -95,7 +95,7 @@ async def test_discovery(hass, mock_daikin):
async def test_device_abort(hass, mock_daikin, s_effect, reason): async def test_device_abort(hass, mock_daikin, s_effect, reason):
"""Test device abort.""" """Test device abort."""
flow = init_config_flow(hass) 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}) result = await flow.async_step_user({CONF_HOST: HOST})
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT