mirror of
https://github.com/home-assistant/core.git
synced 2025-09-24 20:39:28 +00:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c456b725fd | ||
![]() |
524f5a7264 | ||
![]() |
637a16799f | ||
![]() |
4df6b3c76a | ||
![]() |
05ee15c28c | ||
![]() |
0a5cde7ac3 | ||
![]() |
c64fe19260 | ||
![]() |
8f232f3c69 | ||
![]() |
059d2572a2 |
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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)
|
||||
|
@@ -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."],
|
||||
|
@@ -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",
|
||||
|
@@ -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": []
|
||||
|
@@ -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,
|
||||
|
@@ -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:
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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."""
|
||||
|
@@ -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")
|
||||
|
@@ -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),
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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),
|
||||
|
Reference in New Issue
Block a user