mirror of
https://github.com/home-assistant/core.git
synced 2025-09-27 13:59:29 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c1da182e2 | ||
![]() |
39bdb562d3 | ||
![]() |
1d1f8df509 | ||
![]() |
ccb8b6b9c8 | ||
![]() |
c0dcd9c674 | ||
![]() |
6062854666 | ||
![]() |
4be9766498 | ||
![]() |
4080d6a822 | ||
![]() |
6d06844318 | ||
![]() |
a150d6dcf3 | ||
![]() |
a0390783bb | ||
![]() |
91b10e875f | ||
![]() |
f04969cf30 | ||
![]() |
cdde5a37cd |
@@ -3,7 +3,7 @@
|
||||
"name": "Coronavirus (COVID-19)",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/coronavirus",
|
||||
"requirements": ["coronavirus==1.0.1"],
|
||||
"requirements": ["coronavirus==1.1.0"],
|
||||
"ssdp": [],
|
||||
"zeroconf": [],
|
||||
"homekit": {},
|
||||
|
@@ -4,5 +4,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/dwd_weather_warnings",
|
||||
"requirements": [],
|
||||
"dependencies": [],
|
||||
"after_dependencies": ["rest"],
|
||||
"codeowners": []
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/emulated_hue",
|
||||
"requirements": ["aiohttp_cors==0.7.0"],
|
||||
"dependencies": [],
|
||||
"after_dependencies": ["http"],
|
||||
"codeowners": [],
|
||||
"quality_scale": "internal"
|
||||
}
|
||||
|
@@ -97,7 +97,12 @@ class FacebookNotificationService(BaseNotificationService):
|
||||
else:
|
||||
recipient = {"id": target}
|
||||
|
||||
body = {"recipient": recipient, "message": body_message}
|
||||
body = {
|
||||
"recipient": recipient,
|
||||
"message": body_message,
|
||||
"messaging_type": "MESSAGE_TAG",
|
||||
"tag": "ACCOUNT_UPDATE",
|
||||
}
|
||||
resp = requests.post(
|
||||
BASE_URL,
|
||||
data=json.dumps(body),
|
||||
|
@@ -123,10 +123,8 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
|
||||
hass, username, password, icloud_dir, max_interval, gps_accuracy_threshold,
|
||||
)
|
||||
await hass.async_add_executor_job(account.setup)
|
||||
if not account.devices:
|
||||
return False
|
||||
|
||||
hass.data[DOMAIN][username] = account
|
||||
hass.data[DOMAIN][entry.unique_id] = account
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
|
@@ -10,6 +10,7 @@ from pyicloud.services.findmyiphone import AppleDevice
|
||||
|
||||
from homeassistant.components.zone import async_active_zone
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||
from homeassistant.helpers.event import track_point_in_utc_time
|
||||
from homeassistant.helpers.storage import Store
|
||||
@@ -37,7 +38,7 @@ from .const import (
|
||||
DEVICE_STATUS,
|
||||
DEVICE_STATUS_CODES,
|
||||
DEVICE_STATUS_SET,
|
||||
SERVICE_UPDATE,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
ATTRIBUTION = "Data provided by Apple iCloud"
|
||||
@@ -91,7 +92,7 @@ class IcloudAccount:
|
||||
self._family_members_fullname = {}
|
||||
self._devices = {}
|
||||
|
||||
self.unsub_device_tracker = None
|
||||
self.listeners = []
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Set up an iCloud account."""
|
||||
@@ -104,13 +105,17 @@ class IcloudAccount:
|
||||
_LOGGER.error("Error logging into iCloud Service: %s", error)
|
||||
return
|
||||
|
||||
user_info = None
|
||||
try:
|
||||
api_devices = self.api.devices
|
||||
# Gets device owners infos
|
||||
user_info = self.api.devices.response["userInfo"]
|
||||
except PyiCloudNoDevicesException:
|
||||
user_info = api_devices.response["userInfo"]
|
||||
except (KeyError, PyiCloudNoDevicesException):
|
||||
_LOGGER.error("No iCloud device found")
|
||||
return
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
if DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending":
|
||||
_LOGGER.warning("Pending devices, trying again ...")
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
self._owner_fullname = f"{user_info['firstName']} {user_info['lastName']}"
|
||||
|
||||
@@ -132,13 +137,21 @@ class IcloudAccount:
|
||||
api_devices = {}
|
||||
try:
|
||||
api_devices = self.api.devices
|
||||
except PyiCloudNoDevicesException:
|
||||
_LOGGER.error("No iCloud device found")
|
||||
return
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
_LOGGER.error("Unknown iCloud error: %s", err)
|
||||
self._fetch_interval = 5
|
||||
dispatcher_send(self.hass, SERVICE_UPDATE)
|
||||
self._fetch_interval = 2
|
||||
dispatcher_send(self.hass, self.signal_device_update)
|
||||
track_point_in_utc_time(
|
||||
self.hass,
|
||||
self.keep_alive,
|
||||
utcnow() + timedelta(minutes=self._fetch_interval),
|
||||
)
|
||||
return
|
||||
|
||||
if DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending":
|
||||
_LOGGER.warning("Pending devices, trying again in 15s")
|
||||
self._fetch_interval = 0.25
|
||||
dispatcher_send(self.hass, self.signal_device_update)
|
||||
track_point_in_utc_time(
|
||||
self.hass,
|
||||
self.keep_alive,
|
||||
@@ -147,10 +160,19 @@ class IcloudAccount:
|
||||
return
|
||||
|
||||
# Gets devices infos
|
||||
new_device = False
|
||||
for device in api_devices:
|
||||
status = device.status(DEVICE_STATUS_SET)
|
||||
device_id = status[DEVICE_ID]
|
||||
device_name = status[DEVICE_NAME]
|
||||
device_status = DEVICE_STATUS_CODES.get(status[DEVICE_STATUS], "error")
|
||||
|
||||
if (
|
||||
device_status == "pending"
|
||||
or status[DEVICE_BATTERY_STATUS] == "Unknown"
|
||||
or status.get(DEVICE_BATTERY_LEVEL) is None
|
||||
):
|
||||
continue
|
||||
|
||||
if self._devices.get(device_id, None) is not None:
|
||||
# Seen device -> updating
|
||||
@@ -165,9 +187,14 @@ class IcloudAccount:
|
||||
)
|
||||
self._devices[device_id] = IcloudDevice(self, device, status)
|
||||
self._devices[device_id].update(status)
|
||||
new_device = True
|
||||
|
||||
self._fetch_interval = self._determine_interval()
|
||||
dispatcher_send(self.hass, SERVICE_UPDATE)
|
||||
|
||||
dispatcher_send(self.hass, self.signal_device_update)
|
||||
if new_device:
|
||||
dispatcher_send(self.hass, self.signal_device_new)
|
||||
|
||||
track_point_in_utc_time(
|
||||
self.hass,
|
||||
self.keep_alive,
|
||||
@@ -291,6 +318,16 @@ class IcloudAccount:
|
||||
"""Return the account devices."""
|
||||
return self._devices
|
||||
|
||||
@property
|
||||
def signal_device_new(self) -> str:
|
||||
"""Event specific per Freebox entry to signal new device."""
|
||||
return f"{DOMAIN}-{self._username}-device-new"
|
||||
|
||||
@property
|
||||
def signal_device_update(self) -> str:
|
||||
"""Event specific per Freebox entry to signal updates in devices."""
|
||||
return f"{DOMAIN}-{self._username}-device-update"
|
||||
|
||||
|
||||
class IcloudDevice:
|
||||
"""Representation of a iCloud device."""
|
||||
@@ -348,6 +385,8 @@ class IcloudDevice:
|
||||
and self._status[DEVICE_LOCATION][DEVICE_LOCATION_LATITUDE]
|
||||
):
|
||||
location = self._status[DEVICE_LOCATION]
|
||||
if self._location is None:
|
||||
dispatcher_send(self._account.hass, self._account.signal_device_new)
|
||||
self._location = location
|
||||
|
||||
def play_sound(self) -> None:
|
||||
|
@@ -1,7 +1,6 @@
|
||||
"""iCloud component constants."""
|
||||
|
||||
DOMAIN = "icloud"
|
||||
SERVICE_UPDATE = f"{DOMAIN}_update"
|
||||
|
||||
CONF_MAX_INTERVAL = "max_interval"
|
||||
CONF_GPS_ACCURACY_THRESHOLD = "gps_accuracy_threshold"
|
||||
|
@@ -5,17 +5,16 @@ from typing import Dict
|
||||
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
||||
from homeassistant.components.device_tracker.config_entry import TrackerEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .account import IcloudDevice
|
||||
from .account import IcloudAccount, IcloudDevice
|
||||
from .const import (
|
||||
DEVICE_LOCATION_HORIZONTAL_ACCURACY,
|
||||
DEVICE_LOCATION_LATITUDE,
|
||||
DEVICE_LOCATION_LONGITUDE,
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -30,25 +29,45 @@ async def async_setup_scanner(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Configure a dispatcher connection based on a config entry."""
|
||||
username = entry.data[CONF_USERNAME]
|
||||
) -> None:
|
||||
"""Set up device tracker for iCloud component."""
|
||||
account = hass.data[DOMAIN][entry.unique_id]
|
||||
tracked = set()
|
||||
|
||||
for device in hass.data[DOMAIN][username].devices.values():
|
||||
if device.location is None:
|
||||
_LOGGER.debug("No position found for %s", device.name)
|
||||
@callback
|
||||
def update_account():
|
||||
"""Update the values of the account."""
|
||||
add_entities(account, async_add_entities, tracked)
|
||||
|
||||
account.listeners.append(
|
||||
async_dispatcher_connect(hass, account.signal_device_new, update_account)
|
||||
)
|
||||
|
||||
update_account()
|
||||
|
||||
|
||||
@callback
|
||||
def add_entities(account, async_add_entities, tracked):
|
||||
"""Add new tracker entities from the account."""
|
||||
new_tracked = []
|
||||
|
||||
for dev_id, device in account.devices.items():
|
||||
if dev_id in tracked or device.location is None:
|
||||
continue
|
||||
|
||||
_LOGGER.debug("Adding device_tracker for %s", device.name)
|
||||
new_tracked.append(IcloudTrackerEntity(account, device))
|
||||
tracked.add(dev_id)
|
||||
|
||||
async_add_entities([IcloudTrackerEntity(device)])
|
||||
if new_tracked:
|
||||
async_add_entities(new_tracked, True)
|
||||
|
||||
|
||||
class IcloudTrackerEntity(TrackerEntity):
|
||||
"""Represent a tracked device."""
|
||||
|
||||
def __init__(self, device: IcloudDevice):
|
||||
def __init__(self, account: IcloudAccount, device: IcloudDevice):
|
||||
"""Set up the iCloud tracker entity."""
|
||||
self._account = account
|
||||
self._device = device
|
||||
self._unsub_dispatcher = None
|
||||
|
||||
@@ -115,7 +134,7 @@ class IcloudTrackerEntity(TrackerEntity):
|
||||
async def async_added_to_hass(self):
|
||||
"""Register state update callback."""
|
||||
self._unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass, SERVICE_UPDATE, self.async_write_ha_state
|
||||
self.hass, self._account.signal_device_update, self.async_write_ha_state
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
|
@@ -3,14 +3,15 @@ import logging
|
||||
from typing import Dict
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_USERNAME, DEVICE_CLASS_BATTERY
|
||||
from homeassistant.const import DEVICE_CLASS_BATTERY
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.icon import icon_for_battery_level
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .account import IcloudDevice
|
||||
from .const import DOMAIN, SERVICE_UPDATE
|
||||
from .account import IcloudAccount, IcloudDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -18,23 +19,44 @@ _LOGGER = logging.getLogger(__name__)
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
) -> None:
|
||||
"""Set up iCloud devices sensors based on a config entry."""
|
||||
username = entry.data[CONF_USERNAME]
|
||||
"""Set up device tracker for iCloud component."""
|
||||
account = hass.data[DOMAIN][entry.unique_id]
|
||||
tracked = set()
|
||||
|
||||
entities = []
|
||||
for device in hass.data[DOMAIN][username].devices.values():
|
||||
if device.battery_level is not None:
|
||||
_LOGGER.debug("Adding battery sensor for %s", device.name)
|
||||
entities.append(IcloudDeviceBatterySensor(device))
|
||||
@callback
|
||||
def update_account():
|
||||
"""Update the values of the account."""
|
||||
add_entities(account, async_add_entities, tracked)
|
||||
|
||||
async_add_entities(entities, True)
|
||||
account.listeners.append(
|
||||
async_dispatcher_connect(hass, account.signal_device_new, update_account)
|
||||
)
|
||||
|
||||
update_account()
|
||||
|
||||
|
||||
@callback
|
||||
def add_entities(account, async_add_entities, tracked):
|
||||
"""Add new tracker entities from the account."""
|
||||
new_tracked = []
|
||||
|
||||
for dev_id, device in account.devices.items():
|
||||
if dev_id in tracked or device.battery_level is None:
|
||||
continue
|
||||
|
||||
new_tracked.append(IcloudDeviceBatterySensor(account, device))
|
||||
tracked.add(dev_id)
|
||||
|
||||
if new_tracked:
|
||||
async_add_entities(new_tracked, True)
|
||||
|
||||
|
||||
class IcloudDeviceBatterySensor(Entity):
|
||||
"""Representation of a iCloud device battery sensor."""
|
||||
|
||||
def __init__(self, device: IcloudDevice):
|
||||
def __init__(self, account: IcloudAccount, device: IcloudDevice):
|
||||
"""Initialize the battery sensor."""
|
||||
self._account = account
|
||||
self._device = device
|
||||
self._unsub_dispatcher = None
|
||||
|
||||
@@ -94,7 +116,7 @@ class IcloudDeviceBatterySensor(Entity):
|
||||
async def async_added_to_hass(self):
|
||||
"""Register state update callback."""
|
||||
self._unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass, SERVICE_UPDATE, self.async_write_ha_state
|
||||
self.hass, self._account.signal_device_update, self.async_write_ha_state
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
|
@@ -199,6 +199,9 @@ def humanify(hass, events):
|
||||
"""
|
||||
domain_prefixes = tuple(f"{dom}." for dom in CONTINUOUS_DOMAINS)
|
||||
|
||||
# Track last states to filter out duplicates
|
||||
last_state = {}
|
||||
|
||||
# Group events in batches of GROUP_BY_MINUTES
|
||||
for _, g_events in groupby(
|
||||
events, lambda event: event.time_fired.minute // GROUP_BY_MINUTES
|
||||
@@ -236,9 +239,15 @@ def humanify(hass, events):
|
||||
# Yield entries
|
||||
for event in events_batch:
|
||||
if event.event_type == EVENT_STATE_CHANGED:
|
||||
|
||||
to_state = State.from_dict(event.data.get("new_state"))
|
||||
|
||||
# Filter out states that become same state again (force_update=True)
|
||||
# or light becoming different color
|
||||
if last_state.get(to_state.entity_id) == to_state.state:
|
||||
continue
|
||||
|
||||
last_state[to_state.entity_id] = to_state.state
|
||||
|
||||
domain = to_state.domain
|
||||
|
||||
# Skip all but the last sensor state
|
||||
|
@@ -61,7 +61,7 @@ class PushoverNotificationService(BaseNotificationService):
|
||||
url = data.get(ATTR_URL, None)
|
||||
url_title = data.get(ATTR_URL_TITLE, None)
|
||||
priority = data.get(ATTR_PRIORITY, None)
|
||||
retry = data.get(ATTR_PRIORITY, None)
|
||||
retry = data.get(ATTR_RETRY, None)
|
||||
expire = data.get(ATTR_EXPIRE, None)
|
||||
callback_url = data.get(ATTR_CALLBACK_URL, None)
|
||||
timestamp = data.get(ATTR_TIMESTAMP, None)
|
||||
|
@@ -4,5 +4,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/pvoutput",
|
||||
"requirements": [],
|
||||
"dependencies": [],
|
||||
"after_dependencies": ["rest"],
|
||||
"codeowners": ["@fabaff"]
|
||||
}
|
||||
|
@@ -68,3 +68,8 @@ class TeslaDeviceEntity(TeslaDevice, TrackerEntity):
|
||||
def source_type(self):
|
||||
"""Return the source type, eg gps or router, of the device."""
|
||||
return SOURCE_TYPE_GPS
|
||||
|
||||
@property
|
||||
def force_update(self):
|
||||
"""All updates do not need to be written to the state machine."""
|
||||
return False
|
||||
|
@@ -4,7 +4,7 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/unifi",
|
||||
"requirements": [
|
||||
"aiounifi==13"
|
||||
"aiounifi==14"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": [
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"domain": "velbus",
|
||||
"name": "Velbus",
|
||||
"documentation": "https://www.home-assistant.io/integrations/velbus",
|
||||
"requirements": ["python-velbus==2.0.41"],
|
||||
"requirements": ["python-velbus==2.0.42"],
|
||||
"config_flow": true,
|
||||
"dependencies": [],
|
||||
"codeowners": ["@Cereal2nd", "@brefra"]
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 106
|
||||
PATCH_VERSION = "4"
|
||||
PATCH_VERSION = "6"
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER = (3, 7, 0)
|
||||
|
@@ -199,7 +199,7 @@ aiopylgtv==0.3.3
|
||||
aioswitcher==2019.4.26
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==13
|
||||
aiounifi==14
|
||||
|
||||
# homeassistant.components.wwlln
|
||||
aiowwlln==2.0.2
|
||||
@@ -399,7 +399,7 @@ connect-box==0.2.5
|
||||
construct==2.9.45
|
||||
|
||||
# homeassistant.components.coronavirus
|
||||
coronavirus==1.0.1
|
||||
coronavirus==1.1.0
|
||||
|
||||
# homeassistant.scripts.credstash
|
||||
# credstash==1.15.0
|
||||
@@ -1643,7 +1643,7 @@ python-telnet-vlc==1.0.4
|
||||
python-twitch-client==0.6.0
|
||||
|
||||
# homeassistant.components.velbus
|
||||
python-velbus==2.0.41
|
||||
python-velbus==2.0.42
|
||||
|
||||
# homeassistant.components.vlc
|
||||
python-vlc==1.1.2
|
||||
|
@@ -78,7 +78,7 @@ aiopylgtv==0.3.3
|
||||
aioswitcher==2019.4.26
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==13
|
||||
aiounifi==14
|
||||
|
||||
# homeassistant.components.wwlln
|
||||
aiowwlln==2.0.2
|
||||
@@ -144,7 +144,7 @@ colorlog==4.1.0
|
||||
construct==2.9.45
|
||||
|
||||
# homeassistant.components.coronavirus
|
||||
coronavirus==1.0.1
|
||||
coronavirus==1.1.0
|
||||
|
||||
# homeassistant.scripts.credstash
|
||||
# credstash==1.15.0
|
||||
@@ -581,7 +581,7 @@ python-nest==4.1.0
|
||||
python-twitch-client==0.6.0
|
||||
|
||||
# homeassistant.components.velbus
|
||||
python-velbus==2.0.41
|
||||
python-velbus==2.0.42
|
||||
|
||||
# homeassistant.components.awair
|
||||
python_awair==0.0.4
|
||||
|
@@ -65,7 +65,7 @@ class ImportCollector(ast.NodeVisitor):
|
||||
|
||||
# self.hass.components.hue.async_create()
|
||||
# Name(id=self)
|
||||
# .Attribute(attr=hass)
|
||||
# .Attribute(attr=hass) or .Attribute(attr=_hass)
|
||||
# .Attribute(attr=hue)
|
||||
# .Attribute(attr=async_create)
|
||||
if (
|
||||
@@ -78,7 +78,7 @@ class ImportCollector(ast.NodeVisitor):
|
||||
)
|
||||
or (
|
||||
isinstance(node.value.value, ast.Attribute)
|
||||
and node.value.value.attr == "hass"
|
||||
and node.value.value.attr in ("hass", "_hass")
|
||||
)
|
||||
)
|
||||
):
|
||||
@@ -89,20 +89,47 @@ class ImportCollector(ast.NodeVisitor):
|
||||
|
||||
|
||||
ALLOWED_USED_COMPONENTS = {
|
||||
# This component will always be set up
|
||||
"persistent_notification",
|
||||
# These allow to register things without being set up
|
||||
"conversation",
|
||||
"frontend",
|
||||
"hassio",
|
||||
"system_health",
|
||||
"websocket_api",
|
||||
# Internal integrations
|
||||
"alert",
|
||||
"automation",
|
||||
"conversation",
|
||||
"device_automation",
|
||||
"zone",
|
||||
"frontend",
|
||||
"group",
|
||||
"hassio",
|
||||
"homeassistant",
|
||||
"system_log",
|
||||
"input_boolean",
|
||||
"input_datetime",
|
||||
"input_number",
|
||||
"input_select",
|
||||
"input_text",
|
||||
"persistent_notification",
|
||||
"person",
|
||||
"script",
|
||||
"shopping_list",
|
||||
"sun",
|
||||
"system_health",
|
||||
"system_log",
|
||||
"timer",
|
||||
"webhook",
|
||||
"websocket_api",
|
||||
"zone",
|
||||
# Entity integrations with platforms
|
||||
"alarm_control_panel",
|
||||
"binary_sensor",
|
||||
"climate",
|
||||
"cover",
|
||||
"device_tracker",
|
||||
"fan",
|
||||
"image_processing",
|
||||
"light",
|
||||
"lock",
|
||||
"media_player",
|
||||
"scene",
|
||||
"sensor",
|
||||
"switch",
|
||||
"vacuum",
|
||||
"water_heater",
|
||||
# Other
|
||||
"mjpeg", # base class, has no reqs or component to load.
|
||||
"stream", # Stream cannot install on all systems, can be imported without reqs.
|
||||
@@ -121,18 +148,7 @@ IGNORE_VIOLATIONS = {
|
||||
# This should become a helper method that integrations can submit data to
|
||||
("websocket_api", "lovelace"),
|
||||
("websocket_api", "shopping_list"),
|
||||
# Expose HA to external systems
|
||||
"homekit",
|
||||
"alexa",
|
||||
"google_assistant",
|
||||
"emulated_hue",
|
||||
"prometheus",
|
||||
"conversation",
|
||||
"logbook",
|
||||
"mobile_app",
|
||||
# These should be extracted to external package
|
||||
"pvoutput",
|
||||
"dwd_weather_warnings",
|
||||
}
|
||||
|
||||
|
||||
|
@@ -30,6 +30,8 @@ class TestFacebook(unittest.TestCase):
|
||||
expected_body = {
|
||||
"recipient": {"phone_number": target[0]},
|
||||
"message": {"text": message},
|
||||
"messaging_type": "MESSAGE_TAG",
|
||||
"tag": "ACCOUNT_UPDATE",
|
||||
}
|
||||
assert mock.last_request.json() == expected_body
|
||||
|
||||
@@ -53,6 +55,8 @@ class TestFacebook(unittest.TestCase):
|
||||
expected_body = {
|
||||
"recipient": {"phone_number": target},
|
||||
"message": {"text": message},
|
||||
"messaging_type": "MESSAGE_TAG",
|
||||
"tag": "ACCOUNT_UPDATE",
|
||||
}
|
||||
assert request.json() == expected_body
|
||||
|
||||
@@ -77,7 +81,12 @@ class TestFacebook(unittest.TestCase):
|
||||
assert mock.called
|
||||
assert mock.call_count == 1
|
||||
|
||||
expected_body = {"recipient": {"phone_number": target[0]}, "message": data}
|
||||
expected_body = {
|
||||
"recipient": {"phone_number": target[0]},
|
||||
"message": data,
|
||||
"messaging_type": "MESSAGE_TAG",
|
||||
"tag": "ACCOUNT_UPDATE",
|
||||
}
|
||||
assert mock.last_request.json() == expected_body
|
||||
|
||||
expected_params = {"access_token": ["page-access-token"]}
|
||||
|
@@ -1484,3 +1484,36 @@ async def test_humanify_script_started_event(hass):
|
||||
assert event2["domain"] == "script"
|
||||
assert event2["message"] == "started"
|
||||
assert event2["entity_id"] == "script.bye"
|
||||
|
||||
|
||||
async def test_humanify_same_state(hass):
|
||||
"""Test humanifying Script Run event."""
|
||||
state_50 = ha.State("light.kitchen", "on", {"brightness": 50}).as_dict()
|
||||
state_100 = ha.State("light.kitchen", "on", {"brightness": 100}).as_dict()
|
||||
state_200 = ha.State("light.kitchen", "on", {"brightness": 200}).as_dict()
|
||||
|
||||
events = list(
|
||||
logbook.humanify(
|
||||
hass,
|
||||
[
|
||||
ha.Event(
|
||||
EVENT_STATE_CHANGED,
|
||||
{
|
||||
"entity_id": "light.kitchen",
|
||||
"old_state": state_50,
|
||||
"new_state": state_100,
|
||||
},
|
||||
),
|
||||
ha.Event(
|
||||
EVENT_STATE_CHANGED,
|
||||
{
|
||||
"entity_id": "light.kitchen",
|
||||
"old_state": state_100,
|
||||
"new_state": state_200,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
assert len(events) == 1
|
||||
|
Reference in New Issue
Block a user