Compare commits

..

110 Commits

Author SHA1 Message Date
Franck Nijhof
7c783dc1b4 Merge pull request #43102 from home-assistant/rc
Co-authored-by: Sören Oldag <soeren_oldag@freenet.de>
Co-authored-by: Hmmbob <33529490+hmmbob@users.noreply.github.com>
Co-authored-by: jjlawren <jjlawren@users.noreply.github.com>
2020-11-11 18:30:59 +01:00
Franck Nijhof
650f3413a4 Bumped version to 0.117.6 2020-11-11 18:04:17 +01:00
jjlawren
176c2f3978 Fix Plex auth issues by setting header (#43081) 2020-11-11 18:01:48 +01:00
Hmmbob
9d617d446e Bump gTTS-token to 1.1.4 (#43015) 2020-11-11 17:59:07 +01:00
Sören Oldag
ea5f621351 Bump pwmled to v1.6.7 (#42903) 2020-11-11 17:58:58 +01:00
Sören Oldag
b49aa9485c Remove unneeded state restoration of the physical device in rpi_gpio_pwm integration (#42804)
* Add @soldag to code owners of rpi_gpio_pwm platform

* Remove unneeded state restoration of the physical device in rpi_gpio_pwm integration
2020-11-11 17:58:48 +01:00
Paulus Schoutsen
22f16759e5 Merge pull request #42874 from home-assistant/rc 2020-11-05 16:33:21 +01:00
rikroe
8895c65dd4 Bump bimmer_connected to 0.7.12 (#42875)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2020-11-05 15:32:12 +00:00
Paulus Schoutsen
dfed834617 Bumped version to 0.117.5 2020-11-05 13:45:58 +00:00
Andreas Billmeier
7d0f5fa31a Revert "Fix broken maxcube component" (#42859)
This reverts commit 898f50386f.
2020-11-05 13:45:43 +00:00
cgtobi
b65c92ce91 Fix Netatmo public weather sensor bug (#42858) 2020-11-05 13:45:42 +00:00
Aaron Bach
7c084975b7 Fix missing/incorrect SimpliSafe entities (#42846)
* Entity types

* Don't overdo it

* Fix missing/incorrect SimpliSafe entities
2020-11-05 13:44:57 +00:00
Aaron Bach
943cc243b5 Fix missing sensor exceptions in SimpliSafe (#42845)
* Fix missing sensor exceptions in SimpliSafe

* Entity types

* Don't overdo it
2020-11-05 13:44:56 +00:00
Aaron Bach
ba48fd6c51 Clean up SimpliSafe binary sensor implementation (#42841)
* Clean up SimpliSafe binary sensor implementation

* Cleanup

* Constant name
2020-11-05 13:44:55 +00:00
Erik Montnemery
2f1d30fa85 Bump hatasmota to 0.0.25.1 (#42850) 2020-11-04 21:59:08 +01:00
Paulus Schoutsen
7a96f8a4eb Merge pull request #42836 from home-assistant/rc 2020-11-04 17:20:54 +01:00
Paulus Schoutsen
df6ee2e51f Bumped version to 0.117.4 2020-11-04 15:47:28 +00:00
Maciej Bieniek
6bacda3cee Call coordinator.shutdown() when ConfigEntryNotReady (#42833) 2020-11-04 15:47:11 +00:00
Rob Bierbooms
d4b68454a3 Cleanup dispatchers when unloading rfxtrx (#42803)
* Cleanup dispatchers

* Fix name

* Refactor code

* Fix typo in function description

* Fix pylint error

* Change function definition

* Move cleanup_callbacks data to domain
2020-11-04 15:47:11 +00:00
Clifford Roche
36b099592e Update greeclimate to 0.9.5 (#42796)
Provides better discovery across subnets in some cases
2020-11-04 15:47:09 +00:00
Paulus Schoutsen
d156e95f8a Merge pull request #42824 from home-assistant/rc 2020-11-04 12:22:18 +01:00
Paulus Schoutsen
f3c7b77afa Bumped version to 0.117.3 2020-11-04 09:24:41 +00:00
Paulus Schoutsen
45dbd49852 Fix evohome scheduling coroutines (#42821) 2020-11-04 09:24:33 +00:00
J. Nick Koston
e4f88a02f7 Fix rest sensors with resource templates (#42818) 2020-11-04 09:24:32 +00:00
Rob Bierbooms
32652fde48 Filter rfxtrx replace devices in option flow on existing config entry (#42800)
* Fix function _can_replace_device

* Rearrange
2020-11-04 09:24:31 +00:00
rikroe
0dc926a131 Bump bimmer_connected to 0.7.11 (#42788) 2020-11-04 09:24:30 +00:00
Michael
43486917bf Fix broken 2fa authentication in synology_dsm (#42775) 2020-11-04 09:24:30 +00:00
cgtobi
d967ecc8c6 Bump pyatmo to 4.2.0 (#42774) 2020-11-04 09:24:29 +00:00
Erik Montnemery
a7bdd2aafe Force color or white mode exclusivity for Tasmota lights (#42772) 2020-11-04 09:24:28 +00:00
Aaron Bach
e6cc35cff2 Fix incorrect property usage for SimpliSafe (#42770) 2020-11-04 09:24:27 +00:00
Aaron Bach
b7d0a21a8b Bump pyairvisual to 5.0.4 (#42760) 2020-11-04 09:24:26 +00:00
rikroe
beaaa1478c Bump bimmer_connected to 0.7.10: fix login issues, add (PH)EV services (#42747)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2020-11-04 09:24:25 +00:00
Raman Gupta
74d8569aab Fix Vizio host string for zeroconf discovery (#42738) 2020-11-04 09:24:25 +00:00
Andreas Billmeier
dc0d27ec18 Fix broken maxcube component (#42674) 2020-11-04 09:24:24 +00:00
Andreas Billmeier
64707d89dd Please set exact cryptography version cryptography==3.2 (#42611) 2020-11-04 09:24:23 +00:00
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
178 changed files with 1933 additions and 756 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
@@ -363,6 +362,7 @@ homeassistant/components/rmvtransport/* @cgtobi
homeassistant/components/roku/* @ctalkington
homeassistant/components/roomba/* @pschmitt @cyr-ius @shenxn
homeassistant/components/roon/* @pavoni
homeassistant/components/rpi_gpio_pwm/* @soldag
homeassistant/components/rpi_power/* @shenxn @swetoast
homeassistant/components/ruckus_unleashed/* @gabe565
homeassistant/components/safe_mode/* @home-assistant/core

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

@@ -3,6 +3,6 @@
"name": "AirVisual",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airvisual",
"requirements": ["pyairvisual==5.0.3"],
"requirements": ["pyairvisual==5.0.4"],
"codeowners": ["@bachya"]
}

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

@@ -2,7 +2,7 @@
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"requirements": ["bimmer_connected==0.7.8"],
"requirements": ["bimmer_connected==0.7.12"],
"dependencies": [],
"codeowners": ["@gerard33", "@rikroe"]
}

View File

@@ -34,6 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
await coordinator.async_refresh()
if not coordinator.last_update_success:
coordinator.shutdown()
raise ConfigEntryNotReady
hass.data.setdefault(DOMAIN, {})

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

@@ -431,7 +431,7 @@ class EvoBroker:
return
if refresh:
self.hass.helpers.event.async_call_later(1, self.async_update())
self.hass.helpers.event.async_call_later(1, self.async_update)
return result

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

@@ -2,6 +2,6 @@
"domain": "google_translate",
"name": "Google Translate Text-to-Speech",
"documentation": "https://www.home-assistant.io/integrations/google_translate",
"requirements": ["gTTS-token==1.1.3"],
"codeowners": ["@awarecan"]
"requirements": ["gTTS-token==1.1.4"],
"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.5"],
"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

@@ -3,7 +3,7 @@
"name": "Netatmo",
"documentation": "https://www.home-assistant.io/integrations/netatmo",
"requirements": [
"pyatmo==4.1.0"
"pyatmo==4.2.0"
],
"after_dependencies": [
"cloud",

View File

@@ -45,10 +45,10 @@ class NetatmoBase(Entity):
data_class["name"],
signal_name,
self.async_update_callback,
LAT_NE=data_class["LAT_NE"],
LON_NE=data_class["LON_NE"],
LAT_SW=data_class["LAT_SW"],
LON_SW=data_class["LON_SW"],
lat_ne=data_class["lat_ne"],
lon_ne=data_class["lon_ne"],
lat_sw=data_class["lat_sw"],
lon_sw=data_class["lon_sw"],
)
else:

View File

@@ -202,10 +202,10 @@ async def async_setup_entry(hass, entry, async_add_entities):
PUBLICDATA_DATA_CLASS_NAME,
signal_name,
None,
LAT_NE=area.lat_ne,
LON_NE=area.lon_ne,
LAT_SW=area.lat_sw,
LON_SW=area.lon_sw,
lat_ne=area.lat_ne,
lon_ne=area.lon_ne,
lat_sw=area.lat_sw,
lon_sw=area.lon_sw,
)
for sensor_type in SUPPORTED_PUBLIC_SENSOR_TYPES:
new_entities.append(
@@ -473,10 +473,10 @@ class NetatmoPublicSensor(NetatmoBase):
self._data_classes.append(
{
"name": PUBLICDATA_DATA_CLASS_NAME,
"LAT_NE": area.lat_ne,
"LON_NE": area.lon_ne,
"LAT_SW": area.lat_sw,
"LON_SW": area.lon_sw,
"lat_ne": area.lat_ne,
"lon_ne": area.lon_ne,
"lat_sw": area.lat_sw,
"lon_sw": area.lon_sw,
"area_name": area.area_name,
SIGNAL_NAME: self._signal_name,
}
@@ -563,10 +563,10 @@ class NetatmoPublicSensor(NetatmoBase):
self._data_classes = [
{
"name": PUBLICDATA_DATA_CLASS_NAME,
"LAT_NE": area.lat_ne,
"LON_NE": area.lon_ne,
"LAT_SW": area.lat_sw,
"LON_SW": area.lon_sw,
"lat_ne": area.lat_ne,
"lon_ne": area.lon_ne,
"lat_sw": area.lat_sw,
"lon_sw": area.lon_sw,
"area_name": area.area_name,
SIGNAL_NAME: self._signal_name,
}
@@ -577,10 +577,10 @@ class NetatmoPublicSensor(NetatmoBase):
PUBLICDATA_DATA_CLASS_NAME,
self._signal_name,
self.async_update_callback,
LAT_NE=area.lat_ne,
LON_NE=area.lon_ne,
LAT_SW=area.lat_sw,
LON_SW=area.lon_sw,
lat_ne=area.lat_ne,
lon_ne=area.lon_ne,
lat_sw=area.lat_sw,
lon_sw=area.lon_sw,
)
@callback

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

@@ -289,6 +289,8 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_plex_website_auth(self):
"""Begin external auth flow on Plex website."""
self.hass.http.register_view(PlexAuthorizationCallbackView)
hass_url = get_url(self.hass)
headers = {"Origin": hass_url}
payload = {
"X-Plex-Device-Name": X_PLEX_DEVICE_NAME,
"X-Plex-Version": X_PLEX_VERSION,
@@ -298,9 +300,9 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"X-Plex-Model": "Plex OAuth",
}
session = async_get_clientsession(self.hass)
self.plexauth = PlexAuth(payload, session)
self.plexauth = PlexAuth(payload, session, headers)
await self.plexauth.initiate_auth()
forward_url = f"{get_url(self.hass)}{AUTH_CALLBACK_PATH}?flow_id={self.flow_id}"
forward_url = f"{hass_url}{AUTH_CALLBACK_PATH}?flow_id={self.flow_id}"
auth_url = self.plexauth.auth_url(forward_url)
return self.async_external_step(step_id="obtain_token", url=auth_url)

View File

@@ -4,9 +4,9 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/plex",
"requirements": [
"plexapi==4.1.1",
"plexauth==0.0.5",
"plexwebsocket==0.0.12"
"plexapi==4.1.1",
"plexauth==0.0.6",
"plexwebsocket==0.0.12"
],
"dependencies": ["http"],
"after_dependencies": ["sonos"],

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.async_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.async_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.async_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.async_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

@@ -49,6 +49,9 @@ from .const import (
CONF_OFF_DELAY,
CONF_REMOVE_DEVICE,
CONF_SIGNAL_REPETITIONS,
DATA_CLEANUP_CALLBACKS,
DATA_LISTENER,
DATA_RFXOBJECT,
DEVICE_PACKET_TYPE_LIGHTING4,
EVENT_RFXTRX_EVENT,
SERVICE_SEND,
@@ -93,8 +96,6 @@ DATA_TYPES = OrderedDict(
)
_LOGGER = logging.getLogger(__name__)
DATA_RFXOBJECT = "rfxobject"
DATA_LISTENER = "ha_stop"
def _bytearray_string(data):
@@ -188,6 +189,8 @@ async def async_setup_entry(hass, entry: config_entries.ConfigEntry):
"""Set up the RFXtrx component."""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS] = []
await async_setup_internal(hass, entry)
for domain in DOMAINS:
@@ -212,12 +215,17 @@ async def async_unload_entry(hass, entry: config_entries.ConfigEntry):
hass.services.async_remove(DOMAIN, SERVICE_SEND)
for cleanup_callback in hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS]:
cleanup_callback()
listener = hass.data[DOMAIN][DATA_LISTENER]
listener()
rfx_object = hass.data[DOMAIN][DATA_RFXOBJECT]
await hass.async_add_executor_job(rfx_object.close_connection)
hass.data.pop(DOMAIN)
return True
@@ -428,6 +436,14 @@ def get_device_id(device, data_bits=None):
return (f"{device.packettype:x}", f"{device.subtype:x}", id_string)
def connect_auto_add(hass, entry_data, callback_fun):
"""Connect to dispatcher for automatic add."""
if entry_data[CONF_AUTOMATIC_ADD]:
hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS].append(
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, callback_fun)
)
class RfxtrxEntity(RestoreEntity):
"""Represents a Rfxtrx device.

View File

@@ -19,11 +19,10 @@ from homeassistant.core import callback
from homeassistant.helpers import event as evt
from . import (
CONF_AUTOMATIC_ADD,
CONF_DATA_BITS,
CONF_OFF_DELAY,
SIGNAL_EVENT,
RfxtrxEntity,
connect_auto_add,
find_possible_pt2262_device,
get_device_id,
get_pt2262_cmd,
@@ -147,10 +146,7 @@ async def async_setup_entry(
async_add_entities([sensor])
# Subscribe to main RFXtrx events
if discovery_info[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_EVENT, binary_sensor_update
)
connect_auto_add(hass, discovery_info, binary_sensor_update)
class RfxtrxBinarySensor(RfxtrxEntity, BinarySensorEntity):

View File

@@ -360,13 +360,17 @@ class OptionsFlow(config_entries.OptionsFlow):
"""Check if device can be replaced with selected device."""
device_data = self._get_device_data(entry_id)
event_code = device_data[CONF_EVENT_CODE]
rfx_obj = get_rfx_object(event_code)
if (
rfx_obj.device.packettype == self._selected_device_object.device.packettype
and rfx_obj.device.subtype == self._selected_device_object.device.subtype
and self._selected_device_event_code != event_code
):
return True
if event_code is not None:
rfx_obj = get_rfx_object(event_code)
if (
rfx_obj.device.packettype
== self._selected_device_object.device.packettype
and rfx_obj.device.subtype
== self._selected_device_object.device.subtype
and self._selected_device_event_code != event_code
):
return True
return False

View File

@@ -33,3 +33,7 @@ SERVICE_SEND = "send"
DEVICE_PACKET_TYPE_LIGHTING4 = 0x13
EVENT_RFXTRX_EVENT = "rfxtrx_event"
DATA_RFXOBJECT = "rfxobject"
DATA_LISTENER = "ha_stop"
DATA_CLEANUP_CALLBACKS = "cleanup_callbacks"

View File

@@ -6,12 +6,11 @@ from homeassistant.const import CONF_DEVICES, STATE_OPEN
from homeassistant.core import callback
from . import (
CONF_AUTOMATIC_ADD,
CONF_DATA_BITS,
CONF_SIGNAL_REPETITIONS,
DEFAULT_SIGNAL_REPETITIONS,
SIGNAL_EVENT,
RfxtrxCommandEntity,
connect_auto_add,
get_device_id,
get_rfx_object,
)
@@ -81,8 +80,7 @@ async def async_setup_entry(
async_add_entities([entity])
# Subscribe to main RFXtrx events
if discovery_info[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, cover_update)
connect_auto_add(hass, discovery_info, cover_update)
class RfxtrxCover(RfxtrxCommandEntity, CoverEntity):

View File

@@ -12,12 +12,11 @@ from homeassistant.const import CONF_DEVICES, STATE_ON
from homeassistant.core import callback
from . import (
CONF_AUTOMATIC_ADD,
CONF_DATA_BITS,
CONF_SIGNAL_REPETITIONS,
DEFAULT_SIGNAL_REPETITIONS,
SIGNAL_EVENT,
RfxtrxCommandEntity,
connect_auto_add,
get_device_id,
get_rfx_object,
)
@@ -95,8 +94,7 @@ async def async_setup_entry(
async_add_entities([entity])
# Subscribe to main RFXtrx events
if discovery_info[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, light_update)
connect_auto_add(hass, discovery_info, light_update)
class RfxtrxLight(RfxtrxCommandEntity, LightEntity):

View File

@@ -20,11 +20,10 @@ from homeassistant.const import (
from homeassistant.core import callback
from . import (
CONF_AUTOMATIC_ADD,
CONF_DATA_BITS,
DATA_TYPES,
SIGNAL_EVENT,
RfxtrxEntity,
connect_auto_add,
get_device_id,
get_rfx_object,
)
@@ -127,8 +126,7 @@ async def async_setup_entry(
async_add_entities([entity])
# Subscribe to main RFXtrx events
if discovery_info[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, sensor_update)
connect_auto_add(hass, discovery_info, sensor_update)
class RfxtrxSensor(RfxtrxEntity):

View File

@@ -8,13 +8,12 @@ from homeassistant.const import CONF_DEVICES, STATE_ON
from homeassistant.core import callback
from . import (
CONF_AUTOMATIC_ADD,
CONF_DATA_BITS,
CONF_SIGNAL_REPETITIONS,
DEFAULT_SIGNAL_REPETITIONS,
DOMAIN,
SIGNAL_EVENT,
RfxtrxCommandEntity,
connect_auto_add,
get_device_id,
get_rfx_object,
)
@@ -92,8 +91,7 @@ async def async_setup_entry(
async_add_entities([entity])
# Subscribe to main RFXtrx events
if discovery_info[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, switch_update)
connect_auto_add(hass, discovery_info, switch_update)
class RfxtrxSwitch(RfxtrxCommandEntity, SwitchEntity):

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

@@ -123,9 +123,6 @@ class PwmSimpleLed(LightEntity, RestoreEntity):
self._brightness = last_state.attributes.get(
"brightness", DEFAULT_BRIGHTNESS
)
self._led.set(
is_on=self._is_on, brightness=_from_hass_brightness(self._brightness)
)
@property
def should_poll(self):
@@ -199,7 +196,6 @@ class PwmRgbLed(PwmSimpleLed):
last_state = await self.async_get_last_state()
if last_state:
self._color = last_state.attributes.get("hs_color", DEFAULT_COLOR)
self._led.set(color=_from_hass_color(self._color))
@property
def hs_color(self):

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"],
"codeowners": []
"requirements": ["pwmled==1.6.7"],
"codeowners": ["@soldag"]
}

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,7 @@ import asyncio
from uuid import UUID
from simplipy import API
from simplipy.entity import EntityTypes
from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError
from simplipy.websocket import (
EVENT_CAMERA_MOTION_DETECTED,
@@ -590,6 +591,13 @@ class SimpliSafeEntity(CoordinatorEntity):
else:
self._serial = system.serial
try:
sensor_type = EntityTypes(
simplisafe.initial_event_to_use[system.system_id].get("sensorType")
)
except ValueError:
sensor_type = EntityTypes.unknown
self._attrs = {
ATTR_LAST_EVENT_INFO: simplisafe.initial_event_to_use[system.system_id].get(
"info"
@@ -597,9 +605,7 @@ class SimpliSafeEntity(CoordinatorEntity):
ATTR_LAST_EVENT_SENSOR_NAME: simplisafe.initial_event_to_use[
system.system_id
].get("sensorName"),
ATTR_LAST_EVENT_SENSOR_TYPE: simplisafe.initial_event_to_use[
system.system_id
].get("sensorType"),
ATTR_LAST_EVENT_SENSOR_TYPE: sensor_type.name,
ATTR_LAST_EVENT_TIMESTAMP: simplisafe.initial_event_to_use[
system.system_id
].get("eventTimestamp"),
@@ -724,3 +730,23 @@ class SimpliSafeEntity(CoordinatorEntity):
@callback
def async_update_from_websocket_event(self, event):
"""Update the entity with the provided websocket event."""
class SimpliSafeBaseSensor(SimpliSafeEntity):
"""Define a SimpliSafe base (binary) sensor."""
def __init__(self, simplisafe, system, sensor):
"""Initialize."""
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
self._device_info["identifiers"] = {(DOMAIN, sensor.serial)}
self._device_info["model"] = sensor.type.name
self._device_info["name"] = sensor.name
self._sensor = sensor
self._sensor_type_human_name = " ".join(
[w.title() for w in self._sensor.type.name.split("_")]
)
@property
def name(self):
"""Return the name of the sensor."""
return f"{self._system.address} {self._name} {self._sensor_type_human_name}"

View File

@@ -159,7 +159,7 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
try:
await self._system.set_off()
except SimplipyError as err:
LOGGER.error('Error while disarming "%s": %s', self._system.name, err)
LOGGER.error('Error while disarming "%s": %s', self._system.system_id, err)
return
self._state = STATE_ALARM_DISARMED
@@ -172,7 +172,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
try:
await self._system.set_home()
except SimplipyError as err:
LOGGER.error('Error while arming "%s" (home): %s', self._system.name, err)
LOGGER.error(
'Error while arming "%s" (home): %s', self._system.system_id, err
)
return
self._state = STATE_ALARM_ARMED_HOME
@@ -185,7 +187,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
try:
await self._system.set_away()
except SimplipyError as err:
LOGGER.error('Error while arming "%s" (away): %s', self._system.name, err)
LOGGER.error(
'Error while arming "%s" (away): %s', self._system.system_id, err
)
return
self._state = STATE_ALARM_ARMING

View File

@@ -6,42 +6,32 @@ from homeassistant.components.binary_sensor import (
DEVICE_CLASS_DOOR,
DEVICE_CLASS_GAS,
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
BinarySensorEntity,
)
from homeassistant.core import callback
from . import SimpliSafeEntity
from . import SimpliSafeBaseSensor
from .const import DATA_CLIENT, DOMAIN, LOGGER
SUPPORTED_BATTERY_SENSOR_TYPES = [
EntityTypes.carbon_monoxide,
EntityTypes.entry,
EntityTypes.leak,
EntityTypes.lock,
EntityTypes.lock_keypad,
EntityTypes.smoke,
EntityTypes.temperature,
]
SUPPORTED_SENSOR_TYPES = [
EntityTypes.entry,
EntityTypes.carbon_monoxide,
EntityTypes.smoke,
EntityTypes.leak,
]
HA_SENSOR_TYPES = {
EntityTypes.entry: DEVICE_CLASS_DOOR,
TRIGGERED_SENSOR_TYPES = {
EntityTypes.carbon_monoxide: DEVICE_CLASS_GAS,
EntityTypes.smoke: DEVICE_CLASS_SMOKE,
EntityTypes.entry: DEVICE_CLASS_DOOR,
EntityTypes.glass_break: DEVICE_CLASS_SAFETY,
EntityTypes.leak: DEVICE_CLASS_MOISTURE,
}
SENSOR_MODELS = {
EntityTypes.entry: "Entry Sensor",
EntityTypes.carbon_monoxide: "Carbon Monoxide Detector",
EntityTypes.smoke: "Smoke Detector",
EntityTypes.leak: "Water Sensor",
EntityTypes.motion: DEVICE_CLASS_MOTION,
EntityTypes.smoke: DEVICE_CLASS_SMOKE,
}
@@ -56,37 +46,34 @@ async def async_setup_entry(hass, entry, async_add_entities):
continue
for sensor in system.sensors.values():
if sensor.type in SUPPORTED_SENSOR_TYPES:
sensors.append(SimpliSafeBinarySensor(simplisafe, system, sensor))
if sensor.type in TRIGGERED_SENSOR_TYPES:
sensors.append(
TriggeredBinarySensor(
simplisafe,
system,
sensor,
TRIGGERED_SENSOR_TYPES[sensor.type],
)
)
if sensor.type in SUPPORTED_BATTERY_SENSOR_TYPES:
sensors.append(SimpliSafeSensorBattery(simplisafe, system, sensor))
sensors.append(BatteryBinarySensor(simplisafe, system, sensor))
async_add_entities(sensors)
class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity):
"""Define a SimpliSafe binary sensor entity."""
class TriggeredBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity):
"""Define a binary sensor related to whether an entity has been triggered."""
def __init__(self, simplisafe, system, sensor):
def __init__(self, simplisafe, system, sensor, device_class):
"""Initialize."""
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
self._system = system
self._sensor = sensor
super().__init__(simplisafe, system, sensor)
self._device_class = device_class
self._is_on = False
@property
def device_class(self):
"""Return type of sensor."""
return HA_SENSOR_TYPES[self._sensor.type]
@property
def device_info(self):
"""Return device registry information for this entity."""
info = super().device_info
info["identifiers"] = {(DOMAIN, self._sensor.serial)}
info["model"] = SENSOR_MODELS[self._sensor.type]
info["name"] = self._sensor.name
return info
return self._device_class
@property
def is_on(self):
@@ -99,19 +86,14 @@ class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity):
self._is_on = self._sensor.triggered
class SimpliSafeSensorBattery(SimpliSafeEntity, BinarySensorEntity):
class BatteryBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity):
"""Define a SimpliSafe battery binary sensor entity."""
def __init__(self, simplisafe, system, sensor):
"""Initialize."""
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
self._sensor = sensor
super().__init__(simplisafe, system, sensor)
self._is_low = False
self._device_info["identifiers"] = {(DOMAIN, sensor.serial)}
self._device_info["model"] = SENSOR_MODELS[sensor.type]
self._device_info["name"] = sensor.name
@property
def device_class(self):
"""Return type of sensor."""

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

@@ -4,7 +4,7 @@ from simplipy.entity import EntityTypes
from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT
from homeassistant.core import callback
from . import SimpliSafeEntity
from . import SimpliSafeBaseSensor
from .const import DATA_CLIENT, DOMAIN, LOGGER
@@ -25,19 +25,14 @@ async def async_setup_entry(hass, entry, async_add_entities):
async_add_entities(sensors)
class SimplisafeFreezeSensor(SimpliSafeEntity):
class SimplisafeFreezeSensor(SimpliSafeBaseSensor):
"""Define a SimpliSafe freeze sensor entity."""
def __init__(self, simplisafe, system, sensor):
"""Initialize."""
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
self._sensor = sensor
super().__init__(simplisafe, system, sensor)
self._state = None
self._device_info["identifiers"] = {(DOMAIN, sensor.serial)}
self._device_info["model"] = "Freeze Sensor"
self._device_info["name"] = sensor.name
@property
def device_class(self):
"""Return type of sensor."""

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

@@ -258,10 +258,9 @@ class SynoApi:
self._entry.data[CONF_PASSWORD],
self._entry.data[CONF_SSL],
timeout=self._entry.options.get(CONF_TIMEOUT),
device_token=self._entry.data.get("device_token"),
)
await self._hass.async_add_executor_job(
self.dsm.login, self._entry.data.get("device_token")
)
await self._hass.async_add_executor_job(self.dsm.login)
self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
@@ -316,6 +315,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."""
@@ -127,6 +128,11 @@ class TasmotaLight(
white_value = float(attributes["white_value"])
percent_white = white_value / TASMOTA_BRIGHTNESS_MAX
self._white_value = percent_white * 255
if self._white_value == 0:
self._color_temp = None
self._white_value = None
if self._white_value is not None and self._white_value > 0:
self._hs = None
self.async_write_ha_state()
@property

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.1"],
"dependencies": ["mqtt"],
"mqtt": ["tasmota/discovery/#"],
"codeowners": ["@emontnemery"]

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