Merge pull request #54249 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2021-08-07 23:19:11 -07:00 committed by GitHub
commit f3de8b9f28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 133 additions and 46 deletions

View File

@ -91,13 +91,13 @@ class AdsCover(AdsEntity, CoverEntity):
):
"""Initialize AdsCover entity."""
super().__init__(ads_hub, name, ads_var_is_closed)
if self._ads_var is None:
if self._attr_unique_id is None:
if ads_var_position is not None:
self._unique_id = ads_var_position
self._attr_unique_id = ads_var_position
elif ads_var_pos_set is not None:
self._unique_id = ads_var_pos_set
self._attr_unique_id = ads_var_pos_set
elif ads_var_open is not None:
self._unique_id = ads_var_open
self._attr_unique_id = ads_var_open
self._state_dict[STATE_KEY_POSITION] = None
self._ads_var_position = ads_var_position

View File

@ -49,7 +49,10 @@ def setup_platform(
try:
if not acc.login():
raise ValueError("Username or Password is incorrect")
add_entities(AladdinDevice(acc, door) for door in acc.get_doors())
add_entities(
(AladdinDevice(acc, door) for door in acc.get_doors()),
update_before_add=True,
)
except (TypeError, KeyError, NameError, ValueError) as ex:
_LOGGER.error("%s", ex)
hass.components.persistent_notification.create(

View File

@ -449,6 +449,11 @@ class ADBDevice(MediaPlayerEntity):
ATTR_HDMI_INPUT: None,
}
@property
def media_image_hash(self):
"""Hash value for media image."""
return f"{datetime.now().timestamp()}" if self._screencap else None
@adb_decorator()
async def _adb_screencap(self):
"""Take a screen capture from the device."""
@ -458,9 +463,6 @@ class ADBDevice(MediaPlayerEntity):
"""Fetch current playing image."""
if not self._screencap or self.state in [STATE_OFF, None] or not self.available:
return None, None
self._attr_media_image_hash = (
f"{datetime.now().timestamp()}" if self._screencap else None
)
media_data = await self._adb_screencap()
if media_data:

View File

@ -223,7 +223,6 @@ SENSOR_TYPES = {
None,
4,
DEVICE_CLASS_TEMPERATURE,
None,
],
"Flame": ["Flame", None, "mdi:toggle-switch", 2, None],
"PowerEnergyConsumptionHeatingCircuit": [

View File

@ -66,6 +66,7 @@ from .const import (
CONF_INPUT_TYPE,
CONF_MAX_TEMP,
CONF_MIN_TEMP,
CONF_MSG_WAIT,
CONF_PARITY,
CONF_PRECISION,
CONF_RETRIES,
@ -283,6 +284,7 @@ MODBUS_SCHEMA = vol.Schema(
vol.Optional(CONF_DELAY, default=0): cv.positive_int,
vol.Optional(CONF_RETRIES, default=3): cv.positive_int,
vol.Optional(CONF_RETRY_ON_EMPTY, default=False): cv.boolean,
vol.Optional(CONF_MSG_WAIT): cv.positive_int,
vol.Optional(CONF_BINARY_SENSORS): vol.All(
cv.ensure_list, [BINARY_SENSOR_SCHEMA]
),

View File

@ -31,6 +31,7 @@ CONF_INPUTS = "inputs"
CONF_INPUT_TYPE = "input_type"
CONF_MAX_TEMP = "max_temp"
CONF_MIN_TEMP = "min_temp"
CONF_MSG_WAIT = "message_wait_milliseconds"
CONF_PARITY = "parity"
CONF_REGISTER = "register"
CONF_REGISTER_TYPE = "register_type"

View File

@ -39,6 +39,7 @@ from .const import (
CONF_BAUDRATE,
CONF_BYTESIZE,
CONF_CLOSE_COMM_ON_ERROR,
CONF_MSG_WAIT,
CONF_PARITY,
CONF_RETRIES,
CONF_RETRY_ON_EMPTY,
@ -229,6 +230,12 @@ class ModbusHub:
self._pb_params["framer"] = ModbusRtuFramer
Defaults.Timeout = client_config[CONF_TIMEOUT]
if CONF_MSG_WAIT in client_config:
self._msg_wait = client_config[CONF_MSG_WAIT] / 1000
elif self._config_type == CONF_SERIAL:
self._msg_wait = 30 / 1000
else:
self._msg_wait = 0
def _log_error(self, text: str, error_state=True):
log_text = f"Pymodbus: {text}"
@ -322,7 +329,7 @@ class ModbusHub:
result = await self.hass.async_add_executor_job(
self._pymodbus_call, unit, address, value, use_call
)
if self._config_type == "serial":
if self._msg_wait:
# small delay until next request/response
await asyncio.sleep(30 / 1000)
await asyncio.sleep(self._msg_wait)
return result

View File

@ -72,6 +72,7 @@ def struct_validator(config):
_LOGGER.warning(error)
try:
data_type = OLD_DATA_TYPES[data_type][config.get(CONF_COUNT, 1)]
config[CONF_DATA_TYPE] = data_type
except KeyError as exp:
error = f"{name} cannot convert automatically {data_type}"
raise vol.Invalid(error) from exp

View File

@ -53,7 +53,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
)
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.network import get_url
from homeassistant.helpers.network import NoURLAvailableError, get_url
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
@ -145,12 +145,21 @@ def listen_for_new_cameras(
@callback
def async_generate_motioneye_webhook(hass: HomeAssistant, webhook_id: str) -> str:
def async_generate_motioneye_webhook(
hass: HomeAssistant, webhook_id: str
) -> str | None:
"""Generate the full local URL for a webhook_id."""
return "{}{}".format(
get_url(hass, allow_cloud=False),
async_generate_path(webhook_id),
)
try:
return "{}{}".format(
get_url(hass, allow_cloud=False),
async_generate_path(webhook_id),
)
except NoURLAvailableError:
_LOGGER.warning(
"Unable to get Home Assistant URL. Have you set the internal and/or "
"external URLs in Configuration -> General?"
)
return None
@callback
@ -228,28 +237,31 @@ def _add_camera(
if entry.options.get(CONF_WEBHOOK_SET, DEFAULT_WEBHOOK_SET):
url = async_generate_motioneye_webhook(hass, entry.data[CONF_WEBHOOK_ID])
if _set_webhook(
_build_url(
device,
url,
EVENT_MOTION_DETECTED,
EVENT_MOTION_DETECTED_KEYS,
),
KEY_WEB_HOOK_NOTIFICATIONS_URL,
KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD,
KEY_WEB_HOOK_NOTIFICATIONS_ENABLED,
camera,
) | _set_webhook(
_build_url(
device,
url,
EVENT_FILE_STORED,
EVENT_FILE_STORED_KEYS,
),
KEY_WEB_HOOK_STORAGE_URL,
KEY_WEB_HOOK_STORAGE_HTTP_METHOD,
KEY_WEB_HOOK_STORAGE_ENABLED,
camera,
if url and (
_set_webhook(
_build_url(
device,
url,
EVENT_MOTION_DETECTED,
EVENT_MOTION_DETECTED_KEYS,
),
KEY_WEB_HOOK_NOTIFICATIONS_URL,
KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD,
KEY_WEB_HOOK_NOTIFICATIONS_ENABLED,
camera,
)
| _set_webhook(
_build_url(
device,
url,
EVENT_FILE_STORED,
EVENT_FILE_STORED_KEYS,
),
KEY_WEB_HOOK_STORAGE_URL,
KEY_WEB_HOOK_STORAGE_HTTP_METHOD,
KEY_WEB_HOOK_STORAGE_ENABLED,
camera,
)
):
hass.async_create_task(client.async_set_camera(camera_id, camera))

View File

@ -45,6 +45,8 @@ from .const import (
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_AIRHUMIDIFIER_CA1,
MODEL_AIRHUMIDIFIER_CB1,
MODELS_HUMIDIFIER_MIOT,
)
from .device import XiaomiCoordinatedMiioEntity, XiaomiMiioEntity
@ -63,16 +65,17 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
}
)
ATTR_POWER = "power"
ATTR_ACTUAL_SPEED = "actual_speed"
ATTR_CHARGING = "charging"
ATTR_DISPLAY_CLOCK = "display_clock"
ATTR_HUMIDITY = "humidity"
ATTR_MOTOR_SPEED = "motor_speed"
ATTR_NIGHT_MODE = "night_mode"
ATTR_NIGHT_TIME_BEGIN = "night_time_begin"
ATTR_NIGHT_TIME_END = "night_time_end"
ATTR_POWER = "power"
ATTR_SENSOR_STATE = "sensor_state"
ATTR_WATER_LEVEL = "water_level"
ATTR_HUMIDITY = "humidity"
ATTR_ACTUAL_MOTOR_SPEED = "actual_speed"
@dataclass
@ -121,6 +124,13 @@ SENSOR_TYPES = {
valid_min_value=200.0,
valid_max_value=2000.0,
),
"motor_speed": SensorType(
unit="rpm",
icon="mdi:fast-forward",
state_class=STATE_CLASS_MEASUREMENT,
valid_min_value=200.0,
valid_max_value=2000.0,
),
}
HUMIDIFIER_SENSORS = {
@ -128,11 +138,17 @@ HUMIDIFIER_SENSORS = {
ATTR_TEMPERATURE: "temperature",
}
HUMIDIFIER_CA1_CB1_SENSORS = {
ATTR_HUMIDITY: "humidity",
ATTR_TEMPERATURE: "temperature",
ATTR_MOTOR_SPEED: "motor_speed",
}
HUMIDIFIER_SENSORS_MIOT = {
ATTR_HUMIDITY: "humidity",
ATTR_TEMPERATURE: "temperature",
ATTR_WATER_LEVEL: "water_level",
ATTR_ACTUAL_MOTOR_SPEED: "actual_speed",
ATTR_ACTUAL_SPEED: "actual_speed",
}
@ -191,11 +207,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
sensors = []
if model in MODELS_HUMIDIFIER_MIOT:
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
sensors = HUMIDIFIER_SENSORS_MIOT
elif model in (MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CB1):
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
sensors = HUMIDIFIER_CA1_CB1_SENSORS
elif model.startswith("zhimi.humidifier."):
device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
sensors = HUMIDIFIER_SENSORS
else:
unique_id = config_entry.unique_id

View File

@ -5,7 +5,7 @@ from typing import Final
MAJOR_VERSION: Final = 2021
MINOR_VERSION: Final = 8
PATCH_VERSION: Final = "3"
PATCH_VERSION: Final = "4"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)

View File

@ -51,6 +51,11 @@ httplib2>=0.19.0
# https://github.com/home-assistant/core/issues/40148
grpcio==1.31.0
# Newer versions of cloud pubsub pin a higher version of grpcio. This can
# be reverted when the grpcio pin is reverted, see:
# https://github.com/home-assistant/core/issues/53427
google-cloud-pubsub==2.1.0
# This is a old unmaintained library and is replaced with pycryptodome
pycrypto==1000000000.0.0

View File

@ -73,6 +73,11 @@ httplib2>=0.19.0
# https://github.com/home-assistant/core/issues/40148
grpcio==1.31.0
# Newer versions of cloud pubsub pin a higher version of grpcio. This can
# be reverted when the grpcio pin is reverted, see:
# https://github.com/home-assistant/core/issues/53427
google-cloud-pubsub==2.1.0
# This is a old unmaintained library and is replaced with pycryptodome
pycrypto==1000000000.0.0

View File

@ -40,6 +40,7 @@ from homeassistant.components.modbus.const import (
CONF_BYTESIZE,
CONF_DATA_TYPE,
CONF_INPUT_TYPE,
CONF_MSG_WAIT,
CONF_PARITY,
CONF_STOPBITS,
CONF_SWAP,
@ -245,6 +246,7 @@ async def test_exception_struct_validator(do_config):
CONF_PORT: "usb01",
CONF_PARITY: "E",
CONF_STOPBITS: 1,
CONF_MSG_WAIT: 100,
},
{
CONF_TYPE: "serial",

View File

@ -32,11 +32,13 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.network import NoURLAvailableError
from homeassistant.setup import async_setup_component
from . import (
TEST_CAMERA,
TEST_CAMERA_DEVICE_IDENTIFIER,
TEST_CAMERA_ENTITY_ID,
TEST_CAMERA_ID,
TEST_CAMERA_NAME,
TEST_CAMERAS,
@ -251,6 +253,35 @@ async def test_setup_camera_with_correct_webhook(
assert not client.async_set_camera.called
async def test_setup_camera_with_no_home_assistant_urls(
hass: HomeAssistant,
caplog: Any,
) -> None:
"""Verify setup works without Home Assistant internal/external URLs."""
client = create_mock_motioneye_client()
config_entry = create_mock_motioneye_config_entry(hass, data={CONF_URL: TEST_URL})
with patch(
"homeassistant.components.motioneye.get_url", side_effect=NoURLAvailableError
):
await setup_mock_motioneye_config_entry(
hass,
config_entry=config_entry,
client=client,
)
# Should log a warning ...
assert "Unable to get Home Assistant URL" in caplog.text
# ... should not set callbacks in the camera ...
assert not client.async_set_camera.called
# ... but camera should still be present.
entity_state = hass.states.get(TEST_CAMERA_ENTITY_ID)
assert entity_state
async def test_good_query(hass: HomeAssistant, aiohttp_client: Any) -> None:
"""Test good callbacks."""
await async_setup_component(hass, "http", {"http": {}})