Compare commits

..

28 Commits

Author SHA1 Message Date
Pascal Vizeli
baf7fb7264 Merge pull request #33206 from home-assistant/rc
0.107.6
2020-03-24 14:08:27 +01:00
Pascal Vizeli
edfe8e1583 Bump version to 0.107.6 2020-03-24 11:50:25 +00:00
Pascal Vizeli
b36b1dbc70 Bump OZW fork to 0.1.10 (#33205) 2020-03-24 11:49:35 +00:00
Franck Nijhof
253c848692 Fix minut point updating frozen config entry data (#33148)
* Fix minut point updating frozen config entry data

* Update webhooks handling for configuration entry
2020-03-24 11:49:33 +00:00
Paulus Schoutsen
7a6ac578b4 Fix script logging with name (#33120) 2020-03-24 11:49:32 +00:00
Pascal Vizeli
95de94e53f Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 16:48:44 +00:00
Pascal Vizeli
181b2803cd Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 15:56:50 +00:00
Pascal Vizeli
e0f2fa33df [skip ci] Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 13:19:14 +00:00
Pascal Vizeli
884c346bdf Fix dockerfile 2020-03-23 13:19:02 +00:00
Pascal Vizeli
c218ff5a75 Integrate dockerbuild (#33168)
* Integrate dockerbuild

* cleanup
2020-03-23 13:18:48 +00:00
Pascal Vizeli
fa43a218d2 Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 13:18:33 +00:00
Pascal Vizeli
f4cc64d289 [skip ci] add rc into build 2020-03-23 13:18:18 +00:00
Pascal Vizeli
4c31829832 [skip ci] update wheels builder version 2020-03-23 13:18:00 +00:00
Pascal Vizeli
ff32c1c3e9 Update azure-pipelines-wheels.yml 2020-03-23 13:17:49 +00:00
Paulus Schoutsen
677c276b41 Merge pull request #33118 from home-assistant/rc
0.107.5
2020-03-21 17:22:31 -07:00
Paulus Schoutsen
ca1c696f54 Bumped version to 0.107.5 2020-03-21 16:34:11 -07:00
Franck Nijhof
78e5878247 Fix Extend ONVIF unique ID with profile index (#33103) 2020-03-21 16:34:05 -07:00
guillempages
470537bc5f Fix tankerkoenig with more than 10 stations (#33098)
* Fix tankerkoenig with more than 10 stations

There seemed to be a problem, if more than 10 fuel stations were tracked.
Added a warning in this case, and split the calls to the API in chunks of
10, so that the data can be fetched anyway.

* Update homeassistant/components/tankerkoenig/__init__.py

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-03-21 16:33:55 -07:00
Austin Mroczek
64556f6f69 Fix totalconnect AttributeError introduced in 0.107 (#33079)
* Fit git

* Remove period from loggging message

* Remove tests for now

* Add const.py

* Fix lint error
2020-03-21 16:32:26 -07:00
Alexei Chetroi
2785b067e3 Split ZHA device loading and entities adding (#33075)
* Split ZHA device loading and entities adding.
Load zha devices from ZHA gateway, but add entities after integration
was setup.

* Use hass.loop.create_task()
* Split ZHA device initialization from loading.
Restore and initialize ZHA devices separately.

* Use hass.async_create_task()
Load devices prior group initialization.
2020-03-21 16:32:26 -07:00
escoand
a129bc05ae Try all Samsung TV websocket ports (#33001)
* Update bridge.py

* add test

* silence pylint

* correct pylint

* add some tests

* Update test_media_player.py
2020-03-21 16:32:25 -07:00
Ville Skyttä
197736f66b Upgrade huawei-lte-api to 1.4.11 (#32791)
https://github.com/Salamek/huawei-lte-api/releases/tag/1.4.11
2020-03-21 15:16:33 -07:00
Franck Nijhof
d82d7fa2e9 Merge pull request #33080 from home-assistant/rc
0.107.4
2020-03-21 07:51:18 +01:00
Paulus Schoutsen
663db747e9 Bumped version to 0.107.4 2020-03-20 23:02:50 -07:00
Paulus Schoutsen
57998f6f0f Fix package default extraction (#33071) 2020-03-20 23:02:29 -07:00
Knapoc
edbb995fff Bump aioasuswrt to 1.2.3 and fix asuswrt sensor (#33064)
* Bump aioasuswrt to 1.2.3

* Fix asuswrt connection setup parameters

* fix typo
2020-03-20 23:02:29 -07:00
Aaron Bach
312903025d Bump simplisafe-python to 9.0.4 (#33059) 2020-03-20 23:02:28 -07:00
Paulus Schoutsen
0ae5c325fe Add negative tests for identify schema for packages (#33050) 2020-03-20 23:02:27 -07:00
38 changed files with 373 additions and 148 deletions

View File

@@ -2,9 +2,15 @@
.git
.github
config
docs
# Development
.devcontainer
.vscode
# Test related files
.tox
tests
# Other virtualization methods
venv

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
ARG BUILD_FROM
FROM ${BUILD_FROM}
WORKDIR /usr/src
## Setup Home Assistant
COPY . homeassistant/
RUN pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
-r homeassistant/requirements_all.txt -c homeassistant/homeassistant/package_constraints.txt \
&& pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
-e ./homeassistant \
&& python3 -m compileall homeassistant/homeassistant
# Home Assistant S6-Overlay
COPY rootfs /
WORKDIR /config

View File

@@ -14,7 +14,7 @@ schedules:
always: true
variables:
- name: versionBuilder
value: '6.9'
value: '7.2.0'
- group: docker
- group: github
- group: twine
@@ -108,11 +108,9 @@ stages:
docker run --rm --privileged \
-v ~/.docker:/root/.docker:rw \
-v /run/docker.sock:/run/docker.sock:rw \
-v $(pwd):/homeassistant:ro \
-v $(pwd):/data:ro \
homeassistant/amd64-builder:$(versionBuilder) \
--homeassistant $(homeassistantRelease) "--$(buildArch)" \
-r https://github.com/home-assistant/hassio-homeassistant \
-t generic --docker-hub homeassistant
--generic $(homeassistantRelease) "--$(buildArch)" -t /data \
docker run --rm --privileged \
-v ~/.docker:/root/.docker \

View File

@@ -5,6 +5,7 @@ trigger:
branches:
include:
- dev
- rc
paths:
include:
- requirements_all.txt
@@ -18,7 +19,7 @@ schedules:
always: true
variables:
- name: versionWheels
value: '1.4-3.7-alpine3.10'
value: '1.10.1-3.7-alpine3.11'
resources:
repositories:
- repository: azure
@@ -32,8 +33,10 @@ jobs:
builderVersion: '$(versionWheels)'
builderApk: 'build-base;cmake;git;linux-headers;bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;autoconf;automake;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev'
builderPip: 'Cython;numpy'
skipBinary: 'aiohttp'
wheelsRequirement: 'requirements_wheels.txt'
wheelsRequirementDiff: 'requirements_diff.txt'
wheelsConstraint: 'homeassistant/package_constraints.txt'
preBuild:
- script: |
cp requirements_all.txt requirements_wheels.txt
@@ -69,9 +72,5 @@ jobs:
sed -i "s|# py_noaa|py_noaa|g" ${requirement_file}
sed -i "s|# bme680|bme680|g" ${requirement_file}
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
if [[ "$(buildArch)" =~ arm ]]; then
sed -i "s|# VL53L1X|VL53L1X|g" ${requirement_file}
fi
done
displayName: 'Prepare requirements files for Hass.io'

14
build.json Normal file
View File

@@ -0,0 +1,14 @@
{
"image": "homeassistant/{arch}-homeassistant",
"build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:7.0.1",
"armhf": "homeassistant/armhf-homeassistant-base:7.0.1",
"armv7": "homeassistant/armv7-homeassistant-base:7.0.1",
"amd64": "homeassistant/amd64-homeassistant-base:7.0.1",
"i386": "homeassistant/i386-homeassistant-base:7.0.1"
},
"labels": {
"io.hass.type": "core"
},
"version_tag": true
}

View File

@@ -73,8 +73,8 @@ async def async_setup(hass, config):
conf.get("ssh_key", conf.get("pub_key", "")),
conf[CONF_MODE],
conf[CONF_REQUIRE_IP],
conf[CONF_INTERFACE],
conf[CONF_DNSMASQ],
interface=conf[CONF_INTERFACE],
dnsmasq=conf[CONF_DNSMASQ],
)
await api.connection.async_connect()

View File

@@ -2,7 +2,7 @@
"domain": "asuswrt",
"name": "ASUSWRT",
"documentation": "https://www.home-assistant.io/integrations/asuswrt",
"requirements": ["aioasuswrt==1.2.2"],
"requirements": ["aioasuswrt==1.2.3"],
"dependencies": [],
"codeowners": ["@kennedyshead"]
}

View File

@@ -5,7 +5,7 @@
"documentation": "https://www.home-assistant.io/integrations/huawei_lte",
"requirements": [
"getmac==0.8.1",
"huawei-lte-api==1.4.10",
"huawei-lte-api==1.4.11",
"stringcase==1.2.0",
"url-normalize==1.4.1"
],

View File

@@ -505,4 +505,6 @@ class ONVIFHassCamera(Camera):
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
if self._profile_index:
return f"{self._mac}_{self._profile_index}"
return self._mac

View File

@@ -75,8 +75,9 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
def token_saver(token):
_LOGGER.debug("Saving updated token")
entry.data[CONF_TOKEN] = token
hass.config_entries.async_update_entry(entry, data={**entry.data})
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_TOKEN: token}
)
# Force token update.
entry.data[CONF_TOKEN]["expires_in"] = -1
@@ -105,12 +106,18 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
async def async_setup_webhook(hass: HomeAssistantType, entry: ConfigEntry, session):
"""Set up a webhook to handle binary sensor events."""
if CONF_WEBHOOK_ID not in entry.data:
entry.data[CONF_WEBHOOK_ID] = hass.components.webhook.async_generate_id()
entry.data[CONF_WEBHOOK_URL] = hass.components.webhook.async_generate_url(
entry.data[CONF_WEBHOOK_ID]
webhook_id = hass.components.webhook.async_generate_id()
webhook_url = hass.components.webhook.async_generate_url(webhook_id)
_LOGGER.info("Registering new webhook at: %s", webhook_url)
hass.config_entries.async_update_entry(
entry,
data={
**entry.data,
CONF_WEBHOOK_ID: webhook_id,
CONF_WEBHOOK_URL: webhook_url,
},
)
_LOGGER.info("Registering new webhook at: %s", entry.data[CONF_WEBHOOK_URL])
hass.config_entries.async_update_entry(entry, data={**entry.data})
await hass.async_add_executor_job(
session.update_webhook,
entry.data[CONF_WEBHOOK_URL],

View File

@@ -206,6 +206,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
CONF_TIMEOUT: 31,
}
result = None
try:
LOGGER.debug("Try config: %s", config)
with SamsungTVWS(
@@ -223,9 +224,13 @@ class SamsungTVWSBridge(SamsungTVBridge):
return RESULT_SUCCESS
except WebSocketException:
LOGGER.debug("Working but unsupported config: %s", config)
return RESULT_NOT_SUPPORTED
result = RESULT_NOT_SUPPORTED
except (OSError, ConnectionFailure) as err:
LOGGER.debug("Failing config: %s, error: %s", config, err)
# pylint: disable=useless-else-on-loop
else:
if result:
return result
return RESULT_NOT_SUCCESSFUL

View File

@@ -3,7 +3,7 @@
"name": "SimpliSafe",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
"requirements": ["simplisafe-python==9.0.3"],
"requirements": ["simplisafe-python==9.0.4"],
"dependencies": [],
"codeowners": ["@bachya"]
}

View File

@@ -1,6 +1,7 @@
"""Ask tankerkoenig.de for petrol price information."""
from datetime import timedelta
import logging
from math import ceil
import pytankerkoenig
import voluptuous as vol
@@ -164,27 +165,41 @@ class TankerkoenigData:
)
return False
self.add_station(additional_station_data["station"])
if len(self.stations) > 10:
_LOGGER.warning(
"Found more than 10 stations to check. "
"This might invalidate your api-key on the long run. "
"Try using a smaller radius"
)
return True
async def fetch_data(self):
"""Get the latest data from tankerkoenig.de."""
_LOGGER.debug("Fetching new data from tankerkoenig.de")
station_ids = list(self.stations)
data = await self._hass.async_add_executor_job(
pytankerkoenig.getPriceList, self._api_key, station_ids
)
if data["ok"]:
prices = {}
# The API seems to only return at most 10 results, so split the list in chunks of 10
# and merge it together.
for index in range(ceil(len(station_ids) / 10)):
data = await self._hass.async_add_executor_job(
pytankerkoenig.getPriceList,
self._api_key,
station_ids[index * 10 : (index + 1) * 10],
)
_LOGGER.debug("Received data: %s", data)
if not data["ok"]:
_LOGGER.error(
"Error fetching data from tankerkoenig.de: %s", data["message"]
)
raise TankerkoenigError(data["message"])
if "prices" not in data:
_LOGGER.error("Did not receive price information from tankerkoenig.de")
raise TankerkoenigError("No prices in data")
else:
_LOGGER.error(
"Error fetching data from tankerkoenig.de: %s", data["message"]
)
raise TankerkoenigError(data["message"])
return data["prices"]
prices.update(data["prices"])
return prices
def add_station(self, station: dict):
"""Add fuel station to the entity list."""

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
STATE_ALARM_TRIGGERED,
)
from . import DOMAIN as TOTALCONNECT_DOMAIN
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -30,7 +30,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
alarms = []
client = hass.data[TOTALCONNECT_DOMAIN].client
client = hass.data[DOMAIN].client
for location_id, location in client.locations.items():
location_name = location.location_name
@@ -71,7 +71,7 @@ class TotalConnectAlarm(alarm.AlarmControlPanel):
def update(self):
"""Return the state of the device."""
status = self._client.get_armed_status(self._location_id)
self._client.get_armed_status(self._location_id)
attr = {
"location_name": self._name,
"location_id": self._location_id,
@@ -79,47 +79,36 @@ class TotalConnectAlarm(alarm.AlarmControlPanel):
"low_battery": self._client.locations[self._location_id].low_battery,
"cover_tampered": self._client.locations[
self._location_id
].is_cover_tampered,
].is_cover_tampered(),
"triggered_source": None,
"triggered_zone": None,
}
if status in (self._client.DISARMED, self._client.DISARMED_BYPASS):
if self._client.locations[self._location_id].is_disarmed():
state = STATE_ALARM_DISARMED
elif status in (
self._client.ARMED_STAY,
self._client.ARMED_STAY_INSTANT,
self._client.ARMED_STAY_INSTANT_BYPASS,
):
elif self._client.locations[self._location_id].is_armed_home():
state = STATE_ALARM_ARMED_HOME
elif status == self._client.ARMED_STAY_NIGHT:
elif self._client.locations[self._location_id].is_armed_night():
state = STATE_ALARM_ARMED_NIGHT
elif status in (
self._client.ARMED_AWAY,
self._client.ARMED_AWAY_BYPASS,
self._client.ARMED_AWAY_INSTANT,
self._client.ARMED_AWAY_INSTANT_BYPASS,
):
elif self._client.locations[self._location_id].is_armed_away():
state = STATE_ALARM_ARMED_AWAY
elif status == self._client.ARMED_CUSTOM_BYPASS:
elif self._client.locations[self._location_id].is_armed_custom_bypass():
state = STATE_ALARM_ARMED_CUSTOM_BYPASS
elif status == self._client.ARMING:
elif self._client.locations[self._location_id].is_arming():
state = STATE_ALARM_ARMING
elif status == self._client.DISARMING:
elif self._client.locations[self._location_id].is_disarming():
state = STATE_ALARM_DISARMING
elif status == self._client.ALARMING:
elif self._client.locations[self._location_id].is_triggered_police():
state = STATE_ALARM_TRIGGERED
attr["triggered_source"] = "Police/Medical"
elif status == self._client.ALARMING_FIRE_SMOKE:
elif self._client.locations[self._location_id].is_triggered_fire():
state = STATE_ALARM_TRIGGERED
attr["triggered_source"] = "Fire/Smoke"
elif status == self._client.ALARMING_CARBON_MONOXIDE:
elif self._client.locations[self._location_id].is_triggered_gas():
state = STATE_ALARM_TRIGGERED
attr["triggered_source"] = "Carbon Monoxide"
else:
logging.info(
"Total Connect Client returned unknown status code: %s", status
)
logging.info("Total Connect Client returned unknown status")
state = None
self._state = state

View File

@@ -0,0 +1,3 @@
"""TotalConnect constants."""
DOMAIN = "totalconnect"

View File

@@ -8,6 +8,8 @@ import voluptuous as vol
from homeassistant import config_entries, const as ha_const
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.typing import HomeAssistantType
from . import api
from .core import ZHAGateway
@@ -27,6 +29,7 @@ from .core.const import (
DEFAULT_BAUDRATE,
DEFAULT_RADIO_TYPE,
DOMAIN,
SIGNAL_ADD_ENTITIES,
RadioType,
)
@@ -90,8 +93,15 @@ async def async_setup_entry(hass, config_entry):
"""
zha_data = hass.data.setdefault(DATA_ZHA, {})
zha_data[DATA_ZHA_PLATFORM_LOADED] = {}
config = zha_data.get(DATA_ZHA_CONFIG, {})
zha_data[DATA_ZHA_DISPATCHERS] = []
for component in COMPONENTS:
zha_data[component] = []
coro = hass.config_entries.async_forward_entry_setup(config_entry, component)
zha_data[DATA_ZHA_PLATFORM_LOADED][component] = hass.async_create_task(coro)
if config.get(CONF_ENABLE_QUIRKS, True):
# needs to be done here so that the ZHA module is finished loading
# before zhaquirks is imported
@@ -100,22 +110,6 @@ async def async_setup_entry(hass, config_entry):
zha_gateway = ZHAGateway(hass, config, config_entry)
await zha_gateway.async_initialize()
zha_data[DATA_ZHA_DISPATCHERS] = []
zha_data[DATA_ZHA_PLATFORM_LOADED] = asyncio.Event()
platforms = []
for component in COMPONENTS:
platforms.append(
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component)
)
)
async def _platforms_loaded():
await asyncio.gather(*platforms)
zha_data[DATA_ZHA_PLATFORM_LOADED].set()
hass.async_create_task(_platforms_loaded())
device_registry = await hass.helpers.device_registry.async_get_registry()
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
@@ -134,7 +128,7 @@ async def async_setup_entry(hass, config_entry):
await zha_data[DATA_ZHA_GATEWAY].async_update_device_storage()
hass.bus.async_listen_once(ha_const.EVENT_HOMEASSISTANT_STOP, async_zha_shutdown)
hass.async_create_task(zha_gateway.async_load_devices())
hass.async_create_task(async_load_entities(hass, config_entry))
return True
@@ -152,3 +146,20 @@ async def async_unload_entry(hass, config_entry):
await hass.config_entries.async_forward_entry_unload(config_entry, component)
return True
async def async_load_entities(
hass: HomeAssistantType, config_entry: config_entries.ConfigEntry
) -> None:
"""Load entities after integration was setup."""
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].async_prepare_entities()
to_setup = [
hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED][comp]
for comp in COMPONENTS
if hass.data[DATA_ZHA][comp]
]
results = await asyncio.gather(*to_setup, return_exceptions=True)
for res in results:
if isinstance(res, Exception):
_LOGGER.warning("Couldn't setup zha platform: %s", res)
async_dispatcher_send(hass, SIGNAL_ADD_ENTITIES)

View File

@@ -49,7 +49,7 @@ STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation binary sensor from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -8,6 +8,16 @@ from homeassistant.core import callback
from homeassistant.helpers.typing import HomeAssistantType
from . import const as zha_const, registries as zha_regs, typing as zha_typing
from .. import ( # noqa: F401 pylint: disable=unused-import,
binary_sensor,
cover,
device_tracker,
fan,
light,
lock,
sensor,
switch,
)
from .channels import base
_LOGGER = logging.getLogger(__name__)

View File

@@ -36,7 +36,6 @@ from .const import (
DATA_ZHA,
DATA_ZHA_BRIDGE_ID,
DATA_ZHA_GATEWAY,
DATA_ZHA_PLATFORM_LOADED,
DEBUG_COMP_BELLOWS,
DEBUG_COMP_ZHA,
DEBUG_COMP_ZIGPY,
@@ -157,34 +156,40 @@ class ZHAGateway:
self._hass.data[DATA_ZHA][DATA_ZHA_BRIDGE_ID] = str(
self.application_controller.ieee
)
await self.async_load_devices()
self._initialize_groups()
async def async_load_devices(self) -> None:
"""Restore ZHA devices from zigpy application state."""
await self._hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED].wait()
zigpy_devices = self.application_controller.devices.values()
for zigpy_device in zigpy_devices:
self._async_get_or_create_device(zigpy_device, restored=True)
async def async_prepare_entities(self) -> None:
"""Prepare entities by initializing device channels."""
semaphore = asyncio.Semaphore(2)
async def _throttle(device: zha_typing.ZigpyDeviceType):
async def _throttle(zha_device: zha_typing.ZhaDeviceType, cached: bool):
async with semaphore:
await self.async_device_restored(device)
await zha_device.async_initialize(from_cache=cached)
zigpy_devices = self.application_controller.devices.values()
_LOGGER.debug("Loading battery powered devices")
await asyncio.gather(
*[
_throttle(dev)
for dev in zigpy_devices
if not dev.node_desc.is_mains_powered
_throttle(dev, cached=True)
for dev in self.devices.values()
if not dev.is_mains_powered
]
)
async_dispatcher_send(self._hass, SIGNAL_ADD_ENTITIES)
_LOGGER.debug("Loading mains powered devices")
await asyncio.gather(
*[_throttle(dev) for dev in zigpy_devices if dev.node_desc.is_mains_powered]
*[
_throttle(dev, cached=False)
for dev in self.devices.values()
if dev.is_mains_powered
]
)
async_dispatcher_send(self._hass, SIGNAL_ADD_ENTITIES)
def device_joined(self, device):
"""Handle device joined.

View File

@@ -29,7 +29,7 @@ STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation cover from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -26,7 +26,7 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation device tracker from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -53,7 +53,7 @@ STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation fan from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -52,7 +52,7 @@ _REFRESH_INTERVAL = (45, 75)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation light from config entry."""
entities_to_create = hass.data[DATA_ZHA][light.DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][light.DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -36,7 +36,7 @@ VALUE_TO_STATE = dict(enumerate(STATE_LIST))
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation Door Lock from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -68,7 +68,7 @@ STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation sensor from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -26,7 +26,7 @@ STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation switch from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,

View File

@@ -3,7 +3,7 @@
"name": "Z-Wave",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/zwave",
"requirements": ["homeassistant-pyozw==0.1.9", "pydispatcher==2.0.5"],
"requirements": ["homeassistant-pyozw==0.1.10", "pydispatcher==2.0.5"],
"dependencies": [],
"codeowners": ["@home-assistant/z-wave"]
}

View File

@@ -562,34 +562,36 @@ def _log_pkg_error(package: str, component: str, config: Dict, message: str) ->
_LOGGER.error(message)
def _identify_config_schema(module: ModuleType) -> Tuple[Optional[str], Optional[Dict]]:
def _identify_config_schema(module: ModuleType) -> Optional[str]:
"""Extract the schema and identify list or dict based."""
try:
key = next(k for k in module.CONFIG_SCHEMA.schema if k == module.DOMAIN) # type: ignore
except (AttributeError, StopIteration):
return None, None
return None
schema = module.CONFIG_SCHEMA.schema[key] # type: ignore
if hasattr(key, "default") and not isinstance(
key.default, vol.schema_builder.Undefined
):
default_value = schema(key.default())
default_value = module.CONFIG_SCHEMA({module.DOMAIN: key.default()})[ # type: ignore
module.DOMAIN # type: ignore
]
if isinstance(default_value, dict):
return "dict", schema
return "dict"
if isinstance(default_value, list):
return "list", schema
return "list"
return None, None
return None
t_schema = str(schema)
if t_schema.startswith("{") or "schema_with_slug_keys" in t_schema:
return ("dict", schema)
return "dict"
if t_schema.startswith(("[", "All(<function ensure_list")):
return ("list", schema)
return "", schema
return "list"
return None
def _recursive_merge(conf: Dict[str, Any], package: Dict[str, Any]) -> Union[bool, str]:
@@ -642,8 +644,7 @@ async def merge_packages_config(
merge_list = hasattr(component, "PLATFORM_SCHEMA")
if not merge_list and hasattr(component, "CONFIG_SCHEMA"):
merge_type, _ = _identify_config_schema(component)
merge_list = merge_type == "list"
merge_list = _identify_config_schema(component) == "list"
if merge_list:
config[comp_name] = cv.remove_falsy(

View File

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

View File

@@ -701,7 +701,9 @@ class Script:
def _log(self, msg, *args, level=logging.INFO):
if self.name:
msg = f"{self.name}: {msg}"
msg = f"%s: {msg}"
args = [self.name, *args]
if level == _LOG_EXCEPTION:
self._logger.exception(msg, *args)
else:

View File

@@ -139,7 +139,7 @@ aio_georss_gdacs==0.3
aioambient==1.0.4
# homeassistant.components.asuswrt
aioasuswrt==1.2.2
aioasuswrt==1.2.3
# homeassistant.components.automatic
aioautomatic==0.6.5
@@ -699,7 +699,7 @@ holidays==0.10.1
home-assistant-frontend==20200318.1
# homeassistant.components.zwave
homeassistant-pyozw==0.1.9
homeassistant-pyozw==0.1.10
# homeassistant.components.homematicip_cloud
homematicip==0.10.17
@@ -712,7 +712,7 @@ horimote==0.4.1
httplib2==0.10.3
# homeassistant.components.huawei_lte
huawei-lte-api==1.4.10
huawei-lte-api==1.4.11
# homeassistant.components.hydrawise
hydrawiser==0.1.1
@@ -1856,7 +1856,7 @@ simplehound==0.3
simplepush==1.1.4
# homeassistant.components.simplisafe
simplisafe-python==9.0.3
simplisafe-python==9.0.4
# homeassistant.components.sisyphus
sisyphus-control==2.2.1

View File

@@ -50,7 +50,7 @@ aio_georss_gdacs==0.3
aioambient==1.0.4
# homeassistant.components.asuswrt
aioasuswrt==1.2.2
aioasuswrt==1.2.3
# homeassistant.components.automatic
aioautomatic==0.6.5
@@ -266,7 +266,7 @@ holidays==0.10.1
home-assistant-frontend==20200318.1
# homeassistant.components.zwave
homeassistant-pyozw==0.1.9
homeassistant-pyozw==0.1.10
# homeassistant.components.homematicip_cloud
homematicip==0.10.17
@@ -276,7 +276,7 @@ homematicip==0.10.17
httplib2==0.10.3
# homeassistant.components.huawei_lte
huawei-lte-api==1.4.10
huawei-lte-api==1.4.11
# homeassistant.components.iaqualink
iaqualink==0.3.1
@@ -649,7 +649,7 @@ sentry-sdk==0.13.5
simplehound==0.3
# homeassistant.components.simplisafe
simplisafe-python==9.0.3
simplisafe-python==9.0.4
# homeassistant.components.sleepiq
sleepyq==0.7

View File

@@ -0,0 +1,5 @@
#!/usr/bin/execlineb -S0
# ==============================================================================
# Take down the S6 supervision tree when Home Assistant fails
# ==============================================================================
s6-svscanctl -t /var/run/s6/services

View File

@@ -0,0 +1,7 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Start Home Assistant service
# ==============================================================================
cd /config || bashio::exit.nok "Can't find config folder!"
exec python3 -m homeassistant --config /config

View File

@@ -1,5 +1,5 @@
"""Tests for Samsung TV config flow."""
from unittest.mock import call, patch
from unittest.mock import Mock, PropertyMock, call, patch
from asynctest import mock
import pytest
@@ -19,7 +19,7 @@ from homeassistant.components.ssdp import (
ATTR_UPNP_MODEL_NAME,
ATTR_UPNP_UDN,
)
from homeassistant.const import CONF_HOST, CONF_ID, CONF_METHOD, CONF_NAME
from homeassistant.const import CONF_HOST, CONF_ID, CONF_METHOD, CONF_NAME, CONF_TOKEN
MOCK_USER_DATA = {CONF_HOST: "fake_host", CONF_NAME: "fake_name"}
MOCK_SSDP_DATA = {
@@ -46,6 +46,20 @@ AUTODETECT_LEGACY = {
"host": "fake_host",
"timeout": 31,
}
AUTODETECT_WEBSOCKET_PLAIN = {
"host": "fake_host",
"name": "HomeAssistant",
"port": 8001,
"timeout": 31,
"token": None,
}
AUTODETECT_WEBSOCKET_SSL = {
"host": "fake_host",
"name": "HomeAssistant",
"port": 8002,
"timeout": 31,
"token": None,
}
@pytest.fixture(name="remote")
@@ -446,20 +460,48 @@ async def test_autodetect_websocket(hass, remote, remotews):
with patch(
"homeassistant.components.samsungtv.bridge.Remote", side_effect=OSError("Boom"),
), patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remotews:
enter = Mock()
type(enter).token = PropertyMock(return_value="123456789")
remote = Mock()
remote.__enter__ = Mock(return_value=enter)
remote.__exit__ = Mock(return_value=False)
remotews.return_value = remote
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=MOCK_USER_DATA
)
assert result["type"] == "create_entry"
assert result["data"][CONF_METHOD] == "websocket"
assert result["data"][CONF_TOKEN] == "123456789"
assert remotews.call_count == 1
assert remotews.call_args_list == [call(**AUTODETECT_WEBSOCKET_PLAIN)]
async def test_autodetect_websocket_ssl(hass, remote, remotews):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote", side_effect=OSError("Boom"),
), patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS",
side_effect=[WebSocketProtocolException("Boom"), mock.DEFAULT],
) as remotews:
enter = Mock()
type(enter).token = PropertyMock(return_value="123456789")
remote = Mock()
remote.__enter__ = Mock(return_value=enter)
remote.__exit__ = Mock(return_value=False)
remotews.return_value = remote
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=MOCK_USER_DATA
)
assert result["type"] == "create_entry"
assert result["data"][CONF_METHOD] == "websocket"
assert result["data"][CONF_TOKEN] == "123456789"
assert remotews.call_count == 2
assert remotews.call_args_list == [
call(
host="fake_host",
name="HomeAssistant",
port=8001,
timeout=31,
token=None,
)
call(**AUTODETECT_WEBSOCKET_PLAIN),
call(**AUTODETECT_WEBSOCKET_SSL),
]
@@ -524,18 +566,6 @@ async def test_autodetect_none(hass, remote, remotews):
]
assert remotews.call_count == 2
assert remotews.call_args_list == [
call(
host="fake_host",
name="HomeAssistant",
port=8001,
timeout=31,
token=None,
),
call(
host="fake_host",
name="HomeAssistant",
port=8002,
timeout=31,
token=None,
),
call(**AUTODETECT_WEBSOCKET_PLAIN),
call(**AUTODETECT_WEBSOCKET_SSL),
]

View File

@@ -34,8 +34,11 @@ from homeassistant.const import (
ATTR_FRIENDLY_NAME,
ATTR_SUPPORTED_FEATURES,
CONF_HOST,
CONF_IP_ADDRESS,
CONF_METHOD,
CONF_NAME,
CONF_PORT,
CONF_TOKEN,
SERVICE_MEDIA_NEXT_TRACK,
SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_PLAY,
@@ -51,7 +54,7 @@ from homeassistant.const import (
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from tests.common import async_fire_time_changed
from tests.common import MockConfigEntry, async_fire_time_changed
ENTITY_ID = f"{DOMAIN}.fake"
MOCK_CONFIG = {
@@ -64,17 +67,40 @@ MOCK_CONFIG = {
}
]
}
MOCK_CONFIGWS = {
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake",
CONF_NAME: "fake",
CONF_PORT: 8001,
CONF_TOKEN: "123456789",
CONF_ON_ACTION: [{"delay": "00:00:01"}],
}
]
}
MOCK_CALLS_WS = {
"host": "fake",
"port": 8001,
"token": None,
"timeout": 31,
"name": "HomeAssistant",
}
MOCK_ENTRY_WS = {
CONF_IP_ADDRESS: "test",
CONF_HOST: "fake",
CONF_METHOD: "websocket",
CONF_NAME: "fake",
CONF_PORT: 8001,
CONF_TOKEN: "abcde",
}
MOCK_CALLS_ENTRY_WS = {
"host": "fake",
"name": "HomeAssistant",
"port": 8001,
"timeout": 1,
"token": "abcde",
}
ENTITY_ID_NOTURNON = f"{DOMAIN}.fake_noturnon"
MOCK_CONFIG_NOTURNON = {
@@ -155,6 +181,52 @@ async def test_setup_without_turnon(hass, remote):
assert hass.states.get(ENTITY_ID_NOTURNON)
async def test_setup_websocket(hass, remotews, mock_now):
"""Test setup of platform."""
with patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remote_class:
enter = mock.Mock()
type(enter).token = mock.PropertyMock(return_value="987654321")
remote = mock.Mock()
remote.__enter__ = mock.Mock(return_value=enter)
remote.__exit__ = mock.Mock()
remote_class.return_value = remote
await setup_samsungtv(hass, MOCK_CONFIGWS)
assert remote_class.call_count == 1
assert remote_class.call_args_list == [call(**MOCK_CALLS_WS)]
assert hass.states.get(ENTITY_ID)
async def test_setup_websocket_2(hass, mock_now):
"""Test setup of platform from config entry."""
entity_id = f"{DOMAIN}.fake"
entry = MockConfigEntry(
domain=SAMSUNGTV_DOMAIN, data=MOCK_ENTRY_WS, unique_id=entity_id,
)
entry.add_to_hass(hass)
config_entries = hass.config_entries.async_entries(SAMSUNGTV_DOMAIN)
assert len(config_entries) == 1
assert entry is config_entries[0]
assert await async_setup_component(hass, SAMSUNGTV_DOMAIN, {})
await hass.async_block_till_done()
next_update = mock_now + timedelta(minutes=5)
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
) as remote, patch("homeassistant.util.dt.utcnow", return_value=next_update):
async_fire_time_changed(hass, next_update)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state
assert remote.call_count == 1
assert remote.call_args_list == [call(**MOCK_CALLS_ENTRY_WS)]
async def test_update_on(hass, remote, mock_now):
"""Testing update tv on."""
await setup_samsungtv(hass, MOCK_CONFIG)

View File

@@ -1743,3 +1743,15 @@ async def test_if_running_parallel(hass):
assert len(events) == 4
assert events[2].data["value"] == 2
assert events[3].data["value"] == 2
async def test_script_logging(caplog):
"""Test script logging."""
script_obj = script.Script(None, [], "Script with % Name")
script_obj._log("Test message with name %s", 1)
assert "Script with % Name: Test message with name 1" in caplog.text
script_obj = script.Script(None, [])
script_obj._log("Test message without name %s", 2)
assert "Test message without name 2" in caplog.text

View File

@@ -722,7 +722,7 @@ async def test_merge_id_schema(hass):
for domain, expected_type in types.items():
integration = await async_get_integration(hass, domain)
module = integration.get_component()
typ, _ = config_util._identify_config_schema(module)
typ = config_util._identify_config_schema(module)
assert typ == expected_type, f"{domain} expected {expected_type}, got {typ}"
@@ -995,15 +995,30 @@ async def test_component_config_exceptions(hass, caplog):
@pytest.mark.parametrize(
"domain, schema, expected",
[
("zone", vol.Schema({vol.Optional("zone", default=[]): list}), "list"),
("zone", vol.Schema({vol.Optional("zone", default=dict): dict}), "dict"),
("zone", vol.Schema({vol.Optional("zone", default=list): [int]}), "list"),
("zone", vol.Schema({vol.Optional("zone", default=[]): [int]}), "list"),
(
"zone",
vol.Schema({vol.Optional("zone", default={}): {vol.Optional("hello"): 1}}),
"dict",
),
(
"zone",
vol.Schema(
{vol.Optional("zone", default=dict): {vol.Optional("hello"): 1}}
),
"dict",
),
("zone", vol.Schema({vol.Optional("zone"): int}), None),
("zone", vol.Schema({"zone": int}), None),
("not_existing", vol.Schema({vol.Optional("zone", default=dict): dict}), None,),
("non_existing", vol.Schema({"zone": int}), None),
("zone", vol.Schema({}), None),
],
)
def test_identify_config_schema(domain, schema, expected):
"""Test identify config schema."""
assert (
config_util._identify_config_schema(Mock(DOMAIN=domain, CONFIG_SCHEMA=schema))[
0
]
config_util._identify_config_schema(Mock(DOMAIN=domain, CONFIG_SCHEMA=schema))
== expected
)