mirror of
https://github.com/home-assistant/core.git
synced 2025-04-28 19:27:51 +00:00
commit
68d2a1107e
@ -94,6 +94,7 @@ class BaseEditConfigView(HomeAssistantView):
|
|||||||
self.data_schema = data_schema
|
self.data_schema = data_schema
|
||||||
self.post_write_hook = post_write_hook
|
self.post_write_hook = post_write_hook
|
||||||
self.data_validator = data_validator
|
self.data_validator = data_validator
|
||||||
|
self.mutation_lock = asyncio.Lock()
|
||||||
|
|
||||||
def _empty_config(self):
|
def _empty_config(self):
|
||||||
"""Empty config if file not found."""
|
"""Empty config if file not found."""
|
||||||
@ -114,8 +115,9 @@ class BaseEditConfigView(HomeAssistantView):
|
|||||||
async def get(self, request, config_key):
|
async def get(self, request, config_key):
|
||||||
"""Fetch device specific config."""
|
"""Fetch device specific config."""
|
||||||
hass = request.app["hass"]
|
hass = request.app["hass"]
|
||||||
current = await self.read_config(hass)
|
async with self.mutation_lock:
|
||||||
value = self._get_value(hass, current, config_key)
|
current = await self.read_config(hass)
|
||||||
|
value = self._get_value(hass, current, config_key)
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
return self.json_message("Resource not found", 404)
|
return self.json_message("Resource not found", 404)
|
||||||
@ -148,10 +150,11 @@ class BaseEditConfigView(HomeAssistantView):
|
|||||||
|
|
||||||
path = hass.config.path(self.path)
|
path = hass.config.path(self.path)
|
||||||
|
|
||||||
current = await self.read_config(hass)
|
async with self.mutation_lock:
|
||||||
self._write_value(hass, current, config_key, data)
|
current = await self.read_config(hass)
|
||||||
|
self._write_value(hass, current, config_key, data)
|
||||||
|
|
||||||
await hass.async_add_executor_job(_write, path, current)
|
await hass.async_add_executor_job(_write, path, current)
|
||||||
|
|
||||||
if self.post_write_hook is not None:
|
if self.post_write_hook is not None:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
@ -163,15 +166,16 @@ class BaseEditConfigView(HomeAssistantView):
|
|||||||
async def delete(self, request, config_key):
|
async def delete(self, request, config_key):
|
||||||
"""Remove an entry."""
|
"""Remove an entry."""
|
||||||
hass = request.app["hass"]
|
hass = request.app["hass"]
|
||||||
current = await self.read_config(hass)
|
async with self.mutation_lock:
|
||||||
value = self._get_value(hass, current, config_key)
|
current = await self.read_config(hass)
|
||||||
path = hass.config.path(self.path)
|
value = self._get_value(hass, current, config_key)
|
||||||
|
path = hass.config.path(self.path)
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
return self.json_message("Resource not found", 404)
|
return self.json_message("Resource not found", 404)
|
||||||
|
|
||||||
self._delete_value(hass, current, config_key)
|
self._delete_value(hass, current, config_key)
|
||||||
await hass.async_add_executor_job(_write, path, current)
|
await hass.async_add_executor_job(_write, path, current)
|
||||||
|
|
||||||
if self.post_write_hook is not None:
|
if self.post_write_hook is not None:
|
||||||
hass.async_create_task(self.post_write_hook(ACTION_DELETE, config_key))
|
hass.async_create_task(self.post_write_hook(ACTION_DELETE, config_key))
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Home Assistant Frontend",
|
"name": "Home Assistant Frontend",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"home-assistant-frontend==20200130.2"
|
"home-assistant-frontend==20200130.3"
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"api",
|
"api",
|
||||||
|
@ -133,7 +133,6 @@ DEVICE_CLASS_TO_GOOGLE_TYPES = {
|
|||||||
(binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_OPENING): TYPE_SENSOR,
|
(binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_OPENING): TYPE_SENSOR,
|
||||||
(binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_WINDOW): TYPE_SENSOR,
|
(binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_WINDOW): TYPE_SENSOR,
|
||||||
(media_player.DOMAIN, media_player.DEVICE_CLASS_TV): TYPE_TV,
|
(media_player.DOMAIN, media_player.DEVICE_CLASS_TV): TYPE_TV,
|
||||||
(media_player.DOMAIN, media_player.DEVICE_CLASS_SPEAKER): TYPE_SPEAKER,
|
|
||||||
(sensor.DOMAIN, sensor.DEVICE_CLASS_TEMPERATURE): TYPE_SENSOR,
|
(sensor.DOMAIN, sensor.DEVICE_CLASS_TEMPERATURE): TYPE_SENSOR,
|
||||||
(sensor.DOMAIN, sensor.DEVICE_CLASS_HUMIDITY): TYPE_SENSOR,
|
(sensor.DOMAIN, sensor.DEVICE_CLASS_HUMIDITY): TYPE_SENSOR,
|
||||||
}
|
}
|
||||||
@ -146,3 +145,5 @@ STORE_AGENT_USER_IDS = "agent_user_ids"
|
|||||||
|
|
||||||
SOURCE_CLOUD = "cloud"
|
SOURCE_CLOUD = "cloud"
|
||||||
SOURCE_LOCAL = "local"
|
SOURCE_LOCAL = "local"
|
||||||
|
|
||||||
|
NOT_EXPOSE_LOCAL = {TYPE_ALARM, TYPE_LOCK}
|
||||||
|
@ -28,6 +28,7 @@ from .const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
DOMAIN_TO_GOOGLE_TYPES,
|
DOMAIN_TO_GOOGLE_TYPES,
|
||||||
ERR_FUNCTION_NOT_SUPPORTED,
|
ERR_FUNCTION_NOT_SUPPORTED,
|
||||||
|
NOT_EXPOSE_LOCAL,
|
||||||
SOURCE_LOCAL,
|
SOURCE_LOCAL,
|
||||||
STORE_AGENT_USER_IDS,
|
STORE_AGENT_USER_IDS,
|
||||||
)
|
)
|
||||||
@ -351,6 +352,18 @@ class GoogleEntity:
|
|||||||
"""If entity should be exposed."""
|
"""If entity should be exposed."""
|
||||||
return self.config.should_expose(self.state)
|
return self.config.should_expose(self.state)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def should_expose_local(self) -> bool:
|
||||||
|
"""Return if the entity should be exposed locally."""
|
||||||
|
return (
|
||||||
|
self.should_expose()
|
||||||
|
and get_google_type(
|
||||||
|
self.state.domain, self.state.attributes.get(ATTR_DEVICE_CLASS)
|
||||||
|
)
|
||||||
|
not in NOT_EXPOSE_LOCAL
|
||||||
|
and not self.might_2fa()
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def is_supported(self) -> bool:
|
def is_supported(self) -> bool:
|
||||||
"""Return if the entity is supported by Google."""
|
"""Return if the entity is supported by Google."""
|
||||||
@ -401,7 +414,7 @@ class GoogleEntity:
|
|||||||
if aliases:
|
if aliases:
|
||||||
device["name"]["nicknames"] = [name] + aliases
|
device["name"]["nicknames"] = [name] + aliases
|
||||||
|
|
||||||
if self.config.is_local_sdk_active:
|
if self.config.is_local_sdk_active and self.should_expose_local():
|
||||||
device["otherDeviceIds"] = [{"deviceId": self.entity_id}]
|
device["otherDeviceIds"] = [{"deviceId": self.entity_id}]
|
||||||
device["customData"] = {
|
device["customData"] = {
|
||||||
"webhookId": self.config.local_sdk_webhook_id,
|
"webhookId": self.config.local_sdk_webhook_id,
|
||||||
|
@ -243,9 +243,7 @@ async def async_devices_reachable(hass, data: RequestData, payload):
|
|||||||
"devices": [
|
"devices": [
|
||||||
entity.reachable_device_serialize()
|
entity.reachable_device_serialize()
|
||||||
for entity in async_get_entities(hass, data.config)
|
for entity in async_get_entities(hass, data.config)
|
||||||
if entity.entity_id in google_ids
|
if entity.entity_id in google_ids and entity.should_expose_local()
|
||||||
and entity.should_expose()
|
|
||||||
and not entity.might_2fa()
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,11 @@ class HomematicipSmokeDetector(HomematicipGenericDevice, BinarySensorDevice):
|
|||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return true if smoke is detected."""
|
"""Return true if smoke is detected."""
|
||||||
return self._device.smokeDetectorAlarmType != SmokeDetectorAlarmType.IDLE_OFF
|
if self._device.smokeDetectorAlarmType:
|
||||||
|
return (
|
||||||
|
self._device.smokeDetectorAlarmType != SmokeDetectorAlarmType.IDLE_OFF
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class HomematicipWaterDetector(HomematicipGenericDevice, BinarySensorDevice):
|
class HomematicipWaterDetector(HomematicipGenericDevice, BinarySensorDevice):
|
||||||
|
@ -24,7 +24,10 @@ PRESENCE_SENSOR_TYPES = {
|
|||||||
}
|
}
|
||||||
TAG_SENSOR_TYPES = {"Tag Vibration": "vibration", "Tag Open": "opening"}
|
TAG_SENSOR_TYPES = {"Tag Vibration": "vibration", "Tag Open": "opening"}
|
||||||
|
|
||||||
SENSOR_TYPES = {"NACamera": WELCOME_SENSOR_TYPES, "NOC": PRESENCE_SENSOR_TYPES}
|
SENSOR_TYPES = {
|
||||||
|
"NACamera": WELCOME_SENSOR_TYPES,
|
||||||
|
"NOC": PRESENCE_SENSOR_TYPES,
|
||||||
|
}
|
||||||
|
|
||||||
CONF_HOME = "home"
|
CONF_HOME = "home"
|
||||||
CONF_CAMERAS = "cameras"
|
CONF_CAMERAS = "cameras"
|
||||||
@ -61,12 +64,28 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
sensor_types.update(SENSOR_TYPES[camera["type"]])
|
sensor_types.update(SENSOR_TYPES[camera["type"]])
|
||||||
|
|
||||||
# Tags are only supported with Netatmo Welcome indoor cameras
|
# Tags are only supported with Netatmo Welcome indoor cameras
|
||||||
if camera["type"] == "NACamera" and data.get_modules(camera["id"]):
|
modules = data.get_modules(camera["id"])
|
||||||
sensor_types.update(TAG_SENSOR_TYPES)
|
if camera["type"] == "NACamera" and modules:
|
||||||
|
for module in modules:
|
||||||
|
for sensor_type in TAG_SENSOR_TYPES:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Adding camera tag %s (%s)",
|
||||||
|
module["name"],
|
||||||
|
module["id"],
|
||||||
|
)
|
||||||
|
entities.append(
|
||||||
|
NetatmoBinarySensor(
|
||||||
|
data,
|
||||||
|
camera["id"],
|
||||||
|
home_id,
|
||||||
|
sensor_type,
|
||||||
|
module["id"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for sensor_name in sensor_types:
|
for sensor_type in sensor_types:
|
||||||
entities.append(
|
entities.append(
|
||||||
NetatmoBinarySensor(data, camera["id"], home_id, sensor_name)
|
NetatmoBinarySensor(data, camera["id"], home_id, sensor_type)
|
||||||
)
|
)
|
||||||
except pyatmo.NoDevice:
|
except pyatmo.NoDevice:
|
||||||
_LOGGER.debug("No camera entities to add")
|
_LOGGER.debug("No camera entities to add")
|
||||||
@ -115,6 +134,15 @@ class NetatmoBinarySensor(BinarySensorDevice):
|
|||||||
"""Return the unique ID for this sensor."""
|
"""Return the unique ID for this sensor."""
|
||||||
return self._unique_id
|
return self._unique_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the class of this sensor."""
|
||||||
|
if self._camera_type == "NACamera":
|
||||||
|
return WELCOME_SENSOR_TYPES.get(self._sensor_type)
|
||||||
|
if self._camera_type == "NOC":
|
||||||
|
return PRESENCE_SENSOR_TYPES.get(self._sensor_type)
|
||||||
|
return TAG_SENSOR_TYPES.get(self._sensor_type)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
"""Return the device info for the sensor."""
|
"""Return the device info for the sensor."""
|
||||||
|
@ -88,7 +88,11 @@ _UNDEF = object()
|
|||||||
async def async_create_person(hass, name, *, user_id=None, device_trackers=None):
|
async def async_create_person(hass, name, *, user_id=None, device_trackers=None):
|
||||||
"""Create a new person."""
|
"""Create a new person."""
|
||||||
await hass.data[DOMAIN][1].async_create_item(
|
await hass.data[DOMAIN][1].async_create_item(
|
||||||
{ATTR_NAME: name, ATTR_USER_ID: user_id, "device_trackers": device_trackers}
|
{
|
||||||
|
ATTR_NAME: name,
|
||||||
|
ATTR_USER_ID: user_id,
|
||||||
|
CONF_DEVICE_TRACKERS: device_trackers or [],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -103,14 +107,14 @@ async def async_add_user_device_tracker(
|
|||||||
if person.get(ATTR_USER_ID) != user_id:
|
if person.get(ATTR_USER_ID) != user_id:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
device_trackers = person["device_trackers"]
|
device_trackers = person[CONF_DEVICE_TRACKERS]
|
||||||
|
|
||||||
if device_tracker_entity_id in device_trackers:
|
if device_tracker_entity_id in device_trackers:
|
||||||
return
|
return
|
||||||
|
|
||||||
await coll.async_update_item(
|
await coll.async_update_item(
|
||||||
person[collection.CONF_ID],
|
person[collection.CONF_ID],
|
||||||
{"device_trackers": device_trackers + [device_tracker_entity_id]},
|
{CONF_DEVICE_TRACKERS: device_trackers + [device_tracker_entity_id]},
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -161,6 +165,23 @@ class PersonStorageCollection(collection.StorageCollection):
|
|||||||
super().__init__(store, logger, id_manager)
|
super().__init__(store, logger, id_manager)
|
||||||
self.yaml_collection = yaml_collection
|
self.yaml_collection = yaml_collection
|
||||||
|
|
||||||
|
async def _async_load_data(self) -> Optional[dict]:
|
||||||
|
"""Load the data.
|
||||||
|
|
||||||
|
A past bug caused onboarding to create invalid person objects.
|
||||||
|
This patches it up.
|
||||||
|
"""
|
||||||
|
data = await super()._async_load_data()
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return data
|
||||||
|
|
||||||
|
for person in data["items"]:
|
||||||
|
if person[CONF_DEVICE_TRACKERS] is None:
|
||||||
|
person[CONF_DEVICE_TRACKERS] = []
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
async def async_load(self) -> None:
|
async def async_load(self) -> None:
|
||||||
"""Load the Storage collection."""
|
"""Load the Storage collection."""
|
||||||
await super().async_load()
|
await super().async_load()
|
||||||
@ -179,14 +200,16 @@ class PersonStorageCollection(collection.StorageCollection):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for person in list(self.data.values()):
|
for person in list(self.data.values()):
|
||||||
if entity_id not in person["device_trackers"]:
|
if entity_id not in person[CONF_DEVICE_TRACKERS]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
await self.async_update_item(
|
await self.async_update_item(
|
||||||
person[collection.CONF_ID],
|
person[collection.CONF_ID],
|
||||||
{
|
{
|
||||||
"device_trackers": [
|
CONF_DEVICE_TRACKERS: [
|
||||||
devt for devt in person["device_trackers"] if devt != entity_id
|
devt
|
||||||
|
for devt in person[CONF_DEVICE_TRACKERS]
|
||||||
|
if devt != entity_id
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -315,7 +338,9 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType):
|
|||||||
conf = await entity_component.async_prepare_reload(skip_reset=True)
|
conf = await entity_component.async_prepare_reload(skip_reset=True)
|
||||||
if conf is None:
|
if conf is None:
|
||||||
return
|
return
|
||||||
await yaml_collection.async_load(await filter_yaml_data(hass, conf[DOMAIN]))
|
await yaml_collection.async_load(
|
||||||
|
await filter_yaml_data(hass, conf.get(DOMAIN, []))
|
||||||
|
)
|
||||||
|
|
||||||
service.async_register_admin_service(
|
service.async_register_admin_service(
|
||||||
hass, DOMAIN, SERVICE_RELOAD, async_reload_yaml
|
hass, DOMAIN, SERVICE_RELOAD, async_reload_yaml
|
||||||
@ -406,7 +431,7 @@ class Person(RestoreEntity):
|
|||||||
self._unsub_track_device()
|
self._unsub_track_device()
|
||||||
self._unsub_track_device = None
|
self._unsub_track_device = None
|
||||||
|
|
||||||
trackers = self._config.get(CONF_DEVICE_TRACKERS)
|
trackers = self._config[CONF_DEVICE_TRACKERS]
|
||||||
|
|
||||||
if trackers:
|
if trackers:
|
||||||
_LOGGER.debug("Subscribe to device trackers for %s", self.entity_id)
|
_LOGGER.debug("Subscribe to device trackers for %s", self.entity_id)
|
||||||
@ -426,7 +451,7 @@ class Person(RestoreEntity):
|
|||||||
def _update_state(self):
|
def _update_state(self):
|
||||||
"""Update the state."""
|
"""Update the state."""
|
||||||
latest_non_gps_home = latest_not_home = latest_gps = latest = None
|
latest_non_gps_home = latest_not_home = latest_gps = latest = None
|
||||||
for entity_id in self._config.get(CONF_DEVICE_TRACKERS, []):
|
for entity_id in self._config[CONF_DEVICE_TRACKERS]:
|
||||||
state = self.hass.states.get(entity_id)
|
state = self.hass.states.get(entity_id)
|
||||||
|
|
||||||
if not state or state.state in IGNORE_STATES:
|
if not state or state.state in IGNORE_STATES:
|
||||||
|
@ -157,7 +157,8 @@ class SpotifyMediaPlayer(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def media_content_id(self) -> Optional[str]:
|
def media_content_id(self) -> Optional[str]:
|
||||||
"""Return the media URL."""
|
"""Return the media URL."""
|
||||||
return self._currently_playing.get("item", {}).get("name")
|
item = self._currently_playing.get("item") or {}
|
||||||
|
return item.get("name")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_content_type(self) -> Optional[str]:
|
def media_content_type(self) -> Optional[str]:
|
||||||
@ -203,7 +204,8 @@ class SpotifyMediaPlayer(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def media_title(self) -> Optional[str]:
|
def media_title(self) -> Optional[str]:
|
||||||
"""Return the media title."""
|
"""Return the media title."""
|
||||||
return self._currently_playing.get("item", {}).get("name")
|
item = self._currently_playing.get("item") or {}
|
||||||
|
return item.get("name")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_artist(self) -> Optional[str]:
|
def media_artist(self) -> Optional[str]:
|
||||||
@ -224,7 +226,8 @@ class SpotifyMediaPlayer(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def media_track(self) -> Optional[int]:
|
def media_track(self) -> Optional[int]:
|
||||||
"""Track number of current playing media, music track only."""
|
"""Track number of current playing media, music track only."""
|
||||||
return self._currently_playing.get("item", {}).get("track_number")
|
item = self._currently_playing.get("item") or {}
|
||||||
|
return item.get("track_number")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_playlist(self):
|
def media_playlist(self):
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
play_playlist:
|
|
||||||
description: Play a Spotify playlist.
|
|
||||||
fields:
|
|
||||||
media_content_id:
|
|
||||||
description: Spotify URI of the playlist.
|
|
||||||
example: 'spotify:playlist:0IpRnqCHSjun48oQRX1Dy7'
|
|
||||||
random_song:
|
|
||||||
description: True to select random song at start, False to start from beginning.
|
|
||||||
example: true
|
|
@ -1,7 +1,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 105
|
MINOR_VERSION = 105
|
||||||
PATCH_VERSION = "3"
|
PATCH_VERSION = "4"
|
||||||
__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)
|
||||||
|
@ -158,9 +158,13 @@ class StorageCollection(ObservableCollection):
|
|||||||
"""Home Assistant object."""
|
"""Home Assistant object."""
|
||||||
return self.store.hass
|
return self.store.hass
|
||||||
|
|
||||||
|
async def _async_load_data(self) -> Optional[dict]:
|
||||||
|
"""Load the data."""
|
||||||
|
return cast(Optional[dict], await self.store.async_load())
|
||||||
|
|
||||||
async def async_load(self) -> None:
|
async def async_load(self) -> None:
|
||||||
"""Load the storage Manager."""
|
"""Load the storage Manager."""
|
||||||
raw_storage = cast(Optional[dict], await self.store.async_load())
|
raw_storage = await self._async_load_data()
|
||||||
|
|
||||||
if raw_storage is None:
|
if raw_storage is None:
|
||||||
raw_storage = {"items": []}
|
raw_storage = {"items": []}
|
||||||
|
@ -11,7 +11,7 @@ cryptography==2.8
|
|||||||
defusedxml==0.6.0
|
defusedxml==0.6.0
|
||||||
distro==1.4.0
|
distro==1.4.0
|
||||||
hass-nabucasa==0.31
|
hass-nabucasa==0.31
|
||||||
home-assistant-frontend==20200130.2
|
home-assistant-frontend==20200130.3
|
||||||
importlib-metadata==1.4.0
|
importlib-metadata==1.4.0
|
||||||
jinja2>=2.10.3
|
jinja2>=2.10.3
|
||||||
netdisco==2.6.0
|
netdisco==2.6.0
|
||||||
|
@ -679,7 +679,7 @@ hole==0.5.0
|
|||||||
holidays==0.9.12
|
holidays==0.9.12
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20200130.2
|
home-assistant-frontend==20200130.3
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.8
|
homeassistant-pyozw==0.1.8
|
||||||
|
@ -247,7 +247,7 @@ hole==0.5.0
|
|||||||
holidays==0.9.12
|
holidays==0.9.12
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20200130.2
|
home-assistant-frontend==20200130.3
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.8
|
homeassistant-pyozw==0.1.8
|
||||||
|
@ -7,6 +7,7 @@ import pytest
|
|||||||
from homeassistant.components.google_assistant import helpers
|
from homeassistant.components.google_assistant import helpers
|
||||||
from homeassistant.components.google_assistant.const import ( # noqa: F401
|
from homeassistant.components.google_assistant.const import ( # noqa: F401
|
||||||
EVENT_COMMAND_RECEIVED,
|
EVENT_COMMAND_RECEIVED,
|
||||||
|
NOT_EXPOSE_LOCAL,
|
||||||
)
|
)
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util import dt
|
from homeassistant.util import dt
|
||||||
@ -46,6 +47,15 @@ async def test_google_entity_sync_serialize_with_local_sdk(hass):
|
|||||||
"webhookId": "mock-webhook-id",
|
"webhookId": "mock-webhook-id",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for device_type in NOT_EXPOSE_LOCAL:
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.google_assistant.helpers.get_google_type",
|
||||||
|
return_value=device_type,
|
||||||
|
):
|
||||||
|
serialized = await entity.sync_serialize(None)
|
||||||
|
assert "otherDeviceIds" not in serialized
|
||||||
|
assert "customData" not in serialized
|
||||||
|
|
||||||
|
|
||||||
async def test_config_local_sdk(hass, hass_client):
|
async def test_config_local_sdk(hass, hass_client):
|
||||||
"""Test the local SDK."""
|
"""Test the local SDK."""
|
||||||
|
@ -682,7 +682,6 @@ async def test_device_class_cover(hass, device_class, google_type):
|
|||||||
"device_class,google_type",
|
"device_class,google_type",
|
||||||
[
|
[
|
||||||
("non_existing_class", "action.devices.types.SWITCH"),
|
("non_existing_class", "action.devices.types.SWITCH"),
|
||||||
("speaker", "action.devices.types.SPEAKER"),
|
|
||||||
("tv", "action.devices.types.TV"),
|
("tv", "action.devices.types.TV"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""The tests for the person component."""
|
"""The tests for the person component."""
|
||||||
import logging
|
import logging
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
|
from asynctest import patch
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import person
|
from homeassistant.components import person
|
||||||
@ -773,3 +773,15 @@ async def test_reload(hass, hass_admin_user):
|
|||||||
assert state_2 is None
|
assert state_2 is None
|
||||||
assert state_3 is not None
|
assert state_3 is not None
|
||||||
assert state_3.name == "Person 3"
|
assert state_3.name == "Person 3"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_person_storage_fixing_device_trackers(storage_collection):
|
||||||
|
"""Test None device trackers become lists."""
|
||||||
|
with patch.object(
|
||||||
|
storage_collection.store,
|
||||||
|
"async_load",
|
||||||
|
return_value={"items": [{"id": "bla", "name": "bla", "device_trackers": None}]},
|
||||||
|
):
|
||||||
|
await storage_collection.async_load()
|
||||||
|
|
||||||
|
assert storage_collection.data["bla"]["device_trackers"] == []
|
||||||
|
Loading…
x
Reference in New Issue
Block a user