Compare commits

...

20 Commits

Author SHA1 Message Date
Paulus Schoutsen
6c138e2982 Merge pull request #28456 from home-assistant/rc
101.2
2019-11-01 20:08:30 -07:00
Paulus Schoutsen
969b36a447 Bumped version to 0.101.2 2019-11-01 17:32:57 -07:00
Paulus Schoutsen
3f8dc5ed75 Also install after_deps (#28453) 2019-11-01 17:32:53 -07:00
jjlawren
fc7d43269c Use server-specific unique_ids for Plex media_players (#28447) 2019-11-01 17:32:52 -07:00
Robin Pronk
b88c0cf314 SNMP switch fix integer support (#28425) 2019-11-01 17:32:52 -07:00
Mister Wil
9e9537a3d0 Change Abode cache file path, add cache path to config flow (#28389)
* Changed cache file path

* Cache file naming scheme matches original

* Restart tests

* Adding cache path to config_flow.py

* Moved DEFAULT_CACHEDB to consts file

* Use correct cache path

* Linting issues
2019-11-01 17:32:51 -07:00
phispi
84e4e94d8e Prevent TypeError when KNX RGB(W) light value contains None (#28358)
* Prevent TypeError when KNX RGB(W) light value contains None.

* Pylint doesn't like 'w' as variable name, therefore using 'white' instead.

* Simplified code as suggested by pvizeli.
2019-11-01 17:32:51 -07:00
Paulus Schoutsen
c10e046323 Merge pull request #28399 from home-assistant/rc
0.101.1
2019-10-31 14:16:57 -07:00
Paulus Schoutsen
633d006554 Bumped version to 0.101.1 2019-10-31 12:11:35 -07:00
Paulus Schoutsen
4e2a8fde86 Check for import errors before validating config (#28395) 2019-10-31 12:11:30 -07:00
Paulus Schoutsen
d57fe0334f Fix check config (#28393) 2019-10-31 12:11:29 -07:00
Tsvi Mostovicz
75b8070c99 Fix hdate spamming homeassistant log (#28392)
* Fix hdate spamming homeassistant log

* Lower verbosity of another spammy message
2019-10-31 12:11:28 -07:00
Maciej Bieniek
b71e1affdb Fix Airly asyncio timeout error (#28387)
* Raise ConfigEntryNotReady

* Better asyncio.TimeoutError handling

* Sort imports

* Increase asyncio timeout
2019-10-31 12:11:28 -07:00
Steve M
d92060a461 Bump env_canada to fixed 0.0.29 version (#28360)
* Bump env_canada to fixed 0.0.29 version

* bump env_canada to 0.29
2019-10-31 12:09:43 -07:00
fredericvl
3c9482b2d3 Bump pysaj to v0.0.13 (fix for sensor date) (#28351) 2019-10-31 12:09:43 -07:00
Aaron Bach
5571c0c60a Bump pymyq to 2.0.1 (#28348) 2019-10-31 12:09:43 -07:00
Teemu R
54481598b7 Bump songpal to fix a regression (#28115)
The new release fixes a single regression from requests to aiohttp conversion.
Some devices do not respond with the correct mimetype which was not enforced
by requests but is enforced by aiohttp.

Related PR https://github.com/rytilahti/python-songpal/pull/59
2019-10-31 12:09:43 -07:00
Pascal Vizeli
2d7208470e Merge pull request #28354 from home-assistant/rc
0.101.0
2019-10-30 21:20:32 +01:00
Pascal Vizeli
7eceedea10 Bump version 0.101.0 2019-10-30 19:50:48 +00:00
springstan
8aee92347f Fix KeyError in decora setup (#28279)
* Imported homeassistant.util and slugified address if no name is specified

* Added a custom validator function in case name is not set in config

* Removed logger.debug line only used for testing
2019-10-30 19:47:14 +00:00
21 changed files with 154 additions and 58 deletions

View File

@@ -23,14 +23,12 @@ from homeassistant.const import (
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity
from .const import ATTRIBUTION, DOMAIN
from .const import ATTRIBUTION, DOMAIN, DEFAULT_CACHEDB
_LOGGER = logging.getLogger(__name__)
CONF_POLLING = "polling"
DEFAULT_CACHEDB = "./abodepy_cache.pickle"
SERVICE_SETTINGS = "change_setting"
SERVICE_CAPTURE_IMAGE = "capture_image"
SERVICE_TRIGGER = "trigger_quick_action"

View File

@@ -10,7 +10,7 @@ from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from .const import DOMAIN # pylint: disable=W0611
from .const import DOMAIN, DEFAULT_CACHEDB # pylint: disable=W0611
CONF_POLLING = "polling"
@@ -42,9 +42,12 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
username = user_input[CONF_USERNAME]
password = user_input[CONF_PASSWORD]
polling = user_input.get(CONF_POLLING, False)
cache = self.hass.config.path(DEFAULT_CACHEDB)
try:
await self.hass.async_add_executor_job(Abode, username, password, True)
await self.hass.async_add_executor_job(
Abode, username, password, True, True, True, cache
)
except (AbodeException, ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Abode: %s", str(ex))

View File

@@ -1,3 +1,5 @@
"""Constants for the Abode Security System component."""
DOMAIN = "abode"
ATTRIBUTION = "Data provided by goabode.com"
DEFAULT_CACHEDB = "abodepy_cache.pickle"

View File

@@ -1,15 +1,16 @@
"""The Airly component."""
import asyncio
import logging
from datetime import timedelta
import logging
import async_timeout
from aiohttp.client_exceptions import ClientConnectorError
from airly import Airly
from airly.exceptions import AirlyError
import async_timeout
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import Throttle
@@ -45,6 +46,9 @@ async def async_setup_entry(hass, config_entry):
await airly.async_update()
if not airly.data:
raise ConfigEntryNotReady()
hass.data[DOMAIN] = {}
hass.data[DOMAIN][DATA_CLIENT] = {}
hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = airly
@@ -81,7 +85,7 @@ class AirlyData:
"""Update Airly data."""
try:
with async_timeout.timeout(10):
with async_timeout.timeout(20):
measurements = self.airly.create_measurements_session_point(
self.latitude, self.longitude
)
@@ -104,11 +108,8 @@ class AirlyData:
self.data[ATTR_API_CAQI_DESCRIPTION] = index["description"]
self.data[ATTR_API_ADVICE] = index["advice"]
_LOGGER.debug("Data retrieved from Airly")
except (
ValueError,
AirlyError,
asyncio.TimeoutError,
ClientConnectorError,
) as error:
except asyncio.TimeoutError:
_LOGGER.error("Asyncio Timeout Error")
except (ValueError, AirlyError, ClientConnectorError) as error:
_LOGGER.error(error)
self.data = {}

View File

@@ -1,4 +1,5 @@
"""Support for Decora dimmers."""
import copy
from functools import wraps
import logging
import time
@@ -15,17 +16,34 @@ from homeassistant.components.light import (
)
from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME
import homeassistant.helpers.config_validation as cv
import homeassistant.util as util
_LOGGER = logging.getLogger(__name__)
SUPPORT_DECORA_LED = SUPPORT_BRIGHTNESS
def _name_validator(config):
"""Validate the name."""
config = copy.deepcopy(config)
for address, device_config in config[CONF_DEVICES].items():
if CONF_NAME not in device_config:
device_config[CONF_NAME] = util.slugify(address)
return config
DEVICE_SCHEMA = vol.Schema(
{vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_API_KEY): cv.string}
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}}
PLATFORM_SCHEMA = vol.Schema(
vol.All(
PLATFORM_SCHEMA.extend(
{vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}}
),
_name_validator,
)
)

View File

@@ -2,11 +2,7 @@
"domain": "environment_canada",
"name": "Environment Canada",
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
"requirements": [
"env_canada==0.0.27"
],
"requirements": ["env_canada==0.0.29"],
"dependencies": [],
"codeowners": [
"@michaeldavie"
]
"codeowners": ["@michaeldavie"]
}

View File

@@ -3,7 +3,7 @@
"name": "Jewish calendar",
"documentation": "https://www.home-assistant.io/integrations/jewish_calendar",
"requirements": [
"hdate==0.9.1"
"hdate==0.9.3"
],
"dependencies": [],
"codeowners": [

View File

@@ -180,13 +180,9 @@ class KNXLight(Light):
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
if self.device.supports_brightness:
return self.device.current_brightness
if (
self.device.supports_color or self.device.supports_rgbw
) and self.device.current_color:
return max(self.device.current_color)
return None
if not self.device.supports_brightness:
return None
return self.device.current_brightness
@property
def hs_color(self):

View File

@@ -3,7 +3,7 @@
"name": "Myq",
"documentation": "https://www.home-assistant.io/integrations/myq",
"requirements": [
"pymyq==2.0.0"
"pymyq==2.0.1"
],
"dependencies": [],
"codeowners": []

View File

@@ -6,7 +6,7 @@ from xml.etree.ElementTree import ParseError
import plexapi.exceptions
import requests.exceptions
from homeassistant.components.media_player import MediaPlayerDevice
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN, MediaPlayerDevice
from homeassistant.components.media_player.const import (
MEDIA_TYPE_MOVIE,
MEDIA_TYPE_MUSIC,
@@ -30,6 +30,7 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_registry import async_get_registry
from homeassistant.util import dt as dt_util
from .const import (
@@ -56,10 +57,11 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Plex media_player from a config entry."""
server_id = config_entry.data[CONF_SERVER_IDENTIFIER]
registry = await async_get_registry(hass)
def async_new_media_players(new_entities):
_async_add_entities(
hass, config_entry, async_add_entities, server_id, new_entities
hass, registry, config_entry, async_add_entities, server_id, new_entities
)
unsub = async_dispatcher_connect(
@@ -70,7 +72,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
@callback
def _async_add_entities(
hass, config_entry, async_add_entities, server_id, new_entities
hass, registry, config_entry, async_add_entities, server_id, new_entities
):
"""Set up Plex media_player entities."""
entities = []
@@ -79,6 +81,19 @@ def _async_add_entities(
plex_mp = PlexMediaPlayer(plexserver, **entity_params)
entities.append(plex_mp)
# Migration to per-server unique_ids
old_entity_id = registry.async_get_entity_id(
MP_DOMAIN, PLEX_DOMAIN, plex_mp.machine_identifier
)
if old_entity_id is not None:
new_unique_id = f"{server_id}:{plex_mp.machine_identifier}"
_LOGGER.debug(
"Migrating unique_id from [%s] to [%s]",
plex_mp.machine_identifier,
new_unique_id,
)
registry.async_update_entity(old_entity_id, new_unique_id=new_unique_id)
async_add_entities(entities, True)
@@ -126,6 +141,7 @@ class PlexMediaPlayer(MediaPlayerDevice):
async def async_added_to_hass(self):
"""Run when about to be added to hass."""
server_id = self.plex_server.machine_identifier
unsub = async_dispatcher_connect(
self.hass,
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL.format(self.unique_id),
@@ -315,6 +331,11 @@ class PlexMediaPlayer(MediaPlayerDevice):
@property
def unique_id(self):
"""Return the id of this plex client."""
return f"{self.plex_server.machine_identifier}:{self._machine_identifier}"
@property
def machine_identifier(self):
"""Return the Plex-provided identifier of this plex client."""
return self._machine_identifier
@property

View File

@@ -94,9 +94,10 @@ class PlexServer:
def refresh_entity(self, machine_identifier, device, session):
"""Forward refresh dispatch to media_player."""
unique_id = f"{self.machine_identifier}:{machine_identifier}"
dispatcher_send(
self._hass,
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL.format(machine_identifier),
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL.format(unique_id),
device,
session,
)

View File

@@ -3,7 +3,7 @@
"name": "SAJ",
"documentation": "https://www.home-assistant.io/integrations/saj",
"requirements": [
"pysaj==0.0.12"
"pysaj==0.0.13"
],
"dependencies": [],
"codeowners": [

View File

@@ -1,6 +1,8 @@
"""Support for SNMP enabled switch."""
import logging
from pyasn1.type.univ import Integer
import pysnmp.hlapi.asyncio as hlapi
from pysnmp.hlapi.asyncio import (
CommunityData,
@@ -190,15 +192,20 @@ class SnmpSwitch(SwitchDevice):
async def async_turn_on(self, **kwargs):
"""Turn on the switch."""
await self._set(self._command_payload_on)
if self._command_payload_on.isdigit():
await self._set(Integer(self._command_payload_on))
else:
await self._set(self._command_payload_on)
async def async_turn_off(self, **kwargs):
"""Turn off the switch."""
await self._set(self._command_payload_off)
if self._command_payload_on.isdigit():
await self._set(Integer(self._command_payload_off))
else:
await self._set(self._command_payload_off)
async def async_update(self):
"""Update the state."""
errindication, errstatus, errindex, restable = await getCmd(
*self._request_args, ObjectType(ObjectIdentity(self._baseoid))
)
@@ -215,8 +222,12 @@ class SnmpSwitch(SwitchDevice):
for resrow in restable:
if resrow[-1] == self._payload_on:
self._state = True
elif resrow[-1] == Integer(self._payload_on):
self._state = True
elif resrow[-1] == self._payload_off:
self._state = False
elif resrow[-1] == Integer(self._payload_off):
self._state = False
else:
self._state = None

View File

@@ -3,7 +3,7 @@
"name": "Songpal",
"documentation": "https://www.home-assistant.io/integrations/songpal",
"requirements": [
"python-songpal==0.11.1"
"python-songpal==0.11.2"
],
"dependencies": [],
"codeowners": [

View File

@@ -1,7 +1,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 101
PATCH_VERSION = "0b4"
PATCH_VERSION = "2"
__short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION)
__version__ = "{}.{}".format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 6, 1)

View File

@@ -35,13 +35,25 @@ async def async_get_integration_with_requirements(
This can raise IntegrationNotFound if manifest or integration
is invalid, RequirementNotFound if there was some type of
failure to install requirements.
Does not handle circular dependencies.
"""
integration = await async_get_integration(hass, domain)
if hass.config.skip_pip or not integration.requirements:
if hass.config.skip_pip:
return integration
await async_process_requirements(hass, integration.domain, integration.requirements)
if integration.requirements:
await async_process_requirements(
hass, integration.domain, integration.requirements
)
deps = integration.dependencies + (integration.after_dependencies or [])
if deps:
await asyncio.gather(
*[async_get_integration_with_requirements(hass, dep) for dep in deps]
)
return integration

View File

@@ -132,6 +132,17 @@ async def _async_setup_component(
log_error(str(err))
return False
# Some integrations fail on import because they call functions incorrectly.
# So we do it before validating config to catch these errors.
try:
component = integration.get_component()
except ImportError:
log_error("Unable to import component", False)
return False
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Setup failed for %s: unknown error", domain)
return False
processed_config = await conf_util.async_process_component_config(
hass, config, integration
)
@@ -143,12 +154,6 @@ async def _async_setup_component(
start = timer()
_LOGGER.info("Setting up %s", domain)
try:
component = integration.get_component()
except ImportError:
log_error("Unable to import component", False)
return False
if hasattr(component, "PLATFORM_SCHEMA"):
# Entity components have their own warning
warn_task = None

View File

@@ -456,7 +456,7 @@ enocean==0.50
enturclient==0.2.0
# homeassistant.components.environment_canada
env_canada==0.0.27
env_canada==0.0.29
# homeassistant.components.envirophat
# envirophat==0.0.6
@@ -622,7 +622,7 @@ hass-nabucasa==0.22
hbmqtt==0.9.5
# homeassistant.components.jewish_calendar
hdate==0.9.1
hdate==0.9.3
# homeassistant.components.heatmiser
heatmiserV3==0.9.1
@@ -1328,7 +1328,7 @@ pymsteams==0.1.12
pymusiccast==0.1.6
# homeassistant.components.myq
pymyq==2.0.0
pymyq==2.0.1
# homeassistant.components.mysensors
pymysensors==0.18.0
@@ -1423,7 +1423,7 @@ pyrepetier==3.0.5
pysabnzbd==1.1.0
# homeassistant.components.saj
pysaj==0.0.12
pysaj==0.0.13
# homeassistant.components.sony_projector
pysdcp==1
@@ -1564,7 +1564,7 @@ python-ripple-api==0.0.3
python-sochain-api==0.0.2
# homeassistant.components.songpal
python-songpal==0.11.1
python-songpal==0.11.2
# homeassistant.components.synologydsm
python-synology==0.2.0

View File

@@ -230,7 +230,7 @@ hass-nabucasa==0.22
hbmqtt==0.9.5
# homeassistant.components.jewish_calendar
hdate==0.9.1
hdate==0.9.3
# homeassistant.components.here_travel_time
herepy==0.6.3.1

View File

@@ -112,7 +112,24 @@ async def test_install_missing_package(hass):
async def test_get_integration_with_requirements(hass):
"""Check getting an integration with loaded requirements."""
hass.config.skip_pip = False
mock_integration(hass, MockModule("test_component", requirements=["hello==1.0.0"]))
mock_integration(
hass, MockModule("test_component_dep", requirements=["test-comp-dep==1.0.0"])
)
mock_integration(
hass,
MockModule(
"test_component_after_dep", requirements=["test-comp-after-dep==1.0.0"]
),
)
mock_integration(
hass,
MockModule(
"test_component",
requirements=["test-comp==1.0.0"],
dependencies=["test_component_dep"],
partial_manifest={"after_dependencies": ["test_component_after_dep"]},
),
)
with patch(
"homeassistant.util.package.is_installed", return_value=False
@@ -126,8 +143,15 @@ async def test_get_integration_with_requirements(hass):
assert integration
assert integration.domain == "test_component"
assert len(mock_is_installed.mock_calls) == 1
assert len(mock_inst.mock_calls) == 1
assert len(mock_is_installed.mock_calls) == 3
assert mock_is_installed.mock_calls[0][1][0] == "test-comp==1.0.0"
assert mock_is_installed.mock_calls[1][1][0] == "test-comp-dep==1.0.0"
assert mock_is_installed.mock_calls[2][1][0] == "test-comp-after-dep==1.0.0"
assert len(mock_inst.mock_calls) == 3
assert mock_inst.mock_calls[0][1][0] == "test-comp==1.0.0"
assert mock_inst.mock_calls[1][1][0] == "test-comp-dep==1.0.0"
assert mock_inst.mock_calls[2][1][0] == "test-comp-after-dep==1.0.0"
async def test_install_with_wheels_index(hass):

View File

@@ -527,3 +527,11 @@ async def test_when_setup_already_loaded(hass):
setup.async_when_setup(hass, "test", mock_callback)
await hass.async_block_till_done()
assert calls == ["test", "test"]
async def test_setup_import_blows_up(hass):
"""Test that we handle it correctly when importing integration blows up."""
with mock.patch(
"homeassistant.loader.Integration.get_component", side_effect=ValueError
):
assert not await setup.async_setup_component(hass, "sun", {})