mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
commit
c2218e8a64
@ -14,6 +14,8 @@ from .const import CONF_MODEL, DOMAIN
|
|||||||
from .device import get_device
|
from .device import get_device
|
||||||
from .errors import AlreadyConfigured, AuthenticationRequired, CannotConnect
|
from .errors import AlreadyConfigured, AuthenticationRequired, CannotConnect
|
||||||
|
|
||||||
|
AXIS_OUI = {'00408C', 'ACCC8E', 'B8A44F'}
|
||||||
|
|
||||||
CONFIG_FILE = 'axis.conf'
|
CONFIG_FILE = 'axis.conf'
|
||||||
|
|
||||||
EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound',
|
EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound',
|
||||||
@ -151,10 +153,14 @@ class AxisFlowHandler(config_entries.ConfigFlow):
|
|||||||
|
|
||||||
This flow is triggered by the discovery component.
|
This flow is triggered by the discovery component.
|
||||||
"""
|
"""
|
||||||
|
serialnumber = discovery_info['properties']['macaddress']
|
||||||
|
|
||||||
|
if serialnumber[:6] not in AXIS_OUI:
|
||||||
|
return self.async_abort(reason='not_axis_device')
|
||||||
|
|
||||||
if discovery_info[CONF_HOST].startswith('169.254'):
|
if discovery_info[CONF_HOST].startswith('169.254'):
|
||||||
return self.async_abort(reason='link_local_address')
|
return self.async_abort(reason='link_local_address')
|
||||||
|
|
||||||
serialnumber = discovery_info['properties']['macaddress']
|
|
||||||
# pylint: disable=unsupported-assignment-operation
|
# pylint: disable=unsupported-assignment-operation
|
||||||
self.context['macaddress'] = serialnumber
|
self.context['macaddress'] = serialnumber
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Device is already configured",
|
"already_configured": "Device is already configured",
|
||||||
"bad_config_file": "Bad data from config file",
|
"bad_config_file": "Bad data from config file",
|
||||||
"link_local_address": "Link local addresses are not supported"
|
"link_local_address": "Link local addresses are not supported",
|
||||||
|
"not_axis_device": "Discovered device not an Axis device"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -56,7 +56,7 @@ async def websocket_update_config(hass, connection, msg):
|
|||||||
data.pop('type')
|
data.pop('type')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await hass.config.update(**data)
|
await hass.config.async_update(**data)
|
||||||
connection.send_result(msg['id'])
|
connection.send_result(msg['id'])
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
connection.send_error(
|
connection.send_error(
|
||||||
|
@ -9,7 +9,6 @@ from pydeconz.utils import (
|
|||||||
async_discovery, async_get_api_key, async_get_bridgeid)
|
async_discovery, async_get_api_key, async_get_bridgeid)
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL
|
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
@ -154,6 +153,9 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||||||
|
|
||||||
async def async_step_ssdp(self, discovery_info):
|
async def async_step_ssdp(self, discovery_info):
|
||||||
"""Handle a discovered deCONZ bridge."""
|
"""Handle a discovered deCONZ bridge."""
|
||||||
|
from homeassistant.components.ssdp import (
|
||||||
|
ATTR_MANUFACTURERURL, ATTR_SERIAL)
|
||||||
|
|
||||||
if discovery_info[ATTR_MANUFACTURERURL] != DECONZ_MANUFACTURERURL:
|
if discovery_info[ATTR_MANUFACTURERURL] != DECONZ_MANUFACTURERURL:
|
||||||
return self.async_abort(reason='not_deconz_bridge')
|
return self.async_abort(reason='not_deconz_bridge')
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/components/deconz",
|
"documentation": "https://www.home-assistant.io/components/deconz",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"pydeconz==59"
|
"pydeconz==60"
|
||||||
],
|
],
|
||||||
"ssdp": {
|
"ssdp": {
|
||||||
"manufacturer": [
|
"manufacturer": [
|
||||||
|
@ -11,19 +11,21 @@ from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, \
|
|||||||
from homeassistant.helpers import config_entry_flow
|
from homeassistant.helpers import config_entry_flow
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER
|
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER
|
||||||
from .const import DOMAIN
|
from .const import (
|
||||||
|
DOMAIN,
|
||||||
|
ATTR_ALTITUDE,
|
||||||
|
ATTR_ACCURACY,
|
||||||
|
ATTR_ACTIVITY,
|
||||||
|
ATTR_DEVICE,
|
||||||
|
ATTR_DIRECTION,
|
||||||
|
ATTR_PROVIDER,
|
||||||
|
ATTR_SPEED,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN)
|
TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN)
|
||||||
|
|
||||||
ATTR_ALTITUDE = 'altitude'
|
|
||||||
ATTR_ACCURACY = 'accuracy'
|
|
||||||
ATTR_ACTIVITY = 'activity'
|
|
||||||
ATTR_DEVICE = 'device'
|
|
||||||
ATTR_DIRECTION = 'direction'
|
|
||||||
ATTR_PROVIDER = 'provider'
|
|
||||||
ATTR_SPEED = 'speed'
|
|
||||||
|
|
||||||
DEFAULT_ACCURACY = 200
|
DEFAULT_ACCURACY = 200
|
||||||
DEFAULT_BATTERY = -1
|
DEFAULT_BATTERY = -1
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
"""Const for GPSLogger."""
|
"""Const for GPSLogger."""
|
||||||
|
|
||||||
DOMAIN = 'gpslogger'
|
DOMAIN = 'gpslogger'
|
||||||
|
|
||||||
|
ATTR_ALTITUDE = 'altitude'
|
||||||
|
ATTR_ACCURACY = 'accuracy'
|
||||||
|
ATTR_ACTIVITY = 'activity'
|
||||||
|
ATTR_DEVICE = 'device'
|
||||||
|
ATTR_DIRECTION = 'direction'
|
||||||
|
ATTR_PROVIDER = 'provider'
|
||||||
|
ATTR_SPEED = 'speed'
|
||||||
|
@ -2,14 +2,29 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_BATTERY_LEVEL,
|
||||||
|
ATTR_GPS_ACCURACY,
|
||||||
|
ATTR_LATITUDE,
|
||||||
|
ATTR_LONGITUDE,
|
||||||
|
)
|
||||||
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
||||||
from homeassistant.components.device_tracker.config_entry import (
|
from homeassistant.components.device_tracker.config_entry import (
|
||||||
DeviceTrackerEntity
|
DeviceTrackerEntity
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers import device_registry
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
from . import DOMAIN as GPL_DOMAIN, TRACKER_UPDATE
|
from . import DOMAIN as GPL_DOMAIN, TRACKER_UPDATE
|
||||||
|
from .const import (
|
||||||
|
ATTR_ACTIVITY,
|
||||||
|
ATTR_ALTITUDE,
|
||||||
|
ATTR_DIRECTION,
|
||||||
|
ATTR_PROVIDER,
|
||||||
|
ATTR_SPEED,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -32,8 +47,27 @@ async def async_setup_entry(hass: HomeAssistantType, entry,
|
|||||||
hass.data[GPL_DOMAIN]['unsub_device_tracker'][entry.entry_id] = \
|
hass.data[GPL_DOMAIN]['unsub_device_tracker'][entry.entry_id] = \
|
||||||
async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data)
|
async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data)
|
||||||
|
|
||||||
|
# Restore previously loaded devices
|
||||||
|
dev_reg = await device_registry.async_get_registry(hass)
|
||||||
|
dev_ids = {
|
||||||
|
identifier[1]
|
||||||
|
for device in dev_reg.devices.values()
|
||||||
|
for identifier in device.identifiers
|
||||||
|
if identifier[0] == GPL_DOMAIN
|
||||||
|
}
|
||||||
|
if not dev_ids:
|
||||||
|
return
|
||||||
|
|
||||||
class GPSLoggerEntity(DeviceTrackerEntity):
|
entities = []
|
||||||
|
for dev_id in dev_ids:
|
||||||
|
hass.data[GPL_DOMAIN]['devices'].add(dev_id)
|
||||||
|
entity = GPSLoggerEntity(dev_id, None, None, None, None)
|
||||||
|
entities.append(entity)
|
||||||
|
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
class GPSLoggerEntity(DeviceTrackerEntity, RestoreEntity):
|
||||||
"""Represent a tracked device."""
|
"""Represent a tracked device."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -102,11 +136,46 @@ class GPSLoggerEntity(DeviceTrackerEntity):
|
|||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Register state update callback."""
|
"""Register state update callback."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
self._unsub_dispatcher = async_dispatcher_connect(
|
self._unsub_dispatcher = async_dispatcher_connect(
|
||||||
self.hass, TRACKER_UPDATE, self._async_receive_data)
|
self.hass, TRACKER_UPDATE, self._async_receive_data)
|
||||||
|
|
||||||
|
# don't restore if we got created with data
|
||||||
|
if self._location is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
state = await self.async_get_last_state()
|
||||||
|
if state is None:
|
||||||
|
self._location = (None, None)
|
||||||
|
self._accuracy = None
|
||||||
|
self._attributes = {
|
||||||
|
ATTR_ALTITUDE: None,
|
||||||
|
ATTR_ACTIVITY: None,
|
||||||
|
ATTR_DIRECTION: None,
|
||||||
|
ATTR_PROVIDER: None,
|
||||||
|
ATTR_SPEED: None,
|
||||||
|
}
|
||||||
|
self._battery = None
|
||||||
|
return
|
||||||
|
|
||||||
|
attr = state.attributes
|
||||||
|
self._location = (
|
||||||
|
attr.get(ATTR_LATITUDE),
|
||||||
|
attr.get(ATTR_LONGITUDE),
|
||||||
|
)
|
||||||
|
self._accuracy = attr.get(ATTR_GPS_ACCURACY)
|
||||||
|
self._attributes = {
|
||||||
|
ATTR_ALTITUDE: attr.get(ATTR_ALTITUDE),
|
||||||
|
ATTR_ACTIVITY: attr.get(ATTR_ACTIVITY),
|
||||||
|
ATTR_DIRECTION: attr.get(ATTR_DIRECTION),
|
||||||
|
ATTR_PROVIDER: attr.get(ATTR_PROVIDER),
|
||||||
|
ATTR_SPEED: attr.get(ATTR_SPEED),
|
||||||
|
}
|
||||||
|
self._battery = attr.get(ATTR_BATTERY_LEVEL)
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self):
|
async def async_will_remove_from_hass(self):
|
||||||
"""Clean up after entity before removal."""
|
"""Clean up after entity before removal."""
|
||||||
|
await super().async_will_remove_from_hass()
|
||||||
self._unsub_dispatcher()
|
self._unsub_dispatcher()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -9,7 +9,8 @@ from homeassistant.auth.const import GROUP_ID_ADMIN
|
|||||||
from homeassistant.components.homeassistant import SERVICE_CHECK_CONFIG
|
from homeassistant.components.homeassistant import SERVICE_CHECK_CONFIG
|
||||||
import homeassistant.config as conf_util
|
import homeassistant.config as conf_util
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP)
|
ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP,
|
||||||
|
EVENT_CORE_CONFIG_UPDATE)
|
||||||
from homeassistant.core import DOMAIN as HASS_DOMAIN, callback
|
from homeassistant.core import DOMAIN as HASS_DOMAIN, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
@ -194,8 +195,13 @@ async def async_setup(hass, config):
|
|||||||
|
|
||||||
await hassio.update_hass_api(config.get('http', {}), refresh_token.token)
|
await hassio.update_hass_api(config.get('http', {}), refresh_token.token)
|
||||||
|
|
||||||
if 'homeassistant' in config:
|
async def push_config(_):
|
||||||
await hassio.update_hass_timezone(config['homeassistant'])
|
"""Push core config to Hass.io."""
|
||||||
|
await hassio.update_hass_timezone(str(hass.config.time_zone))
|
||||||
|
|
||||||
|
hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, push_config)
|
||||||
|
|
||||||
|
await push_config(None)
|
||||||
|
|
||||||
async def async_service_handler(service):
|
async def async_service_handler(service):
|
||||||
"""Handle service calls for Hass.io."""
|
"""Handle service calls for Hass.io."""
|
||||||
|
@ -11,7 +11,7 @@ from homeassistant.components.http import (
|
|||||||
CONF_SERVER_PORT,
|
CONF_SERVER_PORT,
|
||||||
CONF_SSL_CERTIFICATE,
|
CONF_SSL_CERTIFICATE,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_TIME_ZONE, SERVER_PORT
|
from homeassistant.const import SERVER_PORT
|
||||||
|
|
||||||
from .const import X_HASSIO
|
from .const import X_HASSIO
|
||||||
|
|
||||||
@ -140,13 +140,13 @@ class HassIO:
|
|||||||
payload=options)
|
payload=options)
|
||||||
|
|
||||||
@_api_bool
|
@_api_bool
|
||||||
def update_hass_timezone(self, core_config):
|
def update_hass_timezone(self, timezone):
|
||||||
"""Update Home-Assistant timezone data on Hass.io.
|
"""Update Home-Assistant timezone data on Hass.io.
|
||||||
|
|
||||||
This method return a coroutine.
|
This method return a coroutine.
|
||||||
"""
|
"""
|
||||||
return self.send_command("/supervisor/options", payload={
|
return self.send_command("/supervisor/options", payload={
|
||||||
'timezone': core_config.get(CONF_TIME_ZONE)
|
'timezone': timezone
|
||||||
})
|
})
|
||||||
|
|
||||||
async def send_command(self, command, method="post", payload=None,
|
async def send_command(self, command, method="post", payload=None,
|
||||||
|
@ -8,7 +8,6 @@ import async_timeout
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
|
||||||
@ -146,6 +145,8 @@ class HueFlowHandler(config_entries.ConfigFlow):
|
|||||||
This flow is triggered by the SSDP component. It will check if the
|
This flow is triggered by the SSDP component. It will check if the
|
||||||
host is already configured and delegate to the import step if not.
|
host is already configured and delegate to the import step if not.
|
||||||
"""
|
"""
|
||||||
|
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL
|
||||||
|
|
||||||
if discovery_info[ATTR_MANUFACTURERURL] != HUE_MANUFACTURERURL:
|
if discovery_info[ATTR_MANUFACTURERURL] != HUE_MANUFACTURERURL:
|
||||||
return self.async_abort(reason='not_hue_bridge')
|
return self.async_abort(reason='not_hue_bridge')
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS)
|
CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS,
|
||||||
|
EVENT_CORE_CONFIG_UPDATE)
|
||||||
from homeassistant.helpers import config_per_platform
|
from homeassistant.helpers import config_per_platform
|
||||||
from homeassistant.helpers.entity import async_generate_entity_id
|
from homeassistant.helpers.entity import async_generate_entity_id
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
@ -90,13 +92,25 @@ async def async_setup(hass, config):
|
|||||||
hass.async_create_task(zone.async_update_ha_state())
|
hass.async_create_task(zone.async_update_ha_state())
|
||||||
entities.add(zone.entity_id)
|
entities.add(zone.entity_id)
|
||||||
|
|
||||||
if ENTITY_ID_HOME not in entities and HOME_ZONE not in zone_entries:
|
if ENTITY_ID_HOME in entities or HOME_ZONE in zone_entries:
|
||||||
|
return True
|
||||||
|
|
||||||
zone = Zone(hass, hass.config.location_name,
|
zone = Zone(hass, hass.config.location_name,
|
||||||
hass.config.latitude, hass.config.longitude,
|
hass.config.latitude, hass.config.longitude,
|
||||||
DEFAULT_RADIUS, ICON_HOME, False)
|
DEFAULT_RADIUS, ICON_HOME, False)
|
||||||
zone.entity_id = ENTITY_ID_HOME
|
zone.entity_id = ENTITY_ID_HOME
|
||||||
hass.async_create_task(zone.async_update_ha_state())
|
hass.async_create_task(zone.async_update_ha_state())
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def core_config_updated(_):
|
||||||
|
"""Handle core config updated."""
|
||||||
|
zone.name = hass.config.location_name
|
||||||
|
zone.latitude = hass.config.latitude
|
||||||
|
zone.longitude = hass.config.longitude
|
||||||
|
zone.async_write_ha_state()
|
||||||
|
|
||||||
|
hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_updated)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,21 +23,18 @@ def in_zone(zone, latitude, longitude, radius=0) -> bool:
|
|||||||
class Zone(Entity):
|
class Zone(Entity):
|
||||||
"""Representation of a Zone."""
|
"""Representation of a Zone."""
|
||||||
|
|
||||||
|
name = None
|
||||||
|
|
||||||
def __init__(self, hass, name, latitude, longitude, radius, icon, passive):
|
def __init__(self, hass, name, latitude, longitude, radius, icon, passive):
|
||||||
"""Initialize the zone."""
|
"""Initialize the zone."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._name = name
|
self.name = name
|
||||||
self._latitude = latitude
|
self.latitude = latitude
|
||||||
self._longitude = longitude
|
self.longitude = longitude
|
||||||
self._radius = radius
|
self._radius = radius
|
||||||
self._icon = icon
|
self._icon = icon
|
||||||
self._passive = passive
|
self._passive = passive
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the zone."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state property really does nothing for a zone."""
|
"""Return the state property really does nothing for a zone."""
|
||||||
@ -53,8 +50,8 @@ class Zone(Entity):
|
|||||||
"""Return the state attributes of the zone."""
|
"""Return the state attributes of the zone."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_HIDDEN: True,
|
ATTR_HIDDEN: True,
|
||||||
ATTR_LATITUDE: self._latitude,
|
ATTR_LATITUDE: self.latitude,
|
||||||
ATTR_LONGITUDE: self._longitude,
|
ATTR_LONGITUDE: self.longitude,
|
||||||
ATTR_RADIUS: self._radius,
|
ATTR_RADIUS: self._radius,
|
||||||
}
|
}
|
||||||
if self._passive:
|
if self._passive:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 94
|
MINOR_VERSION = 94
|
||||||
PATCH_VERSION = '1'
|
PATCH_VERSION = '2'
|
||||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||||
REQUIRED_PYTHON_VER = (3, 5, 3)
|
REQUIRED_PYTHON_VER = (3, 5, 3)
|
||||||
|
@ -1288,10 +1288,7 @@ class Config:
|
|||||||
unit_system: Optional[str] = None,
|
unit_system: Optional[str] = None,
|
||||||
location_name: Optional[str] = None,
|
location_name: Optional[str] = None,
|
||||||
time_zone: Optional[str] = None) -> None:
|
time_zone: Optional[str] = None) -> None:
|
||||||
"""Update the configuration from a dictionary.
|
"""Update the configuration from a dictionary."""
|
||||||
|
|
||||||
Async friendly.
|
|
||||||
"""
|
|
||||||
self.config_source = source
|
self.config_source = source
|
||||||
if latitude is not None:
|
if latitude is not None:
|
||||||
self.latitude = latitude
|
self.latitude = latitude
|
||||||
@ -1309,11 +1306,8 @@ class Config:
|
|||||||
if time_zone is not None:
|
if time_zone is not None:
|
||||||
self.set_time_zone(time_zone)
|
self.set_time_zone(time_zone)
|
||||||
|
|
||||||
async def update(self, **kwargs: Any) -> None:
|
async def async_update(self, **kwargs: Any) -> None:
|
||||||
"""Update the configuration from a dictionary.
|
"""Update the configuration from a dictionary."""
|
||||||
|
|
||||||
Async friendly.
|
|
||||||
"""
|
|
||||||
self._update(source=SOURCE_STORAGE, **kwargs)
|
self._update(source=SOURCE_STORAGE, **kwargs)
|
||||||
await self.async_store()
|
await self.async_store()
|
||||||
self.hass.bus.async_fire(
|
self.hass.bus.async_fire(
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
"""Helpers for listening to events."""
|
"""Helpers for listening to events."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import functools as ft
|
import functools as ft
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.helpers.sun import get_astral_event_next
|
from homeassistant.helpers.sun import get_astral_event_next
|
||||||
from ..core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from ..const import (
|
from homeassistant.const import (
|
||||||
ATTR_NOW, EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL,
|
ATTR_NOW, EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL,
|
||||||
SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET)
|
SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, EVENT_CORE_CONFIG_UPDATE)
|
||||||
from ..util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
from ..util.async_ import run_callback_threadsafe
|
from homeassistant.util.async_ import run_callback_threadsafe
|
||||||
|
|
||||||
# PyLint does not like the use of threaded_listener_factory
|
# PyLint does not like the use of threaded_listener_factory
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
@ -263,30 +266,71 @@ def async_track_time_interval(hass, action, interval):
|
|||||||
track_time_interval = threaded_listener_factory(async_track_time_interval)
|
track_time_interval = threaded_listener_factory(async_track_time_interval)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class SunListener:
|
||||||
|
"""Helper class to help listen to sun events."""
|
||||||
|
|
||||||
|
hass = attr.ib(type=HomeAssistant)
|
||||||
|
action = attr.ib(type=Callable)
|
||||||
|
event = attr.ib(type=str)
|
||||||
|
offset = attr.ib(type=timedelta)
|
||||||
|
_unsub_sun = attr.ib(default=None)
|
||||||
|
_unsub_config = attr.ib(default=None)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_attach(self):
|
||||||
|
"""Attach a sun listener."""
|
||||||
|
assert self._unsub_config is None
|
||||||
|
|
||||||
|
self._unsub_config = self.hass.bus.async_listen(
|
||||||
|
EVENT_CORE_CONFIG_UPDATE, self._handle_config_event)
|
||||||
|
|
||||||
|
self._listen_next_sun_event()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_detach(self):
|
||||||
|
"""Detach the sun listener."""
|
||||||
|
assert self._unsub_sun is not None
|
||||||
|
assert self._unsub_config is not None
|
||||||
|
|
||||||
|
self._unsub_sun()
|
||||||
|
self._unsub_sun = None
|
||||||
|
self._unsub_config()
|
||||||
|
self._unsub_config = None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _listen_next_sun_event(self):
|
||||||
|
"""Set up the sun event listener."""
|
||||||
|
assert self._unsub_sun is None
|
||||||
|
|
||||||
|
self._unsub_sun = async_track_point_in_utc_time(
|
||||||
|
self.hass, self._handle_sun_event,
|
||||||
|
get_astral_event_next(self.hass, self.event, offset=self.offset)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_sun_event(self, _now):
|
||||||
|
"""Handle solar event."""
|
||||||
|
self._unsub_sun = None
|
||||||
|
self._listen_next_sun_event()
|
||||||
|
self.hass.async_run_job(self.action)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_config_event(self, _event):
|
||||||
|
"""Handle core config update."""
|
||||||
|
assert self._unsub_sun is not None
|
||||||
|
self._unsub_sun()
|
||||||
|
self._unsub_sun = None
|
||||||
|
self._listen_next_sun_event()
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def async_track_sunrise(hass, action, offset=None):
|
def async_track_sunrise(hass, action, offset=None):
|
||||||
"""Add a listener that will fire a specified offset from sunrise daily."""
|
"""Add a listener that will fire a specified offset from sunrise daily."""
|
||||||
remove = None
|
listener = SunListener(hass, action, SUN_EVENT_SUNRISE, offset)
|
||||||
|
listener.async_attach()
|
||||||
@callback
|
return listener.async_detach
|
||||||
def sunrise_automation_listener(now):
|
|
||||||
"""Handle points in time to execute actions."""
|
|
||||||
nonlocal remove
|
|
||||||
remove = async_track_point_in_utc_time(
|
|
||||||
hass, sunrise_automation_listener, get_astral_event_next(
|
|
||||||
hass, SUN_EVENT_SUNRISE, offset=offset))
|
|
||||||
hass.async_run_job(action)
|
|
||||||
|
|
||||||
remove = async_track_point_in_utc_time(
|
|
||||||
hass, sunrise_automation_listener, get_astral_event_next(
|
|
||||||
hass, SUN_EVENT_SUNRISE, offset=offset))
|
|
||||||
|
|
||||||
def remove_listener():
|
|
||||||
"""Remove sunset listener."""
|
|
||||||
remove()
|
|
||||||
|
|
||||||
return remove_listener
|
|
||||||
|
|
||||||
|
|
||||||
track_sunrise = threaded_listener_factory(async_track_sunrise)
|
track_sunrise = threaded_listener_factory(async_track_sunrise)
|
||||||
@ -296,26 +340,9 @@ track_sunrise = threaded_listener_factory(async_track_sunrise)
|
|||||||
@bind_hass
|
@bind_hass
|
||||||
def async_track_sunset(hass, action, offset=None):
|
def async_track_sunset(hass, action, offset=None):
|
||||||
"""Add a listener that will fire a specified offset from sunset daily."""
|
"""Add a listener that will fire a specified offset from sunset daily."""
|
||||||
remove = None
|
listener = SunListener(hass, action, SUN_EVENT_SUNSET, offset)
|
||||||
|
listener.async_attach()
|
||||||
@callback
|
return listener.async_detach
|
||||||
def sunset_automation_listener(now):
|
|
||||||
"""Handle points in time to execute actions."""
|
|
||||||
nonlocal remove
|
|
||||||
remove = async_track_point_in_utc_time(
|
|
||||||
hass, sunset_automation_listener, get_astral_event_next(
|
|
||||||
hass, SUN_EVENT_SUNSET, offset=offset))
|
|
||||||
hass.async_run_job(action)
|
|
||||||
|
|
||||||
remove = async_track_point_in_utc_time(
|
|
||||||
hass, sunset_automation_listener, get_astral_event_next(
|
|
||||||
hass, SUN_EVENT_SUNSET, offset=offset))
|
|
||||||
|
|
||||||
def remove_listener():
|
|
||||||
"""Remove sunset listener."""
|
|
||||||
remove()
|
|
||||||
|
|
||||||
return remove_listener
|
|
||||||
|
|
||||||
|
|
||||||
track_sunset = threaded_listener_factory(async_track_sunset)
|
track_sunset = threaded_listener_factory(async_track_sunset)
|
||||||
|
@ -1045,7 +1045,7 @@ pydaikin==1.4.6
|
|||||||
pydanfossair==0.1.0
|
pydanfossair==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==59
|
pydeconz==60
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
pydispatcher==2.0.5
|
pydispatcher==2.0.5
|
||||||
|
@ -230,7 +230,7 @@ pyHS100==0.3.5
|
|||||||
pyblackbird==0.5
|
pyblackbird==0.5
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==59
|
pydeconz==60
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
pydispatcher==2.0.5
|
pydispatcher==2.0.5
|
||||||
|
@ -169,7 +169,7 @@ async def test_zeroconf_flow(hass):
|
|||||||
data={
|
data={
|
||||||
config_flow.CONF_HOST: '1.2.3.4',
|
config_flow.CONF_HOST: '1.2.3.4',
|
||||||
config_flow.CONF_PORT: 80,
|
config_flow.CONF_PORT: 80,
|
||||||
'properties': {'macaddress': '1234'}
|
'properties': {'macaddress': '00408C12345'}
|
||||||
},
|
},
|
||||||
context={'source': 'zeroconf'}
|
context={'source': 'zeroconf'}
|
||||||
)
|
)
|
||||||
@ -184,7 +184,7 @@ async def test_zeroconf_flow_known_device(hass):
|
|||||||
This is legacy support from devices registered with configurator.
|
This is legacy support from devices registered with configurator.
|
||||||
"""
|
"""
|
||||||
with patch('homeassistant.components.axis.config_flow.load_json',
|
with patch('homeassistant.components.axis.config_flow.load_json',
|
||||||
return_value={'1234ABCD': {
|
return_value={'00408C12345': {
|
||||||
config_flow.CONF_HOST: '2.3.4.5',
|
config_flow.CONF_HOST: '2.3.4.5',
|
||||||
config_flow.CONF_USERNAME: 'user',
|
config_flow.CONF_USERNAME: 'user',
|
||||||
config_flow.CONF_PASSWORD: 'pass',
|
config_flow.CONF_PASSWORD: 'pass',
|
||||||
@ -208,7 +208,7 @@ async def test_zeroconf_flow_known_device(hass):
|
|||||||
config_flow.CONF_HOST: '1.2.3.4',
|
config_flow.CONF_HOST: '1.2.3.4',
|
||||||
config_flow.CONF_PORT: 80,
|
config_flow.CONF_PORT: 80,
|
||||||
'hostname': 'name',
|
'hostname': 'name',
|
||||||
'properties': {'macaddress': '1234ABCD'}
|
'properties': {'macaddress': '00408C12345'}
|
||||||
},
|
},
|
||||||
context={'source': 'zeroconf'}
|
context={'source': 'zeroconf'}
|
||||||
)
|
)
|
||||||
@ -221,7 +221,7 @@ async def test_zeroconf_flow_already_configured(hass):
|
|||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=axis.DOMAIN,
|
domain=axis.DOMAIN,
|
||||||
data={axis.CONF_DEVICE: {axis.config_flow.CONF_HOST: '1.2.3.4'},
|
data={axis.CONF_DEVICE: {axis.config_flow.CONF_HOST: '1.2.3.4'},
|
||||||
axis.config_flow.CONF_MAC: '1234ABCD'}
|
axis.config_flow.CONF_MAC: '00408C12345'}
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ async def test_zeroconf_flow_already_configured(hass):
|
|||||||
config_flow.CONF_PASSWORD: 'pass',
|
config_flow.CONF_PASSWORD: 'pass',
|
||||||
config_flow.CONF_PORT: 80,
|
config_flow.CONF_PORT: 80,
|
||||||
'hostname': 'name',
|
'hostname': 'name',
|
||||||
'properties': {'macaddress': '1234ABCD'}
|
'properties': {'macaddress': '00408C12345'}
|
||||||
},
|
},
|
||||||
context={'source': 'zeroconf'}
|
context={'source': 'zeroconf'}
|
||||||
)
|
)
|
||||||
@ -242,11 +242,29 @@ async def test_zeroconf_flow_already_configured(hass):
|
|||||||
assert result['reason'] == 'already_configured'
|
assert result['reason'] == 'already_configured'
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zeroconf_flow_ignore_non_axis_device(hass):
|
||||||
|
"""Test that zeroconf doesn't setup devices with link local addresses."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
config_flow.DOMAIN,
|
||||||
|
data={
|
||||||
|
config_flow.CONF_HOST: '169.254.3.4',
|
||||||
|
'properties': {'macaddress': '01234567890'}
|
||||||
|
},
|
||||||
|
context={'source': 'zeroconf'}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result['type'] == 'abort'
|
||||||
|
assert result['reason'] == 'not_axis_device'
|
||||||
|
|
||||||
|
|
||||||
async def test_zeroconf_flow_ignore_link_local_address(hass):
|
async def test_zeroconf_flow_ignore_link_local_address(hass):
|
||||||
"""Test that zeroconf doesn't setup devices with link local addresses."""
|
"""Test that zeroconf doesn't setup devices with link local addresses."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
config_flow.DOMAIN,
|
config_flow.DOMAIN,
|
||||||
data={config_flow.CONF_HOST: '169.254.3.4'},
|
data={
|
||||||
|
config_flow.CONF_HOST: '169.254.3.4',
|
||||||
|
'properties': {'macaddress': '00408C12345'}
|
||||||
|
},
|
||||||
context={'source': 'zeroconf'}
|
context={'source': 'zeroconf'}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -257,7 +275,7 @@ async def test_zeroconf_flow_ignore_link_local_address(hass):
|
|||||||
async def test_zeroconf_flow_bad_config_file(hass):
|
async def test_zeroconf_flow_bad_config_file(hass):
|
||||||
"""Test that zeroconf discovery with bad config files abort."""
|
"""Test that zeroconf discovery with bad config files abort."""
|
||||||
with patch('homeassistant.components.axis.config_flow.load_json',
|
with patch('homeassistant.components.axis.config_flow.load_json',
|
||||||
return_value={'1234ABCD': {
|
return_value={'00408C12345': {
|
||||||
config_flow.CONF_HOST: '2.3.4.5',
|
config_flow.CONF_HOST: '2.3.4.5',
|
||||||
config_flow.CONF_USERNAME: 'user',
|
config_flow.CONF_USERNAME: 'user',
|
||||||
config_flow.CONF_PASSWORD: 'pass',
|
config_flow.CONF_PASSWORD: 'pass',
|
||||||
@ -268,7 +286,7 @@ async def test_zeroconf_flow_bad_config_file(hass):
|
|||||||
config_flow.DOMAIN,
|
config_flow.DOMAIN,
|
||||||
data={
|
data={
|
||||||
config_flow.CONF_HOST: '1.2.3.4',
|
config_flow.CONF_HOST: '1.2.3.4',
|
||||||
'properties': {'macaddress': '1234ABCD'}
|
'properties': {'macaddress': '00408C12345'}
|
||||||
},
|
},
|
||||||
context={'source': 'zeroconf'}
|
context={'source': 'zeroconf'}
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ from unittest.mock import Mock, patch
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from homeassistant.components.deconz import config_flow
|
from homeassistant.components.deconz import config_flow
|
||||||
|
from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
import pydeconz
|
import pydeconz
|
||||||
@ -175,8 +176,8 @@ async def test_bridge_ssdp_discovery(hass):
|
|||||||
data={
|
data={
|
||||||
config_flow.CONF_HOST: '1.2.3.4',
|
config_flow.CONF_HOST: '1.2.3.4',
|
||||||
config_flow.CONF_PORT: 80,
|
config_flow.CONF_PORT: 80,
|
||||||
config_flow.ATTR_SERIAL: 'id',
|
ATTR_SERIAL: 'id',
|
||||||
config_flow.ATTR_MANUFACTURERURL:
|
ATTR_MANUFACTURERURL:
|
||||||
config_flow.DECONZ_MANUFACTURERURL,
|
config_flow.DECONZ_MANUFACTURERURL,
|
||||||
config_flow.ATTR_UUID: 'uuid:1234'
|
config_flow.ATTR_UUID: 'uuid:1234'
|
||||||
},
|
},
|
||||||
@ -192,7 +193,7 @@ async def test_bridge_ssdp_discovery_not_deconz_bridge(hass):
|
|||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
config_flow.DOMAIN,
|
config_flow.DOMAIN,
|
||||||
data={
|
data={
|
||||||
config_flow.ATTR_MANUFACTURERURL: 'not deconz bridge'
|
ATTR_MANUFACTURERURL: 'not deconz bridge'
|
||||||
},
|
},
|
||||||
context={'source': 'ssdp'}
|
context={'source': 'ssdp'}
|
||||||
)
|
)
|
||||||
@ -217,8 +218,8 @@ async def test_bridge_discovery_update_existing_entry(hass):
|
|||||||
config_flow.DOMAIN,
|
config_flow.DOMAIN,
|
||||||
data={
|
data={
|
||||||
config_flow.CONF_HOST: 'mock-deconz',
|
config_flow.CONF_HOST: 'mock-deconz',
|
||||||
config_flow.ATTR_SERIAL: 'id',
|
ATTR_SERIAL: 'id',
|
||||||
config_flow.ATTR_MANUFACTURERURL:
|
ATTR_MANUFACTURERURL:
|
||||||
config_flow.DECONZ_MANUFACTURERURL,
|
config_flow.DECONZ_MANUFACTURERURL,
|
||||||
config_flow.ATTR_UUID: 'uuid:1234'
|
config_flow.ATTR_UUID: 'uuid:1234'
|
||||||
},
|
},
|
||||||
|
@ -29,11 +29,16 @@ def hassio_env():
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock):
|
def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock):
|
||||||
"""Create mock hassio http client."""
|
"""Create mock hassio http client."""
|
||||||
with patch('homeassistant.components.hassio.HassIO.update_hass_api',
|
with patch(
|
||||||
Mock(return_value=mock_coro({"result": "ok"}))), \
|
'homeassistant.components.hassio.HassIO.update_hass_api',
|
||||||
patch('homeassistant.components.hassio.HassIO.'
|
return_value=mock_coro({"result": "ok"})
|
||||||
'get_homeassistant_info',
|
), patch(
|
||||||
Mock(side_effect=HassioAPIError())):
|
'homeassistant.components.hassio.HassIO.update_hass_timezone',
|
||||||
|
return_value=mock_coro({"result": "ok"})
|
||||||
|
), patch(
|
||||||
|
'homeassistant.components.hassio.HassIO.get_homeassistant_info',
|
||||||
|
side_effect=HassioAPIError()
|
||||||
|
):
|
||||||
hass.state = CoreState.starting
|
hass.state = CoreState.starting
|
||||||
hass.loop.run_until_complete(async_setup_component(hass, 'hassio', {
|
hass.loop.run_until_complete(async_setup_component(hass, 'hassio', {
|
||||||
'http': {
|
'http': {
|
||||||
|
@ -56,7 +56,7 @@ async def test_hassio_addon_panel_startup(hass, aioclient_mock, hassio_env):
|
|||||||
})
|
})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 2
|
assert aioclient_mock.call_count == 3
|
||||||
assert mock_panel.called
|
assert mock_panel.called
|
||||||
mock_panel.assert_called_with(
|
mock_panel.assert_called_with(
|
||||||
hass, 'test1', {
|
hass, 'test1', {
|
||||||
@ -98,7 +98,7 @@ async def test_hassio_addon_panel_api(hass, aioclient_mock, hassio_env,
|
|||||||
})
|
})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 2
|
assert aioclient_mock.call_count == 3
|
||||||
assert mock_panel.called
|
assert mock_panel.called
|
||||||
mock_panel.assert_called_with(
|
mock_panel.assert_called_with(
|
||||||
hass, 'test1', {
|
hass, 'test1', {
|
||||||
|
@ -43,7 +43,7 @@ def test_setup_api_ping(hass, aioclient_mock):
|
|||||||
result = yield from async_setup_component(hass, 'hassio', {})
|
result = yield from async_setup_component(hass, 'hassio', {})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
assert hass.components.hassio.get_homeassistant_version() == "10.0"
|
assert hass.components.hassio.get_homeassistant_version() == "10.0"
|
||||||
assert hass.components.hassio.is_hassio()
|
assert hass.components.hassio.is_hassio()
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ def test_setup_api_push_api_data(hass, aioclient_mock):
|
|||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
||||||
assert aioclient_mock.mock_calls[1][2]['port'] == 9999
|
assert aioclient_mock.mock_calls[1][2]['port'] == 9999
|
||||||
assert aioclient_mock.mock_calls[1][2]['watchdog']
|
assert aioclient_mock.mock_calls[1][2]['watchdog']
|
||||||
@ -101,7 +101,7 @@ def test_setup_api_push_api_data_server_host(hass, aioclient_mock):
|
|||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
||||||
assert aioclient_mock.mock_calls[1][2]['port'] == 9999
|
assert aioclient_mock.mock_calls[1][2]['port'] == 9999
|
||||||
assert not aioclient_mock.mock_calls[1][2]['watchdog']
|
assert not aioclient_mock.mock_calls[1][2]['watchdog']
|
||||||
@ -117,7 +117,7 @@ async def test_setup_api_push_api_data_default(hass, aioclient_mock,
|
|||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
||||||
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
|
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
|
||||||
refresh_token = aioclient_mock.mock_calls[1][2]['refresh_token']
|
refresh_token = aioclient_mock.mock_calls[1][2]['refresh_token']
|
||||||
@ -177,27 +177,29 @@ async def test_setup_api_existing_hassio_user(hass, aioclient_mock,
|
|||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
||||||
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
|
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
|
||||||
assert aioclient_mock.mock_calls[1][2]['refresh_token'] == token.token
|
assert aioclient_mock.mock_calls[1][2]['refresh_token'] == token.token
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def test_setup_core_push_timezone(hass, aioclient_mock):
|
||||||
def test_setup_core_push_timezone(hass, aioclient_mock):
|
|
||||||
"""Test setup with API push default data."""
|
"""Test setup with API push default data."""
|
||||||
|
hass.config.time_zone = 'testzone'
|
||||||
|
|
||||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||||
result = yield from async_setup_component(hass, 'hassio', {
|
result = await async_setup_component(hass, 'hassio', {
|
||||||
'hassio': {},
|
'hassio': {},
|
||||||
'homeassistant': {
|
|
||||||
'time_zone': 'testzone',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 5
|
assert aioclient_mock.call_count == 5
|
||||||
assert aioclient_mock.mock_calls[2][2]['timezone'] == "testzone"
|
assert aioclient_mock.mock_calls[2][2]['timezone'] == "testzone"
|
||||||
|
|
||||||
|
await hass.config.async_update(time_zone='America/New_York')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert aioclient_mock.mock_calls[-1][2]['timezone'] == "America/New_York"
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_setup_hassio_no_additional_data(hass, aioclient_mock):
|
def test_setup_hassio_no_additional_data(hass, aioclient_mock):
|
||||||
@ -209,7 +211,7 @@ def test_setup_hassio_no_additional_data(hass, aioclient_mock):
|
|||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
assert aioclient_mock.mock_calls[-1][3]['X-Hassio-Key'] == "123456"
|
assert aioclient_mock.mock_calls[-1][3]['X-Hassio-Key'] == "123456"
|
||||||
|
|
||||||
|
|
||||||
@ -288,14 +290,14 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
|
|||||||
'hassio', 'addon_stdin', {'addon': 'test', 'input': 'test'})
|
'hassio', 'addon_stdin', {'addon': 'test', 'input': 'test'})
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 6
|
assert aioclient_mock.call_count == 7
|
||||||
assert aioclient_mock.mock_calls[-1][2] == 'test'
|
assert aioclient_mock.mock_calls[-1][2] == 'test'
|
||||||
|
|
||||||
yield from hass.services.async_call('hassio', 'host_shutdown', {})
|
yield from hass.services.async_call('hassio', 'host_shutdown', {})
|
||||||
yield from hass.services.async_call('hassio', 'host_reboot', {})
|
yield from hass.services.async_call('hassio', 'host_reboot', {})
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 8
|
assert aioclient_mock.call_count == 9
|
||||||
|
|
||||||
yield from hass.services.async_call('hassio', 'snapshot_full', {})
|
yield from hass.services.async_call('hassio', 'snapshot_full', {})
|
||||||
yield from hass.services.async_call('hassio', 'snapshot_partial', {
|
yield from hass.services.async_call('hassio', 'snapshot_partial', {
|
||||||
@ -305,7 +307,7 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
|
|||||||
})
|
})
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 10
|
assert aioclient_mock.call_count == 11
|
||||||
assert aioclient_mock.mock_calls[-1][2] == {
|
assert aioclient_mock.mock_calls[-1][2] == {
|
||||||
'addons': ['test'], 'folders': ['ssl'], 'password': "123456"}
|
'addons': ['test'], 'folders': ['ssl'], 'password': "123456"}
|
||||||
|
|
||||||
@ -321,7 +323,7 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
|
|||||||
})
|
})
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 12
|
assert aioclient_mock.call_count == 13
|
||||||
assert aioclient_mock.mock_calls[-1][2] == {
|
assert aioclient_mock.mock_calls[-1][2] == {
|
||||||
'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False,
|
'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False,
|
||||||
'password': "123456"
|
'password': "123456"
|
||||||
@ -341,12 +343,12 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock):
|
|||||||
yield from hass.services.async_call('homeassistant', 'stop')
|
yield from hass.services.async_call('homeassistant', 'stop')
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 3
|
assert aioclient_mock.call_count == 4
|
||||||
|
|
||||||
yield from hass.services.async_call('homeassistant', 'check_config')
|
yield from hass.services.async_call('homeassistant', 'check_config')
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 3
|
assert aioclient_mock.call_count == 4
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
'homeassistant.config.async_check_ha_config_file',
|
'homeassistant.config.async_check_ha_config_file',
|
||||||
@ -356,4 +358,4 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock):
|
|||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
assert mock_check_config.called
|
assert mock_check_config.called
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 4
|
assert aioclient_mock.call_count == 5
|
||||||
|
@ -221,3 +221,24 @@ class TestComponentZone(unittest.TestCase):
|
|||||||
|
|
||||||
assert zone.zone.in_zone(self.hass.states.get('zone.passive_zone'),
|
assert zone.zone.in_zone(self.hass.states.get('zone.passive_zone'),
|
||||||
latitude, longitude)
|
latitude, longitude)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_core_config_update(hass):
|
||||||
|
"""Test updating core config will update home zone."""
|
||||||
|
assert await setup.async_setup_component(hass, 'zone', {})
|
||||||
|
|
||||||
|
home = hass.states.get('zone.home')
|
||||||
|
|
||||||
|
await hass.config.async_update(
|
||||||
|
location_name='Updated Name',
|
||||||
|
latitude=10,
|
||||||
|
longitude=20,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
home_updated = hass.states.get('zone.home')
|
||||||
|
|
||||||
|
assert home is not home_updated
|
||||||
|
assert home_updated.name == 'Updated Name'
|
||||||
|
assert home_updated.attributes['latitude'] == 10
|
||||||
|
assert home_updated.attributes['longitude'] == 20
|
||||||
|
@ -436,6 +436,68 @@ async def test_track_sunrise(hass):
|
|||||||
assert len(offset_runs) == 1
|
assert len(offset_runs) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_track_sunrise_update_location(hass):
|
||||||
|
"""Test track the sunrise."""
|
||||||
|
# Setup sun component
|
||||||
|
hass.config.latitude = 32.87336
|
||||||
|
hass.config.longitude = 117.22743
|
||||||
|
assert await async_setup_component(hass, sun.DOMAIN, {
|
||||||
|
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||||
|
|
||||||
|
# Get next sunrise
|
||||||
|
astral = Astral()
|
||||||
|
utc_now = datetime(2014, 5, 24, 12, 0, 0, tzinfo=dt_util.UTC)
|
||||||
|
utc_today = utc_now.date()
|
||||||
|
|
||||||
|
mod = -1
|
||||||
|
while True:
|
||||||
|
next_rising = (astral.sunrise_utc(
|
||||||
|
utc_today + timedelta(days=mod),
|
||||||
|
hass.config.latitude, hass.config.longitude))
|
||||||
|
if next_rising > utc_now:
|
||||||
|
break
|
||||||
|
mod += 1
|
||||||
|
|
||||||
|
# Track sunrise
|
||||||
|
runs = []
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=utc_now):
|
||||||
|
async_track_sunrise(hass, lambda: runs.append(1))
|
||||||
|
|
||||||
|
# Mimick sunrise
|
||||||
|
_send_time_changed(hass, next_rising)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(runs) == 1
|
||||||
|
|
||||||
|
# Move!
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=utc_now):
|
||||||
|
await hass.config.async_update(
|
||||||
|
latitude=40.755931,
|
||||||
|
longitude=-73.984606,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Mimick sunrise
|
||||||
|
_send_time_changed(hass, next_rising)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
# Did not increase
|
||||||
|
assert len(runs) == 1
|
||||||
|
|
||||||
|
# Get next sunrise
|
||||||
|
mod = -1
|
||||||
|
while True:
|
||||||
|
next_rising = (astral.sunrise_utc(
|
||||||
|
utc_today + timedelta(days=mod),
|
||||||
|
hass.config.latitude, hass.config.longitude))
|
||||||
|
if next_rising > utc_now:
|
||||||
|
break
|
||||||
|
mod += 1
|
||||||
|
|
||||||
|
# Mimick sunrise at new location
|
||||||
|
_send_time_changed(hass, next_rising)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(runs) == 2
|
||||||
|
|
||||||
|
|
||||||
async def test_track_sunset(hass):
|
async def test_track_sunset(hass):
|
||||||
"""Test track the sunset."""
|
"""Test track the sunset."""
|
||||||
latitude = 32.87336
|
latitude = 32.87336
|
||||||
|
@ -444,7 +444,7 @@ async def test_updating_configuration(hass, hass_storage):
|
|||||||
hass_storage["core.config"] = dict(core_data)
|
hass_storage["core.config"] = dict(core_data)
|
||||||
await config_util.async_process_ha_core_config(
|
await config_util.async_process_ha_core_config(
|
||||||
hass, {'whitelist_external_dirs': '/tmp'})
|
hass, {'whitelist_external_dirs': '/tmp'})
|
||||||
await hass.config.update(latitude=50)
|
await hass.config.async_update(latitude=50)
|
||||||
|
|
||||||
new_core_data = copy.deepcopy(core_data)
|
new_core_data = copy.deepcopy(core_data)
|
||||||
new_core_data['data']['latitude'] = 50
|
new_core_data['data']['latitude'] = 50
|
||||||
|
@ -955,7 +955,7 @@ async def test_event_on_update(hass, hass_storage):
|
|||||||
|
|
||||||
assert hass.config.latitude != 12
|
assert hass.config.latitude != 12
|
||||||
|
|
||||||
await hass.config.update(latitude=12)
|
await hass.config.async_update(latitude=12)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.config.latitude == 12
|
assert hass.config.latitude == 12
|
||||||
@ -963,10 +963,10 @@ async def test_event_on_update(hass, hass_storage):
|
|||||||
assert events[0].data == {'latitude': 12}
|
assert events[0].data == {'latitude': 12}
|
||||||
|
|
||||||
|
|
||||||
def test_bad_timezone_raises_value_error(hass):
|
async def test_bad_timezone_raises_value_error(hass):
|
||||||
"""Test bad timezone raises ValueError."""
|
"""Test bad timezone raises ValueError."""
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
hass.config.set_time_zone('not_a_timezone')
|
await hass.config.async_update(time_zone='not_a_timezone')
|
||||||
|
|
||||||
|
|
||||||
@patch('homeassistant.core.monotonic')
|
@patch('homeassistant.core.monotonic')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user