forked from home-assistant/core
Compare commits
266 Commits
integratio
...
2024.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
058b012e6c | ||
|
|
71370758a8 | ||
|
|
38a44676eb | ||
|
|
05ce3d35b3 | ||
|
|
2151086b0a | ||
|
|
9c83af3789 | ||
|
|
ac3eecc879 | ||
|
|
ec0910e3da | ||
|
|
fd0c26cd56 | ||
|
|
a4c5dee082 | ||
|
|
37c09dbdb6 | ||
|
|
73d1973625 | ||
|
|
5a04a886cf | ||
|
|
50802f84f0 | ||
|
|
138b68ecc0 | ||
|
|
e0b01ee94e | ||
|
|
4f2c3df518 | ||
|
|
51a6bb1c22 | ||
|
|
6bf9ec69f3 | ||
|
|
21309eeb5d | ||
|
|
0a1b46c52f | ||
|
|
9512f9eec3 | ||
|
|
ab94422c18 | ||
|
|
ec105e5265 | ||
|
|
cadd8521ae | ||
|
|
8825c50671 | ||
|
|
a72cc3c248 | ||
|
|
780f7254c1 | ||
|
|
37621e77ae | ||
|
|
8017386c73 | ||
|
|
a5f4c25a2c | ||
|
|
1d7bddf449 | ||
|
|
711bdaf373 | ||
|
|
803d9c5a8e | ||
|
|
1133c41fa8 | ||
|
|
a06af7ee93 | ||
|
|
c54717707e | ||
|
|
440d83d754 | ||
|
|
1cf62916a7 | ||
|
|
e3958d4adb | ||
|
|
dfccd4abf9 | ||
|
|
994d6f552c | ||
|
|
b015611a2a | ||
|
|
f4e362c5d0 | ||
|
|
a542236614 | ||
|
|
651439ea06 | ||
|
|
eda450838e | ||
|
|
b906daa493 | ||
|
|
ac668dce7d | ||
|
|
1bb4d62a3e | ||
|
|
0b970f9a85 | ||
|
|
d2b695e7b5 | ||
|
|
b2f23c1a5e | ||
|
|
f403afb012 | ||
|
|
ee276aff44 | ||
|
|
0acd1dc5d1 | ||
|
|
21815e1621 | ||
|
|
15933bb16f | ||
|
|
930cd0dc50 | ||
|
|
fc4af48179 | ||
|
|
ba1cf84ea5 | ||
|
|
59cf01e252 | ||
|
|
46e681f4fc | ||
|
|
2b64f6f2ab | ||
|
|
1080a4ef1e | ||
|
|
d94b36cfbb | ||
|
|
85168239cd | ||
|
|
547b24ce58 | ||
|
|
e8bcb3e11e | ||
|
|
c89a9b5ce0 | ||
|
|
13631250b4 | ||
|
|
6621cf475a | ||
|
|
36e74cd9a6 | ||
|
|
16827ea09e | ||
|
|
c4956b66b0 | ||
|
|
84204c38be | ||
|
|
febd1a3772 | ||
|
|
1665cb40ac | ||
|
|
1b9f27fab7 | ||
|
|
d1e76d5c3c | ||
|
|
4377f4cbea | ||
|
|
6b045a7d7b | ||
|
|
1fa6972a66 | ||
|
|
b3e833f677 | ||
|
|
807ed0ce10 | ||
|
|
5cb41106b5 | ||
|
|
98a2e46d4a | ||
|
|
24afbde79e | ||
|
|
65d2ca53cb | ||
|
|
23b905b422 | ||
|
|
de458493f8 | ||
|
|
efd3252849 | ||
|
|
3b6acd5380 | ||
|
|
1e6dc74812 | ||
|
|
74687f3b60 | ||
|
|
2f307d6a8a | ||
|
|
d8f55763c5 | ||
|
|
4b2be448f0 | ||
|
|
8a7e2c05a5 | ||
|
|
887ab1dc58 | ||
|
|
a787ce8633 | ||
|
|
88ed43c779 | ||
|
|
16d7764f18 | ||
|
|
a0f8012f48 | ||
|
|
5a052feb87 | ||
|
|
779a7ddaa2 | ||
|
|
a9740faeda | ||
|
|
3a0e85beb8 | ||
|
|
c19fb35d02 | ||
|
|
6f716c1753 | ||
|
|
40384b9acd | ||
|
|
3bbf8df6d6 | ||
|
|
14af3661f3 | ||
|
|
af733425c2 | ||
|
|
4fc89e8861 | ||
|
|
bcec268c04 | ||
|
|
becf9fcce2 | ||
|
|
ad9e0ef8e4 | ||
|
|
f58eafe8fc | ||
|
|
a7246400b3 | ||
|
|
38a30b343d | ||
|
|
08a0eaf184 | ||
|
|
3ee8f6edba | ||
|
|
e866417c01 | ||
|
|
05c63eb884 | ||
|
|
bb52bfd73d | ||
|
|
7319492bf3 | ||
|
|
66932e3d9a | ||
|
|
0ec07001bd | ||
|
|
0dcfd38cdc | ||
|
|
b45eff9a2b | ||
|
|
ec577c7bd3 | ||
|
|
723c4a1eb5 | ||
|
|
b30b4d5a3a | ||
|
|
8165acddeb | ||
|
|
0f3ed3bb67 | ||
|
|
d1a96ef362 | ||
|
|
917eeba984 | ||
|
|
59bb8b360e | ||
|
|
6028e5b77a | ||
|
|
83df470307 | ||
|
|
20ac0aa7b1 | ||
|
|
f57c942901 | ||
|
|
8994ab1686 | ||
|
|
b350ba9657 | ||
|
|
5fd589053a | ||
|
|
2d5961fa4f | ||
|
|
d7a59748cf | ||
|
|
cada78496b | ||
|
|
c5fa9ad272 | ||
|
|
fe8b5656dd | ||
|
|
0ae11b0335 | ||
|
|
76780ca04e | ||
|
|
3932ce57b9 | ||
|
|
35d145d3bc | ||
|
|
1227d56aa2 | ||
|
|
ef3ecb6183 | ||
|
|
ca515f740e | ||
|
|
876fb234ce | ||
|
|
f28cbf1909 | ||
|
|
9b5d0f72dc | ||
|
|
23056f839b | ||
|
|
0b8dd738f1 | ||
|
|
411633d3b3 | ||
|
|
f3ab3bd5cb | ||
|
|
476b9909ac | ||
|
|
e756328d52 | ||
|
|
b9c9921847 | ||
|
|
09dbd8e7eb | ||
|
|
07dd832c58 | ||
|
|
f9c5661c66 | ||
|
|
94f8f8281f | ||
|
|
f6aa25c717 | ||
|
|
f9ca85735d | ||
|
|
be086c581c | ||
|
|
03d198dd64 | ||
|
|
a8d6866f9f | ||
|
|
0e1dc9878f | ||
|
|
6849597764 | ||
|
|
3022d3bfa0 | ||
|
|
4836d6620b | ||
|
|
b290e95350 | ||
|
|
89ac3ce832 | ||
|
|
1933454b76 | ||
|
|
38601d48ff | ||
|
|
7256f23376 | ||
|
|
7519603bf5 | ||
|
|
ef47daad9d | ||
|
|
18d283bed6 | ||
|
|
210e906a4d | ||
|
|
dcffd6bd7a | ||
|
|
2c2261254b | ||
|
|
53e49861a1 | ||
|
|
3da8d0a741 | ||
|
|
0701b0daa9 | ||
|
|
bea6fe30b8 | ||
|
|
7d5d81b229 | ||
|
|
242b3fa609 | ||
|
|
74204e2ee6 | ||
|
|
da01635a07 | ||
|
|
b5c34808e6 | ||
|
|
80e70993c8 | ||
|
|
2e01e169ef | ||
|
|
b35442ed2d | ||
|
|
1b45069620 | ||
|
|
d3d0e05817 | ||
|
|
3d164c6721 | ||
|
|
6dd1e09354 | ||
|
|
ba456f2564 | ||
|
|
3492171ff8 | ||
|
|
f5c640ee5b | ||
|
|
713abf4c6b | ||
|
|
32c07180f6 | ||
|
|
55101fcc45 | ||
|
|
3d5d4f8ddb | ||
|
|
30a3e9af2b | ||
|
|
862cd76f89 | ||
|
|
f65d91f6d2 | ||
|
|
f4fa5b581e | ||
|
|
294e1d4fc4 | ||
|
|
66a803e56c | ||
|
|
d0f82d6f02 | ||
|
|
4defc4a58f | ||
|
|
af9b4b98ca | ||
|
|
43d686e0f1 | ||
|
|
7eb9875a9e | ||
|
|
fc2968bc1b | ||
|
|
b9be491016 | ||
|
|
ec16fc235b | ||
|
|
fd67fe417e | ||
|
|
e39d26bdc0 | ||
|
|
d515a7f063 | ||
|
|
f5ff19d602 | ||
|
|
69e0227682 | ||
|
|
0d2aeb846f | ||
|
|
1d0aa6bff0 | ||
|
|
b07453dca4 | ||
|
|
972b85a75b | ||
|
|
13a9efb6a6 | ||
|
|
a36c40a434 | ||
|
|
f0590f08b1 | ||
|
|
e8a3e3c8db | ||
|
|
34e266762e | ||
|
|
7ef1db0549 | ||
|
|
be7a2c2cc2 | ||
|
|
d5bcfe9822 | ||
|
|
9bbeb5d608 | ||
|
|
f55ddfecf4 | ||
|
|
36d8ffa79a | ||
|
|
fac8349c37 | ||
|
|
d00fe1ce7f | ||
|
|
c49fce5541 | ||
|
|
348ceca19f | ||
|
|
32bab97f00 | ||
|
|
912136be25 | ||
|
|
d4dc7d76d9 | ||
|
|
a8bf671663 | ||
|
|
42d235ce4d | ||
|
|
44aad2b821 | ||
|
|
7b7b97a7a4 | ||
|
|
ae73500beb | ||
|
|
d527113d59 | ||
|
|
afbd24adfe | ||
|
|
2c17d84fab | ||
|
|
17946c4b45 | ||
|
|
59ae297ccd |
@@ -58,11 +58,6 @@ omit =
|
||||
homeassistant/components/airvisual/sensor.py
|
||||
homeassistant/components/airvisual_pro/__init__.py
|
||||
homeassistant/components/airvisual_pro/sensor.py
|
||||
homeassistant/components/aladdin_connect/__init__.py
|
||||
homeassistant/components/aladdin_connect/api.py
|
||||
homeassistant/components/aladdin_connect/application_credentials.py
|
||||
homeassistant/components/aladdin_connect/cover.py
|
||||
homeassistant/components/aladdin_connect/sensor.py
|
||||
homeassistant/components/alarmdecoder/__init__.py
|
||||
homeassistant/components/alarmdecoder/alarm_control_panel.py
|
||||
homeassistant/components/alarmdecoder/binary_sensor.py
|
||||
@@ -1690,8 +1685,6 @@ omit =
|
||||
homeassistant/components/yolink/siren.py
|
||||
homeassistant/components/yolink/switch.py
|
||||
homeassistant/components/yolink/valve.py
|
||||
homeassistant/components/youless/__init__.py
|
||||
homeassistant/components/youless/sensor.py
|
||||
homeassistant/components/zabbix/*
|
||||
homeassistant/components/zamg/coordinator.py
|
||||
homeassistant/components/zengge/light.py
|
||||
|
||||
@@ -370,6 +370,7 @@ homeassistant.components.rhasspy.*
|
||||
homeassistant.components.ridwell.*
|
||||
homeassistant.components.ring.*
|
||||
homeassistant.components.rituals_perfume_genie.*
|
||||
homeassistant.components.roborock.*
|
||||
homeassistant.components.roku.*
|
||||
homeassistant.components.romy.*
|
||||
homeassistant.components.rpi_power.*
|
||||
|
||||
@@ -80,8 +80,6 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/airzone/ @Noltari
|
||||
/homeassistant/components/airzone_cloud/ @Noltari
|
||||
/tests/components/airzone_cloud/ @Noltari
|
||||
/homeassistant/components/aladdin_connect/ @swcloudgenie
|
||||
/tests/components/aladdin_connect/ @swcloudgenie
|
||||
/homeassistant/components/alarm_control_panel/ @home-assistant/core
|
||||
/tests/components/alarm_control_panel/ @home-assistant/core
|
||||
/homeassistant/components/alert/ @home-assistant/core @frenck
|
||||
@@ -1460,8 +1458,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/tomorrowio/ @raman325 @lymanepp
|
||||
/homeassistant/components/totalconnect/ @austinmroczek
|
||||
/tests/components/totalconnect/ @austinmroczek
|
||||
/homeassistant/components/tplink/ @rytilahti @thegardenmonkey @bdraco @sdb9696
|
||||
/tests/components/tplink/ @rytilahti @thegardenmonkey @bdraco @sdb9696
|
||||
/homeassistant/components/tplink/ @rytilahti @bdraco @sdb9696
|
||||
/tests/components/tplink/ @rytilahti @bdraco @sdb9696
|
||||
/homeassistant/components/tplink_omada/ @MarkGodwin
|
||||
/tests/components/tplink_omada/ @MarkGodwin
|
||||
/homeassistant/components/traccar/ @ludeeus
|
||||
|
||||
@@ -55,13 +55,6 @@ class InvalidUser(HomeAssistantError):
|
||||
Will not be raised when validating authentication.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidUsername(InvalidUser):
|
||||
"""Raised when invalid username is specified.
|
||||
|
||||
Will not be raised when validating authentication.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args: object,
|
||||
@@ -77,6 +70,13 @@ class InvalidUsername(InvalidUser):
|
||||
)
|
||||
|
||||
|
||||
class InvalidUsername(InvalidUser):
|
||||
"""Raised when invalid username is specified.
|
||||
|
||||
Will not be raised when validating authentication.
|
||||
"""
|
||||
|
||||
|
||||
class Data:
|
||||
"""Hold the user data."""
|
||||
|
||||
@@ -216,7 +216,7 @@ class Data:
|
||||
break
|
||||
|
||||
if index is None:
|
||||
raise InvalidUser
|
||||
raise InvalidUser(translation_key="user_not_found")
|
||||
|
||||
self.users.pop(index)
|
||||
|
||||
@@ -232,7 +232,7 @@ class Data:
|
||||
user["password"] = self.hash_password(new_password, True).decode()
|
||||
break
|
||||
else:
|
||||
raise InvalidUser
|
||||
raise InvalidUser(translation_key="user_not_found")
|
||||
|
||||
@callback
|
||||
def _validate_new_username(self, new_username: str) -> None:
|
||||
@@ -275,7 +275,7 @@ class Data:
|
||||
self._async_check_for_not_normalized_usernames(self._data)
|
||||
break
|
||||
else:
|
||||
raise InvalidUser
|
||||
raise InvalidUser(translation_key="user_not_found")
|
||||
|
||||
async def async_save(self) -> None:
|
||||
"""Save data."""
|
||||
|
||||
@@ -8,7 +8,7 @@ import contextlib
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
import logging
|
||||
import logging.handlers
|
||||
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
|
||||
import mimetypes
|
||||
from operator import contains, itemgetter
|
||||
import os
|
||||
@@ -257,12 +257,12 @@ async def async_setup_hass(
|
||||
) -> core.HomeAssistant | None:
|
||||
"""Set up Home Assistant."""
|
||||
|
||||
def create_hass() -> core.HomeAssistant:
|
||||
async def create_hass() -> core.HomeAssistant:
|
||||
"""Create the hass object and do basic setup."""
|
||||
hass = core.HomeAssistant(runtime_config.config_dir)
|
||||
loader.async_setup(hass)
|
||||
|
||||
async_enable_logging(
|
||||
await async_enable_logging(
|
||||
hass,
|
||||
runtime_config.verbose,
|
||||
runtime_config.log_rotate_days,
|
||||
@@ -287,7 +287,7 @@ async def async_setup_hass(
|
||||
async with hass.timeout.async_timeout(10):
|
||||
await hass.async_stop()
|
||||
|
||||
hass = create_hass()
|
||||
hass = await create_hass()
|
||||
|
||||
if runtime_config.skip_pip or runtime_config.skip_pip_packages:
|
||||
_LOGGER.warning(
|
||||
@@ -326,13 +326,13 @@ async def async_setup_hass(
|
||||
if config_dict is None:
|
||||
recovery_mode = True
|
||||
await stop_hass(hass)
|
||||
hass = create_hass()
|
||||
hass = await create_hass()
|
||||
|
||||
elif not basic_setup_success:
|
||||
_LOGGER.warning("Unable to set up core integrations. Activating recovery mode")
|
||||
recovery_mode = True
|
||||
await stop_hass(hass)
|
||||
hass = create_hass()
|
||||
hass = await create_hass()
|
||||
|
||||
elif any(domain not in hass.config.components for domain in CRITICAL_INTEGRATIONS):
|
||||
_LOGGER.warning(
|
||||
@@ -345,7 +345,7 @@ async def async_setup_hass(
|
||||
|
||||
recovery_mode = True
|
||||
await stop_hass(hass)
|
||||
hass = create_hass()
|
||||
hass = await create_hass()
|
||||
|
||||
if old_logging:
|
||||
hass.data[DATA_LOGGING] = old_logging
|
||||
@@ -523,8 +523,7 @@ async def async_from_config_dict(
|
||||
return hass
|
||||
|
||||
|
||||
@core.callback
|
||||
def async_enable_logging(
|
||||
async def async_enable_logging(
|
||||
hass: core.HomeAssistant,
|
||||
verbose: bool = False,
|
||||
log_rotate_days: int | None = None,
|
||||
@@ -607,23 +606,9 @@ def async_enable_logging(
|
||||
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
|
||||
not err_path_exists and os.access(err_dir, os.W_OK)
|
||||
):
|
||||
err_handler: (
|
||||
logging.handlers.RotatingFileHandler
|
||||
| logging.handlers.TimedRotatingFileHandler
|
||||
err_handler = await hass.async_add_executor_job(
|
||||
_create_log_file, err_log_path, log_rotate_days
|
||||
)
|
||||
if log_rotate_days:
|
||||
err_handler = logging.handlers.TimedRotatingFileHandler(
|
||||
err_log_path, when="midnight", backupCount=log_rotate_days
|
||||
)
|
||||
else:
|
||||
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
|
||||
err_log_path, backupCount=1
|
||||
)
|
||||
|
||||
try:
|
||||
err_handler.doRollover()
|
||||
except OSError as err:
|
||||
_LOGGER.error("Error rolling over log file: %s", err)
|
||||
|
||||
err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
|
||||
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
|
||||
@@ -640,7 +625,29 @@ def async_enable_logging(
|
||||
async_activate_log_queue_handler(hass)
|
||||
|
||||
|
||||
class _RotatingFileHandlerWithoutShouldRollOver(logging.handlers.RotatingFileHandler):
|
||||
def _create_log_file(
|
||||
err_log_path: str, log_rotate_days: int | None
|
||||
) -> RotatingFileHandler | TimedRotatingFileHandler:
|
||||
"""Create log file and do roll over."""
|
||||
err_handler: RotatingFileHandler | TimedRotatingFileHandler
|
||||
if log_rotate_days:
|
||||
err_handler = TimedRotatingFileHandler(
|
||||
err_log_path, when="midnight", backupCount=log_rotate_days
|
||||
)
|
||||
else:
|
||||
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
|
||||
err_log_path, backupCount=1
|
||||
)
|
||||
|
||||
try:
|
||||
err_handler.doRollover()
|
||||
except OSError as err:
|
||||
_LOGGER.error("Error rolling over log file: %s", err)
|
||||
|
||||
return err_handler
|
||||
|
||||
|
||||
class _RotatingFileHandlerWithoutShouldRollOver(RotatingFileHandler):
|
||||
"""RotatingFileHandler that does not check if it should roll over on every log."""
|
||||
|
||||
def shouldRollover(self, record: logging.LogRecord) -> bool:
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
},
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["jaraco.abode", "lomond"],
|
||||
"requirements": ["jaraco.abode==5.1.2"]
|
||||
"requirements": ["jaraco.abode==5.2.1"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["adguardhome"],
|
||||
"requirements": ["adguardhome==0.6.3"]
|
||||
"requirements": ["adguardhome==0.7.0"]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
DEVICE_CLASSES_SCHEMA,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as COVER_PLATFORM_SCHEMA,
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
@@ -36,7 +36,7 @@ CONF_ADS_VAR_OPEN = "adsvar_open"
|
||||
CONF_ADS_VAR_CLOSE = "adsvar_close"
|
||||
CONF_ADS_VAR_STOP = "adsvar_stop"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = COVER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_ADS_VAR): cv.string,
|
||||
vol.Optional(CONF_ADS_VAR_POSITION): cv.string,
|
||||
|
||||
@@ -9,7 +9,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
)
|
||||
@@ -29,7 +29,7 @@ from . import (
|
||||
)
|
||||
|
||||
DEFAULT_NAME = "ADS Light"
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ADS_VAR): cv.string,
|
||||
vol.Optional(CONF_ADS_VAR_BRIGHTNESS): cv.string,
|
||||
|
||||
@@ -4,7 +4,10 @@ from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -22,7 +25,7 @@ from . import (
|
||||
)
|
||||
|
||||
DEFAULT_NAME = "ADS sensor"
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ADS_VAR): cv.string,
|
||||
vol.Optional(CONF_ADS_FACTOR): cv.positive_int,
|
||||
|
||||
@@ -9,10 +9,7 @@ from typing import Final, final
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType, StateType
|
||||
@@ -21,6 +18,11 @@ from .const import DOMAIN
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
|
||||
ATTR_AQI: Final = "air_quality_index"
|
||||
ATTR_CO2: Final = "carbon_dioxide"
|
||||
ATTR_CO: Final = "carbon_monoxide"
|
||||
@@ -33,10 +35,6 @@ ATTR_PM_10: Final = "particulate_matter_10"
|
||||
ATTR_PM_2_5: Final = "particulate_matter_2_5"
|
||||
ATTR_SO2: Final = "sulphur_dioxide"
|
||||
|
||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
|
||||
PROP_TO_ATTR: Final[dict[str, str]] = {
|
||||
"air_quality_index": ATTR_AQI,
|
||||
"carbon_dioxide": ATTR_CO2,
|
||||
|
||||
@@ -20,6 +20,7 @@ PLATFORMS: list[Platform] = [
|
||||
Platform.NUMBER,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,34 @@
|
||||
"default": "mdi:lightbulb-on-outline"
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"led_bar_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
},
|
||||
"display_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"configuration_control": {
|
||||
"default": "mdi:cloud-cog"
|
||||
},
|
||||
"display_temperature_unit": {
|
||||
"default": "mdi:thermometer-lines"
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"default": "mdi:led-strip"
|
||||
},
|
||||
"nox_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"voc_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"default": "mdi:molecule-co2"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"total_volatile_organic_component_index": {
|
||||
"default": "mdi:molecule"
|
||||
@@ -17,6 +45,32 @@
|
||||
},
|
||||
"pm003_count": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"led_bar_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
},
|
||||
"display_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
},
|
||||
"display_temperature_unit": {
|
||||
"default": "mdi:thermometer-lines"
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"default": "mdi:led-strip"
|
||||
},
|
||||
"nox_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"voc_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"default": "mdi:molecule-co2"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"post_data_to_airgradient": {
|
||||
"default": "mdi:cogs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airgradient",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["airgradient==0.6.0"],
|
||||
"requirements": ["airgradient==0.6.1"],
|
||||
"zeroconf": ["_airgradient._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -79,6 +79,63 @@ LED_BAR_ENTITIES: tuple[AirGradientSelectEntityDescription, ...] = (
|
||||
),
|
||||
)
|
||||
|
||||
LEARNING_TIME_OFFSET_OPTIONS = [
|
||||
"12",
|
||||
"60",
|
||||
"120",
|
||||
"360",
|
||||
"720",
|
||||
]
|
||||
|
||||
ABC_DAYS = [
|
||||
"1",
|
||||
"8",
|
||||
"30",
|
||||
"90",
|
||||
"180",
|
||||
"0",
|
||||
]
|
||||
|
||||
|
||||
def _get_value(value: int, values: list[str]) -> str | None:
|
||||
str_value = str(value)
|
||||
return str_value if str_value in values else None
|
||||
|
||||
|
||||
CONTROL_ENTITIES: tuple[AirGradientSelectEntityDescription, ...] = (
|
||||
AirGradientSelectEntityDescription(
|
||||
key="nox_index_learning_time_offset",
|
||||
translation_key="nox_index_learning_time_offset",
|
||||
options=LEARNING_TIME_OFFSET_OPTIONS,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
value_fn=lambda config: _get_value(
|
||||
config.nox_learning_offset, LEARNING_TIME_OFFSET_OPTIONS
|
||||
),
|
||||
set_value_fn=lambda client, value: client.set_nox_learning_offset(int(value)),
|
||||
),
|
||||
AirGradientSelectEntityDescription(
|
||||
key="voc_index_learning_time_offset",
|
||||
translation_key="voc_index_learning_time_offset",
|
||||
options=LEARNING_TIME_OFFSET_OPTIONS,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
value_fn=lambda config: _get_value(
|
||||
config.tvoc_learning_offset, LEARNING_TIME_OFFSET_OPTIONS
|
||||
),
|
||||
set_value_fn=lambda client, value: client.set_tvoc_learning_offset(int(value)),
|
||||
),
|
||||
AirGradientSelectEntityDescription(
|
||||
key="co2_automatic_baseline_calibration",
|
||||
translation_key="co2_automatic_baseline_calibration",
|
||||
options=ABC_DAYS,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
value_fn=lambda config: _get_value(
|
||||
config.co2_automatic_baseline_calibration_days, ABC_DAYS
|
||||
),
|
||||
set_value_fn=lambda client,
|
||||
value: client.set_co2_automatic_baseline_calibration(int(value)),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -104,7 +161,10 @@ async def async_setup_entry(
|
||||
coordinator.data.configuration_control is ConfigurationControl.LOCAL
|
||||
and not added_entities
|
||||
):
|
||||
entities: list[AirGradientSelect] = []
|
||||
entities: list[AirGradientSelect] = [
|
||||
AirGradientSelect(coordinator, description)
|
||||
for description in CONTROL_ENTITIES
|
||||
]
|
||||
if "I" in model:
|
||||
entities.extend(
|
||||
AirGradientSelect(coordinator, description)
|
||||
@@ -123,7 +183,9 @@ async def async_setup_entry(
|
||||
and added_entities
|
||||
):
|
||||
entity_registry = er.async_get(hass)
|
||||
for entity_description in DISPLAY_SELECT_TYPES + LED_BAR_ENTITIES:
|
||||
for entity_description in (
|
||||
DISPLAY_SELECT_TYPES + LED_BAR_ENTITIES + CONTROL_ENTITIES
|
||||
):
|
||||
unique_id = f"{coordinator.serial_number}-{entity_description.key}"
|
||||
if entity_id := entity_registry.async_get_entity_id(
|
||||
SELECT_DOMAIN, DOMAIN, unique_id
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
|
||||
},
|
||||
"error": {
|
||||
@@ -69,6 +70,37 @@
|
||||
"co2": "Carbon dioxide",
|
||||
"pm": "Particulate matter"
|
||||
}
|
||||
},
|
||||
"nox_index_learning_time_offset": {
|
||||
"name": "NOx index learning offset",
|
||||
"state": {
|
||||
"12": "12 hours",
|
||||
"60": "60 hours",
|
||||
"120": "120 hours",
|
||||
"360": "360 hours",
|
||||
"720": "720 hours"
|
||||
}
|
||||
},
|
||||
"voc_index_learning_time_offset": {
|
||||
"name": "VOC index learning offset",
|
||||
"state": {
|
||||
"12": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::12%]",
|
||||
"60": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::60%]",
|
||||
"120": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::120%]",
|
||||
"360": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::360%]",
|
||||
"720": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::720%]"
|
||||
}
|
||||
},
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"name": "CO2 automatic baseline duration",
|
||||
"state": {
|
||||
"1": "1 day",
|
||||
"8": "8 days",
|
||||
"30": "30 days",
|
||||
"90": "90 days",
|
||||
"180": "180 days",
|
||||
"0": "[%key:common::state::off%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
@@ -98,10 +130,10 @@
|
||||
"name": "Carbon dioxide automatic baseline calibration"
|
||||
},
|
||||
"nox_learning_offset": {
|
||||
"name": "NOx learning offset"
|
||||
"name": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::name%]"
|
||||
},
|
||||
"tvoc_learning_offset": {
|
||||
"name": "VOC learning offset"
|
||||
"name": "[%key:component::airgradient::entity::select::voc_index_learning_time_offset::name%]"
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"name": "[%key:component::airgradient::entity::select::led_bar_mode::name%]",
|
||||
@@ -124,6 +156,11 @@
|
||||
"display_brightness": {
|
||||
"name": "[%key:component::airgradient::entity::number::display_brightness::name%]"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"post_data_to_airgradient": {
|
||||
"name": "Post data to Airgradient"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
110
homeassistant/components/airgradient/switch.py
Normal file
110
homeassistant/components/airgradient/switch.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""Support for AirGradient switch entities."""
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from airgradient import AirGradientClient, Config
|
||||
from airgradient.models import ConfigurationControl
|
||||
|
||||
from homeassistant.components.switch import (
|
||||
DOMAIN as SWITCH_DOMAIN,
|
||||
SwitchEntity,
|
||||
SwitchEntityDescription,
|
||||
)
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AirGradientConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AirGradientConfigCoordinator
|
||||
from .entity import AirGradientEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class AirGradientSwitchEntityDescription(SwitchEntityDescription):
|
||||
"""Describes AirGradient switch entity."""
|
||||
|
||||
value_fn: Callable[[Config], bool]
|
||||
set_value_fn: Callable[[AirGradientClient, bool], Awaitable[None]]
|
||||
|
||||
|
||||
POST_DATA_TO_AIRGRADIENT = AirGradientSwitchEntityDescription(
|
||||
key="post_data_to_airgradient",
|
||||
translation_key="post_data_to_airgradient",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
value_fn=lambda config: config.post_data_to_airgradient,
|
||||
set_value_fn=lambda client, value: client.enable_sharing_data(enable=value),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: AirGradientConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up AirGradient switch entities based on a config entry."""
|
||||
coordinator = entry.runtime_data.config
|
||||
|
||||
added_entities = False
|
||||
|
||||
@callback
|
||||
def _async_check_entities() -> None:
|
||||
nonlocal added_entities
|
||||
|
||||
if (
|
||||
coordinator.data.configuration_control is ConfigurationControl.LOCAL
|
||||
and not added_entities
|
||||
):
|
||||
async_add_entities(
|
||||
[AirGradientSwitch(coordinator, POST_DATA_TO_AIRGRADIENT)]
|
||||
)
|
||||
added_entities = True
|
||||
elif (
|
||||
coordinator.data.configuration_control is not ConfigurationControl.LOCAL
|
||||
and added_entities
|
||||
):
|
||||
entity_registry = er.async_get(hass)
|
||||
unique_id = f"{coordinator.serial_number}-{POST_DATA_TO_AIRGRADIENT.key}"
|
||||
if entity_id := entity_registry.async_get_entity_id(
|
||||
SWITCH_DOMAIN, DOMAIN, unique_id
|
||||
):
|
||||
entity_registry.async_remove(entity_id)
|
||||
added_entities = False
|
||||
|
||||
coordinator.async_add_listener(_async_check_entities)
|
||||
_async_check_entities()
|
||||
|
||||
|
||||
class AirGradientSwitch(AirGradientEntity, SwitchEntity):
|
||||
"""Defines an AirGradient switch entity."""
|
||||
|
||||
entity_description: AirGradientSwitchEntityDescription
|
||||
coordinator: AirGradientConfigCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirGradientConfigCoordinator,
|
||||
description: AirGradientSwitchEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize AirGradient switch."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.serial_number}-{description.key}"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the switch."""
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
await self.entity_description.set_value_fn(self.coordinator.client, True)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch off."""
|
||||
await self.entity_description.set_value_fn(self.coordinator.client, False)
|
||||
await self.coordinator.async_request_refresh()
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airtouch5",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["airtouch5py"],
|
||||
"requirements": ["airtouch5py==0.2.8"]
|
||||
"requirements": ["airtouch5py==0.2.10"]
|
||||
}
|
||||
|
||||
@@ -2,93 +2,37 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from genie_partner_sdk.client import AladdinConnectClient
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
OAuth2Session,
|
||||
async_get_config_entry_implementation,
|
||||
)
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from .api import AsyncConfigEntryAuth
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AladdinConnectCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.COVER, Platform.SENSOR]
|
||||
|
||||
type AladdinConnectConfigEntry = ConfigEntry[AladdinConnectCoordinator]
|
||||
DOMAIN = "aladdin_connect"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: AladdinConnectConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Aladdin Connect Genie from a config entry."""
|
||||
implementation = await async_get_config_entry_implementation(hass, entry)
|
||||
|
||||
session = OAuth2Session(hass, entry, implementation)
|
||||
auth = AsyncConfigEntryAuth(async_get_clientsession(hass), session)
|
||||
coordinator = AladdinConnectCoordinator(hass, AladdinConnectClient(auth))
|
||||
|
||||
await coordinator.async_setup()
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
async_remove_stale_devices(hass, entry)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: AladdinConnectConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_migrate_entry(
|
||||
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
|
||||
) -> bool:
|
||||
"""Migrate old config."""
|
||||
if config_entry.version < 2:
|
||||
config_entry.async_start_reauth(hass)
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
version=2,
|
||||
minor_version=1,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def async_remove_stale_devices(
|
||||
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
|
||||
) -> None:
|
||||
"""Remove stale devices from device registry."""
|
||||
device_registry = dr.async_get(hass)
|
||||
device_entries = dr.async_entries_for_config_entry(
|
||||
device_registry, config_entry.entry_id
|
||||
async def async_setup_entry(hass: HomeAssistant, _: ConfigEntry) -> bool:
|
||||
"""Set up Aladdin Connect from a config entry."""
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
DOMAIN,
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="integration_removed",
|
||||
translation_placeholders={
|
||||
"entries": "/config/integrations/integration/aladdin_connect",
|
||||
},
|
||||
)
|
||||
all_device_ids = {door.unique_id for door in config_entry.runtime_data.doors}
|
||||
|
||||
for device_entry in device_entries:
|
||||
device_id: str | None = None
|
||||
return True
|
||||
|
||||
for identifier in device_entry.identifiers:
|
||||
if identifier[0] == DOMAIN:
|
||||
device_id = identifier[1]
|
||||
break
|
||||
|
||||
if device_id is None or device_id not in all_device_ids:
|
||||
# If device_id is None an invalid device entry was found for this config entry.
|
||||
# If the device_id is not in existing device ids it's a stale device entry.
|
||||
# Remove config entry from this device entry in either case.
|
||||
device_registry.async_update_device(
|
||||
device_entry.id, remove_config_entry_id=config_entry.entry_id
|
||||
)
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if all(
|
||||
config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
for config_entry in hass.config_entries.async_entries(DOMAIN)
|
||||
if config_entry.entry_id != entry.entry_id
|
||||
):
|
||||
ir.async_delete_issue(hass, DOMAIN, DOMAIN)
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
"""API for Aladdin Connect Genie bound to Home Assistant OAuth."""
|
||||
|
||||
from typing import cast
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from genie_partner_sdk.auth import Auth
|
||||
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
|
||||
|
||||
API_URL = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1"
|
||||
API_KEY = "k6QaiQmcTm2zfaNns5L1Z8duBtJmhDOW8JawlCC3"
|
||||
|
||||
|
||||
class AsyncConfigEntryAuth(Auth): # type: ignore[misc]
|
||||
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
websession: ClientSession,
|
||||
oauth_session: OAuth2Session,
|
||||
) -> None:
|
||||
"""Initialize Aladdin Connect Genie auth."""
|
||||
super().__init__(
|
||||
websession, API_URL, oauth_session.token["access_token"], API_KEY
|
||||
)
|
||||
self._oauth_session = oauth_session
|
||||
|
||||
async def async_get_access_token(self) -> str:
|
||||
"""Return a valid access token."""
|
||||
await self._oauth_session.async_ensure_token_valid()
|
||||
|
||||
return cast(str, self._oauth_session.token["access_token"])
|
||||
@@ -1,14 +0,0 @@
|
||||
"""application_credentials platform the Aladdin Connect Genie integration."""
|
||||
|
||||
from homeassistant.components.application_credentials import AuthorizationServer
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN
|
||||
|
||||
|
||||
async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer:
|
||||
"""Return authorization server."""
|
||||
return AuthorizationServer(
|
||||
authorize_url=OAUTH2_AUTHORIZE,
|
||||
token_url=OAUTH2_TOKEN,
|
||||
)
|
||||
@@ -1,70 +1,11 @@
|
||||
"""Config flow for Aladdin Connect Genie."""
|
||||
"""Config flow for Aladdin Connect integration."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
|
||||
import jwt
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
|
||||
|
||||
from .const import DOMAIN
|
||||
from . import DOMAIN
|
||||
|
||||
|
||||
class AladdinConnectOAuth2FlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
|
||||
"""Config flow to handle Aladdin Connect Genie OAuth2 authentication."""
|
||||
class AladdinConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Aladdin Connect."""
|
||||
|
||||
DOMAIN = DOMAIN
|
||||
VERSION = 2
|
||||
MINOR_VERSION = 1
|
||||
|
||||
reauth_entry: ConfigEntry | None = None
|
||||
|
||||
async def async_step_reauth(
|
||||
self, user_input: Mapping[str, Any]
|
||||
) -> ConfigFlowResult:
|
||||
"""Perform reauth upon API auth error or upgrade from v1 to v2."""
|
||||
self.reauth_entry = self.hass.config_entries.async_get_entry(
|
||||
self.context["entry_id"]
|
||||
)
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
self, user_input: Mapping[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Dialog that informs the user that reauth is required."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(step_id="reauth_confirm")
|
||||
return await self.async_step_user()
|
||||
|
||||
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Create an oauth config entry or update existing entry for reauth."""
|
||||
token_payload = jwt.decode(
|
||||
data[CONF_TOKEN][CONF_ACCESS_TOKEN], options={"verify_signature": False}
|
||||
)
|
||||
if not self.reauth_entry:
|
||||
await self.async_set_unique_id(token_payload["sub"])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=token_payload["username"],
|
||||
data=data,
|
||||
)
|
||||
|
||||
if self.reauth_entry.unique_id == token_payload["username"]:
|
||||
return self.async_update_reload_and_abort(
|
||||
self.reauth_entry,
|
||||
data=data,
|
||||
unique_id=token_payload["sub"],
|
||||
)
|
||||
if self.reauth_entry.unique_id == token_payload["sub"]:
|
||||
return self.async_update_reload_and_abort(self.reauth_entry, data=data)
|
||||
|
||||
return self.async_abort(reason="wrong_account")
|
||||
|
||||
@property
|
||||
def logger(self) -> logging.Logger:
|
||||
"""Return logger."""
|
||||
return logging.getLogger(__name__)
|
||||
VERSION = 1
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
"""Constants for the Aladdin Connect Genie integration."""
|
||||
|
||||
DOMAIN = "aladdin_connect"
|
||||
|
||||
OAUTH2_AUTHORIZE = "https://app.aladdinconnect.net/login.html"
|
||||
OAUTH2_TOKEN = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1/oauth2/token"
|
||||
@@ -1,38 +0,0 @@
|
||||
"""Define an object to coordinate fetching Aladdin Connect data."""
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from genie_partner_sdk.client import AladdinConnectClient
|
||||
from genie_partner_sdk.model import GarageDoor
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AladdinConnectCoordinator(DataUpdateCoordinator[None]):
|
||||
"""Aladdin Connect Data Update Coordinator."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, acc: AladdinConnectClient) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(
|
||||
hass,
|
||||
logger=_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(seconds=15),
|
||||
)
|
||||
self.acc = acc
|
||||
self.doors: list[GarageDoor] = []
|
||||
|
||||
async def async_setup(self) -> None:
|
||||
"""Fetch initial data."""
|
||||
self.doors = await self.acc.get_doors()
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
"""Fetch data from API endpoint."""
|
||||
for door in self.doors:
|
||||
await self.acc.update_door(door.device_id, door.door_number)
|
||||
@@ -1,84 +0,0 @@
|
||||
"""Cover Entity for Genie Garage Door."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from genie_partner_sdk.model import GarageDoor
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
CoverDeviceClass,
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
|
||||
from .entity import AladdinConnectEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: AladdinConnectConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Aladdin Connect platform."""
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(AladdinDevice(coordinator, door) for door in coordinator.doors)
|
||||
|
||||
|
||||
class AladdinDevice(AladdinConnectEntity, CoverEntity):
|
||||
"""Representation of Aladdin Connect cover."""
|
||||
|
||||
_attr_device_class = CoverDeviceClass.GARAGE
|
||||
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
|
||||
) -> None:
|
||||
"""Initialize the Aladdin Connect cover."""
|
||||
super().__init__(coordinator, device)
|
||||
self._attr_unique_id = device.unique_id
|
||||
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Issue open command to cover."""
|
||||
await self.coordinator.acc.open_door(
|
||||
self._device.device_id, self._device.door_number
|
||||
)
|
||||
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Issue close command to cover."""
|
||||
await self.coordinator.acc.close_door(
|
||||
self._device.device_id, self._device.door_number
|
||||
)
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool | None:
|
||||
"""Update is closed attribute."""
|
||||
value = self.coordinator.acc.get_door_status(
|
||||
self._device.device_id, self._device.door_number
|
||||
)
|
||||
if value is None:
|
||||
return None
|
||||
return bool(value == "closed")
|
||||
|
||||
@property
|
||||
def is_closing(self) -> bool | None:
|
||||
"""Update is closing attribute."""
|
||||
value = self.coordinator.acc.get_door_status(
|
||||
self._device.device_id, self._device.door_number
|
||||
)
|
||||
if value is None:
|
||||
return None
|
||||
return bool(value == "closing")
|
||||
|
||||
@property
|
||||
def is_opening(self) -> bool | None:
|
||||
"""Update is opening attribute."""
|
||||
value = self.coordinator.acc.get_door_status(
|
||||
self._device.device_id, self._device.door_number
|
||||
)
|
||||
if value is None:
|
||||
return None
|
||||
return bool(value == "opening")
|
||||
@@ -1,27 +0,0 @@
|
||||
"""Defines a base Aladdin Connect entity."""
|
||||
|
||||
from genie_partner_sdk.model import GarageDoor
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AladdinConnectCoordinator
|
||||
|
||||
|
||||
class AladdinConnectEntity(CoordinatorEntity[AladdinConnectCoordinator]):
|
||||
"""Defines a base Aladdin Connect entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(coordinator)
|
||||
self._device = device
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, device.unique_id)},
|
||||
name=device.name,
|
||||
manufacturer="Overhead Door",
|
||||
)
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"domain": "aladdin_connect",
|
||||
"name": "Aladdin Connect",
|
||||
"codeowners": ["@swcloudgenie"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["application_credentials"],
|
||||
"codeowners": [],
|
||||
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
|
||||
"integration_type": "system",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["genie-partner-sdk==1.0.2"]
|
||||
"requirements": []
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
"""Support for Aladdin Connect Garage Door sensors."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
|
||||
from genie_partner_sdk.client import AladdinConnectClient
|
||||
from genie_partner_sdk.model import GarageDoor
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
|
||||
from .entity import AladdinConnectEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class AccSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes AladdinConnect sensor entity."""
|
||||
|
||||
value_fn: Callable[[AladdinConnectClient, str, int], float | None]
|
||||
|
||||
|
||||
SENSORS: tuple[AccSensorEntityDescription, ...] = (
|
||||
AccSensorEntityDescription(
|
||||
key="battery_level",
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
entity_registry_enabled_default=False,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=AladdinConnectClient.get_battery_status,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: AladdinConnectConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Aladdin Connect sensor devices."""
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
AladdinConnectSensor(coordinator, door, description)
|
||||
for description in SENSORS
|
||||
for door in coordinator.doors
|
||||
)
|
||||
|
||||
|
||||
class AladdinConnectSensor(AladdinConnectEntity, SensorEntity):
|
||||
"""A sensor implementation for Aladdin Connect devices."""
|
||||
|
||||
entity_description: AccSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AladdinConnectCoordinator,
|
||||
device: GarageDoor,
|
||||
description: AccSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize a sensor for an Aladdin Connect device."""
|
||||
super().__init__(coordinator, device)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{device.unique_id}-{description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self.entity_description.value_fn(
|
||||
self.coordinator.acc, self._device.device_id, self._device.door_number
|
||||
)
|
||||
@@ -1,29 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"title": "[%key:common::config_flow::title::reauth%]",
|
||||
"description": "Aladdin Connect needs to re-authenticate your account"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
|
||||
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
|
||||
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
|
||||
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
|
||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||
"issues": {
|
||||
"integration_removed": {
|
||||
"title": "The Aladdin Connect integration has been removed",
|
||||
"description": "The Aladdin Connect integration has been removed from Home Assistant.\n\nTo resolve this issue, please remove the (now defunct) integration entries from your Home Assistant setup. [Click here to see your existing Aladdin Connect integration entries]({entries})."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,10 @@ from .const import ( # noqa: F401
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA: Final = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE: Final = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
|
||||
CONF_DEFAULT_CODE = "default_code"
|
||||
|
||||
@@ -61,8 +63,6 @@ ALARM_SERVICE_SCHEMA: Final = make_entity_service_schema(
|
||||
{vol.Optional(ATTR_CODE): cv.string}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA: Final = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE: Final = cv.PLATFORM_SCHEMA_BASE
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ from alpha_vantage.timeseries import TimeSeries
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY, CONF_CURRENCY, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -59,7 +62,7 @@ CURRENCY_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_FOREIGN_EXCHANGE): vol.All(cv.ensure_list, [CURRENCY_SCHEMA]),
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/anova",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["anova_wifi"],
|
||||
"requirements": ["anova-wifi==0.12.0"]
|
||||
"requirements": ["anova-wifi==0.17.0"]
|
||||
}
|
||||
|
||||
@@ -68,4 +68,8 @@ class OnlineStatus(CoordinatorEntity[APCUPSdCoordinator], BinarySensorEntity):
|
||||
"""Returns true if the UPS is online."""
|
||||
# Check if ONLINE bit is set in STATFLAG.
|
||||
key = self.entity_description.key.upper()
|
||||
return int(self.coordinator.data[key], 16) & _VALUE_ONLINE_MASK != 0
|
||||
# The daemon could either report just a hex ("0x05000008"), or a hex with a "Status Flag"
|
||||
# suffix ("0x05000008 Status Flag") in older versions.
|
||||
# Here we trim the suffix if it exists to support both.
|
||||
flag = self.coordinator.data[key].removesuffix(" Status Flag")
|
||||
return int(flag, 16) & _VALUE_ONLINE_MASK != 0
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyaprilaire"],
|
||||
"requirements": ["pyaprilaire==0.7.0"]
|
||||
"requirements": ["pyaprilaire==0.7.4"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/apsystems",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["apsystems-ez1==1.3.1"]
|
||||
"requirements": ["apsystems-ez1==1.3.3"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioaquacell"],
|
||||
"requirements": ["aioaquacell==0.1.7"]
|
||||
"requirements": ["aioaquacell==0.1.8"]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ from dataclasses import dataclass
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
@@ -104,7 +104,7 @@ SENSOR_TYPES: tuple[AquaLogicSensorEntityDescription, ...] = (
|
||||
|
||||
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=SENSOR_KEYS): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
|
||||
@@ -10,7 +10,7 @@ import sharp_aquos_rc
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
@@ -37,7 +37,7 @@ DEFAULT_PASSWORD = "password"
|
||||
DEFAULT_TIMEOUT = 0.5
|
||||
DEFAULT_RETRIES = 2
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -9,7 +9,10 @@ import logging
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_MONITORED_VARIABLES,
|
||||
CONF_NAME,
|
||||
@@ -41,7 +44,7 @@ PIN_VARIABLE_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_RESOURCE): cv.url,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/arve",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["asyncarve==0.0.9"]
|
||||
"requirements": ["asyncarve==0.1.1"]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ from pyatome.client import AtomeClient, PyAtomeError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
@@ -49,7 +49,7 @@ WEEKLY_TYPE = "week"
|
||||
MONTHLY_TYPE = "month"
|
||||
YEARLY_TYPE = "year"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
from aiohttp import ClientResponseError
|
||||
from path import Path
|
||||
from yalexs.exceptions import AugustApiAIOHTTPError
|
||||
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
|
||||
from yalexs.manager.gateway import Config as YaleXSConfig
|
||||
|
||||
@@ -28,5 +28,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/august",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pubnub", "yalexs"],
|
||||
"requirements": ["yalexs==6.4.1", "yalexs-ble==2.4.3"]
|
||||
"requirements": ["yalexs==6.4.2", "yalexs-ble==2.4.3"]
|
||||
}
|
||||
|
||||
@@ -37,7 +37,10 @@
|
||||
"message": "Username \"{username}\" already exists"
|
||||
},
|
||||
"username_not_normalized": {
|
||||
"message": "Username \"{new_username}\" is not normalized"
|
||||
"message": "Username \"{new_username}\" is not normalized. Please make sure the username is lowercase and does not contain any whitespace."
|
||||
},
|
||||
"user_not_found": {
|
||||
"message": "User not found"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
|
||||
@@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
)
|
||||
@@ -35,7 +35,7 @@ DEVICE_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA},
|
||||
vol.Optional(CONF_USERNAME): cv.string,
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["axis"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["axis==61"],
|
||||
"requirements": ["axis==62"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "AXIS"
|
||||
|
||||
@@ -45,7 +45,7 @@ from homeassistant.components.media_player import (
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_MODEL
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
@@ -316,7 +316,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
|
||||
@callback
|
||||
def _async_update_playback_error(self, data: PlaybackError) -> None:
|
||||
"""Show playback error."""
|
||||
_LOGGER.error(data.error)
|
||||
raise HomeAssistantError(data.error)
|
||||
|
||||
@callback
|
||||
def _async_update_playback_progress(self, data: PlaybackProgress) -> None:
|
||||
@@ -366,7 +366,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
|
||||
@property
|
||||
def volume_level(self) -> float | None:
|
||||
"""Volume level of the media player (0..1)."""
|
||||
if self._volume.level and self._volume.level.level:
|
||||
if self._volume.level and self._volume.level.level is not None:
|
||||
return float(self._volume.level.level / 100)
|
||||
return None
|
||||
|
||||
@@ -516,7 +516,9 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
|
||||
|
||||
self.async_write_ha_state()
|
||||
else:
|
||||
_LOGGER.error("Seeking is currently only supported when using Deezer")
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN, translation_key="non_deezer_seeking"
|
||||
)
|
||||
|
||||
async def async_media_previous_track(self) -> None:
|
||||
"""Send the previous track command."""
|
||||
@@ -529,12 +531,14 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
|
||||
async def async_select_source(self, source: str) -> None:
|
||||
"""Select an input source."""
|
||||
if source not in self._sources.values():
|
||||
_LOGGER.error(
|
||||
"Invalid source: %s. Valid sources are: %s",
|
||||
source,
|
||||
list(self._sources.values()),
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_source",
|
||||
translation_placeholders={
|
||||
"invalid_source": source,
|
||||
"valid_sources": ",".join(list(self._sources.values())),
|
||||
},
|
||||
)
|
||||
return
|
||||
|
||||
key = [x for x in self._sources if self._sources[x] == source][0]
|
||||
|
||||
@@ -559,12 +563,14 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
|
||||
media_type = MediaType.MUSIC
|
||||
|
||||
if media_type not in VALID_MEDIA_TYPES:
|
||||
_LOGGER.error(
|
||||
"%s is an invalid type. Valid values are: %s",
|
||||
media_type,
|
||||
VALID_MEDIA_TYPES,
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_media_type",
|
||||
translation_placeholders={
|
||||
"invalid_media_type": media_type,
|
||||
"valid_media_types": ",".join(VALID_MEDIA_TYPES),
|
||||
},
|
||||
)
|
||||
return
|
||||
|
||||
if media_source.is_media_source_id(media_id):
|
||||
sourced_media = await media_source.async_resolve_media(
|
||||
@@ -681,7 +687,14 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
|
||||
)
|
||||
|
||||
except ApiException as error:
|
||||
_LOGGER.error(json.loads(error.body)["message"])
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="play_media_error",
|
||||
translation_placeholders={
|
||||
"media_type": media_type,
|
||||
"error_message": json.loads(error.body)["message"],
|
||||
},
|
||||
) from error
|
||||
|
||||
async def async_browse_media(
|
||||
self,
|
||||
|
||||
@@ -28,6 +28,18 @@
|
||||
"exceptions": {
|
||||
"m3u_invalid_format": {
|
||||
"message": "Media sources with the .m3u extension are not supported."
|
||||
},
|
||||
"non_deezer_seeking": {
|
||||
"message": "Seeking is currently only supported when using Deezer"
|
||||
},
|
||||
"invalid_source": {
|
||||
"message": "Invalid source: {invalid_source}. Valid sources are: {valid_sources}"
|
||||
},
|
||||
"invalid_media_type": {
|
||||
"message": "{invalid_media_type} is an invalid type. Valid values are: {valid_media_types}."
|
||||
},
|
||||
"play_media_error": {
|
||||
"message": "An error occurred while attempting to play {media_type}: {error_message}."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
@@ -80,7 +80,7 @@ SENSOR_TYPES_UPTIME: tuple[SensorEntityDescription, ...] = (
|
||||
|
||||
SENSOR_KEYS: list[str] = [desc.key for desc in (*SENSOR_TYPES, *SENSOR_TYPES_UPTIME)]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_VARIABLES): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
|
||||
@@ -6,7 +6,7 @@ from beewi_smartclim import BeewiSmartClimPoller
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
)
|
||||
@@ -26,7 +26,7 @@ SENSOR_TYPES = [
|
||||
[SensorDeviceClass.BATTERY, "Battery", PERCENTAGE],
|
||||
]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MAC): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -14,10 +14,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
@@ -30,11 +27,11 @@ from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DOMAIN = "binary_sensor"
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
|
||||
class BinarySensorDeviceClass(StrEnum):
|
||||
|
||||
@@ -9,7 +9,7 @@ from blockchain import exchangerates, statistics
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
@@ -127,7 +127,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
|
||||
OPTION_KEYS = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_DISPLAY_OPTIONS, default=[]): vol.All(
|
||||
cv.ensure_list, [vol.In(OPTION_KEYS)]
|
||||
|
||||
@@ -7,7 +7,10 @@ from contextlib import suppress
|
||||
from bizkaibus.bizkaibus import BizkaibusData
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -21,7 +24,7 @@ CONF_ROUTE = "route"
|
||||
|
||||
DEFAULT_NAME = "Next bus"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_STOP_ID): cv.string,
|
||||
vol.Required(CONF_ROUTE): cv.string,
|
||||
|
||||
@@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_HS_COLOR,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
)
|
||||
@@ -25,7 +25,7 @@ CONF_SERIAL = "serial"
|
||||
|
||||
DEFAULT_NAME = "Blinkstick"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_SERIAL): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -8,7 +8,10 @@ import logging
|
||||
from pyblockchain import get_balance, validate_address
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -23,7 +26,7 @@ DEFAULT_NAME = "Bitcoin Balance"
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ADDRESSES): [cv.string],
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
)
|
||||
@@ -63,7 +63,7 @@ SENSOR_DEVICE_CLASS = {
|
||||
# Which sensors to format numerically
|
||||
FORMAT_NUMBERS = ["Temperature", "Pressure", "Voltage"]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_MONITORED_CONDITIONS, default=SENSOR_TYPES): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
|
||||
@@ -18,7 +18,7 @@ import xmltodict
|
||||
|
||||
from homeassistant.components import media_source
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
BrowseMedia,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
@@ -70,7 +70,7 @@ UPDATE_CAPTURE_INTERVAL = timedelta(minutes=30)
|
||||
UPDATE_PRESETS_INTERVAL = timedelta(minutes=30)
|
||||
UPDATE_SERVICES_INTERVAL = timedelta(minutes=30)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_HOSTS): vol.All(
|
||||
cv.ensure_list,
|
||||
|
||||
@@ -72,7 +72,10 @@ def _async_migrate_options_from_data_if_missing(
|
||||
options = dict(entry.options)
|
||||
|
||||
if CONF_READ_ONLY in data or list(options) != list(DEFAULT_OPTIONS):
|
||||
options = dict(DEFAULT_OPTIONS, **options)
|
||||
options = dict(
|
||||
DEFAULT_OPTIONS,
|
||||
**{k: v for k, v in options.items() if k in DEFAULT_OPTIONS},
|
||||
)
|
||||
options[CONF_READ_ONLY] = data.pop(CONF_READ_ONLY, False)
|
||||
|
||||
hass.config_entries.async_update_entry(entry, data=data, options=options)
|
||||
|
||||
@@ -13,10 +13,7 @@ import voluptuous as vol
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
@@ -25,14 +22,15 @@ from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DOMAIN, SERVICE_PRESS
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ButtonDeviceClass(StrEnum):
|
||||
"""Device class for buttons."""
|
||||
|
||||
@@ -29,12 +29,7 @@ from homeassistant.core import (
|
||||
callback,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
time_period_str,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.event import async_track_point_in_time
|
||||
@@ -74,6 +69,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "calendar"
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = datetime.timedelta(seconds=60)
|
||||
|
||||
# Don't support rrules more often than daily
|
||||
@@ -469,7 +466,7 @@ def extract_offset(summary: str, offset_prefix: str) -> tuple[str, datetime.time
|
||||
else:
|
||||
time = f"0:{time}"
|
||||
|
||||
offset_time = time_period_str(time)
|
||||
offset_time = cv.time_period_str(time)
|
||||
summary = (summary[: search.start()] + summary[search.end() :]).strip()
|
||||
return (summary, offset_time)
|
||||
return (summary, datetime.timedelta())
|
||||
|
||||
@@ -48,11 +48,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
@@ -87,14 +83,16 @@ from .prefs import CameraPreferences, DynamicStreamSettings # noqa: F401
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
|
||||
SERVICE_ENABLE_MOTION: Final = "enable_motion_detection"
|
||||
SERVICE_DISABLE_MOTION: Final = "disable_motion_detection"
|
||||
SERVICE_SNAPSHOT: Final = "snapshot"
|
||||
SERVICE_PLAY_STREAM: Final = "play_stream"
|
||||
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||
|
||||
ATTR_FILENAME: Final = "filename"
|
||||
ATTR_MEDIA_PLAYER: Final = "media_player"
|
||||
ATTR_FORMAT: Final = "format"
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Any
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
)
|
||||
@@ -27,7 +27,7 @@ from .const import DEFAULT_PORT, DOMAIN
|
||||
|
||||
SCAN_INTERVAL = timedelta(hours=12)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
|
||||
@@ -8,7 +8,7 @@ from pychannels import Channels
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
@@ -26,7 +26,7 @@ DATA_CHANNELS = "channels"
|
||||
DEFAULT_NAME = "Channels"
|
||||
DEFAULT_PORT = 57000
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -9,7 +9,7 @@ from clementineremote import ClementineRemote
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
@@ -26,7 +26,7 @@ DEFAULT_PORT = 5500
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=5)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_ACCESS_TOKEN): cv.positive_int,
|
||||
|
||||
@@ -25,13 +25,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
make_entity_service_schema,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
@@ -117,24 +111,25 @@ from .const import ( # noqa: F401
|
||||
HVACMode,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
DEFAULT_MIN_TEMP = 7
|
||||
DEFAULT_MAX_TEMP = 35
|
||||
DEFAULT_MIN_HUMIDITY = 30
|
||||
DEFAULT_MAX_HUMIDITY = 99
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
CONVERTIBLE_ATTRIBUTE = [ATTR_TEMPERATURE, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
SET_TEMPERATURE_SCHEMA = vol.All(
|
||||
cv.has_at_least_one_key(
|
||||
ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW
|
||||
),
|
||||
make_entity_service_schema(
|
||||
cv.make_entity_service_schema(
|
||||
{
|
||||
vol.Exclusive(ATTR_TEMPERATURE, "temperature"): vol.Coerce(float),
|
||||
vol.Inclusive(ATTR_TARGET_TEMP_HIGH, "temperature"): vol.Coerce(float),
|
||||
@@ -382,6 +377,14 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
# Return if integration has migrated already
|
||||
return
|
||||
|
||||
supported_features = self.supported_features
|
||||
if supported_features & (
|
||||
ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
|
||||
):
|
||||
# The entity supports both turn_on and turn_off, the backwards compatibility
|
||||
# checks are not needed
|
||||
return
|
||||
|
||||
supported_features = self.supported_features
|
||||
if not supported_features & ClimateEntityFeature.TURN_OFF and (
|
||||
type(self).async_turn_off is not ClimateEntity.async_turn_off
|
||||
|
||||
@@ -9,7 +9,7 @@ from pycmus import exceptions, remote
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
@@ -26,7 +26,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
DEFAULT_NAME = "cmus"
|
||||
DEFAULT_PORT = 3000
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Inclusive(CONF_HOST, "remote"): cv.string,
|
||||
vol.Inclusive(CONF_PASSWORD, "remote"): cv.string,
|
||||
|
||||
@@ -11,7 +11,7 @@ import aiohttp
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
@@ -57,7 +57,7 @@ SENSORS_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{vol.Required(CONF_MONITORED_FEEDS): [SENSORS_SCHEMA]}
|
||||
)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ from pycomfoconnect import (
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
@@ -263,7 +263,7 @@ SENSOR_TYPES = (
|
||||
),
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_RESOURCES, default=[]): vol.All(
|
||||
cv.ensure_list, [vol.In([desc.key for desc in SENSOR_TYPES])]
|
||||
|
||||
@@ -53,11 +53,7 @@ async def websocket_create(
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
await provider.async_add_auth(msg["username"], msg["password"])
|
||||
except auth_ha.InvalidUser:
|
||||
connection.send_error(msg["id"], "username_exists", "Username already exists")
|
||||
return
|
||||
await provider.async_add_auth(msg["username"], msg["password"])
|
||||
|
||||
credentials = await provider.async_get_or_create_credentials(
|
||||
{"username": msg["username"]}
|
||||
@@ -94,13 +90,7 @@ async def websocket_delete(
|
||||
connection.send_result(msg["id"])
|
||||
return
|
||||
|
||||
try:
|
||||
await provider.async_remove_auth(msg["username"])
|
||||
except auth_ha.InvalidUser:
|
||||
connection.send_error(
|
||||
msg["id"], "auth_not_found", "Given username was not found."
|
||||
)
|
||||
return
|
||||
await provider.async_remove_auth(msg["username"])
|
||||
|
||||
connection.send_result(msg["id"])
|
||||
|
||||
@@ -187,14 +177,8 @@ async def websocket_admin_change_password(
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
await provider.async_change_password(username, msg["password"])
|
||||
connection.send_result(msg["id"])
|
||||
except auth_ha.InvalidUser:
|
||||
connection.send_error(
|
||||
msg["id"], "credentials_not_found", "Credentials not found"
|
||||
)
|
||||
return
|
||||
await provider.async_change_password(username, msg["password"])
|
||||
connection.send_result(msg["id"])
|
||||
|
||||
|
||||
@websocket_api.websocket_command(
|
||||
|
||||
@@ -5,7 +5,10 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
import uuid
|
||||
|
||||
from homeassistant.components.scene import DOMAIN, PLATFORM_SCHEMA
|
||||
from homeassistant.components.scene import (
|
||||
DOMAIN,
|
||||
PLATFORM_SCHEMA as SCENE_PLATFORM_SCHEMA,
|
||||
)
|
||||
from homeassistant.config import SCENE_CONFIG_PATH
|
||||
from homeassistant.const import CONF_ID, SERVICE_RELOAD
|
||||
from homeassistant.core import DOMAIN as HA_DOMAIN, HomeAssistant, callback
|
||||
@@ -14,6 +17,8 @@ from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
from .const import ACTION_DELETE
|
||||
from .view import EditIdBasedConfigView
|
||||
|
||||
PLATFORM_SCHEMA = SCENE_PLATFORM_SCHEMA
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup(hass: HomeAssistant) -> bool:
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/conversation",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.6.21"]
|
||||
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.7.3"]
|
||||
}
|
||||
|
||||
@@ -30,10 +30,7 @@ from homeassistant.const import (
|
||||
STATE_OPENING,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
@@ -49,9 +46,10 @@ from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=15)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = timedelta(seconds=15)
|
||||
|
||||
|
||||
class CoverDeviceClass(StrEnum):
|
||||
|
||||
@@ -9,7 +9,10 @@ from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
@@ -47,7 +50,7 @@ SCAN_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
PRINTER_STATES = {3: "idle", 4: "printing", 5: "stopped"}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_PRINTERS): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(CONF_IS_CUPS_SERVER, default=DEFAULT_IS_CUPS_SERVER): cv.boolean,
|
||||
|
||||
@@ -8,7 +8,10 @@ import logging
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY, CONF_BASE, CONF_NAME, CONF_QUOTE
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -24,7 +27,7 @@ DEFAULT_NAME = "CurrencyLayer Sensor"
|
||||
|
||||
SCAN_INTERVAL = timedelta(hours=4)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Required(CONF_QUOTE): vol.All(cv.ensure_list, [cv.string]),
|
||||
|
||||
@@ -13,21 +13,19 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_DATE
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN, SERVICE_SET_VALUE
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ["DOMAIN", "DateEntity", "DateEntityDescription"]
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@ import voluptuous as vol
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
@@ -23,11 +19,13 @@ from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import ATTR_DATETIME, DOMAIN, SERVICE_SET_VALUE
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
|
||||
PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ["ATTR_DATETIME", "DOMAIN", "DateTimeEntity", "DateTimeEntityDescription"]
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import voluptuous as vol
|
||||
from homeassistant import util
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
)
|
||||
@@ -48,7 +48,7 @@ DEVICE_SCHEMA = vol.Schema(
|
||||
|
||||
PLATFORM_SCHEMA = vol.Schema(
|
||||
vol.All(
|
||||
PLATFORM_SCHEMA.extend(
|
||||
LIGHT_PLATFORM_SCHEMA.extend(
|
||||
{vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}}
|
||||
),
|
||||
_name_validator,
|
||||
|
||||
@@ -15,7 +15,7 @@ from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_TRANSITION,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
LightEntityFeature,
|
||||
@@ -29,7 +29,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Validation of the user's configuration
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
|
||||
{vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
|
||||
)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from pydelijn.common import HttpException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
)
|
||||
@@ -31,7 +31,7 @@ CONF_NUMBER_OF_DEPARTURES = "number_of_departures"
|
||||
|
||||
DEFAULT_NAME = "De Lijn"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Required(CONF_NEXT_DEPARTURE): [
|
||||
|
||||
@@ -8,7 +8,7 @@ import telnetlib # pylint: disable=deprecated-module
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
@@ -38,7 +38,7 @@ SUPPORT_MEDIA_MODES = (
|
||||
| MediaPlayerEntityFeature.PLAY
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -9,7 +9,11 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, RestoreSensor, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
RestoreSensor,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
@@ -62,7 +66,7 @@ UNIT_TIME = {
|
||||
DEFAULT_ROUND = 3
|
||||
DEFAULT_TIME_WINDOW = 0
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_SOURCE): cv.entity_id,
|
||||
|
||||
@@ -10,7 +10,7 @@ import discogs_client
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
@@ -58,7 +58,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
)
|
||||
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TOKEN): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -6,15 +6,16 @@ import io
|
||||
|
||||
import face_recognition
|
||||
|
||||
from homeassistant.components.image_processing import ImageProcessingFaceEntity
|
||||
from homeassistant.components.image_processing import (
|
||||
PLATFORM_SCHEMA as IMAGE_PROCESSING_PLATFORM_SCHEMA,
|
||||
ImageProcessingFaceEntity,
|
||||
)
|
||||
from homeassistant.const import ATTR_LOCATION, CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE
|
||||
from homeassistant.core import HomeAssistant, split_entity_id
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from homeassistant.components.image_processing import ( # noqa: F401, isort:skip
|
||||
PLATFORM_SCHEMA,
|
||||
)
|
||||
PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA
|
||||
|
||||
|
||||
def setup_platform(
|
||||
|
||||
@@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components.image_processing import (
|
||||
CONF_CONFIDENCE,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as IMAGE_PROCESSING_PLATFORM_SCHEMA,
|
||||
ImageProcessingFaceEntity,
|
||||
)
|
||||
from homeassistant.const import ATTR_NAME, CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE
|
||||
@@ -23,7 +23,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_FACES = "faces"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_FACES): {cv.string: cv.isfile},
|
||||
vol.Optional(CONF_CONFIDENCE, default=0.6): vol.Coerce(float),
|
||||
|
||||
@@ -4,11 +4,11 @@ from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from pizzapi import Address, Customer, Order
|
||||
from pizzapi.address import StoreException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import http
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
@@ -118,7 +118,7 @@ class Dominos:
|
||||
self.country = conf.get(ATTR_COUNTRY)
|
||||
try:
|
||||
self.closest_store = self.address.closest_store()
|
||||
except StoreException:
|
||||
except Exception: # noqa: BLE001
|
||||
self.closest_store = None
|
||||
|
||||
def handle_order(self, call: ServiceCall) -> None:
|
||||
@@ -139,7 +139,7 @@ class Dominos:
|
||||
"""Update the shared closest store (if open)."""
|
||||
try:
|
||||
self.closest_store = self.address.closest_store()
|
||||
except StoreException:
|
||||
except Exception: # noqa: BLE001
|
||||
self.closest_store = None
|
||||
return False
|
||||
return True
|
||||
@@ -219,7 +219,7 @@ class DominosOrder(Entity):
|
||||
"""Update the order state and refreshes the store."""
|
||||
try:
|
||||
self.dominos.update_closest_store()
|
||||
except StoreException:
|
||||
except Exception: # noqa: BLE001
|
||||
self._orderable = False
|
||||
return
|
||||
|
||||
@@ -227,13 +227,13 @@ class DominosOrder(Entity):
|
||||
order = self.order()
|
||||
order.pay_with()
|
||||
self._orderable = True
|
||||
except StoreException:
|
||||
except Exception: # noqa: BLE001
|
||||
self._orderable = False
|
||||
|
||||
def order(self):
|
||||
"""Create the order object."""
|
||||
if self.dominos.closest_store is None:
|
||||
raise StoreException
|
||||
raise HomeAssistantError("No store available")
|
||||
|
||||
order = Order(
|
||||
self.dominos.closest_store,
|
||||
@@ -252,7 +252,7 @@ class DominosOrder(Entity):
|
||||
try:
|
||||
order = self.order()
|
||||
order.place()
|
||||
except StoreException:
|
||||
except Exception: # noqa: BLE001
|
||||
self._orderable = False
|
||||
_LOGGER.warning(
|
||||
"Attempted to order Dominos - Order invalid or store closed"
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/dominos",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pizzapi"],
|
||||
"requirements": ["pizzapi==0.0.3"]
|
||||
"requirements": ["pizzapi==0.0.6"]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components.image_processing import (
|
||||
CONF_CONFIDENCE,
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as IMAGE_PROCESSING_PLATFORM_SCHEMA,
|
||||
ImageProcessingEntity,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
@@ -66,7 +66,7 @@ LABEL_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_URL): cv.string,
|
||||
vol.Required(CONF_DETECTOR): cv.string,
|
||||
|
||||
@@ -9,7 +9,7 @@ import re
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
@@ -78,7 +78,7 @@ SENSOR_TYPES: tuple[DovadoSensorEntityDescription, ...] = (
|
||||
|
||||
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{vol.Required(CONF_SENSORS): vol.All(cv.ensure_list, [vol.In(SENSOR_KEYS)])}
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
@@ -30,7 +30,7 @@ DEFAULT_NAME = "Current Energy Usage"
|
||||
DEFAULT_VERSION = 1
|
||||
DOMAIN = "dte_energy_bridge"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_IP_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -13,7 +13,10 @@ from http import HTTPStatus
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -38,7 +41,7 @@ DEFAULT_NAME = "Next Bus"
|
||||
SCAN_INTERVAL = timedelta(minutes=1)
|
||||
TIME_STR_FORMAT = "%H:%M"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_STOP_ID): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
|
||||
@@ -9,7 +9,10 @@ import logging
|
||||
import dweepy
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE,
|
||||
CONF_NAME,
|
||||
@@ -27,7 +30,7 @@ DEFAULT_NAME = "Dweet.io Sensor"
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_DEVICE): cv.string,
|
||||
vol.Required(CONF_VALUE_TEMPLATE): cv.template,
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["easyenergy==2.1.1"]
|
||||
"requirements": ["easyenergy==2.1.2"]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ from pyebox.client import PyEboxError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
@@ -139,7 +139,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
|
||||
SENSOR_TYPE_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_VARIABLES): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPE_KEYS)]
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
|
||||
"requirements": ["py-sucks==0.9.10", "deebot-client==8.0.0"]
|
||||
"requirements": ["py-sucks==0.9.10", "deebot-client==8.1.1"]
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ from beacontools import BeaconScanner, EddystoneFilter, EddystoneTLMFrame
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
)
|
||||
@@ -43,7 +43,7 @@ BEACON_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_BT_DEVICE_ID, default=0): cv.positive_int,
|
||||
vol.Required(CONF_BEACONS): vol.Schema({cv.string: BEACON_SCHEMA}),
|
||||
|
||||
@@ -9,7 +9,7 @@ import eliqonline
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
@@ -29,7 +29,7 @@ DEFAULT_NAME = "ELIQ Online"
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ACCESS_TOKEN): cv.string,
|
||||
vol.Required(CONF_CHANNEL_ID): cv.positive_int,
|
||||
|
||||
@@ -8,7 +8,7 @@ from pyemby import EmbyServer
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
@@ -47,7 +47,7 @@ SUPPORT_EMBY = (
|
||||
| MediaPlayerEntityFeature.PLAY
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
|
||||
|
||||
@@ -10,7 +10,7 @@ from pyemoncms import EmoncmsClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
@@ -54,7 +54,7 @@ DEFAULT_UNIT = UnitOfPower.WATT
|
||||
|
||||
ONLY_INCL_EXCL_NONE = "only_include_exclude_or_none"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Required(CONF_URL): cv.string,
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["sense_energy"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["sense-energy==0.12.2"]
|
||||
"requirements": ["sense-energy==0.12.4"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/energyzero",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["energyzero==2.1.0"]
|
||||
"requirements": ["energyzero==2.1.1"]
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user