mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 16:17:20 +00:00
commit
d51b2ad675
@ -37,7 +37,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
gateway.option_allow_clip_sensor
|
gateway.option_allow_clip_sensor
|
||||||
or not sensor.type.startswith("CLIP")
|
or not sensor.type.startswith("CLIP")
|
||||||
)
|
)
|
||||||
and sensor.deconz_id not in gateway.deconz_ids.values()
|
|
||||||
):
|
):
|
||||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
entities.append(DeconzBinarySensor(sensor, gateway))
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
gateway.option_allow_clip_sensor
|
gateway.option_allow_clip_sensor
|
||||||
or not sensor.type.startswith("CLIP")
|
or not sensor.type.startswith("CLIP")
|
||||||
)
|
)
|
||||||
and sensor.deconz_id not in gateway.deconz_ids.values()
|
|
||||||
):
|
):
|
||||||
entities.append(DeconzThermostat(sensor, gateway))
|
entities.append(DeconzThermostat(sensor, gateway))
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
if group.lights and group.deconz_id not in gateway.deconz_ids.values():
|
if group.lights:
|
||||||
entities.append(DeconzGroup(group, gateway))
|
entities.append(DeconzGroup(group, gateway))
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
@ -68,7 +68,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
gateway.option_allow_clip_sensor
|
gateway.option_allow_clip_sensor
|
||||||
or not sensor.type.startswith("CLIP")
|
or not sensor.type.startswith("CLIP")
|
||||||
)
|
)
|
||||||
and sensor.deconz_id not in gateway.deconz_ids.values()
|
|
||||||
):
|
):
|
||||||
entities.append(DeconzSensor(sensor, gateway))
|
entities.append(DeconzSensor(sensor, gateway))
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
# Protocol version specific obis
|
# Protocol version specific obis
|
||||||
if dsmr_version in ("4", "5"):
|
if dsmr_version in ("4", "5"):
|
||||||
gas_obis = obis_ref.HOURLY_GAS_METER_READING
|
gas_obis = obis_ref.HOURLY_GAS_METER_READING
|
||||||
elif dsmr_version in ("5B"):
|
elif dsmr_version in ("5B",):
|
||||||
gas_obis = obis_ref.BELGIUM_HOURLY_GAS_METER_READING
|
gas_obis = obis_ref.BELGIUM_HOURLY_GAS_METER_READING
|
||||||
else:
|
else:
|
||||||
gas_obis = obis_ref.GAS_METER_READING
|
gas_obis = obis_ref.GAS_METER_READING
|
||||||
@ -238,7 +238,7 @@ class DSMREntity(Entity):
|
|||||||
"""Convert 2/1 to normal/low depending on DSMR version."""
|
"""Convert 2/1 to normal/low depending on DSMR version."""
|
||||||
# DSMR V5B: Note: In Belgium values are swapped:
|
# DSMR V5B: Note: In Belgium values are swapped:
|
||||||
# Rate code 2 is used for low rate and rate code 1 is used for normal rate.
|
# Rate code 2 is used for low rate and rate code 1 is used for normal rate.
|
||||||
if dsmr_version in ("5B"):
|
if dsmr_version in ("5B",):
|
||||||
if value == "0001":
|
if value == "0001":
|
||||||
value = "0002"
|
value = "0002"
|
||||||
elif value == "0002":
|
elif value == "0002":
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Code to handle a Hue bridge."""
|
"""Code to handle a Hue bridge."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import logging
|
||||||
|
|
||||||
from aiohttp import client_exceptions
|
from aiohttp import client_exceptions
|
||||||
import aiohue
|
import aiohue
|
||||||
@ -24,7 +25,8 @@ SCENE_SCHEMA = vol.Schema(
|
|||||||
{vol.Required(ATTR_GROUP_NAME): cv.string, vol.Required(ATTR_SCENE_NAME): cv.string}
|
{vol.Required(ATTR_GROUP_NAME): cv.string, vol.Required(ATTR_SCENE_NAME): cv.string}
|
||||||
)
|
)
|
||||||
# How long should we sleep if the hub is busy
|
# How long should we sleep if the hub is busy
|
||||||
HUB_BUSY_SLEEP = 0.01
|
HUB_BUSY_SLEEP = 0.5
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HueBridge:
|
class HueBridge:
|
||||||
@ -123,9 +125,14 @@ class HueBridge:
|
|||||||
except (
|
except (
|
||||||
client_exceptions.ClientOSError,
|
client_exceptions.ClientOSError,
|
||||||
client_exceptions.ClientResponseError,
|
client_exceptions.ClientResponseError,
|
||||||
|
client_exceptions.ServerDisconnectedError,
|
||||||
) as err:
|
) as err:
|
||||||
if tries == 3 or (
|
if tries == 3:
|
||||||
# We only retry if it's a server error. So raise on all 4XX errors.
|
_LOGGER.error("Request failed %s times, giving up.", tries)
|
||||||
|
raise
|
||||||
|
|
||||||
|
# We only retry if it's a server error. So raise on all 4XX errors.
|
||||||
|
if (
|
||||||
isinstance(err, client_exceptions.ClientResponseError)
|
isinstance(err, client_exceptions.ClientResponseError)
|
||||||
and err.status < 500
|
and err.status < 500
|
||||||
):
|
):
|
||||||
|
@ -5,6 +5,7 @@ from functools import partial
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
from aiohttp import client_exceptions
|
||||||
import aiohue
|
import aiohue
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
@ -172,7 +173,11 @@ async def async_safe_fetch(bridge, fetch_method):
|
|||||||
except aiohue.Unauthorized:
|
except aiohue.Unauthorized:
|
||||||
await bridge.handle_unauthorized_error()
|
await bridge.handle_unauthorized_error()
|
||||||
raise UpdateFailed
|
raise UpdateFailed
|
||||||
except (asyncio.TimeoutError, aiohue.AiohueException):
|
except (
|
||||||
|
asyncio.TimeoutError,
|
||||||
|
aiohue.AiohueException,
|
||||||
|
client_exceptions.ClientError,
|
||||||
|
):
|
||||||
raise UpdateFailed
|
raise UpdateFailed
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import asyncio
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp import client_exceptions
|
||||||
from aiohue import AiohueException, Unauthorized
|
from aiohue import AiohueException, Unauthorized
|
||||||
from aiohue.sensors import TYPE_ZLL_PRESENCE
|
from aiohue.sensors import TYPE_ZLL_PRESENCE
|
||||||
import async_timeout
|
import async_timeout
|
||||||
@ -60,7 +61,7 @@ class SensorManager:
|
|||||||
except Unauthorized:
|
except Unauthorized:
|
||||||
await self.bridge.handle_unauthorized_error()
|
await self.bridge.handle_unauthorized_error()
|
||||||
raise UpdateFailed
|
raise UpdateFailed
|
||||||
except (asyncio.TimeoutError, AiohueException):
|
except (asyncio.TimeoutError, AiohueException, client_exceptions.ClientError):
|
||||||
raise UpdateFailed
|
raise UpdateFailed
|
||||||
|
|
||||||
async def async_register_component(self, binary, async_add_entities):
|
async def async_register_component(self, binary, async_add_entities):
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Instituto Português do Mar e Atmosfera (IPMA)",
|
"name": "Instituto Português do Mar e Atmosfera (IPMA)",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/ipma",
|
"documentation": "https://www.home-assistant.io/integrations/ipma",
|
||||||
"requirements": ["pyipma==2.0.3"],
|
"requirements": ["pyipma==2.0.4"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@dgomes", "@abmantis"]
|
"codeowners": ["@dgomes", "@abmantis"]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"device_automation": {
|
"device_automation": {
|
||||||
"action_type": {
|
"action_type": {
|
||||||
|
"brightness_decrease": "Decrease {entity_name} brightness",
|
||||||
|
"brightness_increase": "Increase {entity_name} brightness",
|
||||||
"toggle": "Toggle {entity_name}",
|
"toggle": "Toggle {entity_name}",
|
||||||
"turn_on": "Turn on {entity_name}",
|
"turn_on": "Turn on {entity_name}",
|
||||||
"turn_off": "Turn off {entity_name}"
|
"turn_off": "Turn off {entity_name}"
|
||||||
|
@ -154,6 +154,7 @@ CONFIG_SCHEMA = vol.Schema(
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_save_refresh_token(hass, config_entry, token):
|
def _async_save_refresh_token(hass, config_entry, token):
|
||||||
|
"""Save a refresh token to the config entry."""
|
||||||
hass.config_entries.async_update_entry(
|
hass.config_entries.async_update_entry(
|
||||||
config_entry, data={**config_entry.data, CONF_TOKEN: token}
|
config_entry, data={**config_entry.data, CONF_TOKEN: token}
|
||||||
)
|
)
|
||||||
@ -547,12 +548,7 @@ class SimpliSafe:
|
|||||||
_LOGGER.error("Unknown error while updating: %s", result)
|
_LOGGER.error("Unknown error while updating: %s", result)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._api.refresh_token_dirty:
|
if self._api.refresh_token != self._config_entry.data[CONF_TOKEN]:
|
||||||
# Reconnect the websocket:
|
|
||||||
await self._api.websocket.async_disconnect()
|
|
||||||
await self._api.websocket.async_connect()
|
|
||||||
|
|
||||||
# Save the new refresh token:
|
|
||||||
_async_save_refresh_token(
|
_async_save_refresh_token(
|
||||||
self._hass, self._config_entry, self._api.refresh_token
|
self._hass, self._config_entry, self._api.refresh_token
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "SimpliSafe",
|
"name": "SimpliSafe",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
|
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
|
||||||
"requirements": ["simplisafe-python==8.1.1"],
|
"requirements": ["simplisafe-python==9.0.2"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@bachya"]
|
"codeowners": ["@bachya"]
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,11 @@ class UniFiClientTracker(UniFiClient, ScannerEntity):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
self.wired_bug = None
|
self.wired_bug = None
|
||||||
|
|
||||||
|
# A client that has never been seen cannot be connected.
|
||||||
|
if self.client.last_seen is None:
|
||||||
|
return False
|
||||||
|
|
||||||
since_last_seen = dt_util.utcnow() - dt_util.utc_from_timestamp(
|
since_last_seen = dt_util.utcnow() - dt_util.utc_from_timestamp(
|
||||||
float(self.client.last_seen)
|
float(self.client.last_seen)
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 106
|
MINOR_VERSION = 106
|
||||||
PATCH_VERSION = "0"
|
PATCH_VERSION = "1"
|
||||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER = (3, 7, 0)
|
REQUIRED_PYTHON_VER = (3, 7, 0)
|
||||||
|
@ -1305,7 +1305,7 @@ pyicloud==0.9.2
|
|||||||
pyintesishome==1.6
|
pyintesishome==1.6
|
||||||
|
|
||||||
# homeassistant.components.ipma
|
# homeassistant.components.ipma
|
||||||
pyipma==2.0.3
|
pyipma==2.0.4
|
||||||
|
|
||||||
# homeassistant.components.iqvia
|
# homeassistant.components.iqvia
|
||||||
pyiqvia==0.2.1
|
pyiqvia==0.2.1
|
||||||
@ -1827,7 +1827,7 @@ simplehound==0.3
|
|||||||
simplepush==1.1.4
|
simplepush==1.1.4
|
||||||
|
|
||||||
# homeassistant.components.simplisafe
|
# homeassistant.components.simplisafe
|
||||||
simplisafe-python==8.1.1
|
simplisafe-python==9.0.2
|
||||||
|
|
||||||
# homeassistant.components.sisyphus
|
# homeassistant.components.sisyphus
|
||||||
sisyphus-control==2.2.1
|
sisyphus-control==2.2.1
|
||||||
|
@ -477,7 +477,7 @@ pyhomematic==0.1.64
|
|||||||
pyicloud==0.9.2
|
pyicloud==0.9.2
|
||||||
|
|
||||||
# homeassistant.components.ipma
|
# homeassistant.components.ipma
|
||||||
pyipma==2.0.3
|
pyipma==2.0.4
|
||||||
|
|
||||||
# homeassistant.components.iqvia
|
# homeassistant.components.iqvia
|
||||||
pyiqvia==0.2.1
|
pyiqvia==0.2.1
|
||||||
@ -626,7 +626,7 @@ sentry-sdk==0.13.5
|
|||||||
simplehound==0.3
|
simplehound==0.3
|
||||||
|
|
||||||
# homeassistant.components.simplisafe
|
# homeassistant.components.simplisafe
|
||||||
simplisafe-python==8.1.1
|
simplisafe-python==9.0.2
|
||||||
|
|
||||||
# homeassistant.components.sleepiq
|
# homeassistant.components.sleepiq
|
||||||
sleepyq==0.7
|
sleepyq==0.7
|
||||||
|
@ -187,6 +187,50 @@ async def test_v4_meter(hass, mock_connection_factory):
|
|||||||
assert gas_consumption.attributes.get("unit_of_measurement") == "m3"
|
assert gas_consumption.attributes.get("unit_of_measurement") == "m3"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_v5_meter(hass, mock_connection_factory):
|
||||||
|
"""Test if v5 meter is correctly parsed."""
|
||||||
|
(connection_factory, transport, protocol) = mock_connection_factory
|
||||||
|
|
||||||
|
from dsmr_parser.obis_references import (
|
||||||
|
HOURLY_GAS_METER_READING,
|
||||||
|
ELECTRICITY_ACTIVE_TARIFF,
|
||||||
|
)
|
||||||
|
from dsmr_parser.objects import CosemObject, MBusObject
|
||||||
|
|
||||||
|
config = {"platform": "dsmr", "dsmr_version": "5"}
|
||||||
|
|
||||||
|
telegram = {
|
||||||
|
HOURLY_GAS_METER_READING: MBusObject(
|
||||||
|
[
|
||||||
|
{"value": datetime.datetime.fromtimestamp(1551642213)},
|
||||||
|
{"value": Decimal(745.695), "unit": "m³"},
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ELECTRICITY_ACTIVE_TARIFF: CosemObject([{"value": "0001", "unit": ""}]),
|
||||||
|
}
|
||||||
|
|
||||||
|
with assert_setup_component(1):
|
||||||
|
await async_setup_component(hass, "sensor", {"sensor": config})
|
||||||
|
|
||||||
|
telegram_callback = connection_factory.call_args_list[0][0][2]
|
||||||
|
|
||||||
|
# simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
|
||||||
|
telegram_callback(telegram)
|
||||||
|
|
||||||
|
# after receiving telegram entities need to have the chance to update
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
# tariff should be translated in human readable and have no unit
|
||||||
|
power_tariff = hass.states.get("sensor.power_tariff")
|
||||||
|
assert power_tariff.state == "low"
|
||||||
|
assert power_tariff.attributes.get("unit_of_measurement") == ""
|
||||||
|
|
||||||
|
# check if gas consumption is parsed correctly
|
||||||
|
gas_consumption = hass.states.get("sensor.gas_consumption")
|
||||||
|
assert gas_consumption.state == "745.695"
|
||||||
|
assert gas_consumption.attributes.get("unit_of_measurement") == "m³"
|
||||||
|
|
||||||
|
|
||||||
async def test_belgian_meter(hass, mock_connection_factory):
|
async def test_belgian_meter(hass, mock_connection_factory):
|
||||||
"""Test if Belgian meter is correctly parsed."""
|
"""Test if Belgian meter is correctly parsed."""
|
||||||
(connection_factory, transport, protocol) = mock_connection_factory
|
(connection_factory, transport, protocol) = mock_connection_factory
|
||||||
|
@ -54,6 +54,14 @@ CLIENT_4 = {
|
|||||||
"last_seen": 1562600145,
|
"last_seen": 1562600145,
|
||||||
"mac": "00:00:00:00:00:04",
|
"mac": "00:00:00:00:00:04",
|
||||||
}
|
}
|
||||||
|
CLIENT_5 = {
|
||||||
|
"essid": "ssid",
|
||||||
|
"hostname": "client_5",
|
||||||
|
"ip": "10.0.0.5",
|
||||||
|
"is_wired": True,
|
||||||
|
"last_seen": None,
|
||||||
|
"mac": "00:00:00:00:00:05",
|
||||||
|
}
|
||||||
|
|
||||||
DEVICE_1 = {
|
DEVICE_1 = {
|
||||||
"board_rev": 3,
|
"board_rev": 3,
|
||||||
@ -111,11 +119,11 @@ async def test_tracked_devices(hass):
|
|||||||
controller = await setup_unifi_integration(
|
controller = await setup_unifi_integration(
|
||||||
hass,
|
hass,
|
||||||
options={CONF_SSID_FILTER: ["ssid"]},
|
options={CONF_SSID_FILTER: ["ssid"]},
|
||||||
clients_response=[CLIENT_1, CLIENT_2, CLIENT_3, client_4_copy],
|
clients_response=[CLIENT_1, CLIENT_2, CLIENT_3, CLIENT_5, client_4_copy],
|
||||||
devices_response=[DEVICE_1, DEVICE_2],
|
devices_response=[DEVICE_1, DEVICE_2],
|
||||||
known_wireless_clients=(CLIENT_4["mac"],),
|
known_wireless_clients=(CLIENT_4["mac"],),
|
||||||
)
|
)
|
||||||
assert len(hass.states.async_all()) == 6
|
assert len(hass.states.async_all()) == 7
|
||||||
|
|
||||||
client_1 = hass.states.get("device_tracker.client_1")
|
client_1 = hass.states.get("device_tracker.client_1")
|
||||||
assert client_1 is not None
|
assert client_1 is not None
|
||||||
@ -134,6 +142,11 @@ async def test_tracked_devices(hass):
|
|||||||
assert client_4 is not None
|
assert client_4 is not None
|
||||||
assert client_4.state == "not_home"
|
assert client_4.state == "not_home"
|
||||||
|
|
||||||
|
# A client that has never been seen should be marked away.
|
||||||
|
client_5 = hass.states.get("device_tracker.client_5")
|
||||||
|
assert client_5 is not None
|
||||||
|
assert client_5.state == "not_home"
|
||||||
|
|
||||||
device_1 = hass.states.get("device_tracker.device_1")
|
device_1 = hass.states.get("device_tracker.device_1")
|
||||||
assert device_1 is not None
|
assert device_1 is not None
|
||||||
assert device_1.state == "not_home"
|
assert device_1.state == "not_home"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user