Compare commits

..

75 Commits

Author SHA1 Message Date
Paulus Schoutsen
6c51ff63ad Merge pull request #42724 from home-assistant/rc 2020-11-01 21:29:21 +01:00
Paulus Schoutsen
aecc3476e8 Bumped version to 0.117.2 2020-11-01 20:00:21 +00:00
Matthew Donoughe
43a990350b Use pylutron_caseta 0.7.1 (#42701) 2020-11-01 19:59:52 +00:00
Chris Talkington
769c5c1779 Fix canary camera entity inheritance (#42691) 2020-11-01 19:59:51 +00:00
Alexei Chetroi
a93ae418a2 Bump up ZHA dependencies (#42679)
* Bump up ZHA dependencies

* Fix tests because of zigpy request signature change
2020-11-01 19:59:51 +00:00
Jason Hunter
562ccbbe25 attempt to renew subscription immediately to stop endless loop if it fails after setup (#42651) 2020-11-01 19:59:50 +00:00
Erik Montnemery
5f28c82837 Make sure Tasmota status sensors are disabled (#42643) 2020-11-01 19:59:49 +00:00
Joakim Sørensen
92f9a77770 Bump pycfdns to 1.2.1 (#42634) 2020-11-01 19:59:49 +00:00
Malte Franken
2430c5ea22 Fix geo_rss_events import statement (#42629)
* bump integration library version

* fix import statement
2020-11-01 19:59:48 +00:00
airthusiast
6d54ed14b4 Fix Fibaro HC2 climate device missing temperature (#42627) 2020-11-01 19:59:47 +00:00
Sören Oldag
faf73e304a Bump pwmled to v1.6.6 (#42607) 2020-11-01 19:59:47 +00:00
Paulus Schoutsen
e902cfcbf0 Merge pull request #42624 from home-assistant/rc 2020-10-30 10:11:50 +01:00
Paulus Schoutsen
750bb11895 Fix broken time trigger test (#42606) 2020-10-30 08:50:34 +00:00
Paulus Schoutsen
2dcd57bf38 Bumped version to 0.117.1 2020-10-30 08:31:58 +00:00
Clifford Roche
26766d68aa Update greeclimate to 0.9.2 (#42616)
Fixes issue with erroneous await in UDP recv queue
2020-10-30 08:30:57 +00:00
Erik Montnemery
d26bceb110 Bump hatasmota to 0.0.25 (#42605) 2020-10-30 08:30:56 +00:00
Erik Montnemery
aa52ade5a0 Fix MQTT template light (#42598) 2020-10-30 08:30:56 +00:00
Bram Kragten
c57d697df5 Update frontend to 20201021.4 (#42590) 2020-10-30 08:30:55 +00:00
cgtobi
c604595f98 Fix RMV giving wrong data and ignoring given parameters (#42561) 2020-10-30 08:30:54 +00:00
Jc2k
1ab2d55a7a Bump aiohomekit to 0.2.54 (#42532) 2020-10-30 08:30:53 +00:00
Rob Bierbooms
048db9cb7d Enable polling for DSMR derivative entity (#42524) 2020-10-30 08:30:53 +00:00
Michael
0959777b81 Fix adding Virtual DSM system in synology_dsm (#42523) 2020-10-30 08:30:52 +00:00
Franck Nijhof
9c0a2d69ff Merge pull request #42516 from home-assistant/rc 2020-10-28 20:01:21 +01:00
Franck Nijhof
529b849efe Bumped version to 0.117.0 2020-10-28 18:08:23 +01:00
Paulus Schoutsen
a34d06e363 Default legacy templates to true (#42511)
* Default legacy templates to true

* Disable legacy_templates in tests

Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-10-28 18:08:04 +01:00
Paulus Schoutsen
5b85776566 Fix string representation of template result wrappers (#42494) 2020-10-28 18:08:00 +01:00
Angelo Gagliano
68d2938c6b Move async_add_entities back to event loop for tplink component (#42454) 2020-10-28 18:07:55 +01:00
Paulus Schoutsen
5a460e609f Bumped version to 0.117.0b6 2020-10-27 22:26:04 +00:00
Erik Montnemery
a88e011bba Fix race in Tasmota discovery (#42492) 2020-10-27 22:25:57 +00:00
Paulus Schoutsen
40be42299d Improve mqtt test (#42490) 2020-10-27 22:25:56 +00:00
Paulus Schoutsen
7dcebbe89d Bump cryptography to 3.2 (#42489) 2020-10-27 22:25:55 +00:00
Paulus Schoutsen
cb453d3ae1 Make result wrappers more robust (#42488) 2020-10-27 22:25:55 +00:00
Bram Kragten
2effbb6604 Update frontend to 20201021.3 (#42486) 2020-10-27 22:25:54 +00:00
Erik Montnemery
ba6acc286c Bump hatasmota to 0.0.24 (#42472) 2020-10-27 22:25:53 +00:00
Paulus Schoutsen
45c1c2acac Fix initial state of pi4ioe (#42459) 2020-10-27 22:25:53 +00:00
Quentame
1962a7c7ce Fix DSM Surveillance Station when only home mode is enabled (#42456) 2020-10-27 22:25:51 +00:00
Pascal Vizeli
8da732a89d Fix hassio-version (#42449) 2020-10-27 22:25:51 +00:00
João Gabriel
2697056ba9 Add device info default values to Panasonic Viera (#42441) 2020-10-27 22:25:50 +00:00
rajlaud
91e8593fb6 Improve Squeezebox media browser performance (#42439)
* Bump pysqueezebox version to better handle large libraries for media browser

* Merge changes into requirements
2020-10-27 22:25:49 +00:00
Aaron Bach
9fa97473cb Bump simplisafe-python to 9.6.0 (#42437) 2020-10-27 22:25:48 +00:00
Jason Hunter
29e7aa753c Remove title string from Xbox translations (#42431) 2020-10-27 22:25:47 +00:00
Tsvi Mostovicz
af31aa7995 Fix DLNA DMR media receiver when using Python 3.9 (#42430) 2020-10-27 22:25:47 +00:00
Robert Svensson
e3d29429d3 Make sure to clean up httpx session on failed connection to Axis device (#42428) 2020-10-27 22:25:46 +00:00
Angelo Gagliano
93e71ef218 Add retry for unavailable static tplink devices after HA starts (#42247) 2020-10-27 22:25:45 +00:00
Paulus Schoutsen
2893972c69 Bumped version to 0.117.0b5 2020-10-26 19:30:18 +00:00
Franck Nijhof
13b8bc6290 Add missing config flow translation key for Nest (#42423) 2020-10-26 19:29:44 +00:00
Franck Nijhof
326d36d303 Skip template result parsing in several places (#42408)
* Skip template result parsing in several places

* Adjust alert integration

* Adjust Alexa integration

* Adjust apns integration

* Adjust arest integration

* Adjust dialogflow integration

* Adjust generic camera integration

* Adjust imap email content  integration

* Adjust InfluxDB integration

* Adjust intent integration

* Adjust logbook integration

* Adjust HP ILO integration

* Adjust manual alarm control panel integration

* Adjust manual mqtt alarm control panel integration

* Adjust minio integration

* Adjust mqtt integration

* Adjust notify integration

* Adjust persistent notification integration

* Adjust rest integration

* Adjust rss feed template integration

* Adjust slack integration

* Adjust Xiaomi integration

* Adjust TCP integration

* Adjust Telegram Bot integration

* Bump CI cache version

* Revert "Bump CI cache version"

This reverts commit 875efe58cf.

* Adjust demo tests
2020-10-26 19:28:21 +00:00
Paulus Schoutsen
287b33eef3 Do not set up DSM when setting up Nest legacy (#42406) 2020-10-26 19:28:20 +00:00
Paulus Schoutsen
3ec83dc29e Catch ValueError in google_translate (#42405) 2020-10-26 19:28:20 +00:00
Franck Nijhof
f716b7714b Allow skip parsing template result (#42401) 2020-10-26 19:28:19 +00:00
Paulus Schoutsen
b7c958719e Fix MQTT publish from a script with templates (#42398) 2020-10-26 19:28:18 +00:00
Paulus Schoutsen
934ac73ae5 Fix CI (#42397) 2020-10-26 19:28:17 +00:00
Tsvi Mostovicz
bcd2ae78a0 Bump libhdate depndency (#42396)
* Bump libhdate depndency

This version fixes an important bug found by @amitfin in py-libhdate/py-libhdate#78

* Update reqs

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-10-26 19:28:17 +00:00
Erik Montnemery
c00ab762d3 Bump hatasmota to 0.0.23 (#42394) 2020-10-26 19:28:16 +00:00
Paulus Schoutsen
1fb18580b2 Store original result on template results (#42391)
* Store original result on template results

* Fix shell command test
2020-10-26 19:28:15 +00:00
Maksym D
9c6351c1b3 Change AfterShip polling interval from 5m to 15m (#42360)
This change will prevent reaching API requests limit of AfterShip free-tier account.
2020-10-26 19:28:14 +00:00
Paulus Schoutsen
4eacf3f6c0 Bumped version to 0.117.0b4 2020-10-25 22:54:50 +00:00
J. Nick Koston
35d2badb24 Ensure config entry platforms are excluded from reload (#42367) 2020-10-25 22:54:40 +00:00
Andre Lengwenus
331d5ba7ef Fix parameter issue in LCN cover close/stop (#42342) 2020-10-25 22:54:40 +00:00
mezz64
a2c157b5e9 Bump pyhik to 0.2.8 (#42322) 2020-10-25 22:54:39 +00:00
Jörg Thalheim
883fb8c168 Fix iCloud matching accounts (#42303)
`name` is no longer defined but `username`.
Fixes #38393
2020-10-25 22:54:38 +00:00
airthusiast
2e7ce63cbc Fix for Fibaro HC3 support, climate temp sensor and target temp (#42300) 2020-10-25 22:54:38 +00:00
Franck Nijhof
b925ae39cb Upgrade spotipy to 2.16.1 (#42293) 2020-10-25 22:54:37 +00:00
Erik Montnemery
5c608eb1bc Fix reconfiguring of Tasmota lights (#42288) 2020-10-25 22:54:36 +00:00
Erik Montnemery
3032f9280c Fix Tasmota CT light (#42287) 2020-10-25 22:54:35 +00:00
uvjustin
00284a87d0 Fix AVError and allow more missing DTS packets in stream (#42277)
* Fix AVError and allow more missing DTS in stream init

* Avoid recreating container demux iterator

* Relax missing dts requirement

* Fix spelling error

* Fix error message count

* Add timeout to av.open read

* Increase STREAM_TIMEOUT to 30
2020-10-25 22:54:35 +00:00
Alan Tse
21917e86c9 Fix Tesla attribute refreshing (#42257)
* Fix Tesla attribute refreshing

* Remove extraneous copy
2020-10-25 22:54:34 +00:00
Allen Porter
b6994689b1 Update nest integration with fixes from initial PR (#42250) 2020-10-25 22:54:33 +00:00
Teemu R
0c1c7d797c Only log xiaomi_miio update exceptions once (#41226)
* xiaomi_miio: only log update exceptions once

Replaces #37695

* add som more missed exception logger cases + do not change the control flow as pointed out by @cgtobi

* Use patch&MagickMock from tests.async_mock

* Fix linting for alarm_control_panel

* update the test to verify that the warning on update is only logged when the device was previously available
2020-10-25 22:54:33 +00:00
Paulus Schoutsen
9c60195780 Bumped version to 0.117.0b3 2020-10-23 15:45:06 +00:00
J. Nick Koston
d8fea1c582 Ensure event listener integrations do the queue insert as a callback (#42265) 2020-10-23 15:44:56 +00:00
Erik Montnemery
84ab40c57d Fix Tasmota relay acting as on/off light (#42259) 2020-10-23 15:44:56 +00:00
Alan Tse
aace9dce38 Fix is_on test for Tesla Charger switch (#42251)
closes #41755
2020-10-23 15:44:55 +00:00
TheJulianJES
f6f95d0422 Fix polling of color for ZHA lights not updating (#42248) 2020-10-23 15:44:54 +00:00
BrianWithAHat
e0466d4ac8 Fix MaryTTS filename extensions (#42228)
Co-authored-by: Brian Laferriere <BrianWithAHat@users.noreply.github.com>
2020-10-23 15:44:53 +00:00
151 changed files with 1662 additions and 612 deletions

View File

@@ -161,7 +161,6 @@ homeassistant/components/goalzero/* @tkdrob
homeassistant/components/gogogate2/* @vangorra
homeassistant/components/google_assistant/* @home-assistant/cloud
homeassistant/components/google_cloud/* @lufton
homeassistant/components/google_translate/* @awarecan
homeassistant/components/gpsd/* @fabaff
homeassistant/components/gree/* @cmroche
homeassistant/components/greeneye_monitor/* @jkeljo

View File

@@ -143,8 +143,8 @@ stages:
version="$(homeassistantRelease)"
git clone https://github.com/home-assistant/hassio-version
cd hassio-version
git clone https://github.com/home-assistant/version
cd version
dev_version="$(jq --raw-output '.homeassistant.default' dev.json)"
beta_version="$(jq --raw-output '.homeassistant.default' beta.json)"

View File

@@ -31,7 +31,7 @@ UPDATE_TOPIC = f"{DOMAIN}_update"
ICON = "mdi:package-variant-closed"
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
SERVICE_ADD_TRACKING = "add_tracking"
SERVICE_REMOVE_TRACKING = "remove_tracking"

View File

@@ -276,7 +276,7 @@ class Alert(ToggleEntity):
self._send_done_message = True
if self._message_template is not None:
message = self._message_template.async_render()
message = self._message_template.async_render(parse_result=False)
else:
message = self._name
@@ -291,7 +291,7 @@ class Alert(ToggleEntity):
if self._done_message_template is None:
return
message = self._done_message_template.async_render()
message = self._done_message_template.async_render(parse_result=False)
await self._send_notification_message(message)
@@ -300,7 +300,7 @@ class Alert(ToggleEntity):
msg_payload = {ATTR_MESSAGE: message}
if self._title_template is not None:
title = self._title_template.async_render()
title = self._title_template.async_render(parse_result=False)
msg_payload.update({ATTR_TITLE: title})
if self._data:
msg_payload.update({ATTR_DATA: self._data})

View File

@@ -80,13 +80,17 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
output = {}
if item.get(CONF_TITLE) is not None:
if isinstance(item.get(CONF_TITLE), template.Template):
output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render()
output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render(
parse_result=False
)
else:
output[ATTR_TITLE_TEXT] = item.get(CONF_TITLE)
if item.get(CONF_TEXT) is not None:
if isinstance(item.get(CONF_TEXT), template.Template):
output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render()
output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render(
parse_result=False
)
else:
output[ATTR_MAIN_TEXT] = item.get(CONF_TEXT)
@@ -97,13 +101,17 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
if item.get(CONF_AUDIO) is not None:
if isinstance(item.get(CONF_AUDIO), template.Template):
output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render()
output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render(
parse_result=False
)
else:
output[ATTR_STREAM_URL] = item.get(CONF_AUDIO)
if item.get(CONF_DISPLAY_URL) is not None:
if isinstance(item.get(CONF_DISPLAY_URL), template.Template):
output[ATTR_REDIRECTION_URL] = item[CONF_DISPLAY_URL].async_render()
output[ATTR_REDIRECTION_URL] = item[CONF_DISPLAY_URL].async_render(
parse_result=False
)
else:
output[ATTR_REDIRECTION_URL] = item.get(CONF_DISPLAY_URL)

View File

@@ -271,7 +271,7 @@ class AlexaResponse:
self.reprompt = {
"type": speech_type.value,
key: text.async_render(self.variables),
key: text.async_render(self.variables, parse_result=False),
}
def as_dict(self):

View File

@@ -410,7 +410,7 @@ class APITemplateView(HomeAssistantView):
try:
data = await request.json()
tpl = template.Template(data["template"], request.app["hass"])
return str(tpl.async_render(data.get("variables")))
return tpl.async_render(variables=data.get("variables"), parse_result=False)
except (ValueError, TemplateError) as ex:
return self.json_message(
f"Error rendering template: {ex}", HTTP_BAD_REQUEST

View File

@@ -229,7 +229,7 @@ class ApnsNotificationService(BaseNotificationService):
if isinstance(message, str):
rendered_message = message
elif isinstance(message, template_helper.Template):
rendered_message = message.render()
rendered_message = message.render(parse_result=False)
else:
rendered_message = ""

View File

@@ -78,7 +78,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
def _render(value):
try:
return value_template.async_render({"value": value})
return value_template.async_render({"value": value}, parse_result=False)
except TemplateError:
_LOGGER.exception("Error parsing value")
return value

View File

@@ -280,12 +280,15 @@ async def get_device(hass, host, port, username, password):
except axis.Unauthorized as err:
LOGGER.warning("Connected to device at %s but not registered.", host)
await device.vapix.close()
raise AuthenticationRequired from err
except (asyncio.TimeoutError, axis.RequestError) as err:
LOGGER.error("Error connecting to the Axis device at %s", host)
await device.vapix.close()
raise CannotConnect from err
except axis.AxisException as err:
LOGGER.exception("Unknown Axis communication error occurred")
await device.vapix.close()
raise AuthenticationRequired from err

View File

@@ -78,6 +78,7 @@ class CanaryCamera(CoordinatorEntity, Camera):
def __init__(self, hass, coordinator, location_id, device, timeout, ffmpeg_args):
"""Initialize a Canary security camera."""
super().__init__(coordinator)
Camera.__init__(self)
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = ffmpeg_args
self._location_id = location_id

View File

@@ -2,7 +2,7 @@
"domain": "cloudflare",
"name": "Cloudflare",
"documentation": "https://www.home-assistant.io/integrations/cloudflare",
"requirements": ["pycfdns==1.1.1"],
"requirements": ["pycfdns==1.2.1"],
"codeowners": ["@ludeeus", "@ctalkington"],
"config_flow": true
}

View File

@@ -161,7 +161,7 @@ class DialogflowResponse:
assert self.speech is None
if isinstance(text, template.Template):
text = text.async_render(self.parameters)
text = text.async_render(self.parameters, parse_result=False)
self.speech = text

View File

@@ -168,7 +168,7 @@ async def async_setup_platform(
requester = AiohttpSessionRequester(session, True)
# ensure event handler has been started
with await hass.data[DLNA_DMR_DATA]["lock"]:
async with hass.data[DLNA_DMR_DATA]["lock"]:
server_host = config.get(CONF_LISTEN_IP)
if server_host is None:
server_host = get_local_ip()
@@ -220,7 +220,7 @@ class DlnaDmrDevice(MediaPlayerEntity):
async def _async_on_hass_stop(self, event):
"""Event handler on Home Assistant stop."""
with await self.hass.data[DLNA_DMR_DATA]["lock"]:
async with self.hass.data[DLNA_DMR_DATA]["lock"]:
await self._device.async_unsubscribe_services()
async def async_update(self):

View File

@@ -320,6 +320,16 @@ class DerivativeDSMREntity(DSMREntity):
"""Return the calculated current hourly rate."""
return self._state
@property
def force_update(self):
"""Disable force update."""
return False
@property
def should_poll(self):
"""Enable polling."""
return True
async def async_update(self):
"""Recalculate hourly rate if timestamp has changed.

View File

@@ -132,7 +132,10 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
elif (
self._temp_sensor_device is None
and "unit" in device.properties
and "value" in device.properties
and (
"value" in device.properties
or "heatingThermostatSetpoint" in device.properties
)
and (device.properties.unit == "C" or device.properties.unit == "F")
):
self._temp_sensor_device = FibaroDevice(device)
@@ -141,6 +144,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if (
"setTargetLevel" in device.actions
or "setThermostatSetpoint" in device.actions
or "setHeatingThermostatSetpoint" in device.actions
):
self._target_temp_device = FibaroDevice(device)
self._support_flags |= SUPPORT_TARGET_TEMPERATURE
@@ -317,6 +321,8 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
"""Return the current temperature."""
if self._temp_sensor_device:
device = self._temp_sensor_device.fibaro_device
if "heatingThermostatSetpoint" in device.properties:
return float(device.properties.heatingThermostatSetpoint)
return float(device.properties.value)
return None
@@ -325,6 +331,8 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
"""Return the temperature we try to reach."""
if self._target_temp_device:
device = self._target_temp_device.fibaro_device
if "heatingThermostatSetpointFuture" in device.properties:
return float(device.properties.heatingThermostatSetpointFuture)
return float(device.properties.targetLevel)
return None
@@ -335,5 +343,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if temperature is not None:
if "setThermostatSetpoint" in target.fibaro_device.actions:
target.action("setThermostatSetpoint", self.fibaro_op_mode, temperature)
elif "setHeatingThermostatSetpoint" in target.fibaro_device.actions:
target.action("setHeatingThermostatSetpoint", temperature)
else:
target.action("setTargetLevel", temperature)

View File

@@ -2,7 +2,7 @@
"domain": "frontend",
"name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": ["home-assistant-frontend==20201021.2"],
"requirements": ["home-assistant-frontend==20201021.4"],
"dependencies": [
"api",
"auth",

View File

@@ -121,7 +121,7 @@ class GenericCamera(Camera):
async def async_camera_image(self):
"""Return a still image response from the camera."""
try:
url = self._still_image_url.async_render()
url = self._still_image_url.async_render(parse_result=False)
except TemplateError as err:
_LOGGER.error("Error parsing template %s: %s", self._still_image_url, err)
return self._last_image
@@ -178,7 +178,7 @@ class GenericCamera(Camera):
return None
try:
return self._stream_source.async_render()
return self._stream_source.async_render(parse_result=False)
except TemplateError as err:
_LOGGER.error("Error parsing template %s: %s", self._stream_source, err)
return None

View File

@@ -2,6 +2,6 @@
"domain": "geo_rss_events",
"name": "GeoRSS",
"documentation": "https://www.home-assistant.io/integrations/geo_rss_events",
"requirements": ["georss_generic_client==0.3"],
"requirements": ["georss_generic_client==0.4"],
"codeowners": ["@exxamalte"]
}

View File

@@ -9,7 +9,7 @@ from datetime import timedelta
import logging
from georss_client import UPDATE_OK, UPDATE_OK_NO_DATA
from georss_client.generic_feed import GenericFeed
from georss_generic_client import GenericFeed
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA

View File

@@ -3,5 +3,5 @@
"name": "Google Translate Text-to-Speech",
"documentation": "https://www.home-assistant.io/integrations/google_translate",
"requirements": ["gTTS-token==1.1.3"],
"codeowners": ["@awarecan"]
"codeowners": []
}

View File

@@ -123,9 +123,14 @@ class GoogleProvider(Provider):
data = b""
for idx, part in enumerate(message_parts):
part_token = await self.hass.async_add_executor_job(
token.calculate_token, part
)
try:
part_token = await self.hass.async_add_executor_job(
token.calculate_token, part
)
except ValueError as err:
# If token seed fetching fails.
_LOGGER.warning(err)
return None, None
url_param = {
"ie": "UTF-8",

View File

@@ -3,6 +3,6 @@
"name": "Gree Climate",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/gree",
"requirements": ["greeclimate==0.9.0"],
"requirements": ["greeclimate==0.9.2"],
"codeowners": ["@cmroche"]
}

View File

@@ -2,6 +2,6 @@
"domain": "hikvision",
"name": "Hikvision",
"documentation": "https://www.home-assistant.io/integrations/hikvision",
"requirements": ["pyhik==0.2.7"],
"requirements": ["pyhik==0.2.8"],
"codeowners": ["@mezz64"]
}

View File

@@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"requirements": [
"aiohomekit==0.2.53"
"aiohomekit==0.2.54"
],
"zeroconf": [
"_hap._tcp.local."

View File

@@ -157,7 +157,9 @@ class HpIloSensor(Entity):
ilo_data = getattr(self.hp_ilo_data.data, self._ilo_function)()
if self._sensor_value_template is not None:
ilo_data = self._sensor_value_template.render(ilo_data=ilo_data)
ilo_data = self._sensor_value_template.render(
ilo_data=ilo_data, parse_result=False
)
self._state = ilo_data

View File

@@ -187,7 +187,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
icloud_account = hass.data[DOMAIN].get(account_identifier)
if icloud_account is None:
for account in hass.data[DOMAIN].values():
if account.name == account_identifier:
if account.username == account_identifier:
icloud_account = account
if icloud_account is None:

View File

@@ -183,7 +183,7 @@ class EmailContentSensor(Entity):
ATTR_DATE: email_message["Date"],
ATTR_BODY: EmailContentSensor.get_msg_text(email_message),
}
return self._value_template.render(variables)
return self._value_template.render(variables, parse_result=False)
def sender_allowed(self, email_message):
"""Check if the sender is in the allowed senders list."""

View File

@@ -26,6 +26,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import callback
from homeassistant.helpers import event as event_helper, state as state_helper
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_values import EntityValues
@@ -500,6 +501,7 @@ class InfluxThread(threading.Thread):
self.shutdown = False
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
@callback
def _event_listener(self, event):
"""Listen for new messages on the bus and queue them for Influx."""
item = (time.monotonic(), event)

View File

@@ -268,7 +268,7 @@ class InfluxFluxSensorData:
"""Get the latest data by querying influx."""
_LOGGER.debug(RENDERING_QUERY_MESSAGE, self.query)
try:
rendered_query = self.query.render()
rendered_query = self.query.render(parse_result=False)
except TemplateError as ex:
_LOGGER.error(RENDERING_QUERY_ERROR_MESSAGE, ex)
return
@@ -312,7 +312,7 @@ class InfluxQLSensorData:
"""Get the latest data with a shell command."""
_LOGGER.debug(RENDERING_WHERE_MESSAGE, self.where)
try:
where_clause = self.where.render()
where_clause = self.where.render(parse_result=False)
except TemplateError as ex:
_LOGGER.error(RENDERING_WHERE_ERROR_MESSAGE, ex)
return

View File

@@ -87,13 +87,14 @@ class ScriptIntentHandler(intent.IntentHandler):
if speech is not None:
response.async_set_speech(
speech[CONF_TEXT].async_render(slots), speech[CONF_TYPE]
speech[CONF_TEXT].async_render(slots, parse_result=False),
speech[CONF_TYPE],
)
if card is not None:
response.async_set_card(
card[CONF_TITLE].async_render(slots),
card[CONF_CONTENT].async_render(slots),
card[CONF_TITLE].async_render(slots, parse_result=False),
card[CONF_CONTENT].async_render(slots, parse_result=False),
card[CONF_TYPE],
)

View File

@@ -2,6 +2,6 @@
"domain": "jewish_calendar",
"name": "Jewish Calendar",
"documentation": "https://www.home-assistant.io/integrations/jewish_calendar",
"requirements": ["hdate==0.9.5"],
"requirements": ["hdate==0.9.12"],
"codeowners": ["@tsvi"]
}

View File

@@ -89,7 +89,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity):
self._is_opening = False
self._is_closing = True
state = pypck.lcn_defs.MotorStateModifier.DOWN
self.address_connection.control_motors_outputs(state)
self.address_connection.control_motors_outputs(state, self.reverse_time)
self.async_write_ha_state()
async def async_open_cover(self, **kwargs):
@@ -106,7 +106,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity):
self._is_closing = False
self._is_opening = False
state = pypck.lcn_defs.MotorStateModifier.STOP
self.address_connection.control_motors_outputs(state, self.reverse_time)
self.address_connection.control_motors_outputs(state)
self.async_write_ha_state()
def input_received(self, input_obj):

View File

@@ -144,7 +144,7 @@ async def async_setup(hass, config):
domain = DOMAIN
message.hass = hass
message = message.async_render()
message = message.async_render(parse_result=False)
async_log_entry(hass, name, message, domain, entity_id)
hass.components.frontend.async_register_built_in_panel(

View File

@@ -3,7 +3,7 @@
"name": "Lutron Caséta",
"documentation": "https://www.home-assistant.io/integrations/lutron_caseta",
"requirements": [
"pylutron-caseta==0.7.0"
"pylutron-caseta==0.7.1"
],
"codeowners": [
"@swails"

View File

@@ -385,7 +385,9 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity):
if isinstance(self._code, str):
alarm_code = self._code
else:
alarm_code = self._code.render(from_state=self._state, to_state=state)
alarm_code = self._code.render(
parse_result=False, from_state=self._state, to_state=state
)
check = not alarm_code or code == alarm_code
if not check:
_LOGGER.warning("Invalid code given for %s", state)

View File

@@ -406,7 +406,9 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity):
if isinstance(self._code, str):
alarm_code = self._code
else:
alarm_code = self._code.render(from_state=self._state, to_state=state)
alarm_code = self._code.render(
from_state=self._state, to_state=state, parse_result=False
)
check = not alarm_code or code == alarm_code
if not check:
_LOGGER.warning("Invalid code given for %s", state)

View File

@@ -21,6 +21,8 @@ DEFAULT_VOICE = "cmu-slt-hsmm"
DEFAULT_CODEC = "WAVE_FILE"
DEFAULT_EFFECTS = {}
MAP_MARYTTS_CODEC = {"WAVE_FILE": "wav", "AIFF_FILE": "aiff", "AU_FILE": "au"}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
@@ -81,5 +83,6 @@ class MaryTTSProvider(Provider):
effects = options[CONF_EFFECT]
data = self._mary.speak(message, effects)
audiotype = MAP_MARYTTS_CODEC[self._mary.codec]
return self._mary.codec, data
return audiotype, data

View File

@@ -124,7 +124,7 @@ def setup(hass, config):
def _render_service_value(service, key):
value = service.data[key]
value.hass = hass
return value.async_render()
return value.async_render(parse_result=False)
def put_file(service):
"""Upload file service."""

View File

@@ -322,7 +322,7 @@ MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend(
MQTT_PUBLISH_SCHEMA = vol.Schema(
{
vol.Required(ATTR_TOPIC): valid_publish_topic,
vol.Exclusive(ATTR_PAYLOAD, CONF_PAYLOAD): object,
vol.Exclusive(ATTR_PAYLOAD, CONF_PAYLOAD): cv.string,
vol.Exclusive(ATTR_PAYLOAD_TEMPLATE, CONF_PAYLOAD): cv.string,
vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
@@ -567,7 +567,9 @@ async def async_setup_entry(hass, entry):
retain: bool = call.data[ATTR_RETAIN]
if payload_template is not None:
try:
payload = template.Template(payload_template, hass).async_render()
payload = template.Template(payload_template, hass).async_render(
parse_result=False
)
except template.jinja2.TemplateError as exc:
_LOGGER.error(
"Unable to publish to %s: rendering payload template of "

View File

@@ -338,7 +338,7 @@ class MqttAlarm(
"""Publish via mqtt."""
command_template = self._config[CONF_COMMAND_TEMPLATE]
values = {"action": action, "code": code}
payload = command_template.async_render(**values)
payload = command_template.async_render(**values, parse_result=False)
mqtt.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC],

View File

@@ -557,7 +557,7 @@ class MqttCover(
position = kwargs[ATTR_POSITION]
percentage_position = position
if set_position_template is not None:
position = set_position_template.async_render(**kwargs)
position = set_position_template.async_render(parse_result=False, **kwargs)
else:
position = self.find_in_range_from_percent(position, COVER_PAYLOAD)

View File

@@ -252,7 +252,7 @@ class MqttLightTemplate(
except ValueError:
_LOGGER.warning("Invalid color value received")
if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None:
if self._templates[CONF_WHITE_VALUE_TEMPLATE] is not None:
try:
self._white_value = int(
self._templates[
@@ -441,7 +441,9 @@ class MqttLightTemplate(
mqtt.async_publish(
self.hass,
self._topics[CONF_COMMAND_TOPIC],
self._templates[CONF_COMMAND_ON_TEMPLATE].async_render(**values),
self._templates[CONF_COMMAND_ON_TEMPLATE].async_render(
parse_result=False, **values
),
self._config[CONF_QOS],
self._config[CONF_RETAIN],
)
@@ -464,7 +466,9 @@ class MqttLightTemplate(
mqtt.async_publish(
self.hass,
self._topics[CONF_COMMAND_TOPIC],
self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render(**values),
self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render(
parse_result=False, **values
),
self._config[CONF_QOS],
self._config[CONF_RETAIN],
)

View File

@@ -30,7 +30,11 @@ from homeassistant.helpers import (
config_entry_oauth2_flow,
config_validation as cv,
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
dispatcher_send,
)
from homeassistant.helpers.entity import Entity
from . import api, config_flow, local_auth
@@ -176,7 +180,7 @@ class SignalUpdateCallback(EventCallback):
# This event triggered an update to a device that changed some
# properties which the DeviceManager should already have received.
# Send a signal to refresh state of all listening devices.
dispatcher_send(self._hass, SIGNAL_NEST_UPDATE)
async_dispatcher_send(self._hass, SIGNAL_NEST_UPDATE)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
@@ -203,7 +207,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
auth, config[CONF_PROJECT_ID], config[CONF_SUBSCRIBER_ID]
)
subscriber.set_update_callback(SignalUpdateCallback(hass))
hass.loop.create_task(subscriber.start_async())
asyncio.create_task(subscriber.start_async())
hass.data[DOMAIN][entry.entry_id] = subscriber
for component in PLATFORMS:

View File

@@ -61,6 +61,10 @@ class CodeInvalid(NestAuthError):
"""Raised when invalid authorization code."""
class UnexpectedStateError(HomeAssistantError):
"""Raised when the config flow is invoked in a 'should not happen' case."""
@config_entries.HANDLERS.register(DOMAIN)
class NestFlowHandler(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
@@ -111,7 +115,7 @@ class NestFlowHandler(
async def async_step_init(self, user_input=None):
"""Handle a flow start."""
if self.is_sdm_api():
return None
raise UnexpectedStateError("Step only supported for legacy API")
flows = self.hass.data.get(DATA_FLOW_IMPL, {})
@@ -142,7 +146,7 @@ class NestFlowHandler(
deliver the authentication code.
"""
if self.is_sdm_api():
return None
raise UnexpectedStateError("Step only supported for legacy API")
flow = self.hass.data[DATA_FLOW_IMPL][self.flow_impl]
@@ -185,7 +189,7 @@ class NestFlowHandler(
async def async_step_import(self, info):
"""Import existing auth from Nest."""
if self.is_sdm_api():
return None
raise UnexpectedStateError("Step only supported for legacy API")
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="single_instance_allowed")

View File

@@ -13,5 +13,7 @@ async def async_setup_entry(
) -> None:
"""Set up the sensors."""
if DATA_SDM not in entry.data:
return await async_setup_legacy_entry(hass, entry, async_add_entities)
return await async_setup_sdm_entry(hass, entry, async_add_entities)
await async_setup_legacy_entry(hass, entry, async_add_entities)
return
await async_setup_sdm_entry(hass, entry, async_add_entities)

View File

@@ -18,6 +18,13 @@ from homeassistant.helpers.typing import HomeAssistantType
from .const import DOMAIN, SIGNAL_NEST_UPDATE
DEVICE_TYPE_MAP = {
"sdm.devices.types.CAMERA": "Camera",
"sdm.devices.types.DISPLAY": "Display",
"sdm.devices.types.DOORBELL": "Doorbell",
"sdm.devices.types.THERMOSTAT": "Thermostat",
}
async def async_setup_sdm_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
@@ -46,7 +53,7 @@ class SensorBase(Entity):
self._device = device
@property
def should_pool(self) -> bool:
def should_poll(self) -> bool:
"""Disable polling since entities have state pushed via pubsub."""
return False
@@ -89,28 +96,19 @@ class SensorBase(Entity):
# The API intentionally returns minimal information about specific
# devices, instead relying on traits, but we can infer a generic model
# name based on the type
if self._device.type == "sdm.devices.types.CAMERA":
return "Camera"
if self._device.type == "sdm.devices.types.DISPLAY":
return "Display"
if self._device.type == "sdm.devices.types.DOORBELL":
return "Doorbell"
if self._device.type == "sdm.devices.types.THERMOSTAT":
return "Thermostat"
return None
return DEVICE_TYPE_MAP.get(self._device.type)
async def async_added_to_hass(self):
"""Run when entity is added to register update signal handler."""
async def async_update_state():
"""Update sensor state."""
await self.async_update_ha_state(True)
# Event messages trigger the SIGNAL_NEST_UPDATE, which is intercepted
# here to re-fresh the signals from _device. Unregister this callback
# when the entity is removed.
self.async_on_remove(
async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, async_update_state)
async_dispatcher_connect(
self.hass,
SIGNAL_NEST_UPDATE,
self.async_write_ha_state,
)
)

View File

@@ -25,7 +25,8 @@
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"authorize_url_fail": "Unknown error generating an authorize url."
"authorize_url_fail": "Unknown error generating an authorize url.",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]"
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"

View File

@@ -133,7 +133,7 @@ class BaseNotificationService:
if title:
title.hass = self.hass
kwargs[ATTR_TITLE] = title.async_render()
kwargs[ATTR_TITLE] = title.async_render(parse_result=False)
if self._registered_targets.get(service.service) is not None:
kwargs[ATTR_TARGET] = [self._registered_targets[service.service]]
@@ -141,7 +141,7 @@ class BaseNotificationService:
kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET)
message.hass = self.hass
kwargs[ATTR_MESSAGE] = message.async_render()
kwargs[ATTR_MESSAGE] = message.async_render(parse_result=False)
kwargs[ATTR_DATA] = service.data.get(ATTR_DATA)
await self.async_send_message(**kwargs)
@@ -229,12 +229,12 @@ async def async_setup(hass, config):
payload = {}
message = service.data[ATTR_MESSAGE]
message.hass = hass
payload[ATTR_MESSAGE] = message.async_render()
payload[ATTR_MESSAGE] = message.async_render(parse_result=False)
title = service.data.get(ATTR_TITLE)
if title:
title.hass = hass
payload[ATTR_TITLE] = title.async_render()
payload[ATTR_TITLE] = title.async_render(parse_result=False)
await hass.services.async_call(
pn.DOMAIN, pn.SERVICE_CREATE, payload, blocking=True

View File

@@ -74,6 +74,14 @@ class EventManager:
async def async_start(self) -> bool:
"""Start polling events."""
if await self.device.create_pullpoint_subscription():
# Create subscription manager
self._subscription = self.device.create_subscription_service(
"PullPointSubscription"
)
# Renew immediately
await self.async_renew()
# Initialize events
pullpoint = self.device.create_pullpoint_service()
try:
@@ -87,11 +95,6 @@ class EventManager:
# Parse event initialization
await self.async_parse_messages(response.NotificationMessage)
# Create subscription manager
self._subscription = self.device.create_subscription_service(
"PullPointSubscription"
)
self.started = True
return True

View File

@@ -245,7 +245,9 @@ class Remote:
"""Return device info."""
if self._control is None:
return None
return await self._handle_errors(self._control.get_device_info)
device_info = await self._handle_errors(self._control.get_device_info)
_LOGGER.debug("Fetched device info: %s", str(device_info))
return device_info
async def _handle_errors(self, func, *args):
"""Handle errors from func, set available and reconnect if needed."""

View File

@@ -18,4 +18,7 @@ ATTR_MANUFACTURER = "manufacturer"
ATTR_MODEL_NUMBER = "modelNumber"
ATTR_UDN = "UDN"
DEFAULT_MANUFACTURER = "Panasonic"
DEFAULT_MODEL_NUMBER = "Panasonic Viera"
ERROR_INVALID_PIN_CODE = "invalid_pin_code"

View File

@@ -26,6 +26,8 @@ from .const import (
ATTR_MODEL_NUMBER,
ATTR_REMOTE,
ATTR_UDN,
DEFAULT_MANUFACTURER,
DEFAULT_MODEL_NUMBER,
DOMAIN,
)
@@ -69,11 +71,11 @@ class PanasonicVieraTVEntity(MediaPlayerEntity):
self._device_info = device_info
@property
def unique_id(self) -> str:
def unique_id(self):
"""Return the unique ID of the device."""
if self._device_info is not None:
return self._device_info[ATTR_UDN]
return None
if self._device_info is None:
return None
return self._device_info[ATTR_UDN]
@property
def device_info(self):
@@ -83,8 +85,10 @@ class PanasonicVieraTVEntity(MediaPlayerEntity):
return {
"name": self._name,
"identifiers": {(DOMAIN, self._device_info[ATTR_UDN])},
"manufacturer": self._device_info[ATTR_MANUFACTURER],
"model": self._device_info[ATTR_MODEL_NUMBER],
"manufacturer": self._device_info.get(
ATTR_MANUFACTURER, DEFAULT_MANUFACTURER
),
"model": self._device_info.get(ATTR_MODEL_NUMBER, DEFAULT_MODEL_NUMBER),
}
@property

View File

@@ -119,7 +119,7 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
if title is not None:
try:
title.hass = hass
title = title.async_render()
title = title.async_render(parse_result=False)
except TemplateError as ex:
_LOGGER.error("Error rendering title %s: %s", title, ex)
title = title.template
@@ -128,7 +128,7 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
try:
message.hass = hass
message = message.async_render()
message = message.async_render(parse_result=False)
except TemplateError as ex:
_LOGGER.error("Error rendering message %s: %s", message, ex)
message = message.template

View File

@@ -55,7 +55,7 @@ class Pi4ioe5v9Switch(SwitchEntity):
self._name = name or DEVICE_DEFAULT_NAME
self._pin = pin
self._invert_logic = invert_logic
self._state = False
self._state = not invert_logic
@property
def name(self):

View File

@@ -84,7 +84,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
if resource_template is not None:
resource_template.hass = hass
resource = resource_template.render()
resource = resource_template.render(parse_result=False)
if value_template is not None:
value_template.hass = hass
@@ -189,6 +189,6 @@ class RestBinarySensor(BinarySensorEntity):
async def async_update(self):
"""Get the latest data from REST API and updates the state."""
if self._resource_template is not None:
self.rest.set_url(self._resource_template.render())
self.rest.set_url(self._resource_template.render(parse_result=False))
await self.rest.async_update()

View File

@@ -163,7 +163,7 @@ class RestNotificationService(BaseNotificationService):
key: _data_template_creator(item) for key, item in value.items()
}
value.hass = self._hass
return value.async_render(kwargs)
return value.async_render(kwargs, parse_result=False)
data.update(_data_template_creator(self._data_template))

View File

@@ -103,7 +103,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
if resource_template is not None:
resource_template.hass = hass
resource = resource_template.render()
resource = resource_template.render(parse_result=False)
if username and password:
if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION:
@@ -202,7 +202,7 @@ class RestSensor(Entity):
async def async_update(self):
"""Get the latest data from REST API and update the state."""
if self._resource_template is not None:
self.rest.set_url(self._resource_template.render())
self.rest.set_url(self._resource_template.render(parse_result=False))
await self.rest.async_update()

View File

@@ -162,7 +162,7 @@ class RestSwitch(SwitchEntity):
async def async_turn_on(self, **kwargs):
"""Turn the device on."""
body_on_t = self._body_on.async_render()
body_on_t = self._body_on.async_render(parse_result=False)
try:
req = await self.set_device_state(body_on_t)
@@ -178,7 +178,7 @@ class RestSwitch(SwitchEntity):
async def async_turn_off(self, **kwargs):
"""Turn the device off."""
body_off_t = self._body_off.async_render()
body_off_t = self._body_off.async_render(parse_result=False)
try:
req = await self.set_device_state(body_off_t)

View File

@@ -93,17 +93,22 @@ async def async_setup(hass, config):
payload = None
if template_payload:
payload = bytes(
template_payload.async_render(variables=service.data), "utf-8"
template_payload.async_render(
variables=service.data, parse_result=False
),
"utf-8",
)
request_url = template_url.async_render(variables=service.data)
request_url = template_url.async_render(
variables=service.data, parse_result=False
)
headers = None
if template_headers:
headers = {}
for header_name, template_header in template_headers.items():
headers[header_name] = template_header.async_render(
variables=service.data
variables=service.data, parse_result=False
)
if content_type:

View File

@@ -263,10 +263,10 @@ class RMVDepartureData:
if not dest_found:
continue
elif self._lines and journey["number"] not in self._lines:
if self._lines and journey["number"] not in self._lines:
continue
elif journey["minutes"] < self._time_offset:
if journey["minutes"] < self._time_offset:
continue
for attr in ["direction", "departure_time", "product", "minutes"]:

View File

@@ -2,6 +2,6 @@
"domain": "rpi_gpio_pwm",
"name": "pigpio Daemon PWM LED",
"documentation": "https://www.home-assistant.io/integrations/rpi_gpio_pwm",
"requirements": ["pwmled==1.5.3"],
"requirements": ["pwmled==1.6.6"],
"codeowners": []
}

View File

@@ -83,17 +83,19 @@ class RssView(HomeAssistantView):
response += "<rss>\n"
if self._title is not None:
response += " <title>%s</title>\n" % escape(self._title.async_render())
response += " <title>%s</title>\n" % escape(
self._title.async_render(parse_result=False)
)
for item in self._items:
response += " <item>\n"
if "title" in item:
response += " <title>"
response += escape(item["title"].async_render())
response += escape(item["title"].async_render(parse_result=False))
response += "</title>\n"
if "description" in item:
response += " <description>"
response += escape(item["description"].async_render())
response += escape(item["description"].async_render(parse_result=False))
response += "</description>\n"
response += " </item>\n"

View File

@@ -45,7 +45,9 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
if args_compiled:
try:
rendered_args = args_compiled.async_render(service.data)
rendered_args = args_compiled.async_render(
variables=service.data, parse_result=False
)
except TemplateError as ex:
_LOGGER.exception("Error rendering command template: %s", ex)
return

View File

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

View File

@@ -118,7 +118,7 @@ def _async_templatize_blocks(hass, value):
}
tmpl = template.Template(value, hass=hass)
return tmpl.async_render()
return tmpl.async_render(parse_result=False)
class SlackNotificationService(BaseNotificationService):

View File

@@ -98,7 +98,7 @@ async def async_setup(hass, config):
for site_id in site_ids:
payload = json.dumps({"siteId": site_id})
hass.components.mqtt.async_publish(
FEEDBACK_ON_TOPIC, None, qos=0, retain=False
FEEDBACK_ON_TOPIC, "", qos=0, retain=False
)
hass.components.mqtt.async_publish(
topic, payload, qos=int(state), retain=state

View File

@@ -2,7 +2,7 @@
"domain": "spotify",
"name": "Spotify",
"documentation": "https://www.home-assistant.io/integrations/spotify",
"requirements": ["spotipy==2.16.0"],
"requirements": ["spotipy==2.16.1"],
"zeroconf": ["_spotify-connect._tcp.local."],
"dependencies": ["http"],
"codeowners": ["@frenck"],

View File

@@ -65,7 +65,7 @@ CONTENT_TYPE_TO_CHILD_TYPE = {
"Genres": MEDIA_TYPE_GENRE,
}
BROWSE_LIMIT = 500
BROWSE_LIMIT = 1000
async def build_item_response(player, payload):

View File

@@ -6,7 +6,7 @@
"@rajlaud"
],
"requirements": [
"pysqueezebox==0.5.1"
"pysqueezebox==0.5.4"
],
"config_flow": true
}

View File

@@ -20,3 +20,6 @@ MIN_SEGMENT_DURATION = 1.5 # Each segment is at least this many seconds
PACKETS_TO_WAIT_FOR_AUDIO = 20 # Some streams have an audio stream with no audio
MAX_TIMESTAMP_GAP = 10000 # seconds - anything from 10 to 50000 is probably reasonable
MAX_MISSING_DTS = 6 # Number of packets missing DTS to allow
STREAM_TIMEOUT = 30 # Timeout for reading stream

View File

@@ -6,7 +6,13 @@ import time
import av
from .const import MAX_TIMESTAMP_GAP, MIN_SEGMENT_DURATION, PACKETS_TO_WAIT_FOR_AUDIO
from .const import (
MAX_MISSING_DTS,
MAX_TIMESTAMP_GAP,
MIN_SEGMENT_DURATION,
PACKETS_TO_WAIT_FOR_AUDIO,
STREAM_TIMEOUT,
)
from .core import Segment, StreamBuffer
_LOGGER = logging.getLogger(__name__)
@@ -62,7 +68,7 @@ def stream_worker(hass, stream, quit_event):
def _stream_worker_internal(hass, stream, quit_event):
"""Handle consuming streams."""
container = av.open(stream.source, options=stream.options)
container = av.open(stream.source, options=stream.options, timeout=STREAM_TIMEOUT)
try:
video_stream = container.streams.video[0]
except (KeyError, IndexError):
@@ -81,13 +87,15 @@ def _stream_worker_internal(hass, stream, quit_event):
if audio_stream and audio_stream.profile is None:
audio_stream = None
# Iterator for demuxing
container_packets = None
# The presentation timestamps of the first packet in each stream we receive
# Use to adjust before muxing or outputting, but we don't adjust internally
first_pts = {}
# The decoder timestamps of the latest packet in each stream we processed
last_dts = None
# Keep track of consecutive packets without a dts to detect end of stream.
last_packet_was_without_dts = False
missing_dts = 0
# Holds the buffers for each stream provider
outputs = None
# Keep track of the number of segments we've processed
@@ -102,8 +110,8 @@ def _stream_worker_internal(hass, stream, quit_event):
# 2 - seeking can be problematic https://trac.ffmpeg.org/ticket/7815
def peek_first_pts():
nonlocal first_pts, audio_stream
missing_dts = False
nonlocal first_pts, audio_stream, container_packets
missing_dts = 0
def empty_stream_dict():
return {
@@ -112,17 +120,20 @@ def _stream_worker_internal(hass, stream, quit_event):
}
try:
container_packets = container.demux((video_stream, audio_stream))
first_packet = empty_stream_dict()
first_pts = empty_stream_dict()
# Get to first video keyframe
while first_packet[video_stream] is None:
packet = next(container.demux())
packet = next(container_packets)
if (
packet.dts is None
): # Allow single packet with no dts, raise error on second
if missing_dts:
raise av.AVError
missing_dts = True
): # Allow MAX_MISSING_DTS packets with no dts, raise error on the next one
if missing_dts >= MAX_MISSING_DTS:
raise StopIteration(
f"Invalid data - got {MAX_MISSING_DTS+1} packets with missing DTS while initializing"
)
missing_dts += 1
continue
if packet.stream == video_stream and packet.is_keyframe:
first_packet[video_stream] = packet
@@ -131,13 +142,15 @@ def _stream_worker_internal(hass, stream, quit_event):
while any(
[pts is None for pts in {**first_packet, **first_pts}.values()]
) and (len(initial_packets) < PACKETS_TO_WAIT_FOR_AUDIO):
packet = next(container.demux((video_stream, audio_stream)))
packet = next(container_packets)
if (
packet.dts is None
): # Allow single packet with no dts, raise error on second
if missing_dts:
raise av.AVError
missing_dts = True
): # Allow MAX_MISSING_DTS packet with no dts, raise error on the next one
if missing_dts >= MAX_MISSING_DTS:
raise StopIteration(
f"Invalid data - got {MAX_MISSING_DTS+1} packets with missing DTS while initializing"
)
missing_dts += 1
continue
if (
first_packet[packet.stream] is None
@@ -223,16 +236,16 @@ def _stream_worker_internal(hass, stream, quit_event):
if len(initial_packets) > 0:
packet = initial_packets.popleft()
else:
packet = next(container.demux((video_stream, audio_stream)))
packet = next(container_packets)
if packet.dts is None:
_LOGGER.error("Stream packet without dts detected, skipping...")
# Allow a single packet without dts before terminating the stream.
if last_packet_was_without_dts:
# If we get a "flushing" packet, the stream is done
raise StopIteration("No dts in consecutive packets")
last_packet_was_without_dts = True
# Allow MAX_MISSING_DTS consecutive packets without dts. Terminate the stream on the next one.
if missing_dts >= MAX_MISSING_DTS:
raise StopIteration(
f"No dts in {MAX_MISSING_DTS+1} consecutive packets"
)
missing_dts += 1
continue
last_packet_was_without_dts = False
missing_dts = 0
except (av.AVError, StopIteration) as ex:
_LOGGER.error("Error demuxing stream: %s", str(ex))
finalize_stream()

View File

@@ -316,6 +316,8 @@ class SynoApi:
)
self._with_surveillance_station = bool(
self._fetching_entities.get(SynoSurveillanceStation.CAMERA_API_KEY)
) or bool(
self._fetching_entities.get(SynoSurveillanceStation.HOME_MODE_API_KEY)
)
# Reset not used API, information is not reset since it's used in device_info

View File

@@ -275,7 +275,6 @@ def _login_and_fetch_syno_info(api, otp_code):
if (
not api.information.serial
or api.utilisation.cpu_user_load is None
or not api.storage.disks_ids
or not api.storage.volumes_ids
or not api.network.macs
):

View File

@@ -19,12 +19,10 @@ from homeassistant.components.mqtt.subscription import (
)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.typing import HomeAssistantType
from . import device_automation, discovery
from .const import CONF_DISCOVERY_PREFIX, DATA_REMOVE_DISCOVER_COMPONENT, PLATFORMS
from .discovery import TASMOTA_DISCOVERY_DEVICE
_LOGGER = logging.getLogger(__name__)
@@ -55,13 +53,11 @@ async def async_setup_entry(hass, entry):
tasmota_mqtt = TasmotaMQTTClient(_publish, _subscribe_topics, _unsubscribe_topics)
async def async_discover_device(config, mac):
"""Discover and add a Tasmota device."""
await async_setup_device(hass, mac, config, entry, tasmota_mqtt)
device_registry = await hass.helpers.device_registry.async_get_registry()
hass.data[
DATA_REMOVE_DISCOVER_COMPONENT.format("device")
] = async_dispatcher_connect(hass, TASMOTA_DISCOVERY_DEVICE, async_discover_device)
def async_discover_device(config, mac):
"""Discover and add a Tasmota device."""
async_setup_device(hass, mac, config, entry, tasmota_mqtt, device_registry)
async def start_platforms():
await device_automation.async_setup_entry(hass, entry)
@@ -73,7 +69,9 @@ async def async_setup_entry(hass, entry):
)
discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX]
await discovery.async_start(hass, discovery_prefix, entry, tasmota_mqtt)
await discovery.async_start(
hass, discovery_prefix, entry, tasmota_mqtt, async_discover_device
)
hass.async_create_task(start_platforms())
return True
@@ -97,7 +95,6 @@ async def async_unload_entry(hass, entry):
# disable discovery
await discovery.async_stop(hass)
hass.data.pop(DEVICE_MACS)
hass.data[DATA_REMOVE_DISCOVER_COMPONENT.format("device")]()
hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))()
for component in PLATFORMS:
hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(component))()
@@ -105,9 +102,8 @@ async def async_unload_entry(hass, entry):
return True
async def _remove_device(hass, config_entry, mac, tasmota_mqtt):
def _remove_device(hass, config_entry, mac, tasmota_mqtt, device_registry):
"""Remove device from device registry."""
device_registry = await hass.helpers.device_registry.async_get_registry()
device = device_registry.async_get_device(set(), {(CONNECTION_NETWORK_MAC, mac)})
if device is None:
@@ -118,9 +114,8 @@ async def _remove_device(hass, config_entry, mac, tasmota_mqtt):
clear_discovery_topic(mac, config_entry.data[CONF_DISCOVERY_PREFIX], tasmota_mqtt)
async def _update_device(hass, config_entry, config):
def _update_device(hass, config_entry, config, device_registry):
"""Add or update device registry."""
device_registry = await hass.helpers.device_registry.async_get_registry()
config_entry_id = config_entry.entry_id
device_info = {
"connections": {(CONNECTION_NETWORK_MAC, config[CONF_MAC])},
@@ -135,9 +130,9 @@ async def _update_device(hass, config_entry, config):
hass.data[DEVICE_MACS][device.id] = config[CONF_MAC]
async def async_setup_device(hass, mac, config, config_entry, tasmota_mqtt):
def async_setup_device(hass, mac, config, config_entry, tasmota_mqtt, device_registry):
"""Set up the Tasmota device."""
if not config:
await _remove_device(hass, config_entry, mac, tasmota_mqtt)
_remove_device(hass, config_entry, mac, tasmota_mqtt, device_registry)
else:
await _update_device(hass, config_entry, config)
_update_device(hass, config_entry, config, device_registry)

View File

@@ -21,7 +21,6 @@ from .const import DOMAIN, PLATFORMS
_LOGGER = logging.getLogger(__name__)
ALREADY_DISCOVERED = "tasmota_discovered_components"
TASMOTA_DISCOVERY_DEVICE = "tasmota_discovery_device"
TASMOTA_DISCOVERY_ENTITY_NEW = "tasmota_discovery_entity_new_{}"
TASMOTA_DISCOVERY_ENTITY_UPDATED = "tasmota_discovery_entity_updated_{}_{}_{}_{}"
TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance"
@@ -29,6 +28,9 @@ TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance"
def clear_discovery_hash(hass, discovery_hash):
"""Clear entry in ALREADY_DISCOVERED list."""
if ALREADY_DISCOVERED not in hass.data:
# Discovery is shutting down
return
del hass.data[ALREADY_DISCOVERED][discovery_hash]
@@ -38,7 +40,7 @@ def set_discovery_hash(hass, discovery_hash):
async def async_start(
hass: HomeAssistantType, discovery_topic, config_entry, tasmota_mqtt
hass: HomeAssistantType, discovery_topic, config_entry, tasmota_mqtt, setup_device
) -> bool:
"""Start Tasmota device discovery."""
@@ -92,9 +94,7 @@ async def async_start(
_LOGGER.debug("Received discovery data for tasmota device: %s", mac)
tasmota_device_config = tasmota_get_device_config(payload)
async_dispatcher_send(
hass, TASMOTA_DISCOVERY_DEVICE, tasmota_device_config, mac
)
setup_device(tasmota_device_config, mac)
if not payload:
return

View File

@@ -80,10 +80,11 @@ class TasmotaLight(
self._setup_from_entity()
async def discovery_update(self, update):
async def discovery_update(self, update, write_state=True):
"""Handle updated discovery message."""
await super().discovery_update(update, write_state=False)
self._setup_from_entity()
await super().discovery_update(update)
self.async_write_ha_state()
def _setup_from_entity(self):
"""(Re)Setup the entity."""

View File

@@ -3,7 +3,7 @@
"name": "Tasmota (beta)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tasmota",
"requirements": ["hatasmota==0.0.20"],
"requirements": ["hatasmota==0.0.25"],
"dependencies": ["mqtt"],
"mqtt": ["tasmota/discovery/#"],
"codeowners": ["@emontnemery"]

View File

@@ -38,11 +38,12 @@ class TasmotaEntity(Entity):
await self._tasmota_entity.unsubscribe_topics()
await super().async_will_remove_from_hass()
async def discovery_update(self, update):
async def discovery_update(self, update, write_state=True):
"""Handle updated discovery message."""
self._tasmota_entity.config_update(update)
await self._subscribe_topics()
self.async_write_ha_state()
if write_state:
self.async_write_ha_state()
async def _subscribe_topics(self):
"""(Re)Subscribe to topics."""

View File

@@ -1,6 +1,7 @@
"""Support for Tasmota sensors."""
from typing import Optional
from hatasmota import status_sensor
from hatasmota.const import (
SENSOR_AMBIENT,
SENSOR_APPARENT_POWERUSAGE,
@@ -162,7 +163,7 @@ class TasmotaSensor(TasmotaAvailability, TasmotaDiscoveryUpdate, Entity):
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
# Hide status sensors to not overwhelm users
if self._tasmota_entity.quantity == SENSOR_STATUS_SIGNAL:
if self._tasmota_entity.quantity in status_sensor.SENSORS:
return False
return True

View File

@@ -136,7 +136,9 @@ class TcpSensor(Entity):
if self._config[CONF_VALUE_TEMPLATE] is not None:
try:
self._state = self._config[CONF_VALUE_TEMPLATE].render(value=value)
self._state = self._config[CONF_VALUE_TEMPLATE].render(
parse_result=False, value=value
)
return
except TemplateError:
_LOGGER.error(

View File

@@ -329,7 +329,9 @@ async def async_setup(hass, config):
else:
attribute_templ.hass = hass
try:
data[attribute] = attribute_templ.async_render()
data[attribute] = attribute_templ.async_render(
parse_result=False
)
except TemplateError as exc:
_LOGGER.error(
"TemplateError in %s: %s -> %s",

View File

@@ -259,6 +259,7 @@ class CoverTemplate(TemplateEntity, CoverEntity):
return
state = str(result).lower()
if state in _VALID_STATES:
if state in ("true", STATE_OPEN):
self._position = 100

View File

@@ -368,6 +368,7 @@ class TemplateFan(TemplateEntity, FanEntity):
def _update_speed(self, speed):
# Validate speed
speed = str(speed)
if speed in self._speed_list:
self._speed = speed
elif speed in [STATE_UNAVAILABLE, STATE_UNKNOWN]:

View File

@@ -412,16 +412,21 @@ class LightTemplate(TemplateEntity, LightEntity):
self._available = True
return
if isinstance(result, bool):
self._state = result
return
state = str(result).lower()
if state in _VALID_STATES:
self._state = state in ("true", STATE_ON)
else:
_LOGGER.error(
"Received invalid light is_on state: %s. Expected: %s",
state,
", ".join(_VALID_STATES),
)
self._state = None
return
_LOGGER.error(
"Received invalid light is_on state: %s. Expected: %s",
state,
", ".join(_VALID_STATES),
)
self._state = None
@callback
def _update_temperature(self, render):

View File

@@ -282,7 +282,7 @@ class TeslaDevice(CoordinatorEntity):
@property
def device_state_attributes(self):
"""Return the state attributes of the device."""
attr = self._attributes.copy()
attr = self._attributes
if self.tesla_device.has_battery():
attr[ATTR_BATTERY_LEVEL] = self.tesla_device.battery_level()
attr[ATTR_BATTERY_CHARGING] = self.tesla_device.battery_charging()
@@ -310,4 +310,5 @@ class TeslaDevice(CoordinatorEntity):
This assumes the coordinator has updated the controller.
"""
self.tesla_device.refresh()
self._attributes = self.tesla_device.attrs.copy()
self.async_write_ha_state()

View File

@@ -2,7 +2,6 @@
import logging
from homeassistant.components.switch import SwitchEntity
from homeassistant.const import STATE_ON
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
@@ -42,7 +41,7 @@ class ChargerSwitch(TeslaDevice, SwitchEntity):
"""Get whether the switch is in on state."""
if self.tesla_device.is_charging() is None:
return None
return self.tesla_device.is_charging() == STATE_ON
return self.tesla_device.is_charging()
class RangeSwitch(TeslaDevice, SwitchEntity):

View File

@@ -13,6 +13,8 @@ from pyHS100 import (
from homeassistant.helpers.typing import HomeAssistantType
from .const import DOMAIN as TPLINK_DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -127,3 +129,29 @@ def get_static_devices(config_data) -> SmartDevices:
"Failed to setup device %s due to %s; not retrying", host, sde
)
return SmartDevices(lights, switches)
def add_available_devices(hass, device_type, device_class):
"""Get sysinfo for all devices."""
devices = hass.data[TPLINK_DOMAIN][device_type]
if f"{device_type}_remaining" in hass.data[TPLINK_DOMAIN]:
devices = hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"]
entities_ready = []
devices_unavailable = []
for device in devices:
try:
device.get_sysinfo()
entities_ready.append(device_class(device))
except SmartDeviceException as ex:
devices_unavailable.append(device)
_LOGGER.warning(
"Unable to communicate with device %s: %s",
device.host,
ex,
)
hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] = devices_unavailable
return entities_ready

View File

@@ -16,7 +16,7 @@ from homeassistant.components.light import (
SUPPORT_COLOR_TEMP,
LightEntity,
)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util.color import (
@@ -26,6 +26,7 @@ from homeassistant.util.color import (
import homeassistant.util.dt as dt_util
from . import CONF_LIGHT, DOMAIN as TPLINK_DOMAIN
from .common import add_available_devices
PARALLEL_UPDATES = 0
SCAN_INTERVAL = timedelta(seconds=5)
@@ -60,20 +61,15 @@ SLEEP_TIME = 2
async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities):
"""Set up lights."""
devices = hass.data[TPLINK_DOMAIN][CONF_LIGHT]
entities = []
entities = await hass.async_add_executor_job(
add_available_devices, hass, CONF_LIGHT, TPLinkSmartBulb
)
await hass.async_add_executor_job(get_devices_sysinfo, devices)
for device in devices:
entities.append(TPLinkSmartBulb(device))
if entities:
async_add_entities(entities, update_before_add=True)
async_add_entities(entities, update_before_add=True)
def get_devices_sysinfo(devices):
"""Get sysinfo for all devices."""
for device in devices:
device.get_sysinfo()
if hass.data[TPLINK_DOMAIN][f"{CONF_LIGHT}_remaining"]:
raise PlatformNotReady
def brightness_to_percentage(byt):

View File

@@ -11,10 +11,12 @@ from homeassistant.components.switch import (
SwitchEntity,
)
from homeassistant.const import ATTR_VOLTAGE
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.typing import HomeAssistantType
from . import CONF_SWITCH, DOMAIN as TPLINK_DOMAIN
from .common import add_available_devices
PARALLEL_UPDATES = 0
@@ -29,20 +31,15 @@ SLEEP_TIME = 2
async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities):
"""Set up switches."""
devices = hass.data[TPLINK_DOMAIN][CONF_SWITCH]
entities = []
entities = await hass.async_add_executor_job(
add_available_devices, hass, CONF_SWITCH, SmartPlugSwitch
)
await hass.async_add_executor_job(get_devices_sysinfo, devices)
for device in devices:
entities.append(SmartPlugSwitch(device))
if entities:
async_add_entities(entities, update_before_add=True)
async_add_entities(entities, update_before_add=True)
def get_devices_sysinfo(devices):
"""Get sysinfo for all devices."""
for device in devices:
device.get_sysinfo()
if hass.data[TPLINK_DOMAIN][f"{CONF_SWITCH}_remaining"]:
raise PlatformNotReady
class SmartPlugSwitch(SwitchEntity):

View File

@@ -21,6 +21,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import callback
from homeassistant.helpers import state as state_helper
import homeassistant.helpers.config_validation as cv
@@ -165,6 +166,7 @@ class WatsonIOTThread(threading.Thread):
self.shutdown = False
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
@callback
def _event_listener(self, event):
"""Listen for new messages on the bus and queue them for Watson IoT."""
item = (time.monotonic(), event)

View File

@@ -1,5 +1,4 @@
{
"title": "xbox",
"config": {
"step": {
"pick_implementation": {

View File

@@ -13,6 +13,5 @@
"title": "Pick Authentication Method"
}
}
},
"title": "xbox"
}
}

View File

@@ -142,7 +142,7 @@ class XiaomiCamera(Camera):
"""Return a still image response from the camera."""
try:
host = self.host.async_render()
host = self.host.async_render(parse_result=False)
except TemplateError as exc:
_LOGGER.error("Error parsing template %s: %s", self.host, exc)
return self._last_image

View File

@@ -195,8 +195,9 @@ class AirMonitorS1(AirMonitorB1):
self._humidity = state.humidity
self._available = True
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
class AirMonitorV1(AirMonitorB1):
@@ -210,8 +211,9 @@ class AirMonitorV1(AirMonitorB1):
self._air_quality_index = state.aqi
self._available = True
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
@property
def unit_of_measurement(self):

View File

@@ -3,7 +3,7 @@
from functools import partial
import logging
from miio.gateway import GatewayException
from miio import DeviceException
from homeassistant.components.alarm_control_panel import (
SUPPORT_ALARM_ARM_AWAY,
@@ -103,7 +103,7 @@ class XiaomiGatewayAlarm(AlarmControlPanelEntity):
partial(func, *args, **kwargs)
)
_LOGGER.debug("Response received from miio device: %s", result)
except GatewayException as exc:
except DeviceException as exc:
_LOGGER.error(mask_error, exc)
async def async_alarm_arm_away(self, code=None):
@@ -122,9 +122,11 @@ class XiaomiGatewayAlarm(AlarmControlPanelEntity):
"""Fetch state from the device."""
try:
state = await self.hass.async_add_executor_job(self._gateway.alarm.status)
except GatewayException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
except DeviceException as ex:
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)

View File

@@ -655,8 +655,10 @@ class XiaomiGenericDevice(FanEntity):
return result == SUCCESS
except DeviceException as exc:
_LOGGER.error(mask_error, exc)
self._available = False
if self._available:
_LOGGER.error(mask_error, exc)
self._available = False
return False
async def async_turn_on(self, speed: str = None, **kwargs) -> None:
@@ -785,8 +787,9 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
@property
def speed_list(self) -> list:
@@ -1029,8 +1032,9 @@ class XiaomiAirHumidifier(XiaomiGenericDevice):
)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
@property
def speed_list(self) -> list:
@@ -1138,8 +1142,9 @@ class XiaomiAirFresh(XiaomiGenericDevice):
)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
@property
def speed_list(self) -> list:

View File

@@ -65,7 +65,7 @@ class XiaomiGatewayDevice(Entity):
self._entry = entry
self._unique_id = sub_device.sid
self._name = f"{sub_device.name} ({sub_device.sid})"
self._available = None
self._available = False
@property
def unique_id(self):
@@ -100,5 +100,6 @@ class XiaomiGatewayDevice(Entity):
await self.hass.async_add_executor_job(self._sub_device.update)
self._available = True
except gateway.GatewayException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)

View File

@@ -324,8 +324,10 @@ class XiaomiPhilipsAbstractLight(LightEntity):
return result == SUCCESS
except DeviceException as exc:
_LOGGER.error(mask_error, exc)
self._available = False
if self._available:
_LOGGER.error(mask_error, exc)
self._available = False
return False
async def async_turn_on(self, **kwargs):
@@ -356,8 +358,10 @@ class XiaomiPhilipsAbstractLight(LightEntity):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)
@@ -380,8 +384,10 @@ class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)
@@ -536,8 +542,10 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)
@@ -593,8 +601,10 @@ class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)
@@ -637,8 +647,10 @@ class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)
@@ -778,8 +790,10 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)
@@ -932,8 +946,10 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
try:
state = await self.hass.async_add_executor_job(self._light.status)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
return
_LOGGER.debug("Got new state: %s", state)

View File

@@ -233,8 +233,9 @@ class XiaomiAirQualityMonitor(Entity):
)
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
if self._available:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
class XiaomiGatewaySensor(XiaomiGatewayDevice):

Some files were not shown because too many files have changed in this diff Show More