Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c71a7b9
New configuration flow for ZHA integration (#35161)
Adminiuga May 6, 2020
d8222a8
Update pydaikin to 2.0.0 (#34807)
fredrike May 6, 2020
1b02410
Change Modbus switch to use verify_register when defined (#34679)
janiversen May 6, 2020
1b3f925
Add retry on empty modbus messages for serial protocol (#34678)
janiversen May 6, 2020
35d8890
Fix Islamic prayer sensor timestamp format (#35243)
engrbm87 May 6, 2020
3219c38
Provide zeroconf option to bind to only the default interface (#35281)
bdraco May 6, 2020
0b8f8db
Clean up script for WLED translations (#35260)
balloob May 6, 2020
19734e7
Refactor ONVIF (#35222)
hunterjm May 6, 2020
b353060
Upgrade zeroconf to 0.26.1 to resolve performance regression (#35291)
bdraco May 6, 2020
f1ecac9
Fail tests if wrapped callbacks or coroutines throw (#35010)
emontnemery May 6, 2020
81651b0
Add Synology DSM scan interval option flow (#35183)
Quentame May 6, 2020
5218b23
UniFi - Support SSID filter of SSIDs from access points with extra co…
Kane610 May 6, 2020
541b666
Reset imap email content state if no email (#35123)
isk0001y May 6, 2020
33077f0
Add config flow support to songpal integration (#34714)
shenxn May 6, 2020
1d6a202
Add state reproduction to the alert component (#35267)
bachya May 6, 2020
e3ca87b
Fix broken CI (#35314)
balloob May 7, 2020
eb826e5
Fix incorrect device registry call in Notion (#35306)
bachya May 7, 2020
64ce8e9
Fix SMS doing I/O in event loop (#35313)
balloob May 7, 2020
dfe2a45
Handle config with a limited Plex account (#35218)
jjlawren May 7, 2020
53f64ba
UniFi - Make devices proper push based (#35152)
Kane610 May 7, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,9 @@ omit =
homeassistant/components/onewire/sensor.py
homeassistant/components/onkyo/media_player.py
homeassistant/components/onvif/__init__.py
homeassistant/components/onvif/base.py
homeassistant/components/onvif/camera.py
homeassistant/components/onvif/device.py
homeassistant/components/opencv/*
homeassistant/components/openevse/sensor.py
homeassistant/components/openexchangerates/sensor.py
Expand Down Expand Up @@ -677,7 +679,8 @@ omit =
homeassistant/components/somfy/*
homeassistant/components/somfy_mylink/*
homeassistant/components/sonarr/sensor.py
homeassistant/components/songpal/*
homeassistant/components/songpal/__init__.py
homeassistant/components/songpal/media_player.py
homeassistant/components/sonos/*
homeassistant/components/sony_projector/switch.py
homeassistant/components/spc/*
Expand Down Expand Up @@ -860,7 +863,6 @@ omit =
homeassistant/components/zengge/light.py
homeassistant/components/zeroconf/*
homeassistant/components/zestimate/sensor.py
homeassistant/components/zha/__init__.py
homeassistant/components/zha/api.py
homeassistant/components/zha/core/channels/*
homeassistant/components/zha/core/const.py
Expand Down
2 changes: 1 addition & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ homeassistant/components/solax/* @squishykid
homeassistant/components/soma/* @ratsept
homeassistant/components/somfy/* @tetienne
homeassistant/components/sonarr/* @ctalkington
homeassistant/components/songpal/* @rytilahti
homeassistant/components/songpal/* @rytilahti @shenxn
homeassistant/components/sonos/* @amelchio
homeassistant/components/spaceapi/* @fabaff
homeassistant/components/speedtestdotnet/* @rohankapoorcom
Expand Down
76 changes: 76 additions & 0 deletions homeassistant/components/alert/reproduce_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""Reproduce an Alert state."""
import asyncio
import logging
from typing import Any, Dict, Iterable, Optional

from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
)
from homeassistant.core import Context, State
from homeassistant.helpers.typing import HomeAssistantType

from . import DOMAIN

_LOGGER = logging.getLogger(__name__)

VALID_STATES = {STATE_ON, STATE_OFF}


async def _async_reproduce_state(
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)

if cur_state is None:
_LOGGER.warning("Unable to find entity %s", state.entity_id)
return

if state.state not in VALID_STATES:
_LOGGER.warning(
"Invalid state specified for %s: %s", state.entity_id, state.state
)
return

# Return if we are already at the right state.
if cur_state.state == state.state:
return

service_data = {ATTR_ENTITY_ID: state.entity_id}

if state.state == STATE_ON:
service = SERVICE_TURN_ON

elif state.state == STATE_OFF:
service = SERVICE_TURN_OFF

await hass.services.async_call(
DOMAIN, service, service_data, context=context, blocking=True
)


async def async_reproduce_states(
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Alert states."""
# Reproduce states in parallel.
await asyncio.gather(
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)
36 changes: 18 additions & 18 deletions homeassistant/components/daikin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@

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__)

Expand Down Expand Up @@ -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})
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -135,20 +141,14 @@ def available(self) -> bool:
"""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("_", "."),
}
10 changes: 2 additions & 8 deletions homeassistant/components/daikin/climate.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -96,12 +95,7 @@ def __init__(self, api):
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
Expand Down Expand Up @@ -156,7 +150,7 @@ def name(self):
@property
def unique_id(self):
"""Return a unique ID."""
return self._api.mac
return self._api.device.mac

@property
def temperature_unit(self):
Expand Down
64 changes: 49 additions & 15 deletions homeassistant/components/daikin/config_flow.py
Original file line number Diff line number Diff line change
@@ -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__)

Expand All @@ -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:
Expand All @@ -49,16 +72,27 @@ async def _create_device(self, host):
_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."""
Expand All @@ -70,4 +104,4 @@ async def async_step_import(self, user_input):
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])
3 changes: 3 additions & 0 deletions homeassistant/components/daikin/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
},
}

CONF_KEY = "key"
CONF_UUID = "uuid"

KEY_MAC = "mac"
KEY_IP = "ip"

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/daikin/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
2 changes: 1 addition & 1 deletion homeassistant/components/daikin/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self, api, monitored_state) -> None:
@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):
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/daikin/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/daikin/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(self, daikin_api, zone_id):
@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):
Expand Down
Loading