Merge pull request #55870 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2021-09-06 16:05:08 -07:00 committed by GitHub
commit f0649855f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 203 additions and 87 deletions

View File

@ -67,13 +67,13 @@ class IncomfortWaterHeater(IncomfortEntity, WaterHeaterEntity):
@property @property
def min_temp(self) -> float: def min_temp(self) -> float:
"""Return max valid temperature that can be set.""" """Return min valid temperature that can be set."""
return 80.0 return 30.0
@property @property
def max_temp(self) -> float: def max_temp(self) -> float:
"""Return max valid temperature that can be set.""" """Return max valid temperature that can be set."""
return 30.0 return 80.0
@property @property
def temperature_unit(self) -> str: def temperature_unit(self) -> str:

View File

@ -110,16 +110,10 @@ class IntegrationSensor(RestoreEntity, SensorEntity):
self._method = integration_method self._method = integration_method
self._name = name if name is not None else f"{source_entity} integral" self._name = name if name is not None else f"{source_entity} integral"
self._unit_template = (
if unit_of_measurement is None: f"{'' if unit_prefix is None else unit_prefix}{{}}{unit_time}"
self._unit_template = ( )
f"{'' if unit_prefix is None else unit_prefix}{{}}{unit_time}" self._unit_of_measurement = unit_of_measurement
)
# we postpone the definition of unit_of_measurement to later
self._unit_of_measurement = None
else:
self._unit_of_measurement = unit_of_measurement
self._unit_prefix = UNIT_PREFIXES[unit_prefix] self._unit_prefix = UNIT_PREFIXES[unit_prefix]
self._unit_time = UNIT_TIME[unit_time] self._unit_time = UNIT_TIME[unit_time]
self._attr_state_class = STATE_CLASS_TOTAL_INCREASING self._attr_state_class = STATE_CLASS_TOTAL_INCREASING
@ -135,10 +129,10 @@ class IntegrationSensor(RestoreEntity, SensorEntity):
_LOGGER.warning("Could not restore last state: %s", err) _LOGGER.warning("Could not restore last state: %s", err)
else: else:
self._attr_device_class = state.attributes.get(ATTR_DEVICE_CLASS) self._attr_device_class = state.attributes.get(ATTR_DEVICE_CLASS)
if self._unit_of_measurement is None:
self._unit_of_measurement = state.attributes.get( self._unit_of_measurement = state.attributes.get(
ATTR_UNIT_OF_MEASUREMENT ATTR_UNIT_OF_MEASUREMENT
) )
@callback @callback
def calc_integration(event): def calc_integration(event):

View File

@ -48,7 +48,7 @@ from homeassistant.helpers.integration_platform import (
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
ENTITY_ID_JSON_TEMPLATE = '"entity_id": ?"{}"' ENTITY_ID_JSON_TEMPLATE = '"entity_id":"{}"'
ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"')
DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"')
ICON_JSON_EXTRACT = re.compile('"icon": ?"([^"]+)"') ICON_JSON_EXTRACT = re.compile('"icon": ?"([^"]+)"')

View File

@ -3,7 +3,7 @@
"name": "Mazda Connected Services", "name": "Mazda Connected Services",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/mazda", "documentation": "https://www.home-assistant.io/integrations/mazda",
"requirements": ["pymazda==0.2.0"], "requirements": ["pymazda==0.2.1"],
"codeowners": ["@bdr99"], "codeowners": ["@bdr99"],
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "cloud_polling" "iot_class": "cloud_polling"

View File

@ -25,9 +25,11 @@ from homeassistant.const import (
from .const import ( from .const import (
CONF_DATA_TYPE, CONF_DATA_TYPE,
CONF_INPUT_TYPE,
CONF_SWAP, CONF_SWAP,
CONF_SWAP_BYTE, CONF_SWAP_BYTE,
CONF_SWAP_NONE, CONF_SWAP_NONE,
CONF_WRITE_TYPE,
DATA_TYPE_CUSTOM, DATA_TYPE_CUSTOM,
DATA_TYPE_FLOAT, DATA_TYPE_FLOAT,
DATA_TYPE_FLOAT16, DATA_TYPE_FLOAT16,
@ -212,6 +214,10 @@ def duplicate_entity_validator(config: dict) -> dict:
for index, entry in enumerate(hub[conf_key]): for index, entry in enumerate(hub[conf_key]):
name = entry[CONF_NAME] name = entry[CONF_NAME]
addr = str(entry[CONF_ADDRESS]) addr = str(entry[CONF_ADDRESS])
if CONF_INPUT_TYPE in entry:
addr += "_" + str(entry[CONF_INPUT_TYPE])
elif CONF_WRITE_TYPE in entry:
addr += "_" + str(entry[CONF_WRITE_TYPE])
if CONF_COMMAND_ON in entry: if CONF_COMMAND_ON in entry:
addr += "_" + str(entry[CONF_COMMAND_ON]) addr += "_" + str(entry[CONF_COMMAND_ON])
if CONF_COMMAND_OFF in entry: if CONF_COMMAND_OFF in entry:
@ -242,7 +248,10 @@ def duplicate_modbus_validator(config: list) -> list:
errors = [] errors = []
for index, hub in enumerate(config): for index, hub in enumerate(config):
name = hub.get(CONF_NAME, DEFAULT_HUB) name = hub.get(CONF_NAME, DEFAULT_HUB)
host = hub[CONF_PORT] if hub[CONF_TYPE] == SERIAL else hub[CONF_HOST] if hub[CONF_TYPE] == SERIAL:
host = hub[CONF_PORT]
else:
host = f"{hub[CONF_HOST]}_{hub[CONF_PORT]}"
if host in hosts: if host in hosts:
err = f"Modbus {name}  contains duplicate host/port {host}, not loaded!" err = f"Modbus {name}  contains duplicate host/port {host}, not loaded!"
_LOGGER.warning(err) _LOGGER.warning(err)

View File

@ -75,7 +75,7 @@ class RfxtrxSensorEntityDescription(SensorEntityDescription):
SENSOR_TYPES = ( SENSOR_TYPES = (
RfxtrxSensorEntityDescription( RfxtrxSensorEntityDescription(
key="Barameter", key="Barometer",
device_class=DEVICE_CLASS_PRESSURE, device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
native_unit_of_measurement=PRESSURE_HPA, native_unit_of_measurement=PRESSURE_HPA,

View File

@ -144,7 +144,7 @@ class SurePetcareAPI:
"""Get the latest data from Sure Petcare.""" """Get the latest data from Sure Petcare."""
try: try:
self.states = await self.surepy.get_entities() self.states = await self.surepy.get_entities(refresh=True)
except SurePetcareError as error: except SurePetcareError as error:
_LOGGER.error("Unable to fetch data: %s", error) _LOGGER.error("Unable to fetch data: %s", error)
return return

View File

@ -80,7 +80,7 @@ class ThinkingCleanerSwitch(SwitchEntity):
self.last_lock_time = None self.last_lock_time = None
self.graceful_state = False self.graceful_state = False
self._attr_name = f"{tc_object} {description.name}" self._attr_name = f"{tc_object.name} {description.name}"
def lock_update(self): def lock_update(self):
"""Lock the update since TC clean takes some time to update.""" """Lock the update since TC clean takes some time to update."""

View File

@ -116,6 +116,13 @@ MODEL_AIRQUALITYMONITOR_B1 = "cgllc.airmonitor.b1"
MODEL_AIRQUALITYMONITOR_S1 = "cgllc.airmonitor.s1" MODEL_AIRQUALITYMONITOR_S1 = "cgllc.airmonitor.s1"
MODEL_AIRQUALITYMONITOR_CGDN1 = "cgllc.airm.cgdn1" MODEL_AIRQUALITYMONITOR_CGDN1 = "cgllc.airm.cgdn1"
MODELS_AIR_QUALITY_MONITOR = [
MODEL_AIRQUALITYMONITOR_V1,
MODEL_AIRQUALITYMONITOR_B1,
MODEL_AIRQUALITYMONITOR_S1,
MODEL_AIRQUALITYMONITOR_CGDN1,
]
# Light Models # Light Models
MODELS_LIGHT_EYECARE = ["philips.light.sread1"] MODELS_LIGHT_EYECARE = ["philips.light.sread1"]
MODELS_LIGHT_CEILING = ["philips.light.ceiling", "philips.light.zyceiling"] MODELS_LIGHT_CEILING = ["philips.light.ceiling", "philips.light.zyceiling"]

View File

@ -405,36 +405,42 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
self._preset_modes = PRESET_MODES_AIRPURIFIER_PRO self._preset_modes = PRESET_MODES_AIRPURIFIER_PRO
self._supported_features = SUPPORT_PRESET_MODE self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1 self._speed_count = 1
self._operation_mode_class = AirpurifierOperationMode
elif self._model == MODEL_AIRPURIFIER_PRO_V7: elif self._model == MODEL_AIRPURIFIER_PRO_V7:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO_V7 self._device_features = FEATURE_FLAGS_AIRPURIFIER_PRO_V7
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO_V7 self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_PRO_V7
self._preset_modes = PRESET_MODES_AIRPURIFIER_PRO_V7 self._preset_modes = PRESET_MODES_AIRPURIFIER_PRO_V7
self._supported_features = SUPPORT_PRESET_MODE self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1 self._speed_count = 1
self._operation_mode_class = AirpurifierOperationMode
elif self._model in [MODEL_AIRPURIFIER_2S, MODEL_AIRPURIFIER_2H]: elif self._model in [MODEL_AIRPURIFIER_2S, MODEL_AIRPURIFIER_2H]:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_2S self._device_features = FEATURE_FLAGS_AIRPURIFIER_2S
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_COMMON
self._preset_modes = PRESET_MODES_AIRPURIFIER_2S self._preset_modes = PRESET_MODES_AIRPURIFIER_2S
self._supported_features = SUPPORT_PRESET_MODE self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1 self._speed_count = 1
self._operation_mode_class = AirpurifierOperationMode
elif self._model in MODELS_PURIFIER_MIOT: elif self._model in MODELS_PURIFIER_MIOT:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_MIOT self._device_features = FEATURE_FLAGS_AIRPURIFIER_MIOT
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_MIOT self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_MIOT
self._preset_modes = PRESET_MODES_AIRPURIFIER_MIOT self._preset_modes = PRESET_MODES_AIRPURIFIER_MIOT
self._supported_features = SUPPORT_SET_SPEED | SUPPORT_PRESET_MODE self._supported_features = SUPPORT_SET_SPEED | SUPPORT_PRESET_MODE
self._speed_count = 3 self._speed_count = 3
self._operation_mode_class = AirpurifierMiotOperationMode
elif self._model == MODEL_AIRPURIFIER_V3: elif self._model == MODEL_AIRPURIFIER_V3:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_V3 self._device_features = FEATURE_FLAGS_AIRPURIFIER_V3
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3 self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_V3
self._preset_modes = PRESET_MODES_AIRPURIFIER_V3 self._preset_modes = PRESET_MODES_AIRPURIFIER_V3
self._supported_features = SUPPORT_PRESET_MODE self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1 self._speed_count = 1
self._operation_mode_class = AirpurifierOperationMode
else: else:
self._device_features = FEATURE_FLAGS_AIRPURIFIER_MIIO self._device_features = FEATURE_FLAGS_AIRPURIFIER_MIIO
self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER
self._preset_modes = PRESET_MODES_AIRPURIFIER self._preset_modes = PRESET_MODES_AIRPURIFIER
self._supported_features = SUPPORT_PRESET_MODE self._supported_features = SUPPORT_PRESET_MODE
self._speed_count = 1 self._speed_count = 1
self._operation_mode_class = AirpurifierOperationMode
self._state_attrs.update( self._state_attrs.update(
{attribute: None for attribute in self._available_attributes} {attribute: None for attribute in self._available_attributes}
@ -446,7 +452,7 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
def preset_mode(self): def preset_mode(self):
"""Get the active preset mode.""" """Get the active preset mode."""
if self._state: if self._state:
preset_mode = AirpurifierOperationMode(self._state_attrs[ATTR_MODE]).name preset_mode = self._operation_mode_class(self._mode).name
return preset_mode if preset_mode in self._preset_modes else None return preset_mode if preset_mode in self._preset_modes else None
return None return None
@ -455,7 +461,7 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
def percentage(self): def percentage(self):
"""Return the current percentage based speed.""" """Return the current percentage based speed."""
if self._state: if self._state:
mode = AirpurifierOperationMode(self._state_attrs[ATTR_MODE]) mode = self._operation_mode_class(self._state_attrs[ATTR_MODE])
if mode in self.REVERSE_SPEED_MODE_MAPPING: if mode in self.REVERSE_SPEED_MODE_MAPPING:
return ranged_value_to_percentage( return ranged_value_to_percentage(
(1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode] (1, self._speed_count), self.REVERSE_SPEED_MODE_MAPPING[mode]
@ -479,7 +485,7 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
await self._try_command( await self._try_command(
"Setting operation mode of the miio device failed.", "Setting operation mode of the miio device failed.",
self._device.set_mode, self._device.set_mode,
AirpurifierOperationMode(self.SPEED_MODE_MAPPING[speed_mode]), self._operation_mode_class(self.SPEED_MODE_MAPPING[speed_mode]),
) )
async def async_set_preset_mode(self, preset_mode: str) -> None: async def async_set_preset_mode(self, preset_mode: str) -> None:
@ -490,11 +496,13 @@ class XiaomiAirPurifier(XiaomiGenericDevice):
if preset_mode not in self.preset_modes: if preset_mode not in self.preset_modes:
_LOGGER.warning("'%s'is not a valid preset mode", preset_mode) _LOGGER.warning("'%s'is not a valid preset mode", preset_mode)
return return
await self._try_command( if await self._try_command(
"Setting operation mode of the miio device failed.", "Setting operation mode of the miio device failed.",
self._device.set_mode, self._device.set_mode,
self.PRESET_MODE_MAPPING[preset_mode], self.PRESET_MODE_MAPPING[preset_mode],
) ):
self._mode = self._operation_mode_class[preset_mode].value
self.async_write_ha_state()
async def async_set_extra_features(self, features: int = 1): async def async_set_extra_features(self, features: int = 1):
"""Set the extra features.""" """Set the extra features."""
@ -538,15 +546,6 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
return None return None
@property
def preset_mode(self):
"""Get the active preset mode."""
if self._state:
preset_mode = AirpurifierMiotOperationMode(self._mode).name
return preset_mode if preset_mode in self._preset_modes else None
return None
async def async_set_percentage(self, percentage: int) -> None: async def async_set_percentage(self, percentage: int) -> None:
"""Set the percentage of the fan. """Set the percentage of the fan.

View File

@ -210,7 +210,7 @@ class XiaomiAirHumidifier(XiaomiGenericHumidifier, HumidifierEntity):
self._available_modes = AVAILABLE_MODES_MJJSQ self._available_modes = AVAILABLE_MODES_MJJSQ
self._min_humidity = 30 self._min_humidity = 30
self._max_humidity = 80 self._max_humidity = 80
self._humidity_steps = 10 self._humidity_steps = 100
else: else:
self._available_modes = AVAILABLE_MODES_OTHER self._available_modes = AVAILABLE_MODES_OTHER
self._min_humidity = 30 self._min_humidity = 30

View File

@ -66,6 +66,7 @@ from .const import (
MODEL_FAN_ZA1, MODEL_FAN_ZA1,
MODEL_FAN_ZA3, MODEL_FAN_ZA3,
MODEL_FAN_ZA4, MODEL_FAN_ZA4,
MODELS_AIR_QUALITY_MONITOR,
MODELS_HUMIDIFIER_MIIO, MODELS_HUMIDIFIER_MIIO,
MODELS_HUMIDIFIER_MIOT, MODELS_HUMIDIFIER_MIOT,
MODELS_HUMIDIFIER_MJJSQ, MODELS_HUMIDIFIER_MJJSQ,
@ -371,23 +372,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
host = config_entry.data[CONF_HOST] host = config_entry.data[CONF_HOST]
token = config_entry.data[CONF_TOKEN] token = config_entry.data[CONF_TOKEN]
model = config_entry.data[CONF_MODEL] model = config_entry.data[CONF_MODEL]
device = hass.data[DOMAIN][config_entry.entry_id].get(KEY_DEVICE)
sensors = []
if model in (MODEL_FAN_ZA1, MODEL_FAN_ZA3, MODEL_FAN_ZA4, MODEL_FAN_P5): if model in (MODEL_FAN_ZA1, MODEL_FAN_ZA3, MODEL_FAN_ZA4, MODEL_FAN_P5):
return return
if model in MODEL_TO_SENSORS_MAP:
sensors = MODEL_TO_SENSORS_MAP[model] if model in MODELS_AIR_QUALITY_MONITOR:
elif model in MODELS_HUMIDIFIER_MIOT:
sensors = HUMIDIFIER_MIOT_SENSORS
elif model in MODELS_HUMIDIFIER_MJJSQ:
sensors = HUMIDIFIER_MJJSQ_SENSORS
elif model in MODELS_HUMIDIFIER_MIIO:
sensors = HUMIDIFIER_MIIO_SENSORS
elif model in MODELS_PURIFIER_MIIO:
sensors = PURIFIER_MIIO_SENSORS
elif model in MODELS_PURIFIER_MIOT:
sensors = PURIFIER_MIOT_SENSORS
else:
unique_id = config_entry.unique_id unique_id = config_entry.unique_id
name = config_entry.title name = config_entry.title
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5]) _LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
@ -399,19 +388,35 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
name, device, config_entry, unique_id, description name, device, config_entry, unique_id, description
) )
) )
for sensor, description in SENSOR_TYPES.items(): else:
if sensor not in sensors: device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE]
continue sensors = []
entities.append( if model in MODEL_TO_SENSORS_MAP:
XiaomiGenericSensor( sensors = MODEL_TO_SENSORS_MAP[model]
f"{config_entry.title} {description.name}", elif model in MODELS_HUMIDIFIER_MIOT:
device, sensors = HUMIDIFIER_MIOT_SENSORS
config_entry, elif model in MODELS_HUMIDIFIER_MJJSQ:
f"{sensor}_{config_entry.unique_id}", sensors = HUMIDIFIER_MJJSQ_SENSORS
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR], elif model in MODELS_HUMIDIFIER_MIIO:
description, sensors = HUMIDIFIER_MIIO_SENSORS
elif model in MODELS_PURIFIER_MIIO:
sensors = PURIFIER_MIIO_SENSORS
elif model in MODELS_PURIFIER_MIOT:
sensors = PURIFIER_MIOT_SENSORS
for sensor, description in SENSOR_TYPES.items():
if sensor not in sensors:
continue
entities.append(
XiaomiGenericSensor(
f"{config_entry.title} {description.name}",
device,
config_entry,
f"{sensor}_{config_entry.unique_id}",
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
description,
)
) )
)
async_add_entities(entities) async_add_entities(entities)

View File

@ -108,7 +108,7 @@ class ZhaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured( self._abort_if_unique_id_configured(
updates={ updates={
CONF_DEVICE: { CONF_DEVICE: {
**current_entry.data[CONF_DEVICE], **current_entry.data.get(CONF_DEVICE, {}),
CONF_DEVICE_PATH: dev_path, CONF_DEVICE_PATH: dev_path,
}, },
} }
@ -172,7 +172,7 @@ class ZhaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured( self._abort_if_unique_id_configured(
updates={ updates={
CONF_DEVICE: { CONF_DEVICE: {
**current_entry.data[CONF_DEVICE], **current_entry.data.get(CONF_DEVICE, {}),
CONF_DEVICE_PATH: device_path, CONF_DEVICE_PATH: device_path,
}, },
} }

View File

@ -5,7 +5,7 @@ import logging
from typing import Any from typing import Any
from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.client import Client as ZwaveClient
from zwave_js_server.const.command_class.barrior_operator import BarrierState from zwave_js_server.const.command_class.barrier_operator import BarrierState
from zwave_js_server.model.value import Value as ZwaveValue from zwave_js_server.model.value import Value as ZwaveValue
from homeassistant.components.cover import ( from homeassistant.components.cover import (

View File

@ -32,8 +32,8 @@ from zwave_js_server.const.command_class.multilevel_sensor import (
) )
from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.node import Node as ZwaveNode
from zwave_js_server.model.value import Value as ZwaveValue, get_value_id from zwave_js_server.model.value import Value as ZwaveValue, get_value_id
from zwave_js_server.util.command_class import ( from zwave_js_server.util.command_class.meter import get_meter_scale_type
get_meter_scale_type, from zwave_js_server.util.command_class.multilevel_sensor import (
get_multilevel_sensor_type, get_multilevel_sensor_type,
) )

View File

@ -3,7 +3,7 @@
"name": "Z-Wave JS", "name": "Z-Wave JS",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/zwave_js", "documentation": "https://www.home-assistant.io/integrations/zwave_js",
"requirements": ["zwave-js-server-python==0.29.1"], "requirements": ["zwave-js-server-python==0.30.0"],
"codeowners": ["@home-assistant/z-wave"], "codeowners": ["@home-assistant/z-wave"],
"dependencies": ["usb", "http", "websocket_api"], "dependencies": ["usb", "http", "websocket_api"],
"iot_class": "local_push", "iot_class": "local_push",

View File

@ -15,7 +15,7 @@ from zwave_js_server.const.command_class.meter import (
) )
from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.node import Node as ZwaveNode
from zwave_js_server.model.value import ConfigurationValue from zwave_js_server.model.value import ConfigurationValue
from zwave_js_server.util.command_class import get_meter_type from zwave_js_server.util.command_class.meter import get_meter_type
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,

View File

@ -5,7 +5,7 @@ import logging
from typing import Any from typing import Any
from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.client import Client as ZwaveClient
from zwave_js_server.const.command_class.barrior_operator import ( from zwave_js_server.const.command_class.barrier_operator import (
BarrierEventSignalingSubsystemState, BarrierEventSignalingSubsystemState,
) )

View File

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

View File

@ -1599,7 +1599,7 @@ pymailgunner==1.4
pymata-express==1.19 pymata-express==1.19
# homeassistant.components.mazda # homeassistant.components.mazda
pymazda==0.2.0 pymazda==0.2.1
# homeassistant.components.mediaroom # homeassistant.components.mediaroom
pymediaroom==0.6.4.1 pymediaroom==0.6.4.1
@ -2489,4 +2489,4 @@ zigpy==0.37.1
zm-py==0.5.2 zm-py==0.5.2
# homeassistant.components.zwave_js # homeassistant.components.zwave_js
zwave-js-server-python==0.29.1 zwave-js-server-python==0.30.0

View File

@ -921,7 +921,7 @@ pymailgunner==1.4
pymata-express==1.19 pymata-express==1.19
# homeassistant.components.mazda # homeassistant.components.mazda
pymazda==0.2.0 pymazda==0.2.1
# homeassistant.components.melcloud # homeassistant.components.melcloud
pymelcloud==2.5.3 pymelcloud==2.5.3
@ -1400,4 +1400,4 @@ zigpy-znp==0.5.4
zigpy==0.37.1 zigpy==0.37.1
# homeassistant.components.zwave_js # homeassistant.components.zwave_js
zwave-js-server-python==0.29.1 zwave-js-server-python==0.30.0

View File

@ -1289,6 +1289,45 @@ async def test_logbook_entity_matches_only(hass, hass_client):
assert json_dict[1]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" assert json_dict[1]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474"
async def test_custom_log_entry_discoverable_via_entity_matches_only(hass, hass_client):
"""Test if a custom log entry is later discoverable via entity_matches_only."""
await hass.async_add_executor_job(init_recorder_component, hass)
await async_setup_component(hass, "logbook", {})
await hass.async_add_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
logbook.async_log_entry(
hass,
"Alarm",
"is triggered",
"switch",
"switch.test_switch",
)
await hass.async_block_till_done()
await hass.async_add_executor_job(trigger_db_commit, hass)
await hass.async_block_till_done()
await hass.async_add_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
client = await hass_client()
# Today time 00:00:00
start = dt_util.utcnow().date()
start_date = datetime(start.year, start.month, start.day)
# Test today entries with filter by end_time
end_time = start + timedelta(hours=24)
response = await client.get(
f"/api/logbook/{start_date.isoformat()}?end_time={end_time.isoformat()}&entity=switch.test_switch&entity_matches_only"
)
assert response.status == 200
json_dict = await response.json()
assert len(json_dict) == 1
assert json_dict[0]["name"] == "Alarm"
assert json_dict[0]["message"] == "is triggered"
assert json_dict[0]["entity_id"] == "switch.test_switch"
async def test_logbook_entity_matches_only_multiple(hass, hass_client): async def test_logbook_entity_matches_only_multiple(hass, hass_client):
"""Test the logbook view with a multiple entities and entity_matches_only.""" """Test the logbook view with a multiple entities and entity_matches_only."""
await hass.async_add_executor_job(init_recorder_component, hass) await hass.async_add_executor_job(init_recorder_component, hass)

View File

@ -7,12 +7,16 @@ from surepy import MESTART_RESOURCE
from . import MOCK_API_DATA from . import MOCK_API_DATA
async def _mock_call(method, resource):
if method == "GET" and resource == MESTART_RESOURCE:
return {"data": MOCK_API_DATA}
@pytest.fixture @pytest.fixture
async def surepetcare(): async def surepetcare():
"""Mock the SurePetcare for easier testing.""" """Mock the SurePetcare for easier testing."""
with patch("surepy.SureAPIClient", autospec=True) as mock_client_class, patch( with patch("surepy.SureAPIClient", autospec=True) as mock_client_class:
"surepy.find_token"
):
client = mock_client_class.return_value client = mock_client_class.return_value
client.resources = {MESTART_RESOURCE: {"data": MOCK_API_DATA}} client.resources = {}
client.call = _mock_call
yield client yield client

View File

@ -12,7 +12,7 @@ EXPECTED_ENTITY_IDS = {
} }
async def test_binary_sensors(hass, surepetcare) -> None: async def test_sensors(hass, surepetcare) -> None:
"""Test the generation of unique ids.""" """Test the generation of unique ids."""
assert await async_setup_component(hass, DOMAIN, MOCK_CONFIG) assert await async_setup_component(hass, DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -113,6 +113,34 @@ async def test_discovery_via_zeroconf_ip_change(detect_mock, hass):
} }
@patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True))
@patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True)
async def test_discovery_via_zeroconf_ip_change_ignored(detect_mock, hass):
"""Test zeroconf flow that was ignored gets updated."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="tube_zb_gw_cc2652p2_poe",
source=config_entries.SOURCE_IGNORE,
)
entry.add_to_hass(hass)
service_info = {
"host": "192.168.1.22",
"port": 6053,
"hostname": "tube_zb_gw_cc2652p2_poe.local.",
"properties": {"address": "tube_zb_gw_cc2652p2_poe.local"},
}
result = await hass.config_entries.flow.async_init(
"zha", context={"source": SOURCE_ZEROCONF}, data=service_info
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
assert entry.data[CONF_DEVICE] == {
CONF_DEVICE_PATH: "socket://192.168.1.22:6638",
}
@patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True) @patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True)
async def test_discovery_via_usb(detect_mock, hass): async def test_discovery_via_usb(detect_mock, hass):
"""Test usb flow -- radio detected.""" """Test usb flow -- radio detected."""
@ -317,6 +345,37 @@ async def test_discovery_via_usb_deconz_ignored(detect_mock, hass):
assert result["step_id"] == "confirm" assert result["step_id"] == "confirm"
@patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True)
async def test_discovery_via_usb_zha_ignored_updates(detect_mock, hass):
"""Test usb flow that was ignored gets updated."""
entry = MockConfigEntry(
domain=DOMAIN,
source=config_entries.SOURCE_IGNORE,
data={},
unique_id="AAAA:AAAA_1234_test_zigbee radio",
)
entry.add_to_hass(hass)
await hass.async_block_till_done()
discovery_info = {
"device": "/dev/ttyZIGBEE",
"pid": "AAAA",
"vid": "AAAA",
"serial_number": "1234",
"description": "zigbee radio",
"manufacturer": "test",
}
result = await hass.config_entries.flow.async_init(
"zha", context={"source": SOURCE_USB}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
assert entry.data[CONF_DEVICE] == {
CONF_DEVICE_PATH: "/dev/ttyZIGBEE",
}
@patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) @patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True))
@patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True) @patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True)
async def test_discovery_already_setup(detect_mock, hass): async def test_discovery_already_setup(detect_mock, hass):

View File

@ -338,7 +338,7 @@ async def test_add_node_secure(
assert len(client.async_send_command.call_args_list) == 1 assert len(client.async_send_command.call_args_list) == 1
assert client.async_send_command.call_args[0][0] == { assert client.async_send_command.call_args[0][0] == {
"command": "controller.begin_inclusion", "command": "controller.begin_inclusion",
"options": {"inclusionStrategy": InclusionStrategy.SECURITY_S0}, "options": {"strategy": InclusionStrategy.SECURITY_S0},
} }
client.async_send_command.reset_mock() client.async_send_command.reset_mock()
@ -363,7 +363,7 @@ async def test_add_node(
assert len(client.async_send_command.call_args_list) == 1 assert len(client.async_send_command.call_args_list) == 1
assert client.async_send_command.call_args[0][0] == { assert client.async_send_command.call_args[0][0] == {
"command": "controller.begin_inclusion", "command": "controller.begin_inclusion",
"options": {"inclusionStrategy": InclusionStrategy.INSECURE}, "options": {"strategy": InclusionStrategy.INSECURE},
} }
event = Event( event = Event(
@ -671,7 +671,7 @@ async def test_replace_failed_node_secure(
assert client.async_send_command.call_args[0][0] == { assert client.async_send_command.call_args[0][0] == {
"command": "controller.replace_failed_node", "command": "controller.replace_failed_node",
"nodeId": nortek_thermostat.node_id, "nodeId": nortek_thermostat.node_id,
"options": {"inclusionStrategy": InclusionStrategy.SECURITY_S0}, "options": {"strategy": InclusionStrategy.SECURITY_S0},
} }
client.async_send_command.reset_mock() client.async_send_command.reset_mock()
@ -720,7 +720,7 @@ async def test_replace_failed_node(
assert client.async_send_command.call_args[0][0] == { assert client.async_send_command.call_args[0][0] == {
"command": "controller.replace_failed_node", "command": "controller.replace_failed_node",
"nodeId": nortek_thermostat.node_id, "nodeId": nortek_thermostat.node_id,
"options": {"inclusionStrategy": InclusionStrategy.INSECURE}, "options": {"strategy": InclusionStrategy.INSECURE},
} }
client.async_send_command.reset_mock() client.async_send_command.reset_mock()