Compare commits

...

9 Commits

Author SHA1 Message Date
Paulus Schoutsen
c456b725fd Bumped version to 0.101.0b2 2019-10-25 10:49:42 -07:00
Bram Kragten
524f5a7264 Updated frontend to 20191025.0 (#28208) 2019-10-25 10:49:36 -07:00
gngj
637a16799f Fix microsoft tts (#28199)
* Update pycsspeechtts

From 1.0.2 to 1.0.3 as the old one is using an api that doesn't work

* Give a option to choose region

Api is now region dependent, so gave it a config
2019-10-25 10:49:35 -07:00
SukramJ
4df6b3c76a Partially revert tensorflow import move (#28184)
* Revert "Refactor imports for tensorflow (#27617)"

This reverts commit 5a83a92390.

* move only some imports to top

* fix lint

* add comments
2019-10-25 10:49:35 -07:00
jjlawren
05ee15c28c Update Plex via websockets (#28158)
* Save client identifier from auth for future use

* Use websocket events to update Plex

* Handle websocket disconnections

* Use aiohttp, shut down socket cleanly

* Bad rebase fix

* Don't connect websocket during config_flow validation, fix tests

* Move websocket handling to external library

* Close websocket session on HA stop

* Use external library, revert unnecessary test change

* Async & lint fixes

* Clean up websocket stopper on entry unload

* Setup websocket in component, pass actual needed object to library
2019-10-25 10:49:34 -07:00
Paulus Schoutsen
0a5cde7ac3 Bumped version to 0.101.0b1 2019-10-24 13:53:30 -07:00
Otto Winter
c64fe19260 Fix ESPHome stacktraces when removing entity and shutting down (#28185) 2019-10-24 13:53:17 -07:00
Otto Winter
8f232f3c69 Bump aioesphomeapi to 2.4.1 (#28170)
* Bump aioesphomeapi to 2.4.1

* Update requirements

* Bump to 2.4.2
2019-10-24 13:53:16 -07:00
Alexei Chetroi
059d2572a2 Fixes/zha ieee tail (#28160)
* Fix ZHA entity_id assignment.

* Update tests.
2019-10-24 13:53:16 -07:00
18 changed files with 123 additions and 51 deletions

View File

@@ -514,6 +514,7 @@ omit =
homeassistant/components/plex/media_player.py
homeassistant/components/plex/sensor.py
homeassistant/components/plex/server.py
homeassistant/components/plex/websockets.py
homeassistant/components/plugwise/*
homeassistant/components/plum_lightpad/*
homeassistant/components/pocketcasts/sensor.py

View File

@@ -95,8 +95,11 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
"""Cleanup the socket client on HA stop."""
await _cleanup_instance(hass, entry)
# Use async_listen instead of async_listen_once so that we don't deregister
# the callback twice when shutting down Home Assistant.
# "Unable to remove unknown listener <function EventBus.async_listen_once.<locals>.onetime_listener>"
entry_data.cleanup_callbacks.append(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_stop)
hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, on_stop)
)
@callback
@@ -365,6 +368,7 @@ async def platform_async_setup_entry(
"""
entry_data: RuntimeEntryData = hass.data[DOMAIN][entry.entry_id]
entry_data.info[component_key] = {}
entry_data.old_info[component_key] = {}
entry_data.state[component_key] = {}
@callback
@@ -390,7 +394,13 @@ async def platform_async_setup_entry(
# Remove old entities
for info in old_infos.values():
entry_data.async_remove_entity(hass, component_key, info.key)
# First copy the now-old info into the backup object
entry_data.old_info[component_key] = entry_data.info[component_key]
# Then update the actual info
entry_data.info[component_key] = new_infos
# Add entities to Home Assistant
async_add_entities(add_entities)
signal = DISPATCHER_ON_LIST.format(entry_id=entry.entry_id)
@@ -524,7 +534,13 @@ class EsphomeEntity(Entity):
@property
def _static_info(self) -> EntityInfo:
return self._entry_data.info[self._component_key][self._key]
# Check if value is in info database. Use a single lookup.
info = self._entry_data.info[self._component_key].get(self._key)
if info is not None:
return info
# This entity is in the removal project and has been removed from .info
# already, look in old_info
return self._entry_data.old_info[self._component_key].get(self._key)
@property
def _device_info(self) -> DeviceInfo:

View File

@@ -56,6 +56,13 @@ class RuntimeEntryData:
reconnect_task = attr.ib(type=Optional[asyncio.Task], default=None)
state = attr.ib(type=Dict[str, Dict[str, Any]], factory=dict)
info = attr.ib(type=Dict[str, Dict[str, Any]], factory=dict)
# A second list of EntityInfo objects
# This is necessary for when an entity is being removed. HA requires
# some static info to be accessible during removal (unique_id, maybe others)
# If an entity can't find anything in the info array, it will look for info here.
old_info = attr.ib(type=Dict[str, Dict[str, Any]], factory=dict)
services = attr.ib(type=Dict[int, "UserService"], factory=dict)
available = attr.ib(type=bool, default=False)
device_info = attr.ib(type=DeviceInfo, default=None)

View File

@@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/esphome",
"requirements": [
"aioesphomeapi==2.4.0"
"aioesphomeapi==2.4.2"
],
"dependencies": [],
"zeroconf": ["_esphomelib._tcp.local."],

View File

@@ -3,7 +3,7 @@
"name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": [
"home-assistant-frontend==20191023.0"
"home-assistant-frontend==20191025.0"
],
"dependencies": [
"api",

View File

@@ -3,7 +3,7 @@
"name": "Microsoft",
"documentation": "https://www.home-assistant.io/integrations/microsoft",
"requirements": [
"pycsspeechtts==1.0.2"
"pycsspeechtts==1.0.3"
],
"dependencies": [],
"codeowners": []

View File

@@ -14,6 +14,7 @@ CONF_RATE = "rate"
CONF_VOLUME = "volume"
CONF_PITCH = "pitch"
CONF_CONTOUR = "contour"
CONF_REGION = "region"
_LOGGER = logging.getLogger(__name__)
@@ -72,6 +73,7 @@ DEFAULT_RATE = 0
DEFAULT_VOLUME = 0
DEFAULT_PITCH = "default"
DEFAULT_CONTOUR = ""
DEFAULT_REGION = "eastus"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
@@ -87,6 +89,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
),
vol.Optional(CONF_PITCH, default=DEFAULT_PITCH): cv.string,
vol.Optional(CONF_CONTOUR, default=DEFAULT_CONTOUR): cv.string,
vol.Optional(CONF_REGION, default=DEFAULT_REGION): cv.string,
}
)
@@ -102,13 +105,16 @@ def get_engine(hass, config):
config[CONF_VOLUME],
config[CONF_PITCH],
config[CONF_CONTOUR],
config[CONF_REGION],
)
class MicrosoftProvider(Provider):
"""The Microsoft speech API provider."""
def __init__(self, apikey, lang, gender, ttype, rate, volume, pitch, contour):
def __init__(
self, apikey, lang, gender, ttype, rate, volume, pitch, contour, region
):
"""Init Microsoft TTS service."""
self._apikey = apikey
self._lang = lang
@@ -119,6 +125,7 @@ class MicrosoftProvider(Provider):
self._volume = f"{volume}%"
self._pitch = pitch
self._contour = contour
self._region = region
self.name = "Microsoft"
@property
@@ -138,7 +145,7 @@ class MicrosoftProvider(Provider):
from pycsspeechtts import pycsspeechtts
try:
trans = pycsspeechtts.TTSTranslator(self._apikey)
trans = pycsspeechtts.TTSTranslator(self._apikey, self._region)
data = trans.speak(
language=language,
gender=self._gender,

View File

@@ -1,9 +1,9 @@
"""Support to embed Plex."""
import asyncio
from datetime import timedelta
import logging
import plexapi.exceptions
from plexwebsocket import PlexWebsocket
import requests.exceptions
import voluptuous as vol
@@ -16,9 +16,14 @@ from homeassistant.const import (
CONF_TOKEN,
CONF_URL,
CONF_VERIFY_SSL,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from .const import (
CONF_USE_EPISODE_ART,
@@ -33,8 +38,9 @@ from .const import (
PLATFORMS,
PLEX_MEDIA_PLAYER_OPTIONS,
PLEX_SERVER_CONFIG,
REFRESH_LISTENERS,
PLEX_UPDATE_PLATFORMS_SIGNAL,
SERVERS,
WEBSOCKETS,
)
from .server import PlexServer
@@ -67,9 +73,7 @@ _LOGGER = logging.getLogger(__package__)
def setup(hass, config):
"""Set up the Plex component."""
hass.data.setdefault(
PLEX_DOMAIN, {SERVERS: {}, REFRESH_LISTENERS: {}, DISPATCHERS: {}}
)
hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}, DISPATCHERS: {}, WEBSOCKETS: {}})
plex_config = config.get(PLEX_DOMAIN, {})
if plex_config:
@@ -136,7 +140,6 @@ async def async_setup_entry(hass, entry):
)
server_id = plex_server.machine_identifier
hass.data[PLEX_DOMAIN][SERVERS][server_id] = plex_server
hass.data[PLEX_DOMAIN][DISPATCHERS][server_id] = []
for platform in PLATFORMS:
hass.async_create_task(
@@ -145,9 +148,29 @@ async def async_setup_entry(hass, entry):
entry.add_update_listener(async_options_updated)
hass.data[PLEX_DOMAIN][REFRESH_LISTENERS][server_id] = async_track_time_interval(
hass, lambda now: plex_server.update_platforms(), timedelta(seconds=10)
unsub = async_dispatcher_connect(
hass,
PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id),
plex_server.update_platforms,
)
hass.data[PLEX_DOMAIN][DISPATCHERS].setdefault(server_id, [])
hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub)
def update_plex():
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
session = async_get_clientsession(hass)
websocket = PlexWebsocket(plex_server.plex_server, update_plex, session)
hass.loop.create_task(websocket.listen())
hass.data[PLEX_DOMAIN][WEBSOCKETS][server_id] = websocket
def close_websocket_session(_):
websocket.close()
unsub = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, close_websocket_session
)
hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub)
return True
@@ -156,8 +179,8 @@ async def async_unload_entry(hass, entry):
"""Unload a config entry."""
server_id = entry.data[CONF_SERVER_IDENTIFIER]
cancel = hass.data[PLEX_DOMAIN][REFRESH_LISTENERS].pop(server_id)
cancel()
websocket = hass.data[PLEX_DOMAIN][WEBSOCKETS].pop(server_id)
websocket.close()
dispatchers = hass.data[PLEX_DOMAIN][DISPATCHERS].pop(server_id)
for unsub in dispatchers:

View File

@@ -10,8 +10,8 @@ DEFAULT_VERIFY_SSL = True
DISPATCHERS = "dispatchers"
PLATFORMS = ["media_player", "sensor"]
REFRESH_LISTENERS = "refresh_listeners"
SERVERS = "servers"
WEBSOCKETS = "websockets"
PLEX_CONFIG_FILE = "plex.conf"
PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options"
@@ -19,6 +19,7 @@ PLEX_SERVER_CONFIG = "server_config"
PLEX_NEW_MP_SIGNAL = "plex_new_mp_signal.{}"
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL = "plex_update_mp_signal.{}"
PLEX_UPDATE_PLATFORMS_SIGNAL = "plex_update_platforms_signal.{}"
PLEX_UPDATE_SENSOR_SIGNAL = "plex_update_sensor_signal.{}"
CONF_CLIENT_IDENTIFIER = "client_id"

View File

@@ -5,7 +5,8 @@
"documentation": "https://www.home-assistant.io/integrations/plex",
"requirements": [
"plexapi==3.0.6",
"plexauth==0.0.5"
"plexauth==0.0.5",
"plexwebsocket==0.0.1"
],
"dependencies": [
"http"

View File

@@ -103,6 +103,8 @@ class PlexServer:
def update_platforms(self):
"""Update the platform entities."""
_LOGGER.debug("Updating devices")
available_clients = {}
new_clients = set()
@@ -164,6 +166,11 @@ class PlexServer:
sessions,
)
@property
def plex_server(self):
"""Return the plexapi PlexServer instance."""
return self._plex_server
@property
def friendly_name(self):
"""Return name of connected Plex server."""

View File

@@ -1,23 +1,12 @@
"""Support for performing TensorFlow classification on images."""
import io
import logging
import os
import sys
import io
import voluptuous as vol
from PIL import Image, ImageDraw
import numpy as np
try:
import cv2
except ImportError:
cv2 = None
try:
# Verify that the TensorFlow Object Detection API is pre-installed
import tensorflow as tf # noqa
from object_detection.utils import label_map_util # noqa
except ImportError:
label_map_util = None
import voluptuous as vol
from homeassistant.components.image_processing import (
CONF_CONFIDENCE,
@@ -98,8 +87,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
# append custom model path to sys.path
sys.path.append(model_dir)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
if label_map_util is None:
try:
# Verify that the TensorFlow Object Detection API is pre-installed
# pylint: disable=unused-import,unused-variable
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
# These imports shouldn't be moved to the top, because they depend on code from the model_dir.
# (The model_dir is created during the manual setup process. See integration docs.)
import tensorflow as tf # noqa
from object_detection.utils import label_map_util # noqa
except ImportError:
# pylint: disable=line-too-long
_LOGGER.error(
"No TensorFlow Object Detection library found! Install or compile "
"for your system following instructions here: "
@@ -107,7 +104,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
) # noqa
return
if cv2 is None:
try:
# Display warning that PIL will be used if no OpenCV is found.
# pylint: disable=unused-import,unused-variable
import cv2 # noqa
except ImportError:
_LOGGER.warning(
"No OpenCV library found. TensorFlow will process image with "
"PIL at reduced resolution"
@@ -282,7 +283,13 @@ class TensorFlowImageProcessor(ImageProcessingEntity):
def process_image(self, image):
"""Process the image."""
if cv2 is None:
try:
import cv2 # pylint: disable=import-error
img = cv2.imdecode(np.asarray(bytearray(image)), cv2.IMREAD_UNCHANGED)
inp = img[:, :, [2, 1, 0]] # BGR->RGB
inp_expanded = inp.reshape(1, inp.shape[0], inp.shape[1], 3)
except ImportError:
img = Image.open(io.BytesIO(bytearray(image))).convert("RGB")
img.thumbnail((460, 460), Image.ANTIALIAS)
img_width, img_height = img.size
@@ -292,10 +299,6 @@ class TensorFlowImageProcessor(ImageProcessingEntity):
.astype(np.uint8)
)
inp_expanded = np.expand_dims(inp, axis=0)
else:
img = cv2.imdecode(np.asarray(bytearray(image)), cv2.IMREAD_UNCHANGED)
inp = img[:, :, [2, 1, 0]] # BGR->RGB
inp_expanded = inp.reshape(1, inp.shape[0], inp.shape[1], 3)
image_tensor = self._graph.get_tensor_by_name("image_tensor:0")
boxes = self._graph.get_tensor_by_name("detection_boxes:0")

View File

@@ -40,7 +40,7 @@ class ZhaEntity(RestoreEntity, LogMixin, entity.Entity):
self._unique_id = unique_id
if not skip_entity_id:
ieee = zha_device.ieee
ieeetail = "".join(["%02x" % (o,) for o in ieee[-4:]])
ieeetail = "".join([f"{o:02x}" for o in ieee[:4]])
self.entity_id = "{}.{}_{}_{}_{}{}".format(
self._domain,
slugify(zha_device.manufacturer),

View File

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

View File

@@ -11,7 +11,7 @@ contextvars==2.4;python_version<"3.7"
cryptography==2.8
distro==1.4.0
hass-nabucasa==0.22
home-assistant-frontend==20191023.0
home-assistant-frontend==20191025.0
importlib-metadata==0.23
jinja2>=2.10.1
netdisco==2.6.0

View File

@@ -139,7 +139,7 @@ aiobotocore==0.10.2
aiodns==2.0.0
# homeassistant.components.esphome
aioesphomeapi==2.4.0
aioesphomeapi==2.4.2
# homeassistant.components.freebox
aiofreepybox==0.0.8
@@ -646,7 +646,7 @@ hole==0.5.0
holidays==0.9.11
# homeassistant.components.frontend
home-assistant-frontend==20191023.0
home-assistant-frontend==20191025.0
# homeassistant.components.zwave
homeassistant-pyozw==0.1.4
@@ -973,6 +973,9 @@ plexapi==3.0.6
# homeassistant.components.plex
plexauth==0.0.5
# homeassistant.components.plex
plexwebsocket==0.0.1
# homeassistant.components.plum_lightpad
plumlightpad==0.0.11
@@ -1129,7 +1132,7 @@ pycomfoconnect==0.3
pycoolmasternet==0.0.4
# homeassistant.components.microsoft
pycsspeechtts==1.0.2
pycsspeechtts==1.0.3
# homeassistant.components.cups
# pycups==1.9.73

View File

@@ -70,7 +70,7 @@ aioautomatic==0.6.5
aiobotocore==0.10.2
# homeassistant.components.esphome
aioesphomeapi==2.4.0
aioesphomeapi==2.4.2
# homeassistant.components.emulated_hue
# homeassistant.components.http
@@ -242,7 +242,7 @@ hole==0.5.0
holidays==0.9.11
# homeassistant.components.frontend
home-assistant-frontend==20191023.0
home-assistant-frontend==20191025.0
# homeassistant.components.zwave
homeassistant-pyozw==0.1.4
@@ -345,6 +345,9 @@ plexapi==3.0.6
# homeassistant.components.plex
plexauth==0.0.5
# homeassistant.components.plex
plexwebsocket==0.0.1
# homeassistant.components.mhz19
# homeassistant.components.serial_pm
pmsensor==0.4

View File

@@ -168,7 +168,7 @@ def make_entity_id(domain, device, cluster, use_suffix=True):
machine so that we can test state changes.
"""
ieee = device.ieee
ieeetail = "".join(["%02x" % (o,) for o in ieee[-4:]])
ieeetail = "".join([f"{o:02x}" for o in ieee[:4]])
entity_id = "{}.{}_{}_{}_{}{}".format(
domain,
slugify(device.manufacturer),